Skip to content

Commit 9fe2604

Browse files
committed
xfs: add clone file and clone range vfs functions
Define two VFS functions which allow userspace to reflink a range of blocks between two files or to reflink one file's contents to another. These functions fit the new VFS ioctls that standardize the checking for the btrfs CLONE and CLONE RANGE ioctls. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent 862bb36 commit 9fe2604

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

fs/xfs/xfs_file.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,146 @@ xfs_file_fallocate(
977977
return error;
978978
}
979979

980+
/*
981+
* Flush all file writes out to disk.
982+
*/
983+
static int
984+
xfs_file_wait_for_io(
985+
struct inode *inode,
986+
loff_t offset,
987+
size_t len)
988+
{
989+
loff_t rounding;
990+
loff_t ioffset;
991+
loff_t iendoffset;
992+
loff_t bs;
993+
int ret;
994+
995+
bs = inode->i_sb->s_blocksize;
996+
inode_dio_wait(inode);
997+
998+
rounding = max_t(xfs_off_t, bs, PAGE_SIZE);
999+
ioffset = round_down(offset, rounding);
1000+
iendoffset = round_up(offset + len, rounding) - 1;
1001+
ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
1002+
iendoffset);
1003+
return ret;
1004+
}
1005+
1006+
/* Hook up to the VFS reflink function */
1007+
STATIC int
1008+
xfs_file_share_range(
1009+
struct file *file_in,
1010+
loff_t pos_in,
1011+
struct file *file_out,
1012+
loff_t pos_out,
1013+
u64 len)
1014+
{
1015+
struct inode *inode_in;
1016+
struct inode *inode_out;
1017+
ssize_t ret;
1018+
loff_t bs;
1019+
loff_t isize;
1020+
int same_inode;
1021+
loff_t blen;
1022+
1023+
inode_in = file_inode(file_in);
1024+
inode_out = file_inode(file_out);
1025+
bs = inode_out->i_sb->s_blocksize;
1026+
1027+
/* Don't touch certain kinds of inodes */
1028+
if (IS_IMMUTABLE(inode_out))
1029+
return -EPERM;
1030+
if (IS_SWAPFILE(inode_in) ||
1031+
IS_SWAPFILE(inode_out))
1032+
return -ETXTBSY;
1033+
1034+
/* Reflink only works within this filesystem. */
1035+
if (inode_in->i_sb != inode_out->i_sb)
1036+
return -EXDEV;
1037+
same_inode = (inode_in->i_ino == inode_out->i_ino);
1038+
1039+
/* Don't reflink dirs, pipes, sockets... */
1040+
if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
1041+
return -EISDIR;
1042+
if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode))
1043+
return -EINVAL;
1044+
if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
1045+
return -EINVAL;
1046+
1047+
/* Are we going all the way to the end? */
1048+
isize = i_size_read(inode_in);
1049+
if (isize == 0)
1050+
return 0;
1051+
if (len == 0)
1052+
len = isize - pos_in;
1053+
1054+
/* Ensure offsets don't wrap and the input is inside i_size */
1055+
if (pos_in + len < pos_in || pos_out + len < pos_out ||
1056+
pos_in + len > isize)
1057+
return -EINVAL;
1058+
1059+
/* If we're linking to EOF, continue to the block boundary. */
1060+
if (pos_in + len == isize)
1061+
blen = ALIGN(isize, bs) - pos_in;
1062+
else
1063+
blen = len;
1064+
1065+
/* Only reflink if we're aligned to block boundaries */
1066+
if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) ||
1067+
!IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs))
1068+
return -EINVAL;
1069+
1070+
/* Don't allow overlapped reflink within the same file */
1071+
if (same_inode && pos_out + blen > pos_in && pos_out < pos_in + blen)
1072+
return -EINVAL;
1073+
1074+
/* Wait for the completion of any pending IOs on srcfile */
1075+
ret = xfs_file_wait_for_io(inode_in, pos_in, len);
1076+
if (ret)
1077+
goto out_unlock;
1078+
ret = xfs_file_wait_for_io(inode_out, pos_out, len);
1079+
if (ret)
1080+
goto out_unlock;
1081+
1082+
ret = xfs_reflink_remap_range(XFS_I(inode_in), pos_in, XFS_I(inode_out),
1083+
pos_out, len);
1084+
if (ret < 0)
1085+
goto out_unlock;
1086+
1087+
out_unlock:
1088+
return ret;
1089+
}
1090+
1091+
STATIC ssize_t
1092+
xfs_file_copy_range(
1093+
struct file *file_in,
1094+
loff_t pos_in,
1095+
struct file *file_out,
1096+
loff_t pos_out,
1097+
size_t len,
1098+
unsigned int flags)
1099+
{
1100+
int error;
1101+
1102+
error = xfs_file_share_range(file_in, pos_in, file_out, pos_out,
1103+
len);
1104+
if (error)
1105+
return error;
1106+
return len;
1107+
}
1108+
1109+
STATIC int
1110+
xfs_file_clone_range(
1111+
struct file *file_in,
1112+
loff_t pos_in,
1113+
struct file *file_out,
1114+
loff_t pos_out,
1115+
u64 len)
1116+
{
1117+
return xfs_file_share_range(file_in, pos_in, file_out, pos_out,
1118+
len);
1119+
}
9801120

9811121
STATIC int
9821122
xfs_file_open(
@@ -1637,6 +1777,8 @@ const struct file_operations xfs_file_operations = {
16371777
.release = xfs_file_release,
16381778
.fsync = xfs_file_fsync,
16391779
.fallocate = xfs_file_fallocate,
1780+
.copy_file_range = xfs_file_copy_range,
1781+
.clone_file_range = xfs_file_clone_range,
16401782
};
16411783

16421784
const struct file_operations xfs_dir_file_operations = {

0 commit comments

Comments
 (0)