Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1638938 > unrolled thread

[RFC][PATCH 00/14] VFS: Introduce superblock configuration context

Started byDavid Howells <dhowells@redhat.com>
First post2017-05-10 18:20 +0200
Last post2017-05-11 10:20 +0200
Articles 18 — 5 participants

Back to article view | Back to linux.kernel


Contents

  [RFC][PATCH 00/14] VFS: Introduce superblock configuration context David Howells <dhowells@redhat.com> - 2017-05-10 18:20 +0200
    [PATCH 01/14] Provide a function to create a NUL-terminated string  from unterminated data David Howells <dhowells@redhat.com> - 2017-05-10 18:20 +0200
    [PATCH 07/14] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
      Re: [PATCH 07/14] Implement fsopen() to prepare for a mount Sargun Dhillon <sargun@sargun.me> - 2017-05-11 00:10 +0200
        Re: [PATCH 07/14] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-11 16:40 +0200
      Re: [PATCH 07/14] Implement fsopen() to prepare for a mount Jeff Layton <jlayton@redhat.com> - 2017-05-11 16:40 +0200
    [PATCH 08/14] Implement fsmount() to effect a pre-configured mount David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 11/14] proc: Add superblock config support to procfs David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 13/14] Support legacy filesystems David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 03/14] VFS: Make get_mnt_ns() return the namespace David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 05/14] VFS: Provide empty name qstr David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 02/14] Clean up whitespace in fs/namespace.c David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 10/14] procfs: Move proc_fill_super() to fs/proc/root.c David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 09/14] Sample program for driving fsopen/fsmount David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    [PATCH 04/14] VFS: Make get_filesystem() return the affected  filesystem David Howells <dhowells@redhat.com> - 2017-05-10 18:30 +0200
    Re: [PATCH 06/14] VFS: Introduce a superblock configuration context Al Viro <viro@ZenIV.linux.org.uk> - 2017-05-11 09:30 +0200
    Re: [PATCH 14/14] Add commands to create or update a superblock Al Viro <viro@ZenIV.linux.org.uk> - 2017-05-11 09:40 +0200
      Re: [PATCH 14/14] Add commands to create or update a superblock Miklos Szeredi <mszeredi@redhat.com> - 2017-05-11 10:20 +0200

#1638938 — [RFC][PATCH 00/14] VFS: Introduce superblock configuration context

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:20 +0200
Subject[RFC][PATCH 00/14] VFS: Introduce superblock configuration context
Message-ID<tFCvf-2Uw-1@gated-at.bofh.it>
Here are a set of patches to create a superblock configuration context
prior to setting up a new mount, populating it with the parsed
options/binary data, creating the superblock and then effecting the mount.

This allows namespaces and other information to be conveyed through the
mount procedure.  It also allows extra error information to be returned
(so many things can go wrong during a mount that a small integer isn't
really sufficient to convey the issue).

This also allows Miklós Szeredi's idea of doing:

	fd = fsopen("nfs");
	write(fd, "option=val", ...);
	fsmount(fd, "/mnt");

that he presented at LSF-2017 to be implemented (see the relevant patches
in the series), to which I can add:

	read(fd, error_buffer, ...);

to read back any error message.  I didn't use netlink as that would make it
depend on CONFIG_NET and would introduce network namespacing issues.

I've implemented mount context handling for procfs and nfs.

Significant changes:

 (*) Renamed struct mount_context to struct sb_config and amended various
     variable names.

 (*) Split the sb_config stuff out into its own header.

 (*) Support non-context aware filesystems through a special set of
     sb_config operations.

 (*) Stored the created superblock and root dentry into the sb_config after
     creation rather than directly into a vfsmount.  This allows some
     arguments to be removed to various NFS functions.

 (*) Added an explicit superblock-creation step.  This allows a created
     superblock to then be mounted multiple times.

 (*) Added a flag to say that the sb_config is degraded and cannot have
     another go at having a superblock creation whilst getting rid of the
     one that says it's already mounted.

Further developments:

 (*) fsmount() needs to take AT_EMPTY_PATH and similar.

 (*) Implement sb reconfiguration (for now it returns ENOANO).

 (*) Implement mount context support in more filesystems, ext4 being next
     on my list.

 (*) Move the walk-from-root stuff that nfs has to generic code so that you
     can do something akin to:

	mount /dev/sda1:/foo/bar /mnt

     See nfs_follow_remote_path() and mount_subtree().  This is slightly
     tricky in NFS as we have to prevent referral loops.

 (*) Move the pid_ns pointer from struct mount_context to struct
     proc_mount_context as I'm not sure it's necessary for anything other
     than procfs.

 (*) Work out how to get at the error message incurred by submounts
     encountered during nfs_follow_remote_path().

     Should the error message be moved to task_struct and made more
     general, perhaps retrieved with a prctl() function?

 (*) Clean up/consolidate the security functions.  Possibly add a
     validation hook to be called at the same time as the mount context
     validate op.

The patches can be found here also:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=mount-context

David
---
David Howells (14):
      Provide a function to create a NUL-terminated string from unterminated data
      Clean up whitespace in fs/namespace.c
      VFS: Make get_mnt_ns() return the namespace
      VFS: Make get_filesystem() return the affected filesystem
      VFS: Provide empty name qstr
      VFS: Introduce a superblock configuration context
      Implement fsopen() to prepare for a mount
      Implement fsmount() to effect a pre-configured mount
      Sample program for driving fsopen/fsmount
      procfs: Move proc_fill_super() to fs/proc/root.c
      proc: Add superblock config support to procfs
      NFS: Add mount context support.
      Support legacy filesystems
      Add commands to create or update a superblock


 Documentation/filesystems/mounting.txt |  463 ++++++++
 arch/x86/entry/syscalls/syscall_32.tbl |    2 
 arch/x86/entry/syscalls/syscall_64.tbl |    2 
 fs/Makefile                            |    3 
 fs/dcache.c                            |    8 
 fs/filesystems.c                       |    3 
 fs/fsopen.c                            |  302 +++++
 fs/gfs2/dir.c                          |    3 
 fs/internal.h                          |    4 
 fs/libfs.c                             |   17 
 fs/mount.h                             |    3 
 fs/namei.c                             |    3 
 fs/namespace.c                         |  495 +++++++--
 fs/nfs/Makefile                        |    2 
 fs/nfs/client.c                        |   74 +
 fs/nfs/getroot.c                       |   75 +
 fs/nfs/internal.h                      |  142 +--
 fs/nfs/mount.c                         | 1500 +++++++++++++++++++++++++++
 fs/nfs/namespace.c                     |   76 +
 fs/nfs/nfs3_fs.h                       |    2 
 fs/nfs/nfs3client.c                    |    6 
 fs/nfs/nfs3proc.c                      |    2 
 fs/nfs/nfs4_fs.h                       |    4 
 fs/nfs/nfs4client.c                    |   82 +
 fs/nfs/nfs4namespace.c                 |  208 ++--
 fs/nfs/nfs4proc.c                      |    3 
 fs/nfs/nfs4super.c                     |  223 ++--
 fs/nfs/proc.c                          |    2 
 fs/nfs/super.c                         | 1791 ++------------------------------
 fs/nsfs.c                              |    3 
 fs/pipe.c                              |    3 
 fs/proc/inode.c                        |   50 -
 fs/proc/internal.h                     |    6 
 fs/proc/root.c                         |  197 +++-
 fs/sb_config.c                         |  532 ++++++++++
 fs/super.c                             |  109 +-
 include/linux/dcache.h                 |    5 
 include/linux/fs.h                     |   13 
 include/linux/lsm_hooks.h              |   47 +
 include/linux/mount.h                  |    4 
 include/linux/nfs_xdr.h                |    7 
 include/linux/sb_config.h              |  104 ++
 include/linux/security.h               |   40 +
 include/linux/string.h                 |    1 
 include/linux/syscalls.h               |    2 
 include/uapi/linux/magic.h             |    1 
 kernel/sys_ni.c                        |    4 
 mm/util.c                              |   24 
 samples/fsmount/test-fsmount.c         |   78 +
 security/security.c                    |   45 +
 security/selinux/hooks.c               |  202 +++-
 51 files changed, 4592 insertions(+), 2385 deletions(-)
 create mode 100644 Documentation/filesystems/mounting.txt
 create mode 100644 fs/fsopen.c
 create mode 100644 fs/nfs/mount.c
 create mode 100644 fs/sb_config.c
 create mode 100644 include/linux/sb_config.h
 create mode 100644 samples/fsmount/test-fsmount.c

[toc] | [next] | [standalone]


#1638941 — [PATCH 01/14] Provide a function to create a NUL-terminated string from unterminated data

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:20 +0200
Subject[PATCH 01/14] Provide a function to create a NUL-terminated string from unterminated data
Message-ID<tFCvf-2Uw-7@gated-at.bofh.it>
In reply to#1638938
Provide a function, kmemdup_nul(), that will create a NUL-terminated string
from an unterminated character array where the length is known in advance.

This is better than kstrndup() in situations where we already know the
string length as the strnlen() in kstrndup() is superfluous.

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

 include/linux/string.h |    1 +
 mm/util.c              |   24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/include/linux/string.h b/include/linux/string.h
index 26b6f6a66f83..0c88c0a1a72b 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -123,6 +123,7 @@ extern char *kstrdup(const char *s, gfp_t gfp) __malloc;
 extern const char *kstrdup_const(const char *s, gfp_t gfp);
 extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
 extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
+extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
 
 extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
 extern void argv_free(char **argv);
diff --git a/mm/util.c b/mm/util.c
index 656dc5e37a87..be68664bfb73 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -83,6 +83,8 @@ EXPORT_SYMBOL(kstrdup_const);
  * @s: the string to duplicate
  * @max: read at most @max chars from @s
  * @gfp: the GFP mask used in the kmalloc() call when allocating memory
+ *
+ * Note: Use kmemdup_nul() instead if the size is known exactly.
  */
 char *kstrndup(const char *s, size_t max, gfp_t gfp)
 {
@@ -121,6 +123,28 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)
 EXPORT_SYMBOL(kmemdup);
 
 /**
+ * kmemdup_nul - Create a NUL-terminated string from unterminated data
+ * @s: The data to stringify
+ * @len: The size of the data
+ * @gfp: the GFP mask used in the kmalloc() call when allocating memory
+ */
+char *kmemdup_nul(const char *s, size_t len, gfp_t gfp)
+{
+	char *buf;
+
+	if (!s)
+		return NULL;
+
+	buf = kmalloc_track_caller(len + 1, gfp);
+	if (buf) {
+		memcpy(buf, s, len);
+		buf[len] = '\0';
+	}
+	return buf;
+}
+EXPORT_SYMBOL(kmemdup_nul);
+
+/**
  * memdup_user - duplicate memory region from user space
  *
  * @src: source address in user space

[toc] | [prev] | [next] | [standalone]


#1638943 — [PATCH 07/14] Implement fsopen() to prepare for a mount

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 07/14] Implement fsopen() to prepare for a mount
Message-ID<tFCEV-2YC-1@gated-at.bofh.it>
In reply to#1638938
Provide an fsopen() system call that starts the process of preparing to
mount, 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, int reserved,
			 int open_flags);

where reserved should be -1 for the moment (it will be used to pass the
namespace information in future) and open_flags can be 0 or O_CLOEXEC.

For example:

	mfd = fsopen("ext4", -1, O_CLOEXEC);
	write(mfd, "s /dev/sdb1"); // note I'm ignoring write's length arg
	write(mfd, "o noatime");
	write(mfd, "o acl");
	write(mfd, "o user_attr");
	write(mfd, "o iversion");
	write(mfd, "o ");
	write(mfd, "r /my/container"); // root inside the fs
	fsmount(mfd, container_fd, "/mnt", AT_NO_FOLLOW);

	mfd = fsopen("afs", -1);
	write(mfd, "s %grand.central.org:root.cell");
	write(mfd, "o cell=grand.central.org");
	write(mfd, "r /");
	fsmount(mfd, AT_FDCWD, "/mnt", 0);

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 write() 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.

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

 arch/x86/entry/syscalls/syscall_32.tbl |    1 
 arch/x86/entry/syscalls/syscall_64.tbl |    1 
 fs/Makefile                            |    2 
 fs/fsopen.c                            |  279 ++++++++++++++++++++++++++++++++
 include/linux/syscalls.h               |    1 
 include/uapi/linux/magic.h             |    1 
 kernel/sys_ni.c                        |    3 
 7 files changed, 287 insertions(+), 1 deletion(-)
 create mode 100644 fs/fsopen.c

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 448ac2161112..9bf8d4c62f85 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -391,3 +391,4 @@
 382	i386	pkey_free		sys_pkey_free
 383	i386	statx			sys_statx
 384	i386	arch_prctl		sys_arch_prctl			compat_sys_arch_prctl
+385	i386	fsopen			sys_fsopen
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 5aef183e2f85..9b198c5fc412 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -339,6 +339,7 @@
 330	common	pkey_alloc		sys_pkey_alloc
 331	common	pkey_free		sys_pkey_free
 332	common	statx			sys_statx
+333	common	fsopen			sys_fsopen
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/Makefile b/fs/Makefile
index 8f5142525866..b8fcf48b0400 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -12,7 +12,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 \
 		stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
-		sb_config.o
+		sb_config.o fsopen.o
 
 ifeq ($(CONFIG_BLOCK),y)
 obj-y +=	buffer.o block_dev.o direct-io.o mpage.o
diff --git a/fs/fsopen.c b/fs/fsopen.c
new file mode 100644
index 000000000000..a4e9d5a7ce2b
--- /dev/null
+++ b/fs/fsopen.c
@@ -0,0 +1,279 @@
+/* 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/sb_config.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/magic.h>
+#include <linux/syscalls.h>
+
+static struct vfsmount *fs_fs_mnt __read_mostly;
+
+static int fs_fs_release(struct inode *inode, struct file *file)
+{
+	struct sb_config *sc = file->private_data;
+
+	file->private_data = NULL;
+
+	put_sb_config(sc);
+	return 0;
+}
+
+/*
+ * Read any error message back from the fd.  Will be prefixed by "e ".
+ */
+static ssize_t fs_fs_read(struct file *file, char __user *_buf, size_t len, loff_t *pos)
+{
+	struct sb_config *sc = file->private_data;
+	const char *msg;
+	size_t mlen;
+
+	msg = READ_ONCE(sc->error_msg);
+	if (!msg)
+		return -ENODATA;
+
+	mlen = strlen(msg);
+	if (mlen + 2 > len)
+		return -ETOOSMALL;
+	if (copy_to_user(_buf, "e ", 2) != 0 ||
+	    copy_to_user(_buf + 2, msg, mlen) != 0)
+		return -EFAULT;
+	return mlen + 2;
+}
+
+/*
+ * Userspace writes configuration data to the fd and we parse it here.  For the
+ * moment, we assume a single option per write.  Each line written is of the form
+ *
+ *	<option_type><space><stuff...>
+ *
+ *	d /dev/sda1				-- Device name
+ *	o noatime				-- Option without value
+ *	o cell=grand.central.org		-- Option with value
+ *	r /					-- Dir within device to mount
+ */
+static ssize_t fs_fs_write(struct file *file,
+			   const char __user *_buf, size_t len, loff_t *pos)
+{
+	struct sb_config *sc = file->private_data;
+	struct inode *inode = file_inode(file);
+	char opt[2], *data;
+	ssize_t ret;
+
+	if (len < 3 || len > 4095)
+		return -EINVAL;
+
+	if (copy_from_user(opt, _buf, 2) != 0)
+		return -EFAULT;
+	switch (opt[0]) {
+	case 's':
+	case 'o':
+		break;
+	default:
+		return sb_cfg_inval(sc, "VFS: Unsupported write spec");
+	}
+	if (opt[1] != ' ')
+		return sb_cfg_inval(sc, "VFS: Unsupported write spec");
+
+	data = memdup_user_nul(_buf + 2, len - 2);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	/* From this point onwards we need to lock the fd against someone
+	 * trying to mount it.
+	 */
+	ret = inode_lock_killable(inode);
+	if (ret < 0)
+		goto err_free;
+
+	ret = -EBUSY;
+	if (sc->mounted)
+		goto err_unlock;
+
+	ret = -EINVAL;
+	switch (opt[0]) {
+	case 's':
+		if (sc->device)
+			goto err_unlock;
+		sc->device = data;
+		data = NULL;
+		break;
+
+	case 'o':
+		ret = vfs_parse_mount_option(sc, data);
+		if (ret < 0)
+			goto err_unlock;
+		break;
+
+	default:
+		goto err_unlock;
+	}
+
+	ret = len;
+err_unlock:
+	inode_unlock(inode);
+err_free:
+	kfree(data);
+	return ret;
+}
+
+const struct file_operations fs_fs_fops = {
+	.read		= fs_fs_read,
+	.write		= fs_fs_write,
+	.release	= fs_fs_release,
+	.llseek		= no_llseek,
+};
+
+/*
+ * Indicate the name we want to display the filesystem file as.
+ */
+static char *fs_fs_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+	return dynamic_dname(dentry, buffer, buflen, "fs:[%lu]",
+			     d_inode(dentry)->i_ino);
+}
+
+static const struct dentry_operations fs_fs_dentry_operations = {
+	.d_dname	= fs_fs_dname,
+};
+
+/*
+ * Create a file that can be used to configure a new mount.
+ */
+static struct file *create_fs_file(struct sb_config *sc)
+{
+	struct inode *inode;
+	struct file *f;
+	struct path path;
+	int ret;
+
+	inode = alloc_anon_inode(fs_fs_mnt->mnt_sb);
+	if (!inode)
+		return ERR_PTR(-ENFILE);
+	inode->i_fop = &fs_fs_fops;
+
+	ret = -ENOMEM;
+	path.dentry = d_alloc_pseudo(fs_fs_mnt->mnt_sb, &empty_name);
+	if (!path.dentry)
+		goto err_inode;
+	path.mnt = mntget(fs_fs_mnt);
+
+	d_instantiate(path.dentry, inode);
+
+	f = alloc_file(&path, FMODE_READ | FMODE_WRITE, &fs_fs_fops);
+	if (IS_ERR(f)) {
+		ret = PTR_ERR(f);
+		goto err_file;
+	}
+
+	f->private_data = sc;
+	return f;
+
+err_file:
+	path_put(&path);
+	return ERR_PTR(ret);
+
+err_inode:
+	iput(inode);
+	return ERR_PTR(ret);
+}
+
+ const struct super_operations fs_fs_ops = {
+	.drop_inode	= generic_delete_inode,
+	.destroy_inode	= free_inode_nonrcu,
+	.statfs		= simple_statfs,
+};
+
+static struct dentry *fs_fs_mount(struct file_system_type *fs_type,
+				  int flags, const char *dev_name,
+				  void *data)
+{
+	return mount_pseudo(fs_type, "fs_fs:", &fs_fs_ops,
+			    &fs_fs_dentry_operations, FS_FS_MAGIC);
+}
+
+static struct file_system_type fs_fs_type = {
+	.name		= "fs_fs",
+	.mount		= fs_fs_mount,
+	.kill_sb	= kill_anon_super,
+};
+
+static int __init init_fs_fs(void)
+{
+	int ret;
+
+	ret = register_filesystem(&fs_fs_type);
+	if (ret < 0)
+		panic("Cannot register fs_fs\n");
+
+	fs_fs_mnt = kern_mount(&fs_fs_type);
+	if (IS_ERR(fs_fs_mnt))
+		panic("Cannot mount fs_fs: %ld\n", PTR_ERR(fs_fs_mnt));
+	return 0;
+}
+
+fs_initcall(init_fs_fs);
+
+/*
+ * 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_DEFINE3(fsopen, const char __user *, _fs_name, int, reserved,
+		unsigned int, flags)
+{
+	struct sb_config *sc;
+	struct file *file;
+	const char *fs_name;
+	int fd, ret;
+
+	if (flags & ~O_CLOEXEC || reserved != -1)
+		return -EINVAL;
+
+	fs_name = strndup_user(_fs_name, PAGE_SIZE);
+	if (IS_ERR(fs_name))
+		return PTR_ERR(fs_name);
+
+	sc = vfs_new_sb_config(fs_name);
+	kfree(fs_name);
+	if (IS_ERR(sc))
+		return PTR_ERR(sc);
+
+	ret = -ENOTSUPP;
+	if (!sc->ops)
+		goto err_sc;
+
+	file = create_fs_file(sc);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		goto err_sc;
+	}
+
+	ret = get_unused_fd_flags(flags & O_CLOEXEC);
+	if (ret < 0)
+		goto err_file;
+
+	fd = ret;
+	fd_install(fd, file);
+	return fd;
+
+err_file:
+	fput(file);
+	return ret;
+
+err_sc:
+	put_sb_config(sc);
+	return ret;
+}
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 980c3c9b06f8..91ec8802ad5d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -905,5 +905,6 @@ asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val);
 asmlinkage long sys_pkey_free(int pkey);
 asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,
 			  unsigned mask, struct statx __user *buffer);
+asmlinkage long sys_fsopen(const char *fs_name, int containerfd, unsigned int flags);
 
 #endif
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index e230af2e6855..88ae83492f7c 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -84,5 +84,6 @@
 #define UDF_SUPER_MAGIC		0x15013346
 #define BALLOON_KVM_MAGIC	0x13661366
 #define ZSMALLOC_MAGIC		0x58295829
+#define FS_FS_MAGIC		0x66736673
 
 #endif /* __LINUX_MAGIC_H__ */
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 8acef8576ce9..de1dc63e7e47 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -258,3 +258,6 @@ cond_syscall(sys_membarrier);
 cond_syscall(sys_pkey_mprotect);
 cond_syscall(sys_pkey_alloc);
 cond_syscall(sys_pkey_free);
+
+/* fd-based mount */
+cond_syscall(sys_fsopen);

[toc] | [prev] | [next] | [standalone]


#1639089 — Re: [PATCH 07/14] Implement fsopen() to prepare for a mount

FromSargun Dhillon <sargun@sargun.me>
Date2017-05-11 00:10 +0200
SubjectRe: [PATCH 07/14] Implement fsopen() to prepare for a mount
Message-ID<tFHXX-6iT-3@gated-at.bofh.it>
In reply to#1638943
On Wed, May 10, 2017 at 9:19 AM, David Howells <dhowells@redhat.com> wrote:
> Provide an fsopen() system call that starts the process of preparing to
> mount, 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, int reserved,
>                          int open_flags);
>
> where reserved should be -1 for the moment (it will be used to pass the
> namespace information in future) and open_flags can be 0 or O_CLOEXEC.
>
> For example:
>
>         mfd = fsopen("ext4", -1, O_CLOEXEC);
>         write(mfd, "s /dev/sdb1"); // note I'm ignoring write's length arg
>         write(mfd, "o noatime");
>         write(mfd, "o acl");
>         write(mfd, "o user_attr");
>         write(mfd, "o iversion");
>         write(mfd, "o ");
>         write(mfd, "r /my/container"); // root inside the fs
>         fsmount(mfd, container_fd, "/mnt", AT_NO_FOLLOW);
>
>         mfd = fsopen("afs", -1);
>         write(mfd, "s %grand.central.org:root.cell");
>         write(mfd, "o cell=grand.central.org");
>         write(mfd, "r /");
>         fsmount(mfd, AT_FDCWD, "/mnt", 0);
>
> 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 write() 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.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>
>  arch/x86/entry/syscalls/syscall_32.tbl |    1
>  arch/x86/entry/syscalls/syscall_64.tbl |    1
>  fs/Makefile                            |    2
>  fs/fsopen.c                            |  279 ++++++++++++++++++++++++++++++++
>  include/linux/syscalls.h               |    1
>  include/uapi/linux/magic.h             |    1
>  kernel/sys_ni.c                        |    3
>  7 files changed, 287 insertions(+), 1 deletion(-)
>  create mode 100644 fs/fsopen.c
>
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index 448ac2161112..9bf8d4c62f85 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -391,3 +391,4 @@
>  382    i386    pkey_free               sys_pkey_free
>  383    i386    statx                   sys_statx
>  384    i386    arch_prctl              sys_arch_prctl                  compat_sys_arch_prctl
> +385    i386    fsopen                  sys_fsopen
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index 5aef183e2f85..9b198c5fc412 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -339,6 +339,7 @@
>  330    common  pkey_alloc              sys_pkey_alloc
>  331    common  pkey_free               sys_pkey_free
>  332    common  statx                   sys_statx
> +333    common  fsopen                  sys_fsopen
>
>  #
>  # x32-specific system call numbers start at 512 to avoid cache impact
> diff --git a/fs/Makefile b/fs/Makefile
> index 8f5142525866..b8fcf48b0400 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -12,7 +12,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 \
>                 stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
> -               sb_config.o
> +               sb_config.o fsopen.o
>
>  ifeq ($(CONFIG_BLOCK),y)
>  obj-y +=       buffer.o block_dev.o direct-io.o mpage.o
> diff --git a/fs/fsopen.c b/fs/fsopen.c
> new file mode 100644
> index 000000000000..a4e9d5a7ce2b
> --- /dev/null
> +++ b/fs/fsopen.c
> @@ -0,0 +1,279 @@
> +/* 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/sb_config.h>
> +#include <linux/mount.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/file.h>
> +#include <linux/magic.h>
> +#include <linux/syscalls.h>
> +
> +static struct vfsmount *fs_fs_mnt __read_mostly;
> +
> +static int fs_fs_release(struct inode *inode, struct file *file)
> +{
> +       struct sb_config *sc = file->private_data;
> +
> +       file->private_data = NULL;
> +
> +       put_sb_config(sc);
> +       return 0;
> +}
> +
> +/*
> + * Read any error message back from the fd.  Will be prefixed by "e ".
> + */
> +static ssize_t fs_fs_read(struct file *file, char __user *_buf, size_t len, loff_t *pos)
> +{
> +       struct sb_config *sc = file->private_data;
> +       const char *msg;
> +       size_t mlen;
> +
> +       msg = READ_ONCE(sc->error_msg);
> +       if (!msg)
> +               return -ENODATA;
> +
> +       mlen = strlen(msg);
> +       if (mlen + 2 > len)
> +               return -ETOOSMALL;
> +       if (copy_to_user(_buf, "e ", 2) != 0 ||
> +           copy_to_user(_buf + 2, msg, mlen) != 0)
> +               return -EFAULT;
> +       return mlen + 2;
> +}
> +
> +/*
> + * Userspace writes configuration data to the fd and we parse it here.  For the
> + * moment, we assume a single option per write.  Each line written is of the form
> + *
> + *     <option_type><space><stuff...>
> + *
> + *     d /dev/sda1                             -- Device name
> + *     o noatime                               -- Option without value
> + *     o cell=grand.central.org                -- Option with value
> + *     r /                                     -- Dir within device to mount
> + */
> +static ssize_t fs_fs_write(struct file *file,
> +                          const char __user *_buf, size_t len, loff_t *pos)
> +{
> +       struct sb_config *sc = file->private_data;
> +       struct inode *inode = file_inode(file);
> +       char opt[2], *data;
> +       ssize_t ret;
> +
> +       if (len < 3 || len > 4095)
> +               return -EINVAL;
> +
> +       if (copy_from_user(opt, _buf, 2) != 0)
> +               return -EFAULT;
> +       switch (opt[0]) {
> +       case 's':
> +       case 'o':
> +               break;
> +       default:
> +               return sb_cfg_inval(sc, "VFS: Unsupported write spec");
> +       }
> +       if (opt[1] != ' ')
> +               return sb_cfg_inval(sc, "VFS: Unsupported write spec");
> +
> +       data = memdup_user_nul(_buf + 2, len - 2);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +
> +       /* From this point onwards we need to lock the fd against someone
> +        * trying to mount it.
> +        */
> +       ret = inode_lock_killable(inode);
> +       if (ret < 0)
> +               goto err_free;
> +
> +       ret = -EBUSY;
> +       if (sc->mounted)
> +               goto err_unlock;
> +
> +       ret = -EINVAL;
> +       switch (opt[0]) {
> +       case 's':
> +               if (sc->device)
> +                       goto err_unlock;
> +               sc->device = data;
> +               data = NULL;
> +               break;
> +
> +       case 'o':
> +               ret = vfs_parse_mount_option(sc, data);
> +               if (ret < 0)
> +                       goto err_unlock;
> +               break;
> +
> +       default:
> +               goto err_unlock;
> +       }
> +
> +       ret = len;
> +err_unlock:
> +       inode_unlock(inode);
> +err_free:
> +       kfree(data);
> +       return ret;
> +}
> +
> +const struct file_operations fs_fs_fops = {
> +       .read           = fs_fs_read,
> +       .write          = fs_fs_write,
> +       .release        = fs_fs_release,
> +       .llseek         = no_llseek,
> +};
> +
> +/*
> + * Indicate the name we want to display the filesystem file as.
> + */
> +static char *fs_fs_dname(struct dentry *dentry, char *buffer, int buflen)
> +{
> +       return dynamic_dname(dentry, buffer, buflen, "fs:[%lu]",
> +                            d_inode(dentry)->i_ino);
> +}
> +
> +static const struct dentry_operations fs_fs_dentry_operations = {
> +       .d_dname        = fs_fs_dname,
> +};
> +
> +/*
> + * Create a file that can be used to configure a new mount.
> + */
> +static struct file *create_fs_file(struct sb_config *sc)
> +{
> +       struct inode *inode;
> +       struct file *f;
> +       struct path path;
> +       int ret;
> +
> +       inode = alloc_anon_inode(fs_fs_mnt->mnt_sb);
> +       if (!inode)
> +               return ERR_PTR(-ENFILE);
> +       inode->i_fop = &fs_fs_fops;
> +
> +       ret = -ENOMEM;
> +       path.dentry = d_alloc_pseudo(fs_fs_mnt->mnt_sb, &empty_name);
> +       if (!path.dentry)
> +               goto err_inode;
> +       path.mnt = mntget(fs_fs_mnt);
> +
> +       d_instantiate(path.dentry, inode);
> +
> +       f = alloc_file(&path, FMODE_READ | FMODE_WRITE, &fs_fs_fops);
> +       if (IS_ERR(f)) {
> +               ret = PTR_ERR(f);
> +               goto err_file;
> +       }
> +
> +       f->private_data = sc;
> +       return f;
> +
> +err_file:
> +       path_put(&path);
> +       return ERR_PTR(ret);
> +
> +err_inode:
> +       iput(inode);
> +       return ERR_PTR(ret);
> +}
> +
> + const struct super_operations fs_fs_ops = {
> +       .drop_inode     = generic_delete_inode,
> +       .destroy_inode  = free_inode_nonrcu,
> +       .statfs         = simple_statfs,
> +};
> +
> +static struct dentry *fs_fs_mount(struct file_system_type *fs_type,
> +                                 int flags, const char *dev_name,
> +                                 void *data)
> +{
> +       return mount_pseudo(fs_type, "fs_fs:", &fs_fs_ops,
> +                           &fs_fs_dentry_operations, FS_FS_MAGIC);
> +}
> +
> +static struct file_system_type fs_fs_type = {
> +       .name           = "fs_fs",
> +       .mount          = fs_fs_mount,
> +       .kill_sb        = kill_anon_super,
> +};
> +
> +static int __init init_fs_fs(void)
> +{
> +       int ret;
> +
> +       ret = register_filesystem(&fs_fs_type);
> +       if (ret < 0)
> +               panic("Cannot register fs_fs\n");
> +
> +       fs_fs_mnt = kern_mount(&fs_fs_type);
> +       if (IS_ERR(fs_fs_mnt))
> +               panic("Cannot mount fs_fs: %ld\n", PTR_ERR(fs_fs_mnt));
> +       return 0;
> +}
> +
> +fs_initcall(init_fs_fs);
> +
> +/*
> + * 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_DEFINE3(fsopen, const char __user *, _fs_name, int, reserved,
> +               unsigned int, flags)
> +{
> +       struct sb_config *sc;
> +       struct file *file;
> +       const char *fs_name;
> +       int fd, ret;
> +
> +       if (flags & ~O_CLOEXEC || reserved != -1)
> +               return -EINVAL;
> +
> +       fs_name = strndup_user(_fs_name, PAGE_SIZE);
> +       if (IS_ERR(fs_name))
> +               return PTR_ERR(fs_name);
> +
> +       sc = vfs_new_sb_config(fs_name);
> +       kfree(fs_name);
> +       if (IS_ERR(sc))
> +               return PTR_ERR(sc);
> +
> +       ret = -ENOTSUPP;
> +       if (!sc->ops)
> +               goto err_sc;
> +
> +       file = create_fs_file(sc);
> +       if (IS_ERR(file)) {
> +               ret = PTR_ERR(file);
> +               goto err_sc;
> +       }
> +
> +       ret = get_unused_fd_flags(flags & O_CLOEXEC);
> +       if (ret < 0)
> +               goto err_file;
> +
> +       fd = ret;
> +       fd_install(fd, file);
> +       return fd;
> +
> +err_file:
> +       fput(file);
> +       return ret;
> +
> +err_sc:
> +       put_sb_config(sc);
> +       return ret;
> +}
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 980c3c9b06f8..91ec8802ad5d 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -905,5 +905,6 @@ asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val);
>  asmlinkage long sys_pkey_free(int pkey);
>  asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,
>                           unsigned mask, struct statx __user *buffer);
> +asmlinkage long sys_fsopen(const char *fs_name, int containerfd, unsigned int flags);
>
>  #endif
> diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> index e230af2e6855..88ae83492f7c 100644
> --- a/include/uapi/linux/magic.h
> +++ b/include/uapi/linux/magic.h
> @@ -84,5 +84,6 @@
>  #define UDF_SUPER_MAGIC                0x15013346
>  #define BALLOON_KVM_MAGIC      0x13661366
>  #define ZSMALLOC_MAGIC         0x58295829
> +#define FS_FS_MAGIC            0x66736673
>
>  #endif /* __LINUX_MAGIC_H__ */
> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
> index 8acef8576ce9..de1dc63e7e47 100644
> --- a/kernel/sys_ni.c
> +++ b/kernel/sys_ni.c
> @@ -258,3 +258,6 @@ cond_syscall(sys_membarrier);
>  cond_syscall(sys_pkey_mprotect);
>  cond_syscall(sys_pkey_alloc);
>  cond_syscall(sys_pkey_free);
> +
> +/* fd-based mount */
> +cond_syscall(sys_fsopen);
>

Instead of string based configuration, does it perhaps make sense to
pass in structured mount data? Something like:

enum mount_command_id {
    MOUNT_OPTION_STR,
    MOUNT_SET_USER_NS
};

struct mount_attr {
   __u64 command_id;
   union {
       char option_str[4095];
       char mount_source[PATH_MAX];
       struct {
           __u32 user_ns_fd
       }
   }
}

It seems a lot less error prone to me.

[toc] | [prev] | [next] | [standalone]


#1639614 — Re: [PATCH 07/14] Implement fsopen() to prepare for a mount

FromDavid Howells <dhowells@redhat.com>
Date2017-05-11 16:40 +0200
SubjectRe: [PATCH 07/14] Implement fsopen() to prepare for a mount
Message-ID<tFXq2-7wN-29@gated-at.bofh.it>
In reply to#1639089
Sargun Dhillon <sargun@sargun.me> wrote:

> Instead of string based configuration, does it perhaps make sense to
> pass in structured mount data? Something like:

I don't think it helps particularly.

> enum mount_command_id {
>     MOUNT_OPTION_STR,
>     MOUNT_SET_USER_NS
> };
> 
> struct mount_attr {
>    __u64 command_id;
>    union {
>        char option_str[4095];
>        char mount_source[PATH_MAX];

Why limit the option size to 4096?  I can see situations where it might be
necessary to hand in a bigger blob - giving cifs a Microsoft Kerberos PAC for
example.

>        struct {
>            __u32 user_ns_fd

There are more than just that namespace that could be relevant.

>        }
>    }
> }
> 
> It seems a lot less error prone to me.

Not really.  The only real difference is how one selects what action is
intended and how one determines the length.  write() has a length parameter.

David

[toc] | [prev] | [next] | [standalone]


#1639603 — Re: [PATCH 07/14] Implement fsopen() to prepare for a mount

FromJeff Layton <jlayton@redhat.com>
Date2017-05-11 16:40 +0200
SubjectRe: [PATCH 07/14] Implement fsopen() to prepare for a mount
Message-ID<tFXq2-7wN-27@gated-at.bofh.it>
In reply to#1638943
On Thu, 2017-05-11 at 15:30 +0100, David Howells wrote:
> Sargun Dhillon <sargun@sargun.me> wrote:
> 
> > Instead of string based configuration, does it perhaps make sense to
> > pass in structured mount data? Something like:
> 
> I don't think it helps particularly.
> 
> > enum mount_command_id {
> >     MOUNT_OPTION_STR,
> >     MOUNT_SET_USER_NS
> > };
> > 
> > struct mount_attr {
> >    __u64 command_id;
> >    union {
> >        char option_str[4095];
> >        char mount_source[PATH_MAX];
> 
> Why limit the option size to 4096?  I can see situations where it might be
> necessary to hand in a bigger blob - giving cifs a Microsoft Kerberos PAC for
> example.
> 
> >        struct {
> >            __u32 user_ns_fd
> 
> There are more than just that namespace that could be relevant.
> 
> >        }
> >    }
> > }
> > 
> > It seems a lot less error prone to me.
> 
> Not really.  The only real difference is how one selects what action is
> intended and how one determines the length.  write() has a length parameter.
> 

Agreed. I like the text based configuration better.

It also has another advantage: It's easy to strace the program and see
what it's doing. With an opaque blob, we'd need to teach strace how to
format the thing to be able to view it.

-- 
Jeff Layton <jlayton@redhat.com>

[toc] | [prev] | [next] | [standalone]


#1638945 — [PATCH 08/14] Implement fsmount() to effect a pre-configured mount

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 08/14] Implement fsmount() to effect a pre-configured mount
Message-ID<tFCEW-2YC-5@gated-at.bofh.it>
In reply to#1638938
Provide a system call by which a filesystem opened with fsopen() and
configured by a series of writes can be mounted:

	int ret = fsmount(int fsfd, int dfd, const char *path);

where fsfd is the fd returned by fsopen(), dfd and path describe the
mountpoint.  dfd can be AT_FDCWD or an fd open to a directory.

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

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

 arch/x86/entry/syscalls/syscall_32.tbl |    1 
 arch/x86/entry/syscalls/syscall_64.tbl |    1 
 fs/namespace.c                         |   87 ++++++++++++++++++++++++++++++++
 include/linux/lsm_hooks.h              |    6 ++
 include/linux/security.h               |    6 ++
 include/linux/syscalls.h               |    1 
 kernel/sys_ni.c                        |    1 
 security/security.c                    |    7 +++
 security/selinux/hooks.c               |   13 +++++
 9 files changed, 123 insertions(+)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 9bf8d4c62f85..abe6ea95e0e6 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -392,3 +392,4 @@
 383	i386	statx			sys_statx
 384	i386	arch_prctl		sys_arch_prctl			compat_sys_arch_prctl
 385	i386	fsopen			sys_fsopen
+386	i386	fsmount			sys_fsmount
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 9b198c5fc412..0977c5079831 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -340,6 +340,7 @@
 331	common	pkey_free		sys_pkey_free
 332	common	statx			sys_statx
 333	common	fsopen			sys_fsopen
+334	common	fsmount			sys_fsmount
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/namespace.c b/fs/namespace.c
index 554e99c980e0..49d630a4fbd4 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3261,6 +3261,93 @@ vfs_submount_sc(const struct dentry *mountpoint, struct sb_config *sc)
 EXPORT_SYMBOL_GPL(vfs_submount_sc);
 
 /*
+ * Mount a new, prepared superblock (specified by fs_fd) on the location
+ * specified by dfd and dir_name.  dfd can be AT_FDCWD, a dir fd or a container
+ * fd.  This cannot be used for binding, moving or remounting mounts.
+ */
+SYSCALL_DEFINE3(fsmount, int, fs_fd, int, dfd, const char __user *, dir_name)
+{
+	struct sb_config *sc;
+	struct inode *inode;
+	struct path mountpoint;
+	struct fd f = fdget(fs_fd);
+	unsigned int mnt_flags = 0;
+	long ret;
+
+	if (!f.file)
+		return -EBADF;
+
+	ret = -EINVAL;
+	if (f.file->f_op != &fs_fs_fops)
+		goto err_fsfd;
+
+	sc = f.file->private_data;
+
+	ret = -EPERM;
+	if (!may_mount() ||
+	    ((sc->ms_flags & MS_MANDLOCK) && !may_mandlock()))
+		goto err_fsfd;
+
+	/* Prevent further changes. */
+	inode = file_inode(f.file);
+	ret = inode_lock_killable(inode);
+	if (ret < 0)
+		goto err_fsfd;
+	ret = -EBUSY;
+	if (!sc->mounted) {
+		sc->mounted = true;
+		ret = 0;
+	}
+	inode_unlock(inode);
+	if (ret < 0)
+		goto err_fsfd;
+
+	/* Find the mountpoint.  A container can be specified in dfd. */
+	ret = user_path_at(dfd, dir_name, LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT,
+			   &mountpoint);
+	if (ret < 0) {
+		sb_cfg_error(sc, "VFS: Mountpoint lookup failed");
+		goto err_fsfd;
+	}
+
+	ret = security_sb_config_mountpoint(sc, &mountpoint);
+	if (ret < 0)
+		goto err_mp;
+
+	/* Default to relatime unless overriden */
+	if (!(sc->ms_flags & MS_NOATIME))
+		mnt_flags |= MNT_RELATIME;
+
+	/* Separate the per-mountpoint flags */
+	if (sc->ms_flags & MS_NOSUID)
+		mnt_flags |= MNT_NOSUID;
+	if (sc->ms_flags & MS_NODEV)
+		mnt_flags |= MNT_NODEV;
+	if (sc->ms_flags & MS_NOEXEC)
+		mnt_flags |= MNT_NOEXEC;
+	if (sc->ms_flags & MS_NOATIME)
+		mnt_flags |= MNT_NOATIME;
+	if (sc->ms_flags & MS_NODIRATIME)
+		mnt_flags |= MNT_NODIRATIME;
+	if (sc->ms_flags & MS_STRICTATIME)
+		mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
+	if (sc->ms_flags & MS_RDONLY)
+		mnt_flags |= MNT_READONLY;
+
+	sc->ms_flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
+			  MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
+			  MS_STRICTATIME | MS_NOREMOTELOCK | MS_SUBMOUNT);
+
+	ret = do_new_mount_sc(sc, &mountpoint, mnt_flags);
+
+err_mp:
+	path_put(&mountpoint);
+err_fsfd:
+	fdput(f);
+	return ret;
+}
+
+/*
  * Return true if path is reachable from root
  *
  * namespace_sem or mount_lock is held
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 6238fccaf5a4..e8e473ce5ddd 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -101,6 +101,10 @@
  *	Equivalent of sb_kern_mount, but with a superblock configuration context.
  *	@sc indicates the superblock configuration context.
  *	@src_sb indicates the new superblock.
+ * @sb_config_mountpoint:
+ *	Equivalent of sb_mount, but with an sb_config.
+ *	@sc indicates the superblock configuration context.
+ *	@mountpoint indicates the path on which the mount will take place.
  *
  * Security hooks for filesystem operations.
  *
@@ -1390,6 +1394,7 @@ union security_list_options {
 	void (*sb_config_free)(struct sb_config *sc);
 	int (*sb_config_parse_option)(struct sb_config *sc, char *opt);
 	int (*sb_config_kern_mount)(struct sb_config *sc, struct super_block *sb);
+	int (*sb_config_mountpoint)(struct sb_config *sc, struct path *mountpoint);
 
 	int (*sb_alloc_security)(struct super_block *sb);
 	void (*sb_free_security)(struct super_block *sb);
@@ -1704,6 +1709,7 @@ struct security_hook_heads {
 	struct list_head sb_config_free;
 	struct list_head sb_config_parse_option;
 	struct list_head sb_config_kern_mount;
+	struct list_head sb_config_mountpoint;
 	struct list_head sb_alloc_security;
 	struct list_head sb_free_security;
 	struct list_head sb_copy_data;
diff --git a/include/linux/security.h b/include/linux/security.h
index 49a7254aa30a..f95dc555cf29 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -226,6 +226,7 @@ int security_sb_config_dup(struct sb_config *sc, struct sb_config *src_sc);
 void security_sb_config_free(struct sb_config *sc);
 int security_sb_config_parse_option(struct sb_config *sc, char *opt);
 int security_sb_config_kern_mount(struct sb_config *sc, struct super_block *sb);
+int security_sb_config_mountpoint(struct sb_config *sc, struct path *mountpoint);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
@@ -541,6 +542,11 @@ static inline int security_sb_config_kern_mount(struct sb_config *sc,
 {
 	return 0;
 }
+static inline int security_sb_config_mountpoint(struct sb_config *sc,
+						struct path *mountpoint)
+{
+	return 0;
+}
 
 static inline int security_sb_alloc(struct super_block *sb)
 {
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 91ec8802ad5d..9ac7d8ca8c2e 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -906,5 +906,6 @@ asmlinkage long sys_pkey_free(int pkey);
 asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,
 			  unsigned mask, struct statx __user *buffer);
 asmlinkage long sys_fsopen(const char *fs_name, int containerfd, unsigned int flags);
+asmlinkage long sys_fsmount(int fsfd, int dfd, const char *path);
 
 #endif
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index de1dc63e7e47..a0fe764bd5dd 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -261,3 +261,4 @@ cond_syscall(sys_pkey_free);
 
 /* fd-based mount */
 cond_syscall(sys_fsopen);
+cond_syscall(sys_fsmount);
diff --git a/security/security.c b/security/security.c
index fa9037186634..8b2b7e6b3112 100644
--- a/security/security.c
+++ b/security/security.c
@@ -334,6 +334,11 @@ int security_sb_config_kern_mount(struct sb_config *sc, struct super_block *sb)
 	return call_int_hook(sb_config_kern_mount, 0, sc, sb);
 }
 
+int security_sb_config_mountpoint(struct sb_config *sc, struct path *mountpoint)
+{
+	return call_int_hook(sb_config_mountpoint, 0, sc, mountpoint);
+}
+
 int security_sb_alloc(struct super_block *sb)
 {
 	return call_int_hook(sb_alloc_security, 0, sb);
@@ -1691,6 +1696,8 @@ struct security_hook_heads security_hook_heads = {
 		LIST_HEAD_INIT(security_hook_heads.sb_config_parse_option),
 	.sb_config_kern_mount =
 		LIST_HEAD_INIT(security_hook_heads.sb_config_kern_mount),
+	.sb_config_mountpoint =
+		LIST_HEAD_INIT(security_hook_heads.sb_config_mountpoint),
 	.sb_alloc_security =
 		LIST_HEAD_INIT(security_hook_heads.sb_alloc_security),
 	.sb_free_security =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 142be91888c9..e1bf18af72f5 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2990,6 +2990,18 @@ static int selinux_sb_config_kern_mount(struct sb_config *sc,
 	return rc;
 }
 
+static int selinux_sb_config_mountpoint(struct sb_config *sc,
+					struct path *mountpoint)
+{
+	const struct cred *cred = current_cred();
+	int ret;
+
+	ret = path_has_perm(cred, mountpoint, FILE__MOUNTON);
+	if (ret < 0)
+		sb_cfg_error(sc, "SELinux: Mount on mountpoint not permitted");
+	return ret;
+}
+
 /* inode security operations */
 
 static int selinux_inode_alloc_security(struct inode *inode)
@@ -6300,6 +6312,7 @@ static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(sb_config_free, selinux_sb_config_free),
 	LSM_HOOK_INIT(sb_config_parse_option, selinux_sb_config_parse_option),
 	LSM_HOOK_INIT(sb_config_kern_mount, selinux_sb_config_kern_mount),
+	LSM_HOOK_INIT(sb_config_mountpoint, selinux_sb_config_mountpoint),
 
 	LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
 	LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),

[toc] | [prev] | [next] | [standalone]


#1638946 — [PATCH 11/14] proc: Add superblock config support to procfs

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 11/14] proc: Add superblock config support to procfs
Message-ID<tFCEW-2YC-7@gated-at.bofh.it>
In reply to#1638938
Add superblock config support to procfs.

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

 fs/proc/inode.c    |    2 -
 fs/proc/internal.h |    2 -
 fs/proc/root.c     |  152 +++++++++++++++++++++++++++++++---------------------
 3 files changed, 94 insertions(+), 62 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 194fa2d13b7e..df9e586ee1af 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -118,7 +118,7 @@ const struct super_operations proc_sops = {
 	.drop_inode	= generic_delete_inode,
 	.evict_inode	= proc_evict_inode,
 	.statfs		= simple_statfs,
-	.remount_fs	= proc_remount,
+	.remount_fs_sc	= proc_remount,
 	.show_options	= proc_show_options,
 };
 
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index b681533f59dd..4546372c2d13 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -262,7 +262,7 @@ static inline void proc_tty_init(void) {}
 extern struct proc_dir_entry proc_root;
 
 extern void proc_self_init(void);
-extern int proc_remount(struct super_block *, int *, char *);
+extern int proc_remount(struct super_block *, struct sb_config *);
 
 /*
  * task_[no]mmu.c
diff --git a/fs/proc/root.c b/fs/proc/root.c
index ee1937b37370..87d1a3ffec70 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -24,9 +24,17 @@
 #include <linux/parser.h>
 #include <linux/cred.h>
 #include <linux/magic.h>
+#include <linux/slab.h>
 
 #include "internal.h"
 
+struct proc_sb_config {
+	struct sb_config	sc;
+	unsigned long		mask;
+	int			hidepid;
+	int			gid;
+};
+
 enum {
 	Opt_gid, Opt_hidepid, Opt_err,
 };
@@ -37,56 +45,62 @@ static const match_table_t tokens = {
 	{Opt_err, NULL},
 };
 
-static int proc_parse_options(char *options, struct pid_namespace *pid)
+static int proc_parse_mount_option(struct sb_config *sc, char *p)
 {
-	char *p;
+	struct proc_sb_config *ctx =
+		container_of(sc, struct proc_sb_config, sc);
 	substring_t args[MAX_OPT_ARGS];
-	int option;
-
-	if (!options)
-		return 1;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
-		if (!*p)
-			continue;
-
-		args[0].to = args[0].from = NULL;
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_gid:
-			if (match_int(&args[0], &option))
-				return 0;
-			pid->pid_gid = make_kgid(current_user_ns(), option);
-			break;
-		case Opt_hidepid:
-			if (match_int(&args[0], &option))
-				return 0;
-			if (option < HIDEPID_OFF ||
-			    option > HIDEPID_INVISIBLE) {
-				pr_err("proc: hidepid value must be between 0 and 2.\n");
-				return 0;
-			}
-			pid->hide_pid = option;
-			break;
-		default:
-			pr_err("proc: unrecognized mount option \"%s\" "
-			       "or missing value\n", p);
-			return 0;
+	int token;
+
+	args[0].to = args[0].from = NULL;
+	token = match_token(p, tokens, args);
+	switch (token) {
+	case Opt_gid:
+		if (match_int(&args[0], &ctx->gid))
+			return sb_cfg_inval(sc, "procfs: Unparseable gid= argument");
+		break;
+
+	case Opt_hidepid:
+		if (match_int(&args[0], &ctx->hidepid))
+			return sb_cfg_inval(sc, "procfs: Unparseable hidepid= argument");
+		if (ctx->hidepid < HIDEPID_OFF ||
+		    ctx->hidepid > HIDEPID_INVISIBLE) {
+			pr_err("proc: hidepid value must be between 0 and 2.\n");
+			return sb_cfg_inval(sc, "procfs: Invalid hidepid= argument");
 		}
+		break;
+
+	default:
+		pr_err("proc: unrecognized mount option \"%s\" "
+		       "or missing value\n", p);
+		return sb_cfg_inval(sc, "procfs: Invalid mount option or missing value");
 	}
 
-	return 1;
+	ctx->mask |= 1 << token;
+	return 0;
+}
+
+static void proc_set_options(struct super_block *s,
+			     struct sb_config *sc,
+			     struct pid_namespace *pid_ns,
+			     struct user_namespace *user_ns)
+{
+	struct proc_sb_config *ctx =
+		container_of(sc, struct proc_sb_config, sc);
+
+	if (ctx->mask & (1 << Opt_gid))
+		pid_ns->pid_gid = make_kgid(user_ns, ctx->gid);
+	if (ctx->mask & (1 << Opt_hidepid))
+		pid_ns->hide_pid = ctx->hidepid;
 }
 
-static int proc_fill_super(struct super_block *s, void *data, int silent)
+static int proc_fill_super(struct super_block *s, struct sb_config *sc)
 {
-	struct pid_namespace *ns = get_pid_ns(s->s_fs_info);
+	struct pid_namespace *pid_ns = get_pid_ns(s->s_fs_info);
 	struct inode *root_inode;
 	int ret;
 
-	if (!proc_parse_options(data, ns))
-		return -EINVAL;
+	proc_set_options(s, sc, pid_ns, current_user_ns());
 
 	/* User space would break if executables or devices appear on proc */
 	s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
@@ -103,7 +117,7 @@ static int proc_fill_super(struct super_block *s, void *data, int silent)
 	 * top of it
 	 */
 	s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
-	
+
 	pde_get(&proc_root);
 	root_inode = proc_get_inode(s, &proc_root);
 	if (!root_inode) {
@@ -124,27 +138,32 @@ static int proc_fill_super(struct super_block *s, void *data, int silent)
 	return proc_setup_thread_self(s);
 }
 
-int proc_remount(struct super_block *sb, int *flags, char *data)
+int proc_remount(struct super_block *sb, struct sb_config *sc)
 {
 	struct pid_namespace *pid = sb->s_fs_info;
 
 	sync_filesystem(sb);
-	return !proc_parse_options(data, pid);
+
+	if (sc)
+		proc_set_options(sb, sc, pid, current_user_ns());
+	return 0;
 }
 
-static struct dentry *proc_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static struct dentry *proc_mount(struct sb_config *sc)
 {
-	struct pid_namespace *ns;
+	return mount_ns_sc(sc, sc->pid_ns);
+}
 
-	if (flags & MS_KERNMOUNT) {
-		ns = data;
-		data = NULL;
-	} else {
-		ns = task_active_pid_ns(current);
-	}
+static const struct sb_config_operations proc_sb_config_ops = {
+	.parse_option	= proc_parse_mount_option,
+	.mount		= proc_mount,
+	.fill_super	= proc_fill_super,
+};
 
-	return mount_ns(fs_type, flags, data, ns, ns->user_ns, proc_fill_super);
+static int proc_init_sb_config(struct sb_config *sc, struct super_block *src_sb)
+{
+	sc->ops = &proc_sb_config_ops;
+	return 0;
 }
 
 static void proc_kill_sb(struct super_block *sb)
@@ -162,7 +181,8 @@ static void proc_kill_sb(struct super_block *sb)
 
 static struct file_system_type proc_fs_type = {
 	.name		= "proc",
-	.mount		= proc_mount,
+	.sb_config_size	= sizeof(struct proc_sb_config),
+	.init_sb_config	= proc_init_sb_config,
 	.kill_sb	= proc_kill_sb,
 	.fs_flags	= FS_USERNS_MOUNT,
 };
@@ -210,7 +230,7 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr
 {
 	if (!proc_pid_lookup(dir, dentry, flags))
 		return NULL;
-	
+
 	return proc_lookup(dir, dentry, flags);
 }
 
@@ -249,12 +269,12 @@ static const struct inode_operations proc_root_inode_operations = {
  * This is the root "inode" in the /proc tree..
  */
 struct proc_dir_entry proc_root = {
-	.low_ino	= PROC_ROOT_INO, 
-	.namelen	= 5, 
-	.mode		= S_IFDIR | S_IRUGO | S_IXUGO, 
-	.nlink		= 2, 
+	.low_ino	= PROC_ROOT_INO,
+	.namelen	= 5,
+	.mode		= S_IFDIR | S_IRUGO | S_IXUGO,
+	.nlink		= 2,
 	.count		= ATOMIC_INIT(1),
-	.proc_iops	= &proc_root_inode_operations, 
+	.proc_iops	= &proc_root_inode_operations,
 	.proc_fops	= &proc_root_operations,
 	.parent		= &proc_root,
 	.subdir		= RB_ROOT,
@@ -263,9 +283,21 @@ struct proc_dir_entry proc_root = {
 
 int pid_ns_prepare_proc(struct pid_namespace *ns)
 {
+	struct sb_config *sc;
 	struct vfsmount *mnt;
 
-	mnt = kern_mount_data(&proc_fs_type, ns);
+	sc = __vfs_new_sb_config(&proc_fs_type, NULL, 0, SB_CONFIG_FOR_NEW);
+	if (IS_ERR(sc))
+		return PTR_ERR(sc);
+
+	if (sc->pid_ns != ns) {
+		put_pid_ns(sc->pid_ns);
+		get_pid_ns(ns);
+		sc->pid_ns = ns;
+	}
+
+	mnt = kern_mount_data_sc(sc);
+	put_sb_config(sc);
 	if (IS_ERR(mnt))
 		return PTR_ERR(mnt);
 

[toc] | [prev] | [next] | [standalone]


#1638947 — [PATCH 13/14] Support legacy filesystems

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 13/14] Support legacy filesystems
Message-ID<tFCEW-2YC-9@gated-at.bofh.it>
In reply to#1638938
Support legacy filesystems by creating a set of legacy sb_config operations
that builds up a list of mount options and then invokes fs_type->mount()
within the sb_config mount operation.

All filesystems can then be accessed using sb_config and the
fs_type->mount() call is _only_ used from within legacy_mount().  This
allows some simplification to take place in the core mount code.
---

 fs/namespace.c |   37 ++-----------
 fs/sb_config.c |  160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 161 insertions(+), 36 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 49d630a4fbd4..6d809f5705cd 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2583,7 +2583,6 @@ static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
 			int mnt_flags, const char *name, void *data)
 {
 	struct sb_config *sc;
-	struct vfsmount *mnt;
 	int err;
 
 	if (!fstype)
@@ -2599,41 +2598,17 @@ static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
 	if (!sc->device)
 		goto err_sc;
 
-	if (sc->ops) {
-		err = parse_monolithic_mount_data(sc, data);
-		if (err < 0)
-			goto err_sc;
-
-		err = do_new_mount_sc(sc, mountpoint, mnt_flags);
-		if (err)
-			goto err_sc;
-
-	} else {
-		mnt = vfs_kern_mount(sc->fs_type, flags, name, data);
-		if (!IS_ERR(mnt) && (sc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
-		    !mnt->mnt_sb->s_subtype)
-			mnt = fs_set_subtype(mnt, fstype);
-
-		if (IS_ERR(mnt)) {
-			err = PTR_ERR(mnt);
-			goto err_sc;
-		}
-
-		err = -EPERM;
-		if (mount_too_revealing(mnt, &mnt_flags))
-			goto err_mnt;
+	err = parse_monolithic_mount_data(sc, data);
+	if (err < 0)
+		goto err_sc;
 
-		err = do_add_mount(real_mount(mnt), mountpoint, mnt_flags,
-				   sc->mnt_ns);
-		if (err)
-			goto err_mnt;
-	}
+	err = do_new_mount_sc(sc, mountpoint, mnt_flags);
+	if (err)
+		goto err_sc;
 
 	put_sb_config(sc);
 	return 0;
 
-err_mnt:
-	mntput(mnt);
 err_sc:
 	if (sc->error_msg)
 		pr_info("Mount failed: %s\n", sc->error_msg);
diff --git a/fs/sb_config.c b/fs/sb_config.c
index 2c6789d1d71b..4429ac35161c 100644
--- a/fs/sb_config.c
+++ b/fs/sb_config.c
@@ -25,6 +25,15 @@
 #include <net/net_namespace.h>
 #include "mount.h"
 
+struct legacy_sb_config {
+	struct sb_config	sc;
+	char			*legacy_data;	/* Data page for legacy filesystems */
+	char			*secdata;
+	unsigned int		data_usage;
+};
+
+static const struct sb_config_operations legacy_sb_config_ops;
+
 static const match_table_t common_set_mount_options = {
 	{ MS_DIRSYNC,		"dirsync" },
 	{ MS_I_VERSION,		"iversion" },
@@ -186,13 +195,15 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
 				      enum sb_config_purpose purpose)
 {
 	struct sb_config *sc;
+	size_t sc_size = fs_type->sb_config_size;
 	int ret;
 
-	BUG_ON(fs_type->init_sb_config &&
-	       fs_type->sb_config_size < sizeof(*sc));
+	BUG_ON(fs_type->init_sb_config && sc_size < sizeof(*sc));
+
+	if (!fs_type->init_sb_config)
+		sc_size = sizeof(struct legacy_sb_config);
 
-	sc = kzalloc(max_t(size_t, fs_type->sb_config_size, sizeof(*sc)),
-		     GFP_KERNEL);
+	sc = kzalloc(sc_size, GFP_KERNEL);
 	if (!sc)
 		return ERR_PTR(-ENOMEM);
 
@@ -211,9 +222,11 @@ struct sb_config *__vfs_new_sb_config(struct file_system_type *fs_type,
 		ret = sc->fs_type->init_sb_config(sc, src_sb);
 		if (ret < 0)
 			goto err_sc;
+	} else {
+		sc->ops = &legacy_sb_config_ops;
 	}
 
-	/* Do the security check last because ->fsopen may change the
+	/* Do the security check last because ->init_sb_config may change the
 	 * namespace subscriptions.
 	 */
 	ret = security_sb_config_alloc(sc, src_sb);
@@ -275,11 +288,16 @@ struct sb_config *vfs_sb_reconfig(struct vfsmount *mnt,
 struct sb_config *vfs_dup_sb_config(struct sb_config *src_sc)
 {
 	struct sb_config *sc;
+	size_t sc_size;
 	int ret;
 
 	if (!src_sc->ops->dup)
 		return ERR_PTR(-ENOTSUPP);
 
+	sc_size = src_sc->fs_type->sb_config_size;
+	if (!src_sc->fs_type->init_sb_config)
+		sc_size = sizeof(struct legacy_sb_config);
+
 	sc = kmemdup(src_sc, src_sc->fs_type->sb_config_size, GFP_KERNEL);
 	if (!sc)
 		return ERR_PTR(-ENOMEM);
@@ -333,3 +351,135 @@ void put_sb_config(struct sb_config *sc)
 	kfree(sc);
 }
 EXPORT_SYMBOL(put_sb_config);
+
+/*
+ * Free the config for a filesystem that doesn't support sb_config.
+ */
+static void legacy_sb_config_free(struct sb_config *sc)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+
+	free_secdata(cfg->secdata);
+	kfree(cfg->legacy_data);
+}
+
+/*
+ * Duplicate a legacy config.
+ */
+static int legacy_sb_config_dup(struct sb_config *sc, struct sb_config *src_sc)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+	struct legacy_sb_config *src_cfg = container_of(src_sc, struct legacy_sb_config, sc);
+
+	cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!cfg->legacy_data)
+		return -ENOMEM;
+	memcpy(cfg->legacy_data, src_cfg->legacy_data, sizeof(PAGE_SIZE));
+	return 0;
+}
+
+/*
+ * Add an option to a legacy config.  We build up a comma-separated list of
+ * options.
+ */
+static int legacy_parse_option(struct sb_config *sc, char *p)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+	unsigned int usage = cfg->data_usage;
+	size_t len = strlen(p);
+
+	if (len > PAGE_SIZE - 2 - usage)
+		return sb_cfg_inval(sc, "VFS: Insufficient data buffer space");
+	if (memchr(p, ',', len) != NULL)
+		return sb_cfg_inval(sc, "VFS: Options cannot contain commas");
+	if (!cfg->legacy_data) {
+		cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+		if (!cfg->legacy_data)
+			return -ENOMEM;
+	}
+
+	cfg->legacy_data[usage++] = ',';
+	memcpy(cfg->legacy_data + usage, p, len);
+	usage += len;
+	cfg->legacy_data[usage] = '\0';
+	cfg->data_usage = usage;
+	return 0;
+}
+
+/*
+ * Add monolithic mount data.
+ */
+static int legacy_monolithic_mount_data(struct sb_config *sc, void *data)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+
+	if (cfg->data_usage != 0)
+		return sb_cfg_inval(sc, "VFS: Can't mix monolithic and individual options");
+	if (!data)
+		return 0;
+	if (!cfg->legacy_data) {
+		cfg->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+		if (!cfg->legacy_data)
+			return -ENOMEM;
+	}
+
+	memcpy(cfg->legacy_data, data, PAGE_SIZE);
+	cfg->data_usage = PAGE_SIZE;
+	return 0;
+}
+
+/*
+ * Use the legacy mount validation step to strip out and process security
+ * config options.
+ */
+static int legacy_validate(struct sb_config *sc)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+
+	if (!cfg->legacy_data || cfg->sc.fs_type->fs_flags & FS_BINARY_MOUNTDATA)
+		return 0;
+
+	cfg->secdata = alloc_secdata();
+	if (!cfg->secdata)
+		return -ENOMEM;
+
+	return security_sb_copy_data(cfg->legacy_data, cfg->secdata);
+}
+
+/*
+ * Perform a legacy mount.
+ */
+static struct dentry *legacy_mount(struct sb_config *sc)
+{
+	struct legacy_sb_config *cfg = container_of(sc, struct legacy_sb_config, sc);
+	struct super_block *sb;
+	struct dentry *root;
+	int ret;
+
+	root = cfg->sc.fs_type->mount(cfg->sc.fs_type, cfg->sc.ms_flags,
+				     cfg->sc.device, cfg->legacy_data);
+	if (IS_ERR(root))
+		return ERR_CAST(root);
+
+	sb = root->d_sb;
+	BUG_ON(!sb);
+	ret = security_sb_kern_mount(sb, cfg->sc.ms_flags, cfg->secdata);
+	if (ret < 0)
+		goto err_sb;
+
+	return root;
+
+err_sb:
+	dput(root);
+	deactivate_locked_super(sb);
+	return ERR_PTR(ret);
+}
+
+static const struct sb_config_operations legacy_sb_config_ops = {
+	.free			= legacy_sb_config_free,
+	.dup			= legacy_sb_config_dup,
+	.parse_option		= legacy_parse_option,
+	.monolithic_mount_data	= legacy_monolithic_mount_data,
+	.validate		= legacy_validate,
+	.mount			= legacy_mount,
+};

[toc] | [prev] | [next] | [standalone]


#1638950 — [PATCH 03/14] VFS: Make get_mnt_ns() return the namespace

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 03/14] VFS: Make get_mnt_ns() return the namespace
Message-ID<tFCEW-2YC-21@gated-at.bofh.it>
In reply to#1638938
Make get_mnt_ns() return the namespace it got a ref on for consistency with
other namespace ref getting functions.

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

 fs/mount.h |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/mount.h b/fs/mount.h
index 2826543a131d..b1e99b38f2ee 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -108,9 +108,10 @@ static inline void detach_mounts(struct dentry *dentry)
 	__detach_mounts(dentry);
 }
 
-static inline void get_mnt_ns(struct mnt_namespace *ns)
+static inline struct mnt_namespace *get_mnt_ns(struct mnt_namespace *ns)
 {
 	atomic_inc(&ns->count);
+	return ns;
 }
 
 extern seqlock_t mount_lock;

[toc] | [prev] | [next] | [standalone]


#1638951 — [PATCH 05/14] VFS: Provide empty name qstr

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 05/14] VFS: Provide empty name qstr
Message-ID<tFCEX-2YC-31@gated-at.bofh.it>
In reply to#1638938
Provide an empty name (ie. "") qstr for general use.

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

 fs/dcache.c            |    8 ++++++--
 fs/gfs2/dir.c          |    3 +--
 fs/namei.c             |    3 +--
 fs/nsfs.c              |    3 +--
 fs/pipe.c              |    3 +--
 include/linux/dcache.h |    5 +++++
 6 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 95d71eda8142..9a17ee020996 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -90,6 +90,11 @@ EXPORT_SYMBOL(rename_lock);
 
 static struct kmem_cache *dentry_cache __read_mostly;
 
+const struct qstr empty_name = QSTR_INIT("", 0);
+EXPORT_SYMBOL(empty_name);
+const struct qstr slash_name = QSTR_INIT("/", 1);
+EXPORT_SYMBOL(slash_name);
+
 /*
  * This is the single most critical data structure when it comes
  * to the dcache: the hashtable for lookups. Somebody should try
@@ -1580,8 +1585,7 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
 	 */
 	dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
 	if (unlikely(!name)) {
-		static const struct qstr anon = QSTR_INIT("/", 1);
-		name = &anon;
+		name = &slash_name;
 		dname = dentry->d_iname;
 	} else if (name->len > DNAME_INLINE_LEN-1) {
 		size_t size = offsetof(struct external_name, name[1]);
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 79113219be5f..a5dfff6a033e 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -872,7 +872,6 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
 	struct buffer_head *bh;
 	struct gfs2_leaf *leaf;
 	struct gfs2_dirent *dent;
-	struct qstr name = { .name = "" };
 	struct timespec tv = current_time(inode);
 
 	error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
@@ -896,7 +895,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
 	leaf->lf_sec = cpu_to_be64(tv.tv_sec);
 	memset(leaf->lf_reserved2, 0, sizeof(leaf->lf_reserved2));
 	dent = (struct gfs2_dirent *)(leaf+1);
-	gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
+	gfs2_qstr2dirent(&empty_name, bh->b_size - sizeof(struct gfs2_leaf), dent);
 	*pbh = bh;
 	return leaf;
 }
diff --git a/fs/namei.c b/fs/namei.c
index 19dcf62133cc..0c2af2c7794f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3368,7 +3368,6 @@ static int do_last(struct nameidata *nd,
 
 struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
 {
-	static const struct qstr name = QSTR_INIT("/", 1);
 	struct dentry *child = NULL;
 	struct inode *dir = dentry->d_inode;
 	struct inode *inode;
@@ -3382,7 +3381,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
 	if (!dir->i_op->tmpfile)
 		goto out_err;
 	error = -ENOMEM;
-	child = d_alloc(dentry, &name);
+	child = d_alloc(dentry, &slash_name);
 	if (unlikely(!child))
 		goto out_err;
 	error = dir->i_op->tmpfile(dir, child, mode);
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 323f492e0822..253cf59b27a1 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -53,7 +53,6 @@ static void nsfs_evict(struct inode *inode)
 static void *__ns_get_path(struct path *path, struct ns_common *ns)
 {
 	struct vfsmount *mnt = nsfs_mnt;
-	struct qstr qname = { .name = "", };
 	struct dentry *dentry;
 	struct inode *inode;
 	unsigned long d;
@@ -85,7 +84,7 @@ static void *__ns_get_path(struct path *path, struct ns_common *ns)
 	inode->i_fop = &ns_file_operations;
 	inode->i_private = ns;
 
-	dentry = d_alloc_pseudo(mnt->mnt_sb, &qname);
+	dentry = d_alloc_pseudo(mnt->mnt_sb, &empty_name);
 	if (!dentry) {
 		iput(inode);
 		return ERR_PTR(-ENOMEM);
diff --git a/fs/pipe.c b/fs/pipe.c
index 73b84baf58f8..97e5be897753 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -739,13 +739,12 @@ int create_pipe_files(struct file **res, int flags)
 	struct inode *inode = get_pipe_inode();
 	struct file *f;
 	struct path path;
-	static struct qstr name = { .name = "" };
 
 	if (!inode)
 		return -ENFILE;
 
 	err = -ENOMEM;
-	path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name);
+	path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &empty_name);
 	if (!path.dentry)
 		goto err_inode;
 	path.mnt = mntget(pipe_mnt);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d2e38dc6172c..3f65a4fa72ed 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -55,6 +55,11 @@ struct qstr {
 
 #define QSTR_INIT(n,l) { { { .len = l } }, .name = n }
 
+extern const char empty_string[];
+extern const struct qstr empty_name;
+extern const char slash_string[];
+extern const struct qstr slash_name;
+
 struct dentry_stat_t {
 	long nr_dentry;
 	long nr_unused;

[toc] | [prev] | [next] | [standalone]


#1638953 — [PATCH 02/14] Clean up whitespace in fs/namespace.c

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 02/14] Clean up whitespace in fs/namespace.c
Message-ID<tFCEW-2YC-23@gated-at.bofh.it>
In reply to#1638938
Clean up line terminal whitespace in fs/namespace.c.

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

 fs/namespace.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index cc1375eff88c..db034b6afd43 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1659,7 +1659,7 @@ void __detach_mounts(struct dentry *dentry)
 	namespace_unlock();
 }
 
-/* 
+/*
  * Is the caller allowed to modify his namespace?
  */
 static inline bool may_mount(void)
@@ -2213,7 +2213,7 @@ static int do_loopback(struct path *path, const char *old_name,
 
 	err = -EINVAL;
 	if (mnt_ns_loop(old_path.dentry))
-		goto out; 
+		goto out;
 
 	mp = lock_mount(path);
 	err = PTR_ERR(mp);

[toc] | [prev] | [next] | [standalone]


#1638954 — [PATCH 10/14] procfs: Move proc_fill_super() to fs/proc/root.c

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 10/14] procfs: Move proc_fill_super() to fs/proc/root.c
Message-ID<tFCEW-2YC-25@gated-at.bofh.it>
In reply to#1638938
Move proc_fill_super() to fs/proc/root.c as that's where the other
superblock stuff is.

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

 fs/proc/inode.c    |   48 +-----------------------------------------------
 fs/proc/internal.h |    4 +---
 fs/proc/root.c     |   48 +++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 49 insertions(+), 51 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 2cc7a8030275..194fa2d13b7e 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -22,7 +22,6 @@
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/mount.h>
-#include <linux/magic.h>
 
 #include <linux/uaccess.h>
 
@@ -113,7 +112,7 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root)
 	return 0;
 }
 
-static const struct super_operations proc_sops = {
+const struct super_operations proc_sops = {
 	.alloc_inode	= proc_alloc_inode,
 	.destroy_inode	= proc_destroy_inode,
 	.drop_inode	= generic_delete_inode,
@@ -470,48 +469,3 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 	       pde_put(de);
 	return inode;
 }
-
-int proc_fill_super(struct super_block *s, void *data, int silent)
-{
-	struct pid_namespace *ns = get_pid_ns(s->s_fs_info);
-	struct inode *root_inode;
-	int ret;
-
-	if (!proc_parse_options(data, ns))
-		return -EINVAL;
-
-	/* User space would break if executables or devices appear on proc */
-	s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
-	s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
-	s->s_blocksize = 1024;
-	s->s_blocksize_bits = 10;
-	s->s_magic = PROC_SUPER_MAGIC;
-	s->s_op = &proc_sops;
-	s->s_time_gran = 1;
-
-	/*
-	 * procfs isn't actually a stacking filesystem; however, there is
-	 * too much magic going on inside it to permit stacking things on
-	 * top of it
-	 */
-	s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
-	
-	pde_get(&proc_root);
-	root_inode = proc_get_inode(s, &proc_root);
-	if (!root_inode) {
-		pr_err("proc_fill_super: get root inode failed\n");
-		return -ENOMEM;
-	}
-
-	s->s_root = d_make_root(root_inode);
-	if (!s->s_root) {
-		pr_err("proc_fill_super: allocate dentry failed\n");
-		return -ENOMEM;
-	}
-
-	ret = proc_setup_self(s);
-	if (ret) {
-		return ret;
-	}
-	return proc_setup_thread_self(s);
-}
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index c5ae09b6c726..b681533f59dd 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -197,13 +197,12 @@ struct pde_opener {
 	struct completion *c;
 };
 extern const struct inode_operations proc_link_inode_operations;
-
 extern const struct inode_operations proc_pid_link_inode_operations;
+extern const struct super_operations proc_sops;
 
 extern void proc_init_inodecache(void);
 void set_proc_pid_nlink(void);
 extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
-extern int proc_fill_super(struct super_block *, void *data, int flags);
 extern void proc_entry_rundown(struct proc_dir_entry *);
 
 /*
@@ -261,7 +260,6 @@ static inline void proc_tty_init(void) {}
  * root.c
  */
 extern struct proc_dir_entry proc_root;
-extern int proc_parse_options(char *options, struct pid_namespace *pid);
 
 extern void proc_self_init(void);
 extern int proc_remount(struct super_block *, int *, char *);
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 3c47399bd095..ee1937b37370 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -23,6 +23,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/parser.h>
 #include <linux/cred.h>
+#include <linux/magic.h>
 
 #include "internal.h"
 
@@ -36,7 +37,7 @@ static const match_table_t tokens = {
 	{Opt_err, NULL},
 };
 
-int proc_parse_options(char *options, struct pid_namespace *pid)
+static int proc_parse_options(char *options, struct pid_namespace *pid)
 {
 	char *p;
 	substring_t args[MAX_OPT_ARGS];
@@ -78,6 +79,51 @@ int proc_parse_options(char *options, struct pid_namespace *pid)
 	return 1;
 }
 
+static int proc_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct pid_namespace *ns = get_pid_ns(s->s_fs_info);
+	struct inode *root_inode;
+	int ret;
+
+	if (!proc_parse_options(data, ns))
+		return -EINVAL;
+
+	/* User space would break if executables or devices appear on proc */
+	s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
+	s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
+	s->s_blocksize = 1024;
+	s->s_blocksize_bits = 10;
+	s->s_magic = PROC_SUPER_MAGIC;
+	s->s_op = &proc_sops;
+	s->s_time_gran = 1;
+
+	/*
+	 * procfs isn't actually a stacking filesystem; however, there is
+	 * too much magic going on inside it to permit stacking things on
+	 * top of it
+	 */
+	s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
+	
+	pde_get(&proc_root);
+	root_inode = proc_get_inode(s, &proc_root);
+	if (!root_inode) {
+		pr_err("proc_fill_super: get root inode failed\n");
+		return -ENOMEM;
+	}
+
+	s->s_root = d_make_root(root_inode);
+	if (!s->s_root) {
+		pr_err("proc_fill_super: allocate dentry failed\n");
+		return -ENOMEM;
+	}
+
+	ret = proc_setup_self(s);
+	if (ret) {
+		return ret;
+	}
+	return proc_setup_thread_self(s);
+}
+
 int proc_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct pid_namespace *pid = sb->s_fs_info;

[toc] | [prev] | [next] | [standalone]


#1638955 — [PATCH 09/14] Sample program for driving fsopen/fsmount

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 09/14] Sample program for driving fsopen/fsmount
Message-ID<tFCEW-2YC-27@gated-at.bofh.it>
In reply to#1638938
---

 samples/fsmount/test-fsmount.c |   78 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
 create mode 100644 samples/fsmount/test-fsmount.c

diff --git a/samples/fsmount/test-fsmount.c b/samples/fsmount/test-fsmount.c
new file mode 100644
index 000000000000..d8d47bdd80f0
--- /dev/null
+++ b/samples/fsmount/test-fsmount.c
@@ -0,0 +1,78 @@
+/* fd-based mount test.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#define E(x) do { if ((x) == -1) { perror(#x); exit(1); } } while(0)
+
+static __attribute__((noreturn))
+void mount_error(int fd, const char *s)
+{
+	char buf[4096];
+	int err, n;
+
+	err = errno;
+	n = read(fd, buf, sizeof(buf));
+	errno = err;
+	if (n > 0) {
+		n -= 2;
+		fprintf(stderr, "Error: '%s': %*.*s: %m\n", s, n, n, buf + 2);
+	} else {
+		fprintf(stderr, "%s: %m\n", s);
+	}
+	exit(1);
+}
+
+#define E_write(fd, s)							\
+	do {								\
+		if (write(fd, s, sizeof(s) - 1) == -1)			\
+			mount_error(fd, s);				\
+	} while (0)
+
+static inline int fsopen(const char *fs_name, int reserved, int flags)
+{
+	return syscall(333, fs_name, reserved, flags);
+}
+
+static inline int fsmount(int fsfd, int dfd, const char *path)
+{
+	return syscall(334, fsfd, dfd, path);
+}
+
+int main()
+{
+	int mfd;
+
+	/* Mount an NFS filesystem */
+	mfd = fsopen("nfs4", -1, 0);
+	if (mfd == -1) {
+		perror("fsopen");
+		exit(1);
+	}
+
+	E_write(mfd, "s warthog:/data");
+	E_write(mfd, "o fsc");
+	E_write(mfd, "o sync");
+	E_write(mfd, "o intr");
+	E_write(mfd, "o vers=4.2");
+	E_write(mfd, "o addr=90.155.74.18");
+	E_write(mfd, "o clientaddr=90.155.74.21");
+	if (fsmount(mfd, AT_FDCWD, "/mnt") < 0)
+		mount_error(mfd, "fsmount");
+	E(close(mfd));
+
+	exit(0);
+}

[toc] | [prev] | [next] | [standalone]


#1638956 — [PATCH 04/14] VFS: Make get_filesystem() return the affected filesystem

FromDavid Howells <dhowells@redhat.com>
Date2017-05-10 18:30 +0200
Subject[PATCH 04/14] VFS: Make get_filesystem() return the affected filesystem
Message-ID<tFCEX-2YC-33@gated-at.bofh.it>
In reply to#1638938
Make get_filesystem() return a pointer to the filesystem on which it just
got a ref.

Suggested-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/filesystems.c   |    3 ++-
 include/linux/fs.h |    2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/filesystems.c b/fs/filesystems.c
index cac75547d35c..591e52d23ed4 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -33,9 +33,10 @@ static struct file_system_type *file_systems;
 static DEFINE_RWLOCK(file_systems_lock);
 
 /* WARNING: This can be used only if we _already_ own a reference */
-void get_filesystem(struct file_system_type *fs)
+struct file_system_type *get_filesystem(struct file_system_type *fs)
 {
 	__module_get(fs->owner);
+	return fs;
 }
 
 void put_filesystem(struct file_system_type *fs)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 30e5c14bd743..45ac992133fc 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2953,7 +2953,7 @@ extern int generic_block_fiemap(struct inode *inode,
 				struct fiemap_extent_info *fieinfo, u64 start,
 				u64 len, get_block_t *get_block);
 
-extern void get_filesystem(struct file_system_type *fs);
+extern struct file_system_type *get_filesystem(struct file_system_type *fs);
 extern void put_filesystem(struct file_system_type *fs);
 extern struct file_system_type *get_fs_type(const char *name);
 extern struct super_block *get_super(struct block_device *);

[toc] | [prev] | [next] | [standalone]


#1639218 — Re: [PATCH 06/14] VFS: Introduce a superblock configuration context

FromAl Viro <viro@ZenIV.linux.org.uk>
Date2017-05-11 09:30 +0200
SubjectRe: [PATCH 06/14] VFS: Introduce a superblock configuration context
Message-ID<tFQHT-3k5-11@gated-at.bofh.it>
In reply to#1638938
On Wed, May 10, 2017 at 05:19:19PM +0100, David Howells wrote:
> + (*) struct mnt_namespace *mnt_ns
> +
> +     This is a subset of the namespaces in use by the invoking process.  This
> +     retains a ref on each namespace.  The subscribed namespaces may be
> +     replaced by the filesystem to reflect other sources, such as the parent
> +     mount superblock on an automount.

I don't think it's a good idea.  No comments on userns stuff, but what's
the situation when you want it to play with the real namespace?  Details,
please...

> + (*) int (*fill_super)(struct super_block *s, struct sb_config *sc);
> +
> +     This is available to be used by things like mount_ns_mc() that are called
> +     by ->mount() to transfer information/resources from the superblock configuration context to
> +     the superblock.

        Don't.  This kind of stuff can bloody well be an explicit callback.
Methods of that kind are trouble - we had that sort of PITA quite a few
times, and it had always been a headache when we eventually had to kill them
off.  Starting with ->read_inode(), if you remember that one...

[toc] | [prev] | [next] | [standalone]


#1639220 — Re: [PATCH 14/14] Add commands to create or update a superblock

FromAl Viro <viro@ZenIV.linux.org.uk>
Date2017-05-11 09:40 +0200
SubjectRe: [PATCH 14/14] Add commands to create or update a superblock
Message-ID<tFQRA-3na-13@gated-at.bofh.it>
In reply to#1638938
On Wed, May 10, 2017 at 05:20:31PM +0100, David Howells wrote:
  
> @@ -64,8 +70,8 @@ struct sb_config_operations {
>  	int (*parse_option)(struct sb_config *sc, char *p);
>  	int (*monolithic_mount_data)(struct sb_config *sc, void *data);
>  	int (*validate)(struct sb_config *sc);
> -	struct dentry *(*mount)(struct sb_config *sc);
> -	int (*fill_super)(struct super_block *s, struct sb_config *sc);
> +	int (*create_super)(struct sb_config *sc);

Hell, NO.

The primary effect of that thing is *NOT* to create a superblock.  It
might be a side effect, and quite often it will happen, but the
real goal here is a mountable tree.  Which might or might not reside
on a new superblock.  And which might very well involve no object
creation whatsoever.

This name is actively misleading and the same goes for its relatives
(vfs_create_super(), etc.).  It's "give me a tree to mount", not
"create something or other".

[toc] | [prev] | [next] | [standalone]


#1639238 — Re: [PATCH 14/14] Add commands to create or update a superblock

FromMiklos Szeredi <mszeredi@redhat.com>
Date2017-05-11 10:20 +0200
SubjectRe: [PATCH 14/14] Add commands to create or update a superblock
Message-ID<tFRuh-3UA-7@gated-at.bofh.it>
In reply to#1639220
On Thu, May 11, 2017 at 9:38 AM, Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Wed, May 10, 2017 at 05:20:31PM +0100, David Howells wrote:
>
>> @@ -64,8 +70,8 @@ struct sb_config_operations {
>>       int (*parse_option)(struct sb_config *sc, char *p);
>>       int (*monolithic_mount_data)(struct sb_config *sc, void *data);
>>       int (*validate)(struct sb_config *sc);
>> -     struct dentry *(*mount)(struct sb_config *sc);
>> -     int (*fill_super)(struct super_block *s, struct sb_config *sc);
>> +     int (*create_super)(struct sb_config *sc);
>
> Hell, NO.
>
> The primary effect of that thing is *NOT* to create a superblock.  It
> might be a side effect, and quite often it will happen, but the
> real goal here is a mountable tree.  Which might or might not reside
> on a new superblock.  And which might very well involve no object
> creation whatsoever.
>
> This name is actively misleading and the same goes for its relatives
> (vfs_create_super(), etc.).  It's "give me a tree to mount", not
> "create something or other".

Indeed, I haven't thought of that.  And there's a good hornet's nest
waiting to poke ones hand into: mounting the same blockdev with
different options will get you either

 - EBUSY if the MS_* flags mismatch
 - silently ignored options in any other case

which is far from being nice and consistent.

Thanks,
Miklos

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web