[PATCH 3/7] ksmbd: report actual xattr value length for stream EndOfFile/AllocationSize

"Gaël Blivet-Bailly" <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <[email protected]>
From: Gael Blivet <[email protected]>

fp->stream.size holds the byte length of the mangled xattr *name*
string (it's used as the attr_name_len argument when looking up the
xattr), not the size of the stream's actual data. CREATE and every
QUERY_INFO handler that reports EndOfFile/AllocationSize for a stream
handle used fp->stream.size directly, so clients received a bogus
size derived from the internal xattr key name length instead of the
stream's real content length.

Add ksmbd_stream_eof() to query the xattr's actual value length via
ksmbd_vfs_casexattr_len(), and use it at every site that reports a
stream handle's size: the CREATE response, get_file_standard_info(),
get_file_all_info(), get_file_network_open_info(), and
find_file_posix_info().

Signed-off-by: Gael Blivet <[email protected]>
---
 fs/smb/server/smb2pdu.c | 55 ++++++++++++++++++++++++++++++++---------
 1 file changed, 43 insertions(+), 12 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index e6d0f58d2..735c3eb72 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2828,6 +2828,22 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
 	return 0;
 }
 
+/*
+ * fp->stream.size is the byte length of the mangled xattr *name*
+ * (used as attr_name_len when looking the xattr up), not the size of
+ * the xattr's value. Reporting it as EndOfFile/AllocationSize for a
+ * stream handle is wrong -- query the xattr's actual value length
+ * instead.
+ */
+static loff_t ksmbd_stream_eof(struct ksmbd_file *fp)
+{
+	ssize_t slen = ksmbd_vfs_casexattr_len(file_mnt_idmap(fp->filp),
+					       fp->filp->f_path.dentry,
+					       fp->stream.name,
+					       fp->stream.size);
+	return slen < 0 ? 0 : (loff_t)slen;
+}
+
 static int smb2_remove_smb_xattrs(const struct path *path)
 {
 	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
@@ -4112,10 +4128,17 @@ int smb2_open(struct ksmbd_work *work)
 	 * using the raw on-disk block count, which can include filesystem
 	 * preallocation and metadata rounding.
 	 */
-	if (!S_ISDIR(stat.mode) && stat.size > fp->allocation_size)
-		fp->allocation_size = round_up(stat.size, stat.blksize);
-	rsp->AllocationSize = cpu_to_le64(fp->allocation_size);
-	rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+	if (ksmbd_stream_fd(fp)) {
+		loff_t seof = ksmbd_stream_eof(fp);
+
+		rsp->AllocationSize = cpu_to_le64((u64)seof);
+		rsp->EndofFile = cpu_to_le64((u64)seof);
+	} else {
+		if (!S_ISDIR(stat.mode) && stat.size > fp->allocation_size)
+			fp->allocation_size = round_up(stat.size, stat.blksize);
+		rsp->AllocationSize = cpu_to_le64(fp->allocation_size);
+		rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+	}
 	rsp->FileAttributes = fp->f_ci->m_fattr;
 
 	rsp->Reserved2 = 0;
@@ -5456,8 +5479,10 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp,
 		sinfo->AllocationSize = cpu_to_le64(fp->allocation_size);
 		sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
 	} else {
-		sinfo->AllocationSize = cpu_to_le64(fp->stream.size);
-		sinfo->EndOfFile = cpu_to_le64(fp->stream.size);
+		loff_t seof = ksmbd_stream_eof(fp);
+
+		sinfo->AllocationSize = cpu_to_le64((u64)seof);
+		sinfo->EndOfFile = cpu_to_le64((u64)seof);
 	}
 	sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending);
 	sinfo->DeletePending = delete_pending;
@@ -5535,8 +5560,10 @@ static int get_file_all_info(struct ksmbd_work *work,
 		file_info->AllocationSize = cpu_to_le64(fp->allocation_size);
 		file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
 	} else {
-		file_info->AllocationSize = cpu_to_le64(fp->stream.size);
-		file_info->EndOfFile = cpu_to_le64(fp->stream.size);
+		loff_t seof = ksmbd_stream_eof(fp);
+
+		file_info->AllocationSize = cpu_to_le64((u64)seof);
+		file_info->EndOfFile = cpu_to_le64((u64)seof);
 	}
 	file_info->NumberOfLinks =
 			cpu_to_le32(get_nlink(&stat) - delete_pending);
@@ -5813,8 +5840,10 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
 		file_info->AllocationSize = cpu_to_le64(fp->allocation_size);
 		file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
 	} else {
-		file_info->AllocationSize = cpu_to_le64(fp->stream.size);
-		file_info->EndOfFile = cpu_to_le64(fp->stream.size);
+		loff_t seof = ksmbd_stream_eof(fp);
+
+		file_info->AllocationSize = cpu_to_le64((u64)seof);
+		file_info->EndOfFile = cpu_to_le64((u64)seof);
 	}
 	file_info->Reserved = cpu_to_le32(0);
 	rsp->OutputBufferLength =
@@ -5945,8 +5974,10 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
 		file_info->EndOfFile = cpu_to_le64(stat.size);
 		file_info->AllocationSize = cpu_to_le64(fp->allocation_size);
 	} else {
-		file_info->EndOfFile = cpu_to_le64(fp->stream.size);
-		file_info->AllocationSize = cpu_to_le64(fp->stream.size);
+		loff_t seof = ksmbd_stream_eof(fp);
+
+		file_info->EndOfFile = cpu_to_le64((u64)seof);
+		file_info->AllocationSize = cpu_to_le64((u64)seof);
 	}
 	file_info->HardLinks = cpu_to_le32(stat.nlink);
 	file_info->Mode = cpu_to_le32(stat.mode & 0777);
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.