| From 69b33d464bcadd6c60018f5d0bde8ad3e7530dc7 Mon Sep 17 00:00:00 2001 | 
 | From: David Howells <dhowells@redhat.com> | 
 | Date: Tue, 3 Mar 2020 14:29:27 +0000 | 
 | Subject: [PATCH 3/3] 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                 | 39 +++++++++++++++++++++++++++++++++++ | 
 |  fs/internal.h               |  2 ++ | 
 |  fs/namespace.c              | 41 +++++++++++++++++++++++++++++++++++++ | 
 |  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 7d9c73e9cbdef..7e2d871eb5dfe 100644 | 
 | --- a/fs/fsinfo.c | 
 | +++ b/fs/fsinfo.c | 
 | @@ -198,6 +198,42 @@ 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 and NUL termination is added. | 
 | + */ | 
 | +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 - 1, | 
 | +	}; | 
 | +	int ret = 0; | 
 | + | 
 | +	switch (ctx->requested_attr) { | 
 | +	case FSINFO_ATTR_CONFIGURATION: | 
 | +		seq_puts(&m, sb_rdonly(sb) ? "ro" : "rw"); | 
 | +		ret = security_sb_show_options(&m, sb); | 
 | +		if (!ret && 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; | 
 | + | 
 | +	((char *)ctx->buffer)[ctx->skip + m.count] = 0; | 
 | +	return m.count + 1; | 
 | +} | 
 | + | 
 |  static const struct fsinfo_attribute fsinfo_common_attributes[] = { | 
 |  	FSINFO_VSTRUCT	(FSINFO_ATTR_STATFS,		fsinfo_generic_statfs), | 
 |  	FSINFO_VSTRUCT	(FSINFO_ATTR_IDS,		fsinfo_generic_ids), | 
 | @@ -206,6 +242,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 1b7460f055a3b..a2a529da7e673 100644 | 
 | --- a/fs/internal.h | 
 | +++ b/fs/internal.h | 
 | @@ -98,6 +98,8 @@ int path_mount(const char *dev_name, struct path *path, | 
 |  		const char *type_page, unsigned long flags, void *data_page); | 
 |  int path_umount(struct path *path, int flags); | 
 |   | 
 | +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 cebaa3e817940..33218f67ad5b1 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" | 
 | @@ -4105,3 +4106,43 @@ 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 - 1, | 
 | +	}; | 
 | +	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; | 
 | + | 
 | +	((char *)ctx->buffer)[m.count] = 0; | 
 | +	return m.count + 1; | 
 | +} | 
 | + | 
 | +#endif /* CONFIG_FSINFO */ | 
 | diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h | 
 | index 65892239ba86c..45d9f4b758787 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 934b25399ffed..cf849c9778f71 100644 | 
 | --- a/samples/vfs/test-fsinfo.c | 
 | +++ b/samples/vfs/test-fsinfo.c | 
 | @@ -294,6 +294,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.25.1 | 
 |  |