From: Darrick J. Wong <djwong@xxxxxxxxxx> Create an experimental ioctl so that we can turn off fsverity. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- 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;