Provide an fspick() system call that can be used to pick an existing
mountpoint into an fs_context which can thereafter be used to reconfigure a
superblock (equivalent of the superblock side of -o remount).

This looks like:

        int fd = fspick(AT_FDCWD, "/mnt",
                        FSPICK_CLOEXEC | FSPICK_NO_AUTOMOUNT);
        write(fd, "o intr");
        write(fd, "o noac");
        write(fd, "x reconfigure");

At the point of fspick being called, the file descriptor referring to the
filesystem context is in exactly the same state as the one that was created
by fsopen() after fsmount() has been successfully called.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 arch/x86/entry/syscalls/syscall_32.tbl |    1 
 arch/x86/entry/syscalls/syscall_64.tbl |    1 
 fs/fsopen.c                            |   94 +++++++++++++++++++++++++-------
 include/linux/syscalls.h               |    1 
 include/uapi/linux/fs.h                |    5 ++
 kernel/sys_ni.c                        |    1 
 6 files changed, 83 insertions(+), 20 deletions(-)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl 
b/arch/x86/entry/syscalls/syscall_32.tbl
index bdcb0c4a0491..b7e2adda092c 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -399,3 +399,4 @@
 385    i386    io_pgetevents           sys_io_pgetevents               
__ia32_compat_sys_io_pgetevents
 386    i386    fsopen                  sys_fsopen                      
__ia32_sys_fsopen
 387    i386    fsmount                 sys_fsmount                     
__ia32_sys_fsmount
+388    i386    fspick                  sys_fspick                      
__ia32_sys_fspick
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl 
b/arch/x86/entry/syscalls/syscall_64.tbl
index 7d932d3897fa..fd322986974b 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -344,6 +344,7 @@
 333    common  io_pgetevents           __x64_sys_io_pgetevents
 334    common  fsopen                  __x64_sys_fsopen
 335    common  fsmount                 __x64_sys_fsmount
+336    common  fspick                  __x64_sys_fspick
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/fsopen.c b/fs/fsopen.c
index 26565ddd7c9e..d69155b9303e 100644
--- a/fs/fsopen.c
+++ b/fs/fsopen.c
@@ -17,6 +17,7 @@
 #include <linux/magic.h>
 #include <linux/syscalls.h>
 #include <linux/security.h>
+#include <linux/namei.h>
 #include "mount.h"
 
 static struct vfsmount *fscontext_fs_mnt __read_mostly;
@@ -286,6 +287,36 @@ static int __init init_fscontext_fs(void)
 
 fs_initcall(init_fscontext_fs);
 
+/*
+ * Attach a filesystem context to a file and an fd.
+ */
+static int fsopen_create_fd(struct fs_context *fc, bool cloexec)
+{
+       struct file *file;
+       int ret;
+
+       file = create_fscontext_file(fc);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto err_fc;
+       }
+
+       ret = get_unused_fd_flags(cloexec);
+       if (ret < 0)
+               goto err_file;
+
+       fd_install(ret, file);
+       return ret;
+
+err_fc:
+       put_fs_context(fc);
+       goto err;
+err_file:
+       fput(file);
+err:
+       return ret;
+}
+
 /*
  * Open a filesystem by name so that it can be configured for mounting.
  *
@@ -298,9 +329,7 @@ SYSCALL_DEFINE5(fsopen, const char __user *, _fs_name, 
unsigned int, flags,
 {
        struct file_system_type *fs_type;
        struct fs_context *fc;
-       struct file *file;
        const char *fs_name;
-       int fd, ret;
 
        if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
                return -EPERM;
@@ -324,29 +353,54 @@ SYSCALL_DEFINE5(fsopen, const char __user *, _fs_name, 
unsigned int, flags,
 
        fc->phase = FS_CONTEXT_CREATE_PARAMS;
 
-       ret = -EOPNOTSUPP;
-       if (!fc->ops)
-               goto err_fc;
+       return fsopen_create_fd(fc, flags & FSOPEN_CLOEXEC);
+}
 
-       file = create_fscontext_file(fc);
-       if (IS_ERR(file)) {
-               ret = PTR_ERR(file);
-               goto err_fc;
-       }
+/*
+ * Pick a superblock into a context for reconfiguration.
+ */
+SYSCALL_DEFINE3(fspick, int, dfd, const char *, path, unsigned int, flags)
+{
+       struct fs_context *fc;
+       struct path target;
+       unsigned int lookup_flags;
+       int ret;
+
+       if ((flags & ~(FSPICK_CLOEXEC |
+                      FSPICK_SYMLINK_NOFOLLOW |
+                      FSPICK_NO_AUTOMOUNT |
+                      FSPICK_EMPTY_PATH)) != 0)
+               return -EINVAL;
 
-       ret = get_unused_fd_flags(flags & O_CLOEXEC);
+       lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+       if (flags & FSPICK_SYMLINK_NOFOLLOW)
+               lookup_flags &= ~LOOKUP_FOLLOW;
+       if (flags & FSPICK_NO_AUTOMOUNT)
+               lookup_flags &= ~LOOKUP_AUTOMOUNT;
+       if (flags & FSPICK_EMPTY_PATH)
+               lookup_flags |= LOOKUP_EMPTY;
+       ret = user_path_at(dfd, path, lookup_flags, &target);
        if (ret < 0)
-               goto err_file;
+               goto err;
+
+       ret = -EOPNOTSUPP;
+       if (!target.dentry->d_sb->s_op->reconfigure)
+               goto err;
+
+       fc = vfs_new_fs_context(target.dentry->d_sb->s_type, target.dentry,
+                               0, FS_CONTEXT_FOR_RECONFIGURE);
+       if (IS_ERR(fc)) {
+               ret = PTR_ERR(fc);
+               goto err_path;
+       }
 
-       fd = ret;
-       fd_install(fd, file);
-       return fd;
+       fc->phase = FS_CONTEXT_RECONF_PARAMS;
 
-err_file:
-       fput(file);
-       return ret;
+       path_put(&target);
+       return fsopen_create_fd(fc, flags & FSPICK_CLOEXEC);
 
-err_fc:
-       put_fs_context(fc);
+err_path:
+       path_put(&target);
+err:
        return ret;
 }
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 178370cad1dd..5130fd687a85 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -900,6 +900,7 @@ asmlinkage long sys_fsopen(const char *fs_name, unsigned 
int flags,
                           void *reserved3, void *reserved4, void *reserved5);
 asmlinkage long sys_fsmount(int fsfd, int dfd, const char *path, unsigned int 
at_flags,
                            unsigned int flags);
+asmlinkage long sys_fspick(int dfd, const char *path, unsigned int at_flags);
 
 
 /*
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index edb1983a9990..f3875a84349d 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -345,4 +345,9 @@ typedef int __bitwise __kernel_rwf_t;
 
 #define FSMOUNT_CLOEXEC                0x00000001
 
+#define FSPICK_CLOEXEC         0x00000001
+#define FSPICK_SYMLINK_NOFOLLOW        0x00000002
+#define FSPICK_NO_AUTOMOUNT    0x00000004
+#define FSPICK_EMPTY_PATH      0x00000008
+
 #endif /* _UAPI_LINUX_FS_H */
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 632a937ca09c..152fdc95d426 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -436,3 +436,4 @@ COND_SYSCALL(setuid16);
 /* fd-based mount */
 COND_SYSCALL(sys_fsopen);
 COND_SYSCALL(sys_fsmount);
+COND_SYSCALL(sys_fspick);

Reply via email to