From: David Howells <dhowells@redhat.com>
To: Al Viro <viro@ZenIV.linux.org.uk>
Cc: dhowells@redhat.com, mszeredi@redhat.com, jlayton@redhat.com,
linux-fsdevel@vger.kernel.org, linux-nfs@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [RFC][PATCH 00/23] VFS: Introduce superblock configuration context [ver #4]
Date: Fri, 02 Jun 2017 11:14:03 +0100 [thread overview]
Message-ID: <1050.1496398443@warthog.procyon.org.uk> (raw)
In-Reply-To: <20170530145043.GG6365@ZenIV.linux.org.uk>
Hi Al,
Here are some changes I've made, based on your comments plus a little more:
(*) Get rid of the old vfs_new_sb_config() and rename
__vfs_new_sb_config() to that. The callers then have to provide a
file_system_type pointer and all the args.
(*) Add a "struct net *s_net_ns" to struct super_block and add a
FS_IS_NETFS filesystem flag to indicate that this is a network
filesystem and that s_net_ns should point to a network namespace (it
should be NULL otherwise).
[*] Make alloc_super() take an sb_config.
[*] Provide an sget_sc() that takes an sb_config rather than type,
flags and data.
[*] sget_sc() sets the type, flags and namespaces from the sb_config
and requires the superblock it's searching for to match both
s_user_ns and s_net_ns.
[*] The compare and set functions used by sget_sc() take an sb_config
rather than a void* data pointer.
[*] The xprt_net check in nfs_compare_super_address() is then
superfluous.
[*] An nfs_server pointer is added to struct nfs_sb_config rendering
struct nfs_sb_mountdata superfluous.
(*) Make vfs_new_sb_config() get the user_ns and net_ns from a source
appropriate to the purpose flag: the current->nsproxy if new, the
reference sb if submount and none if remount.
(*) Call ->validate() in do_remount(). Ideally, I should be able to make
do_remount() use the sb_config path unconditionally, but for
mount_single(). Why does mount_single() remount the fs if it already
exists?
(*) Get rid of vfs_submount_sc() and just call vfs_kern_mount_sc()
directly, having passed SB_CONFIG_FOR_SUBMOUNT to vfs_new_sb_config().
[*] Don't set MS_SUBMOUNT in this path as it's only used as a
permissions skip - and we can just check the purpose.
[*] The user_ns check in vfs_submount_sc() is no longer required as
sb_config carries the user_ns information.
(*) Removed the inode locking from sys_fsmount().
(*) Moved the security_sb_mountpoint() call into do_new_mount_sc().
What I haven't done yet:
(*) Renamed sb_config -> fs_context. I'd rather avoid renaming again
unless I really have to.
(*) Convert MS_* to SB_* flags in s_flags and weed out things that don't
actually go in there (like MS_SUBMOUNT). This needs to be a separate
patch.
(*) Updated the docs.
(*) Other filesystem conversions.
David
---
diff --git a/fs/fsopen.c b/fs/fsopen.c
index cbede77158ba..2787792d1fc1 100644
--- a/fs/fsopen.c
+++ b/fs/fsopen.c
@@ -222,6 +222,7 @@ fs_initcall(init_fs_fs);
SYSCALL_DEFINE3(fsopen, const char __user *, _fs_name, int, reserved,
unsigned int, flags)
{
+ struct file_system_type *fs_type;
struct sb_config *sc;
struct file *file;
const char *fs_name;
@@ -234,8 +235,13 @@ SYSCALL_DEFINE3(fsopen, const char __user *, _fs_name, int, reserved,
if (IS_ERR(fs_name))
return PTR_ERR(fs_name);
- sc = vfs_new_sb_config(fs_name);
+ fs_type = get_fs_type(fs_name);
kfree(fs_name);
+ if (!fs_type)
+ return -ENODEV;
+
+ sc = vfs_new_sb_config(fs_type, NULL, 0, SB_CONFIG_FOR_NEW);
+ put_filesystem(fs_type);
if (IS_ERR(sc))
return PTR_ERR(sc);
diff --git a/fs/libfs.c b/fs/libfs.c
index e8787adf0363..eaaab3b3b820 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -583,7 +583,7 @@ int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *c
if (unlikely(!*mount)) {
spin_unlock(&pin_fs_lock);
- sc = __vfs_new_sb_config(type, NULL, MS_KERNMOUNT, SB_CONFIG_FOR_NEW);
+ sc = vfs_new_sb_config(type, NULL, MS_KERNMOUNT, SB_CONFIG_FOR_NEW);
if (IS_ERR(sc))
return PTR_ERR(sc);
diff --git a/fs/namespace.c b/fs/namespace.c
index 38ee00e8a45d..083ea719383a 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2297,6 +2297,12 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
err = parse_monolithic_mount_data(sc, data);
if (err < 0)
goto err_sc;
+
+ if (sc->ops->validate) {
+ err = sc->ops->validate(sc);
+ if (err < 0)
+ goto err_sc;
+ }
} else {
err = security_sb_remount(sb, data);
if (err)
@@ -2460,6 +2466,10 @@ static int do_new_mount_sc(struct sb_config *sc, struct path *mountpoint,
struct vfsmount *mnt;
int ret;
+ ret = security_sb_mountpoint(sc, mountpoint);
+ if (ret < 0)
+ return ret;;
+
mnt = vfs_kern_mount_sc(sc);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
@@ -2486,21 +2496,28 @@ static int do_new_mount_sc(struct sb_config *sc, struct path *mountpoint,
* create a new mount for userspace and request it to be added into the
* namespace's tree
*/
-static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
- int mnt_flags, const char *name, void *data)
+static int do_new_mount(struct path *mountpoint, const char *fstype,
+ int ms_flags, int mnt_flags, const char *name,
+ void *data)
{
+ struct file_system_type *fs_type;
struct sb_config *sc;
- int err;
+ int err = -EINVAL;
if (!fstype)
- return -EINVAL;
+ goto err;
+
+ err = -ENODEV;
+ fs_type = get_fs_type(fstype);
+ if (!fs_type)
+ goto err;
- sc = vfs_new_sb_config(fstype);
+ sc = vfs_new_sb_config(fs_type, NULL, ms_flags, SB_CONFIG_FOR_NEW);
+ put_filesystem(fs_type);
if (IS_ERR(sc)) {
err = PTR_ERR(sc);
goto err;
}
- sc->ms_flags = flags;
err = -ENOMEM;
sc->device = kstrdup(name, GFP_KERNEL);
@@ -3174,7 +3191,7 @@ struct vfsmount *vfs_kern_mount(struct file_system_type *type,
if (!type)
return ERR_PTR(-EINVAL);
- sc = __vfs_new_sb_config(type, NULL, flags, SB_CONFIG_FOR_NEW);
+ sc = vfs_new_sb_config(type, NULL, flags, SB_CONFIG_FOR_NEW);
if (IS_ERR(sc))
return ERR_CAST(sc);
@@ -3209,21 +3226,6 @@ struct vfsmount *vfs_kern_mount(struct file_system_type *type,
EXPORT_SYMBOL_GPL(vfs_kern_mount);
struct vfsmount *
-vfs_submount_sc(const struct dentry *mountpoint, struct sb_config *sc)
-{
- /* Until it is worked out how to pass the user namespace
- * through from the parent mount to the submount don't support
- * unprivileged mounts with submounts.
- */
- if (mountpoint->d_sb->s_user_ns != &init_user_ns)
- return ERR_PTR(-EPERM);
-
- sc->ms_flags = MS_SUBMOUNT;
- return vfs_kern_mount_sc(sc);
-}
-EXPORT_SYMBOL_GPL(vfs_submount_sc);
-
-struct vfsmount *
vfs_submount(const struct dentry *mountpoint, struct file_system_type *type,
const char *name, void *data)
{
@@ -3247,7 +3249,6 @@ SYSCALL_DEFINE5(fsmount, int, fs_fd, int, dfd, const char __user *, dir_name,
unsigned int, at_flags, unsigned int, flags)
{
struct sb_config *sc;
- struct inode *inode;
struct path mountpoint;
struct fd f;
unsigned int lookup_flags, mnt_flags = 0;
@@ -3316,13 +3317,8 @@ SYSCALL_DEFINE5(fsmount, int, fs_fd, int, dfd, const char __user *, dir_name,
goto err_fsfd;
}
- ret = security_sb_mountpoint(sc, &mountpoint);
- if (ret < 0)
- goto err_mp;
-
ret = do_new_mount_sc(sc, &mountpoint, mnt_flags);
-err_mp:
path_put(&mountpoint);
err_fsfd:
fdput(f);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 7acca9f53bcd..7dd98709d247 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -130,6 +130,7 @@ struct nfs_sb_config {
struct nfs_fh *mntfh;
struct nfs_subversion *nfs_mod;
+ struct nfs_server *server;
int (*set_security)(struct super_block *, struct nfs_sb_config *);
diff --git a/fs/nfs/mount.c b/fs/nfs/mount.c
index 4ab3338b2208..27ac3b373168 100644
--- a/fs/nfs/mount.c
+++ b/fs/nfs/mount.c
@@ -1401,7 +1401,6 @@ static int nfs_mount_init_from_sb(struct sb_config *sc,
{
struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
struct nfs_server *nfss = sb->s_fs_info;
- struct net *net = nfss->nfs_client->cl_net;
cfg->flags = nfss->flags;
cfg->rsize = nfss->rsize;
@@ -1421,9 +1420,9 @@ static int nfs_mount_init_from_sb(struct sb_config *sc,
memcpy(&cfg->nfs_server.address, &nfss->nfs_client->cl_addr,
cfg->nfs_server.addrlen);
- if (cfg->sc.net_ns != net) {
- put_net(cfg->sc.net_ns);
- cfg->sc.net_ns = get_net(net);
+ if (cfg->sc.net_ns != nfss->nfs_client->cl_net) {
+ WARN_ON(1);
+ return -EINVAL;
}
cfg->nfs_mod = nfss->nfs_client->cl_nfs_mod;
@@ -1476,10 +1475,10 @@ static int nfs_init_sb_config(struct sb_config *sc, struct super_block *src_sb)
struct file_system_type nfs_fs_type = {
.owner = THIS_MODULE,
.name = "nfs",
- .init_sb_config = nfs_init_sb_config,
.sb_config_size = sizeof(struct nfs_sb_config),
+ .init_sb_config = nfs_init_sb_config,
.kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|FS_IS_NETFS,
};
MODULE_ALIAS_FS("nfs");
EXPORT_SYMBOL_GPL(nfs_fs_type);
@@ -1491,7 +1490,7 @@ struct file_system_type nfs4_fs_type = {
.sb_config_size = sizeof(struct nfs_sb_config),
.init_sb_config = nfs_init_sb_config,
.kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|FS_IS_NETFS,
};
MODULE_ALIAS_FS("nfs4");
MODULE_ALIAS("nfs4");
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index e95e669e4db8..e8e620b2de41 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -210,15 +210,6 @@ void nfs_release_automount_timer(void)
cancel_delayed_work(&nfs_automount_task);
}
-/*
- * Clone a mountpoint of the appropriate type
- */
-static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
- struct nfs_sb_config *cfg)
-{
- return vfs_submount_sc(cfg->clone_data.dentry, &cfg->sc);
-}
-
/**
* nfs_do_submount - set up mountpoint when crossing a filesystem boundary
* @dentry - parent directory
@@ -239,8 +230,8 @@ struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
/* Open a new mount context, transferring parameters from the parent
* superblock, including the network namespace.
*/
- sc = __vfs_new_sb_config(&nfs_fs_type, dentry->d_sb, 0,
- SB_CONFIG_FOR_SUBMOUNT);
+ sc = vfs_new_sb_config(&nfs_fs_type, dentry->d_sb, 0,
+ SB_CONFIG_FOR_SUBMOUNT);
if (IS_ERR(sc))
return ERR_CAST(sc);
cfg = container_of(sc, struct nfs_sb_config, sc);
@@ -275,7 +266,7 @@ struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
if (ret < 0)
goto err_sc;
- mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), cfg);
+ mnt = vfs_kern_mount_sc(&cfg->sc);
goto err_sc;
err_buffer:
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 60b711aa0618..978ee33a19ab 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -322,7 +322,7 @@ static struct vfsmount *try_location(struct dentry *dentry,
p += cfg->nfs_server.export_path_len;
*p = 0;
- mnt = vfs_submount_sc(cfg->clone_data.dentry, &cfg->sc);
+ mnt = vfs_kern_mount_sc(&cfg->sc);
if (!IS_ERR(mnt))
break;
}
@@ -347,8 +347,8 @@ static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
if (locations == NULL || locations->nlocations <= 0)
goto out;
- sc = __vfs_new_sb_config(&nfs4_fs_type, dentry->d_sb, 0,
- SB_CONFIG_FOR_SUBMOUNT);
+ sc = vfs_new_sb_config(&nfs4_fs_type, dentry->d_sb, 0,
+ SB_CONFIG_FOR_SUBMOUNT);
if (IS_ERR(sc)) {
mnt = ERR_CAST(sc);
goto out;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 34a7c16cb33c..92293315b070 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1013,13 +1013,14 @@ static void nfs_clone_super(struct super_block *sb, struct nfs_sb_config *cfg)
nfs_initialise_sb(sb);
}
-static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
+static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b,
+ const struct nfs_sb_config *cfg)
{
const struct nfs_server *a = s->s_fs_info;
const struct rpc_clnt *clnt_a = a->client;
const struct rpc_clnt *clnt_b = b->client;
- if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK))
+ if ((s->s_flags & NFS_MS_MASK) != (cfg->sc.ms_flags & NFS_MS_MASK))
goto Ebusy;
if (a->nfs_client != b->nfs_client)
goto Ebusy;
@@ -1045,18 +1046,13 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n
return 0;
}
-struct nfs_sb_mountdata {
- struct nfs_server *server;
- int mntflags;
-};
-
-static int nfs_set_super(struct super_block *s, void *data)
+static int nfs_set_super(struct super_block *s, struct sb_config *sc)
{
- struct nfs_sb_mountdata *sb_mntdata = data;
- struct nfs_server *server = sb_mntdata->server;
+ struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+ struct nfs_server *server = cfg->server;
int ret;
- s->s_flags = sb_mntdata->mntflags;
+ s->s_flags = cfg->sc.ms_flags;
s->s_fs_info = server;
s->s_d_op = server->nfs_client->rpc_ops->dentry_ops;
ret = set_anon_super(s, server);
@@ -1069,11 +1065,6 @@ static int nfs_compare_super_address(struct nfs_server *server1,
struct nfs_server *server2)
{
struct sockaddr *sap1, *sap2;
- struct rpc_xprt *xprt1 = server1->client->cl_xprt;
- struct rpc_xprt *xprt2 = server2->client->cl_xprt;
-
- if (!net_eq(xprt1->xprt_net, xprt2->xprt_net))
- return 0;
sap1 = (struct sockaddr *)&server1->nfs_client->cl_addr;
sap2 = (struct sockaddr *)&server2->nfs_client->cl_addr;
@@ -1107,11 +1098,10 @@ static int nfs_compare_super_address(struct nfs_server *server1,
return 1;
}
-static int nfs_compare_super(struct super_block *sb, void *data)
+static int nfs_compare_super(struct super_block *sb, struct sb_config *sc)
{
- struct nfs_sb_mountdata *sb_mntdata = data;
- struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb);
- int mntflags = sb_mntdata->mntflags;
+ struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+ struct nfs_server *server = cfg->server, *old = NFS_SB(sb);
if (!nfs_compare_super_address(old, server))
return 0;
@@ -1120,7 +1110,7 @@ static int nfs_compare_super(struct super_block *sb, void *data)
return 0;
if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0)
return 0;
- return nfs_compare_mount_options(sb, server, mntflags);
+ return nfs_compare_mount_options(sb, server, cfg);
}
#ifdef CONFIG_NFS_FSCACHE
@@ -1199,11 +1189,7 @@ int nfs_get_tree_common(struct nfs_server *server, struct nfs_sb_config *cfg)
{
struct super_block *s;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
- int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
- struct nfs_sb_mountdata sb_mntdata = {
- .mntflags = cfg->sc.ms_flags,
- .server = server,
- };
+ int (*compare_super)(struct super_block *, struct sb_config *) = nfs_compare_super;
int error;
if (server->flags & NFS_MOUNT_UNSHARED)
@@ -1211,15 +1197,16 @@ int nfs_get_tree_common(struct nfs_server *server, struct nfs_sb_config *cfg)
/* -o noac implies -o sync */
if (server->flags & NFS_MOUNT_NOAC)
- sb_mntdata.mntflags |= MS_SYNCHRONOUS;
+ cfg->sc.ms_flags |= MS_SYNCHRONOUS;
if (cfg->clone_data.cloned && cfg->clone_data.sb != NULL)
if (cfg->clone_data.sb->s_flags & MS_SYNCHRONOUS)
- sb_mntdata.mntflags |= MS_SYNCHRONOUS;
+ cfg->sc.ms_flags |= MS_SYNCHRONOUS;
/* Get a superblock - note that we may end up sharing one that already exists */
- s = sget(cfg->nfs_mod->nfs_fs, compare_super, nfs_set_super, cfg->sc.ms_flags,
- &sb_mntdata);
+ cfg->server = server;
+ s = sget_sc(&cfg->sc, compare_super, nfs_set_super);
+ cfg->server = NULL;
if (IS_ERR(s)) {
error = PTR_ERR(s);
errorf("NFS: Couldn't get superblock");
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 9878b62e874c..c5d24c5f23cd 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -299,7 +299,7 @@ int pid_ns_prepare_proc(struct pid_namespace *ns)
struct vfsmount *mnt;
int ret;
- sc = __vfs_new_sb_config(&proc_fs_type, NULL, 0, SB_CONFIG_FOR_NEW);
+ sc = vfs_new_sb_config(&proc_fs_type, NULL, 0, SB_CONFIG_FOR_NEW);
if (IS_ERR(sc))
return PTR_ERR(sc);
diff --git a/fs/sb_config.c b/fs/sb_config.c
index 4d9bfb982d41..74319550e4e7 100644
--- a/fs/sb_config.c
+++ b/fs/sb_config.c
@@ -178,10 +178,10 @@ int generic_monolithic_mount_data(struct sb_config *ctx, void *data)
EXPORT_SYMBOL(generic_monolithic_mount_data);
/**
- * __vfs_new_sb_config - Create a superblock config.
+ * vfs_new_sb_config - Create a superblock config.
* @fs_type: The filesystem type.
* @src_sb: A superblock from which this one derives (or NULL)
- * @ms_flags: Superblock flags and op flags (such as MS_REMOUNT)
+ * @ms_flags: Superblock flags.
* @purpose: The purpose that this configuration shall be used for.
*
* Open a filesystem and create a mount context. The mount context is
@@ -189,10 +189,10 @@ EXPORT_SYMBOL(generic_monolithic_mount_data);
* another superblock (@src_sb), may have parameters such as namespaces copied
* across from that superblock.
*/
-struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
- struct super_block *src_sb,
- unsigned int ms_flags,
- enum sb_config_purpose purpose)
+struct sb_config *vfs_new_sb_config(struct file_system_type *fs_type,
+ struct super_block *src_sb,
+ unsigned int ms_flags,
+ enum sb_config_purpose purpose)
{
struct sb_config *sc;
size_t sc_size = fs_type->sb_config_size;
@@ -210,10 +210,27 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
sc->purpose = purpose;
sc->ms_flags = ms_flags;
sc->fs_type = get_filesystem(fs_type);
- sc->net_ns = get_net(current->nsproxy->net_ns);
- sc->user_ns = get_user_ns(current_user_ns());
sc->cred = get_current_cred();
+ switch (purpose) {
+ case SB_CONFIG_FOR_NEW:
+ sc->user_ns = get_user_ns(sc->cred->user_ns);
+ if (fs_type->fs_flags & FS_IS_NETFS)
+ sc->net_ns = get_net(current->nsproxy->net_ns);
+ break;
+ case SB_CONFIG_FOR_SUBMOUNT:
+ sc->user_ns = get_user_ns(src_sb->s_user_ns);
+ if (src_sb->s_net_ns)
+ sc->net_ns = get_net(src_sb->s_net_ns);
+ break;
+ case SB_CONFIG_FOR_REMOUNT:
+ /* We don't pin any namespaces as the superblock's
+ * subscriptions cannot be changed at this point.
+ */
+ break;
+ }
+
+
/* TODO: Make all filesystems support this unconditionally */
if (sc->fs_type->init_sb_config) {
ret = sc->fs_type->init_sb_config(sc, src_sb);
@@ -236,31 +253,6 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
put_sb_config(sc);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL(__vfs_new_sb_config);
-
-/**
- * vfs_new_sb_config - Create a superblock config for a new mount.
- * @fs_name: The name of the filesystem
- *
- * Open a filesystem and create a superblock config context for a new mount
- * that will hold the mount options, device name, security details, etc.. Note
- * that the caller should check the ->ops pointer in the returned context to
- * determine whether the filesystem actually supports the superblock context
- * itself.
- */
-struct sb_config *vfs_new_sb_config(const char *fs_name)
-{
- struct file_system_type *fs_type;
- struct sb_config *sc;
-
- fs_type = get_fs_type(fs_name);
- if (!fs_type)
- return ERR_PTR(-ENODEV);
-
- sc = __vfs_new_sb_config(fs_type, NULL, 0, SB_CONFIG_FOR_NEW);
- put_filesystem(fs_type);
- return sc;
-}
EXPORT_SYMBOL(vfs_new_sb_config);
/**
@@ -274,8 +266,8 @@ EXPORT_SYMBOL(vfs_new_sb_config);
struct sb_config *vfs_sb_reconfig(struct vfsmount *mnt,
unsigned int ms_flags)
{
- return __vfs_new_sb_config(mnt->mnt_sb->s_type, mnt->mnt_sb,
- ms_flags, SB_CONFIG_FOR_REMOUNT);
+ return vfs_new_sb_config(mnt->mnt_sb->s_type, mnt->mnt_sb,
+ ms_flags, SB_CONFIG_FOR_REMOUNT);
}
/**
diff --git a/fs/super.c b/fs/super.c
index c2c0435550f6..1412ac0a88d8 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -34,6 +34,7 @@
#include <linux/fsnotify.h>
#include <linux/lockdep.h>
#include <linux/user_namespace.h>
+#include <net/net_namespace.h>
#include <linux/sb_config.h>
#include "internal.h"
@@ -174,16 +175,13 @@ static void destroy_super(struct super_block *s)
}
/**
- * alloc_super - create new superblock
- * @type: filesystem type superblock should belong to
- * @flags: the mount flags
- * @user_ns: User namespace for the super_block
+ * alloc_super - Create new superblock
+ * @sc: The filesystem configuration context
*
* Allocates and initializes a new &struct super_block. alloc_super()
* returns a pointer new superblock or %NULL if allocation had failed.
*/
-static struct super_block *alloc_super(struct file_system_type *type, int flags,
- struct user_namespace *user_ns)
+static struct super_block *alloc_super(struct sb_config *sc)
{
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
static const struct super_operations default_op;
@@ -193,7 +191,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
return NULL;
INIT_LIST_HEAD(&s->s_mounts);
- s->s_user_ns = get_user_ns(user_ns);
+ s->s_user_ns = get_user_ns(sc->user_ns);
+ s->s_net_ns = sc->net_ns ? get_net(sc->net_ns) : NULL;
if (security_sb_alloc(s))
goto fail;
@@ -201,12 +200,12 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
for (i = 0; i < SB_FREEZE_LEVELS; i++) {
if (__percpu_init_rwsem(&s->s_writers.rw_sem[i],
sb_writers_name[i],
- &type->s_writers_key[i]))
+ &sc->fs_type->s_writers_key[i]))
goto fail;
}
init_waitqueue_head(&s->s_writers.wait_unfrozen);
s->s_bdi = &noop_backing_dev_info;
- s->s_flags = flags;
+ s->s_flags = sc->ms_flags;
if (s->s_user_ns != &init_user_ns)
s->s_iflags |= SB_I_NODEV;
INIT_HLIST_NODE(&s->s_instances);
@@ -223,7 +222,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
goto fail;
init_rwsem(&s->s_umount);
- lockdep_set_class(&s->s_umount, &type->s_umount_key);
+ lockdep_set_class(&s->s_umount, &sc->fs_type->s_umount_key);
/*
* sget() can have s_umount recursion.
*
@@ -243,7 +242,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
s->s_count = 1;
atomic_set(&s->s_active, 1);
mutex_init(&s->s_vfs_rename_mutex);
- lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key);
+ lockdep_set_class(&s->s_vfs_rename_mutex, &sc->fs_type->s_vfs_rename_key);
mutex_init(&s->s_dquot.dqio_mutex);
s->s_maxbytes = MAX_NON_LFS;
s->s_op = &default_op;
@@ -456,6 +455,80 @@ void generic_shutdown_super(struct super_block *sb)
EXPORT_SYMBOL(generic_shutdown_super);
/**
+ * sget_sc - Find or create a superblock
+ * @sc: Configuration context.
+ * @test: comparison callback
+ * @set: setup callback
+ */
+struct super_block *sget_sc(struct sb_config *sc,
+ int (*test)(struct super_block *, struct sb_config *),
+ int (*set)(struct super_block *, struct sb_config *))
+{
+ struct super_block *s = NULL;
+ struct super_block *old;
+ int err;
+
+ if (!(sc->ms_flags & MS_KERNMOUNT) &&
+ sc->purpose != SB_CONFIG_FOR_SUBMOUNT) {
+ if (!(sc->fs_type->fs_flags & FS_USERNS_MOUNT) &&
+ !capable(CAP_SYS_ADMIN))
+ return ERR_PTR(-EPERM);
+ else if (!ns_capable(sc->user_ns, CAP_SYS_ADMIN))
+ return ERR_PTR(-EPERM);
+ }
+
+retry:
+ spin_lock(&sb_lock);
+ if (test) {
+ hlist_for_each_entry(old, &sc->fs_type->fs_supers, s_instances) {
+ if (!test(old, sc))
+ continue;
+ if (sc->user_ns != old->s_user_ns &&
+ !net_eq(sc->net_ns, old->s_net_ns)) {
+ spin_unlock(&sb_lock);
+ if (s) {
+ up_write(&s->s_umount);
+ destroy_super(s);
+ }
+ return ERR_PTR(-EBUSY);
+ }
+ if (!grab_super(old))
+ goto retry;
+ if (s) {
+ up_write(&s->s_umount);
+ destroy_super(s);
+ s = NULL;
+ }
+ return old;
+ }
+ }
+ if (!s) {
+ spin_unlock(&sb_lock);
+ s = alloc_super(sc);
+ if (!s)
+ return ERR_PTR(-ENOMEM);
+ goto retry;
+ }
+
+ err = set(s, sc);
+ if (err) {
+ spin_unlock(&sb_lock);
+ up_write(&s->s_umount);
+ destroy_super(s);
+ return ERR_PTR(err);
+ }
+ s->s_type = sc->fs_type;
+ strlcpy(s->s_id, s->s_type->name, sizeof(s->s_id));
+ list_add_tail(&s->s_list, &super_blocks);
+ hlist_add_head(&s->s_instances, &s->s_type->fs_supers);
+ spin_unlock(&sb_lock);
+ get_filesystem(s->s_type);
+ register_shrinker(&s->s_shrink);
+ return s;
+}
+EXPORT_SYMBOL(sget_sc);
+
+/**
* sget_userns - find or create a superblock
* @type: filesystem type superblock should belong to
* @test: comparison callback
@@ -504,7 +577,14 @@ struct super_block *sget_userns(struct file_system_type *type,
}
if (!s) {
spin_unlock(&sb_lock);
- s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
+ {
+ struct sb_config sc = {
+ .fs_type = type,
+ .ms_flags = flags & ~MS_SUBMOUNT,
+ .user_ns = user_ns,
+ };
+ s = alloc_super(&sc);
+ }
if (!s)
return ERR_PTR(-ENOMEM);
goto retry;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1acb76f400c4..110aa4125787 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1388,6 +1388,11 @@ struct super_block {
*/
struct user_namespace *s_user_ns;
+ /* If a network filesystem, this is the network namespace in which it
+ * resides.
+ */
+ struct net *s_net_ns;
+
/*
* Keep the lru lists last in the structure so they always sit on their
* own individual cachelines.
@@ -2022,11 +2027,12 @@ int sync_inode_metadata(struct inode *inode, int wait);
struct file_system_type {
const char *name;
- int fs_flags;
+ unsigned int fs_flags;
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
#define FS_HAS_SUBTYPE 4
#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
+#define FS_IS_NETFS 0x10 /* Network fs that uses net namespace (->s_net_ns) */
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
unsigned short sb_config_size; /* Size of superblock config context to allocate */
struct dentry *(*mount) (struct file_system_type *, int,
@@ -2075,6 +2081,9 @@ void deactivate_locked_super(struct super_block *sb);
int set_anon_super(struct super_block *s, void *data);
int get_anon_bdev(dev_t *);
void free_anon_bdev(dev_t);
+struct super_block *sget_sc(struct sb_config *sc,
+ int (*test)(struct super_block *, struct sb_config *),
+ int (*set)(struct super_block *, struct sb_config *));
struct super_block *sget_userns(struct file_system_type *type,
int (*test)(struct super_block *,void *),
int (*set)(struct super_block *,void *),
diff --git a/include/linux/mount.h b/include/linux/mount.h
index a5dca6abc4d5..e57067da7c2a 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -95,8 +95,6 @@ extern struct vfsmount *vfs_kern_mount_sc(struct sb_config *sc);
extern struct vfsmount *vfs_submount(const struct dentry *mountpoint,
struct file_system_type *type,
const char *name, void *data);
-extern struct vfsmount *vfs_submount_sc(const struct dentry *mountpoint,
- struct sb_config *sc);
extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
extern void mark_mounts_for_expiry(struct list_head *mounts);
diff --git a/include/linux/sb_config.h b/include/linux/sb_config.h
index 144258d82fa1..eec37099f388 100644
--- a/include/linux/sb_config.h
+++ b/include/linux/sb_config.h
@@ -69,11 +69,10 @@ struct sb_config_operations {
int (*get_tree)(struct sb_config *sc);
};
-extern struct sb_config *vfs_new_sb_config(const char *fs_name);
-extern struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
- struct super_block *src_sb,
- unsigned int ms_flags,
- enum sb_config_purpose purpose);
+extern struct sb_config *vfs_new_sb_config(struct file_system_type *fs_type,
+ struct super_block *src_sb,
+ unsigned int ms_flags,
+ enum sb_config_purpose purpose);
extern struct sb_config *vfs_sb_reconfig(struct vfsmount *mnt,
unsigned int ms_flags);
extern struct sb_config *vfs_dup_sb_config(struct sb_config *src);
next prev parent reply other threads:[~2017-06-02 10:14 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-05-22 15:50 [RFC][PATCH 00/23] VFS: Introduce superblock configuration context [ver #4] David Howells
2017-05-22 15:51 ` [PATCH 01/23] Provide a function to create a NUL-terminated string from unterminated data " David Howells
2017-05-22 15:51 ` [PATCH 02/23] VFS: Clean up whitespace in fs/namespace.c " David Howells
2017-05-22 15:51 ` [PATCH 03/23] VFS: Make get_mnt_ns() return the namespace " David Howells
2017-05-22 15:52 ` [PATCH 04/23] VFS: Make get_filesystem() return the affected filesystem " David Howells
2017-05-22 15:52 ` [PATCH 05/23] VFS: Provide empty name qstr " David Howells
2017-05-22 15:52 ` [PATCH 06/23] Provide supplementary error message facility " David Howells
2017-05-22 15:52 ` [PATCH 07/23] VFS: Introduce the structs and doc for a superblock configuration context " David Howells
2017-05-22 15:52 ` [PATCH 08/23] VFS: Add LSM hooks for " David Howells
2017-05-22 15:53 ` [PATCH 09/23] VFS: Implement a " David Howells
2017-05-22 15:53 ` [PATCH 10/23] VFS: Remove unused code after superblock config context changes " David Howells
2017-05-22 15:53 ` [PATCH 11/23] VFS: Implement fsopen() to prepare for a mount " David Howells
2017-05-22 15:53 ` [PATCH 12/23] VFS: Implement fsmount() to effect a pre-configured " David Howells
2017-05-22 15:53 ` [PATCH 13/23] VFS: Add a sample program for fsopen/fsmount " David Howells
2017-05-22 15:53 ` [PATCH 14/23] procfs: Move proc_fill_super() to fs/proc/root.c " David Howells
2017-05-22 15:53 ` [PATCH 15/23] proc: Add superblock config support to procfs " David Howells
2017-05-22 15:53 ` [PATCH 16/23] NFS: Move sb-configuration bits into their own file " David Howells
2017-05-22 15:54 ` [PATCH 17/23] NFS: Constify mount argument match tables " David Howells
2017-05-22 15:54 ` [PATCH 18/23] NFS: Rename struct nfs_parsed_mount_data to struct nfs_sb_config " David Howells
2017-05-22 15:54 ` [PATCH 19/23] NFS: Split nfs_parse_mount_options() " David Howells
2017-05-22 15:54 ` [PATCH 20/23] NFS: Deindent nfs_sb_config_parse_option() " David Howells
2017-05-22 15:54 ` [PATCH 21/23] NFS: Add a small buffer in nfs_sb_config to avoid string dup " David Howells
2017-05-22 15:54 ` [PATCH 22/23] NFS: Do some tidying of the parsing code " David Howells
2017-05-22 15:54 ` [PATCH 23/23] NFS: Add sb_config support. " David Howells
2017-05-30 14:50 ` [RFC][PATCH 00/23] VFS: Introduce superblock configuration context " Al Viro
2017-05-30 14:50 ` Al Viro
2017-05-30 15:36 ` David Howells
2017-05-31 7:51 ` Miklos Szeredi
2017-06-02 10:14 ` David Howells [this message]
2017-06-09 7:48 ` Some filesystems set MNT_* flags in superblock->s_flags David Howells
2017-06-09 8:02 ` Miklos Szeredi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1050.1496398443@warthog.procyon.org.uk \
--to=dhowells@redhat.com \
--cc=jlayton@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=mszeredi@redhat.com \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.