summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_file.c
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2022-07-14 11:06:45 -0700
committerDarrick J. Wong <djwong@kernel.org>2022-10-14 14:16:50 -0700
commit528b13a58110eb79f3d3d64145f9c5085cc918ff (patch)
tree3b665b544a3a1cb3de7ba7c4a275bd7033b81748 /fs/xfs/xfs_file.c
parentf604d801d8e390dbafd893732d3e0b55142d8108 (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.c70
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 = {