Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1639484 > unrolled thread
| Started by | David Howells <dhowells@redhat.com> |
|---|---|
| First post | 2017-05-11 16:10 +0200 |
| Last post | 2017-05-11 21:30 +0200 |
| Articles | 11 — 1 participant |
Back to article view | Back to linux.kernel
[RFC][PATCH 00/14] VFS: Introduce superblock configuration context [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 16:10 +0200
[PATCH 03/14] VFS: Make get_mnt_ns() return the namespace [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 02/14] Clean up whitespace in fs/namespace.c [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 07/14] Implement fsopen() to prepare for a mount [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 10/14] procfs: Move proc_fill_super() to fs/proc/root.c [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 05/14] VFS: Provide empty name qstr [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 04/14] VFS: Make get_filesystem() return the affected filesystem [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 01/14] Provide a function to create a NUL-terminated string from unterminated data [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:30 +0200
[PATCH 08/14] Implement fsmount() to effect a pre-configured mount [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:40 +0200
[PATCH 09/14] Sample program for driving fsopen/fsmount [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 18:40 +0200
Re: [RFC][PATCH 00/14] VFS: Introduce superblock configuration context [ver #2] David Howells <dhowells@redhat.com> - 2017-05-11 21:30 +0200
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 16:10 +0200 |
| Subject | [RFC][PATCH 00/14] VFS: Introduce superblock configuration context [ver #2] |
| Message-ID | <tFWWZ-7l8-3@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:
ver #2:
(*) Removed the ->fill_super() from sb_config_operations and passed it in
directly to functions that want to call it. NFS now calls
nfs_fill_super() directly rather than jumping through a pointer to it
since there's only the one option at the moment.
(*) Removed ->mnt_ns and ->sb from sb_config and moved ->pid_ns into
proc_sb_config.
(*) Renamed create_super -> get_tree.
(*) Renamed struct mount_context to struct sb_config and amended various
variable names.
(*) sys_fsmount() acquired AT_* flags and MS_* flags (for MNT_* flags)
arguments.
ver #1:
(*) 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:
(*) 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 | 470 ++++++++
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 | 76 +
fs/nfs/internal.h | 142 +--
fs/nfs/mount.c | 1497 +++++++++++++++++++++++++++
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 | 220 ++--
fs/nfs/proc.c | 2
fs/nfs/super.c | 1782 ++------------------------------
fs/nsfs.c | 3
fs/pipe.c | 3
fs/proc/inode.c | 50 -
fs/proc/internal.h | 6
fs/proc/root.c | 210 +++-
fs/sb_config.c | 524 +++++++++
fs/super.c | 110 +-
include/linux/dcache.h | 5
include/linux/fs.h | 16
include/linux/lsm_hooks.h | 47 +
include/linux/mount.h | 4
include/linux/nfs_xdr.h | 7
include/linux/sb_config.h | 100 ++
include/linux/security.h | 40 +
include/linux/string.h | 1
include/linux/syscalls.h | 3
include/uapi/linux/magic.h | 1
kernel/sys_ni.c | 4
mm/util.c | 24
samples/fsmount/test-fsmount.c | 79 +
security/security.c | 45 +
security/selinux/hooks.c | 202 +++-
51 files changed, 4596 insertions(+), 2381 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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 03/14] VFS: Make get_mnt_ns() return the namespace [ver #2] |
| Message-ID | <tFZ8t-dN-1@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 02/14] Clean up whitespace in fs/namespace.c [ver #2] |
| Message-ID | <tFZ8t-dN-5@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 07/14] Implement fsopen() to prepare for a mount [ver #2] |
| Message-ID | <tFZ8u-dN-13@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 10/14] procfs: Move proc_fill_super() to fs/proc/root.c [ver #2] |
| Message-ID | <tFZ8u-dN-15@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 05/14] VFS: Provide empty name qstr [ver #2] |
| Message-ID | <tFZ8u-dN-29@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 04/14] VFS: Make get_filesystem() return the affected filesystem [ver #2] |
| Message-ID | <tFZ8v-dN-41@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:30 +0200 |
| Subject | [PATCH 01/14] Provide a function to create a NUL-terminated string from unterminated data [ver #2] |
| Message-ID | <tFZ8v-dN-47@gated-at.bofh.it> |
| In reply to | #1639484 |
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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:40 +0200 |
| Subject | [PATCH 08/14] Implement fsmount() to effect a pre-configured mount [ver #2] |
| Message-ID | <tFZia-ha-17@gated-at.bofh.it> |
| In reply to | #1639484 |
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,
unsigned int at_flags, unsigned int flags);
where fsfd is the fd returned by fsopen(), dfd, path and at_flags locate
the mountpoint and flags are the applicable MS_* flags. 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 | 99 ++++++++++++++++++++++++++++++++
include/linux/lsm_hooks.h | 6 ++
include/linux/security.h | 6 ++
include/linux/syscalls.h | 2 +
kernel/sys_ni.c | 1
security/security.c | 7 ++
security/selinux/hooks.c | 13 ++++
9 files changed, 136 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 8ade7252ee34..6e43657d78bd 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3253,6 +3253,105 @@ 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_DEFINE5(fsmount, int, fs_fd, int, dfd, const char __user *, dir_name,
+ unsigned int, at_flags, unsigned int, flags)
+{
+ struct sb_config *sc;
+ struct inode *inode;
+ struct path mountpoint;
+ struct fd f;
+ unsigned int lookup_flags, mnt_flags = 0;
+ long ret;
+
+ if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ AT_EMPTY_PATH)) != 0)
+ return -EINVAL;
+
+ if (flags & ~(MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC |
+ MS_NOATIME | MS_NODIRATIME | MS_RELATIME | MS_STRICTATIME))
+ return -EINVAL;
+
+ if (flags & MS_RDONLY)
+ mnt_flags |= MNT_READONLY;
+ if (flags & MS_NOSUID)
+ mnt_flags |= MNT_NOSUID;
+ if (flags & MS_NODEV)
+ mnt_flags |= MNT_NODEV;
+ if (flags & MS_NOEXEC)
+ mnt_flags |= MNT_NOEXEC;
+ if (flags & MS_NODIRATIME)
+ mnt_flags |= MNT_NODIRATIME;
+
+ if (flags & MS_STRICTATIME) {
+ if (flags & MS_NOATIME)
+ return -EINVAL;
+ } else if (flags & MS_NOATIME) {
+ mnt_flags |= MNT_NOATIME;
+ } else {
+ mnt_flags |= MNT_RELATIME;
+ }
+
+ f = fdget(fs_fd);
+ 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. */
+ lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ if (at_flags & AT_SYMLINK_NOFOLLOW)
+ lookup_flags &= ~LOOKUP_FOLLOW;
+ if (at_flags & AT_NO_AUTOMOUNT)
+ lookup_flags &= ~LOOKUP_AUTOMOUNT;
+ if (at_flags & AT_EMPTY_PATH)
+ lookup_flags |= LOOKUP_EMPTY;
+ ret = user_path_at(dfd, dir_name, lookup_flags, &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;
+
+ 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..07e4f775f24d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -906,5 +906,7 @@ 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, unsigned int at_flags,
+ unsigned int flags);
#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]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 18:40 +0200 |
| Subject | [PATCH 09/14] Sample program for driving fsopen/fsmount [ver #2] |
| Message-ID | <tFZia-ha-19@gated-at.bofh.it> |
| In reply to | #1639484 |
---
samples/fsmount/test-fsmount.c | 79 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 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..98b0258ae08f
--- /dev/null
+++ b/samples/fsmount/test-fsmount.c
@@ -0,0 +1,79 @@
+/* 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,
+ unsigned int at_flags, unsigned int flags)
+{
+ return syscall(334, fsfd, dfd, path, at_flags, flags);
+}
+
+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, 0) < 0)
+ mount_error(mfd, "fsmount");
+ E(close(mfd));
+
+ exit(0);
+}
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-11 21:30 +0200 |
| Subject | Re: [RFC][PATCH 00/14] VFS: Introduce superblock configuration context [ver #2] |
| Message-ID | <tG1WG-20E-19@gated-at.bofh.it> |
| In reply to | #1639484 |
Anna Schumaker <Anna.Schumaker@Netapp.com> wrote: > Is there any way to split the NFS patch into multiple pieces? Are you okay with a patch or two that add code that is unconnected in that patch, but connected in a later one? David
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web