diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h index 8b0e79afb..b62ab5eec 100644 --- a/include/os/linux/zfs/sys/zpl.h +++ b/include/os/linux/zfs/sys/zpl.h @@ -193,6 +193,41 @@ extern int zpl_clone_file_range(struct file *src_file, loff_t src_off, extern int zpl_dedupe_file_range(struct file *src_file, loff_t src_off, struct file *dst_file, loff_t dst_off, uint64_t len); +/* compat for FICLONE/FICLONERANGE/FIDEDUPERANGE ioctls */ +typedef struct { + int64_t fcr_src_fd; + uint64_t fcr_src_offset; + uint64_t fcr_src_length; + uint64_t fcr_dest_offset; +} zfs_ioc_compat_file_clone_range_t; + +typedef struct { + int64_t fdri_dest_fd; + uint64_t fdri_dest_offset; + uint64_t fdri_bytes_deduped; + int32_t fdri_status; + uint32_t fdri_reserved; +} zfs_ioc_compat_dedupe_range_info_t; + +typedef struct { + uint64_t fdr_src_offset; + uint64_t fdr_src_length; + uint16_t fdr_dest_count; + uint16_t fdr_reserved1; + uint32_t fdr_reserved2; + zfs_ioc_compat_dedupe_range_info_t fdr_info[]; +} zfs_ioc_compat_dedupe_range_t; + +#define ZFS_IOC_COMPAT_FICLONE _IOW(0x94, 9, int) +#define ZFS_IOC_COMPAT_FICLONERANGE \ + _IOW(0x94, 13, zfs_ioc_compat_file_clone_range_t) +#define ZFS_IOC_COMPAT_FIDEDUPERANGE \ + _IOWR(0x94, 54, zfs_ioc_compat_dedupe_range_t) + +extern long zpl_ioctl_ficlone(struct file *filp, void *arg); +extern long zpl_ioctl_ficlonerange(struct file *filp, void *arg); +extern long zpl_ioctl_fideduperange(struct file *filp, void *arg); + #if defined(HAVE_INODE_TIMESTAMP_TRUNCATE) #define zpl_inode_timestamp_truncate(ts, ip) timestamp_truncate(ts, ip) 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); +}