aboutsummaryrefslogtreecommitdiffstats
path: root/fs/read_write.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/read_write.c')
-rw-r--r--fs/read_write.c100
1 files changed, 100 insertions, 0 deletions
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);