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

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
...@@ -69,6 +69,13 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, ...@@ -69,6 +69,13 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
case FS_CONTEXT_FOR_MOUNT: case FS_CONTEXT_FOR_MOUNT:
fc->user_ns = get_user_ns(fc->cred->user_ns); fc->user_ns = get_user_ns(fc->cred->user_ns);
break; 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); ret = legacy_init_fs_context(fc);
...@@ -90,6 +97,15 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, ...@@ -90,6 +97,15 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(fs_context_for_mount); 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) void fc_drop_locked(struct fs_context *fc)
{ {
struct super_block *sb = fc->root->d_sb; struct super_block *sb = fc->root->d_sb;
...@@ -99,6 +115,7 @@ void fc_drop_locked(struct fs_context *fc) ...@@ -99,6 +115,7 @@ void fc_drop_locked(struct fs_context *fc)
} }
static void legacy_fs_context_free(struct fs_context *fc); static void legacy_fs_context_free(struct fs_context *fc);
/** /**
* put_fs_context - Dispose of a superblock configuration context. * put_fs_context - Dispose of a superblock configuration context.
* @fc: The context to dispose of. * @fc: The context to dispose of.
...@@ -118,8 +135,7 @@ void put_fs_context(struct fs_context *fc) ...@@ -118,8 +135,7 @@ void put_fs_context(struct fs_context *fc)
legacy_fs_context_free(fc); legacy_fs_context_free(fc);
security_free_mnt_opts(&fc->security); security_free_mnt_opts(&fc->security);
if (fc->net_ns) put_net(fc->net_ns);
put_net(fc->net_ns);
put_user_ns(fc->user_ns); put_user_ns(fc->user_ns);
put_cred(fc->cred); put_cred(fc->cred);
kfree(fc->subtype); kfree(fc->subtype);
...@@ -172,6 +188,21 @@ int legacy_get_tree(struct fs_context *fc) ...@@ -172,6 +188,21 @@ int legacy_get_tree(struct fs_context *fc)
return 0; 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 * Initialise a legacy context for a filesystem that doesn't support
* fs_context. * fs_context.
......
...@@ -56,6 +56,7 @@ extern void __init chrdev_init(void); ...@@ -56,6 +56,7 @@ extern void __init chrdev_init(void);
* fs_context.c * fs_context.c
*/ */
extern int legacy_get_tree(struct fs_context *fc); 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 int parse_monolithic_mount_data(struct fs_context *, void *);
extern void fc_drop_locked(struct fs_context *); extern void fc_drop_locked(struct fs_context *);
...@@ -107,7 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); ...@@ -107,7 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
/* /*
* super.c * 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 bool trylock_super(struct super_block *sb);
extern struct super_block *user_get_super(dev_t); extern struct super_block *user_get_super(dev_t);
......
...@@ -1489,6 +1489,29 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) ...@@ -1489,6 +1489,29 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
static void shrink_submounts(struct mount *mnt); 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) static int do_umount(struct mount *mnt, int flags)
{ {
struct super_block *sb = mnt->mnt.mnt_sb; struct super_block *sb = mnt->mnt.mnt_sb;
...@@ -1554,11 +1577,7 @@ static int do_umount(struct mount *mnt, int flags) ...@@ -1554,11 +1577,7 @@ static int do_umount(struct mount *mnt, int flags)
*/ */
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
down_write(&sb->s_umount); return do_umount_root(sb);
if (!sb_rdonly(sb))
retval = do_remount_sb(sb, SB_RDONLY, NULL, 0);
up_write(&sb->s_umount);
return retval;
} }
namespace_lock(); namespace_lock();
...@@ -2367,7 +2386,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, ...@@ -2367,7 +2386,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
int err; int err;
struct super_block *sb = path->mnt->mnt_sb; struct super_block *sb = path->mnt->mnt_sb;
struct mount *mnt = real_mount(path->mnt); struct mount *mnt = real_mount(path->mnt);
void *sec_opts = NULL; struct fs_context *fc;
if (!check_mnt(mnt)) if (!check_mnt(mnt))
return -EINVAL; return -EINVAL;
...@@ -2378,24 +2397,22 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, ...@@ -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)) if (!can_change_locked_flags(mnt, mnt_flags))
return -EPERM; return -EPERM;
if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) { fc = fs_context_for_reconfigure(path->dentry, sb_flags, MS_RMT_MASK);
err = security_sb_eat_lsm_opts(data, &sec_opts); if (IS_ERR(fc))
if (err) return PTR_ERR(fc);
return err;
}
err = security_sb_remount(sb, sec_opts);
security_free_mnt_opts(&sec_opts);
if (err)
return err;
down_write(&sb->s_umount); err = parse_monolithic_mount_data(fc, data);
err = -EPERM; if (!err) {
if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) { down_write(&sb->s_umount);
err = do_remount_sb(sb, sb_flags, data, 0); err = -EPERM;
if (!err) if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
set_mount_attributes(mnt, mnt_flags); err = reconfigure_super(fc);
if (!err)
set_mount_attributes(mnt, mnt_flags);
}
up_write(&sb->s_umount);
} }
up_write(&sb->s_umount); put_fs_context(fc);
return err; return err;
} }
......
...@@ -836,28 +836,35 @@ struct super_block *user_get_super(dev_t dev) ...@@ -836,28 +836,35 @@ struct super_block *user_get_super(dev_t dev)
} }
/** /**
* do_remount_sb - asks filesystem to change mount options. * reconfigure_super - asks filesystem to change superblock parameters
* @sb: superblock in question * @fc: The superblock and configuration
* @sb_flags: revised superblock flags
* @data: the rest of options
* @force: whether or not to force the change
* *
* 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 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) if (sb->s_writers.frozen != SB_UNFROZEN)
return -EBUSY; return -EBUSY;
retval = security_sb_remount(sb, fc->security);
if (retval)
return retval;
if (fc->sb_flags_mask & SB_RDONLY) {
#ifdef CONFIG_BLOCK #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; return -EACCES;
#endif #endif
remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb); remount_ro = (fc->sb_flags & SB_RDONLY) && !sb_rdonly(sb);
}
if (remount_ro) { if (remount_ro) {
if (!hlist_empty(&sb->s_pins)) { 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) ...@@ -868,13 +875,14 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
return 0; return 0;
if (sb->s_writers.frozen != SB_UNFROZEN) if (sb->s_writers.frozen != SB_UNFROZEN)
return -EBUSY; return -EBUSY;
remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb); remount_ro = !sb_rdonly(sb);
} }
} }
shrink_dcache_sb(sb); shrink_dcache_sb(sb);
/* If we are remounting RDONLY and current sb is read/write, /* If we are reconfiguring to RDONLY and current sb is read/write,
make sure there are no rw files opened */ * make sure there are no files open for writing.
*/
if (remount_ro) { if (remount_ro) {
if (force) { if (force) {
sb->s_readonly_remount = 1; sb->s_readonly_remount = 1;
...@@ -886,17 +894,17 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) ...@@ -886,17 +894,17 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
} }
} }
if (sb->s_op->remount_fs) { retval = legacy_reconfigure(fc);
retval = sb->s_op->remount_fs(sb, &sb_flags, data); if (retval) {
if (retval) { if (!force)
if (!force) goto cancel_readonly;
goto cancel_readonly; /* If forced remount, go ahead despite any errors */
/* If forced remount, go ahead despite any errors */ WARN(1, "forced remount of a %s fs returned %i\n",
WARN(1, "forced remount of a %s fs returned %i\n", sb->s_type->name, retval);
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() */ /* Needs to be ordered wrt mnt_is_readonly() */
smp_wmb(); smp_wmb();
sb->s_readonly_remount = 0; sb->s_readonly_remount = 0;
...@@ -923,10 +931,15 @@ static void do_emergency_remount_callback(struct super_block *sb) ...@@ -923,10 +931,15 @@ static void do_emergency_remount_callback(struct super_block *sb)
down_write(&sb->s_umount); down_write(&sb->s_umount);
if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) && if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) &&
!sb_rdonly(sb)) { !sb_rdonly(sb)) {
/* struct fs_context *fc;
* What lock protects sb->s_flags??
*/ fc = fs_context_for_reconfigure(sb->s_root,
do_remount_sb(sb, SB_RDONLY, NULL, 1); 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); up_write(&sb->s_umount);
} }
...@@ -1213,6 +1226,31 @@ struct dentry *mount_nodev(struct file_system_type *fs_type, ...@@ -1213,6 +1226,31 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(mount_nodev); 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) static int compare_single(struct super_block *s, void *p)
{ {
return 1; return 1;
...@@ -1230,13 +1268,14 @@ struct dentry *mount_single(struct file_system_type *fs_type, ...@@ -1230,13 +1268,14 @@ struct dentry *mount_single(struct file_system_type *fs_type,
return ERR_CAST(s); return ERR_CAST(s);
if (!s->s_root) { if (!s->s_root) {
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0); error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
if (error) { if (!error)
deactivate_locked_super(s); s->s_flags |= SB_ACTIVE;
return ERR_PTR(error);
}
s->s_flags |= SB_ACTIVE;
} else { } 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); return dget(s->s_root);
} }
......
...@@ -1337,6 +1337,7 @@ extern int send_sigurg(struct fown_struct *fown); ...@@ -1337,6 +1337,7 @@ extern int send_sigurg(struct fown_struct *fown);
/* These sb flags are internal to the kernel */ /* These sb flags are internal to the kernel */
#define SB_SUBMOUNT (1<<26) #define SB_SUBMOUNT (1<<26)
#define SB_FORCE (1<<27)
#define SB_NOSEC (1<<28) #define SB_NOSEC (1<<28)
#define SB_BORN (1<<29) #define SB_BORN (1<<29)
#define SB_ACTIVE (1<<30) #define SB_ACTIVE (1<<30)
......
...@@ -25,6 +25,7 @@ struct user_namespace; ...@@ -25,6 +25,7 @@ struct user_namespace;
enum fs_context_purpose { enum fs_context_purpose {
FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */ FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */
FS_CONTEXT_FOR_RECONFIGURE, /* Superblock reconfiguration (remount) */
}; };
/* /*
...@@ -57,6 +58,9 @@ struct fs_context { ...@@ -57,6 +58,9 @@ struct fs_context {
*/ */
extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
unsigned int sb_flags); unsigned int sb_flags);
extern struct fs_context *fs_context_for_reconfigure(struct dentry *dentry,
unsigned int sb_flags,
unsigned int sb_flags_mask);
extern int vfs_get_tree(struct fs_context *fc); extern int vfs_get_tree(struct fs_context *fc);
extern void put_fs_context(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment