diff mbox series

[18/19] ext4: Add example fsinfo information [ver #16]

Message ID 158204563445.3299825.13575924510060131783.stgit@warthog.procyon.org.uk (mailing list archive)
State New, archived
Headers show
Series VFS: Filesystem information and notifications [ver #16] | expand

Commit Message

David Howells Feb. 18, 2020, 5:07 p.m. UTC
Add the ability to list some ext4 volume timestamps as an example.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-ext4@vger.kernel.org
---

 fs/ext4/Makefile            |    1 +
 fs/ext4/ext4.h              |    9 +++++++++
 fs/ext4/fsinfo.c            |   40 ++++++++++++++++++++++++++++++++++++++++
 fs/ext4/super.c             |    1 +
 include/uapi/linux/fsinfo.h |   16 ++++++++++++++++
 samples/vfs/test-fsinfo.c   |   35 +++++++++++++++++++++++++++++++++++
 6 files changed, 102 insertions(+)
 create mode 100644 fs/ext4/fsinfo.c

Comments

Darrick J. Wong Feb. 19, 2020, 5:04 p.m. UTC | #1
On Tue, Feb 18, 2020 at 05:07:14PM +0000, David Howells wrote:
> Add the ability to list some ext4 volume timestamps as an example.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: linux-ext4@vger.kernel.org
> ---
> 
>  fs/ext4/Makefile            |    1 +
>  fs/ext4/ext4.h              |    9 +++++++++
>  fs/ext4/fsinfo.c            |   40 ++++++++++++++++++++++++++++++++++++++++
>  fs/ext4/super.c             |    1 +
>  include/uapi/linux/fsinfo.h |   16 ++++++++++++++++
>  samples/vfs/test-fsinfo.c   |   35 +++++++++++++++++++++++++++++++++++
>  6 files changed, 102 insertions(+)
>  create mode 100644 fs/ext4/fsinfo.c
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index 4ccb3c9189d8..71d5b460c7c7 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -16,3 +16,4 @@ ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
>  ext4-inode-test-objs			+= inode-test.o
>  obj-$(CONFIG_EXT4_KUNIT_TESTS)		+= ext4-inode-test.o
>  ext4-$(CONFIG_FS_VERITY)		+= verity.o
> +ext4-$(CONFIG_FSINFO)			+= fsinfo.o
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 9a2ee2428ecc..d81b04227da7 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -42,6 +42,7 @@
>  
>  #include <linux/fscrypt.h>
>  #include <linux/fsverity.h>
> +#include <linux/fsinfo.h>
>  
>  #include <linux/compiler.h>
>  
> @@ -3166,6 +3167,14 @@ extern const struct inode_operations ext4_file_inode_operations;
>  extern const struct file_operations ext4_file_operations;
>  extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin);
>  
> +/* fsinfo.c */
> +#ifdef CONFIG_FSINFO
> +struct fsinfo_attribute;
> +extern const struct fsinfo_attribute ext4_fsinfo_attributes[];
> +#else
> +#define ext4_fsinfo_attributes NULL
> +#endif
> +
>  /* inline.c */
>  extern int ext4_get_max_inline_size(struct inode *inode);
>  extern int ext4_find_inline_data_nolock(struct inode *inode);
> diff --git a/fs/ext4/fsinfo.c b/fs/ext4/fsinfo.c
> new file mode 100644
> index 000000000000..545424c410ff
> --- /dev/null
> +++ b/fs/ext4/fsinfo.c
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Filesystem information for ext4
> + *
> + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +
> +#include <linux/mount.h>
> +#include "ext4.h"
> +
> +static int ext4_fsinfo_get_volume_name(struct path *path, struct fsinfo_context *ctx)
> +{
> +	const struct ext4_sb_info *sbi = EXT4_SB(path->mnt->mnt_sb);
> +	const struct ext4_super_block *es = sbi->s_es;
> +
> +	memcpy(ctx->buffer, es->s_volume_name, sizeof(es->s_volume_name));

Shouldn't this be checking that ctx->buffer is large enough to hold
s_volume_name?

> +	return strlen(ctx->buffer);

s_volume_name is /not/ a null-terminated string if the label is 16
characters long.

> +}
> +
> +static int ext4_fsinfo_get_timestamps(struct path *path, struct fsinfo_context *ctx)
> +{
> +	const struct ext4_sb_info *sbi = EXT4_SB(path->mnt->mnt_sb);
> +	const struct ext4_super_block *es = sbi->s_es;
> +	struct fsinfo_ext4_timestamps *ts = ctx->buffer;
> +
> +#define Z(R,S) R = S | (((u64)S##_hi) << 32)
> +	Z(ts->mkfs_time,	es->s_mkfs_time);
> +	Z(ts->mount_time,	es->s_mtime);
> +	Z(ts->write_time,	es->s_wtime);
> +	Z(ts->last_check_time,	es->s_lastcheck);
> +	Z(ts->first_error_time,	es->s_first_error_time);
> +	Z(ts->last_error_time,	es->s_last_error_time);
> +	return sizeof(*ts);
> +}
> +
> +const struct fsinfo_attribute ext4_fsinfo_attributes[] = {
> +	FSINFO_STRING	(FSINFO_ATTR_VOLUME_NAME,	ext4_fsinfo_get_volume_name),
> +	FSINFO_VSTRUCT	(FSINFO_ATTR_EXT4_TIMESTAMPS,	ext4_fsinfo_get_timestamps),
> +	{}
> +};
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 8434217549b3..e21c3d99747e 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -1477,6 +1477,7 @@ static const struct super_operations ext4_sops = {
>  	.freeze_fs	= ext4_freeze,
>  	.unfreeze_fs	= ext4_unfreeze,
>  	.statfs		= ext4_statfs,
> +	.fsinfo_attributes = ext4_fsinfo_attributes,
>  	.remount_fs	= ext4_remount,
>  	.show_options	= ext4_show_options,
>  #ifdef CONFIG_QUOTA
> diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
> index 5467f88ca9b0..da9a6f48ec5b 100644
> --- a/include/uapi/linux/fsinfo.h
> +++ b/include/uapi/linux/fsinfo.h
> @@ -38,6 +38,8 @@
>  #define FSINFO_ATTR_AFS_SERVER_NAME	0x301	/* Name of the Nth server (string) */
>  #define FSINFO_ATTR_AFS_SERVER_ADDRESSES 0x302	/* List of addresses of the Nth server */
>  
> +#define FSINFO_ATTR_EXT4_TIMESTAMPS	0x400	/* Ext4 superblock timestamps */

I guess each filesystem gets ... 256 different attrs, and the third
nibble determines the namespace?

--D

>  /*
>   * Optional fsinfo() parameter structure.
>   *
> @@ -323,4 +325,18 @@ struct fsinfo_afs_server_address {
>  
>  #define FSINFO_ATTR_AFS_SERVER_ADDRESSES__STRUCT struct fsinfo_afs_server_address
>  
> +/*
> + * Information struct for fsinfo(FSINFO_ATTR_EXT4_TIMESTAMPS).
> + */
> +struct fsinfo_ext4_timestamps {
> +	__u64		mkfs_time;
> +	__u64		mount_time;
> +	__u64		write_time;
> +	__u64		last_check_time;
> +	__u64		first_error_time;
> +	__u64		last_error_time;
> +};
> +
> +#define FSINFO_ATTR_EXT4_TIMESTAMPS__STRUCT struct fsinfo_ext4_timestamps
> +
>  #endif /* _UAPI_LINUX_FSINFO_H */
> diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
> index fd425c08b00b..53251ee98d1c 100644
> --- a/samples/vfs/test-fsinfo.c
> +++ b/samples/vfs/test-fsinfo.c
> @@ -359,6 +359,40 @@ static void dump_afs_fsinfo_server_address(void *reply, unsigned int size)
>  	printf("family=%u\n", ss->ss_family);
>  }
>  
> +static char *dump_ext4_time(char *buffer, time_t tim)
> +{
> +	struct tm tm;
> +	int len;
> +
> +	if (tim == 0)
> +		return "-";
> +
> +	if (!localtime_r(&tim, &tm)) {
> +		perror("localtime_r");
> +		exit(1);
> +	}
> +	len = strftime(buffer, 100, "%F %T", &tm);
> +	if (len == 0) {
> +		perror("strftime");
> +		exit(1);
> +	}
> +	return buffer;
> +}
> +
> +static void dump_ext4_fsinfo_timestamps(void *reply, unsigned int size)
> +{
> +	struct fsinfo_ext4_timestamps *r = reply;
> +	char buffer[100];
> +
> +	printf("\n");
> +	printf("\tmkfs    : %s\n", dump_ext4_time(buffer, r->mkfs_time));
> +	printf("\tmount   : %s\n", dump_ext4_time(buffer, r->mount_time));
> +	printf("\twrite   : %s\n", dump_ext4_time(buffer, r->write_time));
> +	printf("\tfsck    : %s\n", dump_ext4_time(buffer, r->last_check_time));
> +	printf("\t1st-err : %s\n", dump_ext4_time(buffer, r->first_error_time));
> +	printf("\tlast-err: %s\n", dump_ext4_time(buffer, r->last_error_time));
> +}
> +
>  static void dump_string(void *reply, unsigned int size)
>  {
>  	char *s = reply, *p;
> @@ -433,6 +467,7 @@ static const struct fsinfo_attribute fsinfo_attributes[] = {
>  	FSINFO_STRING	(FSINFO_ATTR_AFS_CELL_NAME,	afs_cell_name),
>  	FSINFO_STRING	(FSINFO_ATTR_AFS_SERVER_NAME,	afs_server_name),
>  	FSINFO_LIST_N	(FSINFO_ATTR_AFS_SERVER_ADDRESSES, afs_fsinfo_server_address),
> +	FSINFO_VSTRUCT	(FSINFO_ATTR_EXT4_TIMESTAMPS,	ext4_fsinfo_timestamps),
>  	{}
>  };
>  
> 
>
kernel test robot Feb. 20, 2020, 12:53 a.m. UTC | #2
Hi David,

I love your patch! Yet something to improve:

[auto build test ERROR on next-20200219]
[cannot apply to tip/x86/asm nfs/linux-next ext4/dev linus/master v5.6-rc2 v5.6-rc1 v5.5 v5.6-rc2]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/David-Howells/VFS-Filesystem-information-and-notifications-ver-16/20200220-072538
base:    1d7f85df0f9c0456520ae86dc597bca87980d253
config: um-x86_64_defconfig (attached as .config)
compiler: gcc-7 (Debian 7.5.0-5) 7.5.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=um SUBARCH=x86_64

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> fs/ext4/super.c:1480:3: error: 'const struct super_operations' has no member named 'fsinfo_attributes'
     .fsinfo_attributes = ext4_fsinfo_attributes,
      ^~~~~~~~~~~~~~~~~

vim +1480 fs/ext4/super.c

  1466	
  1467	static const struct super_operations ext4_sops = {
  1468		.alloc_inode	= ext4_alloc_inode,
  1469		.free_inode	= ext4_free_in_core_inode,
  1470		.destroy_inode	= ext4_destroy_inode,
  1471		.write_inode	= ext4_write_inode,
  1472		.dirty_inode	= ext4_dirty_inode,
  1473		.drop_inode	= ext4_drop_inode,
  1474		.evict_inode	= ext4_evict_inode,
  1475		.put_super	= ext4_put_super,
  1476		.sync_fs	= ext4_sync_fs,
  1477		.freeze_fs	= ext4_freeze,
  1478		.unfreeze_fs	= ext4_unfreeze,
  1479		.statfs		= ext4_statfs,
> 1480		.fsinfo_attributes = ext4_fsinfo_attributes,
  1481		.remount_fs	= ext4_remount,
  1482		.show_options	= ext4_show_options,
  1483	#ifdef CONFIG_QUOTA
  1484		.quota_read	= ext4_quota_read,
  1485		.quota_write	= ext4_quota_write,
  1486		.get_dquots	= ext4_get_dquots,
  1487	#endif
  1488		.bdev_try_to_free_page = bdev_try_to_free_page,
  1489	};
  1490	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
David Howells Feb. 21, 2020, 2:43 p.m. UTC | #3
Darrick J. Wong <darrick.wong@oracle.com> wrote:

> > +	memcpy(ctx->buffer, es->s_volume_name, sizeof(es->s_volume_name));
> 
> Shouldn't this be checking that ctx->buffer is large enough to hold
> s_volume_name?

Well, the buffer is guaranteed to be 4KiB in size.

> > +	return strlen(ctx->buffer);
> 
> s_volume_name is /not/ a null-terminated string if the label is 16
> characters long.

And the buffer is precleared, so it's automatically NULL terminated.

> > +#define FSINFO_ATTR_EXT4_TIMESTAMPS	0x400	/* Ext4 superblock timestamps */
> 
> I guess each filesystem gets ... 256 different attrs, and the third
> nibble determines the namespace?

No.  Think of it as allocating namespace in 256-number blocks.  That means
there are 16 million of them.  If a filesystem uses up an entire block, it can
always allocate another one.  I don't think it likely that we'll get
sufficient filesystems to eat them all.

David
Darrick J. Wong Feb. 21, 2020, 4:26 p.m. UTC | #4
On Fri, Feb 21, 2020 at 02:43:05PM +0000, David Howells wrote:
> Darrick J. Wong <darrick.wong@oracle.com> wrote:
> 
> > > +	memcpy(ctx->buffer, es->s_volume_name, sizeof(es->s_volume_name));
> > 
> > Shouldn't this be checking that ctx->buffer is large enough to hold
> > s_volume_name?
> 
> Well, the buffer is guaranteed to be 4KiB in size.

Ah, ok.

> > > +	return strlen(ctx->buffer);
> > 
> > s_volume_name is /not/ a null-terminated string if the label is 16
> > characters long.
> 
> And the buffer is precleared, so it's automatically NULL terminated.

<nod>

> > > +#define FSINFO_ATTR_EXT4_TIMESTAMPS	0x400	/* Ext4 superblock timestamps */
> > 
> > I guess each filesystem gets ... 256 different attrs, and the third
> > nibble determines the namespace?
> 
> No.  Think of it as allocating namespace in 256-number blocks.  That means
> there are 16 million of them.  If a filesystem uses up an entire block, it can
> always allocate another one.  I don't think it likely that we'll get
> sufficient filesystems to eat them all.

Ah.  In that case I declare that we would like to reserve 0x5800-0x58FF
for XFS. :)

--D

> David
>
diff mbox series

Patch

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 4ccb3c9189d8..71d5b460c7c7 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -16,3 +16,4 @@  ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
 ext4-inode-test-objs			+= inode-test.o
 obj-$(CONFIG_EXT4_KUNIT_TESTS)		+= ext4-inode-test.o
 ext4-$(CONFIG_FS_VERITY)		+= verity.o
+ext4-$(CONFIG_FSINFO)			+= fsinfo.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9a2ee2428ecc..d81b04227da7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -42,6 +42,7 @@ 
 
 #include <linux/fscrypt.h>
 #include <linux/fsverity.h>
+#include <linux/fsinfo.h>
 
 #include <linux/compiler.h>
 
@@ -3166,6 +3167,14 @@  extern const struct inode_operations ext4_file_inode_operations;
 extern const struct file_operations ext4_file_operations;
 extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin);
 
+/* fsinfo.c */
+#ifdef CONFIG_FSINFO
+struct fsinfo_attribute;
+extern const struct fsinfo_attribute ext4_fsinfo_attributes[];
+#else
+#define ext4_fsinfo_attributes NULL
+#endif
+
 /* inline.c */
 extern int ext4_get_max_inline_size(struct inode *inode);
 extern int ext4_find_inline_data_nolock(struct inode *inode);
diff --git a/fs/ext4/fsinfo.c b/fs/ext4/fsinfo.c
new file mode 100644
index 000000000000..545424c410ff
--- /dev/null
+++ b/fs/ext4/fsinfo.c
@@ -0,0 +1,40 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Filesystem information for ext4
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/mount.h>
+#include "ext4.h"
+
+static int ext4_fsinfo_get_volume_name(struct path *path, struct fsinfo_context *ctx)
+{
+	const struct ext4_sb_info *sbi = EXT4_SB(path->mnt->mnt_sb);
+	const struct ext4_super_block *es = sbi->s_es;
+
+	memcpy(ctx->buffer, es->s_volume_name, sizeof(es->s_volume_name));
+	return strlen(ctx->buffer);
+}
+
+static int ext4_fsinfo_get_timestamps(struct path *path, struct fsinfo_context *ctx)
+{
+	const struct ext4_sb_info *sbi = EXT4_SB(path->mnt->mnt_sb);
+	const struct ext4_super_block *es = sbi->s_es;
+	struct fsinfo_ext4_timestamps *ts = ctx->buffer;
+
+#define Z(R,S) R = S | (((u64)S##_hi) << 32)
+	Z(ts->mkfs_time,	es->s_mkfs_time);
+	Z(ts->mount_time,	es->s_mtime);
+	Z(ts->write_time,	es->s_wtime);
+	Z(ts->last_check_time,	es->s_lastcheck);
+	Z(ts->first_error_time,	es->s_first_error_time);
+	Z(ts->last_error_time,	es->s_last_error_time);
+	return sizeof(*ts);
+}
+
+const struct fsinfo_attribute ext4_fsinfo_attributes[] = {
+	FSINFO_STRING	(FSINFO_ATTR_VOLUME_NAME,	ext4_fsinfo_get_volume_name),
+	FSINFO_VSTRUCT	(FSINFO_ATTR_EXT4_TIMESTAMPS,	ext4_fsinfo_get_timestamps),
+	{}
+};
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 8434217549b3..e21c3d99747e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1477,6 +1477,7 @@  static const struct super_operations ext4_sops = {
 	.freeze_fs	= ext4_freeze,
 	.unfreeze_fs	= ext4_unfreeze,
 	.statfs		= ext4_statfs,
+	.fsinfo_attributes = ext4_fsinfo_attributes,
 	.remount_fs	= ext4_remount,
 	.show_options	= ext4_show_options,
 #ifdef CONFIG_QUOTA
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
index 5467f88ca9b0..da9a6f48ec5b 100644
--- a/include/uapi/linux/fsinfo.h
+++ b/include/uapi/linux/fsinfo.h
@@ -38,6 +38,8 @@ 
 #define FSINFO_ATTR_AFS_SERVER_NAME	0x301	/* Name of the Nth server (string) */
 #define FSINFO_ATTR_AFS_SERVER_ADDRESSES 0x302	/* List of addresses of the Nth server */
 
+#define FSINFO_ATTR_EXT4_TIMESTAMPS	0x400	/* Ext4 superblock timestamps */
+
 /*
  * Optional fsinfo() parameter structure.
  *
@@ -323,4 +325,18 @@  struct fsinfo_afs_server_address {
 
 #define FSINFO_ATTR_AFS_SERVER_ADDRESSES__STRUCT struct fsinfo_afs_server_address
 
+/*
+ * Information struct for fsinfo(FSINFO_ATTR_EXT4_TIMESTAMPS).
+ */
+struct fsinfo_ext4_timestamps {
+	__u64		mkfs_time;
+	__u64		mount_time;
+	__u64		write_time;
+	__u64		last_check_time;
+	__u64		first_error_time;
+	__u64		last_error_time;
+};
+
+#define FSINFO_ATTR_EXT4_TIMESTAMPS__STRUCT struct fsinfo_ext4_timestamps
+
 #endif /* _UAPI_LINUX_FSINFO_H */
diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
index fd425c08b00b..53251ee98d1c 100644
--- a/samples/vfs/test-fsinfo.c
+++ b/samples/vfs/test-fsinfo.c
@@ -359,6 +359,40 @@  static void dump_afs_fsinfo_server_address(void *reply, unsigned int size)
 	printf("family=%u\n", ss->ss_family);
 }
 
+static char *dump_ext4_time(char *buffer, time_t tim)
+{
+	struct tm tm;
+	int len;
+
+	if (tim == 0)
+		return "-";
+
+	if (!localtime_r(&tim, &tm)) {
+		perror("localtime_r");
+		exit(1);
+	}
+	len = strftime(buffer, 100, "%F %T", &tm);
+	if (len == 0) {
+		perror("strftime");
+		exit(1);
+	}
+	return buffer;
+}
+
+static void dump_ext4_fsinfo_timestamps(void *reply, unsigned int size)
+{
+	struct fsinfo_ext4_timestamps *r = reply;
+	char buffer[100];
+
+	printf("\n");
+	printf("\tmkfs    : %s\n", dump_ext4_time(buffer, r->mkfs_time));
+	printf("\tmount   : %s\n", dump_ext4_time(buffer, r->mount_time));
+	printf("\twrite   : %s\n", dump_ext4_time(buffer, r->write_time));
+	printf("\tfsck    : %s\n", dump_ext4_time(buffer, r->last_check_time));
+	printf("\t1st-err : %s\n", dump_ext4_time(buffer, r->first_error_time));
+	printf("\tlast-err: %s\n", dump_ext4_time(buffer, r->last_error_time));
+}
+
 static void dump_string(void *reply, unsigned int size)
 {
 	char *s = reply, *p;
@@ -433,6 +467,7 @@  static const struct fsinfo_attribute fsinfo_attributes[] = {
 	FSINFO_STRING	(FSINFO_ATTR_AFS_CELL_NAME,	afs_cell_name),
 	FSINFO_STRING	(FSINFO_ATTR_AFS_SERVER_NAME,	afs_server_name),
 	FSINFO_LIST_N	(FSINFO_ATTR_AFS_SERVER_ADDRESSES, afs_fsinfo_server_address),
+	FSINFO_VSTRUCT	(FSINFO_ATTR_EXT4_TIMESTAMPS,	ext4_fsinfo_timestamps),
 	{}
 };