aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/compat_ioctl.c1
-rw-r--r--fs/ioctl.c38
-rw-r--r--fs/read_write.c100
-rw-r--r--include/linux/fs.h4
-rw-r--r--include/uapi/linux/fs.h30
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
571static 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
601out:
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}
1525EXPORT_SYMBOL(vfs_clone_file_range); 1525EXPORT_SYMBOL(vfs_clone_file_range);
1526
1527int 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
1616next_file:
1617 mnt_drop_write_file(dst_file);
1618next_loop:
1619 fdput(dst_fd);
1620 }
1621
1622out:
1623 return ret;
1624}
1625EXPORT_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
1638struct inode_operations { 1640struct 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);
1689extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in, 1691extern 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);
1693extern int vfs_dedupe_file_range(struct file *file,
1694 struct file_dedupe_range *same);
1691 1695
1692struct super_operations { 1696struct 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 */
60struct 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 */
75struct 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: */
56struct files_stat_struct { 85struct 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)