summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGael Blivet <gael.blivet@gmail.com>2026-07-07 11:55:03 +0200
committerNamjae Jeon <linkinjeon@kernel.org>2026-08-17 15:00:34 +0900
commita9417bb1889e3c869f4c059a67efa8899d8df3f5 (patch)
tree08e3351d260365ee527d73fb8060c71057f1766d
parent495ade881b5c52e2a2b646d04b04a4429e5734e9 (diff)
ksmbd: report actual xattr value length for stream EndOfFile/AllocationSize
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(). Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Gael Blivet <gael.blivet@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
-rw-r--r--fs/smb/server/smb2pdu.c55
1 files changed, 43 insertions, 12 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 0d63752bb158..f737ba5cd82c 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2822,6 +2822,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);
@@ -4106,10 +4122,17 @@ reconnected_fp:
* 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;
@@ -5450,8 +5473,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;
@@ -5529,8 +5554,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);
@@ -5807,8 +5834,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 =
@@ -5939,8 +5968,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);