vfs: syscall: Add fsopen() to prepare for superblock creation
authorDavid Howells <dhowells@redhat.com>
Thu, 1 Nov 2018 23:33:31 +0000 (23:33 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 20 Mar 2019 22:49:06 +0000 (18:49 -0400)
Provide an fsopen() system call that starts the process of preparing to
create a superblock that will then be mountable, using an fd as a context
handle.  fsopen() is given the name of the filesystem that will be used:

int mfd = fsopen(const char *fsname, unsigned int flags);

where flags can be 0 or FSOPEN_CLOEXEC.

For example:

sfd = fsopen("ext4", FSOPEN_CLOEXEC);
fsconfig(sfd, FSCONFIG_SET_PATH, "source", "/dev/sda1", AT_FDCWD);
fsconfig(sfd, FSCONFIG_SET_FLAG, "noatime", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_FLAG, "acl", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_FLAG, "user_xattr", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_STRING, "sb", "1", 0);
fsconfig(sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
fsinfo(sfd, NULL, ...); // query new superblock attributes
mfd = fsmount(sfd, FSMOUNT_CLOEXEC, MS_RELATIME);
move_mount(mfd, "", sfd, AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);

sfd = fsopen("afs", -1);
fsconfig(fd, FSCONFIG_SET_STRING, "source",
 "#grand.central.org:root.cell", 0);
fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mfd = fsmount(sfd, 0, MS_NODEV);
move_mount(mfd, "", sfd, AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);

If an error is reported at any step, an error message may be available to be
read() back (ENODATA will be reported if there isn't an error available) in
the form:

"e <subsys>:<problem>"
"e SELinux:Mount on mountpoint not permitted"

Once fsmount() has been called, further fsconfig() calls will incur EBUSY,
even if the fsmount() fails.  read() is still possible to retrieve error
information.

The fsopen() syscall creates a mount context and hangs it of the fd that it
returns.

Netlink is not used because it is optional and would make the core VFS
dependent on the networking layer and also potentially add network
namespace issues.

Note that, for the moment, the caller must have SYS_CAP_ADMIN to use
fsopen().

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
fs/Makefile
fs/fs_context.c
fs/fsopen.c [new file with mode: 0644]
include/linux/fs_context.h
include/linux/syscalls.h
include/uapi/linux/mount.h

index 0db9effb18d99e53cf4413c9e0ccb7b09eb58ae2..37fd1fc5396e66f054850a7684b6e531bf3c1cf8 100644 (file)
 386    i386    rseq                    sys_rseq                        __ia32_sys_rseq
 387    i386    open_tree               sys_open_tree                   __ia32_sys_open_tree
 388    i386    move_mount              sys_move_mount                  __ia32_sys_move_mount
-# don't use numbers 389 through 392, add new calls at the end
+389    i386    fsopen                  sys_fsopen                      __ia32_sys_fsopen
+# don't use numbers 390 through 392, add new calls at the end
 393    i386    semget                  sys_semget                      __ia32_sys_semget
 394    i386    semctl                  sys_semctl                      __ia32_compat_sys_semctl
 395    i386    shmget                  sys_shmget                      __ia32_sys_shmget
index 0440f0eefa02b69af5fd154ca4b50bfcc6ce11dd..511608a2161153b9690b6ed6357ecec1bb8a3c30 100644 (file)
 334    common  rseq                    __x64_sys_rseq
 335    common  open_tree               __x64_sys_open_tree
 336    common  move_mount              __x64_sys_move_mount
+337    common  fsopen                  __x64_sys_fsopen
 # don't use numbers 387 through 423, add new calls after the last
 # 'common' entry
 424    common  pidfd_send_signal       __x64_sys_pidfd_send_signal
index 35945f8139e61b3c12597211b0a47a011270925f..5a51bc2489ba7bb55bee0594ee70dc4beb7ed187 100644 (file)
@@ -13,7 +13,7 @@ obj-y :=      open.o read_write.o file_table.o super.o \
                seq_file.o xattr.o libfs.o fs-writeback.o \
                pnode.o splice.o sync.o utimes.o d_path.o \
                stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
-               fs_types.o fs_context.o fs_parser.o
+               fs_types.o fs_context.o fs_parser.o fsopen.o
 
 ifeq ($(CONFIG_BLOCK),y)
 obj-y +=       buffer.o block_dev.o direct-io.o mpage.o
index 87e3546b9a522dccb030dbc52f78071872e72404..eb806fae311718961a95432c1c7f308f9203eb9e 100644 (file)
@@ -271,6 +271,8 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
        fc->cred        = get_current_cred();
        fc->net_ns      = get_net(current->nsproxy->net_ns);
 
+       mutex_init(&fc->uapi_mutex);
+
        switch (purpose) {
        case FS_CONTEXT_FOR_MOUNT:
                fc->user_ns = get_user_ns(fc->cred->user_ns);
@@ -353,6 +355,8 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc)
        if (!fc)
                return ERR_PTR(-ENOMEM);
 
+       mutex_init(&fc->uapi_mutex);
+
        fc->fs_private  = NULL;
        fc->s_fs_info   = NULL;
        fc->source      = NULL;
diff --git a/fs/fsopen.c b/fs/fsopen.c
new file mode 100644 (file)
index 0000000..d256f1a
--- /dev/null
@@ -0,0 +1,88 @@
+/* Filesystem access-by-fd.
+ *
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/fs_context.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/security.h>
+#include <linux/anon_inodes.h>
+#include <linux/namei.h>
+#include <linux/file.h>
+#include <uapi/linux/mount.h>
+#include "mount.h"
+
+static int fscontext_release(struct inode *inode, struct file *file)
+{
+       struct fs_context *fc = file->private_data;
+
+       if (fc) {
+               file->private_data = NULL;
+               put_fs_context(fc);
+       }
+       return 0;
+}
+
+const struct file_operations fscontext_fops = {
+       .release        = fscontext_release,
+       .llseek         = no_llseek,
+};
+
+/*
+ * Attach a filesystem context to a file and an fd.
+ */
+static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
+{
+       int fd;
+
+       fd = anon_inode_getfd("fscontext", &fscontext_fops, fc,
+                             O_RDWR | o_flags);
+       if (fd < 0)
+               put_fs_context(fc);
+       return fd;
+}
+
+/*
+ * Open a filesystem by name so that it can be configured for mounting.
+ *
+ * We are allowed to specify a container in which the filesystem will be
+ * opened, thereby indicating which namespaces will be used (notably, which
+ * network namespace will be used for network filesystems).
+ */
+SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
+{
+       struct file_system_type *fs_type;
+       struct fs_context *fc;
+       const char *fs_name;
+
+       if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (flags & ~FSOPEN_CLOEXEC)
+               return -EINVAL;
+
+       fs_name = strndup_user(_fs_name, PAGE_SIZE);
+       if (IS_ERR(fs_name))
+               return PTR_ERR(fs_name);
+
+       fs_type = get_fs_type(fs_name);
+       kfree(fs_name);
+       if (!fs_type)
+               return -ENODEV;
+
+       fc = fs_context_for_mount(fs_type, 0);
+       put_filesystem(fs_type);
+       if (IS_ERR(fc))
+               return PTR_ERR(fc);
+
+       fc->phase = FS_CONTEXT_CREATE_PARAMS;
+       return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);
+}
index eaca452088fa53034b0e64335e7b72719294e9ac..7ab8b44fab3ef551581d5649ea96c2952caed839 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/security.h>
+#include <linux/mutex.h>
 
 struct cred;
 struct dentry;
@@ -34,6 +35,19 @@ enum fs_context_purpose {
        FS_CONTEXT_FOR_RECONFIGURE,     /* Superblock reconfiguration (remount) */
 };
 
+/*
+ * Userspace usage phase for fsopen/fspick.
+ */
+enum fs_context_phase {
+       FS_CONTEXT_CREATE_PARAMS,       /* Loading params for sb creation */
+       FS_CONTEXT_CREATING,            /* A superblock is being created */
+       FS_CONTEXT_AWAITING_MOUNT,      /* Superblock created, awaiting fsmount() */
+       FS_CONTEXT_AWAITING_RECONF,     /* Awaiting initialisation for reconfiguration */
+       FS_CONTEXT_RECONF_PARAMS,       /* Loading params for reconfiguration */
+       FS_CONTEXT_RECONFIGURING,       /* Reconfiguring the superblock */
+       FS_CONTEXT_FAILED,              /* Failed to correctly transition a context */
+};
+
 /*
  * Type of parameter value.
  */
@@ -74,6 +88,7 @@ struct fs_parameter {
  */
 struct fs_context {
        const struct fs_context_operations *ops;
+       struct mutex            uapi_mutex;     /* Userspace access mutex */
        struct file_system_type *fs_type;
        void                    *fs_private;    /* The filesystem's context */
        struct dentry           *root;          /* The root and superblock */
@@ -88,6 +103,7 @@ struct fs_context {
        unsigned int            sb_flags_mask;  /* Superblock flags that were changed */
        unsigned int            lsm_flags;      /* Information flags from the fs to the LSM */
        enum fs_context_purpose purpose:8;
+       enum fs_context_phase   phase:8;        /* The phase the context is in */
        bool                    need_free:1;    /* Need to call ops->free() */
        bool                    global:1;       /* Goes into &init_user_ns */
 };
index 84347fc0a1a71567cbd750e409e7dabb3a9438de..0c9bd5427e8f583323a71e267969533e55764589 100644 (file)
@@ -989,6 +989,7 @@ asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags);
 asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
                               int to_dfd, const char __user *to_path,
                               unsigned int ms_flags);
+asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags);
 asmlinkage long sys_pidfd_send_signal(int pidfd, int sig,
                                       siginfo_t __user *info,
                                       unsigned int flags);
index 3634e065836cb99e6c38b029d15d77c4ef10d602..7570df43d08fdfc097a011e9b67f09f48698987d 100644 (file)
@@ -72,4 +72,9 @@
 #define MOVE_MOUNT_T_EMPTY_PATH                0x00000040 /* Empty to path permitted */
 #define MOVE_MOUNT__MASK               0x00000077
 
+/*
+ * fsopen() flags.
+ */
+#define FSOPEN_CLOEXEC         0x00000001
+
 #endif /* _UAPI_LINUX_MOUNT_H */