lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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(&copy_path, &copy_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

Powered by Openwall GNU/*/Linux Powered by OpenVZ