ksmbd: handle AAPL stream copy length mismatch

macOS can reuse the main file's chunk list when issuing a copychunk
request for alternate data streams. The requested source range can
therefore exceed the length of the xattr-backed stream and currently
fails with STATUS_INVALID_VIEW_SIZE.

For AAPL connections copying between two streams, limit the actual copy
to the available source data while reporting the requested chunk length
as written. Keep the source range validation unchanged for non-AAPL
connections and requests involving a regular file.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-07-07 22:02:18 +09:00
parent 8482150a07
commit f4ce7da9b3

View File

@ -1859,33 +1859,63 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
}
for (i = 0; i < chunk_count; i++) {
bool stream_len_mismatch = false;
size_t copy_len;
src_off = le64_to_cpu(chunks[i].SourceOffset);
dst_off = le64_to_cpu(chunks[i].TargetOffset);
len = le32_to_cpu(chunks[i].Length);
copy_len = len;
if (src_off + len > src_file_size)
if (src_off < 0)
return -E2BIG;
if (src_off > src_file_size || len > src_file_size - src_off) {
/*
* macOS can reuse the main file's chunk list when copying
* streams, so the requested range can exceed the size of
* the xattr-backed stream. For an AAPL connection, copy the
* available stream data and report the requested length to
* avoid a copy length mismatch.
*/
if (!work->conn->is_aapl ||
!ksmbd_stream_fd(src_fp) ||
!ksmbd_stream_fd(dst_fp))
return -E2BIG;
stream_len_mismatch = true;
if (src_off < src_file_size)
copy_len = src_file_size - src_off;
else
copy_len = 0;
}
/*
* vfs_copy_file_range does not support streams or overlapping
* ranges within the same file.
*/
if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp) ||
if (!copy_len) {
ret = 0;
} else if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp) ||
(file_inode(src_fp->filp) == file_inode(dst_fp->filp) &&
dst_off + len > src_off && dst_off < src_off + len))
dst_off + copy_len > src_off &&
dst_off < src_off + copy_len)) {
ret = ksmbd_vfs_copy_file_range_buffered(work, src_fp,
dst_fp, src_off,
dst_off, len);
else {
dst_off, copy_len);
} else {
ret = vfs_copy_file_range(src_fp->filp, src_off,
dst_fp->filp, dst_off, len, 0);
dst_fp->filp, dst_off, copy_len, 0);
if (ret == -EOPNOTSUPP || ret == -EXDEV)
ret = vfs_copy_file_range(src_fp->filp, src_off,
dst_fp->filp, dst_off,
len, COPY_FILE_SPLICE);
copy_len,
COPY_FILE_SPLICE);
}
if (ret < 0)
return ret;
if (stream_len_mismatch)
ret = len;
*chunk_count_written += 1;
*total_size_written += ret;