Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d0347f6 authored by David Howells's avatar David Howells Committed by Al Viro
Browse files

convert do_remount_sb() to fs_context



Replace do_remount_sb() with a function, reconfigure_super(), that's
fs_context aware.  The fs_context is expected to be parameterised already
and have ->root pointing to the superblock to be reconfigured.

A legacy wrapper is provided that is intended to be called from the
fs_context ops when those appear, but for now is called directly from
reconfigure_super().  This wrapper invokes the ->remount_fs() superblock op
for the moment.  It is intended that the remount_fs() op will be phased
out.

The fs_context->purpose is set to FS_CONTEXT_FOR_RECONFIGURE to indicate
that the context is being used for reconfiguration.

do_umount_root() is provided to consolidate remount-to-R/O for umount and
emergency remount by creating a context and invoking reconfiguration.

do_remount(), do_umount() and do_emergency_remount_callback() are switched
to use the new process.

[AV -- fold UMOUNT and EMERGENCY_REMOUNT in; fixes the
umount / bug, gets rid of pointless complexity]
[AV -- set ->net_ns in all cases; nfs remount will need that]
[AV -- shift security_sb_remount() call into reconfigure_super(); the callers
that didn't do security_sb_remount() have NULL fc->security anyway, so it's
a no-op for them]

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Co-developed-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent c9ce29ed
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -69,6 +69,13 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
	case FS_CONTEXT_FOR_MOUNT:
		fc->user_ns = get_user_ns(fc->cred->user_ns);
		break;
	case FS_CONTEXT_FOR_RECONFIGURE:
		/* We don't pin any namespaces as the superblock's
		 * subscriptions cannot be changed at this point.
		 */
		atomic_inc(&reference->d_sb->s_active);
		fc->root = dget(reference);
		break;
	}

	ret = legacy_init_fs_context(fc);
@@ -90,6 +97,15 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
}
EXPORT_SYMBOL(fs_context_for_mount);

struct fs_context *fs_context_for_reconfigure(struct dentry *dentry,
					unsigned int sb_flags,
					unsigned int sb_flags_mask)
{
	return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags,
				sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE);
}
EXPORT_SYMBOL(fs_context_for_reconfigure);

void fc_drop_locked(struct fs_context *fc)
{
	struct super_block *sb = fc->root->d_sb;
@@ -99,6 +115,7 @@ void fc_drop_locked(struct fs_context *fc)
}

static void legacy_fs_context_free(struct fs_context *fc);

/**
 * put_fs_context - Dispose of a superblock configuration context.
 * @fc: The context to dispose of.
@@ -118,7 +135,6 @@ void put_fs_context(struct fs_context *fc)
		legacy_fs_context_free(fc);

	security_free_mnt_opts(&fc->security);
	if (fc->net_ns)
	put_net(fc->net_ns);
	put_user_ns(fc->user_ns);
	put_cred(fc->cred);
@@ -172,6 +188,21 @@ int legacy_get_tree(struct fs_context *fc)
	return 0;
}

/*
 * Handle remount.
 */
int legacy_reconfigure(struct fs_context *fc)
{
	struct legacy_fs_context *ctx = fc->fs_private;
	struct super_block *sb = fc->root->d_sb;

	if (!sb->s_op->remount_fs)
		return 0;

	return sb->s_op->remount_fs(sb, &fc->sb_flags,
				    ctx ? ctx->legacy_data : NULL);
}

/*
 * Initialise a legacy context for a filesystem that doesn't support
 * fs_context.
+2 −1
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ extern void __init chrdev_init(void);
 * fs_context.c
 */
extern int legacy_get_tree(struct fs_context *fc);
extern int legacy_reconfigure(struct fs_context *fc);
extern int parse_monolithic_mount_data(struct fs_context *, void *);
extern void fc_drop_locked(struct fs_context *);

@@ -107,7 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
/*
 * super.c
 */
extern int do_remount_sb(struct super_block *, int, void *, int);
extern int reconfigure_super(struct fs_context *);
extern bool trylock_super(struct super_block *sb);
extern struct super_block *user_get_super(dev_t);

+39 −22
Original line number Diff line number Diff line
@@ -1489,6 +1489,29 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)

static void shrink_submounts(struct mount *mnt);

static int do_umount_root(struct super_block *sb)
{
	int ret = 0;

	down_write(&sb->s_umount);
	if (!sb_rdonly(sb)) {
		struct fs_context *fc;

		fc = fs_context_for_reconfigure(sb->s_root, SB_RDONLY,
						SB_RDONLY);
		if (IS_ERR(fc)) {
			ret = PTR_ERR(fc);
		} else {
			ret = parse_monolithic_mount_data(fc, NULL);
			if (!ret)
				ret = reconfigure_super(fc);
			put_fs_context(fc);
		}
	}
	up_write(&sb->s_umount);
	return ret;
}

static int do_umount(struct mount *mnt, int flags)
{
	struct super_block *sb = mnt->mnt.mnt_sb;
@@ -1554,11 +1577,7 @@ static int do_umount(struct mount *mnt, int flags)
		 */
		if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
			return -EPERM;
		down_write(&sb->s_umount);
		if (!sb_rdonly(sb))
			retval = do_remount_sb(sb, SB_RDONLY, NULL, 0);
		up_write(&sb->s_umount);
		return retval;
		return do_umount_root(sb);
	}

	namespace_lock();
@@ -2367,7 +2386,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
	int err;
	struct super_block *sb = path->mnt->mnt_sb;
	struct mount *mnt = real_mount(path->mnt);
	void *sec_opts = NULL;
	struct fs_context *fc;

	if (!check_mnt(mnt))
		return -EINVAL;
@@ -2378,24 +2397,22 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
	if (!can_change_locked_flags(mnt, mnt_flags))
		return -EPERM;

	if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) {
		err = security_sb_eat_lsm_opts(data, &sec_opts);
		if (err)
			return err;
	}
	err = security_sb_remount(sb, sec_opts);
	security_free_mnt_opts(&sec_opts);
	if (err)
		return err;
	fc = fs_context_for_reconfigure(path->dentry, sb_flags, MS_RMT_MASK);
	if (IS_ERR(fc))
		return PTR_ERR(fc);

	err = parse_monolithic_mount_data(fc, data);
	if (!err) {
		down_write(&sb->s_umount);
		err = -EPERM;
		if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
		err = do_remount_sb(sb, sb_flags, data, 0);
			err = reconfigure_super(fc);
			if (!err)
				set_mount_attributes(mnt, mnt_flags);
		}
		up_write(&sb->s_umount);
	}
	put_fs_context(fc);
	return err;
}

+73 −34
Original line number Diff line number Diff line
@@ -836,28 +836,35 @@ struct super_block *user_get_super(dev_t dev)
}

/**
 *	do_remount_sb - asks filesystem to change mount options.
 *	@sb:	superblock in question
 *	@sb_flags: revised superblock flags
 *	@data:	the rest of options
 *      @force: whether or not to force the change
 * reconfigure_super - asks filesystem to change superblock parameters
 * @fc: The superblock and configuration
 *
 *	Alters the mount options of a mounted file system.
 * Alters the configuration parameters of a live superblock.
 */
int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
int reconfigure_super(struct fs_context *fc)
{
	struct super_block *sb = fc->root->d_sb;
	int retval;
	int remount_ro;
	bool remount_ro = false;
	bool force = fc->sb_flags & SB_FORCE;

	if (fc->sb_flags_mask & ~MS_RMT_MASK)
		return -EINVAL;
	if (sb->s_writers.frozen != SB_UNFROZEN)
		return -EBUSY;

	retval = security_sb_remount(sb, fc->security);
	if (retval)
		return retval;

	if (fc->sb_flags_mask & SB_RDONLY) {
#ifdef CONFIG_BLOCK
	if (!(sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev))
		if (!(fc->sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev))
			return -EACCES;
#endif

	remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb);
		remount_ro = (fc->sb_flags & SB_RDONLY) && !sb_rdonly(sb);
	}

	if (remount_ro) {
		if (!hlist_empty(&sb->s_pins)) {
@@ -868,13 +875,14 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
				return 0;
			if (sb->s_writers.frozen != SB_UNFROZEN)
				return -EBUSY;
			remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb);
			remount_ro = !sb_rdonly(sb);
		}
	}
	shrink_dcache_sb(sb);

	/* If we are remounting RDONLY and current sb is read/write,
	   make sure there are no rw files opened */
	/* If we are reconfiguring to RDONLY and current sb is read/write,
	 * make sure there are no files open for writing.
	 */
	if (remount_ro) {
		if (force) {
			sb->s_readonly_remount = 1;
@@ -886,8 +894,7 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
		}
	}

	if (sb->s_op->remount_fs) {
		retval = sb->s_op->remount_fs(sb, &sb_flags, data);
	retval = legacy_reconfigure(fc);
	if (retval) {
		if (!force)
			goto cancel_readonly;
@@ -895,8 +902,9 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
		WARN(1, "forced remount of a %s fs returned %i\n",
		     sb->s_type->name, retval);
	}
	}
	sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (sb_flags & MS_RMT_MASK);

	WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) |
				 (fc->sb_flags & fc->sb_flags_mask)));
	/* Needs to be ordered wrt mnt_is_readonly() */
	smp_wmb();
	sb->s_readonly_remount = 0;
@@ -923,10 +931,15 @@ static void do_emergency_remount_callback(struct super_block *sb)
	down_write(&sb->s_umount);
	if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) &&
	    !sb_rdonly(sb)) {
		/*
		 * What lock protects sb->s_flags??
		 */
		do_remount_sb(sb, SB_RDONLY, NULL, 1);
		struct fs_context *fc;

		fc = fs_context_for_reconfigure(sb->s_root,
					SB_RDONLY | SB_FORCE, SB_RDONLY);
		if (!IS_ERR(fc)) {
			if (parse_monolithic_mount_data(fc, NULL) == 0)
				(void)reconfigure_super(fc);
			put_fs_context(fc);
		}
	}
	up_write(&sb->s_umount);
}
@@ -1213,6 +1226,31 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
}
EXPORT_SYMBOL(mount_nodev);

static int reconfigure_single(struct super_block *s,
			      int flags, void *data)
{
	struct fs_context *fc;
	int ret;

	/* The caller really need to be passing fc down into mount_single(),
	 * then a chunk of this can be removed.  [Bollocks -- AV]
	 * Better yet, reconfiguration shouldn't happen, but rather the second
	 * mount should be rejected if the parameters are not compatible.
	 */
	fc = fs_context_for_reconfigure(s->s_root, flags, MS_RMT_MASK);
	if (IS_ERR(fc))
		return PTR_ERR(fc);

	ret = parse_monolithic_mount_data(fc, data);
	if (ret < 0)
		goto out;

	ret = reconfigure_super(fc);
out:
	put_fs_context(fc);
	return ret;
}

static int compare_single(struct super_block *s, void *p)
{
	return 1;
@@ -1230,13 +1268,14 @@ struct dentry *mount_single(struct file_system_type *fs_type,
		return ERR_CAST(s);
	if (!s->s_root) {
		error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
		if (error) {
			deactivate_locked_super(s);
			return ERR_PTR(error);
		}
		if (!error)
			s->s_flags |= SB_ACTIVE;
	} else {
		do_remount_sb(s, flags, data, 0);
		error = reconfigure_single(s, flags, data);
	}
	if (unlikely(error)) {
		deactivate_locked_super(s);
		return ERR_PTR(error);
	}
	return dget(s->s_root);
}
+1 −0
Original line number Diff line number Diff line
@@ -1337,6 +1337,7 @@ extern int send_sigurg(struct fown_struct *fown);

/* These sb flags are internal to the kernel */
#define SB_SUBMOUNT     (1<<26)
#define SB_FORCE    	(1<<27)
#define SB_NOSEC	(1<<28)
#define SB_BORN		(1<<29)
#define SB_ACTIVE	(1<<30)
Loading