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> Reviewed-by: Andrey Albershteyn <aalbersh@xxxxxxxxxx> --- Documentation/filesystems/fsverity.rst | 10 ++++++ fs/verity/enable.c | 50 ++++++++++++++++++++++++++++++++ fs/xfs/xfs_fsverity.c | 46 +++++++++++++++++++++++++++++ fs/xfs/xfs_ioctl.c | 6 ++++ include/linux/fsverity.h | 24 +++++++++++++++ include/trace/events/fsverity.h | 13 ++++++++ include/uapi/linux/fsverity.h | 1 + 7 files changed, 150 insertions(+) diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 887cdaf162a99..dc688b2eda68d 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -189,6 +189,16 @@ FS_IOC_ENABLE_VERITY can fail with the following errors: caller's file descriptor, another open file descriptor, or the file reference held by a writable memory map. +FS_IOC_DISABLE_VERITY +-------------------- + +The FS_IOC_DISABLE_VERITY ioctl disables fs-verity on a file. It takes +a file descriptor. + +FS_IOC_DISABLE_VERITY can fail with the following errors: + +- ``EOPNOTSUPP``: the filesystem does not support disabling fs-verity. + FS_IOC_MEASURE_VERITY --------------------- diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 8c6fe4b72b14e..adf8886f4ed29 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -415,3 +415,53 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) return err; } EXPORT_SYMBOL_GPL(fsverity_ioctl_enable); + +/** + * fsverity_ioctl_disable() - disable verity on a file + * @filp: file to enable verity on + * + * Disable fs-verity on a file. See the "FS_IOC_DISABLE_VERITY" section of + * Documentation/filesystems/fsverity.rst for the documentation. + * + * Return: 0 on success, -errno on failure + */ +int fsverity_ioctl_disable(struct file *filp) +{ + struct inode *inode = file_inode(filp); + const struct fsverity_operations *vops = inode->i_sb->s_vop; + struct fsverity_info *vi; + u64 tree_size = 0; + unsigned int block_size = 0; + int err; + + trace_fsverity_disable(inode); + + inode_lock(inode); + if (IS_VERITY(inode)) { + err = 0; + goto out_unlock; + } + + if (!vops->disable_verity) { + err = -EOPNOTSUPP; + goto out_unlock; + } + + vi = fsverity_get_info(inode); + if (vi) { + block_size = vi->tree_params.block_size; + tree_size = vi->tree_params.tree_size; + } + + err = vops->disable_verity(filp, tree_size, block_size); + if (err) + goto out_unlock; + + fsverity_cleanup_inode(inode); + inode_unlock(inode); + return 0; +out_unlock: + inode_unlock(inode); + return err; +} +EXPORT_SYMBOL_GPL(fsverity_ioctl_disable); diff --git a/fs/xfs/xfs_fsverity.c b/fs/xfs/xfs_fsverity.c index 87edf23954336..184c3e14d581f 100644 --- a/fs/xfs/xfs_fsverity.c +++ b/fs/xfs/xfs_fsverity.c @@ -940,9 +940,55 @@ xfs_fsverity_file_corrupt( xfs_inode_mark_sick(XFS_I(inode), XFS_SICK_INO_DATA); } +/* Turn off fs-verity. */ +static int +xfs_fsverity_disable( + struct file *file, + u64 tree_size, + unsigned int block_size) +{ + struct inode *inode = file_inode(file); + struct xfs_inode *ip = XFS_I(inode); + struct xfs_mount *mp = ip->i_mount; + struct xfs_trans *tp; + int error; + + if (xfs_iflags_test(ip, XFS_VERITY_CONSTRUCTION)) + return -EBUSY; + + error = xfs_qm_dqattach(ip); + if (error) + return error; + + xfs_fsverity_drop_cache(ip, tree_size, block_size); + + /* Clear fsverity inode flag */ + error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange, 0, 0, false, + &tp); + if (error) + return error; + + 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) + return error; + + inode->i_flags &= ~S_VERITY; + fsverity_cleanup_inode(inode); + + /* Remove the fsverity xattrs. */ + return xfs_fsverity_delete_metadata(ip, tree_size, block_size); +} + const struct fsverity_operations xfs_fsverity_ops = { .begin_enable_verity = xfs_fsverity_begin_enable, .end_enable_verity = xfs_fsverity_end_enable, + .disable_verity = xfs_fsverity_disable, .get_verity_descriptor = xfs_fsverity_get_descriptor, .read_merkle_tree_block = xfs_fsverity_read_merkle, .write_merkle_tree_block = xfs_fsverity_write_merkle, diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index b05930462f461..d71fc9e6b83eb 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -42,6 +42,7 @@ #include "xfs_exchrange.h" #include "xfs_handle.h" #include "xfs_rtgroup.h" +#include "xfs_fsverity.h" #include <linux/mount.h> #include <linux/fileattr.h> @@ -1590,6 +1591,11 @@ xfs_file_ioctl( return -EOPNOTSUPP; return fsverity_ioctl_read_metadata(filp, arg); + case FS_IOC_DISABLE_VERITY: + if (!xfs_has_verity(mp)) + return -EOPNOTSUPP; + return fsverity_ioctl_disable(filp); + default: return -ENOTTY; } diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 1336f4b9011ea..e9f570f65ed54 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -135,6 +135,24 @@ struct fsverity_operations { size_t desc_size, u64 merkle_tree_size, unsigned int tree_blocksize); + /** + * Disable verity on the given file. + * + * @filp: a readonly file descriptor for the file + * @merkle_tree_size: total bytes the Merkle tree takes up + * @tree_blocksize: the Merkle tree block size + * + * The filesystem must do any needed filesystem-specific preparations + * for disabling verity, e.g. truncating the merkle tree. It also must + * return -EBUSY if verity is already being enabled on the given file. + * + * i_rwsem is held for write. + * + * Return: 0 on success, -errno on failure + */ + int (*disable_verity)(struct file *filp, u64 merkle_tree_size, + unsigned int tree_blocksize); + /** * Get the verity descriptor of the given inode. * @@ -260,6 +278,7 @@ static inline struct fsverity_info *fsverity_get_info(const struct inode *inode) /* enable.c */ int fsverity_ioctl_enable(struct file *filp, const void __user *arg); +int fsverity_ioctl_disable(struct file *filp); /* measure.c */ @@ -326,6 +345,11 @@ static inline int fsverity_ioctl_enable(struct file *filp, return -EOPNOTSUPP; } +static inline int fsverity_ioctl_disable(struct file *filp) +{ + return -EOPNOTSUPP; +} + /* measure.c */ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg) diff --git a/include/trace/events/fsverity.h b/include/trace/events/fsverity.h index 375fdddac6a99..2678dd3249b32 100644 --- a/include/trace/events/fsverity.h +++ b/include/trace/events/fsverity.h @@ -37,6 +37,19 @@ TRACE_EVENT(fsverity_enable, __entry->num_levels) ); +TRACE_EVENT(fsverity_disable, + TP_PROTO(const struct inode *inode), + TP_ARGS(inode), + TP_STRUCT__entry( + __field(ino_t, ino) + ), + TP_fast_assign( + __entry->ino = inode->i_ino; + ), + TP_printk("ino %lu", + (unsigned long) __entry->ino) +); + TRACE_EVENT(fsverity_tree_done, TP_PROTO(const struct inode *inode, const struct fsverity_info *vi, const struct merkle_tree_params *params), diff --git a/include/uapi/linux/fsverity.h b/include/uapi/linux/fsverity.h index 15384e22e331e..73a5f83754792 100644 --- a/include/uapi/linux/fsverity.h +++ b/include/uapi/linux/fsverity.h @@ -99,5 +99,6 @@ struct fsverity_read_metadata_arg { #define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) #define FS_IOC_READ_VERITY_METADATA \ _IOWR('f', 135, struct fsverity_read_metadata_arg) +#define FS_IOC_DISABLE_VERITY _IO('f', 136) #endif /* _UAPI_LINUX_FSVERITY_H */