diff options
author | Darrick J. Wong <djwong@kernel.org> | 2022-07-14 11:06:45 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2022-10-14 14:16:50 -0700 |
commit | 528b13a58110eb79f3d3d64145f9c5085cc918ff (patch) | |
tree | 3b665b544a3a1cb3de7ba7c4a275bd7033b81748 /fs/xfs/xfs_file.c | |
parent | f604d801d8e390dbafd893732d3e0b55142d8108 (diff) |
xfs: add a ->xchg_file_range handler
Add a function to handle file range exchange requests from the vfs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/xfs_file.c')
-rw-r--r-- | fs/xfs/xfs_file.c | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 3a66a1a4623a..236368087158 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -24,6 +24,7 @@ #include "xfs_pnfs.h" #include "xfs_iomap.h" #include "xfs_reflink.h" +#include "xfs_xchgrange.h" #include <linux/dax.h> #include <linux/falloc.h> @@ -1151,6 +1152,74 @@ out_unlock: } STATIC int +xfs_file_xchg_range( + struct file *file1, + struct file *file2, + struct file_xchg_range *fxr) +{ + struct inode *inode1 = file_inode(file1); + struct inode *inode2 = file_inode(file2); + struct xfs_inode *ip1 = XFS_I(inode1); + struct xfs_inode *ip2 = XFS_I(inode2); + struct xfs_mount *mp = ip1->i_mount; + unsigned int priv_flags = 0; + bool use_logging = false; + int error; + + if (xfs_is_shutdown(mp)) + return -EIO; + + /* Update cmtime if the fd/inode don't forbid it. */ + if (likely(!(file1->f_mode & FMODE_NOCMTIME) && !IS_NOCMTIME(inode1))) + priv_flags |= XFS_XCHG_RANGE_UPD_CMTIME1; + if (likely(!(file2->f_mode & FMODE_NOCMTIME) && !IS_NOCMTIME(inode2))) + priv_flags |= XFS_XCHG_RANGE_UPD_CMTIME2; + + /* Lock both files against IO */ + error = xfs_ilock2_io_mmap(ip1, ip2); + if (error) + goto out_err; + + /* Prepare and then exchange file contents. */ + error = xfs_xchg_range_prep(file1, file2, fxr); + if (error) + goto out_unlock; + + /* Get permission to use log-assisted file content swaps. */ + error = xfs_xchg_range_grab_log_assist(mp, + !(fxr->flags & FILE_XCHG_RANGE_NONATOMIC), + &use_logging); + if (error) + goto out_unlock; + if (use_logging) + priv_flags |= XFS_XCHG_RANGE_LOGGED; + + error = xfs_xchg_range(ip1, ip2, fxr, priv_flags); + if (error) + goto out_drop_feat; + + /* + * Finish the exchange by removing special file privileges like any + * other file write would do. This may involve turning on support for + * logged xattrs if either file has security capabilities, which means + * xfs_xchg_range_grab_log_assist before xfs_attr_grab_log_assist. + */ + error = generic_xchg_file_range_finish(file1, file2); + if (error) + goto out_drop_feat; + +out_drop_feat: + if (use_logging) + xfs_xchg_range_rele_log_assist(mp); +out_unlock: + xfs_iunlock2_io_mmap(ip1, ip2); +out_err: + if (error) + trace_xfs_file_xchg_range_error(ip2, error, _RET_IP_); + return error; +} + +STATIC int xfs_file_open( struct inode *inode, struct file *file) @@ -1438,6 +1507,7 @@ const struct file_operations xfs_file_operations = { .fallocate = xfs_file_fallocate, .fadvise = xfs_file_fadvise, .remap_file_range = xfs_file_remap_range, + .xchg_file_range = xfs_file_xchg_range, }; const struct file_operations xfs_dir_file_operations = { |