]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: copy overlapped range within the same file
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 25 Sep 2025 12:15:20 +0000 (21:15 +0900)
committerSteve French <stfrench@microsoft.com>
Wed, 1 Oct 2025 02:37:54 +0000 (21:37 -0500)
cifs.ko request to copy overlapped range within the same file.
ksmbd is using vfs_copy_file_range for this, vfs_copy_file_range() does not
allow overlapped copying within the same file.
This patch use do_splice_direct() if offset and length are overlapped.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/vfs.c

index 1cfa688904b2c4c6fd064c97367f6ce245c7757e..c96e5d934ba986663e1d73caf9728e4de21457fa 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/sched/xacct.h>
 #include <linux/crc32c.h>
 #include <linux/namei.h>
+#include <linux/splice.h>
 
 #include "glob.h"
 #include "oplock.h"
@@ -1829,8 +1830,19 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
                if (src_off + len > src_file_size)
                        return -E2BIG;
 
-               ret = vfs_copy_file_range(src_fp->filp, src_off,
-                                         dst_fp->filp, dst_off, len, 0);
+               /*
+                * vfs_copy_file_range does not allow overlapped copying
+                * within the same file.
+                */
+               if (file_inode(src_fp->filp) == file_inode(dst_fp->filp) &&
+                               dst_off + len > src_off &&
+                               dst_off < src_off + len)
+                       ret = do_splice_direct(src_fp->filp, &src_off,
+                                       dst_fp->filp, &dst_off,
+                                       min_t(size_t, len, MAX_RW_COUNT), 0);
+               else
+                       ret = vfs_copy_file_range(src_fp->filp, src_off,
+                                       dst_fp->filp, dst_off, len, 0);
                if (ret == -EOPNOTSUPP || ret == -EXDEV)
                        ret = vfs_copy_file_range(src_fp->filp, src_off,
                                                  dst_fp->filp, dst_off, len,