Commit ff1d5727 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French
Browse files

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: default avatarHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: default avatarNamjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent d40012a8
Loading
Loading
Loading
Loading
+5 −7
Original line number Diff line number Diff line
@@ -2844,14 +2844,12 @@ int smb2_open(struct ksmbd_work *work)
		 * is already granted.
		 */
		if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) {
			if (ksmbd_vfs_inode_permission(path.dentry,
						       open_flags & O_ACCMODE,
						       may_delete)) {
				rc = -EACCES;
			rc = ksmbd_vfs_inode_permission(path.dentry,
					open_flags & O_ACCMODE, may_delete);
			if (rc)
				goto err_out;
		}
	}
	}

	rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent));
	if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) {
@@ -3260,7 +3258,7 @@ int smb2_open(struct ksmbd_work *work)
			rsp->hdr.Status = STATUS_INVALID_PARAMETER;
		else if (rc == -EOPNOTSUPP)
			rsp->hdr.Status = STATUS_NOT_SUPPORTED;
		else if (rc == -EACCES)
		else if (rc == -EACCES || rc == -ESTALE)
			rsp->hdr.Status = STATUS_ACCESS_DENIED;
		else if (rc == -ENOENT)
			rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
@@ -5938,7 +5936,7 @@ int smb2_set_info(struct ksmbd_work *work)
		rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY;
	else if (rc == -EAGAIN)
		rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT;
	else if (rc == -EBADF)
	else if (rc == -EBADF || rc == -ESTALE)
		rsp->hdr.Status = STATUS_INVALID_HANDLE;
	else if (rc == -EEXIST)
		rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION;
+75 −23
Original line number Diff line number Diff line
@@ -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 mask;
	int mask, ret = 0;

	mask = 0;
	acc_mode &= O_ACCMODE;
@@ -86,24 +86,39 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
		return -EACCES;

	if (delete) {
		struct dentry *parent;
		struct dentry *child, *parent;

		parent = dget_parent(dentry);
		if (!parent)
			return -EINVAL;
		inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
		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)) {
			dput(parent);
			return -EACCES;
			ret = -EACCES;
			goto out_lock;
		}
out_lock:
		inode_unlock(d_inode(parent));
		dput(parent);
	}
	return 0;
	return ret;
}

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);

@@ -120,13 +135,28 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
		*daccess |= FILE_EXECUTE_LE;

	parent = dget_parent(dentry);
	if (!parent)
		return 0;
	inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
	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))
		*daccess |= FILE_DELETE_LE;

out_lock:
	inode_unlock(d_inode(parent));
	dput(parent);
	return 0;
	return ret;
}

/**
@@ -726,7 +756,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
{
	struct path dst_path;
	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;
	int err;

@@ -735,11 +765,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
		return -EINVAL;

	src_dent_parent = dget_parent(fp->filp->f_path.dentry);
	if (!src_dent_parent)
		return -EINVAL;

	src_dent = fp->filp->f_path.dentry;
	dget(src_dent);

	err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path);
	if (err) {
@@ -747,20 +773,36 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
		goto out;
	}
	dst_dent_parent = dst_path.dentry;
	dget(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,
				 src_dent_parent,
				 src_dent,
				 dst_dent_parent,
				 trap_dent,
				 dst_name);
	unlock_rename(src_dent_parent, dst_dent_parent);
out_lock:
	dput(src_dent);
	dput(dst_dent_parent);
	unlock_rename(src_dent_parent, dst_dent_parent);
	path_put(&dst_path);
out:
	dput(src_dent);
	dput(src_dent_parent);
	return err;
}
@@ -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)
{
	struct dentry *child;
	int err = 0;

	dget(dentry);
	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
	if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) {
		err = -ENOENT;
	dget(dentry);
	child = lookup_one_len(dentry->d_name.name, dir,
			dentry->d_name.len);
	if (IS_ERR(child)) {
		err = PTR_ERR(child);
		goto out;
	}

	if (child != dentry) {
		err = -ESTALE;
		dput(child);
		goto out;
	}
	dput(child);

	if (S_ISDIR(d_inode(dentry)->i_mode))
		err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry);
	else
		err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL);

out:
	inode_unlock(d_inode(dir));
	dput(dentry);
	inode_unlock(d_inode(dir));
	if (err)
		ksmbd_debug(VFS, "failed to delete, err %d\n", err);