aboutsummaryrefslogtreecommitdiffstats
path: root/module/os/linux/zfs
diff options
context:
space:
mode:
authorRob Norris <[email protected]>2023-06-27 23:45:00 +1000
committerBrian Behlendorf <[email protected]>2023-07-24 16:36:54 -0700
commit9927f219f1e9f4ee886d426190500abf5b1d602e (patch)
tree628b08c6f65d036616382d4f53a472b87d8f35cb /module/os/linux/zfs
parent5a35c68b67473a7ae0a75c4beb51c928d3e2628d (diff)
linux: implement filesystem-side clone ioctls
Prior to Linux 4.5, the FICLONE etc ioctls were specific to BTRFS, and were implemented as regular filesystem-specific ioctls. This implements those ioctls directly in OpenZFS, allowing cloning to work on older kernels. There's no need to gate these behind version checks; on later kernels Linux will simply never deliver these ioctls, instead calling the approprate VFS op. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Kay Pedersen <[email protected]> Signed-off-by: Rob Norris <[email protected]> Sponsored-By: OpenDrives Inc. Sponsored-By: Klara Inc. Closes #15050
Diffstat (limited to 'module/os/linux/zfs')
-rw-r--r--module/os/linux/zfs/zpl_file.c6
-rw-r--r--module/os/linux/zfs/zpl_file_range.c79
2 files changed, 85 insertions, 0 deletions
diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
index 92b603e98..87a248af8 100644
--- a/module/os/linux/zfs/zpl_file.c
+++ b/module/os/linux/zfs/zpl_file.c
@@ -1257,6 +1257,12 @@ zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return (zpl_ioctl_getdosflags(filp, (void *)arg));
case ZFS_IOC_SETDOSFLAGS:
return (zpl_ioctl_setdosflags(filp, (void *)arg));
+ case ZFS_IOC_COMPAT_FICLONE:
+ return (zpl_ioctl_ficlone(filp, (void *)arg));
+ case ZFS_IOC_COMPAT_FICLONERANGE:
+ return (zpl_ioctl_ficlonerange(filp, (void *)arg));
+ case ZFS_IOC_COMPAT_FIDEDUPERANGE:
+ return (zpl_ioctl_fideduperange(filp, (void *)arg));
default:
return (-ENOTTY);
}
diff --git a/module/os/linux/zfs/zpl_file_range.c b/module/os/linux/zfs/zpl_file_range.c
index db387a748..aad502a80 100644
--- a/module/os/linux/zfs/zpl_file_range.c
+++ b/module/os/linux/zfs/zpl_file_range.c
@@ -181,3 +181,82 @@ zpl_dedupe_file_range(struct file *src_file, loff_t src_off,
return (-EOPNOTSUPP);
}
#endif /* HAVE_VFS_DEDUPE_FILE_RANGE */
+
+/* Entry point for FICLONE, before Linux 4.5. */
+long
+zpl_ioctl_ficlone(struct file *dst_file, void *arg)
+{
+ unsigned long sfd = (unsigned long)arg;
+
+ struct file *src_file = fget(sfd);
+ if (src_file == NULL)
+ return (-EBADF);
+
+ if (dst_file->f_op != src_file->f_op)
+ return (-EXDEV);
+
+ size_t len = i_size_read(file_inode(src_file));
+
+ ssize_t ret =
+ __zpl_clone_file_range(src_file, 0, dst_file, 0, len);
+
+ fput(src_file);
+
+ if (ret < 0) {
+ if (ret == -EOPNOTSUPP)
+ return (-ENOTTY);
+ return (ret);
+ }
+
+ if (ret != len)
+ return (-EINVAL);
+
+ return (0);
+}
+
+/* Entry point for FICLONERANGE, before Linux 4.5. */
+long
+zpl_ioctl_ficlonerange(struct file *dst_file, void __user *arg)
+{
+ zfs_ioc_compat_file_clone_range_t fcr;
+
+ if (copy_from_user(&fcr, arg, sizeof (fcr)))
+ return (-EFAULT);
+
+ struct file *src_file = fget(fcr.fcr_src_fd);
+ if (src_file == NULL)
+ return (-EBADF);
+
+ if (dst_file->f_op != src_file->f_op)
+ return (-EXDEV);
+
+ size_t len = fcr.fcr_src_length;
+ if (len == 0)
+ len = i_size_read(file_inode(src_file)) - fcr.fcr_src_offset;
+
+ ssize_t ret = __zpl_clone_file_range(src_file, fcr.fcr_src_offset,
+ dst_file, fcr.fcr_dest_offset, len);
+
+ fput(src_file);
+
+ if (ret < 0) {
+ if (ret == -EOPNOTSUPP)
+ return (-ENOTTY);
+ return (ret);
+ }
+
+ if (ret != len)
+ return (-EINVAL);
+
+ return (0);
+}
+
+/* Entry point for FIDEDUPERANGE, before Linux 4.5. */
+long
+zpl_ioctl_fideduperange(struct file *filp, void *arg)
+{
+ (void) arg;
+
+ /* No support for dedup yet */
+ return (-ENOTTY);
+}