[PATCH 7/7] ksmbd: fix COPYCHUNK for ADS streams
"Gaël Blivet-Bailly" <[email protected]>
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
From: Gael Blivet <[email protected]> ksmbd_vfs_copy_file_ranges() unconditionally returned -EBADF whenever either the source or destination of a server-side COPYCHUNK was a stream handle. This breaks copying files that carry alternate data streams (e.g. duplicating a file with a resource fork, or an AFP_AfpInfo stream) via COPYCHUNK, since vfs_copy_file_range() doesn't work on xattr-backed streams to begin with -- the copy needs to read the source xattr's value and write it to the destination directly. Implement that path instead of failing outright, and echo back TotalBytesWritten as the sum of the chunk lengths the client sent (macOS reuses the main file's chunk list for stream copies, so the requested total reflects the file size rather than the stream size, and returning the stream's actual size would look like a mismatch to the client). Signed-off-by: Gael Blivet <[email protected]> --- fs/smb/server/vfs.c | 46 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index c491574d5..be93a8ec9 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1821,8 +1821,50 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, return -EACCES; } - if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) - return -EBADF; + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) { + /* + * ADS stream copychunk: copy xattr from src to dst directly. + * vfs_copy_file_range doesn't work on xattr-backed streams. + */ + char *buf = NULL; + ssize_t buf_len; + int sret; + + if (!ksmbd_stream_fd(src_fp) || !ksmbd_stream_fd(dst_fp)) + return -EINVAL; + + buf_len = ksmbd_vfs_getxattr(file_mnt_idmap(src_fp->filp), + file_dentry(src_fp->filp), + src_fp->stream.name, &buf); + if (buf_len < 0) + return buf_len; + + if (buf_len > 0) { + /* + * vfs_setxattr(NULL, 0) removes the xattr on Linux, so + * skip it for empty streams: the Open already created + * the empty xattr via smb2_set_stream_name_xattr. + */ + sret = ksmbd_vfs_setxattr(file_mnt_idmap(dst_fp->filp), + &dst_fp->filp->f_path, + dst_fp->stream.name, + buf, buf_len, 0, false); + kfree(buf); + if (sret < 0) + return sret; + } + + /* + * macOS reuses the main file's chunk list for stream copies, + * so req_total equals the file size, not the stream size. + * Echo back TotalBytesWritten = sum(chunks) so macOS doesn't + * treat the size mismatch as an error. + */ + *chunk_count_written = chunk_count; + for (i = 0; i < chunk_count; i++) + *total_size_written += le32_to_cpu(chunks[i].Length); + return 0; + } smb_break_all_levII_oplock(work, dst_fp, 1); -- 2.43.0