[PATCH 27/29] xfs: make it possible to disable fsverity

Darrick J. Wong
2024-03-30T00:43:07Z

From: Darrick J. Wong <djwong@kernel.org>

Create an experimental ioctl so that we can turn off fsverity.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_fs_staging.h | 3 ++
fs/xfs/xfs_fsverity.c | 73 ++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_fsverity.h | 3 ++
fs/xfs/xfs_ioctl.c | 6 +++
4 files changed, 85 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_fs_staging.h b/fs/xfs/libxfs/xfs_fs_staging.h
index 899a56a569d50..4c29167a2b190 100644
--- a/fs/xfs/libxfs/xfs_fs_staging.h
+++ b/fs/xfs/libxfs/xfs_fs_staging.h
@@ -229,4 +229,7 @@ struct xfs_map_freesp {
*/
#define XFS_IOC_MAP_FREESP _IOWR('X', 64, struct xfs_map_freesp)

+/* Turn off fs-verity */
+#define FS_IOC_DISABLE_VERITY _IO('f', 133)
+
#endif /* __XFS_FS_STAGING_H__ */
diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c
index bfa5c70beec24..f57d8acbd858a 100644
--- a/fs/xfs/xfs_fsverity.c
+++ b/fs/xfs/xfs_fsverity.c
@@ -792,3 +792,76 @@ const struct fsverity_operations xfs_fsverity_ops = {
.drop_merkle_tree_block = xfs_fsverity_drop_merkle,
.fail_validation = xfs_fsverity_fail_validation,
};
+
+/* Turn off fs-verity. */
+int
+xfs_fsverity_disable(
+ struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_trans *tp;
+ u64 merkle_tree_size;
+ unsigned int merkle_blocksize;
+ int error;
+
+ BUILD_BUG_ON(FS_IOC_DISABLE_VERITY == FS_IOC_ENABLE_VERITY);
+
+ if (!xfs_has_verity(mp))
+ return -EOPNOTSUPP;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+ if (!IS_VERITY(inode)) {
+ error = 0;
+ goto out_iolock;
+ }
+
+ if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION)) {
+ error = -EBUSY;
+ goto out_iolock;
+ }
+
+ error = xfs_qm_dqattach(ip);
+ if (error)
+ goto out_iolock;
+
+ error = fsverity_merkle_tree_geometry(inode, &merkle_blocksize,
+ &merkle_tree_size);
+ if (error)
+ goto out_iolock;
+
+ xfs_fsverity_cache_drop(ip);
+
+ /* Clear fsverity inode flag */
+ error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange, 0, 0, false,
+ &tp);
+ if (error)
+ goto out_iolock;
+
+ ip->i_diflags2 &= ~XFS_DIFLAG2_VERITY;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+
+ error = xfs_trans_commit(tp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ if (error)
+ goto out_iolock;
+
+ inode->i_flags &= ~S_VERITY;
+ fsverity_cleanup_inode(inode);
+
+ /* Remove the fsverity xattrs. */
+ error = xfs_fsverity_delete_metadata(ip, merkle_tree_size,
+ merkle_blocksize);
+ if (error)
+ goto out_iolock;
+
+out_iolock:
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return error;
+}
diff --git a/fs/xfs/xfs_fsverity.h b/fs/xfs/xfs_fsverity.h
index 21ba0d82f26d8..4b9fff6b0d2c4 100644
--- a/fs/xfs/xfs_fsverity.h
+++ b/fs/xfs/xfs_fsverity.h
@@ -17,6 +17,8 @@ struct xfs_icwalk;
int xfs_fsverity_scan_inode(struct xfs_inode *ip, struct xfs_icwalk *icw);

extern const struct fsverity_operations xfs_fsverity_ops;
+
+int xfs_fsverity_disable(struct file *file);
#else
# define xfs_fsverity_cache_init(ip) ((void)0)
# define xfs_fsverity_cache_drop(ip) ((void)0)
@@ -24,6 +26,7 @@ extern const struct fsverity_operations xfs_fsverity_ops;
# define xfs_fsverity_register_shrinker(mp) (0)
# define xfs_fsverity_unregister_shrinker(mp) ((void)0)
# define xfs_fsverity_scan_inode(ip, icw) (0)
+# define xfs_fsverity_disable(ip) (-EOPNOTSUPP)
#endif /* CONFIG_FS_VERITY */

#endif /* __XFS_FSVERITY_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 0aa0ceb9ec153..24deaaf5eb0f5 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -44,6 +44,7 @@
#include "xfs_file.h"
#include "xfs_exchrange.h"
#include "xfs_rtgroup.h"
+#include "xfs_fsverity.h"

#include <linux/mount.h>
#include <linux/namei.h>
@@ -2712,6 +2713,11 @@ xfs_file_ioctl(
case XFS_IOC_MAP_FREESP:
return xfs_ioc_map_freesp(filp, arg);

+#ifdef CONFIG_XFS_EXPERIMENTAL_IOCTLS
+ case FS_IOC_DISABLE_VERITY:
+ return xfs_fsverity_disable(filp);
+#endif
+
case FS_IOC_ENABLE_VERITY:
if (!xfs_has_verity(mp))
return -EOPNOTSUPP;