Introduce fsquota package

This introduces a new fsquota package and
a few low-level support packages to simplify the
management of filesystem quotas.

To expose an API that's nice to use while staying
performant and safe the new fsinfo syscall is being
used. Since that syscall is not yet in mainline it has
been backported to our 5.6 kernel.

Test Plan:
Manually validated on our kernel, automated
tests are pending some Bazel work to be able to run them
inside our own kernel.

X-Origin-Diff: phab/D462
GitOrigin-RevId: bb463056589d2b13b7cf32d48ab0b884e70b1bad
diff --git a/third_party/linux/external/0004-fsinfo-Allow-retrieval-of-superblock-devname-options.patch b/third_party/linux/external/0004-fsinfo-Allow-retrieval-of-superblock-devname-options.patch
new file mode 100644
index 0000000..86ba2cc
--- /dev/null
+++ b/third_party/linux/external/0004-fsinfo-Allow-retrieval-of-superblock-devname-options.patch
@@ -0,0 +1,183 @@
+From fa2ff1a0da263362a2c98c8cfe68bcac3a323ac4 Mon Sep 17 00:00:00 2001
+From: David Howells <dhowells@redhat.com>
+Date: Tue, 3 Mar 2020 14:29:27 +0000
+Subject: [PATCH 4/4] fsinfo: Allow retrieval of superblock devname, options
+ and stats
+
+Provide fsinfo() attributes to retrieve superblock device name, options,
+and statistics in string form.  The following attributes are defined:
+
+	FSINFO_ATTR_SOURCE		- Mount-specific device name
+	FSINFO_ATTR_CONFIGURATION	- Mount options
+	FSINFO_ATTR_FS_STATISTICS	- Filesystem statistics
+
+FSINFO_ATTR_SOURCE could be made indexable by params->Nth to handle the
+case where there is more than one source (e.g. the bcachefs filesystem).
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+---
+ fs/fsinfo.c                 | 41 +++++++++++++++++++++++++++++++++++++
+ fs/internal.h               |  2 ++
+ fs/namespace.c              | 39 +++++++++++++++++++++++++++++++++++
+ include/uapi/linux/fsinfo.h |  3 +++
+ samples/vfs/test-fsinfo.c   |  4 ++++
+ 5 files changed, 89 insertions(+)
+
+diff --git a/fs/fsinfo.c b/fs/fsinfo.c
+index 1830c73f37a7..3ed43c5a10e8 100644
+--- a/fs/fsinfo.c
++++ b/fs/fsinfo.c
+@@ -188,6 +188,44 @@ static int fsinfo_generic_volume_id(struct path *path, struct fsinfo_context *ct
+ 	return fsinfo_string(path->dentry->d_sb->s_id, ctx);
+ }
+ 
++/*
++ * Retrieve the superblock configuration (mount options) as a comma-separated
++ * string.  The initial comma is stripped off.
++ */
++static int fsinfo_generic_seq_read(struct path *path, struct fsinfo_context *ctx)
++{
++	struct super_block *sb = path->dentry->d_sb;
++	struct seq_file m = {
++		.buf	= ctx->buffer,
++		.size	= ctx->buf_size,
++	};
++	int ret = 0;
++
++	switch (ctx->requested_attr) {
++	case FSINFO_ATTR_CONFIGURATION:
++		if (sb->s_op->show_options)
++			ret = sb->s_op->show_options(&m, path->mnt->mnt_root);
++		break;
++
++	case FSINFO_ATTR_FS_STATISTICS:
++		if (sb->s_op->show_stats)
++			ret = sb->s_op->show_stats(&m, path->mnt->mnt_root);
++		break;
++	}
++
++	if (ret < 0)
++		return ret;
++	if (seq_has_overflowed(&m))
++		return ctx->buf_size + PAGE_SIZE;
++	if (ctx->requested_attr == FSINFO_ATTR_CONFIGURATION) {
++		if (m.count > 0 && ((char *)ctx->buffer)[0] == ',') {
++			m.count--;
++			ctx->skip = 1;
++		}
++	}
++	return m.count;
++}
++
+ static const struct fsinfo_attribute fsinfo_common_attributes[] = {
+ 	FSINFO_VSTRUCT	(FSINFO_ATTR_STATFS,		fsinfo_generic_statfs),
+ 	FSINFO_VSTRUCT	(FSINFO_ATTR_IDS,		fsinfo_generic_ids),
+@@ -196,6 +234,9 @@ static const struct fsinfo_attribute fsinfo_common_attributes[] = {
+ 	FSINFO_VSTRUCT	(FSINFO_ATTR_TIMESTAMP_INFO,	fsinfo_generic_timestamp_info),
+ 	FSINFO_STRING	(FSINFO_ATTR_VOLUME_ID,		fsinfo_generic_volume_id),
+ 	FSINFO_VSTRUCT	(FSINFO_ATTR_VOLUME_UUID,	fsinfo_generic_volume_uuid),
++	FSINFO_STRING	(FSINFO_ATTR_SOURCE,		fsinfo_generic_mount_source),
++	FSINFO_STRING	(FSINFO_ATTR_CONFIGURATION,	fsinfo_generic_seq_read),
++	FSINFO_STRING	(FSINFO_ATTR_FS_STATISTICS,	fsinfo_generic_seq_read),
+ 
+ 	FSINFO_LIST	(FSINFO_ATTR_FSINFO_ATTRIBUTES,	(void *)123UL),
+ 	FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, (void *)123UL),
+diff --git a/fs/internal.h b/fs/internal.h
+index a0d90f23593c..6f2cc77bf38d 100644
+--- a/fs/internal.h
++++ b/fs/internal.h
+@@ -91,6 +91,8 @@ extern int __mnt_want_write_file(struct file *);
+ extern void __mnt_drop_write_file(struct file *);
+ 
+ extern void dissolve_on_fput(struct vfsmount *);
++extern int fsinfo_generic_mount_source(struct path *, struct fsinfo_context *);
++
+ /*
+  * fs_struct.c
+  */
+diff --git a/fs/namespace.c b/fs/namespace.c
+index 85b5f7bea82e..b4951e2afae1 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -30,6 +30,7 @@
+ #include <uapi/linux/mount.h>
+ #include <linux/fs_context.h>
+ #include <linux/shmem_fs.h>
++#include <linux/fsinfo.h>
+ 
+ #include "pnode.h"
+ #include "internal.h"
+@@ -3975,3 +3976,41 @@ const struct proc_ns_operations mntns_operations = {
+ 	.install	= mntns_install,
+ 	.owner		= mntns_owner,
+ };
++
++#ifdef CONFIG_FSINFO
++static inline void mangle(struct seq_file *m, const char *s)
++{
++	seq_escape(m, s, " \t\n\\");
++}
++
++/*
++ * Return the mount source/device name as seen from this mountpoint.  Shared
++ * mounts may vary here and the filesystem is permitted to substitute its own
++ * rendering.
++ */
++int fsinfo_generic_mount_source(struct path *path, struct fsinfo_context *ctx)
++{
++	struct super_block *sb = path->mnt->mnt_sb;
++	struct mount *mnt = real_mount(path->mnt);
++	struct seq_file m = {
++		.buf	= ctx->buffer,
++		.size	= ctx->buf_size,
++	};
++	int ret;
++
++	if (sb->s_op->show_devname) {
++		ret = sb->s_op->show_devname(&m, mnt->mnt.mnt_root);
++		if (ret < 0)
++			return ret;
++	} else {
++		if (!mnt->mnt_devname)
++			return fsinfo_string("none", ctx);
++		mangle(&m, mnt->mnt_devname);
++	}
++
++	if (seq_has_overflowed(&m))
++		return ctx->buf_size + PAGE_SIZE;
++	return m.count;
++}
++
++#endif /* CONFIG_FSINFO */
+diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
+index e9b35b9b7629..d3722c7142a6 100644
+--- a/include/uapi/linux/fsinfo.h
++++ b/include/uapi/linux/fsinfo.h
+@@ -23,6 +23,9 @@
+ #define FSINFO_ATTR_VOLUME_ID		0x05	/* Volume ID (string) */
+ #define FSINFO_ATTR_VOLUME_UUID		0x06	/* Volume UUID (LE uuid) */
+ #define FSINFO_ATTR_VOLUME_NAME		0x07	/* Volume name (string) */
++#define FSINFO_ATTR_SOURCE		0x09	/* Superblock source/device name (string) */
++#define FSINFO_ATTR_CONFIGURATION	0x0a	/* Superblock configuration/options (string) */
++#define FSINFO_ATTR_FS_STATISTICS	0x0b	/* Superblock filesystem statistics (string) */
+ 
+ #define FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO 0x100	/* Information about attr N (for path) */
+ #define FSINFO_ATTR_FSINFO_ATTRIBUTES	0x101	/* List of supported attrs (for path) */
+diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
+index 2b53c735d330..1797f7b8d08a 100644
+--- a/samples/vfs/test-fsinfo.c
++++ b/samples/vfs/test-fsinfo.c
+@@ -289,6 +289,10 @@ static const struct fsinfo_attribute fsinfo_attributes[] = {
+ 	FSINFO_STRING	(FSINFO_ATTR_VOLUME_ID,		string),
+ 	FSINFO_VSTRUCT	(FSINFO_ATTR_VOLUME_UUID,	fsinfo_generic_volume_uuid),
+ 	FSINFO_STRING	(FSINFO_ATTR_VOLUME_NAME,	string),
++	FSINFO_STRING	(FSINFO_ATTR_SOURCE,		string),
++	FSINFO_STRING	(FSINFO_ATTR_CONFIGURATION,	string),
++	FSINFO_STRING	(FSINFO_ATTR_FS_STATISTICS,	string),
++
+ 	FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, fsinfo_meta_attribute_info),
+ 	FSINFO_LIST	(FSINFO_ATTR_FSINFO_ATTRIBUTES,	fsinfo_meta_attributes),
+ 	{}
+-- 
+2.20.1
+