提交 ff1d5727 编写于 作者: N Namjae Jeon 提交者: Steve French

cifsd: add the check if parent is stable by unexpected rename

This patch add the check if parent is stable by unexpected rename.
Signed-off-by: NHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: NNamjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: NSteve French <stfrench@microsoft.com>
上级 d40012a8
...@@ -2844,12 +2844,10 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2844,12 +2844,10 @@ int smb2_open(struct ksmbd_work *work)
* is already granted. * is already granted.
*/ */
if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) {
if (ksmbd_vfs_inode_permission(path.dentry, rc = ksmbd_vfs_inode_permission(path.dentry,
open_flags & O_ACCMODE, open_flags & O_ACCMODE, may_delete);
may_delete)) { if (rc)
rc = -EACCES;
goto err_out; goto err_out;
}
} }
} }
...@@ -3260,7 +3258,7 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3260,7 +3258,7 @@ int smb2_open(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
else if (rc == -EOPNOTSUPP) else if (rc == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED; rsp->hdr.Status = STATUS_NOT_SUPPORTED;
else if (rc == -EACCES) else if (rc == -EACCES || rc == -ESTALE)
rsp->hdr.Status = STATUS_ACCESS_DENIED; rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -ENOENT) else if (rc == -ENOENT)
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
...@@ -5938,7 +5936,7 @@ int smb2_set_info(struct ksmbd_work *work) ...@@ -5938,7 +5936,7 @@ int smb2_set_info(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY;
else if (rc == -EAGAIN) else if (rc == -EAGAIN)
rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT;
else if (rc == -EBADF) else if (rc == -EBADF || rc == -ESTALE)
rsp->hdr.Status = STATUS_INVALID_HANDLE; rsp->hdr.Status = STATUS_INVALID_HANDLE;
else if (rc == -EEXIST) else if (rc == -EEXIST)
rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION;
......
...@@ -70,7 +70,7 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, ...@@ -70,7 +70,7 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
{ {
int mask; int mask, ret = 0;
mask = 0; mask = 0;
acc_mode &= O_ACCMODE; acc_mode &= O_ACCMODE;
...@@ -86,24 +86,39 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) ...@@ -86,24 +86,39 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
return -EACCES; return -EACCES;
if (delete) { if (delete) {
struct dentry *parent; struct dentry *child, *parent;
parent = dget_parent(dentry); parent = dget_parent(dentry);
if (!parent) inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
return -EINVAL; child = lookup_one_len(dentry->d_name.name, parent,
dentry->d_name.len);
if (IS_ERR(child)) {
ret = PTR_ERR(child);
goto out_lock;
}
if (child != dentry) {
ret = -ESTALE;
dput(child);
goto out_lock;
}
dput(child);
if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) { if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) {
dput(parent); ret = -EACCES;
return -EACCES; goto out_lock;
} }
out_lock:
inode_unlock(d_inode(parent));
dput(parent); dput(parent);
} }
return 0; return ret;
} }
int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
{ {
struct dentry *parent; struct dentry *parent, *child;
int ret = 0;
*daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
...@@ -120,13 +135,28 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) ...@@ -120,13 +135,28 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
*daccess |= FILE_EXECUTE_LE; *daccess |= FILE_EXECUTE_LE;
parent = dget_parent(dentry); parent = dget_parent(dentry);
if (!parent) inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
return 0; child = lookup_one_len(dentry->d_name.name, parent,
dentry->d_name.len);
if (IS_ERR(child)) {
ret = PTR_ERR(child);
goto out_lock;
}
if (child != dentry) {
ret = -ESTALE;
dput(child);
goto out_lock;
}
dput(child);
if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
*daccess |= FILE_DELETE_LE; *daccess |= FILE_DELETE_LE;
out_lock:
inode_unlock(d_inode(parent));
dput(parent); dput(parent);
return 0; return ret;
} }
/** /**
...@@ -726,7 +756,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -726,7 +756,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
{ {
struct path dst_path; struct path dst_path;
struct dentry *src_dent_parent, *dst_dent_parent; struct dentry *src_dent_parent, *dst_dent_parent;
struct dentry *src_dent, *trap_dent; struct dentry *src_dent, *trap_dent, *src_child;
char *dst_name; char *dst_name;
int err; int err;
...@@ -735,11 +765,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -735,11 +765,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
src_dent_parent = dget_parent(fp->filp->f_path.dentry); src_dent_parent = dget_parent(fp->filp->f_path.dentry);
if (!src_dent_parent)
return -EINVAL;
src_dent = fp->filp->f_path.dentry; src_dent = fp->filp->f_path.dentry;
dget(src_dent);
err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path); err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path);
if (err) { if (err) {
...@@ -747,20 +773,36 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -747,20 +773,36 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
goto out; goto out;
} }
dst_dent_parent = dst_path.dentry; dst_dent_parent = dst_path.dentry;
dget(dst_dent_parent);
trap_dent = lock_rename(src_dent_parent, dst_dent_parent); trap_dent = lock_rename(src_dent_parent, dst_dent_parent);
dget(src_dent);
dget(dst_dent_parent);
src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent,
src_dent->d_name.len);
if (IS_ERR(src_child)) {
err = PTR_ERR(src_child);
goto out_lock;
}
if (src_child != src_dent) {
err = -ESTALE;
dput(src_child);
goto out_lock;
}
dput(src_child);
err = __ksmbd_vfs_rename(work, err = __ksmbd_vfs_rename(work,
src_dent_parent, src_dent_parent,
src_dent, src_dent,
dst_dent_parent, dst_dent_parent,
trap_dent, trap_dent,
dst_name); dst_name);
unlock_rename(src_dent_parent, dst_dent_parent); out_lock:
dput(src_dent);
dput(dst_dent_parent); dput(dst_dent_parent);
unlock_rename(src_dent_parent, dst_dent_parent);
path_put(&dst_path); path_put(&dst_path);
out: out:
dput(src_dent);
dput(src_dent_parent); dput(src_dent_parent);
return err; return err;
} }
...@@ -1050,23 +1092,33 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) ...@@ -1050,23 +1092,33 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name)
int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry)
{ {
struct dentry *child;
int err = 0; int err = 0;
dget(dentry);
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) { dget(dentry);
err = -ENOENT; child = lookup_one_len(dentry->d_name.name, dir,
dentry->d_name.len);
if (IS_ERR(child)) {
err = PTR_ERR(child);
goto out; goto out;
} }
if (child != dentry) {
err = -ESTALE;
dput(child);
goto out;
}
dput(child);
if (S_ISDIR(d_inode(dentry)->i_mode)) if (S_ISDIR(d_inode(dentry)->i_mode))
err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry);
else else
err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL);
out: out:
inode_unlock(d_inode(dir));
dput(dentry); dput(dentry);
inode_unlock(d_inode(dir));
if (err) if (err)
ksmbd_debug(VFS, "failed to delete, err %d\n", err); ksmbd_debug(VFS, "failed to delete, err %d\n", err);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册