[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250806203705.2560493-31-dhowells@redhat.com>
Date: Wed, 6 Aug 2025 21:36:51 +0100
From: David Howells <dhowells@...hat.com>
To: Steve French <sfrench@...ba.org>
Cc: David Howells <dhowells@...hat.com>,
Paulo Alcantara <pc@...guebit.org>,
Shyam Prasad N <sprasad@...rosoft.com>,
Tom Talpey <tom@...pey.com>,
Wang Zhaolong <wangzhaolong@...weicloud.com>,
Stefan Metzmacher <metze@...ba.org>,
Mina Almasry <almasrymina@...gle.com>,
linux-cifs@...r.kernel.org,
linux-kernel@...r.kernel.org,
netfs@...ts.linux.dev,
linux-fsdevel@...r.kernel.org
Subject: [RFC PATCH 30/31] cifs: Convert SMB2 Posix Mkdir request
Signed-off-by: David Howells <dhowells@...hat.com>
cc: Steve French <sfrench@...ba.org>
cc: Paulo Alcantara <pc@...guebit.org>
cc: Shyam Prasad N <sprasad@...rosoft.com>
cc: Tom Talpey <tom@...pey.com>
cc: linux-cifs@...r.kernel.org
cc: netfs@...ts.linux.dev
cc: linux-fsdevel@...r.kernel.org
---
fs/smb/client/smb2pdu.c | 244 ++++++++++++++++++++++------------------
1 file changed, 137 insertions(+), 107 deletions(-)
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 85486748dd7b..9fc55b315474 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2388,16 +2388,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
}
-static struct create_posix *
-create_posix_buf(umode_t mode)
+static void fill_posix_buf(struct create_posix *buf, umode_t mode)
{
- struct create_posix *buf;
-
- buf = kzalloc(sizeof(struct create_posix),
- GFP_KERNEL);
- if (!buf)
- return NULL;
-
buf->ccontext.DataOffset =
cpu_to_le16(offsetof(struct create_posix, Mode));
buf->ccontext.DataLength = cpu_to_le32(4);
@@ -2424,20 +2416,25 @@ create_posix_buf(umode_t mode)
buf->Name[15] = 0x7C;
buf->Mode = cpu_to_le32(mode);
cifs_dbg(FYI, "mode on posix create 0%o\n", mode);
- return buf;
}
static int
add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
{
+ struct create_posix *buf;
unsigned int num = *num_iovec;
- iov[num].iov_base = create_posix_buf(mode);
if (mode == ACL_NO_MODE)
cifs_dbg(FYI, "%s: no mode\n", __func__);
- if (iov[num].iov_base == NULL)
+
+ buf = kzalloc(sizeof(struct create_posix), GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- iov[num].iov_len = sizeof(struct create_posix);
+
+ fill_posix_buf(buf, mode);
+
+ iov[num].iov_base = buf;
+ iov[num].iov_len = sizeof(struct create_posix);
*num_iovec = num + 1;
return 0;
}
@@ -2906,72 +2903,140 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
return 0;
}
+struct smb2_create_layout {
+ const struct nls_table *cp;
+ u16 offset; /* Running offset for assembly */
+ u16 path_len; /* Length of utf16 path (or 0) */
+ u16 treename_len; /* Length of DFS prefix (or 0) */
+ u16 name_len; /* Total name length */
+ u16 name_offset; /* Offset in message of name */
+ u16 contexts_offset; /* Offset in message contexts */
+ u16 contexts_len; /* Length of contexts (or 0) */
+ u16 posix_offset; /* Offset in message of posix context */
+ u8 name_pad; /* Amount of name padding needed */
+};
+
+static int smb311_posix_mkdir_layout(struct cifs_tcon *tcon,
+ struct smb2_create_layout *lay)
+{
+ size_t offset = lay->offset;
+ size_t tmp;
+ bool has_contexts = false;
+
+ /* [MS-SMB2] 2.2.13 NameOffset: If SMB2_FLAGS_DFS_OPERATIONS
+ * is set in the Flags field of the SMB2 header, the file name
+ * includes a prefix that will be processed during DFS name
+ * normalization as specified in section 3.3.5.9. Otherwise,
+ * the file name is relative to the share that is identified
+ * by the TreeId in the SMB2 header.
+ */
+ lay->name_offset = offset;
+ if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+ const char *treename = tcon->tree_name;
+ int treename_len;
+
+ /* skip leading "\\" */
+ if (!(treename[0] == '\\' && treename[1] == '\\'))
+ return -EINVAL;
+
+ treename_len = cifs_size_strtoUTF16(treename + 2, INT_MAX, lay->cp);
+
+ lay->treename_len = treename_len;
+ lay->name_len = treename_len;
+ if (lay->path_len)
+ lay->name_len += 2 + lay->path_len;
+ } else {
+ lay->name_len = lay->path_len;
+ }
+
+ offset += lay->name_len;
+ tmp = offset;
+ offset = ALIGN8(offset);
+ lay->name_pad = offset - tmp;
+ lay->contexts_offset = offset;
+
+ if (tcon->posix_extensions) {
+ /* resource #3: posix buf */
+ offset += sizeof(struct create_posix);
+ has_contexts = true;
+ }
+
+ if (has_contexts)
+ lay->contexts_len = offset - lay->contexts_offset;
+ else
+ lay->contexts_offset = 0;
+
+ lay->offset = offset;
+ return 0;
+}
+
int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
umode_t mode, struct cifs_tcon *tcon,
const char *full_path,
struct cifs_sb_info *cifs_sb)
{
- struct smb_rqst rqst;
+ struct TCP_Server_Info *server;
struct smb2_create_req *req;
struct smb2_create_rsp *rsp = NULL;
+ struct smb_message *smb = NULL;
struct cifs_ses *ses = tcon->ses;
- struct kvec iov[3]; /* make sure at least one for each open context */
- struct kvec rsp_iov = {NULL, 0};
- int resp_buftype;
- int uni_path_len;
- __le16 *copy_path = NULL;
- int copy_size;
- int rc = 0;
- unsigned int n_iov = 2;
+ __le16 *utf16_path = NULL;
__u32 file_attributes = 0;
- char *pc_buf = NULL;
+ int retries = 0, cur_sleep = 1, path_len;
int flags = 0;
- unsigned int total_len;
- __le16 *utf16_path = NULL;
- struct TCP_Server_Info *server;
- int retries = 0, cur_sleep = 1;
+ int rc = 0;
+
+ /* resource #1: path allocation */
+ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+ path_len = UniStrlen(utf16_path);
replay_again:
/* reinitialize for possible replay */
flags = 0;
- n_iov = 2;
server = cifs_pick_channel(ses);
cifs_dbg(FYI, "mkdir\n");
- /* resource #1: path allocation */
- utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
- if (!utf16_path)
- return -ENOMEM;
-
- if (!ses || !server) {
+ if (!server) {
rc = -EIO;
goto err_free_path;
}
+ struct smb2_create_layout layout = {
+ .offset = sizeof(struct smb2_create_req),
+ .path_len = path_len,
+ .cp = cifs_sb->local_nls,
+ };
+ rc = smb311_posix_mkdir_layout(tcon, &layout);
+ if (rc < 0)
+ goto err_free_path;
+
/* resource #2: request */
- rc = smb2_plain_req_init(SMB2_CREATE, tcon, server,
- (void **) &req, &total_len);
- if (rc)
+ rc = -ENOMEM;
+ smb = smb2_create_request(SMB2_CREATE, server, tcon,
+ sizeof(*req), layout.offset, 0,
+ SMB2_REQ_DYNAMIC);
+ if (!smb)
goto err_free_path;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
- req->ImpersonationLevel = IL_IMPERSONATION;
- req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
+ req->ImpersonationLevel = IL_IMPERSONATION;
+ req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
/* File attributes ignored on open (used in create though) */
- req->FileAttributes = cpu_to_le32(file_attributes);
- req->ShareAccess = FILE_SHARE_ALL_LE;
- req->CreateDisposition = cpu_to_le32(FILE_CREATE);
- req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE);
-
- iov[0].iov_base = (char *)req;
- /* -1 since last byte is buf[0] which is sent below (path) */
- iov[0].iov_len = total_len - 1;
-
- req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
+ req->FileAttributes = cpu_to_le32(file_attributes);
+ req->ShareAccess = FILE_SHARE_ALL_LE;
+ req->CreateDisposition = cpu_to_le32(FILE_CREATE);
+ req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE);
+ req->NameOffset = cpu_to_le16(layout.name_offset);
+ req->NameLength = cpu_to_le16(layout.name_len);
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+ req->CreateContextsOffset = cpu_to_le32(layout.contexts_offset);
+ req->CreateContextsLength = cpu_to_le32(layout.contexts_len);
/* [MS-SMB2] 2.2.13 NameOffset:
* If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
@@ -2981,78 +3046,48 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
* the share that is identified by the TreeId in the SMB2
* header.
*/
+ __le16 *name = smb->request + layout.name_offset;
+
if (tcon->share_flags & SHI1005_FLAGS_DFS) {
- int name_len;
+ int tmp;
req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
- rc = alloc_path_with_tree_prefix(©_path, ©_size,
- &name_len,
- tcon->tree_name, utf16_path);
- if (rc)
- goto err_free_req;
- req->NameLength = cpu_to_le16(name_len * 2);
- uni_path_len = copy_size;
- /* free before overwriting resource */
- kfree(utf16_path);
- utf16_path = copy_path;
- } else {
- uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2;
- /* MUST set path len (NameLength) to 0 opening root of share */
- req->NameLength = cpu_to_le16(uni_path_len - 2);
- if (uni_path_len % 8 != 0) {
- copy_size = roundup(uni_path_len, 8);
- copy_path = kzalloc(copy_size, GFP_KERNEL);
- if (!copy_path) {
- rc = -ENOMEM;
- goto err_free_req;
- }
- memcpy((char *)copy_path, (const char *)utf16_path,
- uni_path_len);
- uni_path_len = copy_size;
- /* free before overwriting resource */
- kfree(utf16_path);
- utf16_path = copy_path;
+ tmp = cifs_strtoUTF16(name, tcon->tree_name + 2, INT_MAX,
+ layout.cp);
+ WARN_ON(tmp != layout.treename_len);
+ name += tmp;
+ if (layout.path_len) {
+ *name++ = cpu_to_le16('\\');
+ memcpy(name, utf16_path, layout.path_len);
}
+ } else {
+ memcpy(name, utf16_path, layout.path_len);
}
- iov[1].iov_len = uni_path_len;
- iov[1].iov_base = utf16_path;
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+ if (layout.name_pad)
+ memset(smb->request + layout.name_offset + layout.name_len,
+ 0, layout.name_pad);
- if (tcon->posix_extensions) {
+ if (tcon->posix_extensions)
/* resource #3: posix buf */
- rc = add_posix_context(iov, &n_iov, mode);
- if (rc)
- goto err_free_req;
- req->CreateContextsOffset = cpu_to_le32(
- sizeof(struct smb2_create_req) +
- iov[1].iov_len);
- le32_add_cpu(&req->CreateContextsLength, iov[n_iov-1].iov_len);
- pc_buf = iov[n_iov-1].iov_base;
- }
-
-
- memset(&rqst, 0, sizeof(struct smb_rqst));
- rqst.rq_iov = iov;
- rqst.rq_nvec = n_iov;
+ fill_posix_buf(smb->request + layout.posix_offset, mode);
/* no need to inc num_remote_opens because we close it just below */
trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES);
if (retries)
- smb2_set_replay(server, &rqst);
+ smb2_set_replay_smb(server, smb);
/* resource #4: response buffer */
- rc = cifs_send_recv(xid, ses, server,
- &rqst, &resp_buftype, flags, &rsp_iov);
+ rc = smb_send_recv_messages(xid, ses, server, smb, flags);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_CREATE);
trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid,
CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES, rc);
- goto err_free_rsp_buf;
+ goto err_free_req;
}
/*
@@ -3060,10 +3095,9 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
* adding check below is slightly safer long term (and quiets Coverity
* warning)
*/
- rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+ rsp = (struct smb2_create_rsp *)smb->response;
if (rsp == NULL) {
rc = -EIO;
- kfree(pc_buf);
goto err_free_req;
}
@@ -3074,18 +3108,14 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
/* Eventually save off posix specific response info and timestamps */
-err_free_rsp_buf:
- free_rsp_buf(resp_buftype, rsp_iov.iov_base);
- kfree(pc_buf);
err_free_req:
- cifs_small_buf_release(req);
+ smb_put_messages(smb);
err_free_path:
- kfree(utf16_path);
-
if (is_replayable_error(rc) &&
smb2_should_replay(tcon, &retries, &cur_sleep))
goto replay_again;
+ kfree(utf16_path);
return rc;
}
Powered by blists - more mailing lists