diff options
-rw-r--r-- | fs/compat_ioctl.c | 1 | ||||
-rw-r--r-- | fs/ioctl.c | 38 | ||||
-rw-r--r-- | fs/read_write.c | 100 | ||||
-rw-r--r-- | include/linux/fs.h | 4 | ||||
-rw-r--r-- | include/uapi/linux/fs.h | 30 |
5 files changed, 173 insertions, 0 deletions
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 70d4b104c08d..eab31e74b9cc 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c | |||
@@ -1582,6 +1582,7 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, | |||
1582 | 1582 | ||
1583 | case FICLONE: | 1583 | case FICLONE: |
1584 | case FICLONERANGE: | 1584 | case FICLONERANGE: |
1585 | case FIDEDUPERANGE: | ||
1585 | goto do_ioctl; | 1586 | goto do_ioctl; |
1586 | 1587 | ||
1587 | case FIBMAP: | 1588 | case FIBMAP: |
diff --git a/fs/ioctl.c b/fs/ioctl.c index 84c6e79829ab..fcdd33b7ec78 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c | |||
@@ -568,6 +568,41 @@ static int ioctl_fsthaw(struct file *filp) | |||
568 | return thaw_super(sb); | 568 | return thaw_super(sb); |
569 | } | 569 | } |
570 | 570 | ||
571 | static long ioctl_file_dedupe_range(struct file *file, void __user *arg) | ||
572 | { | ||
573 | struct file_dedupe_range __user *argp = arg; | ||
574 | struct file_dedupe_range *same = NULL; | ||
575 | int ret; | ||
576 | unsigned long size; | ||
577 | u16 count; | ||
578 | |||
579 | if (get_user(count, &argp->dest_count)) { | ||
580 | ret = -EFAULT; | ||
581 | goto out; | ||
582 | } | ||
583 | |||
584 | size = offsetof(struct file_dedupe_range __user, info[count]); | ||
585 | |||
586 | same = memdup_user(argp, size); | ||
587 | if (IS_ERR(same)) { | ||
588 | ret = PTR_ERR(same); | ||
589 | same = NULL; | ||
590 | goto out; | ||
591 | } | ||
592 | |||
593 | ret = vfs_dedupe_file_range(file, same); | ||
594 | if (ret) | ||
595 | goto out; | ||
596 | |||
597 | ret = copy_to_user(argp, same, size); | ||
598 | if (ret) | ||
599 | ret = -EFAULT; | ||
600 | |||
601 | out: | ||
602 | kfree(same); | ||
603 | return ret; | ||
604 | } | ||
605 | |||
571 | /* | 606 | /* |
572 | * When you add any new common ioctls to the switches above and below | 607 | * When you add any new common ioctls to the switches above and below |
573 | * please update compat_sys_ioctl() too. | 608 | * please update compat_sys_ioctl() too. |
@@ -629,6 +664,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, | |||
629 | case FICLONERANGE: | 664 | case FICLONERANGE: |
630 | return ioctl_file_clone_range(filp, argp); | 665 | return ioctl_file_clone_range(filp, argp); |
631 | 666 | ||
667 | case FIDEDUPERANGE: | ||
668 | return ioctl_file_dedupe_range(filp, argp); | ||
669 | |||
632 | default: | 670 | default: |
633 | if (S_ISREG(inode->i_mode)) | 671 | if (S_ISREG(inode->i_mode)) |
634 | error = file_ioctl(filp, cmd, arg); | 672 | error = file_ioctl(filp, cmd, arg); |
diff --git a/fs/read_write.c b/fs/read_write.c index 60ee26941231..2116e74a83d3 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -1523,3 +1523,103 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in, | |||
1523 | return ret; | 1523 | return ret; |
1524 | } | 1524 | } |
1525 | EXPORT_SYMBOL(vfs_clone_file_range); | 1525 | EXPORT_SYMBOL(vfs_clone_file_range); |
1526 | |||
1527 | int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) | ||
1528 | { | ||
1529 | struct file_dedupe_range_info *info; | ||
1530 | struct inode *src = file_inode(file); | ||
1531 | u64 off; | ||
1532 | u64 len; | ||
1533 | int i; | ||
1534 | int ret; | ||
1535 | bool is_admin = capable(CAP_SYS_ADMIN); | ||
1536 | u16 count = same->dest_count; | ||
1537 | struct file *dst_file; | ||
1538 | loff_t dst_off; | ||
1539 | ssize_t deduped; | ||
1540 | |||
1541 | if (!(file->f_mode & FMODE_READ)) | ||
1542 | return -EINVAL; | ||
1543 | |||
1544 | if (same->reserved1 || same->reserved2) | ||
1545 | return -EINVAL; | ||
1546 | |||
1547 | off = same->src_offset; | ||
1548 | len = same->src_length; | ||
1549 | |||
1550 | ret = -EISDIR; | ||
1551 | if (S_ISDIR(src->i_mode)) | ||
1552 | goto out; | ||
1553 | |||
1554 | ret = -EINVAL; | ||
1555 | if (!S_ISREG(src->i_mode)) | ||
1556 | goto out; | ||
1557 | |||
1558 | ret = clone_verify_area(file, off, len, false); | ||
1559 | if (ret < 0) | ||
1560 | goto out; | ||
1561 | ret = 0; | ||
1562 | |||
1563 | /* pre-format output fields to sane values */ | ||
1564 | for (i = 0; i < count; i++) { | ||
1565 | same->info[i].bytes_deduped = 0ULL; | ||
1566 | same->info[i].status = FILE_DEDUPE_RANGE_SAME; | ||
1567 | } | ||
1568 | |||
1569 | for (i = 0, info = same->info; i < count; i++, info++) { | ||
1570 | struct inode *dst; | ||
1571 | struct fd dst_fd = fdget(info->dest_fd); | ||
1572 | |||
1573 | dst_file = dst_fd.file; | ||
1574 | if (!dst_file) { | ||
1575 | info->status = -EBADF; | ||
1576 | goto next_loop; | ||
1577 | } | ||
1578 | dst = file_inode(dst_file); | ||
1579 | |||
1580 | ret = mnt_want_write_file(dst_file); | ||
1581 | if (ret) { | ||
1582 | info->status = ret; | ||
1583 | goto next_loop; | ||
1584 | } | ||
1585 | |||
1586 | dst_off = info->dest_offset; | ||
1587 | ret = clone_verify_area(dst_file, dst_off, len, true); | ||
1588 | if (ret < 0) { | ||
1589 | info->status = ret; | ||
1590 | goto next_file; | ||
1591 | } | ||
1592 | ret = 0; | ||
1593 | |||
1594 | if (info->reserved) { | ||
1595 | info->status = -EINVAL; | ||
1596 | } else if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) { | ||
1597 | info->status = -EINVAL; | ||
1598 | } else if (file->f_path.mnt != dst_file->f_path.mnt) { | ||
1599 | info->status = -EXDEV; | ||
1600 | } else if (S_ISDIR(dst->i_mode)) { | ||
1601 | info->status = -EISDIR; | ||
1602 | } else if (dst_file->f_op->dedupe_file_range == NULL) { | ||
1603 | info->status = -EINVAL; | ||
1604 | } else { | ||
1605 | deduped = dst_file->f_op->dedupe_file_range(file, off, | ||
1606 | len, dst_file, | ||
1607 | info->dest_offset); | ||
1608 | if (deduped == -EBADE) | ||
1609 | info->status = FILE_DEDUPE_RANGE_DIFFERS; | ||
1610 | else if (deduped < 0) | ||
1611 | info->status = deduped; | ||
1612 | else | ||
1613 | info->bytes_deduped += deduped; | ||
1614 | } | ||
1615 | |||
1616 | next_file: | ||
1617 | mnt_drop_write_file(dst_file); | ||
1618 | next_loop: | ||
1619 | fdput(dst_fd); | ||
1620 | } | ||
1621 | |||
1622 | out: | ||
1623 | return ret; | ||
1624 | } | ||
1625 | EXPORT_SYMBOL(vfs_dedupe_file_range); | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 5d987aefcf1e..d71814b81a3c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1633,6 +1633,8 @@ struct file_operations { | |||
1633 | loff_t, size_t, unsigned int); | 1633 | loff_t, size_t, unsigned int); |
1634 | int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, | 1634 | int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, |
1635 | u64); | 1635 | u64); |
1636 | ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, | ||
1637 | u64); | ||
1636 | }; | 1638 | }; |
1637 | 1639 | ||
1638 | struct inode_operations { | 1640 | struct inode_operations { |
@@ -1688,6 +1690,8 @@ extern ssize_t vfs_copy_file_range(struct file *, loff_t , struct file *, | |||
1688 | loff_t, size_t, unsigned int); | 1690 | loff_t, size_t, unsigned int); |
1689 | extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in, | 1691 | extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in, |
1690 | struct file *file_out, loff_t pos_out, u64 len); | 1692 | struct file *file_out, loff_t pos_out, u64 len); |
1693 | extern int vfs_dedupe_file_range(struct file *file, | ||
1694 | struct file_dedupe_range *same); | ||
1691 | 1695 | ||
1692 | struct super_operations { | 1696 | struct super_operations { |
1693 | struct inode *(*alloc_inode)(struct super_block *sb); | 1697 | struct inode *(*alloc_inode)(struct super_block *sb); |
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index cd5db7fb3cb7..b38e647664a0 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h | |||
@@ -52,6 +52,35 @@ struct fstrim_range { | |||
52 | __u64 minlen; | 52 | __u64 minlen; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | /* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */ | ||
56 | #define FILE_DEDUPE_RANGE_SAME 0 | ||
57 | #define FILE_DEDUPE_RANGE_DIFFERS 1 | ||
58 | |||
59 | /* from struct btrfs_ioctl_file_extent_same_info */ | ||
60 | struct file_dedupe_range_info { | ||
61 | __s64 dest_fd; /* in - destination file */ | ||
62 | __u64 dest_offset; /* in - start of extent in destination */ | ||
63 | __u64 bytes_deduped; /* out - total # of bytes we were able | ||
64 | * to dedupe from this file. */ | ||
65 | /* status of this dedupe operation: | ||
66 | * < 0 for error | ||
67 | * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds | ||
68 | * == FILE_DEDUPE_RANGE_DIFFERS if data differs | ||
69 | */ | ||
70 | __s32 status; /* out - see above description */ | ||
71 | __u32 reserved; /* must be zero */ | ||
72 | }; | ||
73 | |||
74 | /* from struct btrfs_ioctl_file_extent_same_args */ | ||
75 | struct file_dedupe_range { | ||
76 | __u64 src_offset; /* in - start of extent in source */ | ||
77 | __u64 src_length; /* in - length of extent */ | ||
78 | __u16 dest_count; /* in - total elements in info array */ | ||
79 | __u16 reserved1; /* must be zero */ | ||
80 | __u32 reserved2; /* must be zero */ | ||
81 | struct file_dedupe_range_info info[0]; | ||
82 | }; | ||
83 | |||
55 | /* And dynamically-tunable limits and defaults: */ | 84 | /* And dynamically-tunable limits and defaults: */ |
56 | struct files_stat_struct { | 85 | struct files_stat_struct { |
57 | unsigned long nr_files; /* read only */ | 86 | unsigned long nr_files; /* read only */ |
@@ -168,6 +197,7 @@ struct inodes_stat_t { | |||
168 | #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */ | 197 | #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */ |
169 | #define FICLONE _IOW(0x94, 9, int) | 198 | #define FICLONE _IOW(0x94, 9, int) |
170 | #define FICLONERANGE _IOW(0x94, 13, struct file_clone_range) | 199 | #define FICLONERANGE _IOW(0x94, 13, struct file_clone_range) |
200 | #define FIDEDUPERANGE _IOWR(0x94, 54, struct file_dedupe_range) | ||
171 | 201 | ||
172 | #define FS_IOC_GETFLAGS _IOR('f', 1, long) | 202 | #define FS_IOC_GETFLAGS _IOR('f', 1, long) |
173 | #define FS_IOC_SETFLAGS _IOW('f', 2, long) | 203 | #define FS_IOC_SETFLAGS _IOW('f', 2, long) |