summaryrefslogtreecommitdiffstats
path: root/fs/read_write.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2015-12-03 06:59:50 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2015-12-07 23:11:33 -0500
commit04b38d601239b4d9be641b412cf4b7456a041c67 (patch)
tree196b5fa72848de2a98e09af86099d99da70f2833 /fs/read_write.c
parentacc15575e78e534c12549d8057a692f490a50f61 (diff)
vfs: pull btrfs clone API to vfs layer
The btrfs clone ioctls are now adopted by other file systems, with NFS and CIFS already having support for them, and XFS being under active development. To avoid growth of various slightly incompatible implementations, add one to the VFS. Note that clones are different from file copies in several ways: - they are atomic vs other writers - they support whole file clones - they support 64-bit legth clones - they do not allow partial success (aka short writes) - clones are expected to be a fast metadata operation Because of that it would be rather cumbersome to try to piggyback them on top of the recent clone_file_range infrastructure. The converse isn't true and the clone_file_range system call could try clone file range as a first attempt to copy, something that further patches will enable. Based on earlier work from Peng Tao. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/read_write.c')
-rw-r--r--fs/read_write.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/fs/read_write.c b/fs/read_write.c
index 6cfad4761fd8..c75d02cb13ec 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1451,3 +1451,75 @@ out1:
1451out2: 1451out2:
1452 return ret; 1452 return ret;
1453} 1453}
1454
1455static int clone_verify_area(struct file *file, loff_t pos, u64 len, bool write)
1456{
1457 struct inode *inode = file_inode(file);
1458
1459 if (unlikely(pos < 0))
1460 return -EINVAL;
1461
1462 if (unlikely((loff_t) (pos + len) < 0))
1463 return -EINVAL;
1464
1465 if (unlikely(inode->i_flctx && mandatory_lock(inode))) {
1466 loff_t end = len ? pos + len - 1 : OFFSET_MAX;
1467 int retval;
1468
1469 retval = locks_mandatory_area(inode, file, pos, end,
1470 write ? F_WRLCK : F_RDLCK);
1471 if (retval < 0)
1472 return retval;
1473 }
1474
1475 return security_file_permission(file, write ? MAY_WRITE : MAY_READ);
1476}
1477
1478int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
1479 struct file *file_out, loff_t pos_out, u64 len)
1480{
1481 struct inode *inode_in = file_inode(file_in);
1482 struct inode *inode_out = file_inode(file_out);
1483 int ret;
1484
1485 if (inode_in->i_sb != inode_out->i_sb ||
1486 file_in->f_path.mnt != file_out->f_path.mnt)
1487 return -EXDEV;
1488
1489 if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
1490 return -EISDIR;
1491 if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
1492 return -EOPNOTSUPP;
1493
1494 if (!(file_in->f_mode & FMODE_READ) ||
1495 !(file_out->f_mode & FMODE_WRITE) ||
1496 (file_out->f_flags & O_APPEND) ||
1497 !file_in->f_op->clone_file_range)
1498 return -EBADF;
1499
1500 ret = clone_verify_area(file_in, pos_in, len, false);
1501 if (ret)
1502 return ret;
1503
1504 ret = clone_verify_area(file_out, pos_out, len, true);
1505 if (ret)
1506 return ret;
1507
1508 if (pos_in + len > i_size_read(inode_in))
1509 return -EINVAL;
1510
1511 ret = mnt_want_write_file(file_out);
1512 if (ret)
1513 return ret;
1514
1515 ret = file_in->f_op->clone_file_range(file_in, pos_in,
1516 file_out, pos_out, len);
1517 if (!ret) {
1518 fsnotify_access(file_in);
1519 fsnotify_modify(file_out);
1520 }
1521
1522 mnt_drop_write_file(file_out);
1523 return ret;
1524}
1525EXPORT_SYMBOL(vfs_clone_file_range);