diff mbox series

[3/8] smb: client: allow creating symlinks via reparse points

Message ID 20231125220813.30538-4-pc@manguebit.com (mailing list archive)
State New, archived
Headers show
Series reparse points work | expand

Commit Message

Paulo Alcantara Nov. 25, 2023, 10:08 p.m. UTC
Add support for creating symlinks via IO_REPARSE_TAG_SYMLINK reparse
points in SMB2+.

These are fully supported by most SMB servers and documented in
MS-FSCC.  Also have the advantage of requiring fewer roundtrips as
their symlink targets can be parsed directly from CREATE responses on
STATUS_STOPPED_ON_SYMLINK errors.

Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
 fs/smb/client/cifsglob.h |  6 ++++
 fs/smb/client/link.c     | 15 ++++++---
 fs/smb/client/smb2ops.c  | 70 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+), 5 deletions(-)

Comments

kernel test robot Nov. 26, 2023, 12:56 a.m. UTC | #1
Hi Paulo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on cifs/for-next]
[also build test WARNING on next-20231124]
[cannot apply to linus/master v6.7-rc2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Paulo-Alcantara/smb-client-extend-smb2_compound_op-to-accept-more-commands/20231126-061137
base:   git://git.samba.org/sfrench/cifs-2.6.git for-next
patch link:    https://lore.kernel.org/r/20231125220813.30538-4-pc%40manguebit.com
patch subject: [PATCH 3/8] smb: client: allow creating symlinks via reparse points
config: x86_64-rhel-8.3-rust (https://download.01.org/0day-ci/archive/20231126/202311260838.nx5mkj1j-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231126/202311260838.nx5mkj1j-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311260838.nx5mkj1j-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/smb/client/smb2ops.c:5267:6: warning: variable 'rc' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (!IS_ERR(new))
               ^~~~~~~~~~~~
   fs/smb/client/smb2ops.c:5275:9: note: uninitialized use occurs here
           return rc;
                  ^~
   fs/smb/client/smb2ops.c:5267:2: note: remove the 'if' if its condition is always false
           if (!IS_ERR(new))
           ^~~~~~~~~~~~~~~~~
   fs/smb/client/smb2ops.c:5227:8: note: initialize the variable 'rc' to silence this warning
           int rc;
                 ^
                  = 0
   1 warning generated.


vim +5267 fs/smb/client/smb2ops.c

  5211	
  5212	static int smb2_create_reparse_symlink(const unsigned int xid,
  5213					       struct inode *inode,
  5214					       struct dentry *dentry,
  5215					       struct cifs_tcon *tcon,
  5216					       const char *full_path,
  5217					       const char *symname)
  5218	{
  5219		struct reparse_symlink_data_buffer *buf = NULL;
  5220		struct cifs_open_info_data data;
  5221		struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
  5222		struct inode *new;
  5223		struct kvec iov;
  5224		__le16 *path;
  5225		char *sym;
  5226		u16 len, plen;
  5227		int rc;
  5228	
  5229		sym = kstrdup(symname, GFP_KERNEL);
  5230		if (!sym)
  5231			return -ENOMEM;
  5232	
  5233		data = (struct cifs_open_info_data) {
  5234			.reparse_point = true,
  5235			.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
  5236			.symlink_target = sym,
  5237		};
  5238	
  5239		path = cifs_convert_path_to_utf16(symname, cifs_sb);
  5240		if (!path) {
  5241			rc = -ENOMEM;
  5242			goto out;
  5243		}
  5244	
  5245		plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
  5246		len = sizeof(*buf) + plen * 2;
  5247		buf = kzalloc(len, GFP_KERNEL);
  5248		if (!buf) {
  5249			rc = -ENOMEM;
  5250			goto out;
  5251		}
  5252	
  5253		buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
  5254		buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
  5255		buf->SubstituteNameOffset = cpu_to_le16(plen);
  5256		buf->SubstituteNameLength = cpu_to_le16(plen);
  5257		memcpy((u8 *)buf->PathBuffer + plen, path, plen);
  5258		buf->PrintNameOffset = 0;
  5259		buf->PrintNameLength = cpu_to_le16(plen);
  5260		memcpy(buf->PathBuffer, path, plen);
  5261		buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
  5262	
  5263		iov.iov_base = buf;
  5264		iov.iov_len = len;
  5265		new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
  5266					     tcon, full_path, &iov);
> 5267		if (!IS_ERR(new))
  5268			d_instantiate(dentry, new);
  5269		else
  5270			rc = PTR_ERR(new);
  5271	out:
  5272		kfree(path);
  5273		cifs_free_open_info(&data);
  5274		kfree(buf);
  5275		return rc;
  5276	}
  5277
diff mbox series

Patch

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index be84683b01ee..cec8f8d53e2e 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -559,6 +559,12 @@  struct smb_version_operations {
 	int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
 				   struct kvec *rsp_iov,
 				   struct cifs_open_info_data *data);
+	int (*create_reparse_symlink)(const unsigned int xid,
+				      struct inode *inode,
+				      struct dentry *dentry,
+				      struct cifs_tcon *tcon,
+				      const char *full_path,
+				      const char *symname);
 };
 
 struct smb_version_values {
diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
index 5ce0f74be4ec..82fb069c6ce4 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -569,6 +569,7 @@  cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
 	int rc = -EOPNOTSUPP;
 	unsigned int xid;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct TCP_Server_Info *server;
 	struct tcon_link *tlink;
 	struct cifs_tcon *pTcon;
 	const char *full_path;
@@ -590,6 +591,7 @@  cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
 		goto symlink_exit;
 	}
 	pTcon = tlink_tcon(tlink);
+	server = cifs_pick_channel(pTcon->ses);
 
 	full_path = build_path_from_dentry(direntry, page);
 	if (IS_ERR(full_path)) {
@@ -601,17 +603,20 @@  cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
 	cifs_dbg(FYI, "symname is %s\n", symname);
 
 	/* BB what if DFS and this volume is on different share? BB */
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
+	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
 		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-	else if (pTcon->unix_ext)
+	} else if (pTcon->unix_ext) {
 		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
 					   cifs_sb->local_nls,
 					   cifs_remap(cifs_sb));
 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-	/* else
-	   rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
-					cifs_sb_target->local_nls); */
+	} else if (server->ops->create_reparse_symlink) {
+		rc =  server->ops->create_reparse_symlink(xid, inode, direntry,
+							  pTcon, full_path,
+							  symname);
+		goto symlink_exit;
+	}
 
 	if (rc == 0) {
 		if (pTcon->posix_extensions) {
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index c1fe7ba02f6f..c8441e766369 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -5209,6 +5209,72 @@  static int nfs_make_node(unsigned int xid, struct inode *inode,
 	return rc;
 }
 
+static int smb2_create_reparse_symlink(const unsigned int xid,
+				       struct inode *inode,
+				       struct dentry *dentry,
+				       struct cifs_tcon *tcon,
+				       const char *full_path,
+				       const char *symname)
+{
+	struct reparse_symlink_data_buffer *buf = NULL;
+	struct cifs_open_info_data data;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct inode *new;
+	struct kvec iov;
+	__le16 *path;
+	char *sym;
+	u16 len, plen;
+	int rc;
+
+	sym = kstrdup(symname, GFP_KERNEL);
+	if (!sym)
+		return -ENOMEM;
+
+	data = (struct cifs_open_info_data) {
+		.reparse_point = true,
+		.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
+		.symlink_target = sym,
+	};
+
+	path = cifs_convert_path_to_utf16(symname, cifs_sb);
+	if (!path) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
+	len = sizeof(*buf) + plen * 2;
+	buf = kzalloc(len, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
+	buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
+	buf->SubstituteNameOffset = cpu_to_le16(plen);
+	buf->SubstituteNameLength = cpu_to_le16(plen);
+	memcpy((u8 *)buf->PathBuffer + plen, path, plen);
+	buf->PrintNameOffset = 0;
+	buf->PrintNameLength = cpu_to_le16(plen);
+	memcpy(buf->PathBuffer, path, plen);
+	buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
+
+	iov.iov_base = buf;
+	iov.iov_len = len;
+	new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+				     tcon, full_path, &iov);
+	if (!IS_ERR(new))
+		d_instantiate(dentry, new);
+	else
+		rc = PTR_ERR(new);
+out:
+	kfree(path);
+	cifs_free_open_info(&data);
+	kfree(buf);
+	return rc;
+}
+
 static int smb2_make_node(unsigned int xid, struct inode *inode,
 			  struct dentry *dentry, struct cifs_tcon *tcon,
 			  const char *full_path, umode_t mode, dev_t dev)
@@ -5285,6 +5351,7 @@  struct smb_version_operations smb20_operations = {
 	.parse_reparse_point = smb2_parse_reparse_point,
 	.query_mf_symlink = smb3_query_mf_symlink,
 	.create_mf_symlink = smb3_create_mf_symlink,
+	.create_reparse_symlink = smb2_create_reparse_symlink,
 	.open = smb2_open_file,
 	.set_fid = smb2_set_fid,
 	.close = smb2_close_file,
@@ -5387,6 +5454,7 @@  struct smb_version_operations smb21_operations = {
 	.parse_reparse_point = smb2_parse_reparse_point,
 	.query_mf_symlink = smb3_query_mf_symlink,
 	.create_mf_symlink = smb3_create_mf_symlink,
+	.create_reparse_symlink = smb2_create_reparse_symlink,
 	.open = smb2_open_file,
 	.set_fid = smb2_set_fid,
 	.close = smb2_close_file,
@@ -5492,6 +5560,7 @@  struct smb_version_operations smb30_operations = {
 	.parse_reparse_point = smb2_parse_reparse_point,
 	.query_mf_symlink = smb3_query_mf_symlink,
 	.create_mf_symlink = smb3_create_mf_symlink,
+	.create_reparse_symlink = smb2_create_reparse_symlink,
 	.open = smb2_open_file,
 	.set_fid = smb2_set_fid,
 	.close = smb2_close_file,
@@ -5606,6 +5675,7 @@  struct smb_version_operations smb311_operations = {
 	.parse_reparse_point = smb2_parse_reparse_point,
 	.query_mf_symlink = smb3_query_mf_symlink,
 	.create_mf_symlink = smb3_create_mf_symlink,
+	.create_reparse_symlink = smb2_create_reparse_symlink,
 	.open = smb2_open_file,
 	.set_fid = smb2_set_fid,
 	.close = smb2_close_file,