Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ksmbd: fix racy issue from using ->d_parent and ->d_name

Al pointed out that ksmbd has racy issue from using ->d_parent and ->d_name
in ksmbd_vfs_unlink and smb2_vfs_rename(). and use new lock_rename_child()
to lock stable parent while underlying rename racy.
Introduce vfs_path_parent_lookup helper to avoid out of share access and
export vfs functions like the following ones to use
vfs_path_parent_lookup().
- rename __lookup_hash() to lookup_one_qstr_excl().
- export lookup_one_qstr_excl().
- export getname_kernel() and putname().

vfs_path_parent_lookup() is used for parent lookup of destination file
using absolute pathname given from FILE_RENAME_INFORMATION request.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Namjae Jeon and committed by
Steve French
74d7970f af36c51e

+294 -397
+35 -112
fs/ksmbd/smb2pdu.c
··· 2408 2408 return rc; 2409 2409 } 2410 2410 2411 - rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); 2411 + rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0); 2412 2412 if (rc) { 2413 2413 pr_err("cannot get linux path (%s), err = %d\n", 2414 2414 name, rc); ··· 2699 2699 goto err_out1; 2700 2700 } 2701 2701 2702 - rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); 2702 + rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1); 2703 2703 if (!rc) { 2704 + file_present = true; 2705 + 2704 2706 if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { 2705 2707 /* 2706 2708 * If file exists with under flags, return access ··· 2711 2709 if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || 2712 2710 req->CreateDisposition == FILE_OPEN_IF_LE) { 2713 2711 rc = -EACCES; 2714 - path_put(&path); 2715 2712 goto err_out; 2716 2713 } 2717 2714 ··· 2718 2717 ksmbd_debug(SMB, 2719 2718 "User does not have write permission\n"); 2720 2719 rc = -EACCES; 2721 - path_put(&path); 2722 2720 goto err_out; 2723 2721 } 2724 2722 } else if (d_is_symlink(path.dentry)) { 2725 2723 rc = -EACCES; 2726 - path_put(&path); 2727 2724 goto err_out; 2728 2725 } 2729 - } 2730 2726 2731 - if (rc) { 2727 + file_present = true; 2728 + idmap = mnt_idmap(path.mnt); 2729 + } else { 2732 2730 if (rc != -ENOENT) 2733 2731 goto err_out; 2734 2732 ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", 2735 2733 name, rc); 2736 2734 rc = 0; 2737 - } else { 2738 - file_present = true; 2739 - idmap = mnt_idmap(path.mnt); 2740 2735 } 2736 + 2741 2737 if (stream_name) { 2742 2738 if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { 2743 2739 if (s_type == DATA_STREAM) { ··· 2862 2864 2863 2865 if ((daccess & FILE_DELETE_LE) || 2864 2866 (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { 2865 - rc = ksmbd_vfs_may_delete(idmap, 2866 - path.dentry); 2867 + rc = inode_permission(idmap, 2868 + d_inode(path.dentry->d_parent), 2869 + MAY_EXEC | MAY_WRITE); 2867 2870 if (rc) 2868 2871 goto err_out; 2869 2872 } ··· 3235 3236 } 3236 3237 3237 3238 err_out: 3238 - if (file_present || created) 3239 - path_put(&path); 3239 + if (file_present || created) { 3240 + inode_unlock(d_inode(path.dentry->d_parent)); 3241 + dput(path.dentry); 3242 + } 3240 3243 ksmbd_revert_fsids(work); 3241 3244 err_out1: 3245 + 3242 3246 if (rc) { 3243 3247 if (rc == -EINVAL) 3244 3248 rsp->hdr.Status = STATUS_INVALID_PARAMETER; ··· 5392 5390 5393 5391 static int smb2_rename(struct ksmbd_work *work, 5394 5392 struct ksmbd_file *fp, 5395 - struct mnt_idmap *idmap, 5396 5393 struct smb2_file_rename_info *file_info, 5397 5394 struct nls_table *local_nls) 5398 5395 { 5399 5396 struct ksmbd_share_config *share = fp->tcon->share_conf; 5400 - char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; 5401 - char *pathname = NULL; 5402 - struct path path; 5403 - bool file_present = true; 5404 - int rc; 5397 + char *new_name = NULL; 5398 + int rc, flags = 0; 5405 5399 5406 5400 ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); 5407 - pathname = kmalloc(PATH_MAX, GFP_KERNEL); 5408 - if (!pathname) 5409 - return -ENOMEM; 5410 - 5411 - abs_oldname = file_path(fp->filp, pathname, PATH_MAX); 5412 - if (IS_ERR(abs_oldname)) { 5413 - rc = -EINVAL; 5414 - goto out; 5415 - } 5416 - old_name = strrchr(abs_oldname, '/'); 5417 - if (old_name && old_name[1] != '\0') { 5418 - old_name++; 5419 - } else { 5420 - ksmbd_debug(SMB, "can't get last component in path %s\n", 5421 - abs_oldname); 5422 - rc = -ENOENT; 5423 - goto out; 5424 - } 5425 - 5426 5401 new_name = smb2_get_name(file_info->FileName, 5427 5402 le32_to_cpu(file_info->FileNameLength), 5428 5403 local_nls); 5429 - if (IS_ERR(new_name)) { 5430 - rc = PTR_ERR(new_name); 5431 - goto out; 5432 - } 5404 + if (IS_ERR(new_name)) 5405 + return PTR_ERR(new_name); 5433 5406 5434 5407 if (strchr(new_name, ':')) { 5435 5408 int s_type; ··· 5430 5453 if (rc) 5431 5454 goto out; 5432 5455 5433 - rc = ksmbd_vfs_setxattr(idmap, 5456 + rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp), 5434 5457 fp->filp->f_path.dentry, 5435 5458 xattr_stream_name, 5436 5459 NULL, 0, 0); ··· 5445 5468 } 5446 5469 5447 5470 ksmbd_debug(SMB, "new name %s\n", new_name); 5448 - rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); 5449 - if (rc) { 5450 - if (rc != -ENOENT) 5451 - goto out; 5452 - file_present = false; 5453 - } else { 5454 - path_put(&path); 5455 - } 5456 - 5457 5471 if (ksmbd_share_veto_filename(share, new_name)) { 5458 5472 rc = -ENOENT; 5459 5473 ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); 5460 5474 goto out; 5461 5475 } 5462 5476 5463 - if (file_info->ReplaceIfExists) { 5464 - if (file_present) { 5465 - rc = ksmbd_vfs_remove_file(work, new_name); 5466 - if (rc) { 5467 - if (rc != -ENOTEMPTY) 5468 - rc = -EINVAL; 5469 - ksmbd_debug(SMB, "cannot delete %s, rc %d\n", 5470 - new_name, rc); 5471 - goto out; 5472 - } 5473 - } 5474 - } else { 5475 - if (file_present && 5476 - strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { 5477 - rc = -EEXIST; 5478 - ksmbd_debug(SMB, 5479 - "cannot rename already existing file\n"); 5480 - goto out; 5481 - } 5482 - } 5477 + if (!file_info->ReplaceIfExists) 5478 + flags = RENAME_NOREPLACE; 5483 5479 5484 - rc = ksmbd_vfs_fp_rename(work, fp, new_name); 5480 + rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags); 5485 5481 out: 5486 - kfree(pathname); 5487 - if (!IS_ERR(new_name)) 5488 - kfree(new_name); 5482 + kfree(new_name); 5489 5483 return rc; 5490 5484 } 5491 5485 ··· 5496 5548 } 5497 5549 5498 5550 ksmbd_debug(SMB, "target name is %s\n", target_name); 5499 - rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); 5551 + rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, 5552 + &path, 0); 5500 5553 if (rc) { 5501 5554 if (rc != -ENOENT) 5502 5555 goto out; 5503 5556 file_present = false; 5504 - } else { 5505 - path_put(&path); 5506 5557 } 5507 5558 5508 5559 if (file_info->ReplaceIfExists) { 5509 5560 if (file_present) { 5510 - rc = ksmbd_vfs_remove_file(work, link_name); 5561 + rc = ksmbd_vfs_remove_file(work, &path); 5511 5562 if (rc) { 5512 5563 rc = -EINVAL; 5513 5564 ksmbd_debug(SMB, "cannot delete %s\n", ··· 5526 5579 if (rc) 5527 5580 rc = -EINVAL; 5528 5581 out: 5582 + if (file_present) { 5583 + inode_unlock(d_inode(path.dentry->d_parent)); 5584 + path_put(&path); 5585 + } 5529 5586 if (!IS_ERR(link_name)) 5530 5587 kfree(link_name); 5531 5588 kfree(pathname); ··· 5707 5756 struct smb2_file_rename_info *rename_info, 5708 5757 unsigned int buf_len) 5709 5758 { 5710 - struct mnt_idmap *idmap; 5711 - struct ksmbd_file *parent_fp; 5712 - struct dentry *parent; 5713 - struct dentry *dentry = fp->filp->f_path.dentry; 5714 - int ret; 5715 - 5716 5759 if (!(fp->daccess & FILE_DELETE_LE)) { 5717 5760 pr_err("no right to delete : 0x%x\n", fp->daccess); 5718 5761 return -EACCES; ··· 5716 5771 le32_to_cpu(rename_info->FileNameLength)) 5717 5772 return -EINVAL; 5718 5773 5719 - idmap = file_mnt_idmap(fp->filp); 5720 - if (ksmbd_stream_fd(fp)) 5721 - goto next; 5774 + if (!le32_to_cpu(rename_info->FileNameLength)) 5775 + return -EINVAL; 5722 5776 5723 - parent = dget_parent(dentry); 5724 - ret = ksmbd_vfs_lock_parent(idmap, parent, dentry); 5725 - if (ret) { 5726 - dput(parent); 5727 - return ret; 5728 - } 5729 - 5730 - parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); 5731 - inode_unlock(d_inode(parent)); 5732 - dput(parent); 5733 - 5734 - if (parent_fp) { 5735 - if (parent_fp->daccess & FILE_DELETE_LE) { 5736 - pr_err("parent dir is opened with delete access\n"); 5737 - ksmbd_fd_put(work, parent_fp); 5738 - return -ESHARE; 5739 - } 5740 - ksmbd_fd_put(work, parent_fp); 5741 - } 5742 - next: 5743 - return smb2_rename(work, fp, idmap, rename_info, 5744 - work->conn->local_nls); 5777 + return smb2_rename(work, fp, rename_info, work->conn->local_nls); 5745 5778 } 5746 5779 5747 5780 static int set_file_disposition_info(struct ksmbd_file *fp,
+199 -258
fs/ksmbd/vfs.c
··· 18 18 #include <linux/vmalloc.h> 19 19 #include <linux/sched/xacct.h> 20 20 #include <linux/crc32c.h> 21 + #include <linux/namei.h> 21 22 22 23 #include "glob.h" 23 24 #include "oplock.h" ··· 36 35 #include "mgmt/user_session.h" 37 36 #include "mgmt/user_config.h" 38 37 39 - static char *extract_last_component(char *path) 40 - { 41 - char *p = strrchr(path, '/'); 42 - 43 - if (p && p[1] != '\0') { 44 - *p = '\0'; 45 - p++; 46 - } else { 47 - p = NULL; 48 - } 49 - return p; 50 - } 51 - 52 38 static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, 53 39 struct inode *parent_inode, 54 40 struct inode *inode) ··· 49 61 50 62 /** 51 63 * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable 52 - * 53 - * the parent dentry got by dget_parent or @parent could be 54 - * unstable, we try to lock a parent inode and lookup the 55 - * child dentry again. 56 - * 57 - * the reference count of @parent isn't incremented. 58 64 */ 59 - int ksmbd_vfs_lock_parent(struct mnt_idmap *idmap, struct dentry *parent, 60 - struct dentry *child) 65 + int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) 61 66 { 62 - struct dentry *dentry; 63 - int ret = 0; 64 - 65 67 inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); 66 - dentry = lookup_one(idmap, child->d_name.name, parent, 67 - child->d_name.len); 68 - if (IS_ERR(dentry)) { 69 - ret = PTR_ERR(dentry); 70 - goto out_err; 68 + if (child->d_parent != parent) { 69 + inode_unlock(d_inode(parent)); 70 + return -ENOENT; 71 71 } 72 72 73 - if (dentry != child) { 74 - ret = -ESTALE; 75 - dput(dentry); 76 - goto out_err; 77 - } 78 - 79 - dput(dentry); 80 73 return 0; 81 - out_err: 82 - inode_unlock(d_inode(parent)); 83 - return ret; 84 74 } 85 75 86 - int ksmbd_vfs_may_delete(struct mnt_idmap *idmap, 87 - struct dentry *dentry) 76 + static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, 77 + char *pathname, unsigned int flags, 78 + struct path *path) 88 79 { 89 - struct dentry *parent; 90 - int ret; 80 + struct qstr last; 81 + struct filename *filename; 82 + struct path *root_share_path = &share_conf->vfs_path; 83 + int err, type; 84 + struct path parent_path; 85 + struct dentry *d; 91 86 92 - parent = dget_parent(dentry); 93 - ret = ksmbd_vfs_lock_parent(idmap, parent, dentry); 94 - if (ret) { 95 - dput(parent); 96 - return ret; 87 + if (pathname[0] == '\0') { 88 + pathname = share_conf->path; 89 + root_share_path = NULL; 90 + } else { 91 + flags |= LOOKUP_BENEATH; 97 92 } 98 93 99 - ret = inode_permission(idmap, d_inode(parent), 100 - MAY_EXEC | MAY_WRITE); 94 + filename = getname_kernel(pathname); 95 + if (IS_ERR(filename)) 96 + return PTR_ERR(filename); 101 97 102 - inode_unlock(d_inode(parent)); 103 - dput(parent); 104 - return ret; 98 + err = vfs_path_parent_lookup(filename, flags, 99 + &parent_path, &last, &type, 100 + root_share_path); 101 + putname(filename); 102 + if (err) 103 + return err; 104 + 105 + if (unlikely(type != LAST_NORM)) { 106 + path_put(&parent_path); 107 + return -ENOENT; 108 + } 109 + 110 + inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); 111 + d = lookup_one_qstr_excl(&last, parent_path.dentry, 0); 112 + if (IS_ERR(d)) 113 + goto err_out; 114 + 115 + if (d_is_negative(d)) { 116 + dput(d); 117 + goto err_out; 118 + } 119 + 120 + path->dentry = d; 121 + path->mnt = share_conf->vfs_path.mnt; 122 + path_put(&parent_path); 123 + 124 + return 0; 125 + 126 + err_out: 127 + inode_unlock(parent_path.dentry->d_inode); 128 + path_put(&parent_path); 129 + return -ENOENT; 105 130 } 106 131 107 132 int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, 108 133 struct dentry *dentry, __le32 *daccess) 109 134 { 110 - struct dentry *parent; 111 135 int ret = 0; 112 136 113 137 *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); ··· 136 136 if (!inode_permission(idmap, d_inode(dentry), MAY_OPEN | MAY_EXEC)) 137 137 *daccess |= FILE_EXECUTE_LE; 138 138 139 - parent = dget_parent(dentry); 140 - ret = ksmbd_vfs_lock_parent(idmap, parent, dentry); 141 - if (ret) { 142 - dput(parent); 143 - return ret; 144 - } 145 - 146 - if (!inode_permission(idmap, d_inode(parent), MAY_EXEC | MAY_WRITE)) 139 + if (!inode_permission(idmap, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE)) 147 140 *daccess |= FILE_DELETE_LE; 148 141 149 - inode_unlock(d_inode(parent)); 150 - dput(parent); 151 142 return ret; 152 143 } 153 144 ··· 571 580 * 572 581 * Return: 0 on success, otherwise error 573 582 */ 574 - int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) 583 + int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) 575 584 { 576 585 struct mnt_idmap *idmap; 577 - struct path path; 578 - struct dentry *parent; 586 + struct dentry *parent = path->dentry->d_parent; 579 587 int err; 580 588 581 589 if (ksmbd_override_fsids(work)) 582 590 return -ENOMEM; 583 591 584 - err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); 585 - if (err) { 586 - ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); 587 - ksmbd_revert_fsids(work); 588 - return err; 589 - } 590 - 591 - idmap = mnt_idmap(path.mnt); 592 - parent = dget_parent(path.dentry); 593 - err = ksmbd_vfs_lock_parent(idmap, parent, path.dentry); 594 - if (err) { 595 - dput(parent); 596 - path_put(&path); 597 - ksmbd_revert_fsids(work); 598 - return err; 599 - } 600 - 601 - if (!d_inode(path.dentry)->i_nlink) { 592 + if (!d_inode(path->dentry)->i_nlink) { 602 593 err = -ENOENT; 603 594 goto out_err; 604 595 } 605 596 606 - if (S_ISDIR(d_inode(path.dentry)->i_mode)) { 607 - err = vfs_rmdir(idmap, d_inode(parent), path.dentry); 597 + idmap = mnt_idmap(path->mnt); 598 + if (S_ISDIR(d_inode(path->dentry)->i_mode)) { 599 + err = vfs_rmdir(idmap, d_inode(parent), path->dentry); 608 600 if (err && err != -ENOTEMPTY) 609 - ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, 610 - err); 601 + ksmbd_debug(VFS, "rmdir failed, err %d\n", err); 611 602 } else { 612 - err = vfs_unlink(idmap, d_inode(parent), path.dentry, NULL); 603 + err = vfs_unlink(idmap, d_inode(parent), path->dentry, NULL); 613 604 if (err) 614 - ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, 615 - err); 605 + ksmbd_debug(VFS, "unlink failed, err %d\n", err); 616 606 } 617 607 618 608 out_err: 619 - inode_unlock(d_inode(parent)); 620 - dput(parent); 621 - path_put(&path); 622 609 ksmbd_revert_fsids(work); 623 610 return err; 624 611 } ··· 655 686 return err; 656 687 } 657 688 658 - static int ksmbd_validate_entry_in_use(struct dentry *src_dent) 689 + int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, 690 + char *newname, int flags) 659 691 { 660 - struct dentry *dst_dent; 661 - 662 - spin_lock(&src_dent->d_lock); 663 - list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { 664 - struct ksmbd_file *child_fp; 665 - 666 - if (d_really_is_negative(dst_dent)) 667 - continue; 668 - 669 - child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); 670 - if (child_fp) { 671 - spin_unlock(&src_dent->d_lock); 672 - ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); 673 - return -EACCES; 674 - } 675 - } 676 - spin_unlock(&src_dent->d_lock); 677 - 678 - return 0; 679 - } 680 - 681 - static int __ksmbd_vfs_rename(struct ksmbd_work *work, 682 - struct mnt_idmap *src_idmap, 683 - struct dentry *src_dent_parent, 684 - struct dentry *src_dent, 685 - struct mnt_idmap *dst_idmap, 686 - struct dentry *dst_dent_parent, 687 - struct dentry *trap_dent, 688 - char *dst_name) 689 - { 690 - struct dentry *dst_dent; 691 - int err; 692 - 693 - if (!work->tcon->posix_extensions) { 694 - err = ksmbd_validate_entry_in_use(src_dent); 695 - if (err) 696 - return err; 697 - } 698 - 699 - if (d_really_is_negative(src_dent_parent)) 700 - return -ENOENT; 701 - if (d_really_is_negative(dst_dent_parent)) 702 - return -ENOENT; 703 - if (d_really_is_negative(src_dent)) 704 - return -ENOENT; 705 - if (src_dent == trap_dent) 706 - return -EINVAL; 692 + struct dentry *old_parent, *new_dentry, *trap; 693 + struct dentry *old_child = old_path->dentry; 694 + struct path new_path; 695 + struct qstr new_last; 696 + struct renamedata rd; 697 + struct filename *to; 698 + struct ksmbd_share_config *share_conf = work->tcon->share_conf; 699 + struct ksmbd_file *parent_fp; 700 + int new_type; 701 + int err, lookup_flags = LOOKUP_NO_SYMLINKS; 707 702 708 703 if (ksmbd_override_fsids(work)) 709 704 return -ENOMEM; 710 705 711 - dst_dent = lookup_one(dst_idmap, dst_name, 712 - dst_dent_parent, strlen(dst_name)); 713 - err = PTR_ERR(dst_dent); 714 - if (IS_ERR(dst_dent)) { 715 - pr_err("lookup failed %s [%d]\n", dst_name, err); 716 - goto out; 706 + to = getname_kernel(newname); 707 + if (IS_ERR(to)) { 708 + err = PTR_ERR(to); 709 + goto revert_fsids; 717 710 } 718 711 719 - err = -ENOTEMPTY; 720 - if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { 721 - struct renamedata rd = { 722 - .old_mnt_idmap = src_idmap, 723 - .old_dir = d_inode(src_dent_parent), 724 - .old_dentry = src_dent, 725 - .new_mnt_idmap = dst_idmap, 726 - .new_dir = d_inode(dst_dent_parent), 727 - .new_dentry = dst_dent, 728 - }; 729 - err = vfs_rename(&rd); 730 - } 712 + retry: 713 + err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, 714 + &new_path, &new_last, &new_type, 715 + &share_conf->vfs_path); 731 716 if (err) 732 - pr_err("vfs_rename failed err %d\n", err); 733 - if (dst_dent) 734 - dput(dst_dent); 735 - out: 717 + goto out1; 718 + 719 + if (old_path->mnt != new_path.mnt) { 720 + err = -EXDEV; 721 + goto out2; 722 + } 723 + 724 + trap = lock_rename_child(old_child, new_path.dentry); 725 + 726 + old_parent = dget(old_child->d_parent); 727 + if (d_unhashed(old_child)) { 728 + err = -EINVAL; 729 + goto out3; 730 + } 731 + 732 + parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent)); 733 + if (parent_fp) { 734 + if (parent_fp->daccess & FILE_DELETE_LE) { 735 + pr_err("parent dir is opened with delete access\n"); 736 + err = -ESHARE; 737 + ksmbd_fd_put(work, parent_fp); 738 + goto out3; 739 + } 740 + ksmbd_fd_put(work, parent_fp); 741 + } 742 + 743 + new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, 744 + lookup_flags | LOOKUP_RENAME_TARGET); 745 + if (IS_ERR(new_dentry)) { 746 + err = PTR_ERR(new_dentry); 747 + goto out3; 748 + } 749 + 750 + if (d_is_symlink(new_dentry)) { 751 + err = -EACCES; 752 + goto out4; 753 + } 754 + 755 + if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { 756 + err = -EEXIST; 757 + goto out4; 758 + } 759 + 760 + if (old_child == trap) { 761 + err = -EINVAL; 762 + goto out4; 763 + } 764 + 765 + if (new_dentry == trap) { 766 + err = -ENOTEMPTY; 767 + goto out4; 768 + } 769 + 770 + rd.old_mnt_idmap = mnt_idmap(old_path->mnt), 771 + rd.old_dir = d_inode(old_parent), 772 + rd.old_dentry = old_child, 773 + rd.new_mnt_idmap = mnt_idmap(new_path.mnt), 774 + rd.new_dir = new_path.dentry->d_inode, 775 + rd.new_dentry = new_dentry, 776 + rd.flags = flags, 777 + err = vfs_rename(&rd); 778 + if (err) 779 + ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); 780 + 781 + out4: 782 + dput(new_dentry); 783 + out3: 784 + dput(old_parent); 785 + unlock_rename(old_parent, new_path.dentry); 786 + out2: 787 + path_put(&new_path); 788 + 789 + if (retry_estale(err, lookup_flags)) { 790 + lookup_flags |= LOOKUP_REVAL; 791 + goto retry; 792 + } 793 + out1: 794 + putname(to); 795 + revert_fsids: 736 796 ksmbd_revert_fsids(work); 737 - return err; 738 - } 739 - 740 - int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, 741 - char *newname) 742 - { 743 - struct mnt_idmap *idmap; 744 - struct path dst_path; 745 - struct dentry *src_dent_parent, *dst_dent_parent; 746 - struct dentry *src_dent, *trap_dent, *src_child; 747 - char *dst_name; 748 - int err; 749 - 750 - dst_name = extract_last_component(newname); 751 - if (!dst_name) { 752 - dst_name = newname; 753 - newname = ""; 754 - } 755 - 756 - src_dent_parent = dget_parent(fp->filp->f_path.dentry); 757 - src_dent = fp->filp->f_path.dentry; 758 - 759 - err = ksmbd_vfs_kern_path(work, newname, 760 - LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, 761 - &dst_path, false); 762 - if (err) { 763 - ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); 764 - goto out; 765 - } 766 - dst_dent_parent = dst_path.dentry; 767 - 768 - trap_dent = lock_rename(src_dent_parent, dst_dent_parent); 769 - dget(src_dent); 770 - dget(dst_dent_parent); 771 - idmap = file_mnt_idmap(fp->filp); 772 - src_child = lookup_one(idmap, src_dent->d_name.name, src_dent_parent, 773 - src_dent->d_name.len); 774 - if (IS_ERR(src_child)) { 775 - err = PTR_ERR(src_child); 776 - goto out_lock; 777 - } 778 - 779 - if (src_child != src_dent) { 780 - err = -ESTALE; 781 - dput(src_child); 782 - goto out_lock; 783 - } 784 - dput(src_child); 785 - 786 - err = __ksmbd_vfs_rename(work, 787 - idmap, 788 - src_dent_parent, 789 - src_dent, 790 - mnt_idmap(dst_path.mnt), 791 - dst_dent_parent, 792 - trap_dent, 793 - dst_name); 794 - out_lock: 795 - dput(src_dent); 796 - dput(dst_dent_parent); 797 - unlock_rename(src_dent_parent, dst_dent_parent); 798 - path_put(&dst_path); 799 - out: 800 - dput(src_dent_parent); 801 797 return err; 802 798 } 803 799 ··· 1013 1079 return vfs_removexattr(idmap, dentry, attr_name); 1014 1080 } 1015 1081 1016 - int ksmbd_vfs_unlink(struct mnt_idmap *idmap, 1017 - struct dentry *dir, struct dentry *dentry) 1082 + int ksmbd_vfs_unlink(struct file *filp) 1018 1083 { 1019 1084 int err = 0; 1085 + struct dentry *dir, *dentry = filp->f_path.dentry; 1086 + struct mnt_idmap *idmap = file_mnt_idmap(filp); 1020 1087 1021 - err = ksmbd_vfs_lock_parent(idmap, dir, dentry); 1088 + dir = dget_parent(dentry); 1089 + err = ksmbd_vfs_lock_parent(dir, dentry); 1022 1090 if (err) 1023 - return err; 1091 + goto out; 1024 1092 dget(dentry); 1025 1093 1026 1094 if (S_ISDIR(d_inode(dentry)->i_mode)) ··· 1034 1098 inode_unlock(d_inode(dir)); 1035 1099 if (err) 1036 1100 ksmbd_debug(VFS, "failed to delete, err %d\n", err); 1101 + out: 1102 + dput(dir); 1037 1103 1038 1104 return err; 1039 1105 } ··· 1138 1200 } 1139 1201 1140 1202 /** 1141 - * ksmbd_vfs_kern_path() - lookup a file and get path info 1203 + * ksmbd_vfs_kern_path_locked() - lookup a file and get path info 1142 1204 * @name: file path that is relative to share 1143 1205 * @flags: lookup flags 1144 1206 * @path: if lookup succeed, return path info ··· 1146 1208 * 1147 1209 * Return: 0 on success, otherwise error 1148 1210 */ 1149 - int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, 1150 - unsigned int flags, struct path *path, bool caseless) 1211 + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, 1212 + unsigned int flags, struct path *path, 1213 + bool caseless) 1151 1214 { 1152 1215 struct ksmbd_share_config *share_conf = work->tcon->share_conf; 1153 1216 int err; 1217 + struct path parent_path; 1154 1218 1155 - flags |= LOOKUP_BENEATH; 1156 - err = vfs_path_lookup(share_conf->vfs_path.dentry, 1157 - share_conf->vfs_path.mnt, 1158 - name, 1159 - flags, 1160 - path); 1219 + err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path); 1161 1220 if (!err) 1162 - return 0; 1221 + return err; 1163 1222 1164 1223 if (caseless) { 1165 1224 char *filepath; 1166 - struct path parent; 1167 1225 size_t path_len, remain_len; 1168 1226 1169 1227 filepath = kstrdup(name, GFP_KERNEL); ··· 1169 1235 path_len = strlen(filepath); 1170 1236 remain_len = path_len; 1171 1237 1172 - parent = share_conf->vfs_path; 1173 - path_get(&parent); 1238 + parent_path = share_conf->vfs_path; 1239 + path_get(&parent_path); 1174 1240 1175 - while (d_can_lookup(parent.dentry)) { 1241 + while (d_can_lookup(parent_path.dentry)) { 1176 1242 char *filename = filepath + path_len - remain_len; 1177 1243 char *next = strchrnul(filename, '/'); 1178 1244 size_t filename_len = next - filename; ··· 1181 1247 if (filename_len == 0) 1182 1248 break; 1183 1249 1184 - err = ksmbd_vfs_lookup_in_dir(&parent, filename, 1250 + err = ksmbd_vfs_lookup_in_dir(&parent_path, filename, 1185 1251 filename_len, 1186 1252 work->conn->um); 1187 - path_put(&parent); 1188 1253 if (err) 1189 - goto out; 1254 + goto out2; 1190 1255 1191 1256 next[0] = '\0'; 1192 1257 ··· 1193 1260 share_conf->vfs_path.mnt, 1194 1261 filepath, 1195 1262 flags, 1196 - &parent); 1263 + path); 1197 1264 if (err) 1198 - goto out; 1199 - else if (is_last) { 1200 - *path = parent; 1201 - goto out; 1202 - } 1265 + goto out2; 1266 + else if (is_last) 1267 + goto out1; 1268 + path_put(&parent_path); 1269 + parent_path = *path; 1203 1270 1204 1271 next[0] = '/'; 1205 1272 remain_len -= filename_len + 1; 1206 1273 } 1207 1274 1208 - path_put(&parent); 1209 1275 err = -EINVAL; 1210 - out: 1276 + out2: 1277 + path_put(&parent_path); 1278 + out1: 1211 1279 kfree(filepath); 1280 + } 1281 + 1282 + if (!err) { 1283 + err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry); 1284 + if (err) 1285 + dput(path->dentry); 1286 + path_put(&parent_path); 1212 1287 } 1213 1288 return err; 1214 1289 }
+8 -11
fs/ksmbd/vfs.h
··· 71 71 __le32 file_attributes; 72 72 }; 73 73 74 - int ksmbd_vfs_lock_parent(struct mnt_idmap *idmap, struct dentry *parent, 75 - struct dentry *child); 76 - int ksmbd_vfs_may_delete(struct mnt_idmap *idmap, struct dentry *dentry); 74 + int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); 77 75 int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, 78 76 struct dentry *dentry, __le32 *daccess); 79 77 int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); ··· 82 84 char *buf, size_t count, loff_t *pos, bool sync, 83 85 ssize_t *written); 84 86 int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); 85 - int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); 87 + int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path); 86 88 int ksmbd_vfs_link(struct ksmbd_work *work, 87 89 const char *oldname, const char *newname); 88 90 int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); 89 - int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, 90 - char *newname); 91 + int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, 92 + char *newname, int flags); 91 93 int ksmbd_vfs_truncate(struct ksmbd_work *work, 92 94 struct ksmbd_file *fp, loff_t size); 93 95 struct srv_copychunk; ··· 114 116 size_t *xattr_stream_name_size, int s_type); 115 117 int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, 116 118 struct dentry *dentry, char *attr_name); 117 - int ksmbd_vfs_kern_path(struct ksmbd_work *work, 118 - char *name, unsigned int flags, struct path *path, 119 - bool caseless); 119 + int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, 120 + unsigned int flags, struct path *path, 121 + bool caseless); 120 122 struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, 121 123 const char *name, 122 124 unsigned int flags, ··· 129 131 int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, 130 132 struct file_allocated_range_buffer *ranges, 131 133 unsigned int in_count, unsigned int *out_count); 132 - int ksmbd_vfs_unlink(struct mnt_idmap *idmap, struct dentry *dir, 133 - struct dentry *dentry); 134 + int ksmbd_vfs_unlink(struct file *filp); 134 135 void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); 135 136 int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, 136 137 struct mnt_idmap *idmap,
+1 -4
fs/ksmbd/vfs_cache.c
··· 244 244 245 245 static void __ksmbd_inode_close(struct ksmbd_file *fp) 246 246 { 247 - struct dentry *dir, *dentry; 248 247 struct ksmbd_inode *ci = fp->f_ci; 249 248 int err; 250 249 struct file *filp; ··· 262 263 if (atomic_dec_and_test(&ci->m_count)) { 263 264 write_lock(&ci->m_lock); 264 265 if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { 265 - dentry = filp->f_path.dentry; 266 - dir = dentry->d_parent; 267 266 ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); 268 267 write_unlock(&ci->m_lock); 269 - ksmbd_vfs_unlink(file_mnt_idmap(filp), dir, dentry); 268 + ksmbd_vfs_unlink(filp); 270 269 write_lock(&ci->m_lock); 271 270 } 272 271 write_unlock(&ci->m_lock);
+45 -12
fs/namei.c
··· 254 254 255 255 return result; 256 256 } 257 + EXPORT_SYMBOL(getname_kernel); 257 258 258 259 void putname(struct filename *name) 259 260 { ··· 272 271 } else 273 272 __putname(name); 274 273 } 274 + EXPORT_SYMBOL(putname); 275 275 276 276 /** 277 277 * check_acl - perform ACL permission checking ··· 1583 1581 * when directory is guaranteed to have no in-lookup children 1584 1582 * at all. 1585 1583 */ 1586 - static struct dentry *__lookup_hash(const struct qstr *name, 1587 - struct dentry *base, unsigned int flags) 1584 + struct dentry *lookup_one_qstr_excl(const struct qstr *name, 1585 + struct dentry *base, 1586 + unsigned int flags) 1588 1587 { 1589 1588 struct dentry *dentry = lookup_dcache(name, base, flags); 1590 1589 struct dentry *old; ··· 1609 1606 } 1610 1607 return dentry; 1611 1608 } 1609 + EXPORT_SYMBOL(lookup_one_qstr_excl); 1612 1610 1613 1611 static struct dentry *lookup_fast(struct nameidata *nd) 1614 1612 { ··· 2536 2532 } 2537 2533 2538 2534 /* Note: this does not consume "name" */ 2539 - static int filename_parentat(int dfd, struct filename *name, 2540 - unsigned int flags, struct path *parent, 2541 - struct qstr *last, int *type) 2535 + static int __filename_parentat(int dfd, struct filename *name, 2536 + unsigned int flags, struct path *parent, 2537 + struct qstr *last, int *type, 2538 + const struct path *root) 2542 2539 { 2543 2540 int retval; 2544 2541 struct nameidata nd; 2545 2542 2546 2543 if (IS_ERR(name)) 2547 2544 return PTR_ERR(name); 2548 - set_nameidata(&nd, dfd, name, NULL); 2545 + set_nameidata(&nd, dfd, name, root); 2549 2546 retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); 2550 2547 if (unlikely(retval == -ECHILD)) 2551 2548 retval = path_parentat(&nd, flags, parent); ··· 2559 2554 } 2560 2555 restore_nameidata(); 2561 2556 return retval; 2557 + } 2558 + 2559 + static int filename_parentat(int dfd, struct filename *name, 2560 + unsigned int flags, struct path *parent, 2561 + struct qstr *last, int *type) 2562 + { 2563 + return __filename_parentat(dfd, name, flags, parent, last, type, NULL); 2562 2564 } 2563 2565 2564 2566 /* does lookup, returns the object with parent locked */ ··· 2583 2571 return ERR_PTR(-EINVAL); 2584 2572 } 2585 2573 inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); 2586 - d = __lookup_hash(&last, path->dentry, 0); 2574 + d = lookup_one_qstr_excl(&last, path->dentry, 0); 2587 2575 if (IS_ERR(d)) { 2588 2576 inode_unlock(path->dentry->d_inode); 2589 2577 path_put(path); ··· 2610 2598 2611 2599 } 2612 2600 EXPORT_SYMBOL(kern_path); 2601 + 2602 + /** 2603 + * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair 2604 + * @filename: filename structure 2605 + * @flags: lookup flags 2606 + * @parent: pointer to struct path to fill 2607 + * @last: last component 2608 + * @type: type of the last component 2609 + * @root: pointer to struct path of the base directory 2610 + */ 2611 + int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, 2612 + struct path *parent, struct qstr *last, int *type, 2613 + const struct path *root) 2614 + { 2615 + return __filename_parentat(AT_FDCWD, filename, flags, parent, last, 2616 + type, root); 2617 + } 2618 + EXPORT_SYMBOL(vfs_path_parent_lookup); 2613 2619 2614 2620 /** 2615 2621 * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair ··· 3882 3852 if (last.name[last.len] && !want_dir) 3883 3853 create_flags = 0; 3884 3854 inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); 3885 - dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags); 3855 + dentry = lookup_one_qstr_excl(&last, path->dentry, 3856 + reval_flag | create_flags); 3886 3857 if (IS_ERR(dentry)) 3887 3858 goto unlock; 3888 3859 ··· 4243 4212 goto exit2; 4244 4213 4245 4214 inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); 4246 - dentry = __lookup_hash(&last, path.dentry, lookup_flags); 4215 + dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); 4247 4216 error = PTR_ERR(dentry); 4248 4217 if (IS_ERR(dentry)) 4249 4218 goto exit3; ··· 4376 4345 goto exit2; 4377 4346 retry_deleg: 4378 4347 inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); 4379 - dentry = __lookup_hash(&last, path.dentry, lookup_flags); 4348 + dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); 4380 4349 error = PTR_ERR(dentry); 4381 4350 if (!IS_ERR(dentry)) { 4382 4351 ··· 4940 4909 retry_deleg: 4941 4910 trap = lock_rename(new_path.dentry, old_path.dentry); 4942 4911 4943 - old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); 4912 + old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry, 4913 + lookup_flags); 4944 4914 error = PTR_ERR(old_dentry); 4945 4915 if (IS_ERR(old_dentry)) 4946 4916 goto exit3; ··· 4949 4917 error = -ENOENT; 4950 4918 if (d_is_negative(old_dentry)) 4951 4919 goto exit4; 4952 - new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); 4920 + new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, 4921 + lookup_flags | target_flags); 4953 4922 error = PTR_ERR(new_dentry); 4954 4923 if (IS_ERR(new_dentry)) 4955 4924 goto exit4;
+6
include/linux/namei.h
··· 57 57 return user_path_at_empty(dfd, name, flags, path, NULL); 58 58 } 59 59 60 + struct dentry *lookup_one_qstr_excl(const struct qstr *name, 61 + struct dentry *base, 62 + unsigned int flags); 60 63 extern int kern_path(const char *, unsigned, struct path *); 61 64 62 65 extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int); 63 66 extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); 64 67 extern void done_path_create(struct path *, struct dentry *); 65 68 extern struct dentry *kern_path_locked(const char *, struct path *); 69 + int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, 70 + struct path *parent, struct qstr *last, int *type, 71 + const struct path *root); 66 72 int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, 67 73 unsigned int, struct path *); 68 74