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>
This commit is contained in:
Gael Blivet 2026-07-07 11:55:03 +02:00 committed by Namjae Jeon
parent 495ade881b
commit a9417bb188

View File

@ -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 @@ 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;
@ -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);