diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c
index 3eac3c01749f..0b307ca28a19 100644
--- a/fs/ksmbd/misc.c
+++ b/fs/ksmbd/misc.c
@@ -191,77 +191,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)
diff --git a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h
index b7b10139ada2..af8717d4d85b 100644
--- a/fs/ksmbd/misc.h
+++ b/fs/ksmbd/misc.h
@@ -16,7 +16,8 @@ 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);
 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);
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
index a4b237d4042b..7c5d205bdb22 100644
--- a/fs/ksmbd/smb2pdu.c
+++ b/fs/ksmbd/smb2pdu.c
@@ -642,7 +642,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, *unixname;
 
 	name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
 	if (IS_ERR(name)) {
@@ -651,15 +651,11 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src,
 	}
 
 	/* 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);
+	ksmbd_conv_path_to_unix(name);
+	ksmbd_strip_last_slash(name);
 
-	unixname = convert_to_unix_name(share, norm_name);
-	kfree(norm_name);
+	unixname = convert_to_unix_name(share, name);
+	kfree(name);
 	if (!unixname) {
 		pr_err("can not convert absolute name\n");
 		return ERR_PTR(-ENOMEM);
@@ -2412,7 +2408,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);
@@ -2487,7 +2483,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;
@@ -2559,17 +2555,12 @@ 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(share->path, 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;
@@ -2692,7 +2683,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) {
 			/*
@@ -2721,7 +2712,7 @@ int smb2_open(struct ksmbd_work *work)
 	}
 
 	if (rc) {
-		if (rc == -EACCES) {
+		if (rc != -ENOENT) {
 			ksmbd_debug(SMB,
 				    "User does not have right permission\n");
 			goto err_out;
@@ -3229,7 +3220,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 || 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;
@@ -4801,7 +4792,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 = ksmbd_vfs_kern_path(work, share->path, LOOKUP_NO_SYMLINKS, &path, 0);
 	if (rc) {
 		pr_err("cannot create vfs path\n");
 		return -EIO;
@@ -5378,10 +5369,12 @@ 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)) {
@@ -5456,10 +5449,12 @@ 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) {
@@ -5975,7 +5970,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;
diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
index 3733e4944c1d..c5ff3d1e1ca4 100644
--- a/fs/ksmbd/vfs.c
+++ b/fs/ksmbd/vfs.c
@@ -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"
@@ -1213,33 +1215,48 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen)
  *
  * 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;
+	char *filepath;
 	int err;
 
-	if (name[0] != '/')
+	/* name must start with the share path */
+	if (strlen(name) < share_conf->path_sz ||
+	    strncmp(name, share_conf->path, share_conf->path_sz) != 0)
 		return -EINVAL;
+	else if (strlen(name) == share_conf->path_sz) {
+		*path = share_conf->vfs_path;
+		path_get(path);
+		return 0;
+	}
+
+	filepath = kstrdup(name + share_conf->path_sz + 1,
+			   GFP_KERNEL);
+	if (!filepath)
+		return -ENOMEM;
 
-	err = kern_path(name, flags, path);
-	if (!err)
+	flags |= LOOKUP_BENEATH;
+	err = vfs_path_lookup(share_conf->vfs_path.dentry,
+			      share_conf->vfs_path.mnt,
+			      filepath,
+			      flags,
+			      path);
+	if (!err) {
+		kfree(filepath);
 		return 0;
+	}
 
 	if (caseless) {
-		char *filepath;
 		struct path parent;
 		size_t path_len, remain_len;
 
-		filepath = kstrdup(name, GFP_KERNEL);
-		if (!filepath)
-			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 +1269,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;
 			}
 
@@ -1276,9 +1293,9 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
 
 		path_put(&parent);
 		err = -EINVAL;
-out:
-		kfree(filepath);
 	}
+out:
+	kfree(filepath);
 	return err;
 }
 
diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h
index 85db50abdb24..7089c39e9271 100644
--- a/fs/ksmbd/vfs.h
+++ b/fs/ksmbd/vfs.h
@@ -152,7 +152,8 @@ 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);
 int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
 void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
