]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vfs: syscall: Add fsmount() to create a mount for a superblock
authorDavid Howells <dhowells@redhat.com>
Thu, 1 Nov 2018 23:36:14 +0000 (23:36 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 20 Mar 2019 22:49:06 +0000 (18:49 -0400)
Provide a system call by which a filesystem opened with fsopen() and
configured by a series of fsconfig() calls can have a detached mount object
created for it.  This mount object can then be attached to the VFS mount
hierarchy using move_mount() by passing the returned file descriptor as the
from directory fd.

The system call looks like:

int mfd = fsmount(int fsfd, unsigned int flags,
  unsigned int attr_flags);

where fsfd is the file descriptor returned by fsopen().  flags can be 0 or
FSMOUNT_CLOEXEC.  attr_flags is a bitwise-OR of the following flags:

MOUNT_ATTR_RDONLY Mount read-only
MOUNT_ATTR_NOSUID Ignore suid and sgid bits
MOUNT_ATTR_NODEV Disallow access to device special files
MOUNT_ATTR_NOEXEC Disallow program execution
MOUNT_ATTR__ATIME Setting on how atime should be updated
MOUNT_ATTR_RELATIME - Update atime relative to mtime/ctime
MOUNT_ATTR_NOATIME - Do not update access times
MOUNT_ATTR_STRICTATIME - Always perform atime updates
MOUNT_ATTR_NODIRATIME Do not update directory access times

In the event that fsmount() fails, it may be possible to get an error
message by calling read() on fsfd.  If no message is available, ENODATA
will be reported.

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/namespace.c
include/linux/syscalls.h
include/uapi/linux/mount.h

index 786728143205218e135fab4b780b7b036b64c595..5b5c9189c507dd4382bff257fb3587ce18e4e7f6 100644 (file)
 388    i386    move_mount              sys_move_mount                  __ia32_sys_move_mount
 389    i386    fsopen                  sys_fsopen                      __ia32_sys_fsopen
 390    i386    fsconfig                sys_fsconfig                    __ia32_sys_fsconfig
-# don't use numbers 391 through 392, add new calls at the end
+391    i386    fsmount                 sys_fsmount                     __ia32_sys_fsmount
+# don't use number 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 7039a809d37d10562a53bebdf395e66b59c6d394..984ad594bb2bb7fd0fad5931eb0d383861a0e374 100644 (file)
 336    common  move_mount              __x64_sys_move_mount
 337    common  fsopen                  __x64_sys_fsopen
 338    common  fsconfig                __x64_sys_fsconfig
+339    common  fsmount                 __x64_sys_fsmount
 # 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 1e72d19fa4f85a2b3c7a643d32a7d721c1623101..3357c3d65475a722c345ace8e95552055dc35480 100644 (file)
@@ -3334,9 +3334,149 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 }
 
 /*
- * Move a mount from one place to another.
- * In combination with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be
- * used to copy a mount subtree.
+ * Create a kernel mount representation for a new, prepared superblock
+ * (specified by fs_fd) and attach to an open_tree-like file descriptor.
+ */
+SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
+               unsigned int, attr_flags)
+{
+       struct mnt_namespace *ns;
+       struct fs_context *fc;
+       struct file *file;
+       struct path newmount;
+       struct mount *mnt;
+       struct fd f;
+       unsigned int mnt_flags = 0;
+       long ret;
+
+       if (!may_mount())
+               return -EPERM;
+
+       if ((flags & ~(FSMOUNT_CLOEXEC)) != 0)
+               return -EINVAL;
+
+       if (attr_flags & ~(MOUNT_ATTR_RDONLY |
+                          MOUNT_ATTR_NOSUID |
+                          MOUNT_ATTR_NODEV |
+                          MOUNT_ATTR_NOEXEC |
+                          MOUNT_ATTR__ATIME |
+                          MOUNT_ATTR_NODIRATIME))
+               return -EINVAL;
+
+       if (attr_flags & MOUNT_ATTR_RDONLY)
+               mnt_flags |= MNT_READONLY;
+       if (attr_flags & MOUNT_ATTR_NOSUID)
+               mnt_flags |= MNT_NOSUID;
+       if (attr_flags & MOUNT_ATTR_NODEV)
+               mnt_flags |= MNT_NODEV;
+       if (attr_flags & MOUNT_ATTR_NOEXEC)
+               mnt_flags |= MNT_NOEXEC;
+       if (attr_flags & MOUNT_ATTR_NODIRATIME)
+               mnt_flags |= MNT_NODIRATIME;
+
+       switch (attr_flags & MOUNT_ATTR__ATIME) {
+       case MOUNT_ATTR_STRICTATIME:
+               break;
+       case MOUNT_ATTR_NOATIME:
+               mnt_flags |= MNT_NOATIME;
+               break;
+       case MOUNT_ATTR_RELATIME:
+               mnt_flags |= MNT_RELATIME;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       f = fdget(fs_fd);
+       if (!f.file)
+               return -EBADF;
+
+       ret = -EINVAL;
+       if (f.file->f_op != &fscontext_fops)
+               goto err_fsfd;
+
+       fc = f.file->private_data;
+
+       ret = mutex_lock_interruptible(&fc->uapi_mutex);
+       if (ret < 0)
+               goto err_fsfd;
+
+       /* There must be a valid superblock or we can't mount it */
+       ret = -EINVAL;
+       if (!fc->root)
+               goto err_unlock;
+
+       ret = -EPERM;
+       if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) {
+               pr_warn("VFS: Mount too revealing\n");
+               goto err_unlock;
+       }
+
+       ret = -EBUSY;
+       if (fc->phase != FS_CONTEXT_AWAITING_MOUNT)
+               goto err_unlock;
+
+       ret = -EPERM;
+       if ((fc->sb_flags & SB_MANDLOCK) && !may_mandlock())
+               goto err_unlock;
+
+       newmount.mnt = vfs_create_mount(fc);
+       if (IS_ERR(newmount.mnt)) {
+               ret = PTR_ERR(newmount.mnt);
+               goto err_unlock;
+       }
+       newmount.dentry = dget(fc->root);
+       newmount.mnt->mnt_flags = mnt_flags;
+
+       /* We've done the mount bit - now move the file context into more or
+        * less the same state as if we'd done an fspick().  We don't want to
+        * do any memory allocation or anything like that at this point as we
+        * don't want to have to handle any errors incurred.
+        */
+       vfs_clean_context(fc);
+
+       ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true);
+       if (IS_ERR(ns)) {
+               ret = PTR_ERR(ns);
+               goto err_path;
+       }
+       mnt = real_mount(newmount.mnt);
+       mnt->mnt_ns = ns;
+       ns->root = mnt;
+       ns->mounts = 1;
+       list_add(&mnt->mnt_list, &ns->list);
+
+       /* Attach to an apparent O_PATH fd with a note that we need to unmount
+        * it, not just simply put it.
+        */
+       file = dentry_open(&newmount, O_PATH, fc->cred);
+       if (IS_ERR(file)) {
+               dissolve_on_fput(newmount.mnt);
+               ret = PTR_ERR(file);
+               goto err_path;
+       }
+       file->f_mode |= FMODE_NEED_UNMOUNT;
+
+       ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0);
+       if (ret >= 0)
+               fd_install(ret, file);
+       else
+               fput(file);
+
+err_path:
+       path_put(&newmount);
+err_unlock:
+       mutex_unlock(&fc->uapi_mutex);
+err_fsfd:
+       fdput(f);
+       return ret;
+}
+
+/*
+ * Move a mount from one place to another.  In combination with
+ * fsopen()/fsmount() this is used to install a new mount and in combination
+ * with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be used to copy
+ * a mount subtree.
  *
  * Note the flags value is a combination of MOVE_MOUNT_* flags.
  */
index 925f9dfc356b18bf0477a6441a15679727e9cc25..0e697f595278948d39fd3f0ebac27b3ea3cd7934 100644 (file)
@@ -992,6 +992,7 @@ asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
 asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags);
 asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key,
                             const void __user *value, int aux);
+asmlinkage long sys_fsmount(int fs_fd, unsigned int flags, unsigned int ms_flags);
 asmlinkage long sys_pidfd_send_signal(int pidfd, int sig,
                                       siginfo_t __user *info,
                                       unsigned int flags);
index 4b90ba9d1770d3d71e0045a668282b89ab141da1..3888d3b91dc5035f86f3575e7760f387db1aa299 100644 (file)
@@ -91,4 +91,22 @@ enum fsconfig_command {
        FSCONFIG_CMD_RECONFIGURE = 7,   /* Invoke superblock reconfiguration */
 };
 
+/*
+ * fsmount() flags.
+ */
+#define FSMOUNT_CLOEXEC                0x00000001
+
+/*
+ * Mount attributes.
+ */
+#define MOUNT_ATTR_RDONLY      0x00000001 /* Mount read-only */
+#define MOUNT_ATTR_NOSUID      0x00000002 /* Ignore suid and sgid bits */
+#define MOUNT_ATTR_NODEV       0x00000004 /* Disallow access to device special files */
+#define MOUNT_ATTR_NOEXEC      0x00000008 /* Disallow program execution */
+#define MOUNT_ATTR__ATIME      0x00000070 /* Setting on how atime should be updated */
+#define MOUNT_ATTR_RELATIME    0x00000000 /* - Update atime relative to mtime/ctime. */
+#define MOUNT_ATTR_NOATIME     0x00000010 /* - Do not update access times. */
+#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
+#define MOUNT_ATTR_NODIRATIME  0x00000080 /* Do not update directory access times */
+
 #endif /* _UAPI_LINUX_MOUNT_H */