* [PATCH 29/33] vfs: syscall: Add fsmount() to create a mount for a superblock [ver #11]
From: David Howells @ 2018-08-01 15:27 UTC (permalink / raw)
To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153313703562.13253.5766498657900728120.stgit@warthog.procyon.org.uk>
Provide a system call by which a filesystem opened with fsopen() and
configured by a series of fsconfig() calls can have a detached mount object
created for it. This mount object can then be attached to the VFS mount
hierarchy using move_mount() by passing the returned file descriptor as the
from directory fd.
The system call looks like:
int mfd = fsmount(int fsfd, unsigned int flags,
unsigned int ms_flags);
where fsfd is the file descriptor returned by fsopen(). flags can be 0 or
FSMOUNT_CLOEXEC. ms_flags is a bitwise-OR of the following flags:
MS_RDONLY
MS_NOSUID
MS_NODEV
MS_NOEXEC
MS_NOATIME
MS_NODIRATIME
MS_RELATIME
MS_STRICTATIME
MS_UNBINDABLE
MS_PRIVATE
MS_SLAVE
MS_SHARED
In the event that fsmount() fails, it may be possible to get an error
message by calling read() on fsfd. If no message is available, ENODATA
will be reported.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
---
arch/x86/entry/syscalls/syscall_32.tbl | 1
arch/x86/entry/syscalls/syscall_64.tbl | 1
fs/namespace.c | 141 +++++++++++++++++++++++++++++++-
include/linux/syscalls.h | 1
include/uapi/linux/fs.h | 2
5 files changed, 142 insertions(+), 4 deletions(-)
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index f9970310c126..c78b68256f8a 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -402,3 +402,4 @@
388 i386 move_mount sys_move_mount __ia32_sys_move_mount
389 i386 fsopen sys_fsopen __ia32_sys_fsopen
390 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig
+391 i386 fsmount sys_fsmount __ia32_sys_fsmount
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 4185d36e03bb..d44ead5d4368 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -347,6 +347,7 @@
336 common move_mount __x64_sys_move_mount
337 common fsopen __x64_sys_fsopen
338 common fsconfig __x64_sys_fsconfig
+339 common fsmount __x64_sys_fsmount
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/namespace.c b/fs/namespace.c
index ea07066a2731..7e131b7fc098 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2503,7 +2503,7 @@ static int do_move_mount(struct path *old_path, struct path *new_path)
attached = mnt_has_parent(old);
/*
- * We need to allow open_tree(OPEN_TREE_CLONE) followed by
+ * We need to allow open_tree(OPEN_TREE_CLONE) or fsmount() followed by
* move_mount(), but mustn't allow "/" to be moved.
*/
if (old->mnt_ns && !attached)
@@ -3348,9 +3348,142 @@ struct vfsmount *kern_mount(struct file_system_type *type)
EXPORT_SYMBOL_GPL(kern_mount);
/*
- * Move a mount from one place to another.
- * In combination with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be
- * used to copy a mount subtree.
+ * Create a kernel mount representation for a new, prepared superblock
+ * (specified by fs_fd) and attach to an open_tree-like file descriptor.
+ */
+SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, unsigned int, ms_flags)
+{
+ struct fs_context *fc;
+ struct file *file;
+ struct path newmount;
+ struct fd f;
+ unsigned int mnt_flags = 0;
+ long ret;
+
+ if (!may_mount())
+ return -EPERM;
+
+ if ((flags & ~(FSMOUNT_CLOEXEC)) != 0)
+ return -EINVAL;
+
+ if (ms_flags & ~(MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC |
+ MS_NOATIME | MS_NODIRATIME | MS_RELATIME |
+ MS_STRICTATIME))
+ return -EINVAL;
+
+ if (ms_flags & MS_RDONLY)
+ mnt_flags |= MNT_READONLY;
+ if (ms_flags & MS_NOSUID)
+ mnt_flags |= MNT_NOSUID;
+ if (ms_flags & MS_NODEV)
+ mnt_flags |= MNT_NODEV;
+ if (ms_flags & MS_NOEXEC)
+ mnt_flags |= MNT_NOEXEC;
+ if (ms_flags & MS_NODIRATIME)
+ mnt_flags |= MNT_NODIRATIME;
+
+ if (ms_flags & MS_STRICTATIME) {
+ if (ms_flags & MS_NOATIME)
+ return -EINVAL;
+ } else if (ms_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 != &fscontext_fops)
+ goto err_fsfd;
+
+ fc = f.file->private_data;
+
+ /* There must be a valid superblock or we can't mount it */
+ ret = -EINVAL;
+ if (!fc->root)
+ goto err_fsfd;
+
+ ret = -EPERM;
+ if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) {
+ pr_warn("VFS: Mount too revealing\n");
+ goto err_fsfd;
+ }
+
+ ret = mutex_lock_interruptible(&fc->uapi_mutex);
+ if (ret < 0)
+ goto err_fsfd;
+
+ ret = -EBUSY;
+ if (fc->phase != FS_CONTEXT_AWAITING_MOUNT)
+ goto err_unlock;
+
+ ret = -EPERM;
+ if ((fc->sb_flags & SB_MANDLOCK) && !may_mandlock())
+ goto err_unlock;
+
+ newmount.mnt = vfs_create_mount(fc, mnt_flags);
+ if (IS_ERR(newmount.mnt)) {
+ ret = PTR_ERR(newmount.mnt);
+ goto err_unlock;
+ }
+ newmount.dentry = dget(fc->root);
+
+ /* We've done the mount bit - now move the file context into more or
+ * less the same state as if we'd done an fspick(). We don't want to
+ * do any memory allocation or anything like that at this point as we
+ * don't want to have to handle any errors incurred.
+ */
+ if (fc->ops && fc->ops->free)
+ fc->ops->free(fc);
+ fc->need_free = false;
+ fc->fs_private = NULL;
+ fc->s_fs_info = NULL;
+ fc->sb_flags = 0;
+ fc->sloppy = false;
+ fc->silent = false;
+ security_fs_context_free(fc);
+ fc->security = NULL;
+ kfree(fc->subtype);
+ fc->subtype = NULL;
+ kfree(fc->source);
+ fc->source = NULL;
+
+ fc->purpose = FS_CONTEXT_FOR_RECONFIGURE;
+ fc->phase = FS_CONTEXT_AWAITING_RECONF;
+
+ /* Attach to an apparent O_PATH fd with a note that we need to unmount
+ * it, not just simply put it.
+ */
+ file = dentry_open(&newmount, O_PATH, fc->cred);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err_path;
+ }
+ file->f_mode |= FMODE_NEED_UNMOUNT;
+
+ ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0);
+ if (ret >= 0)
+ fd_install(ret, file);
+ else
+ fput(file);
+
+err_path:
+ path_put(&newmount);
+err_unlock:
+ mutex_unlock(&fc->uapi_mutex);
+err_fsfd:
+ fdput(f);
+ return ret;
+}
+
+/*
+ * Move a mount from one place to another. In combination with
+ * fsopen()/fsmount() this is used to install a new mount and in combination
+ * with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be used to copy
+ * a mount subtree.
*
* Note the flags value is a combination of MOVE_MOUNT_* flags.
*/
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 9628d14a7ede..65db661cc2da 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -907,6 +907,7 @@ asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags);
asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key,
const void __user *value, int aux);
+asmlinkage long sys_fsmount(int fs_fd, unsigned int flags, unsigned int ms_flags);
/*
* Architecture-specific system calls
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index fecbae30a30d..10281d582e28 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -349,6 +349,8 @@ typedef int __bitwise __kernel_rwf_t;
*/
#define FSOPEN_CLOEXEC 0x00000001
+#define FSMOUNT_CLOEXEC 0x00000001
+
/*
* The type of fsconfig() call made.
*/
^ permalink raw reply related
* [PATCH 28/33] vfs: syscall: Add fsconfig() for configuring and managing a context [ver #11]
From: David Howells @ 2018-08-01 15:27 UTC (permalink / raw)
To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153313703562.13253.5766498657900728120.stgit@warthog.procyon.org.uk>
Add a syscall for configuring a filesystem creation context and triggering
actions upon it, to be used in conjunction with fsopen, fspick and fsmount.
long fsconfig(int fs_fd, unsigned int cmd, const char *key,
const void *value, int aux);
Where fs_fd indicates the context, cmd indicates the action to take, key
indicates the parameter name for parameter-setting actions and, if needed,
value points to a buffer containing the value and aux can give more
information for the value.
The following command IDs are proposed:
(*) FSCONFIG_SET_FLAG: No value is specified. The parameter must be
boolean in nature. The key may be prefixed with "no" to invert the
setting. value must be NULL and aux must be 0.
(*) FSCONFIG_SET_STRING: A string value is specified. The parameter can
be expecting boolean, integer, string or take a path. A conversion to
an appropriate type will be attempted (which may include looking up as
a path). value points to a NUL-terminated string and aux must be 0.
(*) FSCONFIG_SET_BINARY: A binary blob is specified. value points to
the blob and aux indicates its size. The parameter must be expecting
a blob.
(*) FSCONFIG_SET_PATH: A non-empty path is specified. The parameter must
be expecting a path object. value points to a NUL-terminated string
that is the path and aux is a file descriptor at which to start a
relative lookup or AT_FDCWD.
(*) FSCONFIG_SET_PATH_EMPTY: As fsconfig_set_path, but with AT_EMPTY_PATH
implied.
(*) FSCONFIG_SET_FD: An open file descriptor is specified. value must
be NULL and aux indicates the file descriptor.
(*) FSCONFIG_CMD_CREATE: Trigger superblock creation.
(*) FSCONFIG_CMD_RECONFIGURE: Trigger superblock reconfiguration.
For the "set" command IDs, the idea is that the file_system_type will point
to a list of parameters and the types of value that those parameters expect
to take. The core code can then do the parse and argument conversion and
then give the LSM and FS a cooked option or array of options to use.
Source specification is also done the same way same way, using special keys
"source", "source1", "source2", etc..
[!] Note that, for the moment, the key and value are just glued back
together and handed to the filesystem. Every filesystem that uses options
uses match_token() and co. to do this, and this will need to be changed -
but not all at once.
Example usage:
fd = fsopen("ext4", FSOPEN_CLOEXEC);
fsconfig(fd, fsconfig_set_path, "source", "/dev/sda1", AT_FDCWD);
fsconfig(fd, fsconfig_set_path_empty, "journal_path", "", journal_fd);
fsconfig(fd, fsconfig_set_fd, "journal_fd", "", journal_fd);
fsconfig(fd, fsconfig_set_flag, "user_xattr", NULL, 0);
fsconfig(fd, fsconfig_set_flag, "noacl", NULL, 0);
fsconfig(fd, fsconfig_set_string, "sb", "1", 0);
fsconfig(fd, fsconfig_set_string, "errors", "continue", 0);
fsconfig(fd, fsconfig_set_string, "data", "journal", 0);
fsconfig(fd, fsconfig_set_string, "context", "unconfined_u:...", 0);
fsconfig(fd, fsconfig_cmd_create, NULL, NULL, 0);
mfd = fsmount(fd, FSMOUNT_CLOEXEC, MS_NOEXEC);
or:
fd = fsopen("ext4", FSOPEN_CLOEXEC);
fsconfig(fd, fsconfig_set_string, "source", "/dev/sda1", 0);
fsconfig(fd, fsconfig_cmd_create, NULL, NULL, 0);
mfd = fsmount(fd, FSMOUNT_CLOEXEC, MS_NOEXEC);
or:
fd = fsopen("afs", FSOPEN_CLOEXEC);
fsconfig(fd, fsconfig_set_string, "source", "#grand.central.org:root.cell", 0);
fsconfig(fd, fsconfig_cmd_create, NULL, NULL, 0);
mfd = fsmount(fd, FSMOUNT_CLOEXEC, MS_NOEXEC);
or:
fd = fsopen("jffs2", FSOPEN_CLOEXEC);
fsconfig(fd, fsconfig_set_string, "source", "mtd0", 0);
fsconfig(fd, fsconfig_cmd_create, NULL, NULL, 0);
mfd = fsmount(fd, FSMOUNT_CLOEXEC, MS_NOEXEC);
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
---
arch/x86/entry/syscalls/syscall_32.tbl | 1
arch/x86/entry/syscalls/syscall_64.tbl | 1
fs/fsopen.c | 279 ++++++++++++++++++++++++++++++++
include/linux/syscalls.h | 2
include/uapi/linux/fs.h | 14 ++
5 files changed, 297 insertions(+)
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 1647fefd2969..f9970310c126 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -401,3 +401,4 @@
387 i386 open_tree sys_open_tree __ia32_sys_open_tree
388 i386 move_mount sys_move_mount __ia32_sys_move_mount
389 i386 fsopen sys_fsopen __ia32_sys_fsopen
+390 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 235d33dbccb2..4185d36e03bb 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -346,6 +346,7 @@
335 common open_tree __x64_sys_open_tree
336 common move_mount __x64_sys_move_mount
337 common fsopen __x64_sys_fsopen
+338 common fsconfig __x64_sys_fsconfig
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/fsopen.c b/fs/fsopen.c
index 7a25b4c3bc18..5d8560e78ce1 100644
--- a/fs/fsopen.c
+++ b/fs/fsopen.c
@@ -10,6 +10,7 @@
*/
#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
@@ -17,6 +18,7 @@
#include <linux/anon_inodes.h>
#include <linux/namei.h>
#include <linux/file.h>
+#include "internal.h"
#include "mount.h"
/*
@@ -152,3 +154,280 @@ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
put_fs_context(fc);
return ret;
}
+
+/*
+ * Check the state and apply the configuration. Note that this function is
+ * allowed to 'steal' the value by setting param->xxx to NULL before returning.
+ */
+static int vfs_fsconfig(struct fs_context *fc, struct fs_parameter *param)
+{
+ int ret;
+
+ /* We need to reinitialise the context if we have reconfiguration
+ * pending after creation or a previous reconfiguration.
+ */
+ if (fc->phase == FS_CONTEXT_AWAITING_RECONF) {
+ if (fc->fs_type->init_fs_context) {
+ ret = fc->fs_type->init_fs_context(fc, fc->root);
+ if (ret < 0) {
+ fc->phase = FS_CONTEXT_FAILED;
+ return ret;
+ }
+ fc->need_free = true;
+ } else {
+ /* Leave legacy context ops in place */
+ }
+
+ /* Do the security check last because ->init_fs_context may
+ * change the namespace subscriptions.
+ */
+ ret = security_fs_context_alloc(fc, fc->root);
+ if (ret < 0) {
+ fc->phase = FS_CONTEXT_FAILED;
+ return ret;
+ }
+
+ fc->phase = FS_CONTEXT_RECONF_PARAMS;
+ }
+
+ if (fc->phase != FS_CONTEXT_CREATE_PARAMS &&
+ fc->phase != FS_CONTEXT_RECONF_PARAMS)
+ return -EBUSY;
+
+ return vfs_parse_fs_param(fc, param);
+}
+
+/*
+ * Perform an action on a context.
+ */
+static int vfs_fsconfig_action(struct fs_context *fc, enum fsconfig_command cmd)
+{
+ int ret = -EINVAL;
+
+ switch (cmd) {
+ case FSCONFIG_CMD_CREATE:
+ if (fc->phase != FS_CONTEXT_CREATE_PARAMS)
+ return -EBUSY;
+ fc->phase = FS_CONTEXT_CREATING;
+ ret = vfs_get_tree(fc);
+ if (ret == 0)
+ fc->phase = FS_CONTEXT_AWAITING_MOUNT;
+ else
+ fc->phase = FS_CONTEXT_FAILED;
+ return ret;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
+ * sys_fsconfig - Set parameters and trigger actions on a context
+ * @fd: The filesystem context to act upon
+ * @cmd: The action to take
+ * @_key: Where appropriate, the parameter key to set
+ * @_value: Where appropriate, the parameter value to set
+ * @aux: Additional information for the value
+ *
+ * This system call is used to set parameters on a context, including
+ * superblock settings, data source and security labelling.
+ *
+ * Actions include triggering the creation of a superblock and the
+ * reconfiguration of the superblock attached to the specified context.
+ *
+ * When setting a parameter, @cmd indicates the type of value being proposed
+ * and @_key indicates the parameter to be altered.
+ *
+ * @_value and @aux are used to specify the value, should a value be required:
+ *
+ * (*) fsconfig_set_flag: No value is specified. The parameter must be boolean
+ * in nature. The key may be prefixed with "no" to invert the
+ * setting. @_value must be NULL and @aux must be 0.
+ *
+ * (*) fsconfig_set_string: A string value is specified. The parameter can be
+ * expecting boolean, integer, string or take a path. A conversion to an
+ * appropriate type will be attempted (which may include looking up as a
+ * path). @_value points to a NUL-terminated string and @aux must be 0.
+ *
+ * (*) fsconfig_set_binary: A binary blob is specified. @_value points to the
+ * blob and @aux indicates its size. The parameter must be expecting a
+ * blob.
+ *
+ * (*) fsconfig_set_path: A non-empty path is specified. The parameter must be
+ * expecting a path object. @_value points to a NUL-terminated string that
+ * is the path and @aux is a file descriptor at which to start a relative
+ * lookup or AT_FDCWD.
+ *
+ * (*) fsconfig_set_path_empty: As fsconfig_set_path, but with AT_EMPTY_PATH
+ * implied.
+ *
+ * (*) fsconfig_set_fd: An open file descriptor is specified. @_value must be
+ * NULL and @aux indicates the file descriptor.
+ */
+SYSCALL_DEFINE5(fsconfig,
+ int, fd,
+ unsigned int, cmd,
+ const char __user *, _key,
+ const void __user *, _value,
+ int, aux)
+{
+ struct fs_context *fc;
+ struct fd f;
+ int ret;
+
+ struct fs_parameter param = {
+ .type = fs_value_is_undefined,
+ };
+
+ if (fd < 0)
+ return -EINVAL;
+
+ switch (cmd) {
+ case FSCONFIG_SET_FLAG:
+ if (!_key || _value || aux)
+ return -EINVAL;
+ break;
+ case FSCONFIG_SET_STRING:
+ if (!_key || !_value || aux)
+ return -EINVAL;
+ break;
+ case FSCONFIG_SET_BINARY:
+ if (!_key || !_value || aux <= 0 || aux > 1024 * 1024)
+ return -EINVAL;
+ break;
+ case FSCONFIG_SET_PATH:
+ case FSCONFIG_SET_PATH_EMPTY:
+ if (!_key || !_value || (aux != AT_FDCWD && aux < 0))
+ return -EINVAL;
+ break;
+ case FSCONFIG_SET_FD:
+ if (!_key || _value || aux < 0)
+ return -EINVAL;
+ break;
+ case FSCONFIG_CMD_CREATE:
+ case FSCONFIG_CMD_RECONFIGURE:
+ if (_key || _value || aux)
+ return -EINVAL;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ f = fdget(fd);
+ if (!f.file)
+ return -EBADF;
+ ret = -EINVAL;
+ if (f.file->f_op != &fscontext_fops)
+ goto out_f;
+
+ fc = f.file->private_data;
+ if (fc->ops == &legacy_fs_context_ops) {
+ switch (cmd) {
+ case FSCONFIG_SET_BINARY:
+ case FSCONFIG_SET_PATH:
+ case FSCONFIG_SET_PATH_EMPTY:
+ case FSCONFIG_SET_FD:
+ ret = -EOPNOTSUPP;
+ goto out_f;
+ }
+ }
+
+ if (_key) {
+ param.key = strndup_user(_key, 256);
+ if (IS_ERR(param.key)) {
+ ret = PTR_ERR(param.key);
+ goto out_f;
+ }
+ }
+
+ switch (cmd) {
+ case FSCONFIG_SET_STRING:
+ param.type = fs_value_is_string;
+ param.string = strndup_user(_value, 256);
+ if (IS_ERR(param.string)) {
+ ret = PTR_ERR(param.string);
+ goto out_key;
+ }
+ param.size = strlen(param.string);
+ break;
+ case FSCONFIG_SET_BINARY:
+ param.type = fs_value_is_blob;
+ param.size = aux;
+ param.blob = memdup_user_nul(_value, aux);
+ if (IS_ERR(param.blob)) {
+ ret = PTR_ERR(param.blob);
+ goto out_key;
+ }
+ break;
+ case FSCONFIG_SET_PATH:
+ param.type = fs_value_is_filename;
+ param.name = getname_flags(_value, 0, NULL);
+ if (IS_ERR(param.name)) {
+ ret = PTR_ERR(param.name);
+ goto out_key;
+ }
+ param.dirfd = aux;
+ param.size = strlen(param.name->name);
+ break;
+ case FSCONFIG_SET_PATH_EMPTY:
+ param.type = fs_value_is_filename_empty;
+ param.name = getname_flags(_value, LOOKUP_EMPTY, NULL);
+ if (IS_ERR(param.name)) {
+ ret = PTR_ERR(param.name);
+ goto out_key;
+ }
+ param.dirfd = aux;
+ param.size = strlen(param.name->name);
+ break;
+ case FSCONFIG_SET_FD:
+ param.type = fs_value_is_file;
+ ret = -EBADF;
+ param.file = fget(aux);
+ if (!param.file)
+ goto out_key;
+ break;
+ default:
+ break;
+ }
+
+ ret = mutex_lock_interruptible(&fc->uapi_mutex);
+ if (ret == 0) {
+ switch (cmd) {
+ case FSCONFIG_CMD_CREATE:
+ case FSCONFIG_CMD_RECONFIGURE:
+ ret = vfs_fsconfig_action(fc, cmd);
+ break;
+ default:
+ ret = vfs_fsconfig(fc, ¶m);
+ break;
+ }
+ mutex_unlock(&fc->uapi_mutex);
+ }
+
+ /* Clean up the our record of any value that we obtained from
+ * userspace. Note that the value may have been stolen by the LSM or
+ * filesystem, in which case the value pointer will have been cleared.
+ */
+ switch (cmd) {
+ case FSCONFIG_SET_STRING:
+ case FSCONFIG_SET_BINARY:
+ kfree(param.string);
+ break;
+ case FSCONFIG_SET_PATH:
+ case FSCONFIG_SET_PATH_EMPTY:
+ if (param.name)
+ putname(param.name);
+ break;
+ case FSCONFIG_SET_FD:
+ if (param.file)
+ fput(param.file);
+ break;
+ default:
+ break;
+ }
+out_key:
+ kfree(param.key);
+out_f:
+ fdput(f);
+ return ret;
+}
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index ad6c7ff33c01..9628d14a7ede 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -905,6 +905,8 @@ asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
int to_dfd, const char __user *to_path,
unsigned int ms_flags);
asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags);
+asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key,
+ const void __user *value, int aux);
/*
* Architecture-specific system calls
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index f8818e6cddd6..fecbae30a30d 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -349,4 +349,18 @@ typedef int __bitwise __kernel_rwf_t;
*/
#define FSOPEN_CLOEXEC 0x00000001
+/*
+ * The type of fsconfig() call made.
+ */
+enum fsconfig_command {
+ FSCONFIG_SET_FLAG = 0, /* Set parameter, supplying no value */
+ FSCONFIG_SET_STRING = 1, /* Set parameter, supplying a string value */
+ FSCONFIG_SET_BINARY = 2, /* Set parameter, supplying a binary blob value */
+ FSCONFIG_SET_PATH = 3, /* Set parameter, supplying an object by path */
+ FSCONFIG_SET_PATH_EMPTY = 4, /* Set parameter, supplying an object by (empty) path */
+ FSCONFIG_SET_FD = 5, /* Set parameter, supplying an object by fd */
+ FSCONFIG_CMD_CREATE = 6, /* Invoke superblock creation */
+ FSCONFIG_CMD_RECONFIGURE = 7, /* Invoke superblock reconfiguration */
+};
+
#endif /* _UAPI_LINUX_FS_H */
^ permalink raw reply related
* [PATCH 25/33] vfs: syscall: Add fsopen() to prepare for superblock creation [ver #11]
From: David Howells @ 2018-08-01 15:26 UTC (permalink / raw)
To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153313703562.13253.5766498657900728120.stgit@warthog.procyon.org.uk>
Provide an fsopen() system call that starts the process of preparing to
create a superblock that will then be mountable, 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, unsigned int flags);
where flags can be 0 or FSOPEN_CLOEXEC.
For example:
sfd = fsopen("ext4", FSOPEN_CLOEXEC);
fsconfig(sfd, FSCONFIG_SET_PATH, "source", "/dev/sda1", AT_FDCWD);
fsconfig(sfd, FSCONFIG_SET_FLAG, "noatime", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_FLAG, "acl", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_FLAG, "user_xattr", NULL, 0);
fsconfig(sfd, FSCONFIG_SET_STRING, "sb", "1", 0);
fsconfig(sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
fsinfo(sfd, NULL, ...); // query new superblock attributes
mfd = fsmount(sfd, FSMOUNT_CLOEXEC, MS_RELATIME);
move_mount(mfd, "", sfd, AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
sfd = fsopen("afs", -1);
fsconfig(fd, FSCONFIG_SET_STRING, "source",
"#grand.central.org:root.cell", 0);
fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mfd = fsmount(sfd, 0, MS_NODEV);
move_mount(mfd, "", sfd, AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
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 fsconfig() 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 and would make the core VFS
dependent on the networking layer and also potentially add network
namespace issues.
Note that, for the moment, the caller must have SYS_CAP_ADMIN to use
fsopen().
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
---
arch/x86/entry/syscalls/syscall_32.tbl | 1
arch/x86/entry/syscalls/syscall_64.tbl | 1
fs/Makefile | 2 -
fs/fs_context.c | 4 +
fs/fsopen.c | 87 ++++++++++++++++++++++++++++++++
include/linux/fs_context.h | 4 +
include/linux/syscalls.h | 1
include/uapi/linux/fs.h | 5 ++
8 files changed, 104 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 76d092b7d1b0..1647fefd2969 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -400,3 +400,4 @@
386 i386 rseq sys_rseq __ia32_sys_rseq
387 i386 open_tree sys_open_tree __ia32_sys_open_tree
388 i386 move_mount sys_move_mount __ia32_sys_move_mount
+389 i386 fsopen sys_fsopen __ia32_sys_fsopen
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 37ba4e65eee6..235d33dbccb2 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -345,6 +345,7 @@
334 common rseq __x64_sys_rseq
335 common open_tree __x64_sys_open_tree
336 common move_mount __x64_sys_move_mount
+337 common fsopen __x64_sys_fsopen
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/Makefile b/fs/Makefile
index ae681523b4b1..e3ea8093b178 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -13,7 +13,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 d_path.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
- fs_context.o fs_parser.o
+ fs_context.o fs_parser.o fsopen.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o block_dev.o direct-io.o mpage.o
diff --git a/fs/fs_context.c b/fs/fs_context.c
index 8f040a20b320..90db81d7008c 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -263,6 +263,8 @@ struct fs_context *vfs_new_fs_context(struct file_system_type *fs_type,
fc->fs_type = get_filesystem(fs_type);
fc->cred = get_current_cred();
+ mutex_init(&fc->uapi_mutex);
+
switch (purpose) {
case FS_CONTEXT_FOR_KERNEL_MOUNT:
fc->sb_flags |= SB_KERNMOUNT;
@@ -348,6 +350,8 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc)
if (!fc)
return ERR_PTR(-ENOMEM);
+ mutex_init(&fc->uapi_mutex);
+
fc->fs_private = NULL;
fc->s_fs_info = NULL;
fc->source = NULL;
diff --git a/fs/fsopen.c b/fs/fsopen.c
new file mode 100644
index 000000000000..f30080e1ebc4
--- /dev/null
+++ b/fs/fsopen.c
@@ -0,0 +1,87 @@
+/* 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/fs_context.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/security.h>
+#include <linux/anon_inodes.h>
+#include <linux/namei.h>
+#include <linux/file.h>
+#include "mount.h"
+
+static int fscontext_release(struct inode *inode, struct file *file)
+{
+ struct fs_context *fc = file->private_data;
+
+ if (fc) {
+ file->private_data = NULL;
+ put_fs_context(fc);
+ }
+ return 0;
+}
+
+const struct file_operations fscontext_fops = {
+ .release = fscontext_release,
+ .llseek = no_llseek,
+};
+
+/*
+ * Attach a filesystem context to a file and an fd.
+ */
+static int fscontext_create_fd(struct fs_context *fc, unsigned int o_flags)
+{
+ int fd;
+
+ fd = anon_inode_getfd("fscontext", &fscontext_fops, fc,
+ O_RDWR | o_flags);
+ if (fd < 0)
+ put_fs_context(fc);
+ return fd;
+}
+
+/*
+ * 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_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
+{
+ struct file_system_type *fs_type;
+ struct fs_context *fc;
+ const char *fs_name;
+
+ if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (flags & ~FSOPEN_CLOEXEC)
+ return -EINVAL;
+
+ fs_name = strndup_user(_fs_name, PAGE_SIZE);
+ if (IS_ERR(fs_name))
+ return PTR_ERR(fs_name);
+
+ fs_type = get_fs_type(fs_name);
+ kfree(fs_name);
+ if (!fs_type)
+ return -ENODEV;
+
+ fc = vfs_new_fs_context(fs_type, NULL, 0, FS_CONTEXT_FOR_USER_MOUNT);
+ put_filesystem(fs_type);
+ if (IS_ERR(fc))
+ return PTR_ERR(fc);
+
+ fc->phase = FS_CONTEXT_CREATE_PARAMS;
+ return fscontext_create_fd(fc, flags & FSOPEN_CLOEXEC ? O_CLOEXEC : 0);
+}
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index bbb8114f2fdc..5445889f705b 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/mutex.h>
struct cred;
struct dentry;
@@ -87,6 +88,7 @@ struct fs_parameter {
*/
struct fs_context {
const struct fs_context_operations *ops;
+ struct mutex uapi_mutex; /* Userspace access mutex */
struct file_system_type *fs_type;
void *fs_private; /* The filesystem's context */
struct dentry *root; /* The root and superblock */
@@ -143,6 +145,8 @@ extern int vfs_get_super(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc));
+extern const struct file_operations fscontext_fops;
+
#define logfc(FC, FMT, ...) pr_notice(FMT, ## __VA_ARGS__)
/**
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 3c0855d9b105..ad6c7ff33c01 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -904,6 +904,7 @@ asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags);
asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
int to_dfd, const char __user *to_path,
unsigned int ms_flags);
+asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags);
/*
* Architecture-specific system calls
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 1c982eb44ff4..f8818e6cddd6 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -344,4 +344,9 @@ typedef int __bitwise __kernel_rwf_t;
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
RWF_APPEND)
+/*
+ * Flags for fsopen() and co.
+ */
+#define FSOPEN_CLOEXEC 0x00000001
+
#endif /* _UAPI_LINUX_FS_H */
^ permalink raw reply related
* [PATCH 02/33] vfs: syscall: Add move_mount(2) to move mounts around [ver #11]
From: David Howells @ 2018-08-01 15:24 UTC (permalink / raw)
To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153313703562.13253.5766498657900728120.stgit@warthog.procyon.org.uk>
Add a move_mount() system call that will move a mount from one place to
another and, in the next commit, allow to attach an unattached mount tree.
The new system call looks like the following:
int move_mount(int from_dfd, const char *from_path,
int to_dfd, const char *to_path,
unsigned int flags);
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
---
arch/x86/entry/syscalls/syscall_32.tbl | 1
arch/x86/entry/syscalls/syscall_64.tbl | 1
fs/namespace.c | 102 ++++++++++++++++++++++++++------
include/linux/lsm_hooks.h | 6 ++
include/linux/security.h | 7 ++
include/linux/syscalls.h | 3 +
include/uapi/linux/mount.h | 11 +++
security/security.c | 5 ++
8 files changed, 118 insertions(+), 18 deletions(-)
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ea1b413afd47..76d092b7d1b0 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -399,3 +399,4 @@
385 i386 io_pgetevents sys_io_pgetevents __ia32_compat_sys_io_pgetevents
386 i386 rseq sys_rseq __ia32_sys_rseq
387 i386 open_tree sys_open_tree __ia32_sys_open_tree
+388 i386 move_mount sys_move_mount __ia32_sys_move_mount
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 0545bed581dc..37ba4e65eee6 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -344,6 +344,7 @@
333 common io_pgetevents __x64_sys_io_pgetevents
334 common rseq __x64_sys_rseq
335 common open_tree __x64_sys_open_tree
+336 common move_mount __x64_sys_move_mount
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/namespace.c b/fs/namespace.c
index a4a01ecbcacd..e2934a4f342b 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2447,43 +2447,37 @@ static inline int tree_contains_unbindable(struct mount *mnt)
return 0;
}
-static int do_move_mount(struct path *path, const char *old_name)
+static int do_move_mount(struct path *old_path, struct path *new_path)
{
- struct path old_path, parent_path;
+ struct path parent_path = {.mnt = NULL, .dentry = NULL};
struct mount *p;
struct mount *old;
struct mountpoint *mp;
int err;
- if (!old_name || !*old_name)
- return -EINVAL;
- err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
- if (err)
- return err;
- mp = lock_mount(path);
+ mp = lock_mount(new_path);
err = PTR_ERR(mp);
if (IS_ERR(mp))
goto out;
- old = real_mount(old_path.mnt);
- p = real_mount(path->mnt);
+ old = real_mount(old_path->mnt);
+ p = real_mount(new_path->mnt);
err = -EINVAL;
if (!check_mnt(p) || !check_mnt(old))
goto out1;
- if (old->mnt.mnt_flags & MNT_LOCKED)
+ if (!mnt_has_parent(old))
goto out1;
- err = -EINVAL;
- if (old_path.dentry != old_path.mnt->mnt_root)
+ if (old->mnt.mnt_flags & MNT_LOCKED)
goto out1;
- if (!mnt_has_parent(old))
+ if (old_path->dentry != old_path->mnt->mnt_root)
goto out1;
- if (d_is_dir(path->dentry) !=
- d_is_dir(old_path.dentry))
+ if (d_is_dir(new_path->dentry) !=
+ d_is_dir(old_path->dentry))
goto out1;
/*
* Don't move a mount residing in a shared parent.
@@ -2501,7 +2495,8 @@ static int do_move_mount(struct path *path, const char *old_name)
if (p == old)
goto out1;
- err = attach_recursive_mnt(old, real_mount(path->mnt), mp, &parent_path);
+ err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp,
+ &parent_path);
if (err)
goto out1;
@@ -2513,6 +2508,22 @@ static int do_move_mount(struct path *path, const char *old_name)
out:
if (!err)
path_put(&parent_path);
+ return err;
+}
+
+static int do_move_mount_old(struct path *path, const char *old_name)
+{
+ struct path old_path;
+ int err;
+
+ if (!old_name || !*old_name)
+ return -EINVAL;
+
+ err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
+ if (err)
+ return err;
+
+ err = do_move_mount(&old_path, path);
path_put(&old_path);
return err;
}
@@ -2934,7 +2945,7 @@ long do_mount(const char *dev_name, const char __user *dir_name,
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
retval = do_change_type(&path, flags);
else if (flags & MS_MOVE)
- retval = do_move_mount(&path, dev_name);
+ retval = do_move_mount_old(&path, dev_name);
else
retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
dev_name, data_page, data_size);
@@ -3169,6 +3180,61 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
return ksys_mount(dev_name, dir_name, type, flags, data);
}
+/*
+ * Move a mount from one place to another.
+ *
+ * Note the flags value is a combination of MOVE_MOUNT_* flags.
+ */
+SYSCALL_DEFINE5(move_mount,
+ int, from_dfd, const char *, from_pathname,
+ int, to_dfd, const char *, to_pathname,
+ unsigned int, flags)
+{
+ struct path from_path, to_path;
+ unsigned int lflags;
+ int ret = 0;
+
+ if (!may_mount())
+ return -EPERM;
+
+ if (flags & ~MOVE_MOUNT__MASK)
+ return -EINVAL;
+
+ /* If someone gives a pathname, they aren't permitted to move
+ * from an fd that requires unmount as we can't get at the flag
+ * to clear it afterwards.
+ */
+ lflags = 0;
+ if (flags & MOVE_MOUNT_F_SYMLINKS) lflags |= LOOKUP_FOLLOW;
+ if (flags & MOVE_MOUNT_F_AUTOMOUNTS) lflags |= LOOKUP_AUTOMOUNT;
+ if (flags & MOVE_MOUNT_F_EMPTY_PATH) lflags |= LOOKUP_EMPTY;
+
+ ret = user_path_at(from_dfd, from_pathname, lflags, &from_path);
+ if (ret < 0)
+ return ret;
+
+ lflags = 0;
+ if (flags & MOVE_MOUNT_T_SYMLINKS) lflags |= LOOKUP_FOLLOW;
+ if (flags & MOVE_MOUNT_T_AUTOMOUNTS) lflags |= LOOKUP_AUTOMOUNT;
+ if (flags & MOVE_MOUNT_T_EMPTY_PATH) lflags |= LOOKUP_EMPTY;
+
+ ret = user_path_at(to_dfd, to_pathname, lflags, &to_path);
+ if (ret < 0)
+ goto out_from;
+
+ ret = security_move_mount(&from_path, &to_path);
+ if (ret < 0)
+ goto out_to;
+
+ ret = do_move_mount(&from_path, &to_path);
+
+out_to:
+ path_put(&to_path);
+out_from:
+ path_put(&from_path);
+ return ret;
+}
+
/*
* Return true if path is reachable from root
*
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b43bbc893074..924424e7be8f 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -147,6 +147,10 @@
* Parse a string of security data filling in the opts structure
* @options string containing all mount options known by the LSM
* @opts binary data structure usable by the LSM
+ * @move_mount:
+ * Check permission before a mount is moved.
+ * @from_path indicates the mount that is going to be moved.
+ * @to_path indicates the mountpoint that will be mounted upon.
* @dentry_init_security:
* Compute a context for a dentry as the inode is not yet available
* since NFSv4 has no label backed by an EA anyway.
@@ -1480,6 +1484,7 @@ union security_list_options {
unsigned long kern_flags,
unsigned long *set_kern_flags);
int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
+ int (*move_mount)(const struct path *from_path, const struct path *to_path);
int (*dentry_init_security)(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
u32 *ctxlen);
@@ -1811,6 +1816,7 @@ struct security_hook_heads {
struct hlist_head sb_set_mnt_opts;
struct hlist_head sb_clone_mnt_opts;
struct hlist_head sb_parse_opts_str;
+ struct hlist_head move_mount;
struct hlist_head dentry_init_security;
struct hlist_head dentry_create_files_as;
#ifdef CONFIG_SECURITY_PATH
diff --git a/include/linux/security.h b/include/linux/security.h
index 1498b9e0539b..9bb5bc6d596c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -245,6 +245,7 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
+int security_move_mount(const struct path *from_path, const struct path *to_path);
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
u32 *ctxlen);
@@ -599,6 +600,12 @@ static inline int security_sb_parse_opts_str(char *options, struct security_mnt_
return 0;
}
+static inline int security_move_mount(const struct path *from_path,
+ const struct path *to_path)
+{
+ return 0;
+}
+
static inline int security_inode_alloc(struct inode *inode)
{
return 0;
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 3cc6b8f8bd2f..3c0855d9b105 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -901,6 +901,9 @@ asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,
asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len,
int flags, uint32_t sig);
asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags);
+asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path,
+ int to_dfd, const char __user *to_path,
+ unsigned int ms_flags);
/*
* Architecture-specific system calls
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index e8db2911adca..89adf0d731ab 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -7,4 +7,15 @@
#define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */
#define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
+/*
+ * move_mount() flags.
+ */
+#define MOVE_MOUNT_F_SYMLINKS 0x00000001 /* Follow symlinks on from path */
+#define MOVE_MOUNT_F_AUTOMOUNTS 0x00000002 /* Follow automounts on from path */
+#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
+#define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */
+#define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */
+#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
+#define MOVE_MOUNT__MASK 0x00000077
+
#endif /* _UAPI_LINUX_MOUNT_H */
diff --git a/security/security.c b/security/security.c
index 7cafc1c90d16..5149c2cbe8a7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -439,6 +439,11 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
}
EXPORT_SYMBOL(security_sb_parse_opts_str);
+int security_move_mount(const struct path *from_path, const struct path *to_path)
+{
+ return call_int_hook(move_mount, 0, from_path, to_path);
+}
+
int security_inode_alloc(struct inode *inode)
{
inode->i_security = NULL;
^ permalink raw reply related
* [PATCH 01/33] vfs: syscall: Add open_tree(2) to reference or clone a mount [ver #11]
From: David Howells @ 2018-08-01 15:24 UTC (permalink / raw)
To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153313703562.13253.5766498657900728120.stgit@warthog.procyon.org.uk>
From: Al Viro <viro@zeniv.linux.org.uk>
open_tree(dfd, pathname, flags)
Returns an O_PATH-opened file descriptor or an error.
dfd and pathname specify the location to open, in usual
fashion (see e.g. fstatat(2)). flags should be an OR of
some of the following:
* AT_PATH_EMPTY, AT_NO_AUTOMOUNT, AT_SYMLINK_NOFOLLOW -
same meanings as usual
* OPEN_TREE_CLOEXEC - make the resulting descriptor
close-on-exec
* OPEN_TREE_CLONE or OPEN_TREE_CLONE | AT_RECURSIVE -
instead of opening the location in question, create a detached
mount tree matching the subtree rooted at location specified by
dfd/pathname. With AT_RECURSIVE the entire subtree is cloned,
without it - only the part within in the mount containing the
location in question. In other words, the same as mount --rbind
or mount --bind would've taken. The detached tree will be
dissolved on the final close of obtained file. Creation of such
detached trees requires the same capabilities as doing mount --bind.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-api@vger.kernel.org
---
arch/x86/entry/syscalls/syscall_32.tbl | 1
arch/x86/entry/syscalls/syscall_64.tbl | 1
fs/file_table.c | 9 +-
fs/internal.h | 1
fs/namespace.c | 132 +++++++++++++++++++++++++++-----
include/linux/fs.h | 3 +
include/linux/syscalls.h | 1
include/uapi/linux/fcntl.h | 2
include/uapi/linux/mount.h | 10 ++
9 files changed, 135 insertions(+), 25 deletions(-)
create mode 100644 include/uapi/linux/mount.h
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 3cf7b533b3d1..ea1b413afd47 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -398,3 +398,4 @@
384 i386 arch_prctl sys_arch_prctl __ia32_compat_sys_arch_prctl
385 i386 io_pgetevents sys_io_pgetevents __ia32_compat_sys_io_pgetevents
386 i386 rseq sys_rseq __ia32_sys_rseq
+387 i386 open_tree sys_open_tree __ia32_sys_open_tree
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index f0b1709a5ffb..0545bed581dc 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -343,6 +343,7 @@
332 common statx __x64_sys_statx
333 common io_pgetevents __x64_sys_io_pgetevents
334 common rseq __x64_sys_rseq
+335 common open_tree __x64_sys_open_tree
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/file_table.c b/fs/file_table.c
index 7ec0b3e5f05d..7480271a0d21 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -189,6 +189,7 @@ static void __fput(struct file *file)
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
struct inode *inode = file->f_inode;
+ fmode_t mode = file->f_mode;
might_sleep();
@@ -209,14 +210,14 @@ static void __fput(struct file *file)
file->f_op->release(inode, file);
security_file_free(file);
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL &&
- !(file->f_mode & FMODE_PATH))) {
+ !(mode & FMODE_PATH))) {
cdev_put(inode->i_cdev);
}
fops_put(file->f_op);
put_pid(file->f_owner.pid);
- if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+ if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_dec(inode);
- if (file->f_mode & FMODE_WRITER) {
+ if (mode & FMODE_WRITER) {
put_write_access(inode);
__mnt_drop_write(mnt);
}
@@ -224,6 +225,8 @@ static void __fput(struct file *file)
file->f_path.mnt = NULL;
file->f_inode = NULL;
file_free(file);
+ if (unlikely(mode & FMODE_NEED_UNMOUNT))
+ dissolve_on_fput(mnt);
dput(dentry);
mntput(mnt);
}
diff --git a/fs/internal.h b/fs/internal.h
index 56533b08532e..383ee4724f77 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -85,6 +85,7 @@ extern void __mnt_drop_write(struct vfsmount *);
extern void __mnt_drop_write_file(struct file *);
extern void mnt_drop_write_file_path(struct file *);
+extern void dissolve_on_fput(struct vfsmount *);
/*
* fs_struct.c
*/
diff --git a/fs/namespace.c b/fs/namespace.c
index 03cc3b5bcf00..a4a01ecbcacd 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -20,12 +20,14 @@
#include <linux/init.h> /* init_rootfs */
#include <linux/fs_struct.h> /* get_fs_root et.al. */
#include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */
+#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/proc_ns.h>
#include <linux/magic.h>
#include <linux/bootmem.h>
#include <linux/task_work.h>
#include <linux/sched/task.h>
+#include <uapi/linux/mount.h>
#include "pnode.h"
#include "internal.h"
@@ -1840,6 +1842,16 @@ struct vfsmount *collect_mounts(const struct path *path)
return &tree->mnt;
}
+void dissolve_on_fput(struct vfsmount *mnt)
+{
+ namespace_lock();
+ lock_mount_hash();
+ mntget(mnt);
+ umount_tree(real_mount(mnt), UMOUNT_SYNC);
+ unlock_mount_hash();
+ namespace_unlock();
+}
+
void drop_collected_mounts(struct vfsmount *mnt)
{
namespace_lock();
@@ -2199,6 +2211,30 @@ static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
return false;
}
+static struct mount *__do_loopback(struct path *old_path, int recurse)
+{
+ struct mount *mnt = ERR_PTR(-EINVAL), *old = real_mount(old_path->mnt);
+
+ if (IS_MNT_UNBINDABLE(old))
+ return mnt;
+
+ if (!check_mnt(old) && old_path->dentry->d_op != &ns_dentry_operations)
+ return mnt;
+
+ if (!recurse && has_locked_children(old, old_path->dentry))
+ return mnt;
+
+ if (recurse)
+ mnt = copy_tree(old, old_path->dentry, CL_COPY_MNT_NS_FILE);
+ else
+ mnt = clone_mnt(old, old_path->dentry, 0);
+
+ if (!IS_ERR(mnt))
+ mnt->mnt.mnt_flags &= ~MNT_LOCKED;
+
+ return mnt;
+}
+
/*
* do loopback mount.
*/
@@ -2206,7 +2242,7 @@ static int do_loopback(struct path *path, const char *old_name,
int recurse)
{
struct path old_path;
- struct mount *mnt = NULL, *old, *parent;
+ struct mount *mnt = NULL, *parent;
struct mountpoint *mp;
int err;
if (!old_name || !*old_name)
@@ -2220,38 +2256,21 @@ static int do_loopback(struct path *path, const char *old_name,
goto out;
mp = lock_mount(path);
- err = PTR_ERR(mp);
- if (IS_ERR(mp))
+ if (IS_ERR(mp)) {
+ err = PTR_ERR(mp);
goto out;
+ }
- old = real_mount(old_path.mnt);
parent = real_mount(path->mnt);
-
- err = -EINVAL;
- if (IS_MNT_UNBINDABLE(old))
- goto out2;
-
if (!check_mnt(parent))
goto out2;
- if (!check_mnt(old) && old_path.dentry->d_op != &ns_dentry_operations)
- goto out2;
-
- if (!recurse && has_locked_children(old, old_path.dentry))
- goto out2;
-
- if (recurse)
- mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE);
- else
- mnt = clone_mnt(old, old_path.dentry, 0);
-
+ mnt = __do_loopback(&old_path, recurse);
if (IS_ERR(mnt)) {
err = PTR_ERR(mnt);
goto out2;
}
- mnt->mnt.mnt_flags &= ~MNT_LOCKED;
-
err = graft_tree(mnt, parent, mp);
if (err) {
lock_mount_hash();
@@ -2265,6 +2284,75 @@ static int do_loopback(struct path *path, const char *old_name,
return err;
}
+SYSCALL_DEFINE3(open_tree, int, dfd, const char *, filename, unsigned, flags)
+{
+ struct file *file;
+ struct path path;
+ int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW;
+ bool detached = flags & OPEN_TREE_CLONE;
+ int error;
+ int fd;
+
+ BUILD_BUG_ON(OPEN_TREE_CLOEXEC != O_CLOEXEC);
+
+ if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE |
+ AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE |
+ OPEN_TREE_CLOEXEC))
+ return -EINVAL;
+
+ if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE)) == AT_RECURSIVE)
+ return -EINVAL;
+
+ if (flags & AT_NO_AUTOMOUNT)
+ lookup_flags &= ~LOOKUP_AUTOMOUNT;
+ if (flags & AT_SYMLINK_NOFOLLOW)
+ lookup_flags &= ~LOOKUP_FOLLOW;
+ if (flags & AT_EMPTY_PATH)
+ lookup_flags |= LOOKUP_EMPTY;
+
+ if (detached && !may_mount())
+ return -EPERM;
+
+ fd = get_unused_fd_flags(flags & O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ error = user_path_at(dfd, filename, lookup_flags, &path);
+ if (error)
+ goto out;
+
+ if (detached) {
+ struct mount *mnt = __do_loopback(&path, flags & AT_RECURSIVE);
+ if (IS_ERR(mnt)) {
+ error = PTR_ERR(mnt);
+ goto out2;
+ }
+ mntput(path.mnt);
+ path.mnt = &mnt->mnt;
+ }
+
+ file = dentry_open(&path, O_PATH, current_cred());
+ if (IS_ERR(file)) {
+ error = PTR_ERR(file);
+ goto out3;
+ }
+
+ if (detached)
+ file->f_mode |= FMODE_NEED_UNMOUNT;
+ path_put(&path);
+ fd_install(fd, file);
+ return fd;
+
+out3:
+ if (detached)
+ dissolve_on_fput(path.mnt);
+out2:
+ path_put(&path);
+out:
+ put_unused_fd(fd);
+ return error;
+}
+
static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
{
int error = 0;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e3a18cddb74e..067f0e31aec7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -154,6 +154,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
/* File is capable of returning -EAGAIN if I/O will block */
#define FMODE_NOWAIT ((__force fmode_t)0x8000000)
+/* File represents mount that needs unmounting */
+#define FMODE_NEED_UNMOUNT ((__force fmode_t)0x10000000)
+
/*
* Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
* that indicates that they should check the contents of the iovec are
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 73810808cdf2..3cc6b8f8bd2f 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -900,6 +900,7 @@ asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,
unsigned mask, struct statx __user *buffer);
asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len,
int flags, uint32_t sig);
+asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags);
/*
* Architecture-specific system calls
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index 6448cdd9a350..594b85f7cb86 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -90,5 +90,7 @@
#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */
#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */
+#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
+
#endif /* _UAPI_LINUX_FCNTL_H */
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
new file mode 100644
index 000000000000..e8db2911adca
--- /dev/null
+++ b/include/uapi/linux/mount.h
@@ -0,0 +1,10 @@
+#ifndef _UAPI_LINUX_MOUNT_H
+#define _UAPI_LINUX_MOUNT_H
+
+/*
+ * open_tree() flags.
+ */
+#define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */
+#define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
+
+#endif /* _UAPI_LINUX_MOUNT_H */
^ permalink raw reply related
* [PATCH 00/33] VFS: Introduce filesystem context [ver #11]
From: David Howells @ 2018-08-01 15:23 UTC (permalink / raw)
To: viro
Cc: John Johansen, Tejun Heo, Eric W. Biederman, selinux, Paul Moore,
Li Zefan, linux-api, apparmor, Casey Schaufler, fenghua.yu,
Greg Kroah-Hartman, Eric Biggers, linux-security-module,
Tetsuo Handa, Johannes Weiner, Stephen Smalley, tomoyo-dev-en,
cgroups, torvalds, dhowells, linux-fsdevel, linux-kernel
Hi Al,
Here are a set of patches to create a filesystem 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 is also used for remount
since much of the parsing stuff is common in many filesystems.
This allows namespaces and other information to be conveyed through the
mount procedure.
This also allows Miklós Szeredi's idea of doing:
fd = fsopen("nfs");
fsconfig(fd, FSCONFIG_SET_STRING, "option", "val", 0);
fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
mfd = fsmount(fd, MS_NODEV);
move_mount(mfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
that he presented at LSF-2017 to be implemented (see the relevant patches
in the series).
I didn't use netlink as that would make the core kernel depend on
CONFIG_NET and CONFIG_NETLINK and would introduce network namespacing
issues.
I've implemented filesystem context handling for procfs, nfs, mqueue,
cpuset, kernfs, sysfs, cgroup and afs filesystems.
Unconverted filesystems are handled by a legacy filesystem wrapper.
====================
WHY DO WE WANT THIS?
====================
Firstly, there's a bunch of problems with the mount(2) syscall:
(1) It's actually six or seven different interfaces rolled into one and
weird combinations of flags make it do different things beyond the
original specification of the syscall.
(2) It produces a particularly large and diverse set of errors, which have
to be mapped back to a small error code. Yes, there's dmesg - if you
have it configured - but you can't necessarily see that if you're
doing a mount inside of a container.
(3) It copies a PAGE_SIZE block of data for each of the type, device name
and options.
(4) The size of the buffers is PAGE_SIZE - and this is arch dependent.
(5) You can't mount into another mount namespace. I could, for example,
build a container without having to be in that container's namespace
if I can do it from outside.
(6) It's not really geared for the specification of multiple sources, but
some filesystems really want that - overlayfs, for example.
and some problems in the internal kernel api:
(1) There's no defined way to supply namespace configuration for the
superblock - so, for instance, I can't say that I want to create a
superblock in a particular network namespace (on automount, say).
NFS hacks around this by creating multiple shadow file_system_types
with different ->mount() ops.
(2) When calling mount internally, unless you have NFS-like hacks, you
have to generate or otherwise provide text config data which then gets
parsed, when some of the time you could bypass the parsing stage
entirely.
(3) The amount of data in the data buffer is not known, but the data
buffer might be on a kernel stack somewhere, leading to the
possibility of tripping the stack underrun guard.
and other issues too:
(1) Superblock remount in some filesystems applies options on an as-parsed
basis, so if there's a parse failure, a partial alteration with no
rollback is effected.
(2) Under some circumstances, the mount data may get copied multiple times
so that it can have multiple parsers applied to it or because it has
to be parsed multiple times - for instance, once to get the
preliminary info required to access the on-disk superblock and then
again to update the superblock record in the kernel.
I want to be able to add support for a bunch of things:
(1) UID, GID and Project ID mapping/translation. I want to be able to
install a translation table of some sort on the superblock to
translate source identifiers (which may be foreign numeric UIDs/GIDs,
text names, GUIDs) into system identifiers. This needs to be done
before the superblock is published[*].
Note that this may, for example, involve using the context and the
superblock held therein to issue an RPC to a server to look up
translations.
[*] By "published" I mean made available through mount so that other
userspace processes can access it by path.
Maybe specifying a translation range element with something like:
fsconfig(fd, fsconfig_translate_uid, "<srcuid> <nsuid> <count>", 0, 0);
The translation information also needs to propagate over an automount
in some circumstances.
(2) Namespace configuration. I want to be able to tell the superblock
creation process what namespaces should be applied when it created (in
particular the userns and netns) for containerisation purposes, e.g.:
fsconfig(fd, FSCONFIG_SET_NAMESPACE, "user", 0, userns_fd);
fsconfig(fd, FSCONFIG_SET_NAMESPACE, "net", 0, netns_fd);
(3) Namespace propagation. I want to have a properly defined mechanism
for propagating namespace configuration over automounts within the
kernel. This will be particularly useful for network filesystems.
(4) Pre-mount attribute query. A chunk of the changes is actually the
fsinfo() syscall to query attributes of the filesystem beyond what's
available in statx() and statfs(). This will allow a created
superblock to be queried before it is published.
(5) Upcall for configuration. I would like to be able to query
configuration that's stored in userspace when an automount is made.
For instance, to look up network parameters for NFS or to find a cache
selector for fscache.
The internal fs_context could be passed to the upcall process or the
kernel could read a config file directly if named appropriately for the
superblock, perhaps:
[/etc/fscontext.d/afs/example.com/cell.cfg]
realm = EXAMPLE.COM
translation = uid,3000,4000,100
fscache = tag=fred
(6) Event notifications. I want to be able to install a watch on a
superblock before it is published to catch things like quota events
and EIO.
(7) Large and binary parameters. There might be at some point a need to
pass large/binary objects like Microsoft PACs around. If I understand
PACs correctly, you can obtain these from the Kerberos server and then
pass them to the file server when you connect.
Having it possible to pass large or binary objects as individual
fsconfig calls make parsing these trivial. OTOH, some or all of this
can potentially be handled with the use of the keyrings interface - as
the afs filesystem does for passing kerberos tokens around; it's just
that that seems overkill for a parameter you may only need once.
===================
SIGNIFICANT CHANGES
===================
ver #11:
(*) Fixed AppArmor.
(*) Capitalised all the UAPI constants.
(*) Explicitly numbered the FSCONFIG_* UAPI constants.
(*) Removed all the places ANON_INODES is selected.
(*) Fixed a bug whereby the context gets freed twice (which broke mounts of
procfs).
(*) Split fsinfo() off into its own patch series.
ver #10:
(*) Renamed "option" to "parameter" in a number of places.
(*) Replaced the use of write() to drive the configuration with an fsconfig()
syscall. This also allows at-style paths and fds to be presented as typed
object.
(*) Routed the key=value parameter concept all the way through from the
fsconfig() system call to the LSM and filesystem.
(*) Added a parameter-description concept and helper functions to help
interpret a parameter and possibly convert the value.
(*) Made it possible to query the parameter description using the fsinfo()
syscall. Added a test-fs-query sample to dump the parameters used by a
filesystem.
ver #9:
(*) Dropped the fd cookie stuff and the FMODE_*/O_* split stuff.
(*) Al added an open_tree() system call to allow a mount tree to be picked
referenced or cloned into an O_PATH-style fd. This can then be used
with sys_move_mount(). Dropped the O_CLONE_MOUNT and O_NON_RECURSIVE
open() flags.
(*) Brought error logging back in, though only in the fs_context and not
in the task_struct.
(*) Separated MS_REMOUNT|MS_BIND handling from MS_REMOUNT handling.
(*) Used anon_inodes for the fd returned by fsopen() and fspick(). This
requires making it unconditional.
(*) Fixed lots of bugs. Especial thanks to Al and Eric Biggers for
finding them and providing patches.
(*) Wrote manual pages, which I'll post separately.
ver #8:
(*) Changed the way fsmount() mounts into the namespace according to some
of Al's ideas.
(*) Put better typing on the fd cookie obtained from __fdget() & co..
(*) Stored the fd cookie in struct nameidata rather than the dfd number.
(*) Changed sys_fsmount() to return an O_PATH-style fd rather than
actually mounting into the mount namespace.
(*) Separated internal FMODE_* handling from O_* handling to free up
certain O_* flag numbers.
(*) Added two new open flags (O_CLONE_MOUNT and O_NON_RECURSIVE) for use
with open(O_PATH) to copy a mount or mount-subtree to an O_PATH fd.
(*) Added a new syscall, sys_move_mount(), to move a mount from an
dfd+path source to a dfd+path destination.
(*) Added a file->f_mode flag (FMODE_NEED_UNMOUNT) that indicates that the
vfsmount attached to file->f_path needs 'unmounting' if set.
(*) Made sys_move_mount() clear FMODE_NEED_UNMOUNT if successful.
[!] This doesn't work quite right.
(*) Added a new syscall, fsinfo(), to query information about a
filesystem. The idea being that this will, in future, work with the
fd from fsopen() too and permit querying of the parameters and
metadata before fsmount() is called.
ver #7:
(*) Undo an incorrect MS_* -> SB_* conversion.
(*) Pass the mount data buffer size to all the mount-related functions that
take the data pointer. This fixes a problem where someone (say SELinux)
tries to copy the mount data, assuming it to be a page in size, and
overruns the buffer - thereby incurring an oops by hitting a guard page.
(*) Made the AFS filesystem use them as an example. This is a much easier to
deal with than with NFS or Ext4 as there are very few mount options.
ver #6:
(*) Dropped the supplementary error string facility for the moment.
(*) Dropped the NFS patches for the moment.
(*) Dropped the reserved file descriptor argument from fsopen() and
replaced it with three reserved pointers that must be NULL.
ver #5:
(*) Renamed sb_config -> fs_context and adjusted variable names.
(*) Differentiated the flags in sb->s_flags (now named SB_*) from those
passed to mount(2) (named MS_*).
(*) Renamed __vfs_new_fs_context() to vfs_new_fs_context() and made the
caller always provide a struct file_system_type pointer and the
parameters required.
(*) Got rid of vfs_submount_fc() in favour of passing
FS_CONTEXT_FOR_SUBMOUNT to vfs_new_fs_context(). The purpose is now
used more.
(*) Call ->validate() on the remount path.
(*) Got rid of the inode locking in sys_fsmount().
(*) Call security_sb_mountpoint() in the mount(2) path.
ver #4:
(*) Split the sb_config patch up somewhat.
(*) Made the supplementary error string facility something attached to the
task_struct rather than the sb_config so that error messages can be
obtained from NFS doing a mount-root-and-pathwalk inside the
nfs_get_tree() operation.
Further, made this managed and read by prctl rather than through the
mount fd so that it's more generally available.
ver #3:
(*) Rebased on 4.12-rc1.
(*) Split the NFS patch up somewhat.
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.
Possible 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.
(*) 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:
https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git
tagged as:
mount-api-20180801
on branch:
mount-api
David
---
Al Viro (2):
vfs: syscall: Add open_tree(2) to reference or clone a mount
teach move_mount(2) to work with OPEN_TREE_CLONE
David Howells (31):
vfs: syscall: Add move_mount(2) to move mounts around
vfs: Suppress MS_* flag defs within the kernel unless explicitly enabled
vfs: Introduce the basic header for the new mount API's filesystem context
vfs: Introduce logging functions
vfs: Add configuration parser helpers
vfs: Add LSM hooks for the new mount API
selinux: Implement the new mount API LSM hooks
smack: Implement filesystem context security hooks
apparmor: Implement security hooks for the new mount API
tomoyo: Implement security hooks for the new mount API
vfs: Separate changing mount flags full remount
vfs: Implement a filesystem superblock creation/configuration context
vfs: Remove unused code after filesystem context changes
procfs: Move proc_fill_super() to fs/proc/root.c
proc: Add fs_context support to procfs
ipc: Convert mqueue fs to fs_context
cpuset: Use fs_context
kernfs, sysfs, cgroup, intel_rdt: Support fs_context
hugetlbfs: Convert to fs_context
vfs: Remove kern_mount_data()
vfs: Provide documentation for new mount API
Make anon_inodes unconditional
vfs: syscall: Add fsopen() to prepare for superblock creation
vfs: Implement logging through fs_context
vfs: Add some logging to the core users of the fs_context log
vfs: syscall: Add fsconfig() for configuring and managing a context
vfs: syscall: Add fsmount() to create a mount for a superblock
vfs: syscall: Add fspick() to select a superblock for reconfiguration
afs: Add fs_context support
afs: Use fs_context to pass parameters over automount
vfs: Add a sample program for the new mount API
Documentation/filesystems/mount_api.txt | 706 ++++++++++++++++++++++++
arch/arc/kernel/setup.c | 1
arch/arm/kernel/atags_parse.c | 1
arch/arm/kvm/Kconfig | 1
arch/arm64/kvm/Kconfig | 1
arch/mips/kvm/Kconfig | 1
arch/powerpc/kvm/Kconfig | 1
arch/s390/kvm/Kconfig | 1
arch/sh/kernel/setup.c | 1
arch/sparc/kernel/setup_32.c | 1
arch/sparc/kernel/setup_64.c | 1
arch/x86/Kconfig | 1
arch/x86/entry/syscalls/syscall_32.tbl | 6
arch/x86/entry/syscalls/syscall_64.tbl | 6
arch/x86/kernel/cpu/intel_rdt.h | 15 +
arch/x86/kernel/cpu/intel_rdt_rdtgroup.c | 184 ++++--
arch/x86/kernel/setup.c | 1
arch/x86/kvm/Kconfig | 1
drivers/base/Kconfig | 1
drivers/base/devtmpfs.c | 1
drivers/char/tpm/Kconfig | 1
drivers/dma-buf/Kconfig | 1
drivers/gpio/Kconfig | 1
drivers/iio/Kconfig | 1
drivers/infiniband/Kconfig | 1
drivers/vfio/Kconfig | 1
fs/Kconfig | 7
fs/Makefile | 5
fs/afs/internal.h | 9
fs/afs/mntpt.c | 148 +++--
fs/afs/super.c | 470 +++++++++-------
fs/afs/volume.c | 4
fs/f2fs/super.c | 2
fs/file_table.c | 9
fs/filesystems.c | 4
fs/fs_context.c | 779 +++++++++++++++++++++++++++
fs/fs_parser.c | 476 ++++++++++++++++
fs/fsopen.c | 491 +++++++++++++++++
fs/hugetlbfs/inode.c | 392 ++++++++------
fs/internal.h | 13
fs/kernfs/mount.c | 87 +--
fs/libfs.c | 19 +
fs/namei.c | 4
fs/namespace.c | 867 +++++++++++++++++++++++-------
fs/notify/fanotify/Kconfig | 1
fs/notify/inotify/Kconfig | 1
fs/pnode.c | 1
fs/proc/inode.c | 51 --
fs/proc/internal.h | 6
fs/proc/root.c | 245 ++++++--
fs/super.c | 368 ++++++++++---
fs/sysfs/mount.c | 67 ++
include/linux/cgroup.h | 3
include/linux/fs.h | 21 +
include/linux/fs_context.h | 208 +++++++
include/linux/fs_parser.h | 116 ++++
include/linux/kernfs.h | 39 +
include/linux/lsm_hooks.h | 70 ++
include/linux/module.h | 6
include/linux/mount.h | 5
include/linux/security.h | 61 ++
include/linux/syscalls.h | 9
include/uapi/linux/fcntl.h | 2
include/uapi/linux/fs.h | 82 +--
include/uapi/linux/mount.h | 75 +++
init/Kconfig | 10
init/do_mounts.c | 1
init/do_mounts_initrd.c | 1
ipc/mqueue.c | 121 +++-
kernel/cgroup/cgroup-internal.h | 50 +-
kernel/cgroup/cgroup-v1.c | 347 +++++++-----
kernel/cgroup/cgroup.c | 256 ++++++---
kernel/cgroup/cpuset.c | 68 ++
samples/Kconfig | 6
samples/Makefile | 2
samples/mount_api/Makefile | 7
samples/mount_api/test-fsmount.c | 118 ++++
security/apparmor/include/mount.h | 11
security/apparmor/lsm.c | 108 ++++
security/apparmor/mount.c | 47 ++
security/security.c | 51 ++
security/selinux/hooks.c | 311 ++++++++++-
security/smack/smack.h | 11
security/smack/smack_lsm.c | 370 ++++++++++++-
security/tomoyo/common.h | 3
security/tomoyo/mount.c | 46 ++
security/tomoyo/tomoyo.c | 15 +
87 files changed, 6637 insertions(+), 1484 deletions(-)
create mode 100644 Documentation/filesystems/mount_api.txt
create mode 100644 fs/fs_context.c
create mode 100644 fs/fs_parser.c
create mode 100644 fs/fsopen.c
create mode 100644 include/linux/fs_context.h
create mode 100644 include/linux/fs_parser.h
create mode 100644 include/uapi/linux/mount.h
create mode 100644 samples/mount_api/Makefile
create mode 100644 samples/mount_api/test-fsmount.c
^ permalink raw reply
* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Oleg Nesterov @ 2018-08-01 14:19 UTC (permalink / raw)
To: Jürg Billeter
Cc: Andrew Morton, Thomas Gleixner, Eric Biederman, linux-api,
linux-kernel
In-Reply-To: <f21a22c9730ab455504f58a5fbda72cba0eb1227.camel@bitron.ch>
On 07/31, Jürg Billeter wrote:
>
> > Could you explain your use-case? Why a shell wants to use
> > CLONE_NEWPID?
>
> To guarantee that there won't be any runaway processes, i.e., ensure
> that no descendants (background helper daemons or misbehaving
> processes) survive when the child process is terminated.
We already have PR_SET_CHILD_SUBREAPER.
Perhaps we can finally add PR_KILL_MY_DESCENDANTS_ON_EXIT? This was already
discussed some time ago, but I can't find the previous discussion... Simple
to implement.
> And to prevent
> children from killing their ancestors.
OK, this is the only reason for CLONE_NEWPID which I can understand so far.
Not that I understand why this is that useful ;)
> > > * As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
> > > itself, it's not possible to implement the stop action in a custom
> > > SIGTSTP handler.
> >
> > Yes. So may be we actually want to change __isig() paths to use
> > SEND_SIG_FORCED (this is not that simple), or perhaps we can change
> > __send_signal() to not drop SIGSTOP sent to itself, or may be we can even
> > introduce SIG_DFL_EVEN_IF_INIT, I dunno.
>
> In my opinion, my patch is much simpler and also more general as it
Yes, yes, let me repeat that I am not arguing with your patch, I am just trying
to understand what
> > I can't understand this. An application should be changed anyway to do
> > PR_SET_KILLABLE?
>
> PR_SET_KILLABLE can be called (e.g., by the shell) between clone() and
> execve().
OK, this is true.
Oleg.
^ permalink raw reply
* Re: [PATCH] prctl: add PR_[GS]ET_KILLABLE
From: Jann Horn @ 2018-08-01 7:56 UTC (permalink / raw)
To: j; +Cc: Andrew Morton, Oleg Nesterov, Eric W. Biederman, Linux API,
kernel list
In-Reply-To: <625ede00c618783eb610b7109c35c514e8faa793.camel@bitron.ch>
On Wed, Aug 1, 2018 at 9:44 AM Jürg Billeter <j@bitron.ch> wrote:
>
> On Tue, 2018-07-31 at 18:26 +0200, Jann Horn wrote:
> > On Mon, Jul 30, 2018 at 10:01 AM Jürg Billeter <j@bitron.ch> wrote:
> >
> > [...]
> > > diff --git a/kernel/sys.c b/kernel/sys.c
> > > index 38509dc1f77b..264de630d548 100644
> > > --- a/kernel/sys.c
> > > +++ b/kernel/sys.c
> >
> > [...]
> > > + case PR_SET_KILLABLE:
> > > + if (arg2 != 1 || arg3 || arg4 || arg5)
> > > + return -EINVAL;
> > > + me->signal->flags &= ~SIGNAL_UNKILLABLE;
> > > + break;
> >
> > I don't have an opinion on this patchset otherwise, but should this
> > prctl maybe block PR_SET_KILLABLE if you're actually the real init
> > process? This seems like it could potentially lead to weird things.
>
> While I don't expect global init to use this, I can't think of a good
> reason to disallow it in the kernel. Do you have specific concerns or
> is the code in kernel/fork.c the only reason?
No, I don't have any other specific concerns.
> I prefer avoiding special cases unless really required.
^ permalink raw reply
* Re: [PATCH] prctl: add PR_[GS]ET_KILLABLE
From: Jürg Billeter @ 2018-08-01 7:43 UTC (permalink / raw)
To: Jann Horn
Cc: Andrew Morton, Oleg Nesterov, Eric W. Biederman, Linux API,
kernel list
In-Reply-To: <CAG48ez1izZ+W_cPdNp3SOUwb_8YzwAYdSA6d=eqvaF=5+GKemA@mail.gmail.com>
On Tue, 2018-07-31 at 18:26 +0200, Jann Horn wrote:
> On Mon, Jul 30, 2018 at 10:01 AM Jürg Billeter <j@bitron.ch> wrote:
>
> [...]
> > diff --git a/kernel/sys.c b/kernel/sys.c
> > index 38509dc1f77b..264de630d548 100644
> > --- a/kernel/sys.c
> > +++ b/kernel/sys.c
>
> [...]
> > + case PR_SET_KILLABLE:
> > + if (arg2 != 1 || arg3 || arg4 || arg5)
> > + return -EINVAL;
> > + me->signal->flags &= ~SIGNAL_UNKILLABLE;
> > + break;
>
> I don't have an opinion on this patchset otherwise, but should this
> prctl maybe block PR_SET_KILLABLE if you're actually the real init
> process? This seems like it could potentially lead to weird things.
While I don't expect global init to use this, I can't think of a good
reason to disallow it in the kernel. Do you have specific concerns or
is the code in kernel/fork.c the only reason? I prefer avoiding special
cases unless really required.
> This code in kernel/fork.c seems to rely on the fact that global init
> is SIGNAL_UNKILLABLE, and probably also leads to weirdness if
> container init is non-SIGNAL_UNKILLABLE:
Yes, Oleg has mentioned this as well. I have to change copy_process()
to directly check for the PID namespace root process instead of
checking for SIGNAL_UNKILLABLE.
Jürg
^ permalink raw reply
* Re: [PATCH 34/38] vfs: syscall: Add fsinfo() to query filesystem information [ver #10]
From: David Howells @ 2018-08-01 1:07 UTC (permalink / raw)
To: Darrick J. Wong
Cc: dhowells, viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <20180731234909.GA4206@magnolia>
Darrick J. Wong <darrick.wong@oracle.com> wrote:
> <snip> I only have time today to review the user interface bits...
Thanks:-)
> > + fsinfo_attr_volume_id = 7, /* Volume ID (string) */
> > + fsinfo_attr_volume_uuid = 8, /* Volume UUID (LE uuid) */
> > + fsinfo_attr_volume_name = 9, /* Volume name (string) */
>
> What's the difference between a volume name and a volume string?
Um? There is no "volume string" defined.
What the parenthesis in the comment means is that fsinfo_attr_volume_name
returns a variable-length string rather than a fixed structure - ie. it's type
information.
> XFS has a uuid and a label that can be set by userspace (sort of);
> should we return the label for volume_id and volume_name?
>
> Hmmm, I see that the default implementations set volume_id from s_id,
> and s_id (for block device filesystems anyway) tends to be the device, I
> guess?
>
> So if blkid told me that:
> /dev/sda1: LABEL="music" UUID="8d9e5b1e-a094-49e5-a179-6d94f7fd8399" TYPE="xfs"
>
> volume_id == sda1, volume_uuid == 8d9e5b1e-a094-49e5-a179-6d94f7fd8399,
> and volume_name == "music" ?
I would do it like that. Note that these things are described in the manual
page that I posted previously. I'll attach that here (note that it needs
updating).
> > + fsinfo_attr_source = 16, /* Nth mount source name (string) */
>
> Hmm, so I guess external log devices and realtime device(s) go here?
Ummm... Not sure. I feel like they should, but they can also go in
FSINFO_ATTR_PARAMETER if they're already described by a mount parameter.
I was thinking more of bcachefs where the "source" parameter to mount(2) looks
something like "/dev/sda1:/dev/sda2".
One of the important considerations for setting up the parser is that we still
have to handle mount(2) for existing filesystems.
> Are we tied to this enum forever, or do you plan to split up the number
> space to allow filesystems to define their own attributes without having
> to add them here?
>
> For example, say you let the upper 8 bits be some sort of per-fs code
> (like how _IO{,R,W} work) and the lower 24 bits can be the subcommand.
> 0x00 would be the generic space; XFS could (say) reserve 0x58000000 -
> 0x58ffffff for XFS (0x58 is the prefix code used for xfs ioctls). If
> there ever are subdivisions of the number space it might be nice to have
> fsinfo_fsinfo return prefix number of the fs-specific subcommands, and
> how many fs-specific subcommands there are.
>
> I mean, I guess each fs' ->fsinfo function can do that privately but I
> suggest having some mechanism in mind to handle these things. XFS's
> geometry ioctl structure is nearly out of space and (some day soon) we
> will have to expand and maybe we can use fsinfo instead.
I was planning on requiring them to be added here and also listed in:
static const u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = {
FSINFO_STRUCT (STATFS, statfs),
FSINFO_STRUCT (FSINFO, fsinfo),
...
};
in fs/statfs.c.
> > + __u32 f_dev_minor;
> > +};
>
> This structure doesn't end on a 64-bit boundary and may cause padding
> problems...
I've fixed that, thanks.
> Maximum inode number possible, for filesystems that can allocate inodes
> dynamically?
>
> Granted, XFS will probably only ever advertise "0xffffffffffffffff"...
Is that possible with any of our current interfaces?
It's something I can add, but I can imagine circumstances where the inode
number space has holes in it that can't be allocated (say inode numbers
correspond to particular blocks on disk). I wonder if that's something I need
worry about.
Btw, note that the fsinfo() interface is constructed such that it's practical
to expand any particular struct in the future, provided any new fields are
tagged on the end and don't mind defaulting to 0. fsinfo() returns just the
data you asked for, truncating the returned data to fit your request. If you
ask for more than it has, then it clears the excess space (hence the
defaulting to 0 condition above).
> > +struct fsinfo_io_size {
> > + __u32 dio_size_gran; /* Size granularity for O_DIRECT */
> > + __u32 dio_mem_align; /* Memory alignment for O_DIRECT */
>
> max io size too?
That needs more discussion, I think, particularly involving Dave Chinner.
> 64-bit too, in case we ever get that insane?
If you really want. 4GiB alignment and granularity is a bit insane, though.
David
---
'\" t
.\" Copyright (c) 2018 David Howells <dhowells@redhat.com>
.\"
.\" %%%LICENSE_START(VERBATIM)
.\" Permission is granted to make and distribute verbatim copies of this
.\" manual provided the copyright notice and this permission notice are
.\" preserved on all copies.
.\"
.\" Permission is granted to copy and distribute modified versions of this
.\" manual under the conditions for verbatim copying, provided that the
.\" entire resulting derived work is distributed under the terms of a
.\" permission notice identical to this one.
.\"
.\" Since the Linux kernel and libraries are constantly changing, this
.\" manual page may be incorrect or out-of-date. The author(s) assume no
.\" responsibility for errors or omissions, or for damages resulting from
.\" the use of the information contained herein. The author(s) may not
.\" have taken the same level of care in the production of this manual,
.\" which is licensed free of charge, as they might when working
.\" professionally.
.\"
.\" Formatted or processed versions of this manual, if unaccompanied by
.\" the source, must acknowledge the copyright and authors of this work.
.\" %%%LICENSE_END
.\"
.TH FSINFO 2 2018-06-06 "Linux" "Linux Programmer's Manual"
.SH NAME
fsinfo \- Get filesystem information
.SH SYNOPSIS
.nf
.B #include <sys/types.h>
.br
.B #include <sys/fsinfo.h>
.br
.B #include <unistd.h>
.br
.BR "#include <fcntl.h> " "/* Definition of AT_* constants */"
.PP
.BI "int fsinfo(int " dirfd ", const char *" pathname ","
.BI " struct fsinfo_params *" params ","
.BI " void *" buffer ", size_t " buf_size );
.fi
.PP
.IR Note :
There is no glibc wrapper for
.BR fsinfo ();
see NOTES.
.SH DESCRIPTION
.PP
fsinfo() retrieves the desired filesystem attribute, as selected by the
parameters pointed to by
.IR params ,
and stores its value in the buffer pointed to by
.IR buffer .
.PP
The parameter structure is optional, defaulting to all the parameters being 0
if the pointer is NULL. The structure looks like the following:
.PP
.in +4n
.nf
struct fsinfo_params {
__u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar flags */
__u32 request; /* Requested attribute */
__u32 Nth; /* Instance of attribute */
__u32 Mth; /* Subinstance of Nth instance */
__u32 __reserved[6]; /* Reserved params; all must be 0 */
};
.fi
.in
.PP
The filesystem to be queried is looked up using a combination of
.IR dfd ", " pathname " and " params->at_flags.
This is discussed in more detail below.
.PP
The desired attribute is indicated by
.IR params->request .
If
.I params
is NULL, this will default to
.BR fsinfo_attr_statfs ,
which retrieves some of the information returned by
.BR statfs ().
The available attributes are described below in the "THE ATTRIBUTES" section.
.PP
Some attributes can have multiple values and some can even have multiple
instances with multiple values. For example, a network filesystem might use
multiple servers. The names of each of these servers can be retrieved by
using
.I params->Nth
to iterate through all the instances until error
.B ENODATA
occurs, indicating the end of the list. Further, each server might have
multiple addresses available; these can be enumerated using
.I params->Nth
to iterate the servers and
.I params->Mth
to iterate the addresses of the Nth server.
.PP
The amount of data written into the buffer depends on the attribute selected.
Some attributes return variable-length strings and some return fixed-size
structures. If either
.IR buffer " is NULL or " buf_size " is 0"
then the size of the attribute value will be returned and nothing will be
written into the buffer.
.PP
The
.I params->__reserved
parameters must all be 0.
.\"_______________________________________________________
.SS
Allowance for Future Attribute Expansion
.PP
To allow for the future expansion and addition of fields to any fixed-size
structure attribute,
.BR fsinfo ()
makes the following guarantees:
.RS 4m
.IP (1) 4m
It will always clear any excess space in the buffer.
.IP (2) 4m
It will always return the actual size of the data.
.IP (3) 4m
It will truncate the data to fit it into the buffer rather than giving an
error.
.IP (4) 4m
Any new version of a structure will incorporate all the fields from the old
version at same offsets.
.RE
.PP
So, for example, if the caller is running on an older version of the kernel
with an older, smaller version of the structure than was asked for, the kernel
will write the smaller version into the buffer and will clear the remainder of
the buffer to make sure any additional fields are set to 0. The function will
return the actual size of the data.
.PP
On the other hand, if the caller is running on a newer version of the kernel
with a newer version of the structure that is larger than the buffer, the write
to the buffer will be truncated to fit as necessary and the actual size of the
data will be returned.
.PP
Note that this doesn't apply to variable-length string attributes.
.\"_______________________________________________________
.SS
Invoking \fBfsinfo\fR():
.PP
To access a file's status, no permissions are required on the file itself, but
in the case of
.BR fsinfo ()
with a path, execute (search) permission is required on all of the directories
in
.I pathname
that lead to the file.
.PP
.BR fsinfo ()
uses
.IR pathname ", " dirfd " and " params->at_flags
to locate the target file in one of a variety of ways:
.TP
[*] By absolute path.
.I pathname
points to an absolute path and
.I dirfd
is ignored. The file is looked up by name, starting from the root of the
filesystem as seen by the calling process.
.TP
[*] By cwd-relative path.
.I pathname
points to a relative path and
.IR dirfd " is " AT_FDCWD .
The file is looked up by name, starting from the current working directory.
.TP
[*] By dir-relative path.
.I pathname
points to relative path and
.I dirfd
indicates a file descriptor pointing to a directory. The file is looked up by
name, starting from the directory specified by
.IR dirfd .
.TP
[*] By file descriptor.
.IR pathname " is " NULL " and " dirfd
indicates a file descriptor. The file attached to the file descriptor is
queried directly. The file descriptor may point to any type of file, not just
a directory.
.PP
.I flags
can be used to influence a path-based lookup. A value for
.I flags
is constructed by OR'ing together zero or more of the following constants:
.TP
.BR AT_EMPTY_PATH
.\" commit 65cfc6722361570bfe255698d9cd4dccaf47570d
If
.I pathname
is an empty string, operate on the file referred to by
.IR dirfd
(which may have been obtained using the
.BR open (2)
.B O_PATH
flag).
If
.I dirfd
is
.BR AT_FDCWD ,
the call operates on the current working directory.
In this case,
.I dirfd
can refer to any type of file, not just a directory.
This flag is Linux-specific; define
.B _GNU_SOURCE
.\" Before glibc 2.16, defining _ATFILE_SOURCE sufficed
to obtain its definition.
.TP
.BR AT_NO_AUTOMOUNT
Don't automount the terminal ("basename") component of
.I pathname
if it is a directory that is an automount point. This allows the caller to
gather attributes of the filesystem holding an automount point (rather than
the filesystem it would mount). This flag can be used in tools that scan
directories to prevent mass-automounting of a directory of automount points.
The
.B AT_NO_AUTOMOUNT
flag has no effect if the mount point has already been mounted over.
This flag is Linux-specific; define
.B _GNU_SOURCE
.\" Before glibc 2.16, defining _ATFILE_SOURCE sufficed
to obtain its definition.
.TP
.B AT_SYMLINK_NOFOLLOW
If
.I pathname
is a symbolic link, do not dereference it:
instead return information about the link itself, like
.BR lstat ().
.SH THE ATTRIBUTES
.PP
There is a range of attributes that can be selected from. These are:
.\" __________________ fsinfo_attr_statfs __________________
.TP
.B fsinfo_attr_statfs
This retrieves the "dynamic"
.B statfs
information, such as block and file counts, that are expected to change whilst
a filesystem is being used. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_statfs {
__u64 f_blocks; /* Total number of blocks in fs */
__u64 f_bfree; /* Total number of free blocks */
__u64 f_bavail; /* Number of free blocks available to ordinary user */
__u64 f_files; /* Total number of file nodes in fs */
__u64 f_ffree; /* Number of free file nodes */
__u64 f_favail; /* Number of free file nodes available to ordinary user */
__u32 f_bsize; /* Optimal block size */
__u32 f_frsize; /* Fragment size */
};
.fi
.in
.RE
.IP
The fields correspond to those of the same name returned by
.BR statfs ().
.\" __________________ fsinfo_attr_fsinfo __________________
.TP
.B fsinfo_attr_fsinfo
This retrieves information about the
.BR fsinfo ()
system call itself. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_fsinfo {
__u32 max_attr;
__u32 max_cap;
};
.fi
.in
.RE
.IP
The
.I max_attr
value indicates the number of attributes supported by the
.BR fsinfo ()
system call, and
.I max_cap
indicates the number of capability bits supported by the
.B fsinfo_attr_capabilities
attribute. The first corresponds to
.I fsinfo_attr__nr
and the second to
.I fsinfo_cap__nr
in the header file.
.\" __________________ fsinfo_attr_ids __________________
.TP
.B fsinfo_attr_ids
This retrieves a number of fixed IDs and other static information otherwise
available through
.BR statfs ().
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_ids {
char f_fs_name[15 + 1]; /* Filesystem name */
__u64 f_flags; /* Filesystem mount flags (MS_*) */
__u64 f_fsid; /* Short 64-bit Filesystem ID */
__u64 f_sb_id; /* Internal superblock ID */
__u32 f_fstype; /* Filesystem type from linux/magic.h */
__u32 f_dev_major; /* As st_dev_* from struct statx */
__u32 f_dev_minor;
};
.fi
.in
.RE
.IP
Most of these are filled in as for
.BR statfs (),
with the addition of the filesystem's symbolic name in
.I f_fs_name
and an identifier for use in notifications in
.IR f_sb_id .
.\" __________________ fsinfo_attr_limits __________________
.TP
.B fsinfo_attr_limits
This retrieves information about the limits of what a filesystem can support.
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_limits {
__u64 max_file_size;
__u64 max_uid;
__u64 max_gid;
__u64 max_projid;
__u32 max_dev_major;
__u32 max_dev_minor;
__u32 max_hard_links;
__u32 max_xattr_body_len;
__u16 max_xattr_name_len;
__u16 max_filename_len;
__u16 max_symlink_len;
__u16 __reserved[1];
};
.fi
.in
.RE
.IP
These indicate the maximum supported sizes for a variety of filesystem objects,
including the file size, the extended attribute name length and body length,
the filename length and the symlink body length.
.IP
It also indicates the maximum representable values for a User ID, a Group ID,
a Project ID, a device major number and a device minor number.
.IP
And finally, it indicates the maximum number of hard links that can be made to
a file.
.IP
Note that some of these values may be zero if the underlying object or concept
is not supported by the filesystem or the medium.
.\" __________________ fsinfo_attr_supports __________________
.TP
.B fsinfo_attr_supports
This retrieves information about what bits a filesystem supports in various
masks. The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_supports {
__u64 stx_attributes;
__u32 stx_mask;
__u32 ioc_flags;
__u32 win_file_attrs;
__u32 __reserved[1];
};
.fi
.in
.RE
.IP
The
.IR stx_attributes " and " stx_mask
fields indicate what bits in the struct statx fields of the matching names
are supported by the filesystem.
.IP
The
.I ioc_flags
field indicates what FS_*_FL flag bits as used through the FS_IOC_GET/SETFLAGS
ioctls are supported by the filesystem.
.IP
The
.I win_file_attrs
indicates what DOS/Windows file attributes a filesystem supports, if any.
.\" __________________ fsinfo_attr_capabilities __________________
.TP
.B fsinfo_attr_capabilities
This retrieves information about what features a filesystem supports as a
series of single bit indicators. The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_capabilities {
__u8 capabilities[(fsinfo_cap__nr + 7) / 8];
};
.fi
.in
.RE
.IP
where the bit of interest can be found by:
.PP
.RS
.in +4n
.nf
p->capabilities[bit / 8] & (1 << (bit % 8)))
.fi
.in
.RE
.IP
The bits are listed by
.I enum fsinfo_capability
and
.B fsinfo_cap__nr
is one more than the last capability bit listed in the header file.
.IP
Note that the number of capability bits actually supported by the kernel can be
found using the
.B fsinfo_attr_fsinfo
attribute.
.IP
The capability bits and their meanings are listed below in the "THE
CAPABILITIES" section.
.\" __________________ fsinfo_attr_timestamp_info __________________
.TP
.B fsinfo_attr_timestamp_info
This retrieves information about what timestamp resolution and scope is
supported by a filesystem for each of the file timestamps. The following
structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_timestamp_info {
__s64 minimum_timestamp;
__s64 maximum_timestamp;
__u16 atime_gran_mantissa;
__u16 btime_gran_mantissa;
__u16 ctime_gran_mantissa;
__u16 mtime_gran_mantissa;
__s8 atime_gran_exponent;
__s8 btime_gran_exponent;
__s8 ctime_gran_exponent;
__s8 mtime_gran_exponent;
__u32 __reserved[1];
};
.fi
.in
.RE
.IP
where
.IR minimum_timestamp " and " maximum_timestamp
are the limits on the timestamps that the filesystem supports and
.IR *time_gran_mantissa " and " *time_gran_exponent
indicate the granularity of each timestamp in terms of seconds, using the
formula:
.PP
.RS
.in +4n
.nf
mantissa * pow(10, exponent) Seconds
.fi
.in
.RE
.IP
where exponent may be negative and the result may be a fraction of a second.
.IP
Four timestamps are detailed: \fBA\fPccess time, \fBB\fPirth/creation time,
\fBC\fPhange time and \fBM\fPodification time. Capability bits are defined
that specify whether each of these exist in the filesystem or not.
.IP
Note that the timestamp description may be approximated or inaccurate if the
file is actually remote or is the union of multiple objects.
.\" __________________ fsinfo_attr_volume_id __________________
.TP
.B fsinfo_attr_volume_id
This retrieves the system's superblock volume identifier as a variable-length
string. This does not necessarily represent a value stored in the medium but
might be constructed on the fly.
.IP
For instance, for a block device this is the block device identifier
(eg. "sdb2"); for AFS this would be the numeric volume identifier.
.\" __________________ fsinfo_attr_volume_uuid __________________
.TP
.B fsinfo_attr_volume_uuid
This retrieves the volume UUID, if there is one, as a little-endian binary
UUID. This fills in the following structure:
.PP
.RS
.in +4n
.nf
struct fsinfo_volume_uuid {
__u8 uuid[16];
};
.fi
.in
.RE
.IP
.\" __________________ fsinfo_attr_volume_name __________________
.TP
.B fsinfo_attr_volume_name
This retrieves the filesystem's volume name as a variable-length string. This
is expected to represent a name stored in the medium.
.IP
For a block device, this might be a label stored in the superblock. For a
network filesystem, this might be a logical volume name of some sort.
.\" __________________ fsinfo_attr_cell/domain __________________
.PP
.B fsinfo_attr_cell_name
.br
.B fsinfo_attr_domain_name
.br
.IP
These two attributes are variable-length string attributes that may be used to
obtain information about network filesystems. An AFS volume, for instance,
belongs to a named cell. CIFS shares may belong to a domain.
.\" __________________ fsinfo_attr_realm_name __________________
.TP
.B fsinfo_attr_realm_name
This attribute is variable-length string that indicates the Kerberos realm that
a filesystem's authentication tokens should come from.
.\" __________________ fsinfo_attr_server_name __________________
.TP
.B fsinfo_attr_server_name
This attribute is a multiple-value attribute that lists the names of the
servers that are backing a network filesystem. Each value is a variable-length
string. The values are enumerated by calling
.BR fsinfo ()
multiple times, incrementing
.I params->Nth
each time until an ENODATA error occurs, thereby indicating the end of the
list.
.\" __________________ fsinfo_attr_server_address __________________
.TP
.B fsinfo_attr_server_address
This attribute is a multiple-instance, multiple-value attribute that lists the
addresses of the servers that are backing a network filesystem. Each value is
a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_server_address {
struct __kernel_sockaddr_storage address;
};
.fi
.in
.RE
.IP
Where the address may be AF_INET, AF_INET6, AF_RXRPC or any other type as
appropriate to the filesystem.
.IP
The values are enumerated by calling
.IR fsinfo ()
multiple times, incrementing
.I params->Nth
to step through the servers and
.I params->Mth
to step through the addresses of the Nth server each time until ENODATA errors
occur, thereby indicating either the end of a server's address list or the end
of the server list.
.IP
Barring the server list changing whilst being accessed, it is expected that the
.I params->Nth
will correspond to
.I params->Nth
for
.BR fsinfo_attr_server_name .
.\" __________________ fsinfo_attr_parameter __________________
.TP
.B fsinfo_attr_parameter
This attribute is a multiple-value attribute that lists the values of the mount
parameters for a filesystem as variable-length strings.
.IP
The parameters are enumerated by calling
.BR fsinfo ()
multiple times, incrementing
.I params->Nth
to step through them until error ENODATA is given.
.IP
Parameter strings are presented in a form akin to the way they're passed to the
context created by the
.BR fsopen ()
system call. For example, straight text parameters will be rendered as
something like:
.PP
.RS
.in +4n
.nf
"o data=journal"
"o noquota"
.fi
.in
.RE
.IP
Where the initial "word" indicates the option form.
.\" __________________ fsinfo_attr_source __________________
.TP
.B fsinfo_attr_source
This attribute is a multiple-value attribute that lists the mount sources for a
filesystem as variable-length strings. Normally only one source will be
available, but the possibility of having more than one is allowed for.
.IP
The sources are enumerated by calling
.BR fsinfo ()
multiple times, incrementing
.I params->Nth
to step through them until error ENODATA is given.
.IP
Source strings are presented in a form akin to the way they're passed to the
context created by the
.BR fsopen ()
system call. For example, they will be rendered as something like:
.PP
.RS
.in +4n
.nf
"s /dev/sda1"
"s example.com/pub/linux/"
.fi
.in
.RE
.IP
Where the initial "word" indicates the option form.
.\" __________________ fsinfo_attr_name_encoding __________________
.TP
.B fsinfo_attr_name_encoding
This attribute is variable-length string that indicates the filename encoding
used by the filesystem. The default is "utf8". Note that this may indicate a
non-8-bit encoding if that's what the underlying filesystem actually supports.
.\" __________________ fsinfo_attr_name_codepage __________________
.TP
.B fsinfo_attr_name_codepage
This attribute is variable-length string that indicates the codepage used to
translate filenames from the filesystem to the system if this is applicable to
the filesystem.
.\" __________________ fsinfo_attr_io_size __________________
.TP
.B fsinfo_attr_io_size
This retrieves information about the I/O sizes supported by the filesystem.
The following structure is filled in:
.PP
.RS
.in +4n
.nf
struct fsinfo_io_size {
__u32 block_size;
__u32 max_single_read_size;
__u32 max_single_write_size;
__u32 best_read_size;
__u32 best_write_size;
};
.fi
.in
.RE
.IP
Where
.I block_size
indicates the fundamental I/O block size of the filesystem as something
O_DIRECT read/write sizes must be a multiple of;
.IR max_single_write_size " and " max_single_write_size
indicate the maximum sizes for individual unbuffered data transfer operations;
and
.IR best_read_size " and " best_write_size
indicate the recommended I/O sizes.
.IP
Note that any of these may be zero if inapplicable or indeterminable.
.SH THE CAPABILITIES
.PP
There are number of capability bits in a bit array that can be retrieved using
.BR fsinfo_attr_capabilities .
These give information about features of the filesystem driver and the specific
filesystem.
.\" __________________ fsinfo_cap_is_*_fs __________________
.PP
.B fsinfo_cap_is_kernel_fs
.br
.B fsinfo_cap_is_block_fs
.br
.B fsinfo_cap_is_flash_fs
.br
.B fsinfo_cap_is_network_fs
.br
.B fsinfo_cap_is_automounter_fs
.IP
These indicate the primary type of the filesystem.
.B kernel
filesystems are special communication interfaces that substitute files for
system calls; examples include procfs and sysfs.
.B block
filesystems require a block device on which to operate; examples include ext4
and XFS.
.B flash
filesystems require an MTD device on which to operate; examples include JFFS2.
.B network
filesystems require access to the network and contact one or more servers;
examples include NFS and AFS.
.B automounter
filesystems are kernel special filesystems that host automount points and
triggers to dynamically create automount points. Examples include autofs and
AFS's dynamic root.
.\" __________________ fsinfo_cap_automounts __________________
.TP
.B fsinfo_cap_automounts
The filesystem may have automount points that can be triggered by pathwalk.
.\" __________________ fsinfo_cap_adv_locks __________________
.TP
.B fsinfo_cap_adv_locks
The filesystem supports advisory file locks. For a network filesystem, this
indicates that the advisory file locks are cross-client (and also between
server and its local filesystem on something like NFS).
.\" __________________ fsinfo_cap_mand_locks __________________
.TP
.B fsinfo_cap_mand_locks
The filesystem supports mandatory file locks. For a network filesystem, this
indicates that the mandatory file locks are cross-client (and also between
server and its local filesystem on something like NFS).
.\" __________________ fsinfo_cap_leases __________________
.TP
.B fsinfo_cap_leases
The filesystem supports leases. For a network filesystem, this means that the
server will tell the client to clean up its state on a file before passing the
lease to another client.
.\" __________________ fsinfo_cap_*ids __________________
.PP
.B fsinfo_cap_uids
.br
.B fsinfo_cap_gids
.br
.B fsinfo_cap_projids
.IP
These indicate that the filesystem supports numeric user IDs, group IDs and
project IDs respectively.
.\" __________________ fsinfo_cap_id_* __________________
.PP
.B fsinfo_cap_id_names
.br
.B fsinfo_cap_id_guids
.IP
These indicate that the filesystem employs textual names and/or GUIDs as
identifiers.
.\" __________________ fsinfo_cap_windows_attrs __________________
.TP
.B fsinfo_cap_windows_attrs
Indicates that the filesystem supports some Windows FILE_* attributes.
.\" __________________ fsinfo_cap_*_quotas __________________
.PP
.B fsinfo_cap_user_quotas
.br
.B fsinfo_cap_group_quotas
.br
.B fsinfo_cap_project_quotas
.IP
These indicate that the filesystem supports quotas for users, groups and
projects respectively.
.\" __________________ fsinfo_cap_xattrs/filetypes __________________
.PP
.B fsinfo_cap_xattrs
.br
.B fsinfo_cap_symlinks
.br
.B fsinfo_cap_hard_links
.br
.B fsinfo_cap_hard_links_1dir
.br
.B fsinfo_cap_device_files
.br
.B fsinfo_cap_unix_specials
.IP
These indicate that the filesystem supports respectively extended attributes;
symbolic links; hard links spanning direcories; hard links, but only within a
directory; block and character device files; and UNIX special files, such as
FIFO and socket.
.\" __________________ fsinfo_cap_*journal* __________________
.PP
.B fsinfo_cap_journal
.br
.B fsinfo_cap_data_is_journalled
.IP
The first of these indicates that the filesystem has a journal and the second
that the file data changes are being journalled.
.\" __________________ fsinfo_cap_o_* __________________
.PP
.B fsinfo_cap_o_sync
.br
.B fsinfo_cap_o_direct
.IP
These indicate that O_SYNC and O_DIRECT are supported respectively.
.\" __________________ fsinfo_cap_o_* __________________
.PP
.B fsinfo_cap_volume_id
.br
.B fsinfo_cap_volume_uuid
.br
.B fsinfo_cap_volume_name
.br
.B fsinfo_cap_volume_fsid
.br
.B fsinfo_cap_cell_name
.br
.B fsinfo_cap_domain_name
.br
.B fsinfo_cap_realm_name
.IP
These indicate if various attributes are supported by the filesystem, where
.B fsinfo_cap_X
here corresponds to
.BR fsinfo_attr_X .
.\" __________________ fsinfo_cap_iver_* __________________
.PP
.B fsinfo_cap_iver_all_change
.br
.B fsinfo_cap_iver_data_change
.br
.B fsinfo_cap_iver_mono_incr
.IP
These indicate if
.I i_version
on an inode in the filesystem is supported and
how it behaves.
.B all_change
indicates that i_version is incremented on metadata changes as well as data
changes.
.B data_change
indicates that i_version is only incremented on data changes, including
truncation.
.B mono_incr
indicates that i_version is incremented by exactly 1 for each change made.
.\" __________________ fsinfo_cap_resource_forks __________________
.TP
.B fsinfo_cap_resource_forks
This indicates that the filesystem supports some sort of resource fork or
alternate data stream on a file. This isn't the same as an extended attribute.
.\" __________________ fsinfo_cap_name_* __________________
.PP
.B fsinfo_cap_name_case_indep
.br
.B fsinfo_cap_name_non_utf8
.br
.B fsinfo_cap_name_has_codepage
.IP
These indicate certain facts about the filenames in a filesystem: whether
they're case-independent; if they're not UTF-8; and if there's a codepage
employed to map the names.
.\" __________________ fsinfo_cap_sparse __________________
.TP
.B fsinfo_cap_sparse
This indicates that the filesystem supports sparse files.
.\" __________________ fsinfo_cap_not_persistent __________________
.TP
.B fsinfo_cap_not_persistent
This indicates that the filesystem is not persistent, and that any data stored
here will not be saved in the event that the filesystem is unmounted, the
machine is rebooted or the machine loses power.
.\" __________________ fsinfo_cap_no_unix_mode __________________
.TP
.B fsinfo_cap_no_unix_mode
This indicates that the filesystem doesn't support the UNIX mode permissions
bits.
.\" __________________ fsinfo_cap_has_*time __________________
.PP
.B fsinfo_cap_has_atime
.br
.B fsinfo_cap_has_btime
.br
.B fsinfo_cap_has_ctime
.br
.B fsinfo_cap_has_mtime
.IP
These indicate as to what timestamps a filesystem supports, including: Access
time, Birth/creation time, Change time (metadata and data) and Modification
time (data only).
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.SH RETURN VALUE
On success, the size of the value that the kernel has available is returned,
irrespective of whether the buffer is large enough to hold that. The data
written to the buffer will be truncated if it is not. On error, \-1 is
returned, and
.I errno
is set appropriately.
.SH ERRORS
.TP
.B EACCES
Search permission is denied for one of the directories
in the path prefix of
.IR pathname .
(See also
.BR path_resolution (7).)
.TP
.B EBADF
.I dirfd
is not a valid open file descriptor.
.TP
.B EFAULT
.I pathname
is NULL or
.IR pathname ", " params " or " buffer
point to a location outside the process's accessible address space.
.TP
.B EINVAL
Reserved flag specified in
.IR params->at_flags " or one of " params->__reserved[]
is not 0.
.TP
.B EOPNOTSUPP
Unsupported attribute requested in
.IR params->request .
This may be beyond the limit of the supported attribute set or may just not be
one that's supported by the filesystem.
.TP
.B ENODATA
Unavailable attribute value requested by
.IR params->Nth " and/or " params->Mth .
.TP
.B ELOOP
Too many symbolic links encountered while traversing the pathname.
.TP
.B ENAMETOOLONG
.I pathname
is too long.
.TP
.B ENOENT
A component of
.I pathname
does not exist, or
.I pathname
is an empty string and
.B AT_EMPTY_PATH
was not specified in
.IR params->at_flags .
.TP
.B ENOMEM
Out of memory (i.e., kernel memory).
.TP
.B ENOTDIR
A component of the path prefix of
.I pathname
is not a directory or
.I pathname
is relative and
.I dirfd
is a file descriptor referring to a file other than a directory.
.SH VERSIONS
.BR fsinfo ()
was added to Linux in kernel 4.18.
.SH CONFORMING TO
.BR fsinfo ()
is Linux-specific.
.SH NOTES
Glibc does not (yet) provide a wrapper for the
.BR fsinfo ()
system call; call it using
.BR syscall (2).
.SH SEE ALSO
.BR ioctl_iflags (2),
.BR statx (2),
.BR statfs (2)
^ permalink raw reply
* Re: [PATCH 34/38] vfs: syscall: Add fsinfo() to query filesystem information [ver #10]
From: Darrick J. Wong @ 2018-07-31 23:49 UTC (permalink / raw)
To: David Howells; +Cc: viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <153271291017.9458.7827028432894772673.stgit@warthog.procyon.org.uk>
On Fri, Jul 27, 2018 at 06:35:10PM +0100, David Howells wrote:
> Add a system call to allow filesystem information to be queried. A request
> value can be given to indicate the desired attribute. Support is provided
> for enumerating multi-value attributes.
>
> ===============
> NEW SYSTEM CALL
> ===============
>
> The new system call looks like:
>
> int ret = fsinfo(int dfd,
> const char *filename,
> const struct fsinfo_params *params,
> void *buffer,
> size_t buf_size);
>
> The params parameter optionally points to a block of parameters:
>
> struct fsinfo_params {
> __u32 at_flags;
> __u32 request;
> __u32 Nth;
> __u32 Mth;
> __u32 __reserved[6];
> };
>
> If params is NULL, it is assumed params->request should be
> fsinfo_attr_statfs, params->Nth should be 0, params->Mth should be 0 and
> params->at_flags should be 0.
>
> If params is given, all of params->__reserved[] must be 0.
>
> dfd, filename and params->at_flags indicate the file to query. There is no
> equivalent of lstat() as that can be emulated with fsinfo() by setting
> AT_SYMLINK_NOFOLLOW in params->at_flags. There is also no equivalent of
> fstat() as that can be emulated by passing a NULL filename to fsinfo() with
> the fd of interest in dfd. AT_NO_AUTOMOUNT can also be used to an allow
> automount point to be queried without triggering it.
>
> params->request indicates the attribute/attributes to be queried. This can
> be one of:
>
> fsinfo_attr_statfs - statfs-style info
> fsinfo_attr_fsinfo - Information about fsinfo()
> fsinfo_attr_ids - Filesystem IDs
> fsinfo_attr_limits - Filesystem limits
> fsinfo_attr_supports - What's supported in statx(), IOC flags
> fsinfo_attr_capabilities - Filesystem capabilities
> fsinfo_attr_timestamp_info - Inode timestamp info
> fsinfo_attr_volume_id - Volume ID (string)
> fsinfo_attr_volume_uuid - Volume UUID
> fsinfo_attr_volume_name - Volume name (string)
> fsinfo_attr_cell_name - Cell name (string)
> fsinfo_attr_domain_name - Domain name (string)
> fsinfo_attr_realm_name - Realm name (string)
> fsinfo_attr_server_name - Name of the Nth server (string)
> fsinfo_attr_server_address - Mth address of the Nth server
> fsinfo_attr_parameter - Nth mount parameter (string)
> fsinfo_attr_source - Nth mount source name (string)
> fsinfo_attr_name_encoding - Filename encoding (string)
> fsinfo_attr_name_codepage - Filename codepage (string)
> fsinfo_attr_io_size - I/O size hints
>
> Some attributes (such as the servers backing a network filesystem) can have
> multiple values. These can be enumerated by setting params->Nth and
> params->Mth to 0, 1, ... until ENODATA is returned.
>
> buffer and buf_size point to the reply buffer. The buffer is filled up to
> the specified size, even if this means truncating the reply. The full size
> of the reply is returned. In future versions, this will allow extra fields
> to be tacked on to the end of the reply, but anyone not expecting them will
> only get the subset they're expecting. If either buffer of buf_size are 0,
> no copy will take place and the data size will be returned.
>
> At the moment, this will only work on x86_64 and i386 as it requires the
> system call to be wired up.
<snip> I only have time today to review the user interface bits...
> diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
> new file mode 100644
> index 000000000000..abcf414dd3be
> --- /dev/null
> +++ b/include/uapi/linux/fsinfo.h
> @@ -0,0 +1,234 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/* fsinfo() definitions.
> + *
> + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +#ifndef _UAPI_LINUX_FSINFO_H
> +#define _UAPI_LINUX_FSINFO_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/*
> + * The filesystem attributes that can be requested. Note that some attributes
> + * may have multiple instances which can be switched in the parameter block.
> + */
> +enum fsinfo_attribute {
> + fsinfo_attr_statfs = 0, /* statfs()-style state */
> + fsinfo_attr_fsinfo = 1, /* Information about fsinfo() */
> + fsinfo_attr_ids = 2, /* Filesystem IDs */
> + fsinfo_attr_limits = 3, /* Filesystem limits */
> + fsinfo_attr_supports = 4, /* What's supported in statx, iocflags, ... */
> + fsinfo_attr_capabilities = 5, /* Filesystem capabilities (bits) */
> + fsinfo_attr_timestamp_info = 6, /* Inode timestamp info */
> + fsinfo_attr_volume_id = 7, /* Volume ID (string) */
> + fsinfo_attr_volume_uuid = 8, /* Volume UUID (LE uuid) */
> + fsinfo_attr_volume_name = 9, /* Volume name (string) */
What's the difference between a volume name and a volume string?
XFS has a uuid and a label that can be set by userspace (sort of);
should we return the label for volume_id and volume_name?
Hmmm, I see that the default implementations set volume_id from s_id,
and s_id (for block device filesystems anyway) tends to be the device, I
guess?
So if blkid told me that:
/dev/sda1: LABEL="music" UUID="8d9e5b1e-a094-49e5-a179-6d94f7fd8399" TYPE="xfs"
volume_id == sda1, volume_uuid == 8d9e5b1e-a094-49e5-a179-6d94f7fd8399,
and volume_name == "music" ?
> + fsinfo_attr_cell_name = 10, /* Cell name (string) */
> + fsinfo_attr_domain_name = 11, /* Domain name (string) */
> + fsinfo_attr_realm_name = 12, /* Realm name (string) */
> + fsinfo_attr_server_name = 13, /* Name of the Nth server */
> + fsinfo_attr_server_address = 14, /* Mth address of the Nth server */
> + fsinfo_attr_parameter = 15, /* Nth mount parameter (string) */
> + fsinfo_attr_source = 16, /* Nth mount source name (string) */
Hmm, so I guess external log devices and realtime device(s) go here?
> + fsinfo_attr_name_encoding = 17, /* Filename encoding (string) */
> + fsinfo_attr_name_codepage = 18, /* Filename codepage (string) */
> + fsinfo_attr_io_size = 19, /* Optimal I/O sizes */
Are we tied to this enum forever, or do you plan to split up the number
space to allow filesystems to define their own attributes without having
to add them here?
For example, say you let the upper 8 bits be some sort of per-fs code
(like how _IO{,R,W} work) and the lower 24 bits can be the subcommand.
0x00 would be the generic space; XFS could (say) reserve 0x58000000 -
0x58ffffff for XFS (0x58 is the prefix code used for xfs ioctls). If
there ever are subdivisions of the number space it might be nice to have
fsinfo_fsinfo return prefix number of the fs-specific subcommands, and
how many fs-specific subcommands there are.
I mean, I guess each fs' ->fsinfo function can do that privately but I
suggest having some mechanism in mind to handle these things. XFS's
geometry ioctl structure is nearly out of space and (some day soon) we
will have to expand and maybe we can use fsinfo instead.
> + fsinfo_attr__nr
> +};
> +
> +/*
> + * Optional fsinfo() parameter structure.
> + *
> + * If this is not given, it is assumed that fsinfo_attr_statfs instance 0,0 is
> + * desired.
> + */
> +struct fsinfo_params {
> + __u32 at_flags; /* AT_SYMLINK_NOFOLLOW and similar flags */
> + __u32 request; /* What is being asking for (enum fsinfo_attribute) */
> + __u32 Nth; /* Instance of it (some may have multiple) */
> + __u32 Mth; /* Subinstance of Nth instance */
> + __u32 __reserved[6]; /* Reserved params; all must be 0 */
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_statfs).
> + * - This gives extended filesystem information.
> + */
> +struct fsinfo_statfs {
> + __u64 f_blocks; /* Total number of blocks in fs */
> + __u64 f_bfree; /* Total number of free blocks */
> + __u64 f_bavail; /* Number of free blocks available to ordinary user */
> + __u64 f_files; /* Total number of file nodes in fs */
> + __u64 f_ffree; /* Number of free file nodes */
> + __u64 f_favail; /* Number of free file nodes available to ordinary user */
> + __u32 f_bsize; /* Optimal block size */
> + __u32 f_frsize; /* Fragment size */
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_ids).
> + *
> + * List of basic identifiers as is normally found in statfs().
> + */
> +struct fsinfo_ids {
> + char f_fs_name[15 + 1];
> + __u64 f_flags; /* Filesystem mount flags (MS_*) */
> + __u64 f_fsid; /* Short 64-bit Filesystem ID (as statfs) */
> + __u64 f_sb_id; /* Internal superblock ID for sbnotify()/mntnotify() */
> + __u32 f_fstype; /* Filesystem type from linux/magic.h [uncond] */
> + __u32 f_dev_major; /* As st_dev_* from struct statx [uncond] */
> + __u32 f_dev_minor;
> +};
This structure doesn't end on a 64-bit boundary and may cause padding
problems...
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_limits).
> + *
> + * List of supported filesystem limits.
> + */
> +struct fsinfo_limits {
> + __u64 max_file_size; /* Maximum file size */
> + __u64 max_uid; /* Maximum UID supported */
> + __u64 max_gid; /* Maximum GID supported */
> + __u64 max_projid; /* Maximum project ID supported */
> + __u32 max_dev_major; /* Maximum device major representable */
> + __u32 max_dev_minor; /* Maximum device minor representable */
> + __u32 max_hard_links; /* Maximum number of hard links on a file */
> + __u32 max_xattr_body_len; /* Maximum xattr content length */
> + __u32 max_xattr_name_len; /* Maximum xattr name length */
> + __u32 max_filename_len; /* Maximum filename length */
> + __u32 max_symlink_len; /* Maximum symlink content length */
> + __u32 __reserved[1];
Maximum inode number possible, for filesystems that can allocate inodes
dynamically?
Granted, XFS will probably only ever advertise "0xffffffffffffffff"...
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_supports).
> + *
> + * What's supported in various masks, such as statx() attribute and mask bits
> + * and IOC flags.
> + */
> +struct fsinfo_supports {
> + __u64 stx_attributes; /* What statx::stx_attributes are supported */
> + __u32 stx_mask; /* What statx::stx_mask bits are supported */
> + __u32 ioc_flags; /* What FS_IOC_* flags are supported */
> + __u32 win_file_attrs; /* What DOS/Windows FILE_* attributes are supported */
> + __u32 __reserved[1];
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_capabilities).
> + *
> + * Bitmask indicating filesystem capabilities where renderable as single bits.
> + */
> +enum fsinfo_capability {
> + fsinfo_cap_is_kernel_fs = 0, /* fs is kernel-special filesystem */
> + fsinfo_cap_is_block_fs = 1, /* fs is block-based filesystem */
> + fsinfo_cap_is_flash_fs = 2, /* fs is flash filesystem */
> + fsinfo_cap_is_network_fs = 3, /* fs is network filesystem */
> + fsinfo_cap_is_automounter_fs = 4, /* fs is automounter special filesystem */
> + fsinfo_cap_automounts = 5, /* fs supports automounts */
> + fsinfo_cap_adv_locks = 6, /* fs supports advisory file locking */
> + fsinfo_cap_mand_locks = 7, /* fs supports mandatory file locking */
> + fsinfo_cap_leases = 8, /* fs supports file leases */
> + fsinfo_cap_uids = 9, /* fs supports numeric uids */
> + fsinfo_cap_gids = 10, /* fs supports numeric gids */
> + fsinfo_cap_projids = 11, /* fs supports numeric project ids */
> + fsinfo_cap_id_names = 12, /* fs supports user names */
> + fsinfo_cap_id_guids = 13, /* fs supports user guids */
> + fsinfo_cap_windows_attrs = 14, /* fs has windows attributes */
> + fsinfo_cap_user_quotas = 15, /* fs has per-user quotas */
> + fsinfo_cap_group_quotas = 16, /* fs has per-group quotas */
> + fsinfo_cap_project_quotas = 17, /* fs has per-project quotas */
> + fsinfo_cap_xattrs = 18, /* fs has xattrs */
> + fsinfo_cap_journal = 19, /* fs has a journal */
> + fsinfo_cap_data_is_journalled = 20, /* fs is using data journalling */
> + fsinfo_cap_o_sync = 21, /* fs supports O_SYNC */
> + fsinfo_cap_o_direct = 22, /* fs supports O_DIRECT */
> + fsinfo_cap_volume_id = 23, /* fs has a volume ID */
> + fsinfo_cap_volume_uuid = 24, /* fs has a volume UUID */
> + fsinfo_cap_volume_name = 25, /* fs has a volume name */
> + fsinfo_cap_volume_fsid = 26, /* fs has a volume FSID */
> + fsinfo_cap_cell_name = 27, /* fs has a cell name */
> + fsinfo_cap_domain_name = 28, /* fs has a domain name */
> + fsinfo_cap_realm_name = 29, /* fs has a realm name */
> + fsinfo_cap_iver_all_change = 30, /* i_version represents data + meta changes */
> + fsinfo_cap_iver_data_change = 31, /* i_version represents data changes only */
> + fsinfo_cap_iver_mono_incr = 32, /* i_version incremented monotonically */
> + fsinfo_cap_symlinks = 33, /* fs supports symlinks */
> + fsinfo_cap_hard_links = 34, /* fs supports hard links */
> + fsinfo_cap_hard_links_1dir = 35, /* fs supports hard links in same dir only */
> + fsinfo_cap_device_files = 36, /* fs supports bdev, cdev */
> + fsinfo_cap_unix_specials = 37, /* fs supports pipe, fifo, socket */
> + fsinfo_cap_resource_forks = 38, /* fs supports resource forks/streams */
> + fsinfo_cap_name_case_indep = 39, /* Filename case independence is mandatory */
> + fsinfo_cap_name_non_utf8 = 40, /* fs has non-utf8 names */
> + fsinfo_cap_name_has_codepage = 41, /* fs has a filename codepage */
> + fsinfo_cap_sparse = 42, /* fs supports sparse files */
> + fsinfo_cap_not_persistent = 43, /* fs is not persistent */
> + fsinfo_cap_no_unix_mode = 44, /* fs does not support unix mode bits */
> + fsinfo_cap_has_atime = 45, /* fs supports access time */
> + fsinfo_cap_has_btime = 46, /* fs supports birth/creation time */
> + fsinfo_cap_has_ctime = 47, /* fs supports change time */
> + fsinfo_cap_has_mtime = 48, /* fs supports modification time */
> + fsinfo_cap__nr
> +};
> +
> +struct fsinfo_capabilities {
> + __u8 capabilities[(fsinfo_cap__nr + 7) / 8];
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_timestamp_info).
> + */
> +struct fsinfo_timestamp_info {
> + __s64 minimum_timestamp; /* Minimum timestamp value in seconds */
> + __s64 maximum_timestamp; /* Maximum timestamp value in seconds */
> + __u16 atime_gran_mantissa; /* Granularity(secs) = mant * 10^exp */
> + __u16 btime_gran_mantissa;
> + __u16 ctime_gran_mantissa;
> + __u16 mtime_gran_mantissa;
> + __s8 atime_gran_exponent;
> + __s8 btime_gran_exponent;
> + __s8 ctime_gran_exponent;
> + __s8 mtime_gran_exponent;
> + __u32 __reserved[1];
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_volume_uuid).
> + */
> +struct fsinfo_volume_uuid {
> + __u8 uuid[16];
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_server_addresses).
> + *
> + * Find the Mth address of the Nth server for a network mount.
> + */
> +struct fsinfo_server_address {
> + struct __kernel_sockaddr_storage address;
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_io_size).
> + *
> + * Retrieve I/O size hints for a filesystem.
> + */
> +struct fsinfo_io_size {
> + __u32 dio_size_gran; /* Size granularity for O_DIRECT */
> + __u32 dio_mem_align; /* Memory alignment for O_DIRECT */
max io size too?
64-bit too, in case we ever get that insane?
--D
> +};
> +
> +/*
> + * Information struct for fsinfo(fsinfo_attr_fsinfo).
> + *
> + * This gives information about fsinfo() itself.
> + */
> +struct fsinfo_fsinfo {
> + __u32 max_attr; /* Number of supported attributes (fsinfo_attr__nr) */
> + __u32 max_cap; /* Number of supported capabilities (fsinfo_cap__nr) */
> +};
> +
> +#endif /* _UAPI_LINUX_FSINFO_H */
> diff --git a/samples/statx/Makefile b/samples/statx/Makefile
> index 59df7c25a9d1..9cb9a88e3a10 100644
> --- a/samples/statx/Makefile
> +++ b/samples/statx/Makefile
> @@ -1,7 +1,10 @@
> # List of programs to build
> -hostprogs-$(CONFIG_SAMPLE_STATX) := test-statx
> +hostprogs-$(CONFIG_SAMPLE_STATX) := test-statx test-fsinfo
>
> # Tell kbuild to always build the programs
> always := $(hostprogs-y)
>
> HOSTCFLAGS_test-statx.o += -I$(objtree)/usr/include
> +
> +HOSTCFLAGS_test-fsinfo.o += -I$(objtree)/usr/include
> +HOSTLOADLIBES_test-fsinfo += -lm
> diff --git a/samples/statx/test-fsinfo.c b/samples/statx/test-fsinfo.c
> new file mode 100644
> index 000000000000..deab0081ecd1
> --- /dev/null
> +++ b/samples/statx/test-fsinfo.c
> @@ -0,0 +1,539 @@
> +/* Test the fsinfo() system call
> + *
> + * Copyright (C) 2018 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.
> + */
> +
> +#define _GNU_SOURCE
> +#define _ATFILE_SOURCE
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <ctype.h>
> +#include <errno.h>
> +#include <time.h>
> +#include <math.h>
> +#include <fcntl.h>
> +#include <sys/syscall.h>
> +#include <linux/fsinfo.h>
> +#include <linux/socket.h>
> +#include <sys/stat.h>
> +
> +static __attribute__((unused))
> +ssize_t fsinfo(int dfd, const char *filename, struct fsinfo_params *params,
> + void *buffer, size_t buf_size)
> +{
> + return syscall(__NR_fsinfo, dfd, filename, params, buffer, buf_size);
> +}
> +
> +#define FSINFO_STRING(N) [fsinfo_attr_##N] = 0x00
> +#define FSINFO_STRUCT(N) [fsinfo_attr_##N] = sizeof(struct fsinfo_##N)/sizeof(__u32)
> +#define FSINFO_STRING_N(N) [fsinfo_attr_##N] = 0x40
> +#define FSINFO_STRUCT_N(N) [fsinfo_attr_##N] = 0x40 | sizeof(struct fsinfo_##N)/sizeof(__u32)
> +#define FSINFO_STRUCT_NM(N) [fsinfo_attr_##N] = 0x80 | sizeof(struct fsinfo_##N)/sizeof(__u32)
> +static const __u8 fsinfo_buffer_sizes[fsinfo_attr__nr] = {
> + FSINFO_STRUCT (statfs),
> + FSINFO_STRUCT (fsinfo),
> + FSINFO_STRUCT (ids),
> + FSINFO_STRUCT (limits),
> + FSINFO_STRUCT (supports),
> + FSINFO_STRUCT (capabilities),
> + FSINFO_STRUCT (timestamp_info),
> + FSINFO_STRING (volume_id),
> + FSINFO_STRUCT (volume_uuid),
> + FSINFO_STRING (volume_name),
> + FSINFO_STRING (cell_name),
> + FSINFO_STRING (domain_name),
> + FSINFO_STRING (realm_name),
> + FSINFO_STRING_N (server_name),
> + FSINFO_STRUCT_NM (server_address),
> + FSINFO_STRING_N (parameter),
> + FSINFO_STRING_N (source),
> + FSINFO_STRING (name_encoding),
> + FSINFO_STRING (name_codepage),
> + FSINFO_STRUCT (io_size),
> +};
> +
> +#define FSINFO_NAME(N) [fsinfo_attr_##N] = #N
> +static const char *fsinfo_attr_names[fsinfo_attr__nr] = {
> + FSINFO_NAME(statfs),
> + FSINFO_NAME(fsinfo),
> + FSINFO_NAME(ids),
> + FSINFO_NAME(limits),
> + FSINFO_NAME(supports),
> + FSINFO_NAME(capabilities),
> + FSINFO_NAME(timestamp_info),
> + FSINFO_NAME(volume_id),
> + FSINFO_NAME(volume_uuid),
> + FSINFO_NAME(volume_name),
> + FSINFO_NAME(cell_name),
> + FSINFO_NAME(domain_name),
> + FSINFO_NAME(realm_name),
> + FSINFO_NAME(server_name),
> + FSINFO_NAME(server_address),
> + FSINFO_NAME(parameter),
> + FSINFO_NAME(source),
> + FSINFO_NAME(name_encoding),
> + FSINFO_NAME(name_codepage),
> + FSINFO_NAME(io_size),
> +};
> +
> +union reply {
> + char buffer[4096];
> + struct fsinfo_statfs statfs;
> + struct fsinfo_fsinfo fsinfo;
> + struct fsinfo_ids ids;
> + struct fsinfo_limits limits;
> + struct fsinfo_supports supports;
> + struct fsinfo_capabilities caps;
> + struct fsinfo_timestamp_info timestamps;
> + struct fsinfo_volume_uuid uuid;
> + struct fsinfo_server_address srv_addr;
> + struct fsinfo_io_size io_size;
> +};
> +
> +static void dump_hex(unsigned int *data, int from, int to)
> +{
> + unsigned offset, print_offset = 1, col = 0;
> +
> + from /= 4;
> + to = (to + 3) / 4;
> +
> + for (offset = from; offset < to; offset++) {
> + if (print_offset) {
> + printf("%04x: ", offset * 8);
> + print_offset = 0;
> + }
> + printf("%08x", data[offset]);
> + col++;
> + if ((col & 3) == 0) {
> + printf("\n");
> + print_offset = 1;
> + } else {
> + printf(" ");
> + }
> + }
> +
> + if (!print_offset)
> + printf("\n");
> +}
> +
> +static void dump_attr_statfs(union reply *r, int size)
> +{
> + struct fsinfo_statfs *f = &r->statfs;
> +
> + printf("\n");
> + printf("\tblocks: n=%llu fr=%llu av=%llu\n",
> + (unsigned long long)f->f_blocks,
> + (unsigned long long)f->f_bfree,
> + (unsigned long long)f->f_bavail);
> +
> + printf("\tfiles : n=%llu fr=%llu av=%llu\n",
> + (unsigned long long)f->f_files,
> + (unsigned long long)f->f_ffree,
> + (unsigned long long)f->f_favail);
> + printf("\tbsize : %u\n", f->f_bsize);
> + printf("\tfrsize: %u\n", f->f_frsize);
> +}
> +
> +static void dump_attr_fsinfo(union reply *r, int size)
> +{
> + struct fsinfo_fsinfo *f = &r->fsinfo;
> +
> + printf("max_attr=%u max_cap=%u\n", f->max_attr, f->max_cap);
> +}
> +
> +static void dump_attr_ids(union reply *r, int size)
> +{
> + struct fsinfo_ids *f = &r->ids;
> +
> + printf("\n");
> + printf("\tdev : %02x:%02x\n", f->f_dev_major, f->f_dev_minor);
> + printf("\tfs : type=%x name=%s\n", f->f_fstype, f->f_fs_name);
> + printf("\tflags : %llx\n", (unsigned long long)f->f_flags);
> + printf("\tfsid : %llx\n", (unsigned long long)f->f_fsid);
> +}
> +
> +static void dump_attr_limits(union reply *r, int size)
> +{
> + struct fsinfo_limits *f = &r->limits;
> +
> + printf("\n");
> + printf("\tmax file size: %llx\n", f->max_file_size);
> + printf("\tmax ids : u=%llx g=%llx p=%llx\n",
> + f->max_uid, f->max_gid, f->max_projid);
> + printf("\tmax dev : maj=%x min=%x\n",
> + f->max_dev_major, f->max_dev_minor);
> + printf("\tmax links : %x\n", f->max_hard_links);
> + printf("\tmax xattr : n=%x b=%x\n",
> + f->max_xattr_name_len, f->max_xattr_body_len);
> + printf("\tmax len : file=%x sym=%x\n",
> + f->max_filename_len, f->max_symlink_len);
> +}
> +
> +static void dump_attr_supports(union reply *r, int size)
> +{
> + struct fsinfo_supports *f = &r->supports;
> +
> + printf("\n");
> + printf("\tstx_attr=%llx\n", f->stx_attributes);
> + printf("\tstx_mask=%x\n", f->stx_mask);
> + printf("\tioc_flags=%x\n", f->ioc_flags);
> + printf("\twin_fattrs=%x\n", f->win_file_attrs);
> +}
> +
> +#define FSINFO_CAP_NAME(C) [fsinfo_cap_##C] = #C
> +static const char *fsinfo_cap_names[fsinfo_cap__nr] = {
> + FSINFO_CAP_NAME(is_kernel_fs),
> + FSINFO_CAP_NAME(is_block_fs),
> + FSINFO_CAP_NAME(is_flash_fs),
> + FSINFO_CAP_NAME(is_network_fs),
> + FSINFO_CAP_NAME(is_automounter_fs),
> + FSINFO_CAP_NAME(automounts),
> + FSINFO_CAP_NAME(adv_locks),
> + FSINFO_CAP_NAME(mand_locks),
> + FSINFO_CAP_NAME(leases),
> + FSINFO_CAP_NAME(uids),
> + FSINFO_CAP_NAME(gids),
> + FSINFO_CAP_NAME(projids),
> + FSINFO_CAP_NAME(id_names),
> + FSINFO_CAP_NAME(id_guids),
> + FSINFO_CAP_NAME(windows_attrs),
> + FSINFO_CAP_NAME(user_quotas),
> + FSINFO_CAP_NAME(group_quotas),
> + FSINFO_CAP_NAME(project_quotas),
> + FSINFO_CAP_NAME(xattrs),
> + FSINFO_CAP_NAME(journal),
> + FSINFO_CAP_NAME(data_is_journalled),
> + FSINFO_CAP_NAME(o_sync),
> + FSINFO_CAP_NAME(o_direct),
> + FSINFO_CAP_NAME(volume_id),
> + FSINFO_CAP_NAME(volume_uuid),
> + FSINFO_CAP_NAME(volume_name),
> + FSINFO_CAP_NAME(volume_fsid),
> + FSINFO_CAP_NAME(cell_name),
> + FSINFO_CAP_NAME(domain_name),
> + FSINFO_CAP_NAME(realm_name),
> + FSINFO_CAP_NAME(iver_all_change),
> + FSINFO_CAP_NAME(iver_data_change),
> + FSINFO_CAP_NAME(iver_mono_incr),
> + FSINFO_CAP_NAME(symlinks),
> + FSINFO_CAP_NAME(hard_links),
> + FSINFO_CAP_NAME(hard_links_1dir),
> + FSINFO_CAP_NAME(device_files),
> + FSINFO_CAP_NAME(unix_specials),
> + FSINFO_CAP_NAME(resource_forks),
> + FSINFO_CAP_NAME(name_case_indep),
> + FSINFO_CAP_NAME(name_non_utf8),
> + FSINFO_CAP_NAME(name_has_codepage),
> + FSINFO_CAP_NAME(sparse),
> + FSINFO_CAP_NAME(not_persistent),
> + FSINFO_CAP_NAME(no_unix_mode),
> + FSINFO_CAP_NAME(has_atime),
> + FSINFO_CAP_NAME(has_btime),
> + FSINFO_CAP_NAME(has_ctime),
> + FSINFO_CAP_NAME(has_mtime),
> +};
> +
> +static void dump_attr_capabilities(union reply *r, int size)
> +{
> + struct fsinfo_capabilities *f = &r->caps;
> + int i;
> +
> + for (i = 0; i < sizeof(f->capabilities); i++)
> + printf("%02x", f->capabilities[i]);
> + printf("\n");
> + for (i = 0; i < fsinfo_cap__nr; i++)
> + if (f->capabilities[i / 8] & (1 << (i % 8)))
> + printf("\t- %s\n", fsinfo_cap_names[i]);
> +}
> +
> +static void dump_attr_timestamp_info(union reply *r, int size)
> +{
> + struct fsinfo_timestamp_info *f = &r->timestamps;
> +
> + printf("range=%llx-%llx\n",
> + (unsigned long long)f->minimum_timestamp,
> + (unsigned long long)f->maximum_timestamp);
> +
> +#define print_time(G) \
> + printf("\t"#G"time : gran=%gs\n", \
> + (f->G##time_gran_mantissa * \
> + pow(10., f->G##time_gran_exponent)))
> + print_time(a);
> + print_time(b);
> + print_time(c);
> + print_time(m);
> +}
> +
> +static void dump_attr_volume_uuid(union reply *r, int size)
> +{
> + struct fsinfo_volume_uuid *f = &r->uuid;
> +
> + printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x"
> + "-%02x%02x%02x%02x%02x%02x\n",
> + f->uuid[ 0], f->uuid[ 1],
> + f->uuid[ 2], f->uuid[ 3],
> + f->uuid[ 4], f->uuid[ 5],
> + f->uuid[ 6], f->uuid[ 7],
> + f->uuid[ 8], f->uuid[ 9],
> + f->uuid[10], f->uuid[11],
> + f->uuid[12], f->uuid[13],
> + f->uuid[14], f->uuid[15]);
> +}
> +
> +static void dump_attr_server_address(union reply *r, int size)
> +{
> + struct fsinfo_server_address *f = &r->srv_addr;
> +
> + printf("family=%u\n", f->address.ss_family);
> +}
> +
> +static void dump_attr_io_size(union reply *r, int size)
> +{
> + struct fsinfo_io_size *f = &r->io_size;
> +
> + printf("dio_size=%u\n", f->dio_size_gran);
> +}
> +
> +/*
> + *
> + */
> +typedef void (*dumper_t)(union reply *r, int size);
> +
> +#define FSINFO_DUMPER(N) [fsinfo_attr_##N] = dump_attr_##N
> +static const dumper_t fsinfo_attr_dumper[fsinfo_attr__nr] = {
> + FSINFO_DUMPER(statfs),
> + FSINFO_DUMPER(fsinfo),
> + FSINFO_DUMPER(ids),
> + FSINFO_DUMPER(limits),
> + FSINFO_DUMPER(supports),
> + FSINFO_DUMPER(capabilities),
> + FSINFO_DUMPER(timestamp_info),
> + FSINFO_DUMPER(volume_uuid),
> + FSINFO_DUMPER(server_address),
> + FSINFO_DUMPER(io_size),
> +};
> +
> +static void dump_fsinfo(enum fsinfo_attribute attr, __u8 about,
> + union reply *r, int size)
> +{
> + dumper_t dumper = fsinfo_attr_dumper[attr];
> + unsigned int len;
> +
> + if (!dumper) {
> + printf("<no dumper>\n");
> + return;
> + }
> +
> + len = (about & 0x3f) * sizeof(__u32);
> + if (size < len) {
> + printf("<short data %u/%u>\n", size, len);
> + return;
> + }
> +
> + dumper(r, size);
> +}
> +
> +/*
> + * Try one subinstance of an attribute.
> + */
> +static int try_one(const char *file, struct fsinfo_params *params, bool raw)
> +{
> + union reply r;
> + char *p;
> + int ret;
> + __u8 about;
> +
> + memset(&r.buffer, 0xbd, sizeof(r.buffer));
> +
> + errno = 0;
> + ret = fsinfo(AT_FDCWD, file, params, r.buffer, sizeof(r.buffer));
> + if (params->request >= fsinfo_attr__nr) {
> + if (ret == -1 && errno == EOPNOTSUPP)
> + exit(0);
> + fprintf(stderr, "Unexpected error for too-large command %u: %m\n",
> + params->request);
> + exit(1);
> + }
> +
> + //printf("fsinfo(%s,%s,%u,%u) = %d: %m\n",
> + // file, fsinfo_attr_names[params->request],
> + // params->Nth, params->Mth, ret);
> +
> + about = fsinfo_buffer_sizes[params->request];
> + if (ret == -1) {
> + if (errno == ENODATA) {
> + switch (about & 0xc0) {
> + case 0x00:
> + if (params->Nth == 0 && params->Mth == 0) {
> + fprintf(stderr,
> + "Unexpected ENODATA1 (%u[%u][%u])\n",
> + params->request, params->Nth, params->Mth);
> + exit(1);
> + }
> + break;
> + case 0x40:
> + if (params->Nth == 0 && params->Mth == 0) {
> + fprintf(stderr,
> + "Unexpected ENODATA2 (%u[%u][%u])\n",
> + params->request, params->Nth, params->Mth);
> + exit(1);
> + }
> + break;
> + }
> + return (params->Mth == 0) ? 2 : 1;
> + }
> + if (errno == EOPNOTSUPP) {
> + if (params->Nth > 0 || params->Mth > 0) {
> + fprintf(stderr,
> + "Should return -ENODATA (%u[%u][%u])\n",
> + params->request, params->Nth, params->Mth);
> + exit(1);
> + }
> + //printf("\e[33m%s\e[m: <not supported>\n",
> + // fsinfo_attr_names[attr]);
> + return 2;
> + }
> + perror(file);
> + exit(1);
> + }
> +
> + if (raw) {
> + if (ret > 4096)
> + ret = 4096;
> + dump_hex((unsigned int *)&r.buffer, 0, ret);
> + return 0;
> + }
> +
> + switch (about & 0xc0) {
> + case 0x00:
> + printf("\e[33m%s\e[m: ",
> + fsinfo_attr_names[params->request]);
> + break;
> + case 0x40:
> + printf("\e[33m%s[%u]\e[m: ",
> + fsinfo_attr_names[params->request],
> + params->Nth);
> + break;
> + case 0x80:
> + printf("\e[33m%s[%u][%u]\e[m: ",
> + fsinfo_attr_names[params->request],
> + params->Nth, params->Mth);
> + break;
> + }
> +
> + switch (about) {
> + /* Struct */
> + case 0x01 ... 0x3f:
> + case 0x41 ... 0x7f:
> + case 0x81 ... 0xbf:
> + dump_fsinfo(params->request, about, &r, ret);
> + return 0;
> +
> + /* String */
> + case 0x00:
> + case 0x40:
> + case 0x80:
> + if (ret >= 4096) {
> + ret = 4096;
> + r.buffer[4092] = '.';
> + r.buffer[4093] = '.';
> + r.buffer[4094] = '.';
> + r.buffer[4095] = 0;
> + } else {
> + r.buffer[ret] = 0;
> + }
> + for (p = r.buffer; *p; p++) {
> + if (!isprint(*p)) {
> + printf("<non-printable>\n");
> + continue;
> + }
> + }
> + printf("%s\n", r.buffer);
> + return 0;
> +
> + default:
> + fprintf(stderr, "Fishy about %u %02x\n", params->request, about);
> + exit(1);
> + }
> +}
> +
> +/*
> + *
> + */
> +int main(int argc, char **argv)
> +{
> + struct fsinfo_params params = {
> + .at_flags = AT_SYMLINK_NOFOLLOW,
> + };
> + unsigned int attr;
> + int raw = 0, opt, Nth, Mth;
> +
> + while ((opt = getopt(argc, argv, "alr"))) {
> + switch (opt) {
> + case 'a':
> + params.at_flags |= AT_NO_AUTOMOUNT;
> + continue;
> + case 'l':
> + params.at_flags &= ~AT_SYMLINK_NOFOLLOW;
> + continue;
> + case 'r':
> + raw = 1;
> + continue;
> + }
> + break;
> + }
> +
> + argc -= optind;
> + argv += optind;
> +
> + if (argc != 1) {
> + printf("Format: test-fsinfo [-alr] <file>\n");
> + exit(2);
> + }
> +
> + for (attr = 0; attr <= fsinfo_attr__nr; attr++) {
> + Nth = 0;
> + do {
> + Mth = 0;
> + do {
> + params.request = attr;
> + params.Nth = Nth;
> + params.Mth = Mth;
> +
> + switch (try_one(argv[0], ¶ms, raw)) {
> + case 0:
> + continue;
> + case 1:
> + goto done_M;
> + case 2:
> + goto done_N;
> + }
> + } while (++Mth < 100);
> +
> + done_M:
> + if (Mth >= 100) {
> + fprintf(stderr, "Fishy: Mth == %u\n", Mth);
> + break;
> + }
> +
> + } while (++Nth < 100);
> +
> + done_N:
> + if (Nth >= 100) {
> + fprintf(stderr, "Fishy: Nth == %u\n", Nth);
> + break;
> + }
> + }
> +
> + return 0;
> +}
>
^ permalink raw reply
* [PATCH ghak90 (was ghak32) V4 10/10] debug audit: read container ID of a process
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Add support for reading the audit container identifier from the proc
filesystem.
This is a read from the proc entry of the form
/proc/PID/audit_containerid where PID is the process ID of the task
whose audit container identifier is sought.
The read expects up to a u64 value (unset: 18446744073709551615).
This read requires CAP_AUDIT_CONTROL.
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
---
fs/proc/base.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1b3cda1..95fc64a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1261,6 +1261,24 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf,
.llseek = generic_file_llseek,
};
+static ssize_t proc_contid_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file_inode(file);
+ struct task_struct *task = get_proc_task(inode);
+ ssize_t length;
+ char tmpbuf[TMPBUFLEN*2];
+
+ if (!task)
+ return -ESRCH;
+ /* if we don't have caps, reject */
+ if (!capable(CAP_AUDIT_CONTROL))
+ return -EPERM;
+ length = scnprintf(tmpbuf, TMPBUFLEN*2, "%llu", audit_get_contid(task));
+ put_task_struct(task);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
static ssize_t proc_contid_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
@@ -1291,6 +1309,7 @@ static ssize_t proc_contid_write(struct file *file, const char __user *buf,
}
static const struct file_operations proc_contid_operations = {
+ .read = proc_contid_read,
.write = proc_contid_write,
.llseek = generic_file_llseek,
};
@@ -2987,7 +3006,7 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
- REG("audit_containerid", S_IWUSR, proc_contid_operations),
+ REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations),
#endif
#ifdef CONFIG_FAULT_INJECTION
REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
@@ -3373,7 +3392,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
- REG("audit_containerid", S_IWUSR, proc_contid_operations),
+ REG("audit_containerid", S_IWUSR|S_IRUSR, proc_contid_operations),
#endif
#ifdef CONFIG_FAULT_INJECTION
REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 09/10] audit: NETFILTER_PKT: record each container ID associated with a netNS
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Add audit container identifier auxiliary record(s) to NETFILTER_PKT
event standalone records. Iterate through all potential audit container
identifiers associated with a network namespace.
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
include/linux/audit.h | 5 +++++
kernel/audit.c | 26 ++++++++++++++++++++++++++
net/netfilter/xt_AUDIT.c | 12 ++++++++++--
3 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9a02095..8755f4d 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -169,6 +169,8 @@ extern int audit_log_contid(struct audit_context *context,
extern void audit_netns_contid_add(struct net *net, u64 contid);
extern void audit_netns_contid_del(struct net *net, u64 contid);
extern void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p);
+extern void audit_log_netns_contid_list(struct net *net,
+ struct audit_context *context);
extern int audit_update_lsm_rules(void);
@@ -228,6 +230,9 @@ static inline void audit_netns_contid_del(struct net *net, u64 contid)
{ }
static inline void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p)
{ }
+static inline void audit_log_netns_contid_list(struct net *net,
+ struct audit_context *context)
+{ }
#define audit_enabled AUDIT_OFF
#endif /* CONFIG_AUDIT */
diff --git a/kernel/audit.c b/kernel/audit.c
index c5fed3b..b23711c 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -392,6 +392,32 @@ void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p)
audit_netns_contid_add(new->net_ns, contid);
}
+void audit_log_netns_contid_list(struct net *net, struct audit_context *context)
+{
+ spinlock_t *lock = audit_get_netns_contid_list_lock(net);
+ struct audit_buffer *ab;
+ struct audit_contid *cont;
+ bool first = true;
+
+ /* Generate AUDIT_CONTAINER record with container ID CSV list */
+ ab = audit_log_start(context, GFP_ATOMIC, AUDIT_CONTAINER);
+ if (!ab) {
+ audit_log_lost("out of memory in audit_log_netns_contid_list");
+ return;
+ }
+ audit_log_format(ab, "contid=");
+ spin_lock(lock);
+ list_for_each_entry(cont, audit_get_netns_contid_list(net), list) {
+ if (!first)
+ audit_log_format(ab, ",");
+ audit_log_format(ab, "%llu", cont->id);
+ first = false;
+ }
+ spin_unlock(lock);
+ audit_log_end(ab);
+}
+EXPORT_SYMBOL(audit_log_netns_contid_list);
+
void audit_panic(const char *message)
{
switch (audit_failure) {
diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c
index af883f1..44fac3f 100644
--- a/net/netfilter/xt_AUDIT.c
+++ b/net/netfilter/xt_AUDIT.c
@@ -71,10 +71,13 @@ static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
{
struct audit_buffer *ab;
int fam = -1;
+ struct audit_context *context;
+ struct net *net;
if (audit_enabled == AUDIT_OFF)
- goto errout;
- ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT);
+ goto out;
+ context = audit_alloc_local(GFP_ATOMIC);
+ ab = audit_log_start(context, GFP_ATOMIC, AUDIT_NETFILTER_PKT);
if (ab == NULL)
goto errout;
@@ -104,7 +107,12 @@ static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
audit_log_end(ab);
+ net = xt_net(par);
+ audit_log_netns_contid_list(net, context);
+
errout:
+ audit_free_context(context);
+out:
return XT_CONTINUE;
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 08/10] audit: add support for containerid to network namespaces
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: ebiederm, luto, carlos, dhowells, Richard Guy Briggs, viro, simo,
eparis, serge
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Audit events could happen in a network namespace outside of a task
context due to packets received from the net that trigger an auditing
rule prior to being associated with a running task. The network
namespace could in use by multiple containers by association to the
tasks in that network namespace. We still want a way to attribute
these events to any potential containers. Keep a list per network
namespace to track these audit container identifiiers.
Add/increment the audit container identifier on:
- initial setting of the audit container identifier via /proc
- clone/fork call that inherits an audit container identifier
- unshare call that inherits an audit container identifier
- setns call that inherits an audit container identifier
Delete/decrement the audit container identifier on:
- an inherited audit container identifier dropped when child set
- process exit
- unshare call that drops a net namespace
- setns call that drops a net namespace
See: https://github.com/linux-audit/audit-kernel/issues/92
See: https://github.com/linux-audit/audit-testsuite/issues/64
See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
include/linux/audit.h | 17 ++++++++++
kernel/audit.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/auditsc.c | 8 ++++-
kernel/nsproxy.c | 4 +++
4 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 5580c25..9a02095 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <uapi/linux/audit.h>
+#include <linux/refcount.h>
#define AUDIT_INO_UNSET ((unsigned long)-1)
#define AUDIT_DEV_UNSET ((dev_t)-1)
@@ -87,6 +88,12 @@ struct audit_field {
u32 op;
};
+struct audit_contid {
+ struct list_head list;
+ u64 id;
+ refcount_t refcount;
+};
+
extern int is_audit_feature_set(int which);
extern int __init audit_register_class(int class, unsigned *list);
@@ -159,6 +166,9 @@ extern void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk);
extern int audit_log_contid(struct audit_context *context,
char *op, u64 contid);
+extern void audit_netns_contid_add(struct net *net, u64 contid);
+extern void audit_netns_contid_del(struct net *net, u64 contid);
+extern void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p);
extern int audit_update_lsm_rules(void);
@@ -212,6 +222,13 @@ static inline void audit_log_task_info(struct audit_buffer *ab,
static inline int audit_log_contid(struct audit_context *context,
char *op, u64 contid)
{ }
+static inline void audit_netns_contid_add(struct net *net, u64 contid)
+{ }
+static inline void audit_netns_contid_del(struct net *net, u64 contid)
+{ }
+static inline void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p)
+{ }
+
#define audit_enabled AUDIT_OFF
#endif /* CONFIG_AUDIT */
diff --git a/kernel/audit.c b/kernel/audit.c
index fc9f026f..c5fed3b 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -100,9 +100,13 @@
/**
* struct audit_net - audit private network namespace data
* @sk: communication socket
+ * @contid_list: audit container identifier list
+ * @contid_list_lock audit container identifier list lock
*/
struct audit_net {
struct sock *sk;
+ struct list_head contid_list;
+ spinlock_t contid_list_lock;
};
/**
@@ -308,6 +312,86 @@ static struct sock *audit_get_sk(const struct net *net)
return aunet->sk;
}
+/**
+ * audit_get_netns_contid_list - Return the audit container ID list for the given network namespace
+ * @net: the destination network namespace
+ *
+ * Description:
+ * Returns the list pointer if valid, NULL otherwise. The caller must ensure
+ * that a reference is held for the network namespace while the sock is in use.
+ */
+struct list_head *audit_get_netns_contid_list(const struct net *net)
+{
+ struct audit_net *aunet = net_generic(net, audit_net_id);
+
+ return &aunet->contid_list;
+}
+
+spinlock_t *audit_get_netns_contid_list_lock(const struct net *net)
+{
+ struct audit_net *aunet = net_generic(net, audit_net_id);
+
+ return &aunet->contid_list_lock;
+}
+
+void audit_netns_contid_add(struct net *net, u64 contid)
+{
+ spinlock_t *lock = audit_get_netns_contid_list_lock(net);
+ struct list_head *contid_list = audit_get_netns_contid_list(net);
+ struct audit_contid *cont;
+
+ if (!audit_contid_valid(contid))
+ return;
+ spin_lock(lock);
+ if (!list_empty(contid_list))
+ list_for_each_entry(cont, contid_list, list)
+ if (cont->id == contid) {
+ refcount_inc(&cont->refcount);
+ goto out;
+ }
+ cont = kmalloc(sizeof(struct audit_contid), GFP_KERNEL);
+ if (cont) {
+ INIT_LIST_HEAD(&cont->list);
+ cont->id = contid;
+ refcount_set(&cont->refcount, 1);
+ list_add(&cont->list, contid_list);
+ }
+out:
+ spin_unlock(lock);
+}
+
+void audit_netns_contid_del(struct net *net, u64 contid)
+{
+ spinlock_t *lock = audit_get_netns_contid_list_lock(net);
+ struct list_head *contid_list = audit_get_netns_contid_list(net);
+ struct audit_contid *cont = NULL;
+
+ if (!audit_contid_valid(contid))
+ return;
+ spin_lock(lock);
+ if (!list_empty(contid_list))
+ list_for_each_entry(cont, contid_list, list)
+ if (cont->id == contid) {
+ list_del(&cont->list);
+ if (refcount_dec_and_test(&cont->refcount))
+ kfree(cont);
+ break;
+ }
+ spin_unlock(lock);
+}
+
+void audit_switch_task_namespaces(struct nsproxy *ns, struct task_struct *p)
+{
+ u64 contid = audit_get_contid(p);
+ struct nsproxy *new = p->nsproxy;
+
+ if (!audit_contid_valid(contid))
+ return;
+ audit_netns_contid_del(ns->net_ns, contid);
+ if (new)
+ audit_netns_contid_add(new->net_ns, contid);
+}
+
void audit_panic(const char *message)
{
switch (audit_failure) {
@@ -1547,6 +1631,8 @@ static int __net_init audit_net_init(struct net *net)
return -ENOMEM;
}
aunet->sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+ INIT_LIST_HEAD(&aunet->contid_list);
+ spin_lock_init(&aunet->contid_list_lock);
return 0;
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 610c6869..fdf3f68 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -75,6 +75,7 @@
#include <linux/uaccess.h>
#include <linux/fsnotify_backend.h>
#include <uapi/linux/limits.h>
+#include <net/net_namespace.h>
#include "audit.h"
@@ -2165,6 +2166,7 @@ int audit_set_contid(struct task_struct *task, u64 contid)
uid_t uid;
struct tty_struct *tty;
char comm[sizeof(current->comm)];
+ struct net *net = task->nsproxy->net_ns;
task_lock(task);
/* Can't set if audit disabled */
@@ -2186,8 +2188,12 @@ int audit_set_contid(struct task_struct *task, u64 contid)
else if (!(thread_group_leader(task) && thread_group_empty(task)))
rc = -EALREADY;
read_unlock(&tasklist_lock);
- if (!rc)
+ if (!rc) {
+ if (audit_contid_valid(oldcontid))
+ audit_netns_contid_del(net, oldcontid);
task->audit->contid = contid;
+ audit_netns_contid_add(net, contid);
+ }
task_unlock(task);
if (!audit_enabled)
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index f6c5d33..718b120 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -27,6 +27,7 @@
#include <linux/syscalls.h>
#include <linux/cgroup.h>
#include <linux/perf_event.h>
+#include <linux/audit.h>
static struct kmem_cache *nsproxy_cachep;
@@ -140,6 +141,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
struct nsproxy *old_ns = tsk->nsproxy;
struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
struct nsproxy *new_ns;
+ u64 contid = audit_get_contid(tsk);
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET |
@@ -167,6 +169,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
return PTR_ERR(new_ns);
tsk->nsproxy = new_ns;
+ audit_netns_contid_add(new_ns->net_ns, contid);
return 0;
}
@@ -224,6 +227,7 @@ void switch_task_namespaces(struct task_struct *p, struct nsproxy *new)
ns = p->nsproxy;
p->nsproxy = new;
task_unlock(p);
+ audit_switch_task_namespaces(ns, p);
if (ns && atomic_dec_and_test(&ns->count))
free_nsproxy(ns);
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 07/10] audit: add containerid filtering
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Implement audit container identifier filtering using the AUDIT_CONTID
field name to send an 8-character string representing a u64 since the
value field is only u32.
Sending it as two u32 was considered, but gathering and comparing two
fields was more complex.
The feature indicator is AUDIT_FEATURE_BITMAP_CONTAINERID.
See: https://github.com/linux-audit/audit-kernel/issues/91
See: https://github.com/linux-audit/audit-userspace/issues/40
See: https://github.com/linux-audit/audit-testsuite/issues/64
See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
---
include/linux/audit.h | 1 +
include/uapi/linux/audit.h | 5 ++++-
kernel/audit.h | 1 +
kernel/auditfilter.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++
kernel/auditsc.c | 3 +++
5 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 1f340ad..5580c25 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -76,6 +76,7 @@ struct audit_field {
u32 type;
union {
u32 val;
+ u64 val64;
kuid_t uid;
kgid_t gid;
struct {
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index dc259c7..8bd2498 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -264,6 +264,7 @@
#define AUDIT_LOGINUID_SET 24
#define AUDIT_SESSIONID 25 /* Session ID */
#define AUDIT_FSTYPE 26 /* FileSystem Type */
+#define AUDIT_CONTID 27 /* Container ID */
/* These are ONLY useful when checking
* at syscall exit time (AUDIT_AT_EXIT). */
@@ -344,6 +345,7 @@ enum {
#define AUDIT_FEATURE_BITMAP_SESSIONID_FILTER 0x00000010
#define AUDIT_FEATURE_BITMAP_LOST_RESET 0x00000020
#define AUDIT_FEATURE_BITMAP_FILTER_FS 0x00000040
+#define AUDIT_FEATURE_BITMAP_CONTAINERID 0x00000080
#define AUDIT_FEATURE_BITMAP_ALL (AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT | \
AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME | \
@@ -351,7 +353,8 @@ enum {
AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND | \
AUDIT_FEATURE_BITMAP_SESSIONID_FILTER | \
AUDIT_FEATURE_BITMAP_LOST_RESET | \
- AUDIT_FEATURE_BITMAP_FILTER_FS)
+ AUDIT_FEATURE_BITMAP_FILTER_FS | \
+ AUDIT_FEATURE_BITMAP_CONTAINERID)
/* deprecated: AUDIT_VERSION_* */
#define AUDIT_VERSION_LATEST AUDIT_FEATURE_BITMAP_ALL
diff --git a/kernel/audit.h b/kernel/audit.h
index a6d00a5..7feaa1f 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -236,6 +236,7 @@ static inline int audit_hash_ino(u32 ino)
extern int audit_match_class(int class, unsigned syscall);
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
+extern int audit_comparator64(const u64 left, const u32 op, const u64 right);
extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right);
extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right);
extern int parent_len(const char *path);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index bf309f2..31a6733 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -410,6 +410,7 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
/* FALL THROUGH */
case AUDIT_ARCH:
case AUDIT_FSTYPE:
+ case AUDIT_CONTID:
if (f->op != Audit_not_equal && f->op != Audit_equal)
return -EINVAL;
break;
@@ -582,6 +583,14 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
}
entry->rule.exe = audit_mark;
break;
+ case AUDIT_CONTID:
+ if (f->val != sizeof(u64))
+ goto exit_free;
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
+ goto exit_free;
+ f->val64 = ((u64 *)str)[0];
+ break;
}
}
@@ -664,6 +673,11 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
data->buflen += data->values[i] =
audit_pack_string(&bufp, audit_mark_path(krule->exe));
break;
+ case AUDIT_CONTID:
+ data->buflen += data->values[i] = sizeof(u64);
+ for (i = 0; i < sizeof(u64); i++)
+ ((char *)bufp)[i] = ((char *)&f->val64)[i];
+ break;
case AUDIT_LOGINUID_SET:
if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) {
data->fields[i] = AUDIT_LOGINUID;
@@ -750,6 +764,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
if (!gid_eq(a->fields[i].gid, b->fields[i].gid))
return 1;
break;
+ case AUDIT_CONTID:
+ if (a->fields[i].val64 != b->fields[i].val64)
+ return 1;
+ break;
default:
if (a->fields[i].val != b->fields[i].val)
return 1;
@@ -1206,6 +1224,31 @@ int audit_comparator(u32 left, u32 op, u32 right)
}
}
+int audit_comparator64(u64 left, u32 op, u64 right)
+{
+ switch (op) {
+ case Audit_equal:
+ return (left == right);
+ case Audit_not_equal:
+ return (left != right);
+ case Audit_lt:
+ return (left < right);
+ case Audit_le:
+ return (left <= right);
+ case Audit_gt:
+ return (left > right);
+ case Audit_ge:
+ return (left >= right);
+ case Audit_bitmask:
+ return (left & right);
+ case Audit_bittest:
+ return ((left & right) == right);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
int audit_uid_comparator(kuid_t left, u32 op, kuid_t right)
{
switch (op) {
@@ -1344,6 +1387,10 @@ int audit_filter(int msgtype, unsigned int listtype)
result = audit_comparator(audit_loginuid_set(current),
f->op, f->val);
break;
+ case AUDIT_CONTID:
+ result = audit_comparator64(audit_get_contid(current),
+ f->op, f->val64);
+ break;
case AUDIT_MSGTYPE:
result = audit_comparator(msgtype, f->op, f->val);
break;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 7627f21..610c6869 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -622,6 +622,9 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_LOGINUID_SET:
result = audit_comparator(audit_loginuid_set(tsk), f->op, f->val);
break;
+ case AUDIT_CONTID:
+ result = audit_comparator64(audit_get_contid(tsk), f->op, f->val64);
+ break;
case AUDIT_SUBJ_USER:
case AUDIT_SUBJ_ROLE:
case AUDIT_SUBJ_TYPE:
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 06/10] audit: add containerid support for tty_audit
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Add audit container identifier auxiliary record to tty logging rule
event standalone records.
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
---
drivers/tty/tty_audit.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index 50f567b..3e21477 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -66,8 +66,9 @@ static void tty_audit_log(const char *description, dev_t dev,
uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
unsigned int sessionid = audit_get_sessionid(tsk);
+ struct audit_context *context = audit_alloc_local(GFP_KERNEL);
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_TTY);
if (ab) {
char name[sizeof(tsk->comm)];
@@ -80,6 +81,8 @@ static void tty_audit_log(const char *description, dev_t dev,
audit_log_n_hex(ab, data, size);
audit_log_end(ab);
}
+ audit_log_contid(context, "tty", audit_get_contid(tsk));
+ audit_free_context(context);
}
/**
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 05/10] audit: add support for non-syscall auxiliary records
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Standalone audit records have the timestamp and serial number generated
on the fly and as such are unique, making them standalone. This new
function audit_alloc_local() generates a local audit context that will
be used only for a standalone record and its auxiliary record(s). The
context is discarded immediately after the local associated records are
produced.
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
---
include/linux/audit.h | 8 ++++++++
kernel/audit.h | 1 +
kernel/auditsc.c | 33 ++++++++++++++++++++++++++++-----
3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 4f514ed..1f340ad 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -234,7 +234,9 @@ struct audit_task_info {
extern struct audit_task_info init_struct_audit;
extern void __init audit_task_init(void);
extern int audit_alloc(struct task_struct *task);
+extern struct audit_context *audit_alloc_local(gfp_t gfpflags);
extern void audit_free(struct task_struct *task);
+extern void audit_free_context(struct audit_context *context);
extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void __audit_syscall_exit(int ret_success, long ret_value);
@@ -495,6 +497,12 @@ static inline int audit_alloc(struct task_struct *task)
{
return 0;
}
+static inline struct audit_context *audit_alloc_local(gfp_t gfpflags)
+{
+ return NULL;
+}
+static inline void audit_free_context(struct audit_context *context)
+{ }
static inline void audit_free(struct task_struct *task)
{ }
static inline void audit_syscall_entry(int major, unsigned long a0,
diff --git a/kernel/audit.h b/kernel/audit.h
index 1cf1c35..a6d00a5 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -110,6 +110,7 @@ struct audit_proctitle {
struct audit_context {
int dummy; /* must be the first element */
int in_syscall; /* 1 if task is in a syscall */
+ bool local; /* local context needed */
enum audit_state state, current_state;
unsigned int serial; /* serial number for record */
int major; /* syscall number */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index cdb24cf..7627f21 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -913,11 +913,13 @@ static inline void audit_free_aux(struct audit_context *context)
}
}
-static inline struct audit_context *audit_alloc_context(enum audit_state state)
+static inline struct audit_context *audit_alloc_context(enum audit_state state,
+ gfp_t gfpflags)
{
struct audit_context *context;
- context = kzalloc(sizeof(*context), GFP_KERNEL);
+ /* We can be called in atomic context via audit_tg() */
+ context = kzalloc(sizeof(*context), gfpflags);
if (!context)
return NULL;
context->state = state;
@@ -970,7 +972,8 @@ int audit_alloc(struct task_struct *tsk)
return 0;
}
- if (!(context = audit_alloc_context(state))) {
+ context = audit_alloc_context(state, GFP_KERNEL);
+ if (!(context)) {
tsk->audit = NULL;
kmem_cache_free(audit_task_cache, info);
kfree(key);
@@ -991,8 +994,27 @@ struct audit_task_info init_struct_audit = {
.ctx = NULL,
};
-static inline void audit_free_context(struct audit_context *context)
+struct audit_context *audit_alloc_local(gfp_t gfpflags)
{
+ struct audit_context *context;
+
+ if (!audit_ever_enabled)
+ return NULL; /* Return if not auditing. */
+
+ context = audit_alloc_context(AUDIT_RECORD_CONTEXT, gfpflags);
+ if (!context)
+ return NULL;
+ context->serial = audit_serial();
+ context->ctime = current_kernel_time64();
+ context->local = true;
+ return context;
+}
+EXPORT_SYMBOL(audit_alloc_local);
+
+void audit_free_context(struct audit_context *context)
+{
+ if (!context)
+ return;
audit_free_names(context);
unroll_tree_refs(context, NULL, 0);
free_tree_refs(context);
@@ -1002,6 +1024,7 @@ static inline void audit_free_context(struct audit_context *context)
audit_proctitle_free(context);
kfree(context);
}
+EXPORT_SYMBOL(audit_free_context);
static int audit_log_pid_context(struct audit_context *context, pid_t pid,
kuid_t auid, kuid_t uid, unsigned int sessionid,
@@ -2024,7 +2047,7 @@ void __audit_inode_child(struct inode *parent,
int auditsc_get_stamp(struct audit_context *ctx,
struct timespec64 *t, unsigned int *serial)
{
- if (!ctx->in_syscall)
+ if (!ctx->in_syscall && !ctx->local)
return 0;
if (!ctx->serial)
ctx->serial = audit_serial();
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 04/10] audit: add containerid support for ptrace and signals
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Add audit container identifier support to ptrace and signals. In
particular, the "op" field provides a way to label the auxiliary record
to which it is associated.
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
---
include/linux/audit.h | 11 +++++------
kernel/audit.c | 13 +++++++------
kernel/audit.h | 2 ++
kernel/auditsc.c | 21 ++++++++++++++++-----
4 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index d5a48dc..4f514ed 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -34,6 +34,7 @@ struct audit_sig_info {
uid_t uid;
pid_t pid;
char ctx[0];
+ u64 cid;
};
struct audit_buffer;
@@ -155,9 +156,8 @@ extern void audit_log_key(struct audit_buffer *ab,
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk);
-extern int audit_log_contid(struct task_struct *tsk,
- struct audit_context *context,
- char *op);
+extern int audit_log_contid(struct audit_context *context,
+ char *op, u64 contid);
extern int audit_update_lsm_rules(void);
@@ -208,9 +208,8 @@ static inline int audit_log_task_context(struct audit_buffer *ab)
static inline void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk)
{ }
-static inline int audit_log_contid(struct task_struct *tsk,
- struct audit_context *context,
- char *op)
+static inline int audit_log_contid(struct audit_context *context,
+ char *op, u64 contid)
{ }
#define audit_enabled AUDIT_OFF
#endif /* CONFIG_AUDIT */
diff --git a/kernel/audit.c b/kernel/audit.c
index 15f54c7..fc9f026f 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -139,6 +139,7 @@ struct audit_net {
kuid_t audit_sig_uid = INVALID_UID;
pid_t audit_sig_pid = -1;
u32 audit_sig_sid = 0;
+u64 audit_sig_cid = AUDIT_CID_UNSET;
/* Records can be lost in several ways:
0) [suppressed in audit_alloc]
@@ -1434,6 +1435,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
memcpy(sig_data->ctx, ctx, len);
security_release_secctx(ctx, len);
}
+ sig_data->cid = audit_sig_cid;
audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0,
sig_data, sizeof(*sig_data) + len);
kfree(sig_data);
@@ -2047,23 +2049,22 @@ void audit_log_session_info(struct audit_buffer *ab)
/*
* audit_log_contid - report container info
- * @tsk: task to be recorded
* @context: task or local context for record
* @op: contid string description
+ * @contid: container ID to report
*/
-int audit_log_contid(struct task_struct *tsk,
- struct audit_context *context, char *op)
+int audit_log_contid(struct audit_context *context,
+ char *op, u64 contid)
{
struct audit_buffer *ab;
- if (!audit_contid_set(tsk))
+ if (!audit_contid_valid(contid))
return 0;
/* Generate AUDIT_CONTAINER record with container ID */
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONTAINER);
if (!ab)
return -ENOMEM;
- audit_log_format(ab, "op=%s contid=%llu",
- op, audit_get_contid(tsk));
+ audit_log_format(ab, "op=%s contid=%llu", op, contid);
audit_log_end(ab);
return 0;
}
diff --git a/kernel/audit.h b/kernel/audit.h
index 214e149..1cf1c35 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -147,6 +147,7 @@ struct audit_context {
kuid_t target_uid;
unsigned int target_sessionid;
u32 target_sid;
+ u64 target_cid;
char target_comm[TASK_COMM_LEN];
struct audit_tree_refs *trees, *first_trees;
@@ -329,6 +330,7 @@ extern void audit_log_d_path_exe(struct audit_buffer *ab,
extern pid_t audit_sig_pid;
extern kuid_t audit_sig_uid;
extern u32 audit_sig_sid;
+extern u64 audit_sig_cid;
extern int audit_filter(int msgtype, unsigned int listtype);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 39e5633..cdb24cf 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -113,6 +113,7 @@ struct audit_aux_data_pids {
kuid_t target_uid[AUDIT_AUX_PIDS];
unsigned int target_sessionid[AUDIT_AUX_PIDS];
u32 target_sid[AUDIT_AUX_PIDS];
+ u64 target_cid[AUDIT_AUX_PIDS];
char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN];
int pid_count;
};
@@ -1454,21 +1455,27 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
for (aux = context->aux_pids; aux; aux = aux->next) {
struct audit_aux_data_pids *axs = (void *)aux;
- for (i = 0; i < axs->pid_count; i++)
+ for (i = 0; i < axs->pid_count; i++) {
+ char axsn[sizeof("aux0xN ")];
+
+ sprintf(axsn, "aux0x%x", i);
if (audit_log_pid_context(context, axs->target_pid[i],
axs->target_auid[i],
axs->target_uid[i],
axs->target_sessionid[i],
axs->target_sid[i],
- axs->target_comm[i]))
+ axs->target_comm[i])
+ || audit_log_contid(context, axsn, axs->target_cid[i]))
call_panic = 1;
+ }
}
if (context->target_pid &&
- audit_log_pid_context(context, context->target_pid,
+ (audit_log_pid_context(context, context->target_pid,
context->target_auid, context->target_uid,
context->target_sessionid,
- context->target_sid, context->target_comm))
+ context->target_sid, context->target_comm)
+ || audit_log_contid(context, "target", context->target_cid)))
call_panic = 1;
if (context->pwd.dentry && context->pwd.mnt) {
@@ -1488,7 +1495,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_proctitle(tsk, context);
- audit_log_contid(tsk, context, "task");
+ audit_log_contid(context, "task", audit_get_contid(tsk));
/* Send end of event record to help user space know we are finished */
ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
@@ -2372,6 +2379,7 @@ void __audit_ptrace(struct task_struct *t)
context->target_uid = task_uid(t);
context->target_sessionid = audit_get_sessionid(t);
security_task_getsecid(t, &context->target_sid);
+ context->target_cid = audit_get_contid(t);
memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
}
@@ -2399,6 +2407,7 @@ int audit_signal_info(int sig, struct task_struct *t)
else
audit_sig_uid = uid;
security_task_getsecid(current, &audit_sig_sid);
+ audit_sig_cid = audit_get_contid(current);
}
if (!audit_signals || audit_dummy_context())
@@ -2412,6 +2421,7 @@ int audit_signal_info(int sig, struct task_struct *t)
ctx->target_uid = t_uid;
ctx->target_sessionid = audit_get_sessionid(t);
security_task_getsecid(t, &ctx->target_sid);
+ ctx->target_cid = audit_get_contid(t);
memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
return 0;
}
@@ -2433,6 +2443,7 @@ int audit_signal_info(int sig, struct task_struct *t)
axp->target_uid[axp->pid_count] = t_uid;
axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
security_task_getsecid(t, &axp->target_sid[axp->pid_count]);
+ axp->target_cid[axp->pid_count] = audit_get_contid(t);
memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
axp->pid_count++;
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 03/10] audit: log container info of syscalls
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Create a new audit record AUDIT_CONTAINER to document the audit
container identifier of a process if it is present.
Called from audit_log_exit(), syscalls are covered.
A sample raw event:
type=SYSCALL msg=audit(1519924845.499:257): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=56374e1cef30 a2=241 a3=1b6 items=2 ppid=606 pid=635 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="tmpcontainerid"
type=CWD msg=audit(1519924845.499:257): cwd="/root"
type=PATH msg=audit(1519924845.499:257): item=0 name="/tmp/" inode=13863 dev=00:27 mode=041777 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:tmp_t:s0 nametype= PARENT cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1519924845.499:257): item=1 name="/tmp/tmpcontainerid" inode=17729 dev=00:27 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 nametype=CREATE cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PROCTITLE msg=audit(1519924845.499:257): proctitle=62617368002D6300736C65657020313B206563686F2074657374203E202F746D702F746D70636F6E7461696E65726964
type=CONTAINER msg=audit(1519924845.499:257): op=task contid=123458
See: https://github.com/linux-audit/audit-kernel/issues/90
See: https://github.com/linux-audit/audit-userspace/issues/51
See: https://github.com/linux-audit/audit-testsuite/issues/64
See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
Acked-by: Steve Grubb <sgrubb@redhat.com>
---
include/linux/audit.h | 7 +++++++
include/uapi/linux/audit.h | 1 +
kernel/audit.c | 24 ++++++++++++++++++++++++
kernel/auditsc.c | 3 +++
4 files changed, 35 insertions(+)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 71a6fc6..d5a48dc 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -155,6 +155,9 @@ extern void audit_log_key(struct audit_buffer *ab,
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk);
+extern int audit_log_contid(struct task_struct *tsk,
+ struct audit_context *context,
+ char *op);
extern int audit_update_lsm_rules(void);
@@ -205,6 +208,10 @@ static inline int audit_log_task_context(struct audit_buffer *ab)
static inline void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk)
{ }
+static inline int audit_log_contid(struct task_struct *tsk,
+ struct audit_context *context,
+ char *op)
+{ }
#define audit_enabled AUDIT_OFF
#endif /* CONFIG_AUDIT */
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 3474f57..dc259c7 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -115,6 +115,7 @@
#define AUDIT_REPLACE 1329 /* Replace auditd if this packet unanswerd */
#define AUDIT_KERN_MODULE 1330 /* Kernel Module events */
#define AUDIT_FANOTIFY 1331 /* Fanotify access decision */
+#define AUDIT_CONTAINER 1332 /* Container ID */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
diff --git a/kernel/audit.c b/kernel/audit.c
index 2a80587..15f54c7 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -2045,6 +2045,30 @@ void audit_log_session_info(struct audit_buffer *ab)
audit_log_format(ab, " auid=%u ses=%u", auid, sessionid);
}
+/*
+ * audit_log_contid - report container info
+ * @tsk: task to be recorded
+ * @context: task or local context for record
+ * @op: contid string description
+ */
+int audit_log_contid(struct task_struct *tsk,
+ struct audit_context *context, char *op)
+{
+ struct audit_buffer *ab;
+
+ if (!audit_contid_set(tsk))
+ return 0;
+ /* Generate AUDIT_CONTAINER record with container ID */
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_CONTAINER);
+ if (!ab)
+ return -ENOMEM;
+ audit_log_format(ab, "op=%s contid=%llu",
+ op, audit_get_contid(tsk));
+ audit_log_end(ab);
+ return 0;
+}
+EXPORT_SYMBOL(audit_log_contid);
+
void audit_log_key(struct audit_buffer *ab, char *key)
{
audit_log_format(ab, " key=");
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6125cef..39e5633 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1488,10 +1488,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_proctitle(tsk, context);
+ audit_log_contid(tsk, context, "task");
+
/* Send end of event record to help user space know we are finished */
ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
if (ab)
audit_log_end(ab);
+
if (call_panic)
audit_panic("error converting sid to string");
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 02/10] audit: add container id
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: ebiederm, luto, carlos, dhowells, Richard Guy Briggs, viro, simo,
eparis, serge
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
Implement the proc fs write to set the audit container identifier of a
process, emitting an AUDIT_CONTAINER_OP record to document the event.
This is a write from the container orchestrator task to a proc entry of
the form /proc/PID/audit_containerid where PID is the process ID of the
newly created task that is to become the first task in a container, or
an additional task added to a container.
The write expects up to a u64 value (unset: 18446744073709551615).
The writer must have capability CAP_AUDIT_CONTROL.
This will produce a record such as this:
type=CONTAINER_ID msg=audit(2018-06-06 12:39:29.636:26949) : op=set opid=2209 old-contid=18446744073709551615 contid=123456 pid=628 auid=root uid=root tty=ttyS0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 comm=bash exe=/usr/bin/bash res=yes
The "op" field indicates an initial set. The "pid" to "ses" fields are
the orchestrator while the "opid" field is the object's PID, the process
being "contained". Old and new audit container identifier values are
given in the "contid" fields, while res indicates its success.
It is not permitted to unset the audit container identifier.
A child inherits its parent's audit container identifier.
See: https://github.com/linux-audit/audit-kernel/issues/90
See: https://github.com/linux-audit/audit-userspace/issues/51
See: https://github.com/linux-audit/audit-testsuite/issues/64
See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
Acked-by: Steve Grubb <sgrubb@redhat.com>
---
fs/proc/base.c | 37 +++++++++++++++++++++++++
include/linux/audit.h | 24 ++++++++++++++++
include/uapi/linux/audit.h | 2 ++
kernel/auditsc.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 131 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b657294..1b3cda1 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1260,6 +1260,41 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf,
.read = proc_sessionid_read,
.llseek = generic_file_llseek,
};
+
+static ssize_t proc_contid_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file_inode(file);
+ u64 contid;
+ int rv;
+ struct task_struct *task = get_proc_task(inode);
+
+ if (!task)
+ return -ESRCH;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ put_task_struct(task);
+ return -EINVAL;
+ }
+
+ rv = kstrtou64_from_user(buf, count, 10, &contid);
+ if (rv < 0) {
+ put_task_struct(task);
+ return rv;
+ }
+
+ rv = audit_set_contid(task, contid);
+ put_task_struct(task);
+ if (rv < 0)
+ return rv;
+ return count;
+}
+
+static const struct file_operations proc_contid_operations = {
+ .write = proc_contid_write,
+ .llseek = generic_file_llseek,
+};
+
#endif
#ifdef CONFIG_FAULT_INJECTION
@@ -2952,6 +2987,7 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
+ REG("audit_containerid", S_IWUSR, proc_contid_operations),
#endif
#ifdef CONFIG_FAULT_INJECTION
REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
@@ -3337,6 +3373,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
+ REG("audit_containerid", S_IWUSR, proc_contid_operations),
#endif
#ifdef CONFIG_FAULT_INJECTION
REG("make-it-fail", S_IRUGO|S_IWUSR, proc_fault_inject_operations),
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 8964332..71a6fc6 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -222,6 +222,7 @@ static inline void audit_log_task_info(struct audit_buffer *ab,
struct audit_task_info {
kuid_t loginuid;
unsigned int sessionid;
+ u64 contid;
struct audit_context *ctx;
};
extern struct audit_task_info init_struct_audit;
@@ -334,6 +335,7 @@ static inline void audit_ptrace(struct task_struct *t)
extern int auditsc_get_stamp(struct audit_context *ctx,
struct timespec64 *t, unsigned int *serial);
extern int audit_set_loginuid(kuid_t loginuid);
+extern int audit_set_contid(struct task_struct *tsk, u64 contid);
static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
{
@@ -351,6 +353,14 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
return AUDIT_SID_UNSET;
}
+static inline u64 audit_get_contid(struct task_struct *tsk)
+{
+ if (!tsk->audit)
+ return AUDIT_CID_UNSET;
+ else
+ return tsk->audit->contid;
+}
+
extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
extern void __audit_bprm(struct linux_binprm *bprm);
@@ -545,6 +555,10 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
{
return AUDIT_SID_UNSET;
}
+static inline kuid_t audit_get_contid(struct task_struct *tsk)
+{
+ return AUDIT_CID_UNSET;
+}
static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
{ }
static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid,
@@ -609,6 +623,16 @@ static inline bool audit_loginuid_set(struct task_struct *tsk)
return uid_valid(audit_get_loginuid(tsk));
}
+static inline bool audit_contid_valid(u64 contid)
+{
+ return contid != AUDIT_CID_UNSET;
+}
+
+static inline bool audit_contid_set(struct task_struct *tsk)
+{
+ return audit_contid_valid(audit_get_contid(tsk));
+}
+
static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
{
audit_log_n_string(ab, buf, strlen(buf));
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 4e3eaba..3474f57 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -71,6 +71,7 @@
#define AUDIT_TTY_SET 1017 /* Set TTY auditing status */
#define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */
#define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */
+#define AUDIT_CONTAINER_OP 1020 /* Define the container id and information */
#define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */
#define AUDIT_USER_AVC 1107 /* We filter this differently */
@@ -468,6 +469,7 @@ struct audit_tty_status {
#define AUDIT_UID_UNSET (unsigned int)-1
#define AUDIT_SID_UNSET ((unsigned int)-1)
+#define AUDIT_CID_UNSET ((u64)-1)
/* audit_rule_data supports filter rules with both integer and string
* fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 88779a7..6125cef 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -956,6 +956,7 @@ int audit_alloc(struct task_struct *tsk)
return -ENOMEM;
info->loginuid = audit_get_loginuid(current);
info->sessionid = audit_get_sessionid(current);
+ info->contid = audit_get_contid(current);
tsk->audit = info;
if (likely(!audit_ever_enabled))
@@ -985,6 +986,7 @@ int audit_alloc(struct task_struct *tsk)
struct audit_task_info init_struct_audit = {
.loginuid = INVALID_UID,
.sessionid = AUDIT_SID_UNSET,
+ .contid = AUDIT_CID_UNSET,
.ctx = NULL,
};
@@ -2112,6 +2114,72 @@ int audit_set_loginuid(kuid_t loginuid)
}
/**
+ * audit_set_contid - set current task's audit_context contid
+ * @contid: contid value
+ *
+ * Returns 0 on success, -EPERM on permission failure.
+ *
+ * Called (set) from fs/proc/base.c::proc_contid_write().
+ */
+int audit_set_contid(struct task_struct *task, u64 contid)
+{
+ u64 oldcontid;
+ int rc = 0;
+ struct audit_buffer *ab;
+ uid_t uid;
+ struct tty_struct *tty;
+ char comm[sizeof(current->comm)];
+
+ task_lock(task);
+ /* Can't set if audit disabled */
+ if (!task->audit) {
+ task_unlock(task);
+ return -ENOPROTOOPT;
+ }
+ oldcontid = audit_get_contid(task);
+ read_lock(&tasklist_lock);
+ /* Don't allow the audit containerid to be unset */
+ if (!audit_contid_valid(contid))
+ rc = -EINVAL;
+ /* if we don't have caps, reject */
+ else if (!capable(CAP_AUDIT_CONTROL))
+ rc = -EPERM;
+ /* if task has children or is not single-threaded, deny */
+ else if (!list_empty(&task->children))
+ rc = -EBUSY;
+ else if (!(thread_group_leader(task) && thread_group_empty(task)))
+ rc = -EALREADY;
+ read_unlock(&tasklist_lock);
+ if (!rc)
+ task->audit->contid = contid;
+ task_unlock(task);
+
+ if (!audit_enabled)
+ return rc;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONTAINER_OP);
+ if (!ab)
+ return rc;
+
+ uid = from_kuid(&init_user_ns, task_uid(current));
+ tty = audit_get_tty(current);
+ audit_log_format(ab, "op=set opid=%d old-contid=%llu contid=%llu pid=%d uid=%u auid=%u tty=%s ses=%u",
+ task_tgid_nr(task), oldcontid, contid,
+ task_tgid_nr(current), uid,
+ from_kuid(&init_user_ns, audit_get_loginuid(current)),
+ tty ? tty_name(tty) : "(none)",
+ audit_get_sessionid(current));
+ audit_put_tty(tty);
+ audit_log_task_context(ab);
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ audit_log_d_path_exe(ab, current->mm);
+ audit_log_format(ab, " res=%d", !rc);
+ audit_log_end(ab);
+ return rc;
+}
+
+/**
* __audit_mq_open - record audit data for a POSIX MQ open
* @oflag: open flag
* @mode: mode bits
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 01/10] audit: collect audit task parameters
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
In-Reply-To: <cover.1533065887.git.rgb@redhat.com>
The audit-related parameters in struct task_struct should ideally be
collected together and accessed through a standard audit API.
Collect the existing loginuid, sessionid and audit_context together in a
new struct audit_task_info called "audit" in struct task_struct.
Use kmem_cache to manage this pool of memory.
Un-inline audit_free() to be able to always recover that memory.
See: https://github.com/linux-audit/audit-kernel/issues/81
Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
---
include/linux/audit.h | 34 ++++++++++++++++++++++++----------
include/linux/sched.h | 5 +----
init/init_task.c | 3 +--
init/main.c | 2 ++
kernel/auditsc.c | 51 ++++++++++++++++++++++++++++++++++++++++++---------
kernel/fork.c | 4 +++-
6 files changed, 73 insertions(+), 26 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9334fbe..8964332 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -219,8 +219,15 @@ static inline void audit_log_task_info(struct audit_buffer *ab,
/* These are defined in auditsc.c */
/* Public API */
+struct audit_task_info {
+ kuid_t loginuid;
+ unsigned int sessionid;
+ struct audit_context *ctx;
+};
+extern struct audit_task_info init_struct_audit;
+extern void __init audit_task_init(void);
extern int audit_alloc(struct task_struct *task);
-extern void __audit_free(struct task_struct *task);
+extern void audit_free(struct task_struct *task);
extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void __audit_syscall_exit(int ret_success, long ret_value);
@@ -242,12 +249,15 @@ extern void audit_seccomp_actions_logged(const char *names,
static inline void audit_set_context(struct task_struct *task, struct audit_context *ctx)
{
- task->audit_context = ctx;
+ task->audit->ctx = ctx;
}
static inline struct audit_context *audit_context(void)
{
- return current->audit_context;
+ if (current->audit)
+ return current->audit->ctx;
+ else
+ return NULL;
}
static inline bool audit_dummy_context(void)
@@ -255,11 +265,7 @@ static inline bool audit_dummy_context(void)
void *p = audit_context();
return !p || *(int *)p;
}
-static inline void audit_free(struct task_struct *task)
-{
- if (unlikely(task->audit_context))
- __audit_free(task);
-}
+
static inline void audit_syscall_entry(int major, unsigned long a0,
unsigned long a1, unsigned long a2,
unsigned long a3)
@@ -331,12 +337,18 @@ extern int auditsc_get_stamp(struct audit_context *ctx,
static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
{
- return tsk->loginuid;
+ if (tsk->audit)
+ return tsk->audit->loginuid;
+ else
+ return INVALID_UID;
}
static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
{
- return tsk->sessionid;
+ if (tsk->audit)
+ return tsk->audit->sessionid;
+ else
+ return AUDIT_SID_UNSET;
}
extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
@@ -461,6 +473,8 @@ static inline void audit_fanotify(unsigned int response)
extern int audit_n_rules;
extern int audit_signals;
#else /* CONFIG_AUDITSYSCALL */
+static inline void __init audit_task_init(void)
+{ }
static inline int audit_alloc(struct task_struct *task)
{
return 0;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 87bf02d..e117272 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -30,7 +30,6 @@
#include <linux/rseq.h>
/* task_struct member predeclarations (sorted alphabetically): */
-struct audit_context;
struct backing_dev_info;
struct bio_list;
struct blk_plug;
@@ -873,10 +872,8 @@ struct task_struct {
struct callback_head *task_works;
- struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
- kuid_t loginuid;
- unsigned int sessionid;
+ struct audit_task_info *audit;
#endif
struct seccomp seccomp;
diff --git a/init/init_task.c b/init/init_task.c
index 74f60ba..4058840 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -119,8 +119,7 @@ struct task_struct init_task
.thread_group = LIST_HEAD_INIT(init_task.thread_group),
.thread_node = LIST_HEAD_INIT(init_signals.thread_head),
#ifdef CONFIG_AUDITSYSCALL
- .loginuid = INVALID_UID,
- .sessionid = AUDIT_SID_UNSET,
+ .audit = &init_struct_audit,
#endif
#ifdef CONFIG_PERF_EVENTS
.perf_event_mutex = __MUTEX_INITIALIZER(init_task.perf_event_mutex),
diff --git a/init/main.c b/init/main.c
index 3b4ada1..6aba171 100644
--- a/init/main.c
+++ b/init/main.c
@@ -92,6 +92,7 @@
#include <linux/rodata_test.h>
#include <linux/jump_label.h>
#include <linux/mem_encrypt.h>
+#include <linux/audit.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -721,6 +722,7 @@ asmlinkage __visible void __init start_kernel(void)
nsfs_init();
cpuset_init();
cgroup_init();
+ audit_task_init();
taskstats_init_early();
delayacct_init();
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index fb20746..88779a7 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -841,7 +841,7 @@ static inline struct audit_context *audit_take_context(struct task_struct *tsk,
int return_valid,
long return_code)
{
- struct audit_context *context = tsk->audit_context;
+ struct audit_context *context = tsk->audit->ctx;
if (!context)
return NULL;
@@ -926,6 +926,15 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
return context;
}
+static struct kmem_cache *audit_task_cache;
+
+void __init audit_task_init(void)
+{
+ audit_task_cache = kmem_cache_create("audit_task",
+ sizeof(struct audit_task_info),
+ 0, SLAB_PANIC, NULL);
+}
+
/**
* audit_alloc - allocate an audit context block for a task
* @tsk: task
@@ -940,17 +949,28 @@ int audit_alloc(struct task_struct *tsk)
struct audit_context *context;
enum audit_state state;
char *key = NULL;
+ struct audit_task_info *info;
+
+ info = kmem_cache_zalloc(audit_task_cache, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->loginuid = audit_get_loginuid(current);
+ info->sessionid = audit_get_sessionid(current);
+ tsk->audit = info;
if (likely(!audit_ever_enabled))
return 0; /* Return if not auditing. */
state = audit_filter_task(tsk, &key);
if (state == AUDIT_DISABLED) {
+ audit_set_context(tsk, NULL);
clear_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT);
return 0;
}
if (!(context = audit_alloc_context(state))) {
+ tsk->audit = NULL;
+ kmem_cache_free(audit_task_cache, info);
kfree(key);
audit_log_lost("out of memory in audit_alloc");
return -ENOMEM;
@@ -962,6 +982,12 @@ int audit_alloc(struct task_struct *tsk)
return 0;
}
+struct audit_task_info init_struct_audit = {
+ .loginuid = INVALID_UID,
+ .sessionid = AUDIT_SID_UNSET,
+ .ctx = NULL,
+};
+
static inline void audit_free_context(struct audit_context *context)
{
audit_free_names(context);
@@ -1469,26 +1495,33 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
}
/**
- * __audit_free - free a per-task audit context
+ * audit_free - free a per-task audit context
* @tsk: task whose audit context block to free
*
* Called from copy_process and do_exit
*/
-void __audit_free(struct task_struct *tsk)
+void audit_free(struct task_struct *tsk)
{
struct audit_context *context;
+ struct audit_task_info *info;
context = audit_take_context(tsk, 0, 0);
- if (!context)
- return;
-
/* Check for system calls that do not go through the exit
* function (e.g., exit_group), then free context block.
* We use GFP_ATOMIC here because we might be doing this
* in the context of the idle thread */
/* that can happen only if we are called from do_exit() */
- if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
+ if (context && context->in_syscall &&
+ context->current_state == AUDIT_RECORD_CONTEXT)
audit_log_exit(context, tsk);
+ /* Freeing the audit_task_info struct must be performed after
+ * audit_log_exit() due to need for loginuid and sessionid.
+ */
+ info = tsk->audit;
+ tsk->audit = NULL;
+ kmem_cache_free(audit_task_cache, info);
+ if (!context)
+ return;
if (!list_empty(&context->killed_trees))
audit_kill_trees(&context->killed_trees);
@@ -2071,8 +2104,8 @@ int audit_set_loginuid(kuid_t loginuid)
sessionid = (unsigned int)atomic_inc_return(&session_id);
}
- task->sessionid = sessionid;
- task->loginuid = loginuid;
+ task->audit->sessionid = sessionid;
+ task->audit->loginuid = loginuid;
out:
audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc);
return rc;
diff --git a/kernel/fork.c b/kernel/fork.c
index 9440d61..1bfb0ff 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1721,7 +1721,9 @@ static __latent_entropy struct task_struct *copy_process(
p->start_time = ktime_get_ns();
p->real_start_time = ktime_get_boot_ns();
p->io_context = NULL;
- audit_set_context(p, NULL);
+#ifdef CONFIG_AUDITSYSCALL
+ p->audit = NULL;
+#endif /* CONFIG_AUDITSYSCALL */
cgroup_fork(p);
#ifdef CONFIG_NUMA
p->mempolicy = mpol_dup(p->mempolicy);
--
1.8.3.1
^ permalink raw reply related
* [PATCH ghak90 (was ghak32) V4 00/10] audit: implement container identifier
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: containers, linux-api, Linux-Audit Mailing List, linux-fsdevel,
LKML, netdev, netfilter-devel
Cc: luto, carlos, viro, dhowells, simo, eparis, serge, ebiederm,
Richard Guy Briggs
Implement kernel audit container identifier.
This patchset is a fourth based on the proposal document (V3)
posted:
https://www.redhat.com/archives/linux-audit/2018-January/msg00014.html
The first patch is the last patch from ghak81 that is included here as a
convenience.
The second patch implements the proc fs write to set the audit container
identifier of a process, emitting an AUDIT_CONTAINER_OP record to announce the
registration of that audit container identifier on that process. This patch
requires userspace support for record acceptance and proper type
display.
The third implements the auxiliary record AUDIT_CONTAINER if an
audit container identifier is identifiable with an event. This patch
requires userspace support for proper type display.
The 4th adds signal and ptrace support.
The 5th creates a local audit context to be able to bind a standalone
record with a locally created auxiliary record.
The 6th patch adds audit container identifier records to the tty
standalone record.
The 7th adds audit container identifier filtering to the exit,
exclude and user lists. This patch adds the AUDIT_CONTID field and
requires auditctl userspace support for the --contid option.
The 8th adds network namespace audit container identifier labelling
based on member tasks' audit container identifier labels.
The 9th adds audit container identifier support to standalone netfilter
records that don't have a task context and lists each container to which
that net namespace belongs.
The 10th implements reading the audit container identifier from the proc
filesystem for debugging. This patch isn't planned for upstream
inclusion.
Example: Set an audit container identifier of 123456 to the "sleep" task:
sleep 2&
child=$!
echo 123456 > /proc/$child/audit_containerid; echo $?
ausearch -ts recent -m container
echo child:$child contid:$( cat /proc/$child/audit_containerid)
This should produce a record such as:
type=CONTAINER_OP msg=audit(2018-06-06 12:39:29.636:26949) : op=set opid=2209 old-contid=18446744073709551615 contid=123456 pid=628 auid=root uid=root tty=ttyS0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 comm=bash exe=/usr/bin/bash res=yes
Example: Set a filter on an audit container identifier 123459 on /tmp/tmpcontainerid:
contid=123459
key=tmpcontainerid
auditctl -a exit,always -F dir=/tmp -F perm=wa -F contid=$contid -F key=$key
perl -e "sleep 1; open(my \$tmpfile, '>', \"/tmp/$key\"); close(\$tmpfile);" &
child=$!
echo $contid > /proc/$child/audit_containerid
sleep 2
ausearch -i -ts recent -k $key
auditctl -d exit,always -F dir=/tmp -F perm=wa -F contid=$contid -F key=$key
rm -f /tmp/$key
This should produce an event such as:
type=CONTAINER msg=audit(2018-06-06 12:46:31.707:26953) : op=task contid=123459
type=PROCTITLE msg=audit(2018-06-06 12:46:31.707:26953) : proctitle=perl -e sleep 1; open(my $tmpfile, '>', "/tmp/tmpcontainerid"); close($tmpfile);
type=PATH msg=audit(2018-06-06 12:46:31.707:26953) : item=1 name=/tmp/tmpcontainerid inode=25656 dev=00:26 mode=file,644 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 nametype=CREATE cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=PATH msg=audit(2018-06-06 12:46:31.707:26953) : item=0 name=/tmp/ inode=8985 dev=00:26 mode=dir,sticky,777 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:tmp_t:s0 nametype=PARENT cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=CWD msg=audit(2018-06-06 12:46:31.707:26953) : cwd=/root
type=SYSCALL msg=audit(2018-06-06 12:46:31.707:26953) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffffffffffff9c a1=0x5621f2b81900 a2=O_WRONLY|O_CREAT|O_TRUNC a3=0x1b6 items=2 ppid=628 pid=2232 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=ttyS0 ses=1 comm=perl exe=/usr/bin/perl subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=tmpcontainerid
Includes: https://github.com/linux-audit/audit-kernel/issues/81
See: https://github.com/linux-audit/audit-kernel/issues/90
See: https://github.com/linux-audit/audit-userspace/issues/40
See: https://github.com/linux-audit/audit-testsuite/issues/64
See: https://github.com/linux-audit/audit-kernel/wiki/RFE-Audit-Container-ID
Changelog:
v4
- preface set with ghak81:"collect audit task parameters"
- add shallyn and sgrubb acks
- rename feature bitmap macro
- rename cid_valid() to audit_contid_valid()
- rename AUDIT_CONTAINER_ID to AUDIT_CONTAINER_OP
- delete audit_get_contid_list() from headers
- move work into inner if, delete "found"
- change netns contid list function names
- move exports for audit_log_contid audit_alloc_local audit_free_context to non-syscall patch
- list contids CSV
- pass in gfp flags to audit_alloc_local() (fix audit_alloc_context callers)
- use "local" in lieu of abusing in_syscall for auditsc_get_stamp()
- read_lock(&tasklist_lock) around children and thread check
- task_lock(tsk) should be taken before first check of tsk->audit
- add spin lock to contid list in aunet
- restrict /proc read to CAP_AUDIT_CONTROL
- remove set again prohibition and inherited flag
- delete contidion spelling fix from patchset, send to netdev/linux-wireless
v3
- switched from containerid in task_struct to audit_task_info (depends on ghak81)
- drop INVALID_CID in favour of only AUDIT_CID_UNSET
- check for !audit_task_info, throw -ENOPROTOOPT on set
- changed -EPERM to -EEXIST for parent check
- return AUDIT_CID_UNSET if !audit_enabled
- squash child/thread check patch into AUDIT_CONTAINER_ID patch
- changed -EPERM to -EBUSY for child check
- separate child and thread checks, use -EALREADY for latter
- move addition of op= from ptrace/signal patch to AUDIT_CONTAINER patch
- fix && to || bashism in ptrace/signal patch
- uninline and export function for audit_free_context()
- drop CONFIG_CHANGE, FEATURE_CHANGE, ANOM_ABEND, ANOM_SECCOMP patches
- move audit_enabled check (xt_AUDIT)
- switched from containerid list in struct net to net_generic's struct audit_net
- move containerid list iteration into audit (xt_AUDIT)
- create function to move namespace switch into audit
- switched /proc/PID/ entry from containerid to audit_containerid
- call kzalloc with GFP_ATOMIC on in_atomic() in audit_alloc_context()
- call kzalloc with GFP_ATOMIC on in_atomic() in audit_log_container_info()
- use xt_net(par) instead of sock_net(skb->sk) to get net
- switched record and field names: initial CONTAINER_ID, aux CONTAINER, field CONTID
- allow to set own contid
- open code audit_set_containerid
- add contid inherited flag
- ccontainerid and pcontainerid eliminated due to inherited flag
- change name of container list funcitons
- rename containerid to contid
- convert initial container record to syscall aux
- fix spelling mistake of contidion in net/rfkill/core.c to avoid contid name collision
v2
- add check for children and threads
- add network namespace container identifier list
- add NETFILTER_PKT audit container identifier logging
- patch description and documentation clean-up and example
- reap unused ppid
Richard Guy Briggs (10):
audit: collect audit task parameters
audit: add container id
audit: log container info of syscalls
audit: add containerid support for ptrace and signals
audit: add support for non-syscall auxiliary records
audit: add containerid support for tty_audit
audit: add containerid filtering
audit: add support for containerid to network namespaces
audit: NETFILTER_PKT: record each container ID associated with a netNS
debug audit: read container ID of a process
drivers/tty/tty_audit.c | 5 +-
fs/proc/base.c | 56 ++++++++++++++
include/linux/audit.h | 95 ++++++++++++++++++++---
include/linux/sched.h | 5 +-
include/uapi/linux/audit.h | 8 +-
init/init_task.c | 3 +-
init/main.c | 2 +
kernel/audit.c | 137 +++++++++++++++++++++++++++++++++
kernel/audit.h | 4 +
kernel/auditfilter.c | 47 ++++++++++++
kernel/auditsc.c | 183 ++++++++++++++++++++++++++++++++++++++++-----
kernel/fork.c | 4 +-
kernel/nsproxy.c | 4 +
net/netfilter/xt_AUDIT.c | 12 ++-
14 files changed, 526 insertions(+), 39 deletions(-)
--
1.8.3.1
^ permalink raw reply
* Re: [RFC PATCH ghak90 (was ghak32) V3 02/10] audit: log container info of syscalls
From: Richard Guy Briggs @ 2018-07-31 20:07 UTC (permalink / raw)
To: Paul Moore
Cc: sgrubb, cgroups, containers, linux-api, linux-audit,
linux-fsdevel, linux-kernel, netdev, ebiederm, luto, carlos,
dhowells, viro, simo, Eric Paris, serge
In-Reply-To: <20180726005129.vlwgj6bgnn2hufld@madcap2.tricolour.ca>
On 2018-07-25 20:51, Richard Guy Briggs wrote:
> On 2018-07-23 14:31, Paul Moore wrote:
> > On Mon, Jul 23, 2018 at 12:48 PM Steve Grubb <sgrubb@redhat.com> wrote:
> > > On Monday, July 23, 2018 11:11:48 AM EDT Richard Guy Briggs wrote:
> > > > On 2018-07-23 09:19, Steve Grubb wrote:
> > > > > On Sunday, July 22, 2018 4:55:10 PM EDT Richard Guy Briggs wrote:
> > > > > > On 2018-07-22 09:32, Steve Grubb wrote:
> > > > > > > On Saturday, July 21, 2018 4:29:30 PM EDT Richard Guy Briggs wrote:
> > > > > > > > > > + * audit_log_contid - report container info
> > > > > > > > > > + * @tsk: task to be recorded
> > > > > > > > > > + * @context: task or local context for record
> > > > > > > > > > + * @op: contid string description
> > > > > > > > > > + */
> > > > > > > > > > +int audit_log_contid(struct task_struct *tsk,
> > > > > > > > > > + struct audit_context *context,
> > > > > > > > > > char
> > > > > > > > > > *op)
> > > > > > > > > > +{
> > > > > > > > > > + struct audit_buffer *ab;
> > > > > > > > > > +
> > > > > > > > > > + if (!audit_contid_set(tsk))
> > > > > > > > > > + return 0;
> > > > > > > > > > + /* Generate AUDIT_CONTAINER record with container ID */
> > > > > > > > > > + ab = audit_log_start(context, GFP_KERNEL,
> > > > > > > > > > AUDIT_CONTAINER);
> > > > > > > > > > + if (!ab)
> > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > + audit_log_format(ab, "op=%s contid=%llu",
> > > > > > > > > > + op, audit_get_contid(tsk));
> > > > > > > > >
> > > > > > > > > Can you explain your reason for including an "op" field in this
> > > > > > > > > record
> > > > > > > > > type? I've been looking at the rest of the patches in this
> > > > > > > > > patchset
> > > > > > > > > and it seems to be used more as an indicator of the record's
> > > > > > > > > generating context rather than any sort of audit container ID
> > > > > > > > > operation.
> > > > > > > >
> > > > > > > > "action" might work, but that's netfilter and numeric... "kind"?
> > > > > > > > Nothing else really seems to fit from a field name, type or lack of
> > > > > > > > searchability perspective.
> > > > > > > >
> > > > > > > > Steve, do you have an opinion?
> > > > > > >
> > > > > > > We only have 1 sample event where we have op=task. What are the other
> > > > > > > possible values?
> > > > > >
> > > > > > For the AUDIT_CONTAINER record we have op= "task", "target" (from the
> > > > > > ptrace and signals patch), "tty".
> > > > > >
> > > > > > For the AUDIT_CONTAINER_ID record we have "op=set".
> > > > >
> > > > > Since the purpose of this record is to log the container id, I think that
> > > > > is all that is needed. We can get the context from the other records in
> > > > > the event. I'd suggest dropping the "op" field.
> > > >
> > > > Ok, the information above it for two different audit container
> > > > identifier records. Which one should drop the "op=" field? Both? Or
> > > > just the AUDIT_CONTAINER record? The AUDIT_CONTAINER_ID record (which
> > > > might be renamed) could use it to distinguish a "set" record from a
> > > > dropped audit container identifier that is no longer registered by any
> > > > task or namespace.
> > >
> > > Neither of them need it. All they need to do is state the container that is
> > > being acted upon.
> >
> > I think we should keep the "op" field for audit container ID
> > management operations, even though we really only have a "set"
> > operation at the moment, but the others should drop the "op" field
> > (see my previous emails in this thread).
>
> In fact, I'd like to question the wisdom of dropping this field and
> perhaps fine a better or new name for it, since these contid records
> could be multiple and different from the primary task that is generating
> this record. In particular, there are extra contid records generated by
> the ptrace/signals patch that could be from other processes in other
> containers that I mentioned in a branch thread that got dropped
> including the auc_pids data and the target_pid also attached to the
> primary task's audit context.
Ok, not seeing any further follow-up on this item in almost a week, I'll
not delay any more and post rev 4 of the patchset.
> > paul moore
>
> - RGB
- RGB
--
Richard Guy Briggs <rgb@redhat.com>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
IRC: rgb, SunRaycer
Voice: +1.647.777.2635, Internal: (81) 32635
^ permalink raw reply
* Re: [PATCH] prctl: add PR_[GS]ET_KILLABLE
From: Jann Horn @ 2018-07-31 16:26 UTC (permalink / raw)
To: j; +Cc: Andrew Morton, Oleg Nesterov, Eric W. Biederman, Linux API,
kernel list
In-Reply-To: <20180730075241.24002-1-j@bitron.ch>
On Mon, Jul 30, 2018 at 10:01 AM Jürg Billeter <j@bitron.ch> wrote:
>
> PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
> CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
> special signal protection for init processes.
>
> This is required for job control in a shell that uses CLONE_NEWPID for
> child processes.
>
> This prctl does not allow setting the SIGNAL_UNKILLABLE flag, only
> clearing.
>
> Signed-off-by: Jürg Billeter <j@bitron.ch>
> ---
[...]
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 38509dc1f77b..264de630d548 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
[...]
> + case PR_SET_KILLABLE:
> + if (arg2 != 1 || arg3 || arg4 || arg5)
> + return -EINVAL;
> + me->signal->flags &= ~SIGNAL_UNKILLABLE;
> + break;
I don't have an opinion on this patchset otherwise, but should this
prctl maybe block PR_SET_KILLABLE if you're actually the real init
process? This seems like it could potentially lead to weird things.
This code in kernel/fork.c seems to rely on the fact that global init
is SIGNAL_UNKILLABLE, and probably also leads to weirdness if
container init is non-SIGNAL_UNKILLABLE:
/*
* Siblings of global init remain as zombies on exit since they are
* not reaped by their parent (swapper). To solve this and to avoid
* multi-rooted process trees, prevent global and container-inits
* from creating siblings.
*/
if ((clone_flags & CLONE_PARENT) &&
current->signal->flags & SIGNAL_UNKILLABLE)
return ERR_PTR(-EINVAL);
^ permalink raw reply
* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Jürg Billeter @ 2018-07-31 16:12 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Andrew Morton, Thomas Gleixner, Eric Biederman, linux-api,
linux-kernel
In-Reply-To: <20180731143949.GA1890@redhat.com>
On Tue, 2018-07-31 at 16:39 +0200, Oleg Nesterov wrote:
> On 07/31, Jürg Billeter wrote:
> > SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
> > While a task with the SIGNAL_UNKILLABLE flag could install handlers for
> > these signals, this is not sufficient to implement a shell that uses
> > CLONE_NEWPID for child processes:
>
> Ah. My question wasn't clear, sorry.
>
> Could you explain your use-case? Why a shell wants to use
> CLONE_NEWPID?
To guarantee that there won't be any runaway processes, i.e., ensure
that no descendants (background helper daemons or misbehaving
processes) survive when the child process is terminated. And to prevent
children from killing their ancestors. This is not something that can
be always-on in all shells, but it could be an option for users that
want this control/isolation.
> And what do we actually want in, say, ^Z case? Just stop the child reaper
> or may be it would be better to stop the whole pid namespace?
Stopping the whole PID namespace would be interesting, however, I think
this should be discussed separately if and when there is a proposal to
support this. For now the process group is stopped, same as without PID
namespaces.
> > * As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
> > itself, it's not possible to implement the stop action in a custom
> > SIGTSTP handler.
>
> Yes. So may be we actually want to change __isig() paths to use
> SEND_SIG_FORCED (this is not that simple), or perhaps we can change
> __send_signal() to not drop SIGSTOP sent to itself, or may be we can even
> introduce SIG_DFL_EVEN_IF_INIT, I dunno.
In my opinion, my patch is much simpler and also more general as it
covers all scenarios where regular signal handling is required or
desired for "init" processes, with minimal code changes (after
PR_SET_KILLABLE, binaries that expect SIG_DFL to work can be executed
without changes).
> > * Many applications do not install handlers for these signals and
> > thus, job control won't work properly with unmodified applications.
>
> I can't understand this. An application should be changed anyway to do
> PR_SET_KILLABLE?
PR_SET_KILLABLE can be called (e.g., by the shell) between clone() and
execve(). (Some applications may have issues running as subreaper but
that's a separate matter, signal handling will work as expected).
> > + case PR_SET_KILLABLE:
> > + if (arg2 != 1 || arg3 || arg4 || arg5)
> > + return -EINVAL;
> > + spin_lock_irq(&me->sighand->siglock);
> > + me->signal->flags &= ~SIGNAL_UNKILLABLE;
> > + spin_unlock_irq(&me->sighand->siglock);
>
> OK, but then you need to change the CLONE_PARENT/SIGNAL_UNKILLABLE check
> in copy_process().
Good point, need a different check for the PID namespace root process
in copy_process().
Thanks,
Jürg
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox