All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/3] fs: online filesystem uuid operations
@ 2020-02-27 21:33 Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 1/3] vfs: create ioctls to query and set the filesystem uuids Darrick J. Wong
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Darrick J. Wong @ 2020-02-27 21:33 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs, linux-fsdevel, linux-ext4

Hi all,

This patch series creates a couple of new ioctls to query and set the
filesystem uuid of mounted filesystems.  This is apparently desirous for
some cloud providers who wish to be able to deploy pre-baked rootfs
images to a machine, boot it, and have the client machine be able to
change the label and uuid to reflect that it's now derivative of the
original image.

For ext4 this is pretty easy to do as all the pieces are already in
place.  For XFS this is a little more difficult because we need to get
our house in order w.r.t. dependencies between the log uuid and
filesystem superblock uuid, which means that this is really new ext4
functionality that I'd like to share with the other filesystems.

I'm particularly curious to hear what people think about the
FORCE_INCOMPAT flag.  There are some circumstances (namely when the
entire fs metadata is keyed to a certain uuid) where we can only change
the uuid by turning on an incompat feature flag.  The currently running
kernel should be able to handle that just fine, but older kernels won't
be able to mount the fs after that.  We (XFS) normally don't do things
like that, which is why I require positive affirmation from userspace
that doing so is ok.

This is an extraordinary way to destroy everything.  Enjoy!
Comments and questions are, as always, welcome.

--D

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] vfs: create ioctls to query and set the filesystem uuids
  2020-02-27 21:33 [PATCH RFC 0/3] fs: online filesystem uuid operations Darrick J. Wong
@ 2020-02-27 21:33 ` Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 2/3] xfs: allow online filesystem uuid queries Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 3/3] ext4: " Darrick J. Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2020-02-27 21:33 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs, linux-fsdevel, linux-ext4

From: Darrick J. Wong <darrick.wong@oracle.com>

Define a pair of ioctls to get and set the filesystem uuid.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_fs.h  |    2 ++
 include/uapi/linux/fs.h |   28 ++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)


diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 558a396c74b3..c968eaf56ec3 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -814,6 +814,8 @@ 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)
+/*	FS_IOC_GETFSUUID ----------- uses 129 */
+/*	FS_IOC_SETFSUUID ----------- uses 130 */
 /*	XFS_IOC_GETFSUUID ---------- deprecated 140	 */
 
 
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 379a612f8f1d..a5423d540ecd 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -142,6 +142,32 @@ struct fsxattr {
 #define FS_XFLAG_COWEXTSIZE	0x00010000	/* CoW extent size allocator hint */
 #define FS_XFLAG_HASATTR	0x80000000	/* no DIFLAG for this	*/
 
+/* Get and set filesystem UUID. */
+struct ioc_fsuuid {
+	uint32_t	fu_flags;
+	uint16_t	fu_length;
+	uint16_t	fu_reserved;
+	uint64_t	fu_reserved1;
+};
+
+/*
+ * Set the UUID even if that would require setting of an incompat or rocompat
+ * feature flag.  This will make the filesystem unmountable on older kernels.
+ */
+#define FS_IOC_SETFSUUID_FORCE_INCOMPAT	(1 << 0)
+
+#define FS_IOC_SETFSUUID_ALL	(FS_IOC_SETFSUUID_FORCE_INCOMPAT)
+
+static inline size_t ioc_fsuuid_sizeof(size_t payload_len)
+{
+	return sizeof(struct ioc_fsuuid) + payload_len;
+}
+
+static inline char *ioc_fsuuid_payload(struct ioc_fsuuid *fu)
+{
+	return (char *)(fu + 1);
+}
+
 /* the read-only stuff doesn't really belong here, but any other place is
    probably as bad and I don't want to create yet another include file. */
 
@@ -214,6 +240,8 @@ struct fsxattr {
 #define FS_IOC_FSSETXATTR		_IOW('X', 32, struct fsxattr)
 #define FS_IOC_GETFSLABEL		_IOR(0x94, 49, char[FSLABEL_MAX])
 #define FS_IOC_SETFSLABEL		_IOW(0x94, 50, char[FSLABEL_MAX])
+#define FS_IOC_GETFSUUID		_IOR('X', 129, struct ioc_fsuuid)
+#define FS_IOC_SETFSUUID		_IOW('X', 130, struct ioc_fsuuid)
 
 /*
  * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] xfs: allow online filesystem uuid queries
  2020-02-27 21:33 [PATCH RFC 0/3] fs: online filesystem uuid operations Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 1/3] vfs: create ioctls to query and set the filesystem uuids Darrick J. Wong
@ 2020-02-27 21:33 ` Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 3/3] ext4: " Darrick J. Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2020-02-27 21:33 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs, linux-fsdevel, linux-ext4

From: Darrick J. Wong <darrick.wong@oracle.com>

Wire up the new ioctls to get and set xfs filesystem uuids.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_ioctl.c |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_mount.c |   30 +++++++++--
 fs/xfs/xfs_mount.h |    3 +
 3 files changed, 169 insertions(+), 5 deletions(-)


diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index b4f5851e2ca5..66bd96d900cf 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1960,6 +1960,142 @@ xfs_fs_eofblocks_from_user(
 	return 0;
 }
 
+static inline int
+xfs_ioc_getfsuuid(
+	struct xfs_mount		*mp,
+	struct ioc_fsuuid __user	*user_fu)
+{
+	struct ioc_fsuuid		fu;
+
+	if (copy_from_user(&fu, user_fu, sizeof(fu)))
+		return -EFAULT;
+
+	if (fu.fu_reserved || fu.fu_reserved1 || fu.fu_flags)
+		return -EINVAL;
+
+	if (fu.fu_length == 0) {
+		fu.fu_length = sizeof(uuid_t);
+		goto out;
+	}
+
+	if (fu.fu_length < sizeof(uuid_t))
+		return -EINVAL;
+
+	if (copy_to_user(user_fu + 1, &mp->m_super->s_uuid, sizeof(uuid_t)))
+		return -EFAULT;
+	fu.fu_length = sizeof(uuid_t);
+
+out:
+	if (copy_to_user(user_fu, &fu, sizeof(fu)))
+		return -EFAULT;
+	return 0;
+}
+
+static inline int
+xfs_ioc_setfsuuid(
+	struct file			*filp,
+	struct xfs_mount		*mp,
+	struct ioc_fsuuid __user	*user_fu)
+{
+	struct ioc_fsuuid		fu;
+	uuid_t				old_uuid;
+	uuid_t				new_uuid;
+	uuid_t				*forget_uuid = NULL;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&fu, user_fu, sizeof(fu)))
+		return -EFAULT;
+
+	if (fu.fu_reserved || fu.fu_reserved1 ||
+	    (fu.fu_flags & ~FS_IOC_SETFSUUID_ALL) ||
+	    fu.fu_length != sizeof(uuid_t))
+		return -EINVAL;
+
+	if (copy_from_user(&new_uuid, user_fu + 1, sizeof(uuid_t)))
+		return -EFAULT;
+	if (uuid_is_null(&new_uuid))
+		return -EINVAL;
+
+	error = mnt_want_write_file(filp);
+	if (error)
+		return error;
+
+	/* Save a slot in the uuid table, if desired. */
+	if (!(mp->m_flags & XFS_MOUNT_NOUUID)) {
+		error = xfs_uuid_remember(&new_uuid);
+		if (error)
+			goto out_drop_write;
+		forget_uuid = &new_uuid;
+	}
+
+	spin_lock(&mp->m_sb_lock);
+	uuid_copy(&old_uuid, &mp->m_sb.sb_uuid);
+
+	/*
+	 * Before v5, the uuid was only set in the superblock, so all we need
+	 * to do here is update the incore sb and write that out to disk.
+	 *
+	 * 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 and the new uuid matches the
+	 * 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 new uuid does not match the
+	 * existing sb_uuid, we need to turn on the meta_uuid feature.  If
+	 * userspace did not set FORCE_INCOMPAT we have to bail out.
+	 * Otherwise, copy sb_uuid to sb_meta_uuid, set the meta_uuid feature
+	 * bit, and set sb_uuid to the new uuid.
+	 */
+	if (xfs_sb_version_hasmetauuid(&mp->m_sb) &&
+	    uuid_equal(&new_uuid, &mp->m_sb.sb_meta_uuid)) {
+		mp->m_sb.sb_features_incompat &= ~XFS_SB_FEAT_INCOMPAT_META_UUID;
+	} else if (xfs_sb_version_hascrc(&mp->m_sb) &&
+		   !xfs_sb_version_hasmetauuid(&mp->m_sb) &&
+		   !uuid_equal(&new_uuid, &mp->m_sb.sb_uuid)) {
+		if (!(fu.fu_flags & FS_IOC_SETFSUUID_FORCE_INCOMPAT)) {
+			spin_unlock(&mp->m_sb_lock);
+			error = -EOPNOTSUPP;
+			goto out_drop_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;
+	}
+	uuid_copy(&mp->m_sb.sb_uuid, &new_uuid);
+	spin_unlock(&mp->m_sb_lock);
+
+	error = xfs_sync_sb_buf(mp);
+	if (error)
+		goto out_drop_uuid;
+
+	/* Update incore state and prepare to drop the old uuid. */
+	uuid_copy(&mp->m_super->s_uuid, &new_uuid);
+	if (!(mp->m_flags & XFS_MOUNT_NOUUID))
+		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);
+
+out_drop_uuid:
+	if (forget_uuid)
+		xfs_uuid_forget(forget_uuid);
+out_drop_write:
+	mnt_drop_write_file(filp);
+	return error;
+}
+
 /*
  * Note: some of the ioctl's return positive numbers as a
  * byte count indicating success, such as readlink_by_handle.
@@ -2246,6 +2382,11 @@ xfs_file_ioctl(
 		return xfs_icache_free_eofblocks(mp, &keofb);
 	}
 
+	case FS_IOC_GETFSUUID:
+		return xfs_ioc_getfsuuid(mp, arg);
+	case FS_IOC_SETFSUUID:
+		return xfs_ioc_setfsuuid(filp, mp, arg);
+
 	default:
 		return -ENOTTY;
 	}
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index e097cece492f..8acd9cffcf50 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -58,7 +58,7 @@ xfs_uuid_mount(
 	struct xfs_mount	*mp)
 {
 	uuid_t			*uuid = &mp->m_sb.sb_uuid;
-	int			hole, i;
+	int			error;
 
 	/* Publish UUID in struct super_block */
 	uuid_copy(&mp->m_super->s_uuid, uuid);
@@ -71,6 +71,21 @@ xfs_uuid_mount(
 		return -EINVAL;
 	}
 
+	error = xfs_uuid_remember(uuid);
+	if (!error)
+		return 0;
+
+	xfs_warn(mp, "Filesystem has duplicate UUID %pU - can't mount", uuid);
+	return error;
+}
+
+int
+xfs_uuid_remember(
+	const uuid_t	*uuid)
+{
+	int		hole;
+	int		i;
+
 	mutex_lock(&xfs_uuid_table_mutex);
 	for (i = 0, hole = -1; i < xfs_uuid_table_size; i++) {
 		if (uuid_is_null(&xfs_uuid_table[i])) {
@@ -94,7 +109,6 @@ xfs_uuid_mount(
 
  out_duplicate:
 	mutex_unlock(&xfs_uuid_table_mutex);
-	xfs_warn(mp, "Filesystem has duplicate UUID %pU - can't mount", uuid);
 	return -EINVAL;
 }
 
@@ -102,12 +116,18 @@ STATIC void
 xfs_uuid_unmount(
 	struct xfs_mount	*mp)
 {
-	uuid_t			*uuid = &mp->m_sb.sb_uuid;
-	int			i;
-
 	if (mp->m_flags & XFS_MOUNT_NOUUID)
 		return;
 
+	xfs_uuid_forget(&mp->m_sb.sb_uuid);
+}
+
+void
+xfs_uuid_forget(
+	const uuid_t		*uuid)
+{
+	int			i;
+
 	mutex_lock(&xfs_uuid_table_mutex);
 	for (i = 0; i < xfs_uuid_table_size; i++) {
 		if (uuid_is_null(&xfs_uuid_table[i]))
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 95ee6b898d3d..df6a7a703fe1 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -448,4 +448,7 @@ void xfs_force_summary_recalc(struct xfs_mount *mp);
 void xfs_mod_delalloc(struct xfs_mount *mp, int64_t delta);
 unsigned int xfs_guess_metadata_threads(struct xfs_mount *mp);
 
+int xfs_uuid_remember(const uuid_t *uuid);
+void xfs_uuid_forget(const uuid_t *uuid);
+
 #endif	/* __XFS_MOUNT_H__ */


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] ext4: allow online filesystem uuid queries
  2020-02-27 21:33 [PATCH RFC 0/3] fs: online filesystem uuid operations Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 1/3] vfs: create ioctls to query and set the filesystem uuids Darrick J. Wong
  2020-02-27 21:33 ` [PATCH 2/3] xfs: allow online filesystem uuid queries Darrick J. Wong
@ 2020-02-27 21:33 ` Darrick J. Wong
  2 siblings, 0 replies; 4+ messages in thread
From: Darrick J. Wong @ 2020-02-27 21:33 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs, linux-fsdevel, linux-ext4

From: Darrick J. Wong <darrick.wong@oracle.com>

Wire up the new ioctls to get and set the ext4 filesystem uuid.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/ext4/ioctl.c |  132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)


diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index a0ec750018dd..c8d556c93cc7 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -813,6 +813,132 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
 	return error;
 }
 
+static int ext4_ioc_getfsuuid(struct super_block *sb,
+			      struct ioc_fsuuid __user *user_fu)
+{
+	struct ioc_fsuuid fu;
+	struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+
+	BUILD_BUG_ON(sizeof(es->s_uuid) != sizeof(uuid_t));
+
+	if (copy_from_user(&fu, user_fu, sizeof(fu)))
+		return -EFAULT;
+
+	if (fu.fu_reserved || fu.fu_reserved1 || fu.fu_flags)
+		return -EINVAL;
+
+	if (fu.fu_length == 0) {
+		fu.fu_length = sizeof(es->s_uuid);
+		goto out;
+	}
+
+	if (fu.fu_length < sizeof(es->s_uuid))
+		return -EINVAL;
+
+	if (copy_to_user(user_fu + 1, es->s_uuid, sizeof(es->s_uuid)))
+		return -EFAULT;
+	fu.fu_length = sizeof(es->s_uuid);
+
+out:
+	if (copy_to_user(user_fu, &fu, sizeof(fu)))
+		return -EFAULT;
+	return 0;
+}
+
+static int ext4_ioc_setfsuuid(struct file *filp, struct super_block *sb,
+			      struct ioc_fsuuid __user *user_fu)
+{
+	struct ioc_fsuuid fu;
+	uuid_t new_uuid;
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	handle_t *handle;
+	int err, err2;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&fu, user_fu, sizeof(fu)))
+		return -EFAULT;
+
+	if (fu.fu_reserved || fu.fu_reserved1 ||
+	    (fu.fu_flags & ~FS_IOC_SETFSUUID_ALL) ||
+	    fu.fu_length != sizeof(uuid_t))
+		return -EINVAL;
+
+	if (copy_from_user(&new_uuid, user_fu + 1, sizeof(uuid_t)))
+		return -EFAULT;
+	if (uuid_is_null(&new_uuid))
+		return -EINVAL;
+
+	err = mnt_want_write_file(filp);
+	if (err)
+		return err;
+
+	handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, 1);
+	if (IS_ERR(handle)) {
+		err = PTR_ERR(handle);
+		goto out_drop_write;
+	}
+	err = ext4_journal_get_write_access(handle, sbi->s_sbh);
+	if (err)
+		goto out_cancel_trans;
+
+	/*
+	 * Older ext4 filesystems with the group descriptor checksum feature
+	 * but not the general metadata checksum features require all group
+	 * descriptors to be rewritten to change the UUID.  We can't do that
+	 * here, so just bail out.
+	 */
+	if (ext4_has_feature_gdt_csum(sb) && !ext4_has_metadata_csum(sb)) {
+		err = -EOPNOTSUPP;
+		goto out_cancel_trans;
+	}
+
+	/*
+	 * Prior to the addition of metadata checksumming, the uuid was only
+	 * used in the superblock, so for those filesystems, all we need to do
+	 * here is update the incore uuid and write the super to disk.
+	 *
+	 * On a metadata_csum filesystem, every metadata object has a checksum
+	 * that is seeded with the checksum of the uuid that was set at
+	 * mkfs time.  The seed value can be stored in the ondisk superblock
+	 * or computed at mount time, depending on feature flags.
+	 *
+	 * If the csum_seed feature is not set, we need to turn on the
+	 * csum_seed feature.  If userspace did not set FORCE_INCOMPAT we have
+	 * to bail out.  Otherwise, copy the incore checksum seed to the ondisk
+	 * superblock, set the csum_seed feature bit, and then we can update
+	 * the incore uuid.
+	 */
+	if ((ext4_has_metadata_csum(sb) || ext4_has_feature_ea_inode(sb)) &&
+	    !ext4_has_feature_csum_seed(sb) &&
+	    memcmp(&new_uuid, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid))) {
+		if (!(fu.fu_flags & FS_IOC_SETFSUUID_FORCE_INCOMPAT)) {
+			err = -EOPNOTSUPP;
+			goto out_cancel_trans;
+		}
+		sbi->s_es->s_checksum_seed = cpu_to_le32(sbi->s_csum_seed);
+		ext4_set_feature_csum_seed(sb);
+	}
+	memcpy(sbi->s_es->s_uuid, &new_uuid, sizeof(uuid_t));
+
+	err = ext4_handle_dirty_super(handle, sb);
+	if (err)
+		goto out_cancel_trans;
+
+	/* Update incore state. */
+	uuid_copy(&sb->s_uuid, &new_uuid);
+	invalidate_bdev(sb->s_bdev);
+
+out_cancel_trans:
+	err2 = ext4_journal_stop(handle);
+	if (!err)
+		err = err2;
+out_drop_write:
+	mnt_drop_write_file(filp);
+	return err;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = file_inode(filp);
@@ -1304,6 +1430,12 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 			return -EOPNOTSUPP;
 		return fsverity_ioctl_measure(filp, (void __user *)arg);
 
+	case FS_IOC_GETFSUUID:
+		return ext4_ioc_getfsuuid(sb, (struct ioc_fsuuid __user *)arg);
+	case FS_IOC_SETFSUUID:
+		return ext4_ioc_setfsuuid(filp, sb,
+					  (struct ioc_fsuuid __user *)arg);
+
 	default:
 		return -ENOTTY;
 	}


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-02-27 21:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-27 21:33 [PATCH RFC 0/3] fs: online filesystem uuid operations Darrick J. Wong
2020-02-27 21:33 ` [PATCH 1/3] vfs: create ioctls to query and set the filesystem uuids Darrick J. Wong
2020-02-27 21:33 ` [PATCH 2/3] xfs: allow online filesystem uuid queries Darrick J. Wong
2020-02-27 21:33 ` [PATCH 3/3] ext4: " Darrick J. Wong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.