Add a new ioctl to set the uuid of a mounted filesystem. Signed-off-by: Catherine Hoang <catherine.hoang@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_fs.h | 1 + fs/xfs/xfs_ioctl.c | 107 +++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_log.c | 19 ++++++++ fs/xfs/xfs_log.h | 2 + 4 files changed, 129 insertions(+) diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h index 1cfd5bc6520a..a350966cce99 100644 --- a/fs/xfs/libxfs/xfs_fs.h +++ b/fs/xfs/libxfs/xfs_fs.h @@ -831,6 +831,7 @@ struct xfs_scrub_metadata { #define XFS_IOC_FSGEOMETRY _IOR ('X', 126, struct xfs_fsop_geom) #define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req) #define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req) +#define XFS_IOC_SETFSUUID _IOR ('X', 129, uuid_t) /* XFS_IOC_GETFSUUID ---------- deprecated 140 */ diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 55bb01173cde..f0699a7169e4 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -38,6 +38,7 @@ #include "xfs_reflink.h" #include "xfs_ioctl.h" #include "xfs_xattr.h" +#include "xfs_log.h" #include <linux/mount.h> #include <linux/namei.h> @@ -1861,6 +1862,109 @@ xfs_fs_eofblocks_from_user( return 0; } +static int +xfs_ioc_setfsuuid( + struct file *filp, + struct xfs_mount *mp, + uuid_t __user *uuid) +{ + uuid_t old_uuid; + uuid_t new_uuid; + uuid_t *forget_uuid = NULL; + int error; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!xfs_sb_is_v5(&mp->m_sb)) + return -EOPNOTSUPP; + + if (copy_from_user(&new_uuid, uuid, sizeof(uuid_t))) + return -EFAULT; + if (uuid_is_null(&new_uuid)) + return -EINVAL; + + /* Check that the uuid is unique and save a slot in the uuid table. */ + if (!(xfs_has_nouuid(mp))) { + error = xfs_uuid_remember(&new_uuid); + if (error) + return error; + forget_uuid = &new_uuid; + } + + error = xfs_internal_freeze(mp); + if (error) + goto out_drop_uuid; + + spin_lock(&mp->m_sb_lock); + uuid_copy(&old_uuid, &mp->m_sb.sb_uuid); + + /* + * On a v5 filesystem, every metadata object has a uuid stamped into + * the header. The particular uuid used is either sb_uuid or + * sb_meta_uuid, depending on whether the meta_uuid feature is set. + * + * If the meta_uuid feature is set: + * - The user visible uuid is set in sb_uuid + * - The uuid used for metadata blocks is set in sb_meta_uuid + * - If new_uuid == sb_meta_uuid, then we'll deactivate the feature + * and set sb_uuid to the new uuid + * + * If the meta_uuid feature is not set: + * - The user visible uuid is set in sb_uuid + * - The uuid used for meta blocks should match sb_uuid + * - If new_uuid != sb_uuid, we need to copy sb_uuid to sb_meta_uuid, + * set the meta_uuid feature bit, and set sb_uuid to the new uuid + */ + if (xfs_has_metauuid(mp) && + uuid_equal(&new_uuid, &mp->m_sb.sb_meta_uuid)) { + mp->m_sb.sb_features_incompat &= ~XFS_SB_FEAT_INCOMPAT_META_UUID; + mp->m_features &= ~XFS_FEAT_META_UUID; + } else if (!xfs_has_metauuid(mp) && + !uuid_equal(&new_uuid, &mp->m_sb.sb_uuid)) { + uuid_copy(&mp->m_sb.sb_meta_uuid, &mp->m_sb.sb_uuid); + mp->m_sb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_META_UUID; + mp->m_features |= XFS_FEAT_META_UUID; + } + + uuid_copy(&mp->m_sb.sb_uuid, &new_uuid); + spin_unlock(&mp->m_sb_lock); + + xlog_iclog_update_uuid(mp); + + xfs_buf_lock(mp->m_sb_bp); + xfs_buf_hold(mp->m_sb_bp); + + xfs_sb_to_disk(mp->m_sb_bp->b_addr, &mp->m_sb); + error = xfs_bwrite(mp->m_sb_bp); + xfs_buf_relse(mp->m_sb_bp); + if (error) + goto out_drop_freeze; + + /* Update incore state and prepare to drop the old uuid. */ + uuid_copy(&mp->m_super->s_uuid, &new_uuid); + if (!(xfs_has_nouuid(mp))) + forget_uuid = &old_uuid; + + /* + * Update the secondary supers, being aware that growfs also updates + * backup supers so we need to lock against that. + */ + mutex_lock(&mp->m_growlock); + error = xfs_update_secondary_sbs(mp); + mutex_unlock(&mp->m_growlock); + + invalidate_bdev(mp->m_ddev_targp->bt_bdev); + xfs_log_clean(mp); + +out_drop_freeze: + xfs_internal_unfreeze(mp); +out_drop_uuid: + if (forget_uuid) + xfs_uuid_forget(forget_uuid); + return error; +} + /* * These long-unused ioctls were removed from the official ioctl API in 5.17, * but retain these definitions so that we can log warnings about them. @@ -2149,6 +2253,9 @@ xfs_file_ioctl( return error; } + case XFS_IOC_SETFSUUID: + return xfs_ioc_setfsuuid(filp, mp, arg); + default: return -ENOTTY; } diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index fc61cc024023..d79b6065ee9c 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -3921,3 +3921,22 @@ xlog_drop_incompat_feat( { up_read(&log->l_incompat_users); } + +/* + * Cycle all the iclog buffers and update the uuid. + */ +void +xlog_iclog_update_uuid( + struct xfs_mount *mp) +{ + int i; + struct xlog *log = mp->m_log; + struct xlog_in_core *iclog = log->l_iclog; + xlog_rec_header_t *head; + + for (i = 0; i < log->l_iclog_bufs; i++) { + head = &iclog->ic_header; + memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t)); + iclog = iclog->ic_next; + } +} diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 2728886c2963..6b607619163e 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -163,4 +163,6 @@ void xlog_use_incompat_feat(struct xlog *log); void xlog_drop_incompat_feat(struct xlog *log); int xfs_attr_use_log_assist(struct xfs_mount *mp); +void xlog_iclog_update_uuid(struct xfs_mount *mp); + #endif /* __XFS_LOG_H__ */ -- 2.34.1