Unverified Commit 1ead2213 authored by NeilBrown's avatar NeilBrown Committed by Christian Brauner
Browse files

smb/server: use end_removing_noperm for for target of smb2_create_link()



Sometimes smb2_create_link() needs to remove the target before creating
the link.
It uses ksmbd_vfs_kern_locked(), and is the only user of that interface.

To match the new naming, that function is changed to
ksmbd_vfs_kern_start_removing(), and related functions or flags are also
renamed.

The lock actually happens in ksmbd_vfs_path_lookup() and that is changed
to use start_removing_noperm() - permission to perform lookup in the
parent was already checked in vfs_path_parent_lookup().

Signed-off-by: default avatarNeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20251113002050.676694-8-neilb@ownmail.net


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent c9ba789d
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -6084,7 +6084,7 @@ static int smb2_create_link(struct ksmbd_work *work,
	}

	ksmbd_debug(SMB, "target name is %s\n", target_name);
	rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
	rc = ksmbd_vfs_kern_path_start_removing(work, link_name, LOOKUP_NO_SYMLINKS,
						&path, 0);
	if (rc) {
		if (rc != -ENOENT)
@@ -6103,7 +6103,7 @@ static int smb2_create_link(struct ksmbd_work *work,
			ksmbd_debug(SMB, "link already exists\n");
			goto out;
		}
		ksmbd_vfs_kern_path_unlock(&path);
		ksmbd_vfs_kern_path_end_removing(&path);
	}
	rc = ksmbd_vfs_link(work, target_name, link_name);
	if (rc)
+12 −15
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)

static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
				 char *pathname, unsigned int flags,
				 struct path *path, bool do_lock)
				 struct path *path, bool for_remove)
{
	struct qstr last;
	struct filename *filename __free(putname) = NULL;
@@ -99,22 +99,20 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
		return -ENOENT;
	}

	if (do_lock) {
	if (for_remove) {
		err = mnt_want_write(path->mnt);
		if (err) {
			path_put(path);
			return -ENOENT;
		}

		inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
		d = lookup_one_qstr_excl(&last, path->dentry, 0);
		d = start_removing_noperm(path->dentry, &last);

		if (!IS_ERR(d)) {
			dput(path->dentry);
			path->dentry = d;
			return 0;
		}
		inode_unlock(path->dentry->d_inode);
		mnt_drop_write(path->mnt);
		path_put(path);
		return -ENOENT;
@@ -1207,7 +1205,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
static
int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
			  unsigned int flags,
			  struct path *path, bool caseless, bool do_lock)
			  struct path *path, bool caseless, bool for_remove)
{
	struct ksmbd_share_config *share_conf = work->tcon->share_conf;
	struct path parent_path;
@@ -1215,7 +1213,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
	int err;

retry:
	err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, do_lock);
	err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove);
	if (!err || !caseless)
		return err;

@@ -1286,7 +1284,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
}

/**
 * ksmbd_vfs_kern_path_locked() - lookup a file and get path info
 * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal
 * @work:		work
 * @filepath:		file path that is relative to share
 * @flags:		lookup flags
@@ -1298,7 +1296,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
 * filesystem will have been gained.
 * Return:	0 on if file was found, otherwise error
 */
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *filepath,
int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *filepath,
				       unsigned int flags,
				       struct path *path, bool caseless)
{
@@ -1306,12 +1304,11 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *filepath,
				     caseless, true);
}

void ksmbd_vfs_kern_path_unlock(const struct path *path)
void ksmbd_vfs_kern_path_end_removing(const struct path *path)
{
	/* While lock is still held, ->d_parent is safe */
	inode_unlock(d_inode(path->dentry->d_parent));
	end_removing(path->dentry);
	mnt_drop_write(path->mnt);
	path_put(path);
	mntput(path->mnt);
}

struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+4 −4
Original line number Diff line number Diff line
@@ -120,10 +120,10 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
			unsigned int flags,
			struct path *path, bool caseless);
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *name,
				       unsigned int flags,
				       struct path *path, bool caseless);
void ksmbd_vfs_kern_path_unlock(const struct path *path);
void ksmbd_vfs_kern_path_end_removing(const struct path *path);
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
					  const char *name,
					  unsigned int flags,