diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/aio.c | 16 | ||||
-rw-r--r-- | fs/anon_inodes.c | 13 | ||||
-rw-r--r-- | fs/binfmt_misc.c | 20 | ||||
-rw-r--r-- | fs/block_dev.c | 17 | ||||
-rw-r--r-- | fs/btrfs/tests/btrfs-tests.c | 15 | ||||
-rw-r--r-- | fs/configfs/mount.c | 20 | ||||
-rw-r--r-- | fs/d_path.c | 1 | ||||
-rw-r--r-- | fs/efivarfs/super.c | 25 | ||||
-rw-r--r-- | fs/fs_parser.c | 1 | ||||
-rw-r--r-- | fs/fsopen.c | 2 | ||||
-rw-r--r-- | fs/fuse/control.c | 2 | ||||
-rw-r--r-- | fs/hugetlbfs/inode.c | 2 | ||||
-rw-r--r-- | fs/internal.h | 3 | ||||
-rw-r--r-- | fs/libfs.c | 82 | ||||
-rw-r--r-- | fs/namespace.c | 15 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 32 | ||||
-rw-r--r-- | fs/nsfs.c | 16 | ||||
-rw-r--r-- | fs/openpromfs/inode.c | 20 | ||||
-rw-r--r-- | fs/pipe.c | 15 | ||||
-rw-r--r-- | fs/proc/root.c | 7 | ||||
-rw-r--r-- | fs/ramfs/inode.c | 6 | ||||
-rw-r--r-- | fs/super.c | 148 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 3 |
23 files changed, 252 insertions, 229 deletions
@@ -42,6 +42,7 @@ #include <linux/ramfs.h> #include <linux/percpu-refcount.h> #include <linux/mount.h> +#include <linux/pseudo_fs.h> #include <asm/kmap_types.h> #include <linux/uaccess.h> @@ -249,15 +250,12 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages) return file; } -static struct dentry *aio_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int aio_init_fs_context(struct fs_context *fc) { - struct dentry *root = mount_pseudo(fs_type, "aio:", NULL, NULL, - AIO_RING_MAGIC); - - if (!IS_ERR(root)) - root->d_sb->s_iflags |= SB_I_NOEXEC; - return root; + if (!init_pseudo(fc, AIO_RING_MAGIC)) + return -ENOMEM; + fc->s_iflags |= SB_I_NOEXEC; + return 0; } /* aio_setup @@ -268,7 +266,7 @@ static int __init aio_setup(void) { static struct file_system_type aio_fs = { .name = "aio", - .mount = aio_mount, + .init_fs_context = aio_init_fs_context, .kill_sb = kill_anon_super, }; aio_mnt = kern_mount(&aio_fs); diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index c2b8663f5b00..89714308c25b 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -20,6 +20,7 @@ #include <linux/kernel.h> #include <linux/magic.h> #include <linux/anon_inodes.h> +#include <linux/pseudo_fs.h> #include <linux/uaccess.h> @@ -39,16 +40,18 @@ static const struct dentry_operations anon_inodefs_dentry_operations = { .d_dname = anon_inodefs_dname, }; -static struct dentry *anon_inodefs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int anon_inodefs_init_fs_context(struct fs_context *fc) { - return mount_pseudo(fs_type, "anon_inode:", NULL, - &anon_inodefs_dentry_operations, ANON_INODE_FS_MAGIC); + struct pseudo_fs_context *ctx = init_pseudo(fc, ANON_INODE_FS_MAGIC); + if (!ctx) + return -ENOMEM; + ctx->dops = &anon_inodefs_dentry_operations; + return 0; } static struct file_system_type anon_inode_fs_type = { .name = "anon_inodefs", - .mount = anon_inodefs_mount, + .init_fs_context = anon_inodefs_init_fs_context, .kill_sb = kill_anon_super, }; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index b8e145552ec7..cdb45829354d 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -23,6 +23,7 @@ #include <linux/pagemap.h> #include <linux/namei.h> #include <linux/mount.h> +#include <linux/fs_context.h> #include <linux/syscalls.h> #include <linux/fs.h> #include <linux/uaccess.h> @@ -821,7 +822,7 @@ static const struct super_operations s_ops = { .evict_inode = bm_evict_inode, }; -static int bm_fill_super(struct super_block *sb, void *data, int silent) +static int bm_fill_super(struct super_block *sb, struct fs_context *fc) { int err; static const struct tree_descr bm_files[] = { @@ -836,10 +837,19 @@ static int bm_fill_super(struct super_block *sb, void *data, int silent) return err; } -static struct dentry *bm_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int bm_get_tree(struct fs_context *fc) { - return mount_single(fs_type, flags, data, bm_fill_super); + return get_tree_single(fc, bm_fill_super); +} + +static const struct fs_context_operations bm_context_ops = { + .get_tree = bm_get_tree, +}; + +static int bm_init_fs_context(struct fs_context *fc) +{ + fc->ops = &bm_context_ops; + return 0; } static struct linux_binfmt misc_format = { @@ -850,7 +860,7 @@ static struct linux_binfmt misc_format = { static struct file_system_type bm_fs_type = { .owner = THIS_MODULE, .name = "binfmt_misc", - .mount = bm_mount, + .init_fs_context = bm_init_fs_context, .kill_sb = kill_litter_super, }; MODULE_ALIAS_FS("binfmt_misc"); diff --git a/fs/block_dev.c b/fs/block_dev.c index f00b569a9f89..4707dfff991b 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -26,6 +26,7 @@ #include <linux/writeback.h> #include <linux/mpage.h> #include <linux/mount.h> +#include <linux/pseudo_fs.h> #include <linux/uio.h> #include <linux/namei.h> #include <linux/log2.h> @@ -821,19 +822,19 @@ static const struct super_operations bdev_sops = { .evict_inode = bdev_evict_inode, }; -static struct dentry *bd_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int bd_init_fs_context(struct fs_context *fc) { - struct dentry *dent; - dent = mount_pseudo(fs_type, "bdev:", &bdev_sops, NULL, BDEVFS_MAGIC); - if (!IS_ERR(dent)) - dent->d_sb->s_iflags |= SB_I_CGROUPWB; - return dent; + struct pseudo_fs_context *ctx = init_pseudo(fc, BDEVFS_MAGIC); + if (!ctx) + return -ENOMEM; + fc->s_iflags |= SB_I_CGROUPWB; + ctx->ops = &bdev_sops; + return 0; } static struct file_system_type bd_type = { .name = "bdev", - .mount = bd_mount, + .init_fs_context = bd_init_fs_context, .kill_sb = kill_anon_super, }; diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c index 9238fd4f1734..1e3ba4949399 100644 --- a/fs/btrfs/tests/btrfs-tests.c +++ b/fs/btrfs/tests/btrfs-tests.c @@ -5,6 +5,7 @@ #include <linux/fs.h> #include <linux/mount.h> +#include <linux/pseudo_fs.h> #include <linux/magic.h> #include "btrfs-tests.h" #include "../ctree.h" @@ -32,17 +33,19 @@ static const struct super_operations btrfs_test_super_ops = { .destroy_inode = btrfs_test_destroy_inode, }; -static struct dentry *btrfs_test_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) + +static int btrfs_test_init_fs_context(struct fs_context *fc) { - return mount_pseudo(fs_type, "btrfs_test:", &btrfs_test_super_ops, - NULL, BTRFS_TEST_MAGIC); + struct pseudo_fs_context *ctx = init_pseudo(fc, BTRFS_TEST_MAGIC); + if (!ctx) + return -ENOMEM; + ctx->ops = &btrfs_test_super_ops; + return 0; } static struct file_system_type test_type = { .name = "btrfs_test_fs", - .mount = btrfs_test_mount, + .init_fs_context = btrfs_test_init_fs_context, .kill_sb = kill_anon_super, }; diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index 791304fdde9d..55438dd58189 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -13,6 +13,7 @@ #include <linux/fs.h> #include <linux/module.h> #include <linux/mount.h> +#include <linux/fs_context.h> #include <linux/pagemap.h> #include <linux/init.h> #include <linux/slab.h> @@ -52,7 +53,7 @@ static struct configfs_dirent configfs_root = { .s_iattr = NULL, }; -static int configfs_fill_super(struct super_block *sb, void *data, int silent) +static int configfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct inode *inode; struct dentry *root; @@ -88,16 +89,25 @@ static int configfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } -static struct dentry *configfs_do_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int configfs_get_tree(struct fs_context *fc) { - return mount_single(fs_type, flags, data, configfs_fill_super); + return get_tree_single(fc, configfs_fill_super); +} + +static const struct fs_context_operations configfs_context_ops = { + .get_tree = configfs_get_tree, +}; + +static int configfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &configfs_context_ops; + return 0; } static struct file_system_type configfs_fs_type = { .owner = THIS_MODULE, .name = "configfs", - .mount = configfs_do_mount, + .init_fs_context = configfs_init_fs_context, .kill_sb = kill_litter_super, }; MODULE_ALIAS_FS("configfs"); diff --git a/fs/d_path.c b/fs/d_path.c index e8fce6b1174f..a7d0a96b35ce 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -316,7 +316,6 @@ char *simple_dname(struct dentry *dentry, char *buffer, int buflen) end = ERR_PTR(-ENAMETOOLONG); return end; } -EXPORT_SYMBOL(simple_dname); /* * Write full pathname from the root of the filesystem into the buffer. diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 5bc3c4a4c563..fa4f6447ddad 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -7,6 +7,7 @@ #include <linux/ctype.h> #include <linux/efi.h> #include <linux/fs.h> +#include <linux/fs_context.h> #include <linux/module.h> #include <linux/pagemap.h> #include <linux/ucs2_string.h> @@ -28,8 +29,6 @@ static const struct super_operations efivarfs_ops = { .evict_inode = efivarfs_evict_inode, }; -static struct super_block *efivarfs_sb; - /* * Compare two efivarfs file names. * @@ -188,14 +187,12 @@ static int efivarfs_destroy(struct efivar_entry *entry, void *data) return 0; } -static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) +static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct inode *inode = NULL; struct dentry *root; int err; - efivarfs_sb = sb; - sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; @@ -223,16 +220,24 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) return err; } -static struct dentry *efivarfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int efivarfs_get_tree(struct fs_context *fc) +{ + return get_tree_single(fc, efivarfs_fill_super); +} + +static const struct fs_context_operations efivarfs_context_ops = { + .get_tree = efivarfs_get_tree, +}; + +static int efivarfs_init_fs_context(struct fs_context *fc) { - return mount_single(fs_type, flags, data, efivarfs_fill_super); + fc->ops = &efivarfs_context_ops; + return 0; } static void efivarfs_kill_sb(struct super_block *sb) { kill_litter_super(sb); - efivarfs_sb = NULL; /* Remove all entries and destroy */ __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL); @@ -241,7 +246,7 @@ static void efivarfs_kill_sb(struct super_block *sb) static struct file_system_type efivarfs_type = { .owner = THIS_MODULE, .name = "efivarfs", - .mount = efivarfs_mount, + .init_fs_context = efivarfs_init_fs_context, .kill_sb = efivarfs_kill_sb, }; diff --git a/fs/fs_parser.c b/fs/fs_parser.c index 0d388faa25d1..460ea4206fa2 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -264,6 +264,7 @@ int fs_lookup_param(struct fs_context *fc, return invalf(fc, "%s: not usable as path", param->key); } + f->refcnt++; /* filename_lookup() drops our ref. */ ret = filename_lookup(param->dirfd, f, flags, _path, NULL); if (ret < 0) { errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name); diff --git a/fs/fsopen.c b/fs/fsopen.c index a8bf83ce8d4e..043ffa8dc263 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -226,6 +226,8 @@ static int vfs_fsconfig_locked(struct fs_context *fc, int cmd, case FSCONFIG_CMD_CREATE: if (fc->phase != FS_CONTEXT_CREATE_PARAMS) return -EBUSY; + if (!mount_capable(fc)) + return -EPERM; fc->phase = FS_CONTEXT_CREATING; ret = vfs_get_tree(fc); if (ret) diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 14ce1e47f980..c23f6f243ad4 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -346,7 +346,7 @@ static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx) static int fuse_ctl_get_tree(struct fs_context *fc) { - return vfs_get_super(fc, vfs_get_single_super, fuse_ctl_fill_super); + return get_tree_single(fc, fuse_ctl_fill_super); } static const struct fs_context_operations fuse_ctl_context_ops = { diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 1dcc57189382..a478df035651 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -1299,7 +1299,7 @@ static int hugetlbfs_get_tree(struct fs_context *fc) int err = hugetlbfs_validate(fc); if (err) return err; - return vfs_get_super(fc, vfs_get_independent_super, hugetlbfs_fill_super); + return get_tree_nodev(fc, hugetlbfs_fill_super); } static void hugetlbfs_fs_context_free(struct fs_context *fc) diff --git a/fs/internal.h b/fs/internal.h index 2f3c3de51fad..b9bad2d30cef 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -14,6 +14,7 @@ struct path; struct mount; struct shrink_control; struct fs_context; +struct user_namespace; /* * block_dev.c @@ -107,6 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); extern int reconfigure_super(struct fs_context *); extern bool trylock_super(struct super_block *sb); extern struct super_block *user_get_super(dev_t); +extern bool mount_capable(struct fs_context *); /* * open.c @@ -154,6 +156,7 @@ extern int d_set_mounted(struct dentry *dentry); extern long prune_dcache_sb(struct super_block *sb, struct shrink_control *sc); extern struct dentry *d_alloc_cursor(struct dentry *); extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *); +extern char *simple_dname(struct dentry *, char *, int); /* * read_write.c diff --git a/fs/libfs.c b/fs/libfs.c index 7e52e77692ec..c9b2850c0f7c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -17,6 +17,8 @@ #include <linux/exportfs.h> #include <linux/writeback.h> #include <linux/buffer_head.h> /* sync_mapping_buffers */ +#include <linux/fs_context.h> +#include <linux/pseudo_fs.h> #include <linux/uaccess.h> @@ -236,34 +238,22 @@ static const struct super_operations simple_super_operations = { .statfs = simple_statfs, }; -/* - * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that - * will never be mountable) - */ -struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name, - const struct super_operations *ops, const struct xattr_handler **xattr, - const struct dentry_operations *dops, unsigned long magic) +static int pseudo_fs_fill_super(struct super_block *s, struct fs_context *fc) { - struct super_block *s; - struct dentry *dentry; + struct pseudo_fs_context *ctx = fc->fs_private; struct inode *root; - struct qstr d_name = QSTR_INIT(name, strlen(name)); - - s = sget_userns(fs_type, NULL, set_anon_super, SB_KERNMOUNT|SB_NOUSER, - &init_user_ns, NULL); - if (IS_ERR(s)) - return ERR_CAST(s); s->s_maxbytes = MAX_LFS_FILESIZE; s->s_blocksize = PAGE_SIZE; s->s_blocksize_bits = PAGE_SHIFT; - s->s_magic = magic; - s->s_op = ops ? ops : &simple_super_operations; - s->s_xattr = xattr; + s->s_magic = ctx->magic; + s->s_op = ctx->ops ?: &simple_super_operations; + s->s_xattr = ctx->xattr; s->s_time_gran = 1; root = new_inode(s); if (!root) - goto Enomem; + return -ENOMEM; + /* * since this is the first inode, make it number 1. New inodes created * after this must take care not to collide with it (by passing @@ -272,22 +262,48 @@ struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name, root->i_ino = 1; root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; root->i_atime = root->i_mtime = root->i_ctime = current_time(root); - dentry = __d_alloc(s, &d_name); - if (!dentry) { - iput(root); - goto Enomem; + s->s_root = d_make_root(root); + if (!s->s_root) + return -ENOMEM; + s->s_d_op = ctx->dops; + return 0; +} + +static int pseudo_fs_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, pseudo_fs_fill_super); +} + +static void pseudo_fs_free(struct fs_context *fc) +{ + kfree(fc->fs_private); +} + +static const struct fs_context_operations pseudo_fs_context_ops = { + .free = pseudo_fs_free, + .get_tree = pseudo_fs_get_tree, +}; + +/* + * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that + * will never be mountable) + */ +struct pseudo_fs_context *init_pseudo(struct fs_context *fc, + unsigned long magic) +{ + struct pseudo_fs_context *ctx; + + ctx = kzalloc(sizeof(struct pseudo_fs_context), GFP_KERNEL); + if (likely(ctx)) { + ctx->magic = magic; + fc->fs_private = ctx; + fc->ops = &pseudo_fs_context_ops; + fc->sb_flags |= SB_NOUSER; + fc->global = true; } - d_instantiate(dentry, root); - s->s_root = dentry; - s->s_d_op = dops; - s->s_flags |= SB_ACTIVE; - return dget(s->s_root); - -Enomem: - deactivate_locked_super(s); - return ERR_PTR(-ENOMEM); + return ctx; } -EXPORT_SYMBOL(mount_pseudo_xattr); +EXPORT_SYMBOL(init_pseudo); int simple_open(struct inode *inode, struct file *file) { diff --git a/fs/namespace.c b/fs/namespace.c index 6fbc9126367a..f0d664adb9ba 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -29,6 +29,7 @@ #include <linux/sched/task.h> #include <uapi/linux/mount.h> #include <linux/fs_context.h> +#include <linux/shmem_fs.h> #include "pnode.h" #include "internal.h" @@ -2788,6 +2789,8 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, err = vfs_parse_fs_string(fc, "source", name, strlen(name)); if (!err) err = parse_monolithic_mount_data(fc, data); + if (!err && !mount_capable(fc)) + err = -EPERM; if (!err) err = vfs_get_tree(fc); if (!err) @@ -3295,8 +3298,8 @@ struct dentry *mount_subtree(struct vfsmount *m, const char *name) } EXPORT_SYMBOL(mount_subtree); -int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type, - unsigned long flags, void __user *data) +int ksys_mount(const char __user *dev_name, const char __user *dir_name, + const char __user *type, unsigned long flags, void __user *data) { int ret; char *kernel_type; @@ -3687,13 +3690,8 @@ static void __init init_mount_tree(void) struct mount *m; struct mnt_namespace *ns; struct path root; - struct file_system_type *type; - type = get_fs_type("rootfs"); - if (!type) - panic("Can't find rootfs type"); - mnt = vfs_kern_mount(type, 0, "rootfs", NULL); - put_filesystem(type); + mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", NULL); if (IS_ERR(mnt)) panic("Can't create rootfs"); @@ -3746,6 +3744,7 @@ void __init mnt_init(void) fs_kobj = kobject_create_and_add("fs", NULL); if (!fs_kobj) printk(KERN_WARNING "%s: kobj create error\n", __func__); + shmem_init(); init_rootfs(); init_mount_tree(); } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 0a9a49ded546..13c548733860 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -8,6 +8,7 @@ #include <linux/slab.h> #include <linux/namei.h> #include <linux/ctype.h> +#include <linux/fs_context.h> #include <linux/sunrpc/svcsock.h> #include <linux/lockd/lockd.h> @@ -1337,7 +1338,7 @@ void nfsd_client_rmdir(struct dentry *dentry) inode_unlock(dir); } -static int nfsd_fill_super(struct super_block * sb, void * data, int silent) +static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) { struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); @@ -1372,7 +1373,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) #endif /* last one */ {""} }; - get_net(sb->s_fs_info); + ret = simple_fill_super(sb, 0x6e667364, nfsd_files); if (ret) return ret; @@ -1381,14 +1382,31 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) return PTR_ERR(dentry); nn->nfsd_client_dir = dentry; return 0; +} +static int nfsd_fs_get_tree(struct fs_context *fc) +{ + fc->s_fs_info = get_net(fc->net_ns); + return vfs_get_super(fc, vfs_get_keyed_super, nfsd_fill_super); } -static struct dentry *nfsd_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static void nfsd_fs_free_fc(struct fs_context *fc) { - struct net *net = current->nsproxy->net_ns; - return mount_ns(fs_type, flags, data, net, net->user_ns, nfsd_fill_super); + if (fc->s_fs_info) + put_net(fc->s_fs_info); +} + +static const struct fs_context_operations nfsd_fs_context_ops = { + .free = nfsd_fs_free_fc, + .get_tree = nfsd_fs_get_tree, +}; + +static int nfsd_init_fs_context(struct fs_context *fc) +{ + put_user_ns(fc->user_ns); + fc->user_ns = get_user_ns(fc->net_ns->user_ns); + fc->ops = &nfsd_fs_context_ops; + return 0; } static void nfsd_umount(struct super_block *sb) @@ -1402,7 +1420,7 @@ static void nfsd_umount(struct super_block *sb) static struct file_system_type nfsd_fs_type = { .owner = THIS_MODULE, .name = "nfsd", - .mount = nfsd_mount, + .init_fs_context = nfsd_init_fs_context, .kill_sb = nfsd_umount, }; MODULE_ALIAS_FS("nfsd"); diff --git a/fs/nsfs.c b/fs/nsfs.c index e3bf08c5af41..a0431642c6b5 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/mount.h> +#include <linux/pseudo_fs.h> #include <linux/file.h> #include <linux/fs.h> #include <linux/proc_ns.h> @@ -258,15 +259,20 @@ static const struct super_operations nsfs_ops = { .evict_inode = nsfs_evict, .show_path = nsfs_show_path, }; -static struct dentry *nsfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) + +static int nsfs_init_fs_context(struct fs_context *fc) { - return mount_pseudo(fs_type, "nsfs:", &nsfs_ops, - &ns_dentry_operations, NSFS_MAGIC); + struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC); + if (!ctx) + return -ENOMEM; + ctx->ops = &nsfs_ops; + ctx->dops = &ns_dentry_operations; + return 0; } + static struct file_system_type nsfs = { .name = "nsfs", - .mount = nsfs_mount, + .init_fs_context = nsfs_init_fs_context, .kill_sb = kill_anon_super, }; diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index e6cb7689fec4..40c8c2e32fa3 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -9,6 +9,7 @@ #include <linux/types.h> #include <linux/string.h> #include <linux/fs.h> +#include <linux/fs_context.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/seq_file.h> @@ -375,7 +376,7 @@ static const struct super_operations openprom_sops = { .remount_fs = openprom_remount, }; -static int openprom_fill_super(struct super_block *s, void *data, int silent) +static int openprom_fill_super(struct super_block *s, struct fs_context *fc) { struct inode *root_inode; struct op_inode_info *oi; @@ -409,16 +410,25 @@ out_no_root: return ret; } -static struct dentry *openprom_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int openpromfs_get_tree(struct fs_context *fc) { - return mount_single(fs_type, flags, data, openprom_fill_super); + return get_tree_single(fc, openprom_fill_super); +} + +static const struct fs_context_operations openpromfs_context_ops = { + .get_tree = openpromfs_get_tree, +}; + +static int openpromfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &openpromfs_context_ops; + return 0; } static struct file_system_type openprom_fs_type = { .owner = THIS_MODULE, .name = "openpromfs", - .mount = openprom_mount, + .init_fs_context = openpromfs_init_fs_context, .kill_sb = kill_anon_super, }; MODULE_ALIAS_FS("openpromfs"); diff --git a/fs/pipe.c b/fs/pipe.c index 41065901106b..8a2ab2f974bd 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -14,6 +14,7 @@ #include <linux/fs.h> #include <linux/log2.h> #include <linux/mount.h> +#include <linux/pseudo_fs.h> #include <linux/magic.h> #include <linux/pipe_fs_i.h> #include <linux/uio.h> @@ -1182,16 +1183,20 @@ static const struct super_operations pipefs_ops = { * any operations on the root directory. However, we need a non-trivial * d_name - pipe: will go nicely and kill the special-casing in procfs. */ -static struct dentry *pipefs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) + +static int pipefs_init_fs_context(struct fs_context *fc) { - return mount_pseudo(fs_type, "pipe:", &pipefs_ops, - &pipefs_dentry_operations, PIPEFS_MAGIC); + struct pseudo_fs_context *ctx = init_pseudo(fc, PIPEFS_MAGIC); + if (!ctx) + return -ENOMEM; + ctx->ops = &pipefs_ops; + ctx->dops = &pipefs_dentry_operations; + return 0; } static struct file_system_type pipe_fs_type = { .name = "pipefs", - .mount = pipefs_mount, + .init_fs_context = pipefs_init_fs_context, .kill_sb = kill_anon_super, }; diff --git a/fs/proc/root.c b/fs/proc/root.c index 522199e9525e..33f72d1b92cc 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -157,8 +157,6 @@ static int proc_get_tree(struct fs_context *fc) { struct proc_fs_context *ctx = fc->fs_private; - put_user_ns(fc->user_ns); - fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); fc->s_fs_info = ctx->pid_ns; return vfs_get_super(fc, vfs_get_keyed_super, proc_fill_super); } @@ -167,8 +165,7 @@ static void proc_fs_context_free(struct fs_context *fc) { struct proc_fs_context *ctx = fc->fs_private; - if (ctx->pid_ns) - put_pid_ns(ctx->pid_ns); + put_pid_ns(ctx->pid_ns); kfree(ctx); } @@ -188,6 +185,8 @@ static int proc_init_fs_context(struct fs_context *fc) return -ENOMEM; ctx->pid_ns = get_pid_ns(task_active_pid_ns(current)); + put_user_ns(fc->user_ns); + fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); fc->fs_private = ctx; fc->ops = &proc_fs_context_ops; return 0; diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 11201b2d06b9..733c6b4193dc 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -266,12 +266,8 @@ static struct file_system_type ramfs_fs_type = { .fs_flags = FS_USERNS_MOUNT, }; -int __init init_ramfs_fs(void) +static int __init init_ramfs_fs(void) { - static unsigned long once; - - if (test_and_set_bit(0, &once)) - return 0; return register_filesystem(&ramfs_fs_type); } fs_initcall(init_ramfs_fs); diff --git a/fs/super.c b/fs/super.c index 2739f57515f8..113c58f19425 100644 --- a/fs/super.c +++ b/fs/super.c @@ -476,6 +476,17 @@ void generic_shutdown_super(struct super_block *sb) EXPORT_SYMBOL(generic_shutdown_super); +bool mount_capable(struct fs_context *fc) +{ + struct user_namespace *user_ns = fc->global ? &init_user_ns + : fc->user_ns; + + if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) + return capable(CAP_SYS_ADMIN); + else + return ns_capable(user_ns, CAP_SYS_ADMIN); +} + /** * sget_fc - Find or create a superblock * @fc: Filesystem context. @@ -503,20 +514,6 @@ struct super_block *sget_fc(struct fs_context *fc, struct user_namespace *user_ns = fc->global ? &init_user_ns : fc->user_ns; int err; - if (!(fc->sb_flags & SB_KERNMOUNT) && - fc->purpose != FS_CONTEXT_FOR_SUBMOUNT) { - /* Don't allow mounting unless the caller has CAP_SYS_ADMIN - * over the namespace. - */ - if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) { - if (!capable(CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); - } else { - if (!ns_capable(fc->user_ns, CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); - } - } - retry: spin_lock(&sb_lock); if (test) { @@ -543,6 +540,7 @@ retry: } fc->s_fs_info = NULL; s->s_type = fc->fs_type; + s->s_iflags |= fc->s_iflags; 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); @@ -565,28 +563,31 @@ share_extant_sb: EXPORT_SYMBOL(sget_fc); /** - * sget_userns - find or create a superblock - * @type: filesystem type superblock should belong to - * @test: comparison callback - * @set: setup callback - * @flags: mount flags - * @user_ns: User namespace for the super_block - * @data: argument to each of them + * sget - find or create a superblock + * @type: filesystem type superblock should belong to + * @test: comparison callback + * @set: setup callback + * @flags: mount flags + * @data: argument to each of them */ -struct super_block *sget_userns(struct file_system_type *type, +struct super_block *sget(struct file_system_type *type, int (*test)(struct super_block *,void *), int (*set)(struct super_block *,void *), - int flags, struct user_namespace *user_ns, + int flags, void *data) { + struct user_namespace *user_ns = current_user_ns(); struct super_block *s = NULL; struct super_block *old; int err; - if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) && - !(type->fs_flags & FS_USERNS_MOUNT) && - !capable(CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); + /* We don't yet pass the user namespace of the parent + * mount through to here so always use &init_user_ns + * until that changes. + */ + if (flags & SB_SUBMOUNT) + user_ns = &init_user_ns; + retry: spin_lock(&sb_lock); if (test) { @@ -627,39 +628,6 @@ retry: register_shrinker_prepared(&s->s_shrink); return s; } - -EXPORT_SYMBOL(sget_userns); - -/** - * sget - find or create a superblock - * @type: filesystem type superblock should belong to - * @test: comparison callback - * @set: setup callback - * @flags: mount flags - * @data: argument to each of them - */ -struct super_block *sget(struct file_system_type *type, - int (*test)(struct super_block *,void *), - int (*set)(struct super_block *,void *), - int flags, - void *data) -{ - struct user_namespace *user_ns = current_user_ns(); - - /* We don't yet pass the user namespace of the parent - * mount through to here so always use &init_user_ns - * until that changes. - */ - if (flags & SB_SUBMOUNT) - user_ns = &init_user_ns; - - /* Ensure the requestor has permissions over the target filesystem */ - if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); - - return sget_userns(type, test, set, flags, user_ns, data); -} - EXPORT_SYMBOL(sget); void drop_super(struct super_block *sb) @@ -1147,50 +1115,6 @@ void kill_litter_super(struct super_block *sb) } EXPORT_SYMBOL(kill_litter_super); -static int ns_test_super(struct super_block *sb, void *data) -{ - return sb->s_fs_info == data; -} - -static int ns_set_super(struct super_block *sb, void *data) -{ - sb->s_fs_info = data; - return set_anon_super(sb, NULL); -} - -struct dentry *mount_ns(struct file_system_type *fs_type, - int flags, void *data, void *ns, struct user_namespace *user_ns, - int (*fill_super)(struct super_block *, void *, int)) -{ - struct super_block *sb; - - /* Don't allow mounting unless the caller has CAP_SYS_ADMIN - * over the namespace. - */ - if (!(flags & SB_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); - - sb = sget_userns(fs_type, ns_test_super, ns_set_super, flags, - user_ns, ns); - if (IS_ERR(sb)) - return ERR_CAST(sb); - - if (!sb->s_root) { - int err; - err = fill_super(sb, data, flags & SB_SILENT ? 1 : 0); - if (err) { - deactivate_locked_super(sb); - return ERR_PTR(err); - } - - sb->s_flags |= SB_ACTIVE; - } - - return dget(sb->s_root); -} - -EXPORT_SYMBOL(mount_ns); - int set_anon_super_fc(struct super_block *sb, struct fs_context *fc) { return set_anon_super(sb, NULL); @@ -1274,6 +1198,22 @@ int vfs_get_super(struct fs_context *fc, } EXPORT_SYMBOL(vfs_get_super); +int get_tree_nodev(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc)) +{ + return vfs_get_super(fc, vfs_get_independent_super, fill_super); +} +EXPORT_SYMBOL(get_tree_nodev); + +int get_tree_single(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc)) +{ + return vfs_get_super(fc, vfs_get_single_super, fill_super); +} +EXPORT_SYMBOL(get_tree_single); + #ifdef CONFIG_BLOCK static int set_bdev_super(struct super_block *s, void *data) { diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 1b56686ab178..db81cfbab9d6 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -72,8 +72,7 @@ static int sysfs_init_fs_context(struct fs_context *fc) fc->fs_private = kfc; fc->ops = &sysfs_fs_context_ops; if (netns) { - if (fc->user_ns) - put_user_ns(fc->user_ns); + put_user_ns(fc->user_ns); fc->user_ns = get_user_ns(netns->user_ns); } fc->global = true; |