Commit 102466f3 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

smb: client: allow creating special files via reparse points



Add support for creating special files (e.g. char/block devices,
sockets, fifos) via NFS reparse points on SMB2+, which are fully
supported by most SMB servers and documented in MS-FSCC.

smb2_get_reparse_inode() creates the file with a corresponding reparse
point buffer set in @iov through a single roundtrip to the server.

Reported-by: default avatarkernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202311260746.HOJ039BV-lkp@intel.com/


Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 3322960c
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -211,8 +211,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
				 struct cifs_fattr *fattr,
				 struct cifs_open_info_data *data);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
			struct super_block *sb, unsigned int xid);

extern int smb311_posix_get_inode_info(struct inode **inode,
				       const char *full_path,
				       struct cifs_open_info_data *data,
				       struct super_block *sb,
				       const unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode,
			const unsigned char *search_path,
			struct super_block *sb, unsigned int xid);
+4 −3
Original line number Diff line number Diff line
@@ -680,9 +680,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
		 full_path, d_inode(direntry));

again:
	if (pTcon->posix_extensions)
		rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid);
	else if (pTcon->unix_ext) {
	if (pTcon->posix_extensions) {
		rc = smb311_posix_get_inode_info(&newInode, full_path, NULL,
						 parent_dir_inode->i_sb, xid);
	} else if (pTcon->unix_ext) {
		rc = cifs_get_inode_info_unix(&newInode, full_path,
					      parent_dir_inode->i_sb, xid);
	} else {
+6 −4
Original line number Diff line number Diff line
@@ -1020,15 +1020,17 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
		if (!is_interrupt_error(rc))
			mapping_set_error(inode->i_mapping, rc);

		if (tcon->posix_extensions)
			rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid);
		else if (tcon->unix_ext)
		if (tcon->posix_extensions) {
			rc = smb311_posix_get_inode_info(&inode, full_path,
							 NULL, inode->i_sb, xid);
		} else if (tcon->unix_ext) {
			rc = cifs_get_inode_info_unix(&inode, full_path,
						      inode->i_sb, xid);
		else
		} else {
			rc = cifs_get_inode_info(&inode, full_path, NULL,
						 inode->i_sb, xid, NULL);
		}
	}
	/*
	 * Else we are writing out data to server already and could deadlock if
	 * we tried to flush data, and since we do not know if we have data that
+50 −26
Original line number Diff line number Diff line
@@ -1061,7 +1061,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
				 const unsigned int xid,
				 struct cifs_tcon *tcon,
				 const char *full_path,
				 struct cifs_fattr *fattr)
				 struct cifs_fattr *fattr,
				 struct cifs_sid *owner,
				 struct cifs_sid *group)
{
	struct TCP_Server_Info *server = tcon->ses->server;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -1092,7 +1094,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
		rc = 0;
		goto out;
	default:
		if (data->symlink_target) {
		/* Check for cached reparse point data */
		if (data->symlink_target || data->reparse.buf) {
			rc = 0;
		} else if (server->ops->parse_reparse_point) {
			rc = server->ops->parse_reparse_point(cifs_sb,
@@ -1101,6 +1104,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
		break;
	}

	if (tcon->posix_extensions)
		smb311_posix_info_to_fattr(fattr, data, owner, group, sb);
	else
		cifs_open_info_to_fattr(fattr, data, sb);
out:
	free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
@@ -1152,7 +1158,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
		 */
		if (cifs_open_data_reparse(data)) {
			rc = reparse_info_to_fattr(data, sb, xid, tcon,
						   full_path, fattr);
						   full_path, fattr,
						   NULL, NULL);
		} else {
			cifs_open_info_to_fattr(fattr, data, sb);
		}
@@ -1290,18 +1297,19 @@ int cifs_get_inode_info(struct inode **inode,
	return rc;
}

static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
				  struct cifs_fattr *fattr,
				  const char *full_path,
				  struct super_block *sb,
				  const unsigned int xid)
{
	struct cifs_open_info_data data = {};
	struct cifs_open_info_data tmp_data = {};
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
	struct cifs_tcon *tcon;
	struct tcon_link *tlink;
	struct cifs_sid owner, group;
	int tmprc;
	int rc;
	int rc = 0;

	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
@@ -1309,12 +1317,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
	tcon = tlink_tcon(tlink);

	/*
	 * 1. Fetch file metadata
	 * 1. Fetch file metadata if not provided (data)
	 */

	if (!data) {
		rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
					  full_path, &data,
						  full_path, &tmp_data,
						  &owner, &group);
		data = &tmp_data;
	}

	/*
	 * 2. Convert it to internal cifs metadata (fattr)
@@ -1322,7 +1332,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,

	switch (rc) {
	case 0:
		smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
		if (cifs_open_data_reparse(data)) {
			rc = reparse_info_to_fattr(data, sb, xid, tcon,
						   full_path, fattr,
						   &owner, &group);
		} else {
			smb311_posix_info_to_fattr(fattr, data,
						   &owner, &group, sb);
		}
		break;
	case -EREMOTE:
		/* DFS link, no metadata available on this server */
@@ -1353,12 +1370,15 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,

out:
	cifs_put_tlink(tlink);
	cifs_free_open_info(&data);
	cifs_free_open_info(data);
	return rc;
}

int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
				struct super_block *sb, const unsigned int xid)
int smb311_posix_get_inode_info(struct inode **inode,
				const char *full_path,
				struct cifs_open_info_data *data,
				struct super_block *sb,
				const unsigned int xid)
{
	struct cifs_fattr fattr = {};
	int rc;
@@ -1368,7 +1388,7 @@ int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
		return 0;
	}

	rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
	rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid);
	if (rc)
		goto out;

@@ -1516,7 +1536,7 @@ struct inode *cifs_root_iget(struct super_block *sb)

	convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
	if (tcon->posix_extensions)
		rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
		rc = smb311_posix_get_fattr(NULL, &fattr, path, sb, xid);
	else
		rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);

@@ -1889,16 +1909,18 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
	int rc = 0;
	struct inode *inode = NULL;

	if (tcon->posix_extensions)
		rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid);
	if (tcon->posix_extensions) {
		rc = smb311_posix_get_inode_info(&inode, full_path,
						 NULL, parent->i_sb, xid);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
	else if (tcon->unix_ext)
	} else if (tcon->unix_ext) {
		rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
					      xid);
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
	else
	} else {
		rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
					 xid, NULL);
	}

	if (rc)
		return rc;
@@ -2579,13 +2601,15 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
		 dentry, cifs_get_time(dentry), jiffies);

again:
	if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
		rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
	else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
	if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) {
		rc = smb311_posix_get_inode_info(&inode, full_path,
						 NULL, sb, xid);
	} else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) {
		rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
	else
	} else {
		rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
					 xid, NULL);
	}
	if (rc == -EAGAIN && count++ < 10)
		goto again;
out:
+6 −4
Original line number Diff line number Diff line
@@ -614,14 +614,16 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
					cifs_sb_target->local_nls); */

	if (rc == 0) {
		if (pTcon->posix_extensions)
			rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid);
		else if (pTcon->unix_ext)
		if (pTcon->posix_extensions) {
			rc = smb311_posix_get_inode_info(&newinode, full_path,
							 NULL, inode->i_sb, xid);
		} else if (pTcon->unix_ext) {
			rc = cifs_get_inode_info_unix(&newinode, full_path,
						      inode->i_sb, xid);
		else
		} else {
			rc = cifs_get_inode_info(&newinode, full_path, NULL,
						 inode->i_sb, xid, NULL);
		}

		if (rc != 0) {
			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
Loading