ksmbd: use LOOKUP_BENEATH to prevent the out of share access
authorHyunchul Lee <hyc.lee@gmail.com>
Fri, 24 Sep 2021 15:06:16 +0000 (00:06 +0900)
committerSteve French <stfrench@microsoft.com>
Sat, 25 Sep 2021 02:25:23 +0000 (21:25 -0500)
instead of removing '..' in a given path, call
kern_path with LOOKUP_BENEATH flag to prevent
the out of share access.

ran various test on this:
smb2-cat-async smb://127.0.0.1/homes/../out_of_share
smb2-cat-async smb://127.0.0.1/homes/foo/../../out_of_share
smbclient //127.0.0.1/homes -c "mkdir ../foo2"
smbclient //127.0.0.1/homes -c "rename bar ../bar"

Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Ralph Boehme <slow@samba.org>
Tested-by: Steve French <smfrench@gmail.com>
Tested-by: Namjae Jeon <linkinjeon@kernel.org>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/ksmbd/misc.c
fs/ksmbd/misc.h
fs/ksmbd/smb2pdu.c
fs/ksmbd/vfs.c
fs/ksmbd/vfs.h

index 3eac3c0..6a19f4b 100644 (file)
@@ -158,25 +158,21 @@ out:
  * Return : windows path string or error
  */
 
-char *convert_to_nt_pathname(char *filename, char *sharepath)
+char *convert_to_nt_pathname(char *filename)
 {
        char *ab_pathname;
-       int len, name_len;
 
-       name_len = strlen(filename);
-       ab_pathname = kmalloc(name_len, GFP_KERNEL);
-       if (!ab_pathname)
-               return NULL;
-
-       ab_pathname[0] = '\\';
-       ab_pathname[1] = '\0';
+       if (strlen(filename) == 0) {
+               ab_pathname = kmalloc(2, GFP_KERNEL);
+               ab_pathname[0] = '\\';
+               ab_pathname[1] = '\0';
+       } else {
+               ab_pathname = kstrdup(filename, GFP_KERNEL);
+               if (!ab_pathname)
+                       return NULL;
 
-       len = strlen(sharepath);
-       if (!strncmp(filename, sharepath, len) && name_len != len) {
-               strscpy(ab_pathname, &filename[len], name_len);
                ksmbd_conv_path_to_windows(ab_pathname);
        }
-
        return ab_pathname;
 }
 
@@ -191,77 +187,19 @@ int get_nlink(struct kstat *st)
        return nlink;
 }
 
-char *ksmbd_conv_path_to_unix(char *path)
+void ksmbd_conv_path_to_unix(char *path)
 {
-       size_t path_len, remain_path_len, out_path_len;
-       char *out_path, *out_next;
-       int i, pre_dotdot_cnt = 0, slash_cnt = 0;
-       bool is_last;
-
        strreplace(path, '\\', '/');
-       path_len = strlen(path);
-       remain_path_len = path_len;
-       if (path_len == 0)
-               return ERR_PTR(-EINVAL);
-
-       out_path = kzalloc(path_len + 2, GFP_KERNEL);
-       if (!out_path)
-               return ERR_PTR(-ENOMEM);
-       out_path_len = 0;
-       out_next = out_path;
-
-       do {
-               char *name = path + path_len - remain_path_len;
-               char *next = strchrnul(name, '/');
-               size_t name_len = next - name;
-
-               is_last = !next[0];
-               if (name_len == 2 && name[0] == '.' && name[1] == '.') {
-                       pre_dotdot_cnt++;
-                       /* handle the case that path ends with "/.." */
-                       if (is_last)
-                               goto follow_dotdot;
-               } else {
-                       if (pre_dotdot_cnt) {
-follow_dotdot:
-                               slash_cnt = 0;
-                               for (i = out_path_len - 1; i >= 0; i--) {
-                                       if (out_path[i] == '/' &&
-                                           ++slash_cnt == pre_dotdot_cnt + 1)
-                                               break;
-                               }
-
-                               if (i < 0 &&
-                                   slash_cnt != pre_dotdot_cnt) {
-                                       kfree(out_path);
-                                       return ERR_PTR(-EINVAL);
-                               }
-
-                               out_next = &out_path[i+1];
-                               *out_next = '\0';
-                               out_path_len = i + 1;
-
-                       }
-
-                       if (name_len != 0 &&
-                           !(name_len == 1 && name[0] == '.') &&
-                           !(name_len == 2 && name[0] == '.' && name[1] == '.')) {
-                               next[0] = '\0';
-                               sprintf(out_next, "%s/", name);
-                               out_next += name_len + 1;
-                               out_path_len += name_len + 1;
-                               next[0] = '/';
-                       }
-                       pre_dotdot_cnt = 0;
-               }
+}
 
-               remain_path_len -= name_len + 1;
-       } while (!is_last);
+void ksmbd_strip_last_slash(char *path)
+{
+       int len = strlen(path);
 
-       if (out_path_len > 0)
-               out_path[out_path_len-1] = '\0';
-       path[path_len] = '\0';
-       return out_path;
+       while (len && path[len - 1] == '/') {
+               path[len - 1] = '\0';
+               len--;
+       }
 }
 
 void ksmbd_conv_path_to_windows(char *path)
@@ -298,7 +236,7 @@ char *ksmbd_extract_sharename(char *treename)
  *
  * Return:     converted name on success, otherwise NULL
  */
-char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
+char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
 {
        int no_slash = 0, name_len, path_len;
        char *new_name;
index b7b1013..253366b 100644 (file)
@@ -14,12 +14,13 @@ struct ksmbd_file;
 int match_pattern(const char *str, size_t len, const char *pattern);
 int ksmbd_validate_filename(char *filename);
 int parse_stream_name(char *filename, char **stream_name, int *s_type);
-char *convert_to_nt_pathname(char *filename, char *sharepath);
+char *convert_to_nt_pathname(char *filename);
 int get_nlink(struct kstat *st);
-char *ksmbd_conv_path_to_unix(char *path);
+void ksmbd_conv_path_to_unix(char *path);
+void ksmbd_strip_last_slash(char *path);
 void ksmbd_conv_path_to_windows(char *path);
 char *ksmbd_extract_sharename(char *treename);
-char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
+char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
 
 #define KSMBD_DIR_INFO_ALIGNMENT       8
 struct ksmbd_dir_info;
index 0c49a0e..761e121 100644 (file)
@@ -634,7 +634,7 @@ static char *
 smb2_get_name(struct ksmbd_share_config *share, const char *src,
              const int maxlen, struct nls_table *local_nls)
 {
-       char *name, *norm_name, *unixname;
+       char *name;
 
        name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
        if (IS_ERR(name)) {
@@ -642,23 +642,9 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src,
                return name;
        }
 
-       /* change it to absolute unix name */
-       norm_name = ksmbd_conv_path_to_unix(name);
-       if (IS_ERR(norm_name)) {
-               kfree(name);
-               return norm_name;
-       }
-       kfree(name);
-
-       unixname = convert_to_unix_name(share, norm_name);
-       kfree(norm_name);
-       if (!unixname) {
-               pr_err("can not convert absolute name\n");
-               return ERR_PTR(-ENOMEM);
-       }
-
-       ksmbd_debug(SMB, "absolute name = %s\n", unixname);
-       return unixname;
+       ksmbd_conv_path_to_unix(name);
+       ksmbd_strip_last_slash(name);
+       return name;
 }
 
 int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
@@ -2352,7 +2338,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
                        return rc;
        }
 
-       rc = ksmbd_vfs_kern_path(name, 0, path, 0);
+       rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
        if (rc) {
                pr_err("cannot get linux path (%s), err = %d\n",
                       name, rc);
@@ -2427,7 +2413,7 @@ int smb2_open(struct ksmbd_work *work)
        struct oplock_info *opinfo;
        __le32 *next_ptr = NULL;
        int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
-       int rc = 0, len = 0;
+       int rc = 0;
        int contxt_cnt = 0, query_disk_id = 0;
        int maximal_access_ctxt = 0, posix_ctxt = 0;
        int s_type = 0;
@@ -2499,17 +2485,11 @@ int smb2_open(struct ksmbd_work *work)
                        goto err_out1;
                }
        } else {
-               len = strlen(share->path);
-               ksmbd_debug(SMB, "share path len %d\n", len);
-               name = kmalloc(len + 1, GFP_KERNEL);
+               name = kstrdup("", GFP_KERNEL);
                if (!name) {
-                       rsp->hdr.Status = STATUS_NO_MEMORY;
                        rc = -ENOMEM;
                        goto err_out1;
                }
-
-               memcpy(name, share->path, len);
-               *(name + len) = '\0';
        }
 
        req_op_level = req->RequestedOplockLevel;
@@ -2632,7 +2612,7 @@ int smb2_open(struct ksmbd_work *work)
                goto err_out1;
        }
 
-       rc = ksmbd_vfs_kern_path(name, LOOKUP_NO_SYMLINKS, &path, 1);
+       rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
        if (!rc) {
                if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
                        /*
@@ -2661,11 +2641,8 @@ int smb2_open(struct ksmbd_work *work)
        }
 
        if (rc) {
-               if (rc == -EACCES) {
-                       ksmbd_debug(SMB,
-                                   "User does not have right permission\n");
+               if (rc != -ENOENT)
                        goto err_out;
-               }
                ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
                            name, rc);
                rc = 0;
@@ -3161,7 +3138,7 @@ err_out1:
                        rsp->hdr.Status = STATUS_INVALID_PARAMETER;
                else if (rc == -EOPNOTSUPP)
                        rsp->hdr.Status = STATUS_NOT_SUPPORTED;
-               else if (rc == -EACCES || rc == -ESTALE)
+               else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV)
                        rsp->hdr.Status = STATUS_ACCESS_DENIED;
                else if (rc == -ENOENT)
                        rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
@@ -4277,8 +4254,7 @@ static int get_file_all_info(struct ksmbd_work *work,
                return -EACCES;
        }
 
-       filename = convert_to_nt_pathname(fp->filename,
-                                         work->tcon->share_conf->path);
+       filename = convert_to_nt_pathname(fp->filename);
        if (!filename)
                return -ENOMEM;
 
@@ -4733,7 +4709,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
        int rc = 0, len;
        int fs_infoclass_size = 0;
 
-       rc = ksmbd_vfs_kern_path(share->path, LOOKUP_NO_SYMLINKS, &path, 0);
+       rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path);
        if (rc) {
                pr_err("cannot create vfs path\n");
                return -EIO;
@@ -5282,7 +5258,7 @@ static int smb2_rename(struct ksmbd_work *work,
                        goto out;
 
                len = strlen(new_name);
-               if (new_name[len - 1] != '/') {
+               if (len > 0 && new_name[len - 1] != '/') {
                        pr_err("not allow base filename in rename\n");
                        rc = -ESHARE;
                        goto out;
@@ -5310,11 +5286,14 @@ static int smb2_rename(struct ksmbd_work *work,
        }
 
        ksmbd_debug(SMB, "new name %s\n", new_name);
-       rc = ksmbd_vfs_kern_path(new_name, LOOKUP_NO_SYMLINKS, &path, 1);
-       if (rc)
+       rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
+       if (rc) {
+               if (rc != -ENOENT)
+                       goto out;
                file_present = false;
-       else
+       } else {
                path_put(&path);
+       }
 
        if (ksmbd_share_veto_filename(share, new_name)) {
                rc = -ENOENT;
@@ -5384,11 +5363,14 @@ static int smb2_create_link(struct ksmbd_work *work,
        }
 
        ksmbd_debug(SMB, "target name is %s\n", target_name);
-       rc = ksmbd_vfs_kern_path(link_name, LOOKUP_NO_SYMLINKS, &path, 0);
-       if (rc)
+       rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
+       if (rc) {
+               if (rc != -ENOENT)
+                       goto out;
                file_present = false;
-       else
+       } else {
                path_put(&path);
+       }
 
        if (file_info->ReplaceIfExists) {
                if (file_present) {
@@ -5548,7 +5530,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
                 * inode size is retained by backup inode size.
                 */
                size = i_size_read(inode);
-               rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512);
+               rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512);
                if (rc) {
                        pr_err("truncate failed! filename : %s, err %d\n",
                               fp->filename, rc);
@@ -5585,7 +5567,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
        if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
                ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n",
                            fp->filename, newsize);
-               rc = ksmbd_vfs_truncate(work, NULL, fp, newsize);
+               rc = ksmbd_vfs_truncate(work, fp, newsize);
                if (rc) {
                        ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n",
                                    fp->filename, rc);
@@ -5862,7 +5844,7 @@ int smb2_set_info(struct ksmbd_work *work)
        return 0;
 
 err_out:
-       if (rc == -EACCES || rc == -EPERM)
+       if (rc == -EACCES || rc == -EPERM || rc == -EXDEV)
                rsp->hdr.Status = STATUS_ACCESS_DENIED;
        else if (rc == -EINVAL)
                rsp->hdr.Status = STATUS_INVALID_PARAMETER;
index 3733e49..b419542 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/sched/xacct.h>
 #include <linux/crc32c.h>
 
+#include "../internal.h"       /* for vfs_path_lookup */
+
 #include "glob.h"
 #include "oplock.h"
 #include "connection.h"
@@ -44,7 +46,6 @@ static char *extract_last_component(char *path)
                p++;
        } else {
                p = NULL;
-               pr_err("Invalid path %s\n", path);
        }
        return p;
 }
@@ -155,7 +156,7 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
 /**
  * ksmbd_vfs_create() - vfs helper for smb create file
  * @work:      work
- * @name:      file name
+ * @name:      file name that is relative to share
  * @mode:      file create mode
  *
  * Return:     0 on success, otherwise error
@@ -166,7 +167,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
        struct dentry *dentry;
        int err;
 
-       dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_NO_SYMLINKS);
+       dentry = ksmbd_vfs_kern_path_create(work, name,
+                                           LOOKUP_NO_SYMLINKS, &path);
        if (IS_ERR(dentry)) {
                err = PTR_ERR(dentry);
                if (err != -ENOENT)
@@ -191,7 +193,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
 /**
  * ksmbd_vfs_mkdir() - vfs helper for smb create directory
  * @work:      work
- * @name:      directory name
+ * @name:      directory name that is relative to share
  * @mode:      directory create mode
  *
  * Return:     0 on success, otherwise error
@@ -203,8 +205,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
        struct dentry *dentry;
        int err;
 
-       dentry = kern_path_create(AT_FDCWD, name, &path,
-                                 LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY);
+       dentry = ksmbd_vfs_kern_path_create(work, name,
+                                           LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
+                                           &path);
        if (IS_ERR(dentry)) {
                err = PTR_ERR(dentry);
                if (err != -EEXIST)
@@ -579,7 +582,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
 
 /**
  * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
- * @name:      absolute directory or file name
+ * @name:      directory or file name that is relative to share
  *
  * Return:     0 on success, otherwise error
  */
@@ -593,7 +596,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
        if (ksmbd_override_fsids(work))
                return -ENOMEM;
 
-       err = kern_path(name, LOOKUP_NO_SYMLINKS, &path);
+       err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false);
        if (err) {
                ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
                ksmbd_revert_fsids(work);
@@ -638,7 +641,7 @@ out_err:
 /**
  * ksmbd_vfs_link() - vfs helper for creating smb hardlink
  * @oldname:   source file name
- * @newname:   hardlink name
+ * @newname:   hardlink name that is relative to share
  *
  * Return:     0 on success, otherwise error
  */
@@ -659,8 +662,9 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
                goto out1;
        }
 
-       dentry = kern_path_create(AT_FDCWD, newname, &newpath,
-                                 LOOKUP_NO_SYMLINKS | LOOKUP_REVAL);
+       dentry = ksmbd_vfs_kern_path_create(work, newname,
+                                           LOOKUP_NO_SYMLINKS | LOOKUP_REVAL,
+                                           &newpath);
        if (IS_ERR(dentry)) {
                err = PTR_ERR(dentry);
                pr_err("path create err for %s, err %d\n", newname, err);
@@ -781,14 +785,17 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
        int err;
 
        dst_name = extract_last_component(newname);
-       if (!dst_name)
-               return -EINVAL;
+       if (!dst_name) {
+               dst_name = newname;
+               newname = "";
+       }
 
        src_dent_parent = dget_parent(fp->filp->f_path.dentry);
        src_dent = fp->filp->f_path.dentry;
 
-       err = kern_path(newname, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
-                       &dst_path);
+       err = ksmbd_vfs_kern_path(work, newname,
+                                 LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
+                                 &dst_path, false);
        if (err) {
                ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
                goto out;
@@ -834,61 +841,43 @@ out:
 /**
  * ksmbd_vfs_truncate() - vfs helper for smb file truncate
  * @work:      work
- * @name:      old filename
  * @fid:       file id of old file
  * @size:      truncate to given size
  *
  * Return:     0 on success, otherwise error
  */
-int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name,
+int ksmbd_vfs_truncate(struct ksmbd_work *work,
                       struct ksmbd_file *fp, loff_t size)
 {
-       struct path path;
        int err = 0;
+       struct file *filp;
 
-       if (name) {
-               err = kern_path(name, LOOKUP_NO_SYMLINKS, &path);
-               if (err) {
-                       pr_err("cannot get linux path for %s, err %d\n",
-                              name, err);
-                       return err;
-               }
-               err = vfs_truncate(&path, size);
-               if (err)
-                       pr_err("truncate failed for %s err %d\n",
-                              name, err);
-               path_put(&path);
-       } else {
-               struct file *filp;
-
-               filp = fp->filp;
-
-               /* Do we need to break any of a levelII oplock? */
-               smb_break_all_levII_oplock(work, fp, 1);
+       filp = fp->filp;
 
-               if (!work->tcon->posix_extensions) {
-                       struct inode *inode = file_inode(filp);
+       /* Do we need to break any of a levelII oplock? */
+       smb_break_all_levII_oplock(work, fp, 1);
 
-                       if (size < inode->i_size) {
-                               err = check_lock_range(filp, size,
-                                                      inode->i_size - 1, WRITE);
-                       } else {
-                               err = check_lock_range(filp, inode->i_size,
-                                                      size - 1, WRITE);
-                       }
+       if (!work->tcon->posix_extensions) {
+               struct inode *inode = file_inode(filp);
 
-                       if (err) {
-                               pr_err("failed due to lock\n");
-                               return -EAGAIN;
-                       }
+               if (size < inode->i_size) {
+                       err = check_lock_range(filp, size,
+                                              inode->i_size - 1, WRITE);
+               } else {
+                       err = check_lock_range(filp, inode->i_size,
+                                              size - 1, WRITE);
                }
 
-               err = vfs_truncate(&filp->f_path, size);
-               if (err)
-                       pr_err("truncate failed for filename : %s err %d\n",
-                              fp->filename, err);
+               if (err) {
+                       pr_err("failed due to lock\n");
+                       return -EAGAIN;
+               }
        }
 
+       err = vfs_truncate(&filp->f_path, size);
+       if (err)
+               pr_err("truncate failed for filename : %s err %d\n",
+                      fp->filename, err);
        return err;
 }
 
@@ -1206,22 +1195,25 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen)
 
 /**
  * ksmbd_vfs_kern_path() - lookup a file and get path info
- * @name:      name of file for lookup
+ * @name:      file path that is relative to share
  * @flags:     lookup flags
  * @path:      if lookup succeed, return path info
  * @caseless:  caseless filename lookup
  *
  * Return:     0 on success, otherwise error
  */
-int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
-                       bool caseless)
+int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+                       unsigned int flags, struct path *path, bool caseless)
 {
+       struct ksmbd_share_config *share_conf = work->tcon->share_conf;
        int err;
 
-       if (name[0] != '/')
-               return -EINVAL;
-
-       err = kern_path(name, flags, path);
+       flags |= LOOKUP_BENEATH;
+       err = vfs_path_lookup(share_conf->vfs_path.dentry,
+                             share_conf->vfs_path.mnt,
+                             name,
+                             flags,
+                             path);
        if (!err)
                return 0;
 
@@ -1235,11 +1227,10 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
                        return -ENOMEM;
 
                path_len = strlen(filepath);
-               remain_len = path_len - 1;
+               remain_len = path_len;
 
-               err = kern_path("/", flags, &parent);
-               if (err)
-                       goto out;
+               parent = share_conf->vfs_path;
+               path_get(&parent);
 
                while (d_can_lookup(parent.dentry)) {
                        char *filename = filepath + path_len - remain_len;
@@ -1252,21 +1243,21 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
 
                        err = ksmbd_vfs_lookup_in_dir(&parent, filename,
                                                      filename_len);
-                       if (err) {
-                               path_put(&parent);
+                       path_put(&parent);
+                       if (err)
                                goto out;
-                       }
 
-                       path_put(&parent);
                        next[0] = '\0';
 
-                       err = kern_path(filepath, flags, &parent);
+                       err = vfs_path_lookup(share_conf->vfs_path.dentry,
+                                             share_conf->vfs_path.mnt,
+                                             filepath,
+                                             flags,
+                                             &parent);
                        if (err)
                                goto out;
-
-                       if (is_last) {
-                               path->mnt = parent.mnt;
-                               path->dentry = parent.dentry;
+                       else if (is_last) {
+                               *path = parent;
                                goto out;
                        }
 
@@ -1282,6 +1273,23 @@ out:
        return err;
 }
 
+struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+                                         struct path *path)
+{
+       char *abs_name;
+       struct dentry *dent;
+
+       abs_name = convert_to_unix_name(work->tcon->share_conf, name);
+       if (!abs_name)
+               return ERR_PTR(-ENOMEM);
+
+       dent = kern_path_create(AT_FDCWD, abs_name, path, flags);
+       kfree(abs_name);
+       return dent;
+}
+
 int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
                                struct dentry *dentry)
 {
index 85db50a..7b1dcaa 100644 (file)
@@ -126,7 +126,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work,
 int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
 int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
                        char *newname);
-int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name,
+int ksmbd_vfs_truncate(struct ksmbd_work *work,
                       struct ksmbd_file *fp, loff_t size);
 struct srv_copychunk;
 int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
@@ -152,8 +152,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
                                size_t *xattr_stream_name_size, int s_type);
 int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
                           struct dentry *dentry, char *attr_name);
-int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
+int ksmbd_vfs_kern_path(struct ksmbd_work *work,
+                       char *name, unsigned int flags, struct path *path,
                        bool caseless);
+struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+                                         struct path *path);
 int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
 void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
 int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,