summaryrefslogtreecommitdiff
path: root/fs/smb/client/smb2inode.c
diff options
context:
space:
mode:
authorPaulo Alcantara <pc@manguebit.com>2023-11-25 23:55:03 -0300
committerSteve French <stfrench@microsoft.com>2024-01-07 15:46:05 -0600
commit102466f303ffcd5cff207b3c122557f73f1041e6 (patch)
tree3f6c69b86dbb4343870b24ea3a289823622835ae /fs/smb/client/smb2inode.c
parent3322960ce222997b1663ffa69e691b2edfec4ac9 (diff)
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: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202311260746.HOJ039BV-lkp@intel.com/ Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/smb/client/smb2inode.c')
-rw-r--r--fs/smb/client/smb2inode.c75
1 files changed, 75 insertions, 0 deletions
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 98fa4f02fbd3..d662bad3b703 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -347,6 +347,22 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
smb2_set_related(&rqst[num_rqst++]);
trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
break;
+ case SMB2_OP_SET_REPARSE:
+ rqst[num_rqst].rq_iov = vars->io_iov;
+ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
+
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMPOUND_FID, COMPOUND_FID,
+ FSCTL_SET_REPARSE_POINT,
+ in_iov[i].iov_base,
+ in_iov[i].iov_len, 0);
+ if (rc)
+ goto finished;
+ smb2_set_next_command(tcon, &rqst[num_rqst]);
+ smb2_set_related(&rqst[num_rqst++]);
+ trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
default:
cifs_dbg(VFS, "Invalid command\n");
rc = -EINVAL;
@@ -503,6 +519,16 @@ finished:
tcon->tid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
+ case SMB2_OP_SET_REPARSE:
+ if (rc) {
+ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
+ tcon->tid, rc);
+ } else {
+ trace_smb3_set_reparse_compound_done(xid, ses->Suid,
+ tcon->tid);
+ }
+ SMB2_ioctl_free(&rqst[num_rqst++]);
+ break;
}
}
SMB2_close_free(&rqst[num_rqst]);
@@ -887,3 +913,52 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
cifs_put_tlink(tlink);
return rc;
}
+
+struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ struct super_block *sb,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+ struct kvec *iov)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsFileInfo *cfile;
+ struct inode *new = NULL;
+ struct kvec in_iov[2];
+ int cmds[2];
+ int da, co, cd;
+ int rc;
+
+ da = SYNCHRONIZE | DELETE |
+ FILE_READ_ATTRIBUTES |
+ FILE_WRITE_ATTRIBUTES;
+ co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
+ cd = FILE_CREATE;
+ cmds[0] = SMB2_OP_SET_REPARSE;
+ in_iov[0] = *iov;
+ in_iov[1].iov_base = data;
+ in_iov[1].iov_len = sizeof(*data);
+
+ if (tcon->posix_extensions) {
+ cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+ cmds, 2, cfile, NULL, NULL, NULL, NULL);
+ if (!rc) {
+ rc = smb311_posix_get_inode_info(&new, full_path,
+ data, sb, xid);
+ }
+ } else {
+ cmds[1] = SMB2_OP_QUERY_INFO;
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+ cmds, 2, cfile, NULL, NULL, NULL, NULL);
+ if (!rc) {
+ rc = cifs_get_inode_info(&new, full_path,
+ data, sb, xid, NULL);
+ }
+ }
+ return rc ? ERR_PTR(rc) : new;
+}