Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1635100 > unrolled thread
| Started by | David Howells <dhowells@redhat.com> |
|---|---|
| First post | 2017-05-03 18:10 +0200 |
| Last post | 2017-05-08 19:10 +0200 |
| Articles | 20 on this page of 61 — 10 participants |
Back to article view | Back to linux.kernel
[RFC][PATCH 0/9] VFS: Introduce mount context David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
[PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data Jeff Layton <jlayton@poochiereds.net> - 2017-05-03 19:00 +0200
Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data Rasmus Villemoes <linux@rasmusvillemoes.dk> - 2017-05-03 21:30 +0200
Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data David Howells <dhowells@redhat.com> - 2017-05-03 22:20 +0200
[PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Jeff Layton <jlayton@poochiereds.net> - 2017-05-03 20:40 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-03 20:50 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Rasmus Villemoes <linux@rasmusvillemoes.dk> - 2017-05-03 22:50 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-04 15:00 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-04 15:00 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Karel Zak <kzak@redhat.com> - 2017-05-04 12:50 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-04 15:10 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Karel Zak <kzak@redhat.com> - 2017-05-04 15:40 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Jeff Layton <jlayton@redhat.com> - 2017-05-09 20:50 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount Miklos Szeredi <mszeredi@redhat.com> - 2017-05-08 17:20 +0200
Re: [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells <dhowells@redhat.com> - 2017-05-09 01:20 +0200
[PATCH 5/9] Implement fsmount() to effect a pre-configured mount David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
[PATCH 2/9] Clean up whitespace in fs/namespace.c David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
[PATCH 8/9] proc: Support the mount context in procfs David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
[PATCH 7/9] procfs: Move proc_fill_super() to fs/proc/root.c David Howells <dhowells@redhat.com> - 2017-05-03 18:10 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Jeff Layton <jlayton@poochiereds.net> - 2017-05-03 18:50 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context David Howells <dhowells@redhat.com> - 2017-05-03 19:00 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Jeff Layton <jlayton@poochiereds.net> - 2017-05-03 19:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Joe Perches <joe@perches.com> - 2017-05-03 20:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-03 20:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Joe Perches <joe@perches.com> - 2017-05-03 20:50 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-03 22:20 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Matthew Wilcox <willy@infradead.org> - 2017-05-03 22:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-03 23:20 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Joe Perches <joe@perches.com> - 2017-05-03 23:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Julia Lawall <julia.lawall@lip6.fr> - 2017-05-04 08:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-04 11:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Joe Perches <joe@perches.com> - 2017-05-04 16:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Rasmus Villemoes <linux@rasmusvillemoes.dk> - 2017-05-03 23:50 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-04 12:30 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-05 16:40 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context David Howells <dhowells@redhat.com> - 2017-05-05 17:50 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-08 10:30 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context David Howells <dhowells@redhat.com> - 2017-05-08 10:40 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-08 10:50 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-08 17:10 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-09 01:00 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-09 10:10 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-09 11:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-09 13:10 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-09 11:50 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-09 14:10 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Jeff Layton <jlayton@redhat.com> - 2017-05-09 21:00 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-10 09:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-10 10:10 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Jeff Layton <jlayton@redhat.com> - 2017-05-10 15:30 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-10 15:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Jeff Layton <jlayton@redhat.com> - 2017-05-10 15:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-10 15:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-10 15:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Jeff Layton <jlayton@redhat.com> - 2017-05-10 15:50 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context David Howells <dhowells@redhat.com> - 2017-05-09 12:00 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Miklos Szeredi <mszeredi@redhat.com> - 2017-05-09 14:40 +0200
Re: [PATCH 3/9] VFS: Introduce a mount context Karel Zak <kzak@redhat.com> - 2017-05-10 14:50 +0200
Re: [RFC][PATCH 0/9] VFS: Introduce mount context Djalal Harouni <tixxdz@gmail.com> - 2017-05-08 19:10 +0200
Page 1 of 4 [1] 2 3 4 Next page →
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 18:10 +0200 |
| Subject | [RFC][PATCH 0/9] VFS: Introduce mount context |
| Message-ID | <tD50J-7GP-3@gated-at.bofh.it> |
Here are a set of patches to create a mount context prior to setting up a
new mount, populating it with the parsed options/binary data 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.
Further developments:
(*) 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 (9):
Provide a function to create a NUL-terminated string from unterminated data
Clean up whitespace in fs/namespace.c
VFS: Introduce a mount 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: Support the mount context in procfs
NFS: Support the mount context and fsopen()
Documentation/filesystems/mounting.txt | 445 ++++++++
arch/x86/entry/syscalls/syscall_32.tbl | 2
arch/x86/entry/syscalls/syscall_64.tbl | 2
fs/Makefile | 3
fs/fsopen.c | 295 +++++
fs/internal.h | 2
fs/mount.h | 3
fs/mount_context.c | 343 ++++++
fs/namespace.c | 367 ++++++-
fs/nfs/Makefile | 2
fs/nfs/client.c | 18
fs/nfs/internal.h | 127 +-
fs/nfs/mount.c | 1539 ++++++++++++++++++++++++++++
fs/nfs/namespace.c | 75 +
fs/nfs/nfs3_fs.h | 2
fs/nfs/nfs3client.c | 6
fs/nfs/nfs3proc.c | 1
fs/nfs/nfs4_fs.h | 4
fs/nfs/nfs4client.c | 80 +
fs/nfs/nfs4namespace.c | 207 ++--
fs/nfs/nfs4proc.c | 1
fs/nfs/nfs4super.c | 184 ++-
fs/nfs/proc.c | 1
fs/nfs/super.c | 1729 ++------------------------------
fs/proc/inode.c | 50 -
fs/proc/internal.h | 6
fs/proc/root.c | 194 +++-
fs/super.c | 50 +
include/linux/fs.h | 11
include/linux/lsm_hooks.h | 43 +
include/linux/mount.h | 67 +
include/linux/nfs_xdr.h | 7
include/linux/security.h | 35 +
include/linux/string.h | 1
include/linux/syscalls.h | 2
include/uapi/linux/magic.h | 1
kernel/sys_ni.c | 4
mm/util.c | 22
samples/fsmount/test-fsmount.c | 79 +
security/security.c | 39 +
security/selinux/hooks.c | 192 ++++
41 files changed, 4148 insertions(+), 2093 deletions(-)
create mode 100644 Documentation/filesystems/mounting.txt
create mode 100644 fs/fsopen.c
create mode 100644 fs/mount_context.c
create mode 100644 fs/nfs/mount.c
create mode 100644 samples/fsmount/test-fsmount.c
[toc] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 18:10 +0200 |
| Subject | [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data |
| Message-ID | <tD50J-7GP-13@gated-at.bofh.it> |
| In reply to | #1635100 |
Provide a function, kstrcreate(), 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 | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/include/linux/string.h b/include/linux/string.h
index 26b6f6a66f83..5596ae56ce0a 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -122,6 +122,7 @@ extern void kfree_const(const void *x);
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 char *kstrcreate(const char *s, size_t len, gfp_t gfp);
extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
diff --git a/mm/util.c b/mm/util.c
index 656dc5e37a87..01887bbdb11e 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -103,6 +103,28 @@ char *kstrndup(const char *s, size_t max, gfp_t gfp)
EXPORT_SYMBOL(kstrndup);
/**
+ * kstrcreate - 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 *kstrcreate(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(kstrcreate);
+
+/**
* kmemdup - duplicate region of memory
*
* @src: memory region to duplicate
[toc] | [prev] | [next] | [standalone]
| From | Jeff Layton <jlayton@poochiereds.net> |
|---|---|
| Date | 2017-05-03 19:00 +0200 |
| Subject | Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data |
| Message-ID | <tD5N7-7Y4-11@gated-at.bofh.it> |
| In reply to | #1635104 |
On Wed, 2017-05-03 at 17:04 +0100, David Howells wrote:
> Provide a function, kstrcreate(), 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 | 22 ++++++++++++++++++++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/include/linux/string.h b/include/linux/string.h
> index 26b6f6a66f83..5596ae56ce0a 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -122,6 +122,7 @@ extern void kfree_const(const void *x);
> 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 char *kstrcreate(const char *s, size_t len, gfp_t gfp);
> extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
>
> extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
> diff --git a/mm/util.c b/mm/util.c
> index 656dc5e37a87..01887bbdb11e 100644
> --- a/mm/util.c
> +++ b/mm/util.c
> @@ -103,6 +103,28 @@ char *kstrndup(const char *s, size_t max, gfp_t gfp)
> EXPORT_SYMBOL(kstrndup);
>
> /**
> + * kstrcreate - 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 *kstrcreate(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(kstrcreate);
> +
> +/**
> * kmemdup - duplicate region of memory
> *
> * @src: memory region to duplicate
>
>
I haven't gotten to the part where this gets used yet, but it looks like
a nice helper.
Reviewed-by: Jeff Layton <jlayton@poochiereds.net>
[toc] | [prev] | [next] | [standalone]
| From | Rasmus Villemoes <linux@rasmusvillemoes.dk> |
|---|---|
| Date | 2017-05-03 21:30 +0200 |
| Subject | Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data |
| Message-ID | <tD88h-1cl-17@gated-at.bofh.it> |
| In reply to | #1635104 |
On Wed, May 03 2017, David Howells <dhowells@redhat.com> wrote: > Provide a function, kstrcreate() <bikeshed>why not kmemdup_nul, since it seems to be to kmemdup exactly as memdup_user_nul is to memdup_user?</bikeshed> Rasmus
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 22:20 +0200 |
| Subject | Re: [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data |
| Message-ID | <tD8UF-1LK-13@gated-at.bofh.it> |
| In reply to | #1635230 |
Rasmus Villemoes <linux@rasmusvillemoes.dk> wrote: > > Provide a function, kstrcreate() > > <bikeshed>why not kmemdup_nul, since it seems to be to kmemdup exactly as > memdup_user_nul is to memdup_user?</bikeshed> Yeah, that would work too. Or kmem2str(). David
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 18:10 +0200 |
| Subject | [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tD50J-7GP-17@gated-at.bofh.it> |
| In reply to | #1635100 |
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, "d /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, "d %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 | 295 ++++++++++++++++++++++++++++++++
include/linux/syscalls.h | 1
include/uapi/linux/magic.h | 1
kernel/sys_ni.c | 3
7 files changed, 303 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 308a104a9a07..b79024dbb37c 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 \
- mount_context.o
+ mount_context.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..f02ea7d265db
--- /dev/null
+++ b/fs/fsopen.c
@@ -0,0 +1,295 @@
+/* fsopen.c: description
+ *
+ * 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/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 struct qstr empty_name = { .name = "" };
+
+static int fs_fs_release(struct inode *inode, struct file *file)
+{
+ struct mount_context *mc = file->private_data;
+
+ file->private_data = NULL;
+
+ put_mount_context(mc);
+ 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 mount_context *mc = file->private_data;
+ const char *msg;
+ size_t mlen;
+
+ msg = mc->error;
+ 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 mount_context *mc = 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 'd':
+ case 'o':
+ case 'r':
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (opt[1] != ' ')
+ return -EINVAL;
+
+ data = kmalloc(len - 2 + 1, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = -EFAULT;
+ if (copy_from_user(data, _buf + 2, len - 2) != 0)
+ goto err_free;
+ data[len - 2] = 0;
+
+ /* From this point onwards we need to lock the fd against someone
+ * trying to mount it.
+ */
+ ret = inode_lock_killable(inode);
+ if (ret < 0)
+ return ret;
+
+ ret = -EBUSY;
+ if (mc->mounted)
+ goto err_unlock;
+
+ ret = -EINVAL;
+ switch (opt[0]) {
+ case 'd':
+ if (mc->device)
+ goto err_unlock;
+ mc->device = data;
+ data = NULL;
+ break;
+
+ case 'o':
+ ret = vfs_mount_option(mc, data);
+ if (ret < 0)
+ goto err_unlock;
+ break;
+
+ case 'r':
+ if (mc->root_path)
+ goto err_unlock;
+ mc->root_path = data;
+ data = NULL;
+ 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 mount_context *mc)
+{
+ 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 = mc;
+ return f;
+
+err_file:
+ path_put(&path);
+ return ERR_PTR(ret);
+
+err_inode:
+ iput(inode);
+ return ERR_PTR(ret);
+}
+
+static 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 mount_context *mc;
+ 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);
+
+ mc = vfs_fsopen(fs_name);
+ if (IS_ERR(mc)) {
+ ret = PTR_ERR(mc);
+ goto err_fs_name;
+ }
+
+ ret = -ENOTSUPP;
+ if (!mc->ops)
+ goto err_mc;
+
+ file = create_fs_file(mc);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err_mc;
+ }
+
+ 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_mc:
+ put_mount_context(mc);
+err_fs_name:
+ kfree(fs_name);
+ 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 | Jeff Layton <jlayton@poochiereds.net> |
|---|---|
| Date | 2017-05-03 20:40 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tD7lT-DA-19@gated-at.bofh.it> |
| In reply to | #1635106 |
On Wed, 2017-05-03 at 17:05 +0100, David Howells 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, "d /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, "d %grand.central.org:root.cell");
> write(mfd, "o cell=grand.central.org");
> write(mfd, "r /");
> fsmount(mfd, AT_FDCWD, "/mnt", 0);
>
I think one of the neat things here is that we can now error out as
soon as a bogus mount option is passed in.
That takes out some of the guesswork on which option is the problem
when you get back something like -EINVAL. On something like CIFS (which
has a lot of crazy mount options) that is much more useful.
> 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"
>
I like this too. No need to printk stuff when there is a problem
mounting. The error goes straight to the caller who initiated the
mount.
That's a very nice thing in heavily containerized environments, for
instance. If you're doing mounts in a container and they fail, then
trying to scrape dmesg and figure out which messages refer to _your_
mount attempt seems like it could be rather nasty. This solves that
problem in a much more sane way, IMO.
> Once fsmount() has been called, further write() calls will incur EBUSY,
> even if the fsmount() fails. read() is still possible to retrieve error
> information.
>
What's the rationale for the above behavior?
A failed attempt to graft it into the tree doesn't seem like it would
have any real effect on the mount_context. While I can't think of a use
case for being able to try fsmount() again, I don't quite understand
why we'd prohibit someone from doing it.
> 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 | 295 ++++++++++++++++++++++++++++++++
> include/linux/syscalls.h | 1
> include/uapi/linux/magic.h | 1
> kernel/sys_ni.c | 3
> 7 files changed, 303 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 308a104a9a07..b79024dbb37c 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 \
> - mount_context.o
> + mount_context.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..f02ea7d265db
> --- /dev/null
> +++ b/fs/fsopen.c
> @@ -0,0 +1,295 @@
> +/* fsopen.c: description
> + *
> + * 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/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 struct qstr empty_name = { .name = "" };
> +
> +static int fs_fs_release(struct inode *inode, struct file *file)
> +{
> + struct mount_context *mc = file->private_data;
> +
> + file->private_data = NULL;
> +
> + put_mount_context(mc);
> + 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 mount_context *mc = file->private_data;
> + const char *msg;
> + size_t mlen;
> +
> + msg = mc->error;
> + 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 mount_context *mc = 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 'd':
> + case 'o':
> + case 'r':
> + break;
> + default:
> + return -EINVAL;
> + }
> + if (opt[1] != ' ')
> + return -EINVAL;
> +
> + data = kmalloc(len - 2 + 1, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = -EFAULT;
> + if (copy_from_user(data, _buf + 2, len - 2) != 0)
> + goto err_free;
> + data[len - 2] = 0;
> +
> + /* From this point onwards we need to lock the fd against someone
> + * trying to mount it.
> + */
> + ret = inode_lock_killable(inode);
> + if (ret < 0)
> + return ret;
> +
> + ret = -EBUSY;
> + if (mc->mounted)
> + goto err_unlock;
> +
> + ret = -EINVAL;
> + switch (opt[0]) {
> + case 'd':
> + if (mc->device)
> + goto err_unlock;
> + mc->device = data;
> + data = NULL;
> + break;
> +
> + case 'o':
> + ret = vfs_mount_option(mc, data);
> + if (ret < 0)
> + goto err_unlock;
> + break;
> +
> + case 'r':
> + if (mc->root_path)
> + goto err_unlock;
> + mc->root_path = data;
> + data = NULL;
> + 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 mount_context *mc)
> +{
> + 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 = mc;
> + return f;
> +
> +err_file:
> + path_put(&path);
> + return ERR_PTR(ret);
> +
> +err_inode:
> + iput(inode);
> + return ERR_PTR(ret);
> +}
> +
> +static 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 mount_context *mc;
> + 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);
> +
> + mc = vfs_fsopen(fs_name);
> + if (IS_ERR(mc)) {
> + ret = PTR_ERR(mc);
> + goto err_fs_name;
> + }
> +
> + ret = -ENOTSUPP;
> + if (!mc->ops)
> + goto err_mc;
> +
> + file = create_fs_file(mc);
> + if (IS_ERR(file)) {
> + ret = PTR_ERR(file);
> + goto err_mc;
> + }
> +
> + 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_mc:
> + put_mount_context(mc);
> +err_fs_name:
> + kfree(fs_name);
> + 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);
>
--
Jeff Layton <jlayton@poochiereds.net>
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 20:50 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tD7vA-Hg-15@gated-at.bofh.it> |
| In reply to | #1635200 |
Jeff Layton <jlayton@poochiereds.net> wrote: > I think one of the neat things here is that we can now error out as > soon as a bogus mount option is passed in. It means that the 'sloppy' option can also now be implemented in userspace. > > Once fsmount() has been called, further write() calls will incur EBUSY, > > even if the fsmount() fails. read() is still possible to retrieve error > > information. > > > > What's the rationale for the above behavior? > > A failed attempt to graft it into the tree doesn't seem like it would > have any real effect on the mount_context. While I can't think of a use > case for being able to try fsmount() again, I don't quite understand > why we'd prohibit someone from doing it. The mount procedure is allowed to preallocate resources and attach them to the mount context and ->mount() is allowed to use them up, say by transferring them to the superblock. The mount context is then in a degraded state and cannot necessarily be reused. David
[toc] | [prev] | [next] | [standalone]
| From | Rasmus Villemoes <linux@rasmusvillemoes.dk> |
|---|---|
| Date | 2017-05-03 22:50 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tD9nI-1VW-19@gated-at.bofh.it> |
| In reply to | #1635106 |
On Wed, May 03 2017, David Howells <dhowells@redhat.com> wrote:
> --- /dev/null
> +++ b/fs/fsopen.c
> @@ -0,0 +1,295 @@
> +/* fsopen.c: description
> + *
leftover from some template?
> + * 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/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 struct qstr empty_name = { .name = "" };
> +
> +static int fs_fs_release(struct inode *inode, struct file *file)
> +{
> + struct mount_context *mc = file->private_data;
> +
> + file->private_data = NULL;
> +
> + put_mount_context(mc);
> + 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 mount_context *mc = file->private_data;
> + const char *msg;
> + size_t mlen;
> +
> + msg = mc->error;
> + 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;
> +}
OK, mc->error must be static data, so no lifetime problems. But is it
possible for the compiler to mess this up and reload msg from mc->error
when it's about to do the user copy, so that if some other thread has
managed to change mc->error (or is the error state sticky and no further
operations allowed?) we'd copy from a string with a different length?
> +/*
> + * 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 mount_context *mc = 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 'd':
> + case 'o':
> + case 'r':
> + break;
> + default:
> + return -EINVAL;
> + }
> + if (opt[1] != ' ')
> + return -EINVAL;
> +
> + data = kmalloc(len - 2 + 1, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = -EFAULT;
> + if (copy_from_user(data, _buf + 2, len - 2) != 0)
> + goto err_free;
> + data[len - 2] = 0;
> +
This hunk seems to be equivalent to
data = memdup_user_nul(_buf + 2, len - 2);
if (IS_ERR(data))
return PTR_ERR(data);
plus the err_free: label gets killed...
>
> + /* From this point onwards we need to lock the fd against someone
> + * trying to mount it.
> + */
> + ret = inode_lock_killable(inode);
> + if (ret < 0)
> + return ret;
...except that we need to jump to it here to avoid leaking data.
> + ret = -EBUSY;
> + if (mc->mounted)
> + goto err_unlock;
> +
> + ret = -EINVAL;
> + switch (opt[0]) {
> + case 'd':
> + if (mc->device)
> + goto err_unlock;
> + mc->device = data;
> + data = NULL;
> + break;
> +
> + case 'o':
> + ret = vfs_mount_option(mc, data);
> + if (ret < 0)
> + goto err_unlock;
> + break;
> +
> + case 'r':
> + if (mc->root_path)
> + goto err_unlock;
> + mc->root_path = data;
> + data = NULL;
> + 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,
> +};
> +
static const struct ?
> +/*
> + * 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 mount_context *mc;
> + 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);
> +
> + mc = vfs_fsopen(fs_name);
> + if (IS_ERR(mc)) {
> + ret = PTR_ERR(mc);
> + goto err_fs_name;
> + }
> +
Where does fs_name now get freed? vfs_fsopen doesn't seem to do it on
success? (If it did, the fallthrough from err_mc: to err_fs_name: would
be wrong.)
> + ret = -ENOTSUPP;
> + if (!mc->ops)
> + goto err_mc;
> +
> + file = create_fs_file(mc);
> + if (IS_ERR(file)) {
> + ret = PTR_ERR(file);
> + goto err_mc;
> + }
> +
> + 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_mc:
> + put_mount_context(mc);
> +err_fs_name:
> + kfree(fs_name);
> + return ret;
> +}
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-04 15:00 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tDowp-3yX-1@gated-at.bofh.it> |
| In reply to | #1635274 |
Rasmus Villemoes <linux@rasmusvillemoes.dk> wrote: > OK, mc->error must be static data, so no lifetime problems. Technically, mc->error is only good as long as we hold a ref on its module. It might be better to copy the string, especially as we can then do printf-style formatting. > But is it possible for the compiler to mess this up and reload msg from > mc->error when it's about to do the user copy, so that if some other thread > has managed to change mc->error (or is the error state sticky and no further > operations allowed?) we'd copy from a string with a different length? Yeah - I need to put a READ_ONCE() in there. > Where does fs_name now get freed? vfs_fsopen doesn't seem to do it on > success? (If it did, the fallthrough from err_mc: to err_fs_name: would > be wrong.) I'll add a free right after the vfs_fsopen() call before checking the error - then I can get rid of err_fs_name. David
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-04 15:00 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tDowp-3yX-13@gated-at.bofh.it> |
| In reply to | #1635274 |
Rasmus Villemoes <linux@rasmusvillemoes.dk> wrote:
> > +const struct file_operations fs_fs_fops = {
> > + .read = fs_fs_read,
> > + .write = fs_fs_write,
> > + .release = fs_fs_release,
> > + .llseek = no_llseek,
> > +};
> > +
>
> static const struct ?
No. It's used in the next patch to validate the fd passed to sys_fsmount():
if (f.file->f_op != &fs_fs_fops)
goto err_fsfd;
David
[toc] | [prev] | [next] | [standalone]
| From | Karel Zak <kzak@redhat.com> |
|---|---|
| Date | 2017-05-04 12:50 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tDmuB-2fe-1@gated-at.bofh.it> |
| In reply to | #1635106 |
On Wed, May 03, 2017 at 05:05:08PM +0100, David Howells wrote:
> mfd = fsopen("ext4", -1, O_CLOEXEC);
> write(mfd, "d /dev/sdb1"); // note I'm ignoring write's length arg
Not sure about 'd', in many cases it is not device, for mount(2)
syscall we call it "source".
> 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, "d %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 very basic mount(2) problem is that you have to parse
/proc/self/mountinfo to get information about the mounted filesystem.
It seems that your read() is also one way communication.
What we really need is to have a way how to specify *what* you want to
read. The error message is not enough, I want to know the finally used
mount options, mount ID, etc. It would be nice to have something like
fsmount(mfd, AT_FDCWD, "/mnt", 0);
write(mfd, "o");
read(mfd, ....); // read mount options
write(mdf, "i");
read(mfd, ....); // read mount ID
but it seems ugly. Maybe introduce another function like
fsinfo(mdf, "o", buf, bufsz)
to get mount options (etc.) and to avoid separate write & read.
> Netlink is not used because it is optional.
+1
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-04 15:10 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tDoG5-3Su-7@gated-at.bofh.it> |
| In reply to | #1635630 |
Karel Zak <kzak@redhat.com> wrote: > > write(mfd, "d /dev/sdb1"); // note I'm ignoring write's length arg > > Not sure about 'd', in many cases it is not device, for mount(2) > syscall we call it "source". sys_mount() calls it devname. But whatever - I'm not particularly attached to the letter 'd' for this. > The very basic mount(2) problem is that you have to parse > /proc/self/mountinfo to get information about the mounted filesystem. > It seems that your read() is also one way communication. > > What we really need is to have a way how to specify *what* you want to > read. The error message is not enough, I want to know the finally used > mount options, mount ID, etc. It would be nice to have something like > > > fsmount(mfd, AT_FDCWD, "/mnt", 0); > > write(mfd, "o"); > read(mfd, ....); // read mount options > > write(mdf, "i"); > read(mfd, ....); // read mount ID > > > but it seems ugly. Maybe introduce another function like > > fsinfo(mdf, "o", buf, bufsz) > > to get mount options (etc.) and to avoid separate write & read. What is it you're trying to do? Just read back the state of the new mount? Or read back the state of a specified extant mount? David
[toc] | [prev] | [next] | [standalone]
| From | Karel Zak <kzak@redhat.com> |
|---|---|
| Date | 2017-05-04 15:40 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tDp98-43E-17@gated-at.bofh.it> |
| In reply to | #1635721 |
On Thu, May 04, 2017 at 02:06:51PM +0100, David Howells wrote:
> Karel Zak <kzak@redhat.com> wrote:
> > The very basic mount(2) problem is that you have to parse
> > /proc/self/mountinfo to get information about the mounted filesystem.
> > It seems that your read() is also one way communication.
> >
> > What we really need is to have a way how to specify *what* you want to
> > read. The error message is not enough, I want to know the finally used
> > mount options, mount ID, etc. It would be nice to have something like
> >
> >
> > fsmount(mfd, AT_FDCWD, "/mnt", 0);
> >
> > write(mfd, "o");
> > read(mfd, ....); // read mount options
> >
> > write(mdf, "i");
> > read(mfd, ....); // read mount ID
> >
> >
> > but it seems ugly. Maybe introduce another function like
> >
> > fsinfo(mdf, "o", buf, bufsz)
> >
> > to get mount options (etc.) and to avoid separate write & read.
>
> What is it you're trying to do? Just read back the state of the new mount?
...read back the state of the new mount, because for example mount
options can be modified by FS driver. It would be also nice to have
API to get state of arbitrary mount without parsing mountinfo (the
file is huge on some systems).
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
[toc] | [prev] | [next] | [standalone]
| From | Jeff Layton <jlayton@redhat.com> |
|---|---|
| Date | 2017-05-09 20:50 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tFimS-5AC-11@gated-at.bofh.it> |
| In reply to | #1635740 |
On Thu, 2017-05-04 at 15:34 +0200, Karel Zak wrote: > On Thu, May 04, 2017 at 02:06:51PM +0100, David Howells wrote: > > Karel Zak <kzak@redhat.com> wrote: > > > The very basic mount(2) problem is that you have to parse > > > /proc/self/mountinfo to get information about the mounted filesystem. > > > It seems that your read() is also one way communication. > > > > > > What we really need is to have a way how to specify *what* you want to > > > read. The error message is not enough, I want to know the finally used > > > mount options, mount ID, etc. It would be nice to have something like > > > > > > > > > fsmount(mfd, AT_FDCWD, "/mnt", 0); > > > > > > write(mfd, "o"); > > > read(mfd, ....); // read mount options > > > > > > write(mdf, "i"); > > > read(mfd, ....); // read mount ID > > > > > > > > > but it seems ugly. Maybe introduce another function like > > > > > > fsinfo(mdf, "o", buf, bufsz) > > > Not sure that it's any prettier, but you could use an ioctl to switch between different read requests: ioctl(mfd, FSOPEN_READ_OPTIONS); read(mfd, ....); ioctl(mfd, FSOPEN_READ_MOUNT_ID); read(mfd, ....); > > > to get mount options (etc.) and to avoid separate write & read. > > > > What is it you're trying to do? Just read back the state of the new mount? > > ...read back the state of the new mount, because for example mount > options can be modified by FS driver. It would be also nice to have > API to get state of arbitrary mount without parsing mountinfo (the > file is huge on some systems). > -- Jeff Layton <jlayton@redhat.com>
[toc] | [prev] | [next] | [standalone]
| From | Miklos Szeredi <mszeredi@redhat.com> |
|---|---|
| Date | 2017-05-08 17:20 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tESC5-5Ad-5@gated-at.bofh.it> |
| In reply to | #1635106 |
On Wed, May 3, 2017 at 6:05 PM, 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.
Someone also suggested using /dev/fs/${FSTYPE} to open the fsfd. I
realize that does not have the namespace info that you also want to
add, but wondering if that really has to come from open and cannot be
set later?
Alternatives are /proc/fs/${FSTYPE}/dev or /sys/fs/${FSTYPE}/dev.
Obviously neither can be used for bootstraping but there's still old
mount(2) for that.
I haven't convinced myself whether using plain open(2) or a new
fsopen(2) syscall is better, just mentioning that this is a
possibility as well.
Thanks,
Miklos
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-09 01:20 +0200 |
| Subject | Re: [PATCH 4/9] Implement fsopen() to prepare for a mount |
| Message-ID | <tF06B-1YT-3@gated-at.bofh.it> |
| In reply to | #1637495 |
Miklos Szeredi <mszeredi@redhat.com> wrote:
> Someone also suggested using /dev/fs/${FSTYPE} to open the fsfd.
The downside of using open() for this is that you then have a chicken-and-egg
problem with respect to booting as you point out.
> I realize that does not have the namespace info that you also want to add,
> but wondering if that really has to come from open and cannot be set later?
When do you do the security checks? Those are going to be affected by the
namespaces. Other things are as well, such as setting hostnames, IP
addresses, device file paths and default UIDs/GIDs, but these are probably
more okay with being deferred to the parameter validation step.
> ALTERNATIVES are /proc/fs/${FSTYPE}/dev or /sys/fs/${FSTYPE}/dev.
>
> Obviously neither can be used for bootstraping but there's still old
> mount(2) for that.
It should also be possible to build-time disable mount(2) in future.
Obviously, this would mean providing other vectors for the other functions of
mount(2).
David
[toc] | [prev] | [next] | [standalone]
| From | David Howells <dhowells@redhat.com> |
|---|---|
| Date | 2017-05-03 18:10 +0200 |
| Subject | [PATCH 5/9] Implement fsmount() to effect a pre-configured mount |
| Message-ID | <tD50K-7GP-31@gated-at.bofh.it> |
| In reply to | #1635100 |
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 | 93 ++++++++++++++++++++++++++++++++
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, 129 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 e0edab9af308..a367b6cb2ac8 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3259,6 +3259,99 @@ static int do_new_mount_mc(struct mount_context *mc, struct path *mountpoint,
}
/*
+ * 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.
+ *
+ * If fd is a container and dir_name is NULL, then we try to make this the root
+ * filesystem of that container. This requires CONTAINER_NEW_EMPTY_FS_NS to
+ * have been passed when creating the container. This operation may only be
+ * done once.
+ */
+SYSCALL_DEFINE3(fsmount, int, fs_fd, int, dfd, const char __user *, dir_name)
+{
+ struct mount_context *mc;
+ 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;
+
+ mc = f.file->private_data;
+
+ ret = -EPERM;
+ if (!may_mount() ||
+ ((mc->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 (!mc->mounted) {
+ mc->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) {
+ mc->error = "VFS: Mountpoint lookup failed";
+ goto err_fsfd;
+ }
+
+ ret = security_mount_ctx_mountpoint(mc, &mountpoint);
+ if (ret < 0)
+ goto err_mp;
+
+ /* Default to relatime unless overriden */
+ if (!(mc->ms_flags & MS_NOATIME))
+ mnt_flags |= MNT_RELATIME;
+
+ /* Separate the per-mountpoint flags */
+ if (mc->ms_flags & MS_NOSUID)
+ mnt_flags |= MNT_NOSUID;
+ if (mc->ms_flags & MS_NODEV)
+ mnt_flags |= MNT_NODEV;
+ if (mc->ms_flags & MS_NOEXEC)
+ mnt_flags |= MNT_NOEXEC;
+ if (mc->ms_flags & MS_NOATIME)
+ mnt_flags |= MNT_NOATIME;
+ if (mc->ms_flags & MS_NODIRATIME)
+ mnt_flags |= MNT_NODIRATIME;
+ if (mc->ms_flags & MS_STRICTATIME)
+ mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
+ if (mc->ms_flags & MS_RDONLY)
+ mnt_flags |= MNT_READONLY;
+ mc->mnt_flags = mnt_flags;
+
+ mc->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_mc(mc, &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 f6aa68b8e68e..fe2bffd7264d 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -100,6 +100,10 @@
* Equivalent of sb_kern_mount, but with a mount_context.
* @mc indicates the mount context.
* @src_sb indicates the new superblock.
+ * @mount_ctx_mountpoint:
+ * Equivalent of sb_mount, but with a mount_context.
+ * @mc indicates the mount context.
+ * @mountpoint indicates the path on which the mount will take place.
*
* Security hooks for filesystem operations.
*
@@ -1389,6 +1393,7 @@ union security_list_options {
void (*mount_ctx_free)(struct mount_context *mc);
int (*mount_ctx_option)(struct mount_context *mc, char *opt);
int (*mount_ctx_kern_mount)(struct mount_context *mc, struct super_block *sb);
+ int (*mount_ctx_mountpoint)(struct mount_context *mc, struct path *mountpoint);
int (*sb_alloc_security)(struct super_block *sb);
void (*sb_free_security)(struct super_block *sb);
@@ -1703,6 +1708,7 @@ struct security_hook_heads {
struct list_head mount_ctx_free;
struct list_head mount_ctx_option;
struct list_head mount_ctx_kern_mount;
+ struct list_head mount_ctx_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 91efe3039bff..b427a554033a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -226,6 +226,7 @@ int security_mount_ctx_dup(struct mount_context *mc, struct mount_context *src);
void security_mount_ctx_free(struct mount_context *mc);
int security_mount_ctx_option(struct mount_context *mc, char *opt);
int security_mount_ctx_kern_mount(struct mount_context *mc, struct super_block *sb);
+int security_mount_ctx_mountpoint(struct mount_context *mc, 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_mount_ctx_kern_mount(struct mount_context *mc,
{
return 0;
}
+static inline int security_mount_ctx_mountpoint(struct mount_context *mc,
+ 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 2e522361df66..56780c1852b5 100644
--- a/security/security.c
+++ b/security/security.c
@@ -334,6 +334,11 @@ int security_mount_ctx_kern_mount(struct mount_context *mc, struct super_block *
return call_int_hook(mount_ctx_kern_mount, 0, mc, sb);
}
+int security_mount_ctx_mountpoint(struct mount_context *mc, struct path *mountpoint)
+{
+ return call_int_hook(mount_ctx_mountpoint, 0, mc, 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.mount_ctx_option),
.mount_ctx_kern_mount =
LIST_HEAD_INIT(security_hook_heads.mount_ctx_kern_mount),
+ .mount_ctx_mountpoint =
+ LIST_HEAD_INIT(security_hook_heads.mount_ctx_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 cf38db840f71..2bd8e73eb9c9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2999,6 +2999,18 @@ static int selinux_mount_ctx_kern_mount(struct mount_context *mc,
return rc;
}
+static int selinux_mount_ctx_mountpoint(struct mount_context *mc,
+ struct path *mountpoint)
+{
+ const struct cred *cred = current_cred();
+ int ret;
+
+ ret = path_has_perm(cred, mountpoint, FILE__MOUNTON);
+ if (ret < 0)
+ mc->error = "SELinux: Mount on mountpoint not permitted";
+ return ret;
+}
+
/* inode security operations */
static int selinux_inode_alloc_security(struct inode *inode)
@@ -6309,6 +6321,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(mount_ctx_free, selinux_mount_ctx_free),
LSM_HOOK_INIT(mount_ctx_option, selinux_mount_ctx_option),
LSM_HOOK_INIT(mount_ctx_kern_mount, selinux_mount_ctx_kern_mount),
+ LSM_HOOK_INIT(mount_ctx_mountpoint, selinux_mount_ctx_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-03 18:10 +0200 |
| Subject | [PATCH 2/9] Clean up whitespace in fs/namespace.c |
| Message-ID | <tD50K-7GP-37@gated-at.bofh.it> |
| In reply to | #1635100 |
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-03 18:10 +0200 |
| Subject | [PATCH 8/9] proc: Support the mount context in procfs |
| Message-ID | <tD50L-7GP-45@gated-at.bofh.it> |
| In reply to | #1635100 |
---
fs/proc/inode.c | 2 -
fs/proc/internal.h | 2 -
fs/proc/root.c | 158 ++++++++++++++++++++++++++++++++--------------------
3 files changed, 100 insertions(+), 62 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 194fa2d13b7e..9ddaf60c6f93 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_mc = proc_remount,
.show_options = proc_show_options,
};
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index b681533f59dd..68b693478e7e 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 mount_context *);
/*
* task_[no]mmu.c
diff --git a/fs/proc/root.c b/fs/proc/root.c
index ff2e810e9e64..132529a5e896 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -23,9 +23,17 @@
#include <linux/parser.h>
#include <linux/cred.h>
#include <linux/magic.h>
+#include <linux/slab.h>
#include "internal.h"
+struct proc_mount_context {
+ struct mount_context mc;
+ unsigned long mask;
+ int hidepid;
+ int gid;
+};
+
enum {
Opt_gid, Opt_hidepid, Opt_err,
};
@@ -36,56 +44,68 @@ static const match_table_t tokens = {
{Opt_err, NULL},
};
-static int proc_parse_options(char *options, struct pid_namespace *pid)
+static int proc_mount_option(struct mount_context *mc, char *p)
{
- char *p;
+ struct proc_mount_context *ctx =
+ container_of(mc, struct proc_mount_context, mc);
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)) {
+ mc->error = "procfs: Unparseable gid= argument";
+ return -EINVAL;
+ }
+ break;
+
+ case Opt_hidepid:
+ if (match_int(&args[0], &ctx->hidepid)) {
+ mc->error = "procfs: Unparseable hidepid= argument";
+ return -EINVAL;
}
+ if (ctx->hidepid < HIDEPID_OFF ||
+ ctx->hidepid > HIDEPID_INVISIBLE) {
+ mc->error = "procfs: Invalid hidepid= argument";
+ pr_err("proc: hidepid value must be between 0 and 2.\n");
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ pr_err("proc: unrecognized mount option \"%s\" "
+ "or missing value\n", p);
+ mc->error = "procfs: Invalid mount option or missing value";
+ return -EINVAL;
}
- return 1;
+ ctx->mask |= 1 << token;
+ return 0;
}
-static int proc_fill_super(struct super_block *s, void *data, int silent)
+static void proc_set_options(struct super_block *s,
+ struct mount_context *mc,
+ struct pid_namespace *pid_ns,
+ struct user_namespace *user_ns)
{
- struct pid_namespace *ns = get_pid_ns(s->s_fs_info);
+ struct proc_mount_context *ctx =
+ container_of(mc, struct proc_mount_context, mc);
+
+ 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, struct mount_context *mc)
+{
+ 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, mc, 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;
@@ -102,7 +122,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) {
@@ -123,27 +143,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 mount_context *mc)
{
struct pid_namespace *pid = sb->s_fs_info;
sync_filesystem(sb);
- return !proc_parse_options(data, pid);
+
+ if (mc)
+ proc_set_options(sb, mc, 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 mount_context *mc)
{
- struct pid_namespace *ns;
+ return mount_ns_mc(mc, mc->pid_ns);
+}
- if (flags & MS_KERNMOUNT) {
- ns = data;
- data = NULL;
- } else {
- ns = task_active_pid_ns(current);
- }
+static const struct mount_context_operations proc_mount_ctx_ops = {
+ .option = proc_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_fsopen(struct mount_context *mc, struct super_block *src_sb)
+{
+ mc->ops = &proc_mount_ctx_ops;
+ return 0;
}
static void proc_kill_sb(struct super_block *sb)
@@ -161,7 +186,8 @@ static void proc_kill_sb(struct super_block *sb)
static struct file_system_type proc_fs_type = {
.name = "proc",
- .mount = proc_mount,
+ .fsopen = proc_fsopen,
+ .mc_size = sizeof(struct proc_mount_context),
.kill_sb = proc_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
};
@@ -209,7 +235,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);
}
@@ -248,12 +274,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,
@@ -262,9 +288,21 @@ struct proc_dir_entry proc_root = {
int pid_ns_prepare_proc(struct pid_namespace *ns)
{
+ struct mount_context *mc;
struct vfsmount *mnt;
- mnt = kern_mount_data(&proc_fs_type, ns);
+ mc = __vfs_fsopen(&proc_fs_type, NULL, 0, 0, MOUNT_TYPE_NEW);
+ if (IS_ERR(mc))
+ return PTR_ERR(mc);
+
+ if (mc->pid_ns != ns) {
+ put_pid_ns(mc->pid_ns);
+ get_pid_ns(ns);
+ mc->pid_ns = ns;
+ }
+
+ mnt = kern_mount_data_mc(mc);
+ put_mount_context(mc);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
[toc] | [prev] | [next] | [standalone]
Page 1 of 4 [1] 2 3 4 Next page →
Back to top | Article view | linux.kernel
csiph-web