Linux userland API discussions
 help / color / mirror / Atom feed
* [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 30/33] vfs: syscall: Add fspick() to select a superblock for reconfiguration [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 an fspick() system call that can be used to pick an existing
mountpoint into an fs_context which can thereafter be used to reconfigure a
superblock (equivalent of the superblock side of -o remount).

This looks like:

	int fd = fspick(AT_FDCWD, "/mnt",
			FSPICK_CLOEXEC | FSPICK_NO_AUTOMOUNT);
        fsconfig(fd, FSCONFIG_SET_FLAG, "intr", NULL, 0);
        fsconfig(fd, FSCONFIG_SET_FLAG, "noac", NULL, 0);
        fsconfig(fd, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0);

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

Signed-off-by: David Howells <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                            |   58 ++++++++++++++++++++++++++++++++
 include/linux/syscalls.h               |    1 +
 include/uapi/linux/fs.h                |    5 +++
 5 files changed, 66 insertions(+)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index c78b68256f8a..d1eb6c815790 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -403,3 +403,4 @@
 389	i386	fsopen			sys_fsopen			__ia32_sys_fsopen
 390	i386	fsconfig		sys_fsconfig			__ia32_sys_fsconfig
 391	i386	fsmount			sys_fsmount			__ia32_sys_fsmount
+392	i386	fspick			sys_fspick			__ia32_sys_fspick
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index d44ead5d4368..d3ab703c02bb 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -348,6 +348,7 @@
 337	common	fsopen			__x64_sys_fsopen
 338	common	fsconfig		__x64_sys_fsconfig
 339	common	fsmount			__x64_sys_fsmount
+340	common	fspick			__x64_sys_fspick
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/fsopen.c b/fs/fsopen.c
index 5d8560e78ce1..e79bb5b085d6 100644
--- a/fs/fsopen.c
+++ b/fs/fsopen.c
@@ -155,6 +155,64 @@ SYSCALL_DEFINE2(fsopen, const char __user *, _fs_name, unsigned int, flags)
 	return ret;
 }
 
+/*
+ * Pick a superblock into a context for reconfiguration.
+ */
+SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags)
+{
+	struct fs_context *fc;
+	struct path target;
+	unsigned int lookup_flags;
+	int ret;
+
+	if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if ((flags & ~(FSPICK_CLOEXEC |
+		       FSPICK_SYMLINK_NOFOLLOW |
+		       FSPICK_NO_AUTOMOUNT |
+		       FSPICK_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+	if (flags & FSPICK_SYMLINK_NOFOLLOW)
+		lookup_flags &= ~LOOKUP_FOLLOW;
+	if (flags & FSPICK_NO_AUTOMOUNT)
+		lookup_flags &= ~LOOKUP_AUTOMOUNT;
+	if (flags & FSPICK_EMPTY_PATH)
+		lookup_flags |= LOOKUP_EMPTY;
+	ret = user_path_at(dfd, path, lookup_flags, &target);
+	if (ret < 0)
+		goto err;
+
+	ret = -EOPNOTSUPP;
+	if (!target.dentry->d_sb->s_op->reconfigure)
+		goto err_path;
+
+	fc = vfs_new_fs_context(target.dentry->d_sb->s_type, target.dentry,
+				0, FS_CONTEXT_FOR_RECONFIGURE);
+	if (IS_ERR(fc)) {
+		ret = PTR_ERR(fc);
+		goto err_path;
+	}
+
+	fc->phase = FS_CONTEXT_RECONF_PARAMS;
+
+	ret = fscontext_alloc_log(fc);
+	if (ret < 0)
+		goto err_fc;
+
+	path_put(&target);
+	return fscontext_create_fd(fc, flags & FSPICK_CLOEXEC ? O_CLOEXEC : 0);
+
+err_fc:
+	put_fs_context(fc);
+err_path:
+	path_put(&target);
+err:
+	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.
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 65db661cc2da..701522957a12 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -908,6 +908,7 @@ 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);
+asmlinkage long sys_fspick(int dfd, const char __user *path, unsigned int flags);
 
 /*
  * Architecture-specific system calls
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 10281d582e28..7f01503a9e9b 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -351,6 +351,11 @@ typedef int __bitwise __kernel_rwf_t;
 
 #define FSMOUNT_CLOEXEC		0x00000001
 
+#define FSPICK_CLOEXEC		0x00000001
+#define FSPICK_SYMLINK_NOFOLLOW	0x00000002
+#define FSPICK_NO_AUTOMOUNT	0x00000004
+#define FSPICK_EMPTY_PATH	0x00000008
+
 /*
  * The type of fsconfig() call made.
  */

^ permalink raw reply related

* [PATCH 0/5] VFS: Introduce filesystem information query syscall
From: David Howells @ 2018-08-01 16:13 UTC (permalink / raw)
  To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel


Hi Al,

Here are a set of patches that adds a syscall, fsinfo(), that allows
attributes of a filesystem/superblock to be queried.  Attributes are of two
basic types: fixed-length structure and variable-length string.  Attributes
can also have multiple values in up to two dimensions.

Note that the attributes *are* allowed to vary depending on the specific
dentry that you're looking at.

I've tried to make the interface as light as possible, so integer/enum
attribute selector rather than string and the core does all the allocation and
extensibility support work rather than leaving that to the filesystems.  That
means sb->s_op->get_fsinfo() may assume that the provided buffer is always
present and always big enough.

Further, this removes the possibility of the filesystem gaining access to the
userspace buffer.


General superblock attributes include:

 - The amount of space/free space in a filesystem (as statfs()).
 - Filesystem identifiers (UUID, volume label, device numbers, ...)
 - The limits on a filesystem's capabilities
 - A variety single-bit flags indicating supported capabilities.
 - Information on supported statx fields and attributes and IOC flags.
 - Timestamp resolution and range.
 - Sources (as per mount(2), but fsconfig() allows multiple sources).
 - In-filesystem filename format information.
 - I/O parameters.
 - Filesystem parameters ("mount -o xxx"-type things).

Specific network superblock attributes include:

 - Cell and domain names.
 - Kerberos realm name.
 - Server names and addresses.

Filesystem configuration metadata attributes include:

 - Filesystem parameter type descriptions.
 - Name -> parameter mappings.
 - Simple enumeration name -> value mappings.

fsinfo() also permits you to retrieve information about what the fsinfo()
syscall itself supports, including the number of attibutes supported and the
number of capability bits supported.

The system is extensible:

 (1) New attributes can be added.  There is no requirement that a filesystem
     implement every attribute.  Note that the core VFS keeps a table of types
     and sizes so it can handle future extensibility rather than delegating
     this to the filesystems.

 (2) Structure-typed attributes can be made larger and have more information
     tacked on the end, provided it keeps the layout of the existing fields.
     If an older process asks for a shorter structure, it will only be given
     the bits it asks for.  If a newer process asks for a longer structure on
     an older kernel, the extra space will be set to 0.  In all cases, the
     size of the data available is returned.

     In essence, the size of a structure is that structure's version: a
     smaller size is an earlier version and a later version includes
     everything that the earlier version did.

 (3) New single-bit capability flags can be added.  This is a structure-typed
     attribute and, as such, (2) applies.  Any bits you wanted but the kernel
     doesn't support are automatically set to 0.

If a filesystem-specific attribute is added, it should just take up the next
number in the enumeration.  Currently, I do not intend that the number space
should be subdivided between interested parties.

fsinfo() may be called like the following, for example:

	struct fsinfo_params params = {
		.at_flags	= AT_SYMLINK_NOFOLLOW,
		.request	= FSINFO_ATTR_SERVER_ADDRESS;
		.Nth		= 2;
		.Mth		= 1;
	};
	struct fsinfo_server_address address;

	len = fsinfo(AT_FDCWD, "/kafs/grand.central.org/doc", &params,
		     &address, sizeof(address));

The above example would query a network filesystem, such as AFS or NFS, and
ask what the 2nd address (Mth) of the 3rd server (Nth) that the superblock is
using is.  Whereas:

	struct fsinfo_params params = {
		.at_flags	= AT_SYMLINK_NOFOLLOW,
		.request	= FSINFO_ATTR_CELL_NAME;
	};
	char cell_name[256];

	len = fsinfo(AT_FDCWD, "/kafs/grand.central.org/doc", &params,
		     &cell_name, sizeof(cell_name));

would retrieve the name of an AFS cell as a string.

fsinfo() can also be used to query a context from fsopen() or fspick():

	fd = fsopen("ext4", 0);
	struct fsinfo_params params = {
		.request	= FSINFO_ATTR_PARAM_DESCRIPTION;
	};
	struct fsinfo_param_description desc;
	fsinfo(fd, NULL, &params, &desc, sizeof(desc));

even if that context doesn't currently have a superblock attached (though if
there's no superblock attached, only filesystem-specific things like parameter
descriptions can be accessed).

The patches can be found here also:

	https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git

on branch:

	fsinfo

David
---
David Howells (5):
      vfs: syscall: Add fsinfo() to query filesystem information
      afs: Add fsinfo support
      vfs: Allow fsinfo() to query what's in an fs_context
      vfs: Allow fsinfo() to be used to query an fs parameter description
      vfs: Implement parameter value retrieval with fsinfo()


 arch/x86/entry/syscalls/syscall_32.tbl |    1 
 arch/x86/entry/syscalls/syscall_64.tbl |    1 
 fs/afs/internal.h                      |    1 
 fs/afs/super.c                         |  167 +++++++++
 fs/hugetlbfs/inode.c                   |   65 ++++
 fs/kernfs/mount.c                      |   16 +
 fs/statfs.c                            |  572 ++++++++++++++++++++++++++++++++
 include/linux/fs.h                     |    4 
 include/linux/fsinfo.h                 |   41 ++
 include/linux/kernfs.h                 |    2 
 include/linux/syscalls.h               |    4 
 include/uapi/linux/fsinfo.h            |  302 +++++++++++++++++
 kernel/cgroup/cgroup-v1.c              |   72 ++++
 kernel/cgroup/cgroup.c                 |   31 ++
 samples/Kconfig                        |    2 
 samples/statx/Makefile                 |    7 
 samples/statx/test-fs-query.c          |  137 ++++++++
 samples/statx/test-fsinfo.c            |  554 +++++++++++++++++++++++++++++++
 18 files changed, 1975 insertions(+), 4 deletions(-)
 create mode 100644 include/linux/fsinfo.h
 create mode 100644 include/uapi/linux/fsinfo.h
 create mode 100644 samples/statx/test-fs-query.c
 create mode 100644 samples/statx/test-fsinfo.c

^ permalink raw reply

* [PATCH 1/5] vfs: syscall: Add fsinfo() to query filesystem information
From: David Howells @ 2018-08-01 16:14 UTC (permalink / raw)
  To: viro; +Cc: linux-api, torvalds, dhowells, linux-fsdevel, linux-kernel
In-Reply-To: <153314002975.18964.1773855756473041897.stgit@warthog.procyon.org.uk>

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.

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/statfs.c                            |  462 +++++++++++++++++++++++++++
 include/linux/fs.h                     |    4 
 include/linux/fsinfo.h                 |   41 ++
 include/linux/syscalls.h               |    4 
 include/uapi/linux/fsinfo.h            |  235 ++++++++++++++
 samples/statx/Makefile                 |    5 
 samples/statx/test-fsinfo.c            |  546 ++++++++++++++++++++++++++++++++
 9 files changed, 1298 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/fsinfo.h
 create mode 100644 include/uapi/linux/fsinfo.h
 create mode 100644 samples/statx/test-fsinfo.c

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index d1eb6c815790..806760188a31 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -404,3 +404,4 @@
 390	i386	fsconfig		sys_fsconfig			__ia32_sys_fsconfig
 391	i386	fsmount			sys_fsmount			__ia32_sys_fsmount
 392	i386	fspick			sys_fspick			__ia32_sys_fspick
+393	i386	fsinfo			sys_fsinfo			__ia32_sys_fsinfo
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index d3ab703c02bb..0823eed2b02e 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -349,6 +349,7 @@
 338	common	fsconfig		__x64_sys_fsconfig
 339	common	fsmount			__x64_sys_fsmount
 340	common	fspick			__x64_sys_fspick
+341	common	fsinfo			__x64_sys_fsinfo
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/statfs.c b/fs/statfs.c
index 5b2a24f0f263..87826933faa0 100644
--- a/fs/statfs.c
+++ b/fs/statfs.c
@@ -9,6 +9,7 @@
 #include <linux/security.h>
 #include <linux/uaccess.h>
 #include <linux/compat.h>
+#include <linux/fsinfo.h>
 #include "internal.h"
 
 static int flags_by_mnt(int mnt_flags)
@@ -384,3 +385,464 @@ COMPAT_SYSCALL_DEFINE2(ustat, unsigned, dev, struct compat_ustat __user *, u)
 	return 0;
 }
 #endif
+
+/*
+ * Get basic filesystem stats from statfs.
+ */
+static int fsinfo_generic_statfs(struct dentry *dentry,
+				 struct fsinfo_statfs *p)
+{
+	struct super_block *sb;
+	struct kstatfs buf;
+	int ret;
+
+	ret = statfs_by_dentry(dentry, &buf);
+	if (ret < 0)
+		return ret;
+
+	sb = dentry->d_sb;
+	p->f_blocks	= buf.f_blocks;
+	p->f_bfree	= buf.f_bfree;
+	p->f_bavail	= buf.f_bavail;
+	p->f_files	= buf.f_files;
+	p->f_ffree	= buf.f_ffree;
+	p->f_favail	= buf.f_ffree;
+	p->f_bsize	= buf.f_bsize;
+	p->f_frsize	= buf.f_frsize;
+	return sizeof(*p);
+}
+
+static int fsinfo_generic_ids(struct dentry *dentry,
+			      struct fsinfo_ids *p)
+{
+	struct super_block *sb;
+	struct kstatfs buf;
+	int ret;
+
+	ret = statfs_by_dentry(dentry, &buf);
+	if (ret < 0)
+		return ret;
+
+	sb = dentry->d_sb;
+	p->f_fstype	= sb->s_magic;
+	p->f_dev_major	= MAJOR(sb->s_dev);
+	p->f_dev_minor	= MINOR(sb->s_dev);
+	p->f_flags	= ST_VALID | flags_by_sb(sb->s_flags);
+
+	memcpy(&p->f_fsid, &buf.f_fsid, sizeof(p->f_fsid));
+	strlcpy(p->f_fs_name, dentry->d_sb->s_type->name, sizeof(p->f_fs_name));
+	return sizeof(*p);
+}
+
+static int fsinfo_generic_limits(struct dentry *dentry,
+				 struct fsinfo_limits *lim)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	lim->max_file_size = sb->s_maxbytes;
+	lim->max_hard_links = sb->s_max_links;
+	lim->max_uid = UINT_MAX;
+	lim->max_gid = UINT_MAX;
+	lim->max_projid = UINT_MAX;
+	lim->max_filename_len = NAME_MAX;
+	lim->max_symlink_len = PAGE_SIZE;
+	lim->max_xattr_name_len = XATTR_NAME_MAX;
+	lim->max_xattr_body_len = XATTR_SIZE_MAX;
+	lim->max_dev_major = 0xffffff;
+	lim->max_dev_minor = 0xff;
+	return sizeof(*lim);
+}
+
+static int fsinfo_generic_supports(struct dentry *dentry,
+				   struct fsinfo_supports *c)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	c->stx_mask = STATX_BASIC_STATS;
+	if (sb->s_d_op && sb->s_d_op->d_automount)
+		c->stx_attributes |= STATX_ATTR_AUTOMOUNT;
+	return sizeof(*c);
+}
+
+static int fsinfo_generic_capabilities(struct dentry *dentry,
+				       struct fsinfo_capabilities *c)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	if (sb->s_mtd)
+		fsinfo_set_cap(c, FSINFO_CAP_IS_FLASH_FS);
+	else if (sb->s_bdev)
+		fsinfo_set_cap(c, FSINFO_CAP_IS_BLOCK_FS);
+
+	if (sb->s_quota_types & QTYPE_MASK_USR)
+		fsinfo_set_cap(c, FSINFO_CAP_USER_QUOTAS);
+	if (sb->s_quota_types & QTYPE_MASK_GRP)
+		fsinfo_set_cap(c, FSINFO_CAP_GROUP_QUOTAS);
+	if (sb->s_quota_types & QTYPE_MASK_PRJ)
+		fsinfo_set_cap(c, FSINFO_CAP_PROJECT_QUOTAS);
+	if (sb->s_d_op && sb->s_d_op->d_automount)
+		fsinfo_set_cap(c, FSINFO_CAP_AUTOMOUNTS);
+	if (sb->s_id[0])
+		fsinfo_set_cap(c, FSINFO_CAP_VOLUME_ID);
+
+	fsinfo_set_cap(c, FSINFO_CAP_HAS_ATIME);
+	fsinfo_set_cap(c, FSINFO_CAP_HAS_CTIME);
+	fsinfo_set_cap(c, FSINFO_CAP_HAS_MTIME);
+	return sizeof(*c);
+}
+
+static int fsinfo_generic_timestamp_info(struct dentry *dentry,
+					 struct fsinfo_timestamp_info *ts)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	/* If unset, assume 1s granularity */
+	u16 mantissa = 1;
+	s8 exponent = 0;
+
+	ts->minimum_timestamp = S64_MIN;
+	ts->maximum_timestamp = S64_MAX;
+	if (sb->s_time_gran < 1000000000) {
+		if (sb->s_time_gran < 1000)
+			exponent = -9;
+		else if (sb->s_time_gran < 1000000)
+			exponent = -6;
+		else
+			exponent = -3;
+	}
+#define set_gran(x)				\
+	do {					\
+		ts->x##_mantissa = mantissa;	\
+		ts->x##_exponent = exponent;	\
+	} while (0)
+	set_gran(atime_gran);
+	set_gran(btime_gran);
+	set_gran(ctime_gran);
+	set_gran(mtime_gran);
+	return sizeof(*ts);
+}
+
+static int fsinfo_generic_volume_uuid(struct dentry *dentry,
+				      struct fsinfo_volume_uuid *vu)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	memcpy(vu, &sb->s_uuid, sizeof(*vu));
+	return sizeof(*vu);
+}
+
+static int fsinfo_generic_volume_id(struct dentry *dentry, char *buf)
+{
+	struct super_block *sb = dentry->d_sb;
+	size_t len = strlen(sb->s_id);
+
+	memcpy(buf, sb->s_id, len + 1);
+	return len;
+}
+
+static int fsinfo_generic_name_encoding(struct dentry *dentry, char *buf)
+{
+	static const char encoding[] = "utf8";
+
+	memcpy(buf, encoding, sizeof(encoding) - 1);
+	return sizeof(encoding) - 1;
+}
+
+static int fsinfo_generic_io_size(struct dentry *dentry,
+				  struct fsinfo_io_size *c)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct kstatfs buf;
+	int ret;
+
+	if (sb->s_op->statfs == simple_statfs) {
+		c->dio_size_gran = 1;
+		c->dio_mem_align = 1;
+	} else {
+		ret = statfs_by_dentry(dentry, &buf);
+		if (ret < 0)
+			return ret;
+		c->dio_size_gran = buf.f_bsize;
+		c->dio_mem_align = buf.f_bsize;
+	}
+	return sizeof(*c);
+}
+
+/*
+ * Implement some queries generically from stuff in the superblock.
+ */
+int generic_fsinfo(struct dentry *dentry, struct fsinfo_kparams *params)
+{
+#define _gen(X, Y) FSINFO_ATTR_##X: return fsinfo_generic_##Y(dentry, params->buffer)
+
+	switch (params->request) {
+	case _gen(STATFS,		statfs);
+	case _gen(IDS,			ids);
+	case _gen(LIMITS,		limits);
+	case _gen(SUPPORTS,		supports);
+	case _gen(CAPABILITIES,		capabilities);
+	case _gen(TIMESTAMP_INFO,	timestamp_info);
+	case _gen(VOLUME_UUID,		volume_uuid);
+	case _gen(VOLUME_ID,		volume_id);
+	case _gen(NAME_ENCODING,	name_encoding);
+	case _gen(IO_SIZE,		io_size);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+EXPORT_SYMBOL(generic_fsinfo);
+
+/*
+ * Retrieve the filesystem info.  We make some stuff up if the operation is not
+ * supported.
+ */
+int vfs_fsinfo(const struct path *path, struct fsinfo_kparams *params)
+{
+	struct dentry *dentry = path->dentry;
+	int (*get_fsinfo)(struct dentry *, struct fsinfo_kparams *);
+	int ret;
+
+	if (params->request == FSINFO_ATTR_FSINFO) {
+		struct fsinfo_fsinfo *info = params->buffer;
+
+		info->max_attr	= FSINFO_ATTR__NR;
+		info->max_cap	= FSINFO_CAP__NR;
+		return sizeof(*info);
+	}
+
+	get_fsinfo = dentry->d_sb->s_op->get_fsinfo;
+	if (!get_fsinfo) {
+		if (!dentry->d_sb->s_op->statfs)
+			return -EOPNOTSUPP;
+		get_fsinfo = generic_fsinfo;
+	}
+
+	ret = security_sb_statfs(dentry);
+	if (ret)
+		return ret;
+
+	ret = get_fsinfo(dentry, params);
+	if (ret < 0)
+		return ret;
+
+	if (params->request == FSINFO_ATTR_IDS &&
+	    params->buffer) {
+		struct fsinfo_ids *p = params->buffer;
+
+		p->f_flags |= flags_by_mnt(path->mnt->mnt_flags);
+	}
+	return ret;
+}
+
+static int vfs_fsinfo_path(int dfd, const char __user *filename,
+			   struct fsinfo_kparams *params)
+{
+	struct path path;
+	unsigned lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+	int ret = -EINVAL;
+
+	if ((params->at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+				 AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	if (params->at_flags & AT_SYMLINK_NOFOLLOW)
+		lookup_flags &= ~LOOKUP_FOLLOW;
+	if (params->at_flags & AT_NO_AUTOMOUNT)
+		lookup_flags &= ~LOOKUP_AUTOMOUNT;
+	if (params->at_flags & AT_EMPTY_PATH)
+		lookup_flags |= LOOKUP_EMPTY;
+
+retry:
+	ret = user_path_at(dfd, filename, lookup_flags, &path);
+	if (ret)
+		goto out;
+
+	ret = vfs_fsinfo(&path, params);
+	path_put(&path);
+	if (retry_estale(ret, lookup_flags)) {
+		lookup_flags |= LOOKUP_REVAL;
+		goto retry;
+	}
+out:
+	return ret;
+}
+
+static int vfs_fsinfo_fd(unsigned int fd, struct fsinfo_kparams *params)
+{
+	struct fd f = fdget_raw(fd);
+	int ret = -EBADF;
+
+	if (f.file) {
+		ret = vfs_fsinfo(&f.file->f_path, params);
+		fdput(f);
+	}
+	return ret;
+}
+
+/*
+ * Return buffer information by requestable attribute.
+ *
+ * STRUCT indicates a fixed-size structure with only one instance.
+ * STRUCT_N indicates a 1D array of STRUCT, indexed by Nth
+ * STRUCT_NM indicates a 2D-array of STRUCT, indexed by Nth, Mth
+ * STRING indicates a string with only one instance.
+ * STRING_N indicates a 1D array of STRING, indexed by Nth
+ * STRING_NM indicates a 2D-array of STRING, indexed by Nth, Mth
+ *
+ * If an entry is marked STRUCT, STRUCT_N or STRUCT_NM then if no buffer is
+ * supplied to sys_fsinfo(), sys_fsinfo() will handle returning the buffer size
+ * without calling vfs_fsinfo() and the filesystem.
+ *
+ * No struct may have more than 252 bytes (ie. 0x3f * 4)
+ */
+#define FSINFO_STRING(X,Y)	 [FSINFO_ATTR_##X] = 0x0000
+#define FSINFO_STRUCT(X,Y)	 [FSINFO_ATTR_##X] = sizeof(struct fsinfo_##Y)
+#define FSINFO_STRING_N(X,Y)	 [FSINFO_ATTR_##X] = 0x4000
+#define FSINFO_STRUCT_N(X,Y)	 [FSINFO_ATTR_##X] = 0x4000 | sizeof(struct fsinfo_##Y)
+#define FSINFO_STRUCT_NM(X,Y)	 [FSINFO_ATTR_##X] = 0x8000 | sizeof(struct fsinfo_##Y)
+#define FSINFO_STRING_NM(X,Y)	 [FSINFO_ATTR_##X] = 0x8000
+static const u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = {
+	FSINFO_STRUCT		(STATFS,		statfs),
+	FSINFO_STRUCT		(FSINFO,		fsinfo),
+	FSINFO_STRUCT		(IDS,			ids),
+	FSINFO_STRUCT		(LIMITS,		limits),
+	FSINFO_STRUCT		(CAPABILITIES,		capabilities),
+	FSINFO_STRUCT		(SUPPORTS,		supports),
+	FSINFO_STRUCT		(TIMESTAMP_INFO,	timestamp_info),
+	FSINFO_STRING		(VOLUME_ID,		volume_id),
+	FSINFO_STRUCT		(VOLUME_UUID,		volume_uuid),
+	FSINFO_STRING		(VOLUME_NAME,		volume_name),
+	FSINFO_STRING		(CELL_NAME,		cell_name),
+	FSINFO_STRING		(DOMAIN_NAME,		domain_name),
+	FSINFO_STRING		(REALM_NAME,		realm_name),
+	FSINFO_STRING_N		(SERVER_NAME,		server_name),
+	FSINFO_STRUCT_NM	(SERVER_ADDRESS,	server_address),
+	FSINFO_STRING_NM	(PARAMETER,		parameter),
+	FSINFO_STRING_N		(SOURCE,		source),
+	FSINFO_STRING		(NAME_ENCODING,		name_encoding),
+	FSINFO_STRING		(NAME_CODEPAGE,		name_codepage),
+	FSINFO_STRUCT		(IO_SIZE,		io_size),
+};
+
+/**
+ * sys_fsinfo - System call to get filesystem information
+ * @dfd: Base directory to pathwalk from or fd referring to filesystem.
+ * @filename: Filesystem to query or NULL.
+ * @_params: Parameters to define request (or NULL for enhanced statfs).
+ * @_buffer: Result buffer.
+ * @buf_size: Size of result buffer.
+ *
+ * Get information on a filesystem.  The filesystem attribute to be queried is
+ * indicated by @_params->request, and some of the attributes can have multiple
+ * values, indexed by @_params->Nth and @_params->Mth.  If @_params is NULL,
+ * then the 0th fsinfo_attr_statfs attribute is queried.  If an attribute does
+ * not exist, EOPNOTSUPP is returned; if the Nth,Mth value does not exist,
+ * ENODATA is returned.
+ *
+ * On success, the size of the attribute's value is returned.  If @buf_size is
+ * 0 or @_buffer is NULL, only the size is returned.  If the size of the value
+ * is larger than @buf_size, it will be truncated by the copy.  If the size of
+ * the value is smaller than @buf_size then the excess buffer space will be
+ * cleared.  The full size of the value will be returned, irrespective of how
+ * much data is actually placed in the buffer.
+ */
+SYSCALL_DEFINE5(fsinfo,
+		int, dfd, const char __user *, filename,
+		struct fsinfo_params __user *, _params,
+		void __user *, _buffer, size_t, buf_size)
+{
+	struct fsinfo_params user_params;
+	struct fsinfo_kparams params;
+	size_t size, n;
+	int ret;
+
+	if (_params) {
+		if (copy_from_user(&user_params, _params, sizeof(user_params)))
+			return -EFAULT;
+		if (user_params.__reserved[0] ||
+		    user_params.__reserved[1] ||
+		    user_params.__reserved[2] ||
+		    user_params.__reserved[3] ||
+		    user_params.__reserved[4] ||
+		    user_params.__reserved[5])
+			return -EINVAL;
+		if (user_params.request >= FSINFO_ATTR__NR)
+			return -EOPNOTSUPP;
+		params.at_flags = user_params.at_flags;
+		params.request = user_params.request;
+		params.Nth = user_params.Nth;
+		params.Mth = user_params.Mth;
+	} else {
+		params.at_flags = 0;
+		params.request = FSINFO_ATTR_STATFS;
+		params.Nth = 0;
+		params.Mth = 0;
+	}
+
+	if (!_buffer || !buf_size) {
+		buf_size = 0;
+		_buffer = NULL;
+	}
+
+	/* Allocate an appropriately-sized buffer.  We will truncate the
+	 * contents when we write the contents back to userspace.
+	 */
+	size = fsinfo_buffer_sizes[params.request];
+	switch (size & 0xc000) {
+	case 0x0000:
+		if (params.Nth != 0)
+			return -ENODATA;
+		/* Fall through */
+	case 0x4000:
+		if (params.Mth != 0)
+			return -ENODATA;
+		/* Fall through */
+	case 0x8000:
+		break;
+	case 0xc000:
+		return -ENOBUFS;
+	}
+
+	size &= ~0xc000;
+	if (size == 0) {
+		params.string_val = true;
+		params.buf_size = 4096;
+	} else {
+		params.string_val = false;
+		params.buf_size = size;
+		if (buf_size == 0)
+			return size; /* We know how big the buffer should be */
+	}
+
+	/* We always allocate a buffer for a string, even if buf_size == 0 and
+	 * we're not going to return any data.  This means that the filesystem
+	 * code needn't care about whether the buffer actually exists or not.
+	 */
+	params.buffer = kzalloc(params.buf_size, GFP_KERNEL);
+	if (!params.buffer)
+		return -ENOMEM;
+
+	if (filename)
+		ret = vfs_fsinfo_path(dfd, filename, &params);
+	else
+		ret = vfs_fsinfo_fd(dfd, &params);
+	if (ret < 0)
+		goto error;
+
+	n = ret;
+	if (n > buf_size)
+		n = buf_size;
+
+	if (n > 0 && copy_to_user(_buffer, params.buffer, buf_size))
+		ret = -EFAULT;
+
+	/* Clear any part of the buffer that we won't fill if we're putting a
+	 * struct in there rather than a string.
+	 */
+	if (buf_size > n && !params.string_val &&
+	    clear_user(_buffer + n, buf_size - n) != 0)
+		return -EFAULT;
+error:
+	kfree(params.buffer);
+	return ret;
+}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3e661d033163..053d53861995 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -64,6 +64,8 @@ struct fscrypt_operations;
 struct fs_context;
 struct fsconfig_parser;
 struct fsconfig_param;
+struct fsinfo_kparams;
+enum fsinfo_attribute;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -1849,6 +1851,7 @@ struct super_operations {
 	int (*thaw_super) (struct super_block *);
 	int (*unfreeze_fs) (struct super_block *);
 	int (*statfs) (struct dentry *, struct kstatfs *);
+	int (*get_fsinfo) (struct dentry *, struct fsinfo_kparams *);
 	int (*remount_fs) (struct super_block *, int *, char *, size_t);
 	int (*reconfigure) (struct super_block *, struct fs_context *);
 	void (*umount_begin) (struct super_block *);
@@ -2226,6 +2229,7 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
 extern int vfs_statfs(const struct path *, struct kstatfs *);
 extern int user_statfs(const char __user *, struct kstatfs *);
 extern int fd_statfs(int, struct kstatfs *);
+extern int vfs_fsinfo(const struct path *, struct fsinfo_kparams *);
 extern int freeze_super(struct super_block *super);
 extern int thaw_super(struct super_block *super);
 extern bool our_mnt(struct vfsmount *mnt);
diff --git a/include/linux/fsinfo.h b/include/linux/fsinfo.h
new file mode 100644
index 000000000000..f2d25b898d48
--- /dev/null
+++ b/include/linux/fsinfo.h
@@ -0,0 +1,41 @@
+/* Filesystem information query
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_FSINFO_H
+#define _LINUX_FSINFO_H
+
+#include <uapi/linux/fsinfo.h>
+
+struct fsinfo_kparams {
+	__u32			at_flags;	/* AT_SYMLINK_NOFOLLOW and similar */
+	enum fsinfo_attribute	request;	/* What is being asking for */
+	__u32			Nth;		/* Instance of it (some may have multiple) */
+	__u32			Mth;		/* Subinstance */
+	bool			string_val;	/* T if variable-length string value */
+	void			*buffer;	/* Where to place the reply */
+	size_t			buf_size;	/* Size of the buffer */
+};
+
+extern int generic_fsinfo(struct dentry *, struct fsinfo_kparams *);
+
+static inline void fsinfo_set_cap(struct fsinfo_capabilities *c,
+				  enum fsinfo_capability cap)
+{
+	c->capabilities[cap / 8] |= 1 << (cap % 8);
+}
+
+static inline void fsinfo_clear_cap(struct fsinfo_capabilities *c,
+				    enum fsinfo_capability cap)
+{
+	c->capabilities[cap / 8] &= ~(1 << (cap % 8));
+}
+
+#endif /* _LINUX_FSINFO_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 701522957a12..bc7173c09f4d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -49,6 +49,7 @@ struct stat64;
 struct statfs;
 struct statfs64;
 struct statx;
+struct fsinfo_params;
 struct __sysctl_args;
 struct sysinfo;
 struct timespec;
@@ -909,6 +910,9 @@ 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);
 asmlinkage long sys_fspick(int dfd, const char __user *path, unsigned int flags);
+asmlinkage long sys_fsinfo(int dfd, const char __user *path,
+			   struct fsinfo_params __user *params,
+			   void __user *buffer, size_t buf_size);
 
 /*
  * Architecture-specific system calls
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
new file mode 100644
index 000000000000..ca82cfb5f38a
--- /dev/null
+++ b/include/uapi/linux/fsinfo.h
@@ -0,0 +1,235 @@
+/* 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) */
+	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) */
+	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 */
+	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;
+	__u32	__reserved[1];
+};
+
+/*
+ * 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];
+};
+
+/*
+ * 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 */
+};
+
+/*
+ * 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..5fb3dcccff26
--- /dev/null
+++ b/samples/statx/test-fsinfo.c
@@ -0,0 +1,546 @@
+/* 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 bool debug = 0;
+
+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(X,Y)	 [FSINFO_ATTR_##X] = 0x0000
+#define FSINFO_STRUCT(X,Y)	 [FSINFO_ATTR_##X] = sizeof(struct fsinfo_##Y)
+#define FSINFO_STRING_N(X,Y)	 [FSINFO_ATTR_##X] = 0x4000
+#define FSINFO_STRUCT_N(X,Y)	 [FSINFO_ATTR_##X] = 0x4000 | sizeof(struct fsinfo_##Y)
+#define FSINFO_STRUCT_NM(X,Y)	 [FSINFO_ATTR_##X] = 0x8000 | sizeof(struct fsinfo_##Y)
+#define FSINFO_STRING_NM(X,Y)	 [FSINFO_ATTR_##X] = 0x8000
+static const __u16 fsinfo_buffer_sizes[FSINFO_ATTR__NR] = {
+	FSINFO_STRUCT		(STATFS,		statfs),
+	FSINFO_STRUCT		(FSINFO,		fsinfo),
+	FSINFO_STRUCT		(IDS,			ids),
+	FSINFO_STRUCT		(LIMITS,		limits),
+	FSINFO_STRUCT		(CAPABILITIES,		capabilities),
+	FSINFO_STRUCT		(SUPPORTS,		supports),
+	FSINFO_STRUCT		(TIMESTAMP_INFO,	timestamp_info),
+	FSINFO_STRING		(VOLUME_ID,		volume_id),
+	FSINFO_STRUCT		(VOLUME_UUID,		volume_uuid),
+	FSINFO_STRING		(VOLUME_NAME,		volume_name),
+	FSINFO_STRING		(CELL_NAME,		cell_name),
+	FSINFO_STRING		(DOMAIN_NAME,		domain_name),
+	FSINFO_STRING		(REALM_NAME,		realm_name),
+	FSINFO_STRING_N		(SERVER_NAME,		server_name),
+	FSINFO_STRUCT_NM	(SERVER_ADDRESS,	server_address),
+	FSINFO_STRING_NM	(PARAMETER,		parameter),
+	FSINFO_STRING_N		(SOURCE,		source),
+	FSINFO_STRING		(NAME_ENCODING,		name_encoding),
+	FSINFO_STRING		(NAME_CODEPAGE,		name_codepage),
+	FSINFO_STRUCT		(IO_SIZE,		io_size),
+};
+
+#define FSINFO_NAME(X,Y) [FSINFO_ATTR_##X] = #Y
+static const char *fsinfo_attr_names[FSINFO_ATTR__NR] = {
+	FSINFO_NAME		(STATFS,		statfs),
+	FSINFO_NAME		(FSINFO,		fsinfo),
+	FSINFO_NAME		(IDS,			ids),
+	FSINFO_NAME		(LIMITS,		limits),
+	FSINFO_NAME		(CAPABILITIES,		capabilities),
+	FSINFO_NAME		(SUPPORTS,		supports),
+	FSINFO_NAME		(TIMESTAMP_INFO,	timestamp_info),
+	FSINFO_NAME		(VOLUME_ID,		volume_id),
+	FSINFO_NAME		(VOLUME_UUID,		volume_uuid),
+	FSINFO_NAME		(VOLUME_NAME,		volume_name),
+	FSINFO_NAME		(CELL_NAME,		cell_name),
+	FSINFO_NAME		(DOMAIN_NAME,		domain_name),
+	FSINFO_NAME		(REALM_NAME,		realm_name),
+	FSINFO_NAME		(SERVER_NAME,		server_name),
+	FSINFO_NAME		(SERVER_ADDRESS,	server_address),
+	FSINFO_NAME		(PARAMETER,		parameter),
+	FSINFO_NAME		(SOURCE,		source),
+	FSINFO_NAME		(NAME_ENCODING,		name_encoding),
+	FSINFO_NAME		(NAME_CODEPAGE,		name_codepage),
+	FSINFO_NAME		(IO_SIZE,		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 & 0x3fff;
+	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;
+	__u16 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);
+	}
+
+	if (debug)
+		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 & 0xc000) {
+			case 0x0000:
+				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 0x4000:
+				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 & 0xc000) {
+	case 0x0000:
+		printf("\e[33m%s\e[m: ",
+		       fsinfo_attr_names[params->request]);
+		break;
+	case 0x4000:
+		printf("\e[33m%s[%u]\e[m: ",
+		       fsinfo_attr_names[params->request],
+		       params->Nth);
+		break;
+	case 0x8000:
+		printf("\e[33m%s[%u][%u]\e[m: ",
+		       fsinfo_attr_names[params->request],
+		       params->Nth, params->Mth);
+		break;
+	}
+
+	switch (about) {
+		/* Struct */
+	case 0x0001 ... 0x3fff:
+	case 0x4001 ... 0x7fff:
+	case 0x8001 ... 0xbfff:
+		dump_fsinfo(params->request, about, &r, ret);
+		return 0;
+
+		/* String */
+	case 0x0000:
+	case 0x4000:
+	case 0x8000:
+		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, "adlr"))) {
+		switch (opt) {
+		case 'a':
+			params.at_flags |= AT_NO_AUTOMOUNT;
+			continue;
+		case 'd':
+			debug = true;
+			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], &params, 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 related

* Re: [PATCH 1/5] vfs: syscall: Add fsinfo() to query filesystem information
From: Theodore Y. Ts'o @ 2018-08-01 16:28 UTC (permalink / raw)
  To: David Howells; +Cc: viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <153314004147.18964.11925284995448007945.stgit@warthog.procyon.org.uk>

On Wed, Aug 01, 2018 at 05:14:01PM +0100, David Howells wrote:
> 
> 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.

How does the caller know whether or not a particular attribute has
multiple values?  Is that a fundamental attribute of a particular
attribute?  (e.g., the documentation for each attribute must state
whether or not that attribute returns multiple attributes or not)

	       	    	      	      - Ted

^ permalink raw reply

* Re: [PATCH 1/5] vfs: syscall: Add fsinfo() to query filesystem information
From: David Howells @ 2018-08-01 22:20 UTC (permalink / raw)
  To: Theodore Y. Ts'o
  Cc: dhowells, viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <20180801162849.GA9187@thunk.org>

Theodore Y. Ts'o <tytso@mit.edu> wrote:

> > 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.
> 
> How does the caller know whether or not a particular attribute has
> multiple values?

It's fundamental to each separate attribute.  I have an attribute that gives
information actually on fsinfo() itself; I could add another that allows you
to enumerate the attribute type table.  Then you'd be able to query the basic
type (string/struct), the size (struct only) and whether it's 0D, 1D or 2D.

> Is that a fundamental attribute of a particular attribute?  (e.g., the
> documentation for each attribute must state whether or not that attribute
> returns multiple attributes or not)

All the values of an attribute must have the same type of value, and if
they're structure types rather than string types, the values will all have the
same size and be based on the same structure.

Note that a struct attribute FSINFO_ATTR_XXX has a struct fsinfo_xxx
associated with it that defines its value, e.g.:

      FSINFO_ATTR_TIMESTAMP_INFO
              This retrieves information about what timestamp  resolution  and
              scope  is  supported  by a filesystem for each of the file time‐
              stamps.  The following structure is filled in:

                  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];
                  };

Note that I have a manual page on this (see attached).

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 2D 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
.IR params->Nth and params->Mth
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
"source=/dev/sda1"
"data=journal"
"noquota"
.fi
.in
.RE
.IP
where the first parameters correspond on a 1-to-1 basis by
.I params->Nth
with the parameters defined by
.IR FSINFO_ATTR_PARAM_SPECIFICATION .
Additional parameters may also be presented.  Further, any particular parameter
may have multiple values (multiple sources for example).  These can be
enumerated with params->Mth.

.\" __________________ 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 dio_size_gran;
    __u32 dio_mem_align;
};
.fi
.in
.RE
.IP
Where
.I dio_size_gran
indicate the fundamental I/O block size that the size of O_DIRECT read/write
calls must be a multiple of and
.I dio_mem_align
indicates the memory alignment requirements of the data buffer in any O_DIRECT
read/write call.
.IP
Note that any of these may be zero if inapplicable or indeterminable.

.\" __________________ FSINFO_ATTR_PARAM_DESCRIPTION __________________
.TP
.B FSINFO_ATTR_PARAM_DESCRIPTION
This retrieves basic information about the superblock configuration parameters
used by the filesystem.  The value returned is of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_description {
    __u32 nr_params;		/* Number of individual parameters */
    __u32 nr_names;		/* Number of parameter names */
    __u32 nr_enum_names;		/* Number of enum names  */
};
.fi
.in
.RE
.IP
Where
.I nr_params indicates the number of described parameters (it's possible for
the configuration to take more than this - cgroup-v1 for example);
.I nr_names
indicates the number of parameter names that there are defined (nr_names can be
more than nr_params if there are synonyms); and
.I nr_enum_names
indicates the number of enum value names that there are defined.

.\" __________________ FSINFO_ATTR_PARAM_SPECIFICATION __________________
.TP
.B FSINFO_ATTR_PARAM_SPECIFICATION
This retrieves information about the Nth superblock configuration parameter
available in the filesystem.  This is enumerated by incrementing
.I params->Nth
each time.  Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_specification {
	__u32		type;
	__u32		flags;
};
.fi
.in
.RE
.IP
Where
.I type
indicates the type of value by way of one of the following constants:
.PP
.RS
.in +4n
.nf
  FSINFO_PARAM_SPEC_NOT_DEFINED
  FSINFO_PARAM_SPEC_TAKES_NO_VALUE
  FSINFO_PARAM_SPEC_IS_BOOL
  FSINFO_PARAM_SPEC_IS_U32
  FSINFO_PARAM_SPEC_IS_U32_OCTAL
  FSINFO_PARAM_SPEC_IS_U32_HEX
  FSINFO_PARAM_SPEC_IS_S32
  FSINFO_PARAM_SPEC_IS_ENUM
  FSINFO_PARAM_SPEC_IS_STRING
  FSINFO_PARAM_SPEC_IS_BLOB
  FSINFO_PARAM_SPEC_IS_BLOCKDEV
  FSINFO_PARAM_SPEC_IS_PATH
  FSINFO_PARAM_SPEC_IS_FD
.fi
.in
.RE
.IP
depending on whether the kernel (incorrectly) didn't define the type, the
parameter takes no value, or takes a bool, one of a number of integers, a named
enum value, a string, a binary blob, a blockdev, an arbitrary path or a file
descriptor.
.PP
.I flags
qualifies the form of the value accepted:
.PP
.RS
.in +4n
.nf
  FSINFO_PARAM_SPEC_VALUE_IS_OPTIONAL
  FSINFO_PARAM_SPEC_PREFIX_NO_IS_NEG
  FSINFO_PARAM_SPEC_EMPTY_STRING_IS_NEG
  FSINFO_PARAM_SPEC_DEPRECATED
.fi
.in
.RE
.IP
These indicate whether the value is optional, the parameter can be made
negative by prefixing with 'no' or giving it an empty value or whether the
parameter is deprecated and a warning issued if it used.

.\" __________________ FSINFO_ATTR_PARAM_NAME __________________
.TP
.B FSINFO_ATTR_PARAM_NAME
This retrieves information about the Nth superblock configuration parameter
available in the filesystem.  This is enumerated by incrementing
.I params->Nth
each time.  Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_name {
	__u32		param_index;
	char		name[252];
};
.fi
.in
.RE
.IP
Where
.I param_index
is refers to the Nth parameter returned by FSINFO_ATTR_PARAM_SPECIFICATION and
.I name
is a name string that maps to the specified parameter.

.\" __________________ FSINFO_ATTR_PARAM_ENUMs __________________
.TP
.B FSINFO_ATTR_PARAM_ENUM
This can be used to list all the enum value symbols available for all the
configuration parameters available in the filesystem.  This is enumerated by
incrementing
.I params->Nth
each time.  Each value is a structure of the following type:
.PP
.RS
.in +4n
.nf
struct fsinfo_param_enum {
	__u32		param_index;	/* Index of the relevant parameter specification */
	char		name[252];	/* Name of the enum value */
};
.fi
.in
.RE
.IP
Where
.I param_index
indicates the enumeration-type parameter to which this value corresponds and
.I name
is the symbolic name.  Note that all the enum values from all the enum
parameters are in one list together


.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_* for names __________________
.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

* [PATCH 1/1] selftest/net: fix protocol family to work for IPv4.
From: Maninder Singh @ 2018-08-02  9:57 UTC (permalink / raw)
  To: davem, shuahkh
  Cc: netdev, linux-api, linux-kernel, edumazet, pankaj.m, a.sahrawat,
	Maninder Singh, Vaneet Narang
In-Reply-To: <CGME20180802100201epcas5p390bb362ccabaa99087eff88af37bffb2@epcas5p3.samsung.com>

use actual protocol family passed by user rather than hardcoded
AF_INTE6 to cerate sockets.
current code is not working for IPv4.

Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
Signed-off-by: Vaneet Narang <v.narang@samsung.com>
---
 tools/testing/selftests/net/tcp_mmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index 77f7627..e8c5dff 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -402,7 +402,7 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
-	fd = socket(AF_INET6, SOCK_STREAM, 0);
+	fd = socket(cfg_family, SOCK_STREAM, 0);
 	if (fd == -1) {
 		perror("socket");
 		exit(1);
-- 
1.9.1

^ permalink raw reply related

* [PATCH 1/1] selftest/net: fix FILE_SIZE for 32 bit architecture.
From: Maninder Singh @ 2018-08-02 10:31 UTC (permalink / raw)
  To: davem, shuahkh
  Cc: netdev, linux-api, linux-kernel, edumazet, pankaj.m, a.sahrawat,
	Maninder Singh, Vaneet Narang
In-Reply-To: <CGME20180802103616epcas5p48ec1e2ea3568b11683aa7b55254dffb0@epcas5p4.samsung.com>

FILE_SZ is defined as (1UL << 35), it will overflow
for 32 bit system and logic will break.

Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
Signed-off-by: Vaneet Narang <v.narang@samsung.com>
---
 tools/testing/selftests/net/tcp_mmap.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index e8c5dff..1d6ca12 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -85,7 +85,7 @@
 #define MSG_ZEROCOPY    0x4000000
 #endif
 
-#define FILE_SZ (1UL << 35)
+#define FILE_SZ (1ULL << 35)
 static int cfg_family = AF_INET6;
 static socklen_t cfg_alen = sizeof(struct sockaddr_in6);
 static int cfg_port = 8787;
@@ -134,7 +134,7 @@ void hash_zone(void *zone, unsigned int length)
 
 void *child_thread(void *arg)
 {
-	unsigned long total_mmap = 0, total = 0;
+	unsigned long long total_mmap = 0, total = 0;
 	struct tcp_zerocopy_receive zc;
 	unsigned long delta_usec;
 	int flags = MAP_SHARED;
@@ -316,7 +316,7 @@ int main(int argc, char *argv[])
 {
 	struct sockaddr_storage listenaddr, addr;
 	unsigned int max_pacing_rate = 0;
-	unsigned long total = 0;
+	unsigned long long total = 0;
 	char *host = NULL;
 	int fd, c, on = 1;
 	char *buffer;
@@ -431,7 +431,7 @@ int main(int argc, char *argv[])
 		zflg = 0;
 	}
 	while (total < FILE_SZ) {
-		long wr = FILE_SZ - total;
+		unsigned long long wr = FILE_SZ - total;
 
 		if (wr > chunk_size)
 			wr = chunk_size;
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH 1/1] selftest/net: fix protocol family to work for IPv4.
From: Eric Dumazet @ 2018-08-02 13:03 UTC (permalink / raw)
  To: Maninder Singh, davem, shuahkh
  Cc: netdev, linux-api, linux-kernel, edumazet, pankaj.m, a.sahrawat,
	Vaneet Narang
In-Reply-To: <20180802100201epcas5p390bb362ccabaa99087eff88af37bffb2~HCQZjTEZd0101301013epcas5p3C@epcas5p3.samsung.com>



On 08/02/2018 02:57 AM, Maninder Singh wrote:
> use actual protocol family passed by user rather than hardcoded
> AF_INTE6 to cerate sockets.
> current code is not working for IPv4.
> 
> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> Signed-off-by: Vaneet Narang <v.narang@samsung.com>
> ---
>  tools/testing/selftests/net/tcp_mmap.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
> index 77f7627..e8c5dff 100644
> --- a/tools/testing/selftests/net/tcp_mmap.c
> +++ b/tools/testing/selftests/net/tcp_mmap.c
> @@ -402,7 +402,7 @@ int main(int argc, char *argv[])
>  		exit(1);
>  	}
>  
> -	fd = socket(AF_INET6, SOCK_STREAM, 0);
> +	fd = socket(cfg_family, SOCK_STREAM, 0);
>  	if (fd == -1) {
>  		perror("socket");
>  		exit(1);
> 


Darn, someone spotted my hidden attempt to kill IPv4 ;)

Reviewed-by: Eric Dumazet <edumazet@google.com>

^ permalink raw reply

* Re: [PATCH 1/1] selftest/net: fix FILE_SIZE for 32 bit architecture.
From: Eric Dumazet @ 2018-08-02 13:08 UTC (permalink / raw)
  To: Maninder Singh, davem, shuahkh
  Cc: netdev, linux-api, linux-kernel, edumazet, pankaj.m, a.sahrawat,
	Vaneet Narang
In-Reply-To: <20180802103616epcas5p48ec1e2ea3568b11683aa7b55254dffb0~HCuTv0d7l2131721317epcas5p4u@epcas5p4.samsung.com>



On 08/02/2018 03:31 AM, Maninder Singh wrote:
> FILE_SZ is defined as (1UL << 35), it will overflow
> for 32 bit system and logic will break.
> 
> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> Signed-off-by: Vaneet Narang <v.narang@samsung.com>
> ---
>  tools/testing/selftests/net/tcp_mmap.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
> index e8c5dff..1d6ca12 100644
> --- a/tools/testing/selftests/net/tcp_mmap.c
> +++ b/tools/testing/selftests/net/tcp_mmap.c
> @@ -85,7 +85,7 @@
>  #define MSG_ZEROCOPY    0x4000000
>  #endif
>  
> -#define FILE_SZ (1UL << 35)
> +#define FILE_SZ (1ULL << 35)
>  static int cfg_family = AF_INET6;
>  static socklen_t cfg_alen = sizeof(struct sockaddr_in6);
>  static int cfg_port = 8787;
> @@ -134,7 +134,7 @@ void hash_zone(void *zone, unsigned int length)
>  
>  void *child_thread(void *arg)
>  {
> -	unsigned long total_mmap = 0, total = 0;
> +	unsigned long long total_mmap = 0, total = 0;
>  	struct tcp_zerocopy_receive zc;
>  	unsigned long delta_usec;
>  	int flags = MAP_SHARED;
> @@ -316,7 +316,7 @@ int main(int argc, char *argv[])
>  {
>  	struct sockaddr_storage listenaddr, addr;
>  	unsigned int max_pacing_rate = 0;
> -	unsigned long total = 0;
> +	unsigned long long total = 0;
>  	char *host = NULL;
>  	int fd, c, on = 1;
>  	char *buffer;
> @@ -431,7 +431,7 @@ int main(int argc, char *argv[])
>  		zflg = 0;
>  	}
>  	while (total < FILE_SZ) {
> -		long wr = FILE_SZ - total;
> +		unsigned long long wr = FILE_SZ - total;
>  
>  		if (wr > chunk_size)
>  			wr = chunk_size;
> 

What about using more conventional size_t instead of "unsigned long long" ?

^ permalink raw reply

* Re: [PATCH 1/1] selftest/net: fix protocol family to work for IPv4.
From: David Miller @ 2018-08-02 17:30 UTC (permalink / raw)
  To: maninder1.s
  Cc: shuahkh, netdev, linux-api, linux-kernel, edumazet, pankaj.m,
	a.sahrawat, v.narang
In-Reply-To: <20180802100201epcas5p390bb362ccabaa99087eff88af37bffb2~HCQZjTEZd0101301013epcas5p3C@epcas5p3.samsung.com>

From: Maninder Singh <maninder1.s@samsung.com>
Date: Thu,  2 Aug 2018 15:27:27 +0530

> use actual protocol family passed by user rather than hardcoded
> AF_INTE6 to cerate sockets.
> current code is not working for IPv4.
> 
> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> Signed-off-by: Vaneet Narang <v.narang@samsung.com>

Applied.

^ permalink raw reply

* Re: [PATCH 01/33] vfs: syscall: Add open_tree(2) to reference or clone a mount [ver #11]
From: Alan Jenkins @ 2018-08-02 17:31 UTC (permalink / raw)
  To: David Howells, viro; +Cc: linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <153313705165.13253.4602180607294286849.stgit@warthog.procyon.org.uk>

Hi

I found this interesting, though I don't entirely follow the kernel 
mount/unmount code.  I had one puzzle about the code, and two questions 
which I was largely able to answer.

On 01/08/18 16:24, David Howells wrote:
> +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();
> +}

Can I ask why  UMOUNT_SYNC is used here?  I feel like I must have missed 
something, but doesn't it skip the IS_MNT_LOCKED() check in 
disconnect_mount() ?

UMOUNT_SYNC seems used for non-lazy unmounts, and in internal cleanups 
where userspace wouldn't be able to see.  But I think userspace can keep 
watching in this case, e.g. by `fd2 = openat(fd, ".", O_PATH)` (or `fd2 
= open_tree(fd, ".", 0)` ?).  I would think this function should avoid 
using UMOUNT_SYNC, like lazy unmount avoids it.

> 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.

One of the limitations documented for `mount --bind`, is that `mount -o 
bind,ro` is not atomic.  There's a workaround if you need it, it's just 
a bit clunky.  I wondered if it was possible to improve `mount` by 
changing the mount flags between OPEN_TREE_CLONE and move_mount().

     fd = open_tree(..., OPEN_TREE_CLONE);
     fchdir(fd);
     mount(NULL, ".", NULL, MS_REMOUNT | MS_BIND | newbindflags, NULL);
     move_mount(fd, ...);

Another closely-related limitation of `mount`, is that it can't 
atomically set the propagation type at mount time.

My conclusion was the above doesn't quite work yet.  do_remount() still 
uses check_mnt(), so it doesn't accept detached mounts.  I wonder if it 
can be changed in future.

>    The detached tree will be
> dissolved on the final close of obtained file.

My last question turned out very dull, feel free to ignore.

It seems the only way to use MNT_FORCE[1], is to first attach the 
filesystem somewhere (and close the file descriptor).  I wondered if 
there was a way to make things more regular.  close_and_umount() feels 
too obscure to live though...

[1] "Ask the filesystem to abort pending requests before attempting 
theunmount. This may allow the unmount to complete without waitingfor an 
inaccessible server. If, after aborting requests, someprocesses still 
have active references to the filesystem, theunmount will still fail."

...and I suppose it's much less useful than I thought.  The point of 
MNT_FORCE is to kick out processes that were blocked _trying to access a 
file by name_, e.g. open() or stat().  But if we're considering a 
detached mount, then it's impossible to access it by name alone.  You 
need an fd (or cwd or root), which would stop the filesystem being 
unmounted anyway. close_and_umount(fd, MNT_FORCE) is pointless unless 
your process has other threads accessing the filesystem through the same 
fd, but that's a really bad idea anyway.

It could prevent someone else getting stuck indefinitely on 
/proc/$PID/fd, but that's still very obscure.

Regards
Alan

^ permalink raw reply

* Re: [PATCH 1/1] selftest/net: fix protocol family to work for IPv4.
From: David Miller @ 2018-08-02 17:35 UTC (permalink / raw)
  To: maninder1.s
  Cc: shuahkh, netdev, linux-api, linux-kernel, edumazet, pankaj.m,
	a.sahrawat, v.narang
In-Reply-To: <20180802100201epcas5p390bb362ccabaa99087eff88af37bffb2~HCQZjTEZd0101301013epcas5p3C@epcas5p3.samsung.com>

From: Maninder Singh <maninder1.s@samsung.com>
Date: Thu,  2 Aug 2018 15:27:27 +0530

> use actual protocol family passed by user rather than hardcoded
> AF_INTE6 to cerate sockets.
> current code is not working for IPv4.
> 
> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> Signed-off-by: Vaneet Narang <v.narang@samsung.com>

Applied.

^ permalink raw reply

* Re: [PATCH 04/24] 32-bit userspace ABI: introduce ARCH_32BIT_OFF_T config option
From: Palmer Dabbelt @ 2018-08-02 18:30 UTC (permalink / raw)
  To: ynorov
  Cc: Arnd Bergmann, linux-doc, szabolcs.nagy, catalin.marinas,
	heiko.carstens, philipp.tomsich, joseph, linux-arch, sellcey,
	Prasun.Kapoor, schwab, agraf, geert, bamv2005, Dave.Martin,
	kilobyte, manuel.montezelo, james.hogan, cmetcalf, pinskia,
	linyongting, klimov.linux, broonie, linux-arm-kernel,
	maxim.kuvyrkov, fweimer, linux-api, Nathan_Lynch, linux-kernel,
	james.morse, ramana.gcc, schwi
In-Reply-To: <20180625061950.GB23998@yury-thinkpad>

On Sun, 24 Jun 2018 23:19:50 PDT (-0700), ynorov@caviumnetworks.com wrote:
> On Mon, Jun 11, 2018 at 02:27:36PM +0300, Yury Norov wrote:
>> On Mon, Jun 11, 2018 at 09:48:02AM +0200, Arnd Bergmann wrote:
>> > On Sat, Jun 9, 2018 at 9:42 AM, Yury Norov <ynorov@caviumnetworks.com> wrote:
>> > > On Fri, Jun 08, 2018 at 06:32:07PM +0100, Catalin Marinas wrote:
>> > >> On Wed, May 16, 2018 at 11:18:49AM +0300, Yury Norov wrote:
>> > >> > diff --git a/arch/Kconfig b/arch/Kconfig
>> > >> > index 76c0b54443b1..ee079244dc3c 100644
>> > >> > --- a/arch/Kconfig
>> > >> > +++ b/arch/Kconfig
>> > >> > @@ -264,6 +264,21 @@ config ARCH_THREAD_STACK_ALLOCATOR
>> > >> >  config ARCH_WANTS_DYNAMIC_TASK_STRUCT
>> > >> >     bool
>> > >> >
>> > >> > +config ARCH_32BIT_OFF_T
>> > >> > +   bool
>> > >> > +   depends on !64BIT
>> > >> > +   help
>> > >> > +     All new 32-bit architectures should have 64-bit off_t type on
>> > >> > +     userspace side which corresponds to the loff_t kernel type. This
>> > >> > +     is the requirement for modern ABIs. Some existing architectures
>> > >> > +     already have 32-bit off_t. This option is enabled for all such
>> > >> > +     architectures explicitly. Namely: arc, arm, blackfin, cris, frv,
>> > >> > +     h8300, hexagon, m32r, m68k, metag, microblaze, mips32, mn10300,
>> > >> > +     nios2, openrisc, parisc32, powerpc32, score, sh, sparc, tile32,
>> > >> > +     unicore32, x86_32 and xtensa. This is the complete list. Any
>> > >> > +     new 32-bit architecture should declare 64-bit off_t type on user
>> > >> > +     side and so should not enable this option.
>> > >>
>> > >> Do you know if this is the case for riscv and nds32, merged in the
>> > >> meantime? If not, I suggest you drop this patch altogether and just
>> > >> define force_o_largefile() for arm64/ilp32 as we don't seem to stick to
>> > >> "all new 32-bit architectures should have 64-bit off_t".
>> > >
>> > > I wrote this patch at request of Arnd Bergmann. This is actually his
>> > > words that all new 32-bit architectures should have 64-bit off_t. So
>> > > I was surprized when riscv was merged with 32-bit off_t (and I didn't
>> > > follow nds32).
>> > >
>> > > If this rule is still in force, we'd better add new exceptions to this
>> > > patch. Otherwise, we can drop it.
>> > >
>> > > Arnd, could you please comment it?
>> >
>> > I completely forgot about it and had assumed that it was merged long
>> > ago, sorry about that.
>>
>> Hi Arnd,
>>
>> There are 3 patches like this in ILP32 series that change ABI for new
>> targets. I've submitted them in separated series:
>> https://lkml.org/lkml/2017/9/25/574
>>
>> They all seems to be acked by you. If you ready to upstream the
>> series, I can rebase it and add riscv32 and nds32 exceptions.
>>
>> If Palmer and riscv people will decide to follow new rules, we can
>> easily drop the exception.
>
> Ping?

Sorry to be a bit slow, but we just decided to skip this current glibc release 
for rv32i and instead focus on getting the 32-bit ABI nice and clean for the 
next release.  Thus we in RISC-V land are OK with taking these changes to the 
32-bit kernel ABI.

^ permalink raw reply

* Re: [PATCH 01/33] vfs: syscall: Add open_tree(2) to reference or clone a mount [ver #11]
From: Al Viro @ 2018-08-02 21:29 UTC (permalink / raw)
  To: Alan Jenkins
  Cc: David Howells, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <7a292a44-7e17-572b-2d1e-0085ef400010@gmail.com>

On Thu, Aug 02, 2018 at 06:31:06PM +0100, Alan Jenkins wrote:
> Hi
> 
> I found this interesting, though I don't entirely follow the kernel
> mount/unmount code.  I had one puzzle about the code, and two questions
> which I was largely able to answer.
> 
> On 01/08/18 16:24, David Howells wrote:
> > +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();
> > +}
> 
> Can I ask why  UMOUNT_SYNC is used here?  I feel like I must have missed
> something, but doesn't it skip the IS_MNT_LOCKED() check in
> disconnect_mount() ?
> 
> UMOUNT_SYNC seems used for non-lazy unmounts, and in internal cleanups where
> userspace wouldn't be able to see.  But I think userspace can keep watching
> in this case, e.g. by `fd2 = openat(fd, ".", O_PATH)` (or `fd2 =
> open_tree(fd, ".", 0)` ?).  I would think this function should avoid using
> UMOUNT_SYNC, like lazy unmount avoids it.

FWIW, I suspect that UMOUNT_CONNECTED might be the right thing here...

^ permalink raw reply

* Re: [PATCH 01/33] vfs: syscall: Add open_tree(2) to reference or clone a mount [ver #11]
From: David Howells @ 2018-08-02 21:51 UTC (permalink / raw)
  To: Alan Jenkins
  Cc: dhowells, viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <7a292a44-7e17-572b-2d1e-0085ef400010@gmail.com>

Alan Jenkins <alan.christopher.jenkins@gmail.com> wrote:

> Another closely-related limitation of `mount`, is that it can't atomically set
> the propagation type at mount time.

I want to add a mount_setattr() too at some point:

	fd = open_tree(..., OPEN_TREE_CLONE);
	mount_setattr(fd, ...);
	move_mount(fd, ...);

I'm not sure whether you should be able to fchdir into the cloned tree since
it isn't attached to any mount namespace.

David

^ permalink raw reply

* Re: [PATCH 01/33] vfs: syscall: Add open_tree(2) to reference or clone a mount [ver #11]
From: Alan Jenkins @ 2018-08-02 23:46 UTC (permalink / raw)
  To: David Howells; +Cc: viro, linux-api, torvalds, linux-fsdevel, linux-kernel
In-Reply-To: <21311.1533246718@warthog.procyon.org.uk>

On 02/08/18 22:51, David Howells wrote:
> Alan Jenkins <alan.christopher.jenkins@gmail.com> wrote:
>
>> Another closely-related limitation of `mount`, is that it can't atomically set
>> the propagation type at mount time.
> I want to add a mount_setattr() too at some point:
>
> 	fd = open_tree(..., OPEN_TREE_CLONE);
> 	mount_setattr(fd, ...);
> 	move_mount(fd, ...);

Cool.  Not having to mess with fchdir() sounds nice.  (And as a bonus, 
being able to avoid the existing multiplexed mount() call, which looks 
ugly from all the NULL arguments if nothing else).

> I'm not sure whether you should be able to fchdir into the cloned tree since
> it isn't attached to any mount namespace.
>
> David

I don't see a check prohibiting it :-). I don't think it's a problem.

You can already chdir/chroot into a different mount namespace, you just 
can't do any mount operations on it.  (You said you want to be able to, 
but so far move_mount() still prohibits it, I guess that's for the future).

And you can already do the same into a mount that has been detached, 
which will have `mount->mnt_ns = NULL` if I'm reading correctly.

Hmm, there is something that's been nagging at me though.  I'm 
suspicious about what happens in this series, when you move_mount() from 
a victim of MNT_DETACH.  I think umount2(MNT_DETACH) sets a flag 
MNT_UMOUNT.  It's a flag that was added to help correctly handle 
MNT_LOCKED in the face of umount2(MNT_DETACH).  It's also the point 
where my understanding of the kernel mount/unmount code breaks down 
:-).  But it seems to override both IS_MNT_LOCKED() and UMOUNT_CONNECTED 
in disconnect_mount().  That would give another chance to defeat locked 
mounts.

Regards
Alan

^ permalink raw reply

* RE: [PATCH 1/1] selftest/net: fix FILE_SIZE for 32 bit architecture.
From: Maninder Singh @ 2018-08-03  3:31 UTC (permalink / raw)
  To: Eric Dumazet, davem@davemloft.net, shuahkh@osg.samsung.com
  Cc: netdev@vger.kernel.org, linux-api@vger.kernel.org,
	linux-kernel@vger.kernel.org, edumazet@google.com, PANKAJ MISHRA,
	AMIT SAHRAWAT, Vaneet Narang
In-Reply-To: <4a412194-99aa-7969-54a4-727368fbf82c@gmail.com>

Hi,

>On 08/02/2018 03:31 AM, Maninder Singh wrote:
>> FILE_SZ is defined as (1UL << 35), it will overflow
>> for 32 bit system and logic will break.
>> 
>> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
>> Signed-off-by: Vaneet Narang <v.narang@samsung.com>
>> ---
>>  tools/testing/selftests/net/tcp_mmap.c | 8 ++++----
>>  1 file changed, 4 insertions(+), 4 deletions(-)
>> 
>> diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
>> index e8c5dff..1d6ca12 100644
>> --- a/tools/testing/selftests/net/tcp_mmap.c
>> +++ b/tools/testing/selftests/net/tcp_mmap.c
>> @@ -85,7 +85,7 @@
>>  #define MSG_ZEROCOPY    0x4000000
>>  #endif
>>  
>> -#define FILE_SZ (1UL << 35)
>> +#define FILE_SZ (1ULL << 35)

...
...
>> @@ -431,7 +431,7 @@ int main(int argc, char *argv[])
>>                  zflg = 0;
>>          }
>>          while (total < FILE_SZ) {
>> -                long wr = FILE_SZ - total;
>> +                unsigned long long wr = FILE_SZ - total;
>>  
>>                  if (wr > chunk_size)
>>                          wr = chunk_size;
>> 
> 
>What about using more conventional size_t instead of "unsigned long long" ?

size_t is also equivalent to unsigned long and it will not hold value of (1 << 35) for 32 bit system.
So we can do two things.

(1) reduce FILE SIZE to (1 << 30), so that UL (size_t) can hold this value.
It will not show any perofrmance boost with ZEROCOPY. (checked on x86_64)

(2) use unsigned long long to work with both 32 and 64 bit system.
It will show performance boost with ZEROCOPY.(checked on x86_64)

What do you think?

Thanks and regards,
Maninder Singh

^ permalink raw reply

* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Jürg Billeter @ 2018-08-03 10:15 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Andrew Morton, Thomas Gleixner, Eric Biederman, linux-api,
	linux-kernel
In-Reply-To: <20180801141914.GA21248@redhat.com>

On Wed, 2018-08-01 at 16:19 +0200, Oleg Nesterov wrote:
> 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.

This would definitely be an option. You mentioned it last October in
the PR_SET_PDEATHSIG_PROC discussion¹. However, as PID namespaces
already exist and appear to be a good fit for the most part, I think it
makes sense to just add the missing pieces to PID namespaces instead of
duplicating part of the PID namespace functionality.

Also, based on Eric's comment in that other discussion about
no_new_privs not being allowed to increase the attack surface,
PR_KILL_MY_DESCENDANTS_ON_EXIT might require CAP_SYS_ADMIN as well (due
to setuid children). In which case the only potential benefit would be
that it still allows the child to kill arbitrary processes, as far as I
can tell.

> > 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 ;)

The overall goal is increasing isolation between (some) child processes
and the rest of the system. Isolation from runaway processes and
isolation from signals are independent aspects and it could be useful
to control them independently. However, I also expect it to be common
that both are wanted at the same time.

Jürg

¹ https://lkml.org/lkml/2017/10/5/546

^ permalink raw reply

* RE: [PATCH 1/1] selftest/net: fix FILE_SIZE for 32 bit architecture.
From: David Laight @ 2018-08-03 11:12 UTC (permalink / raw)
  To: 'maninder1.s@samsung.com', Eric Dumazet,
	davem@davemloft.net, shuahkh@osg.samsung.com
  Cc: netdev@vger.kernel.org, linux-api@vger.kernel.org,
	linux-kernel@vger.kernel.org, edumazet@google.com, PANKAJ MISHRA,
	AMIT SAHRAWAT, Vaneet Narang
In-Reply-To: <20180803033159epcms5p1dd2f5a6834268e9a22efa15421795fa0@epcms5p1>

From: Maninder Singh
> Sent: 03 August 2018 04:32
> >On 08/02/2018 03:31 AM, Maninder Singh wrote:
> >> FILE_SZ is defined as (1UL << 35), it will overflow
> >> for 32 bit system and logic will break.
> >>
> >> Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
> >> Signed-off-by: Vaneet Narang <v.narang@samsung.com>
> >> ---
> >>  tools/testing/selftests/net/tcp_mmap.c | 8 ++++----
> >>  1 file changed, 4 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
> >> index e8c5dff..1d6ca12 100644
> >> --- a/tools/testing/selftests/net/tcp_mmap.c
> >> +++ b/tools/testing/selftests/net/tcp_mmap.c
> >> @@ -85,7 +85,7 @@
> >>  #define MSG_ZEROCOPY    0x4000000
> >>  #endif
> >>
> >> -#define FILE_SZ (1UL << 35)
> >> +#define FILE_SZ (1ULL << 35)
> 
> ...
> ...
> >> @@ -431,7 +431,7 @@ int main(int argc, char *argv[])
> >>                  zflg = 0;
> >>          }
> >>          while (total < FILE_SZ) {
> >> -                long wr = FILE_SZ - total;
> >> +                unsigned long long wr = FILE_SZ - total;
> >>
> >>                  if (wr > chunk_size)
> >>                          wr = chunk_size;
> >>
> >
> >What about using more conventional size_t instead of "unsigned long long" ?
> 
> size_t is also equivalent to unsigned long and it will not hold value of (1 << 35) for 32 bit system.
> So we can do two things.

Wouldn't the 'correct' type be off_t ?
In any case, IIRC, you have to do really horrid things in Linux to
access files larger than 2G on 32bit systems.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

^ permalink raw reply

* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Oleg Nesterov @ 2018-08-03 12:14 UTC (permalink / raw)
  To: Jürg Billeter
  Cc: Andrew Morton, Thomas Gleixner, Eric Biederman, linux-api,
	linux-kernel
In-Reply-To: <7f7c57230e0279f4599bf13ae1d1d449d76ac232.camel@bitron.ch>

On 08/03, Jürg Billeter wrote:
>
> On Wed, 2018-08-01 at 16:19 +0200, Oleg Nesterov wrote:
> > 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.
>
> This would definitely be an option. You mentioned it last October in
> the PR_SET_PDEATHSIG_PROC discussion¹. However, as PID namespaces
> already exist and appear to be a good fit for the most part,

Sure, if CLONE_NEWPID fits your needs you can use it,

> I think it
> makes sense to just add the missing pieces to PID namespaces instead of
> duplicating part of the PID namespace functionality.

Again, I am not arguing with your change.

PR_KILL_MY_DESCENDANTS_ON_EXIT can make sense just like PR_SET_CHILD_SUBREAPER
even if PID namespace functionality implies both. Simply because CLONE_NEWPID
is not necessarily the best tool, if nothing else you do not necessarily want
the pid isolation.

> Also, based on Eric's comment in that other discussion about
> no_new_privs not being allowed to increase the attack surface,
> PR_KILL_MY_DESCENDANTS_ON_EXIT might require CAP_SYS_ADMIN as well (due
> to setuid children).

No, no, the exiting parent should simply do group_send_sig_info(SIGKILL)
for every descendant and rely on check_kill_permission().

OK, lets forget it for now.

Oleg.

^ permalink raw reply

* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Eric W. Biederman @ 2018-08-03 13:34 UTC (permalink / raw)
  To: Jürg Billeter
  Cc: Oleg Nesterov, Andrew Morton, Thomas Gleixner, linux-api,
	linux-kernel
In-Reply-To: <7f7c57230e0279f4599bf13ae1d1d449d76ac232.camel@bitron.ch>

Jürg Billeter <j@bitron.ch> writes:

> On Wed, 2018-08-01 at 16:19 +0200, Oleg Nesterov wrote:
>> 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.
>
> This would definitely be an option. You mentioned it last October in
> the PR_SET_PDEATHSIG_PROC discussion¹. However, as PID namespaces
> already exist and appear to be a good fit for the most part, I think it
> makes sense to just add the missing pieces to PID namespaces instead of
> duplicating part of the PID namespace functionality.
>
> Also, based on Eric's comment in that other discussion about
> no_new_privs not being allowed to increase the attack surface,
> PR_KILL_MY_DESCENDANTS_ON_EXIT might require CAP_SYS_ADMIN as well (due
> to setuid children). In which case the only potential benefit would be
> that it still allows the child to kill arbitrary processes, as far as I
> can tell.

We don't require CAP_SYS_ADMIN if it is a session and so I think a
similar allowance can be made for PR_KILL_MY_DESCENDANTS_ON_EXIT.  There
is a long standing tradition of being able to kill your own descendants
in linux.  I don't think this allows anything that the tranditional
session allowance for killing process won't.


>From the other direction I think we can just go ahead and fix handling
of the job control stop signals as well.  As far as I understand it
there is a legitimate complaint that SIGTSTP SIGTTIN SIGTTOU do not work
on a pid namespace leader.

The current implementation actual overshoots.  We only need to ignore
signals from the descendants in the pid namespace.  Ideally signals from
other processes are treated like normal.  We have only been able to
apply that ideal to SIGSTOP and SIGKILL as we can handle them in
prepare_signal.  Other signals can be blocked which means the logic to
handle them needs to live in get_signal where we may have no sender
information.


Signals with signal handlers we treat as normal.
Signals with whose default action is to ignore the signal we treat as
normal.

If a process is not in a context where job control has been set up then
SIGTSTP SIGTTIN and SIGTTOU are ignored.  I believe a typical init
process lives in just such an environment.  So I think we can safely
remove the special handling for the job control stops and not have
anyone care.

The rule is that the process group of the process must have a parent in
the same session, or the job control signals are ignored.

A typical init processes calls setsid, which guarantees it has no
parents in the same session.  So the default action of the job control
stops will be to ignore the signal.

A process once a session leader will always be a session leader, and
will never have any parents in a different pgrp in the same session.

So I think this gives us wiggle room needed to just fix this behavior.

Let's see.

For the signals SIGTSTP SIGTTIN and SIGTTOU if we are the typical init
process and we are a session leader we simply don't care who sends those
signals they will be ignored.


So I say we double check my assumption.  Look at sysv init, busy box,
upstart, systemd, whatever android uses, and the container runtimes
light weight inits.  Document it in a change log and just remove the
special case.

If except when handling job control signals is interesting init always
winds up a signal group leader I can't see the point in forcing init
to ignore the job control stop signals.

> ¹ https://lkml.org/lkml/2017/10/5/546

In the future please use mesage-id based links to email disccussions.
That way people can look up the conversations in other email archives.


Eric

^ permalink raw reply

* Re: [PATCH v2] prctl: add PR_[GS]ET_KILLABLE
From: Jürg Billeter @ 2018-08-03 14:39 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Oleg Nesterov, Andrew Morton, Thomas Gleixner, linux-api,
	linux-kernel
In-Reply-To: <87sh3vd14s.fsf@xmission.com>

On Fri, 2018-08-03 at 08:34 -0500, Eric W. Biederman wrote:
> From the other direction I think we can just go ahead and fix handling
> of the job control stop signals as well.  As far as I understand it
> there is a legitimate complaint that SIGTSTP SIGTTIN SIGTTOU do not work
> on a pid namespace leader.
> 
> The current implementation actual overshoots.  We only need to ignore
> signals from the descendants in the pid namespace.  Ideally signals from
> other processes are treated like normal.  We have only been able to
> apply that ideal to SIGSTOP and SIGKILL as we can handle them in
> prepare_signal.  Other signals can be blocked which means the logic to
> handle them needs to live in get_signal where we may have no sender
> information.

SIGINT and SIGQUIT are also relevant for job control. Would the same
approach be possible for them?

And I would like to allow regular POSIX signal behavior also for
signals used outside job control, e.g., SIGTERM, for maximum
compatibility with existing applications. Furthermore, it would also be
good to allow a PID namespace leader to send a signal to itself.

Do you think we can and should cover all of the above without a prctl
by loosening the restrictions imposed by SIGNAL_UNKILLABLE (with
reasonable effort)?

In my opinion, my patch still makes sense as it simply allows regular
POSIX signal behavior for PID namespace leaders and it doesn't risk any
compatibility issues as the behavior doesn't change at all for
processes that don't invoke the new prctl. I.e., simple patch, low
risk, and covers all signals.

In the meantime I've tested the missing patch for copy_process() and
will send out v3 of the patch in case the new prctl makes sense after
all.

Jürg

^ permalink raw reply

* [PATCH v3 1/2] fork: do not rely on SIGNAL_UNKILLABLE for init check
From: Jürg Billeter @ 2018-08-03 14:40 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Oleg Nesterov, Thomas Gleixner, Eric Biederman, linux-api,
	linux-kernel, Jürg Billeter
In-Reply-To: <20180730075241.24002-1-j@bitron.ch>

copy_process() currently checks the SIGNAL_UNKILLABLE flag to determine
whether to accept CLONE_PARENT. In preparation for allowing init
processes to opt out of SIGNAL_UNKILLABLE, directly check whether the
process is an init process with is_child_reaper().

Signed-off-by: Jürg Billeter <j@bitron.ch>
---
 kernel/fork.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/fork.c b/kernel/fork.c
index 1b27babc4c78..c019ce461556 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1646,7 +1646,7 @@ static __latent_entropy struct task_struct *copy_process(
 	 * from creating siblings.
 	 */
 	if ((clone_flags & CLONE_PARENT) &&
-				current->signal->flags & SIGNAL_UNKILLABLE)
+				is_child_reaper(task_tgid(current)))
 		return ERR_PTR(-EINVAL);
 
 	/*
-- 
2.18.0

^ permalink raw reply related

* [PATCH v3 2/2] prctl: add PR_[GS]ET_KILLABLE
From: Jürg Billeter @ 2018-08-03 14:40 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Oleg Nesterov, Thomas Gleixner, Eric Biederman, linux-api,
	linux-kernel, Jürg Billeter
In-Reply-To: <20180803144021.56920-1-j@bitron.ch>

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 prctl does not allow
setting the SIGNAL_UNKILLABLE flag, only clearing.

The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.

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:

 * 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.
 * Many applications do not install handlers for these signals and
   thus, job control won't work properly with unmodified applications.

There are other scenarios besides job control in a shell where
applications rely on the default actions as described in signal(7) and
PID isolation may be useful. This new prctl makes the signal protection
for "init" processes optional, without breaking backward compatibility.

Signed-off-by: Jürg Billeter <j@bitron.ch>
---
 include/uapi/linux/prctl.h |  4 ++++
 kernel/sys.c               | 13 +++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index c0d7ea0bf5b6..92afb63da727 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -219,4 +219,8 @@ struct prctl_mm_map {
 # define PR_SPEC_DISABLE		(1UL << 2)
 # define PR_SPEC_FORCE_DISABLE		(1UL << 3)
 
+/* Control SIGNAL_UNKILLABLE */
+#define PR_GET_KILLABLE			54
+#define PR_SET_KILLABLE			55
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/kernel/sys.c b/kernel/sys.c
index 38509dc1f77b..92c9322cfb98 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2484,6 +2484,19 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
 			return -EINVAL;
 		error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
 		break;
+	case PR_GET_KILLABLE:
+		if (arg3 || arg4 || arg5)
+			return -EINVAL;
+		error = put_user(!(me->signal->flags & SIGNAL_UNKILLABLE),
+				 (int __user *)arg2);
+		break;
+	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);
+		break;
 	default:
 		error = -EINVAL;
 		break;
-- 
2.18.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox