aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorNeilBrown <neil@brown.name>2025-07-19 18:46:42 +0900
committerSteve French <stfrench@microsoft.com>2025-07-20 19:17:13 -0500
commita5dc90a9c355a30300ea4b4b25f0732270568d83 (patch)
tree4e88f97716959ec1280a0bc5dd681e75a2eda815
parentsmb/server: use lookup_one_unlocked() (diff)
downloadwireguard-linux-a5dc90a9c355a30300ea4b4b25f0732270568d83.tar.xz
wireguard-linux-a5dc90a9c355a30300ea4b4b25f0732270568d83.zip
smb/server: simplify ksmbd_vfs_kern_path_locked()
ksmbd_vfs_kern_path_locked() first tries to look up the path with the given case. When this fails, if caseless is set, it loops over the components in the path, opening the relevant directory and searching for a name which matches. This name is copied over the original and the the process repeats. Each time a lookup with the newly updated path is repeated from the top (vfs_path_lookup()). When the last component has been case-corrected the simplest next step is to repeat the original lookup with ksmbd_vfs_path_lookup_locked(). If this works it gives exactly what we want, if it fails it gives the correct failure. This observation allows the code to be simplified, in particular removing the ksmbd_vfs_lock_parent() call. This patch also removes the duplication of name and filepath (two names for the one thing) and calls path_put(parent_path) sooner so parent_path can be passed directly to vfs_path_lookup avoiding the need to store it temporarily in path and then copying into parent_path. This patch removes one user of ksmbd_vfs_lock_parent() which will simplify a future patch. Signed-off-by: NeilBrown <neil@brown.name> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/smb/server/vfs.c106
1 files changed, 42 insertions, 64 deletions
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index d3437f6644e3..b1aba0e8f42b 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1200,8 +1200,8 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
/**
* ksmbd_vfs_kern_path_locked() - lookup a file and get path info
- * @work: work
- * @name: file path that is relative to share
+ * @work: work
+ * @filepath: file path that is relative to share
* @flags: lookup flags
* @parent_path: if lookup succeed, return parent_path info
* @path: if lookup succeed, return path info
@@ -1209,84 +1209,62 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
*
* Return: 0 on success, otherwise error
*/
-int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *filepath,
unsigned int flags, struct path *parent_path,
struct path *path, bool caseless)
{
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+ size_t path_len, remain_len;
int err;
- err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path,
+retry:
+ err = ksmbd_vfs_path_lookup_locked(share_conf, filepath, flags, parent_path,
path);
- if (!err)
- return 0;
-
- if (caseless) {
- char *filepath;
- size_t path_len, remain_len;
-
- filepath = name;
- path_len = strlen(filepath);
- remain_len = path_len;
-
- *parent_path = share_conf->vfs_path;
- path_get(parent_path);
-
- while (d_can_lookup(parent_path->dentry)) {
- char *filename = filepath + path_len - remain_len;
- char *next = strchrnul(filename, '/');
- size_t filename_len = next - filename;
- bool is_last = !next[0];
-
- if (filename_len == 0)
- break;
+ if (!err || !caseless)
+ return err;
- err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
- filename_len,
- work->conn->um);
- if (err)
- goto out2;
+ path_len = strlen(filepath);
+ remain_len = path_len;
- next[0] = '\0';
+ *parent_path = share_conf->vfs_path;
+ path_get(parent_path);
- err = vfs_path_lookup(share_conf->vfs_path.dentry,
- share_conf->vfs_path.mnt,
- filepath,
- flags,
- path);
- if (!is_last)
- next[0] = '/';
- if (err)
- goto out2;
- else if (is_last)
- goto out1;
- path_put(parent_path);
- *parent_path = *path;
+ while (d_can_lookup(parent_path->dentry)) {
+ char *filename = filepath + path_len - remain_len;
+ char *next = strchrnul(filename, '/');
+ size_t filename_len = next - filename;
+ bool is_last = !next[0];
- remain_len -= filename_len + 1;
- }
+ if (filename_len == 0)
+ break;
- err = -EINVAL;
-out2:
+ err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
+ filename_len,
+ work->conn->um);
path_put(parent_path);
- }
-
-out1:
- if (!err) {
- err = mnt_want_write(parent_path->mnt);
- if (err) {
- path_put(path);
- path_put(parent_path);
- return err;
+ if (err)
+ goto out;
+ if (is_last) {
+ caseless = false;
+ goto retry;
}
+ next[0] = '\0';
+
+ err = vfs_path_lookup(share_conf->vfs_path.dentry,
+ share_conf->vfs_path.mnt,
+ filepath,
+ flags,
+ parent_path);
+ next[0] = '/';
+ if (err)
+ goto out;
- err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
- if (err) {
- mnt_drop_write(parent_path->mnt);
- path_put(path);
- path_put(parent_path);
- }
+ remain_len -= filename_len + 1;
}
+
+ err = -EINVAL;
+ path_put(parent_path);
+out:
return err;
}