* FAILED: patch "[PATCH] ksmbd: fix named stream write and EOF handling" failed to apply to 6.6-stable tree
@ 2026-11-03 10:21 gregkh
2026-11-03 22:39 ` [PATCH 6.6.y] ksmbd: fix named stream write and EOF handling Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: gregkh @ 2026-11-03 10:21 UTC (permalink / raw)
To: charsyam, linkinjeon; +Cc: stable
The patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 02165c8d402392278a5cd2cf97645ed73a7ad4a1
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026110333-coke-seismic-b0a6@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 02165c8d402392278a5cd2cf97645ed73a7ad4a1 Mon Sep 17 00:00:00 2001
From: DaeMyung Kang <charsyam@gmail.com>
Date: Wed, 7 Oct 2026 22:13:51 +0900
Subject: [PATCH] ksmbd: fix named stream write and EOF handling
An SMB WRITE to an existing named stream can silently discard data after
its written range. ksmbd stores streams as xattr values, but the write
path passes offset + count as the length of the replacement xattr even
when the old stream is longer. Writing 26 bytes at offset 0 to a 60-byte
stream reports 26 bytes written while discarding the remaining 34 bytes.
Keep the existing xattr length when an in-place write ends before EOF.
Reject writes that cross XATTR_SIZE_MAX rather than storing only the
portion that fits while reporting the full requested count.
Commit 4ea0bb8aaedf ("ksmbd: handle set/get info file for streamed file")
intentionally skipped ordinary inode truncation for stream handles: an
EOF SetInfo on an ADS had truncated the base file. That fixed the base
file corruption but left stream EOF changes reporting success without
resizing the stream. Handle these requests by resizing only the stream
xattr; the base file size remains untouched. Zero-fill stream extensions.
This is needed alongside the write fix: a short write's accidental
truncation previously masked an ignored explicit EOF change. The stream
resize helper returns xattr errors directly rather than using the
base-file truncate path's EBADF conversion.
Fixes: f44158485826 ("cifsd: add file operations")
Fixes: 4ea0bb8aaedf ("ksmbd: handle set/get info file for streamed file")
Cc: stable@vger.kernel.org
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index a400f9912896..0fb146b612f0 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -8578,6 +8578,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
newsize = le64_to_cpu(file_eof_info->EndOfFile);
inode = file_inode(fp->filp);
+ if (ksmbd_stream_fd(fp))
+ return ksmbd_vfs_stream_truncate(fp, newsize);
/*
* If FILE_END_OF_FILE_INFORMATION of set_info_file is called
@@ -8586,8 +8588,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
* truncate of some filesystem like FAT32 fill zero data in
* truncated range.
*/
- if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC &&
- ksmbd_stream_fd(fp) == false) {
+ if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize);
rc = ksmbd_vfs_truncate(work, fp, newsize);
if (rc) {
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 2e2c554bc2c1..8bb93196acb9 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -388,23 +388,21 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
const struct cred *saved_cred;
char *stream_buf = NULL, *wbuf;
const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
- size_t size;
+ size_t size, write_end;
ssize_t v_len;
int err = 0;
ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
*pos, count);
- if (*pos >= XATTR_SIZE_MAX) {
+ if (*pos < 0 || *pos >= XATTR_SIZE_MAX) {
pr_err("stream write position %lld is out of bounds\n", *pos);
return -EINVAL;
}
- size = *pos + count;
- if (size > XATTR_SIZE_MAX) {
- size = XATTR_SIZE_MAX;
- count = XATTR_SIZE_MAX - *pos;
- }
+ if (count > XATTR_SIZE_MAX - *pos)
+ return -EFBIG;
+ write_end = *pos + count;
saved_cred = override_creds(fp->filp->f_cred);
v_len = ksmbd_vfs_getcasexattr(idmap,
@@ -417,6 +415,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
err = v_len;
goto out_revert;
}
+ /* Preserve the tail of an existing stream on an in-place write. */
+ size = max_t(size_t, v_len, write_end);
if (v_len < size) {
wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP);
@@ -445,13 +445,69 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
if (err < 0)
goto out;
else
- fp->stream.pos = size;
+ fp->stream.pos = write_end;
err = 0;
out:
kvfree(stream_buf);
return err;
}
+/**
+ * ksmbd_vfs_stream_truncate() - change the length of a named stream
+ * @fp: stream file handle
+ * @newsize: new stream length
+ *
+ * Resize the stream xattr without changing the base inode size.
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize)
+{
+ const struct cred *saved_cred;
+ const struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
+ char *stream_buf = NULL, *new_buf = NULL;
+ ssize_t v_len;
+ int err;
+
+ if (newsize < 0)
+ return -EINVAL;
+ if (newsize > XATTR_SIZE_MAX)
+ return -EFBIG;
+
+ saved_cred = override_creds(fp->filp->f_cred);
+ v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry,
+ fp->stream.name, fp->stream.size,
+ &stream_buf);
+ if (v_len < 0) {
+ err = v_len;
+ goto out;
+ }
+ if (v_len == newsize) {
+ err = 0;
+ goto out;
+ }
+
+ new_buf = stream_buf;
+ if (newsize > v_len) {
+ new_buf = kvzalloc(newsize, KSMBD_DEFAULT_GFP);
+ if (!new_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (v_len)
+ memcpy(new_buf, stream_buf, v_len);
+ }
+
+ err = ksmbd_vfs_setxattr(idmap, &fp->filp->f_path, fp->stream.name,
+ new_buf, newsize, 0, true);
+ if (new_buf != stream_buf)
+ kvfree(new_buf);
+out:
+ kvfree(stream_buf);
+ revert_creds(saved_cred);
+ return err;
+}
+
/**
* ksmbd_vfs_write() - vfs helper for smb file write
* @work: work
diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
index 216c76291fc1..7e545d13a3ff 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -83,6 +83,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
char *buf, size_t count, loff_t *pos, bool sync,
ssize_t *written);
+int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize);
int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path);
int ksmbd_vfs_link(struct ksmbd_work *work,
^ permalink raw reply related [flat|nested] 2+ messages in thread* [PATCH 6.6.y] ksmbd: fix named stream write and EOF handling
2026-11-03 10:21 FAILED: patch "[PATCH] ksmbd: fix named stream write and EOF handling" failed to apply to 6.6-stable tree gregkh
@ 2026-11-03 22:39 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-11-03 22:39 UTC (permalink / raw)
To: stable; +Cc: DaeMyung Kang, Namjae Jeon, Sasha Levin
From: DaeMyung Kang <charsyam@gmail.com>
[ Upstream commit 02165c8d402392278a5cd2cf97645ed73a7ad4a1 ]
An SMB WRITE to an existing named stream can silently discard data after
its written range. ksmbd stores streams as xattr values, but the write
path passes offset + count as the length of the replacement xattr even
when the old stream is longer. Writing 26 bytes at offset 0 to a 60-byte
stream reports 26 bytes written while discarding the remaining 34 bytes.
Keep the existing xattr length when an in-place write ends before EOF.
Reject writes that cross XATTR_SIZE_MAX rather than storing only the
portion that fits while reporting the full requested count.
Commit 4ea0bb8aaedf ("ksmbd: handle set/get info file for streamed file")
intentionally skipped ordinary inode truncation for stream handles: an
EOF SetInfo on an ADS had truncated the base file. That fixed the base
file corruption but left stream EOF changes reporting success without
resizing the stream. Handle these requests by resizing only the stream
xattr; the base file size remains untouched. Zero-fill stream extensions.
This is needed alongside the write fix: a short write's accidental
truncation previously masked an ignored explicit EOF change. The stream
resize helper returns xattr errors directly rather than using the
base-file truncate path's EBADF conversion.
Fixes: f44158485826 ("cifsd: add file operations")
Fixes: 4ea0bb8aaedf ("ksmbd: handle set/get info file for streamed file")
Cc: stable@vger.kernel.org
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
[ sashal: Used fp->filp->f_pos instead of the unavailable fp->stream.pos to store the stream write position. ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/smb/server/smb2pdu.c | 2 ++
fs/smb/server/vfs.c | 72 ++++++++++++++++++++++++++++++++++++-----
fs/smb/server/vfs.h | 1 +
3 files changed, 67 insertions(+), 8 deletions(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index aaccef2e3f3bb..7906e7e747d83 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -6497,6 +6497,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
newsize = le64_to_cpu(file_eof_info->EndOfFile);
inode = file_inode(fp->filp);
+ if (ksmbd_stream_fd(fp))
+ return ksmbd_vfs_stream_truncate(fp, newsize);
/*
* If FILE_END_OF_FILE_INFORMATION of set_info_file is called
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index aed2d2dd9b25a..4daae499769ae 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -417,23 +417,21 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
const struct cred *saved_cred;
char *stream_buf = NULL, *wbuf;
struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
- size_t size;
+ size_t size, write_end;
ssize_t v_len;
int err = 0;
ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
*pos, count);
- if (*pos >= XATTR_SIZE_MAX) {
+ if (*pos < 0 || *pos >= XATTR_SIZE_MAX) {
pr_err("stream write position %lld is out of bounds\n", *pos);
return -EINVAL;
}
- size = *pos + count;
- if (size > XATTR_SIZE_MAX) {
- size = XATTR_SIZE_MAX;
- count = XATTR_SIZE_MAX - *pos;
- }
+ if (count > XATTR_SIZE_MAX - *pos)
+ return -EFBIG;
+ write_end = *pos + count;
saved_cred = override_creds(fp->filp->f_cred);
v_len = ksmbd_vfs_getcasexattr(idmap,
@@ -446,6 +444,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
err = v_len;
goto out_revert;
}
+ /* Preserve the tail of an existing stream on an in-place write. */
+ size = max_t(size_t, v_len, write_end);
if (v_len < size) {
wbuf = kvzalloc(size, GFP_KERNEL);
@@ -474,13 +474,69 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
if (err < 0)
goto out;
- fp->filp->f_pos = *pos;
+ fp->filp->f_pos = write_end;
err = 0;
out:
kvfree(stream_buf);
return err;
}
+/**
+ * ksmbd_vfs_stream_truncate() - change the length of a named stream
+ * @fp: stream file handle
+ * @newsize: new stream length
+ *
+ * Resize the stream xattr without changing the base inode size.
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize)
+{
+ const struct cred *saved_cred;
+ struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
+ char *stream_buf = NULL, *new_buf = NULL;
+ ssize_t v_len;
+ int err;
+
+ if (newsize < 0)
+ return -EINVAL;
+ if (newsize > XATTR_SIZE_MAX)
+ return -EFBIG;
+
+ saved_cred = override_creds(fp->filp->f_cred);
+ v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry,
+ fp->stream.name, fp->stream.size,
+ &stream_buf);
+ if (v_len < 0) {
+ err = v_len;
+ goto out;
+ }
+ if (v_len == newsize) {
+ err = 0;
+ goto out;
+ }
+
+ new_buf = stream_buf;
+ if (newsize > v_len) {
+ new_buf = kvzalloc(newsize, GFP_KERNEL);
+ if (!new_buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (v_len)
+ memcpy(new_buf, stream_buf, v_len);
+ }
+
+ err = ksmbd_vfs_setxattr(idmap, &fp->filp->f_path, fp->stream.name,
+ new_buf, newsize, 0, true);
+ if (new_buf != stream_buf)
+ kvfree(new_buf);
+out:
+ kvfree(stream_buf);
+ revert_creds(saved_cred);
+ return err;
+}
+
/**
* ksmbd_vfs_write() - vfs helper for smb file write
* @work: work
diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
index 06903024a2d88..526c0541ec691 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -82,6 +82,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
char *buf, size_t count, loff_t *pos, bool sync,
ssize_t *written);
+int ksmbd_vfs_stream_truncate(struct ksmbd_file *fp, loff_t newsize);
int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path);
int ksmbd_vfs_link(struct ksmbd_work *work,
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-03 22:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03 10:21 FAILED: patch "[PATCH] ksmbd: fix named stream write and EOF handling" failed to apply to 6.6-stable tree gregkh
2026-11-03 22:39 ` [PATCH 6.6.y] ksmbd: fix named stream write and EOF handling Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox