[v3,3/5] dumpe2fs: add JSON output of superblock

Message ID 20180220095950.23462-4-viktor.prutyanov@virtuozzo.com
State New
Headers show
Series
  • dumpe2fs: add JSON output format
Related show

Commit Message

Viktor Prutyanov Feb. 20, 2018, 9:59 a.m.
This patch adds JSON output of superblock information

Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
---
 debugfs/Makefile.in |   4 +-
 lib/e2p/e2p.h       |   6 +
 lib/e2p/ls.c        | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/e2p/pe.c        |  18 +++
 lib/e2p/ps.c        |  10 ++
 misc/Makefile.in    |   8 +-
 misc/dumpe2fs.c     |   5 +-
 7 files changed, 406 insertions(+), 7 deletions(-)

Comments

Dmitry Monakhov Feb. 20, 2018, 10:53 a.m. | #1
Viktor Prutyanov <viktor.prutyanov@virtuozzo.com> writes:

> This patch adds JSON output of superblock information
>
Looks good. Acked-by: Dmitry Monakhov <dmonakhov@openvz.org>
> Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
> ---
>  debugfs/Makefile.in |   4 +-
>  lib/e2p/e2p.h       |   6 +
>  lib/e2p/ls.c        | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/e2p/pe.c        |  18 +++
>  lib/e2p/ps.c        |  10 ++
>  misc/Makefile.in    |   8 +-
>  misc/dumpe2fs.c     |   5 +-
>  7 files changed, 406 insertions(+), 7 deletions(-)
>
> diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in
> index 57940cde..92a3552a 100644
> --- a/debugfs/Makefile.in
> +++ b/debugfs/Makefile.in
> @@ -34,9 +34,9 @@ SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \
>  	$(srcdir)/journal.c $(srcdir)/../e2fsck/revoke.c \
>  	$(srcdir)/../e2fsck/recovery.c $(srcdir)/do_journal.c
>  
> -LIBS= $(LIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \
> +LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \
>  	$(LIBUUID) $(LIBMAGIC) $(SYSLIBS)
> -DEPLIBS= $(DEPLIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(DEPLIBSS) $(DEPLIBCOM_ERR) \
> +DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT) $(DEPLIBSS) $(DEPLIBCOM_ERR) \
>  	$(DEPLIBBLKID) $(DEPLIBUUID)
>  
>  STATIC_LIBS= $(STATIC_LIBSUPPORT) $(STATIC_LIBEXT2FS) $(STATIC_LIBSS) \
> diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
> index e96cdec8..88c08d73 100644
> --- a/lib/e2p/e2p.h
> +++ b/lib/e2p/e2p.h
> @@ -13,6 +13,8 @@
>  
>  #include <ext2fs/ext2_fs.h>
>  
> +#include "support/json-out.h"
> +
>  #define E2P_FEATURE_COMPAT	0
>  #define E2P_FEATURE_INCOMPAT	1
>  #define E2P_FEATURE_RO_INCOMPAT	2
> @@ -42,8 +44,10 @@ int iterate_on_dir (const char * dir_name,
>  void list_super(struct ext2_super_block * s);
>  void list_super2(struct ext2_super_block * s, FILE *f);
>  void print_fs_errors (FILE * f, unsigned short errors);
> +void snprint_fs_errors (char *str, size_t size, unsigned short errors);
>  void print_flags (FILE * f, unsigned long flags, unsigned options);
>  void print_fs_state (FILE * f, unsigned short state);
> +void snprint_fs_state (char *str, size_t size, unsigned short state);
>  int setflags (int fd, unsigned long flags);
>  int setversion (int fd, unsigned long version);
>  
> @@ -77,3 +81,5 @@ char *e2p_os2string(int os_type);
>  int e2p_string2os(char *str);
>  
>  unsigned int e2p_percent(int percent, unsigned int base);
> +
> +void fill_json_super(struct json_obj *obj, struct ext2_super_block * s);
> diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
> index a7586e09..b690960b 100644
> --- a/lib/e2p/ls.c
> +++ b/lib/e2p/ls.c
> @@ -37,6 +37,15 @@ static void print_user (unsigned short uid, FILE *f)
>  		fprintf(f, "(user %s)\n", pw->pw_name);
>  }
>  
> +static void fill_json_user(struct json_obj *obj, unsigned short uid)
> +{
> +	struct passwd *pw = getpwuid(uid);
> +
> +	json_obj_add_fmt_str(obj, "uid", 32, "%u", uid);
> +	if (pw)
> +		json_obj_add_str(obj, "user", pw->pw_name);
> +}
> +
>  static void print_group (unsigned short gid, FILE *f)
>  {
>  	struct group *gr;
> @@ -49,6 +58,15 @@ static void print_group (unsigned short gid, FILE *f)
>  		fprintf(f, "(group %s)\n", gr->gr_name);
>  }
>  
> +static void fill_json_group(struct json_obj *obj, unsigned short gid)
> +{
> +	struct group *gr = getgrgid(gid);
> +
> +	json_obj_add_fmt_str(obj, "gid", 32, "%u", gid);
> +	if (gr)
> +		json_obj_add_str(obj, "group", gr->gr_name);
> +}
> +
>  #define MONTH_INT (86400 * 30)
>  #define WEEK_INT (86400 * 7)
>  #define DAY_INT	(86400)
> @@ -117,6 +135,24 @@ static void print_features(struct ext2_super_block * s, FILE *f)
>  #endif
>  }
>  
> +static void fill_json_features(struct json_obj *obj,
> +							   struct ext2_super_block *s)
> +{
> +#ifdef EXT2_DYNAMIC_REV
> +	int	i, j;
> +	__u32	*mask = &s->s_feature_compat, m;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"filesystem-features", JSON_VAL_STRING);
> +
> +	for (i=0; i <3; i++,mask++) {
> +		for (j=0,m=1; j < 32; j++, m<<=1) {
> +			if (*mask & m)
> +				json_list_add_str(list, e2p_feature2string(i, m));
> +		}
> +	}
> +#endif
> +}
> +
>  static void print_mntopts(struct ext2_super_block * s, FILE *f)
>  {
>  #ifdef EXT2_DYNAMIC_REV
> @@ -142,6 +178,26 @@ static void print_mntopts(struct ext2_super_block * s, FILE *f)
>  #endif
>  }
>  
> +static void fill_json_mntopts(struct json_obj *obj,
> +							  struct ext2_super_block *s)
> +{
> +#ifdef EXT2_DYNAMIC_REV
> +	int	i;
> +	__u32	mask = s->s_default_mount_opts, m;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"default-mount-options", JSON_VAL_STRING);
> +
> +	if (mask & EXT3_DEFM_JMODE)
> +		json_list_add_str(list, e2p_mntopt2string(mask & EXT3_DEFM_JMODE));
> +	for (i=0,m=1; i < 32; i++, m<<=1) {
> +		if (m & EXT3_DEFM_JMODE)
> +			continue;
> +		if (mask & m)
> +			json_list_add_str(list, e2p_mntopt2string(m));
> +	}
> +#endif
> +}
> +
>  static void print_super_flags(struct ext2_super_block * s, FILE *f)
>  {
>  	int	flags_found = 0;
> @@ -168,6 +224,25 @@ static void print_super_flags(struct ext2_super_block * s, FILE *f)
>  		fputs("(none)\n", f);
>  }
>  
> +static void fill_json_super_flags(struct json_obj *obj,
> +								  struct ext2_super_block *s)
> +{
> +	int	flags_found = 0;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"filesystem-flags", JSON_VAL_STRING);
> +
> +	if (s->s_flags == 0)
> +		return;
> +
> +	if (s->s_flags & EXT2_FLAGS_SIGNED_HASH)
> +		json_list_add_str(list, "signed_directory_hash");
> +	if (s->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
> +		json_list_add_str(list, "unsigned_directory_hash");
> +	if (s->s_flags & EXT2_FLAGS_TEST_FILESYS)
> +		json_list_add_str(list, "test_filesystem");
> +}
> +
> +
>  static __u64 e2p_blocks_count(struct ext2_super_block *super)
>  {
>  	return super->s_blocks_count |
> @@ -213,6 +288,12 @@ static const char *quota_prefix[MAXQUOTAS] = {
>  	[PRJQUOTA] = "Project quota inode:",
>  };
>  
> +static const char *json_quota_prefix[MAXQUOTAS] = {
> +	[USRQUOTA] = "user-quota-inode",
> +	[GRPQUOTA] = "group-quota-inode",
> +	[PRJQUOTA] = "project-quota-inode",
> +};
> +
>  /**
>   * Convert type of quota to written representation
>   */
> @@ -477,3 +558,284 @@ void list_super (struct ext2_super_block * s)
>  	list_super2(s, stdout);
>  }
>  
> +static void fill_json_time(struct json_obj *obj, const char *key, char *buf,
> +						   time_t tm)
> +{
> +	char *str;
> +
> +	if (tm && (ctime_r(&tm, buf))) {
> +		if (str = strchr(buf, '\n'))
> +			*str = '\0';
> +		json_obj_add_str(obj, key, buf);
> +	}
> +}
> +
> +void fill_json_super(struct json_obj *obj, struct ext2_super_block * sb)
> +{
> +	int inode_blocks_per_group;
> +	char buf[80], *str;
> +	time_t	tm;
> +	enum quota_type qtype;
> +	struct json_obj *super_obj = json_obj_create_in_obj(obj, "super");
> +
> +	inode_blocks_per_group = (((sb->s_inodes_per_group *
> +				    EXT2_INODE_SIZE(sb)) +
> +				   EXT2_BLOCK_SIZE(sb) - 1) /
> +				  EXT2_BLOCK_SIZE(sb));
> +	if (sb->s_volume_name[0]) {
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name));
> +	} else
> +		strcpy(buf, "<none>");
> +	json_obj_add_str(super_obj, "fs-volume-name", buf);
> +	if (sb->s_last_mounted[0]) {
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted));
> +	} else
> +		strcpy(buf, "<not available>");
> +	json_obj_add_str(super_obj, "last-mounted-on", buf);
> +	json_obj_add_str(super_obj, "fs-uuid", e2p_uuid2str(sb->s_uuid));
> +	json_obj_add_fmt_buf_str(super_obj, "fs-magic-number", buf, sizeof(buf),
> +		"0x%04X", sb->s_magic);
> +	json_obj_add_fmt_buf_str(super_obj, "fs-revision-level", buf, sizeof(buf),
> +		"%d", sb->s_rev_level);
> +	fill_json_features(super_obj, sb);
> +	fill_json_super_flags(super_obj, sb);
> +	fill_json_mntopts(super_obj, sb);
> +	if (sb->s_mount_opts[0])
> +		json_obj_add_str(super_obj, "mount-options", sb->s_mount_opts);
> +	snprint_fs_state(buf, sizeof(buf), sb->s_state);
> +	json_obj_add_str(super_obj, "fs-state", buf);
> +	snprint_fs_errors(buf, sizeof(buf), sb->s_errors);
> +	json_obj_add_str(super_obj, "errors-behaviour", buf);
> +	str = e2p_os2string(sb->s_creator_os);
> +	json_obj_add_str(super_obj, "fs-os-type", str);
> +	free(str);
> +	json_obj_add_fmt_buf_str(super_obj, "inode-count", buf, sizeof(buf), "%u",
> +		sb->s_inodes_count);
> +	json_obj_add_fmt_buf_str(super_obj, "block-count", buf, sizeof(buf),
> +		"%llu", e2p_blocks_count(sb));
> +	json_obj_add_fmt_buf_str(super_obj, "reserved-block-count", buf,
> +		sizeof(buf), "%llu", e2p_r_blocks_count(sb));
> +	if (sb->s_overhead_blocks)
> +		json_obj_add_fmt_buf_str(super_obj, "overhead-blocks", buf,
> +			sizeof(buf), "%u", sb->s_overhead_blocks);
> +	json_obj_add_fmt_buf_str(super_obj, "free-blocks", buf, sizeof(buf),
> +		"%llu", e2p_free_blocks_count(sb));
> +	json_obj_add_fmt_buf_str(super_obj, "free-inodes", buf, sizeof(buf), "%u",
> +		sb->s_free_inodes_count);
> +	json_obj_add_fmt_buf_str(super_obj, "first-block", buf, sizeof(buf), "%u",
> +		sb->s_first_data_block);
> +	json_obj_add_fmt_buf_str(super_obj, "block-size", buf, sizeof(buf), "%u",
> +		EXT2_BLOCK_SIZE(sb));
> +	if (ext2fs_has_feature_bigalloc(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "cluster-size", buf, sizeof(buf),
> +			"%u", EXT2_CLUSTER_SIZE(sb));
> +	else
> +		json_obj_add_fmt_buf_str(super_obj, "fragment-size", buf, sizeof(buf),
> +			"%u", EXT2_CLUSTER_SIZE(sb));
> +
> +	if (ext2fs_has_feature_64bit(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "group-descriptor-size", buf,
> +			sizeof(buf), "%u", sb->s_desc_size);
> +	if (sb->s_reserved_gdt_blocks)
> +		json_obj_add_fmt_buf_str(super_obj, "reserved-gdt-blocks", buf,
> +			sizeof(buf), "%u", sb->s_reserved_gdt_blocks);
> +	json_obj_add_fmt_buf_str(super_obj, "blocks-per-group", buf, sizeof(buf),
> +		"%u", sb->s_blocks_per_group);
> +	if (ext2fs_has_feature_bigalloc(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "clusters-per-group", buf,
> +			sizeof(buf), "%u", sb->s_clusters_per_group);
> +	else
> +		json_obj_add_fmt_buf_str(super_obj, "fragments-per-group", buf,
> +			sizeof(buf), "%u", sb->s_clusters_per_group);
> +	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf, sizeof(buf),
> +		"%u", sb->s_inodes_per_group);
> +	json_obj_add_fmt_buf_str(super_obj, "inode-blocks-per-group", buf,
> +		sizeof(buf), "%u", inode_blocks_per_group);
> +	if (sb->s_raid_stride)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stride", buf, sizeof(buf),
> +			"%u", sb->s_raid_stride);
> +	if (sb->s_raid_stripe_width)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stripe-width", buf,
> +			sizeof(buf), "%u", sb->s_raid_stripe_width);
> +	if (sb->s_first_meta_bg)
> +		json_obj_add_fmt_buf_str(super_obj, "first-meta-block-group", buf,
> +			sizeof(buf), "%u", sb->s_first_meta_bg);
> +	if (sb->s_log_groups_per_flex)
> +		json_obj_add_fmt_buf_str(super_obj, "flex-block-group-size", buf,
> +			sizeof(buf), "%u", 1 << sb->s_log_groups_per_flex);
> +
> +	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf, sizeof(buf),
> +		"%u", sb->s_inodes_per_group);
> +	json_obj_add_fmt_buf_str(super_obj, "inode-blocks-per-group", buf,
> +		sizeof(buf), "%u", inode_blocks_per_group);
> +	if (sb->s_raid_stride)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stride", buf, sizeof(buf),
> +			"%u", sb->s_raid_stride);
> +	if (sb->s_raid_stripe_width)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stripe-width", buf,
> +			sizeof(buf), "%u", sb->s_raid_stripe_width);
> +	if (sb->s_first_meta_bg)
> +		json_obj_add_fmt_buf_str(super_obj, "first-meta-block-group", buf,
> +			sizeof(buf), "%u", sb->s_first_meta_bg);
> +	if (sb->s_log_groups_per_flex)
> +		json_obj_add_fmt_buf_str(super_obj, "flex-block-group-size", buf,
> +			sizeof(buf), "%u", 1 << sb->s_log_groups_per_flex);
> +	fill_json_time(super_obj, "filesystem-created", buf, sb->s_mkfs_time);
> +	fill_json_time(super_obj, "last-mount-time", buf, sb->s_mtime);
> +	fill_json_time(super_obj, "last-write-time", buf, sb->s_wtime);
> +	json_obj_add_fmt_buf_str(super_obj, "mount-count", buf, sizeof(buf),
> +		"%u", sb->s_mnt_count);
> +	json_obj_add_fmt_buf_str(super_obj, "maximum-mount-count", buf,
> +		sizeof(buf), "%d", sb->s_max_mnt_count);
> +	fill_json_time(super_obj, "last-checked", buf, sb->s_lastcheck);
> +	json_obj_add_fmt_buf_str(super_obj, "check-interval", buf, sizeof(buf),
> +		"%u", sb->s_checkinterval);
> +	json_obj_add_str(super_obj, "check-interval-str",
> +		interval_string(sb->s_checkinterval));
> +	if (sb->s_checkinterval)
> +	{
> +		time_t next = sb->s_lastcheck + sb->s_checkinterval;
> +
> +		fill_json_time(super_obj, "next-check-after", buf,
> +			sb->s_lastcheck + sb->s_checkinterval);
> +	}
> +#define POW2(x) ((__u64) 1 << (x))
> +	if (sb->s_kbytes_written) {
> +		if (sb->s_kbytes_written < POW2(13))
> +			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
> +				sizeof(buf), "%llu kB", sb->s_kbytes_written);
> +		else if (sb->s_kbytes_written < POW2(23))
> +			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
> +				sizeof(buf), "%llu MB",
> +				(sb->s_kbytes_written + POW2(9)) >> 10);
> +		else if (sb->s_kbytes_written < POW2(33))
> +			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
> +				sizeof(buf), "%llu GB",
> +				(sb->s_kbytes_written + POW2(19)) >> 20);
> +		else if (sb->s_kbytes_written < POW2(43))
> +			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
> +				sizeof(buf), "%llu TB",
> +				(sb->s_kbytes_written + POW2(29)) >> 30);
> +		else
> +			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
> +				sizeof(buf), "%llu PB",
> +				(sb->s_kbytes_written + POW2(39)) >> 40);
> +	}
> +	fill_json_user(super_obj, sb->s_def_resuid);
> +	fill_json_group(super_obj, sb->s_def_resgid);
> +	if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
> +		json_obj_add_fmt_buf_str(super_obj, "first-inode", buf, sizeof(buf),
> +				"%d", sb->s_first_ino);
> +		json_obj_add_fmt_buf_str(super_obj, "inode-size", buf, sizeof(buf),
> +				"%d", sb->s_inode_size);
> +		if (sb->s_min_extra_isize)
> +			json_obj_add_fmt_buf_str(super_obj, "required-extra-isize", buf,
> +				sizeof(buf), "%d", sb->s_min_extra_isize);
> +		if (sb->s_want_extra_isize)
> +			json_obj_add_fmt_buf_str(super_obj, "desired-extra-isize", buf,
> +				sizeof(buf), "%d", sb->s_want_extra_isize);
> +	}
> +	if (!e2p_is_null_uuid(sb->s_journal_uuid))
> +		json_obj_add_str(super_obj, "journal-uuid",
> +			e2p_uuid2str(sb->s_journal_uuid));
> +	if (sb->s_journal_inum)
> +		json_obj_add_fmt_buf_str(super_obj, "journal-inode", buf,
> +			sizeof(buf), "%u", sb->s_journal_inum);
> +	if (sb->s_journal_dev)
> +		json_obj_add_fmt_buf_str(super_obj, "journal-device", buf,
> +			sizeof(buf), "0x%04x", sb->s_journal_dev);
> +	if (sb->s_last_orphan)
> +		json_obj_add_fmt_buf_str(super_obj, "first-orphan-inode", buf,
> +			sizeof(buf), "%u", sb->s_last_orphan);
> +	if (ext2fs_has_feature_dir_index(sb) ||
> +	    sb->s_def_hash_version)
> +		json_obj_add_str(super_obj, "default-directory-hash",
> +			e2p_hash2string(sb->s_def_hash_version));
> +	if (!e2p_is_null_uuid(sb->s_hash_seed))
> +		json_obj_add_str(super_obj, "directory-hash-seed",
> +			e2p_uuid2str(sb->s_hash_seed));
> +	if (sb->s_jnl_backup_type) {
> +		switch (sb->s_jnl_backup_type) {
> +		case 1:
> +			json_obj_add_str(super_obj, "journal-backup", "inode blocks");
> +			break;
> +		default:
> +			json_obj_add_fmt_buf_str(super_obj, "journal-backup", buf,
> +				sizeof(buf), "type %u", sb->s_jnl_backup_type);
> +		}
> +	}
> +	if (sb->s_backup_bgs[0])
> +		json_obj_add_fmt_buf_str(super_obj, "backup-block-group-0", buf,
> +			sizeof(buf), "%u", sb->s_backup_bgs[0]);
> +	if (sb->s_backup_bgs[1])
> +		json_obj_add_fmt_buf_str(super_obj, "backup-block-group-1", buf,
> +			sizeof(buf), "%u", sb->s_backup_bgs[1]);
> +	if (sb->s_snapshot_inum) {
> +		json_obj_add_fmt_buf_str(super_obj, "snapshot-inode", buf, sizeof(buf),
> +			"%u", sb->s_snapshot_inum);
> +		json_obj_add_fmt_buf_str(super_obj, "snapshot-id", buf, sizeof(buf),
> +			"%u", sb->s_snapshot_id);
> +		json_obj_add_fmt_buf_str(super_obj, "snapshot-reserved-blocks", buf,
> +			sizeof(buf), "%llu", sb->s_snapshot_r_blocks_count);
> +	}
> +	if (sb->s_snapshot_list)
> +		json_obj_add_fmt_buf_str(super_obj, "snapshot-list-head", buf,
> +			sizeof(buf), "%u", sb->s_snapshot_list);
> +	if (sb->s_error_count)
> +		json_obj_add_fmt_buf_str(super_obj, "fs-error-count", buf, sizeof(buf),
> +			"%u", sb->s_error_count);
> +	if (sb->s_first_error_time) {
> +		fill_json_time(super_obj, "first-error-time", buf,
> +			sb->s_first_error_time);
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, (char *)sb->s_first_error_func,
> +			sizeof(sb->s_first_error_func));
> +		json_obj_add_str(super_obj, "first-error-function", buf);
> +		json_obj_add_fmt_buf_str(super_obj, "first-error-line-num", buf,
> +			sizeof(buf), "%u", sb->s_first_error_line);
> +		json_obj_add_fmt_buf_str(super_obj, "first-error-inode-num", buf,
> +			sizeof(buf), "%u", sb->s_first_error_ino);
> +		json_obj_add_fmt_buf_str(super_obj, "first-error-block-num", buf,
> +			sizeof(buf), "%llu", sb->s_first_error_block);
> +	}
> +	if (sb->s_last_error_time) {
> +		fill_json_time(super_obj, "last-error-time", buf,
> +			sb->s_last_error_time);
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, (char *)sb->s_last_error_func,
> +			sizeof(sb->s_last_error_func));
> +		json_obj_add_str(super_obj, "last-error-function", buf);
> +		json_obj_add_fmt_buf_str(super_obj, "last-error-line-num", buf,
> +			sizeof(buf), "%u", sb->s_last_error_line);
> +		json_obj_add_fmt_buf_str(super_obj, "last-error-inode-num", buf,
> +			sizeof(buf), "%u", sb->s_last_error_ino);
> +		json_obj_add_fmt_buf_str(super_obj, "last-error-block-num", buf,
> +			sizeof(buf), "%llu", sb->s_last_error_block);
> +	}
> +	if (ext2fs_has_feature_mmp(sb)) {
> +		json_obj_add_fmt_buf_str(super_obj, "mmp-block-number", buf,
> +			sizeof(buf), "%llu", (long long)sb->s_mmp_block);
> +		json_obj_add_fmt_buf_str(super_obj, "mmp-update-interval", buf,
> +			sizeof(buf), "%u", sb->s_mmp_update_interval);
> +	}
> +	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
> +		if (*quota_sb_inump(sb, qtype) != 0)
> +			json_obj_add_fmt_buf_str(super_obj, json_quota_prefix[qtype], buf,
> +				sizeof(buf), "%u", *quota_sb_inump(sb, qtype));
> +	}
> +	if (ext2fs_has_feature_metadata_csum(sb)) {
> +		json_obj_add_str(super_obj, "checksum-type",
> +			checksum_type(sb->s_checksum_type));
> +		json_obj_add_fmt_buf_str(super_obj, "checksum", buf, sizeof(buf),
> +			"0x%08x", sb->s_checksum);
> +	}
> +	if (!e2p_is_null_uuid(sb->s_encrypt_pw_salt))
> +		json_obj_add_str(super_obj, "encryption-pw-salt",
> +			e2p_uuid2str(sb->s_encrypt_pw_salt));
> +
> +	if (ext2fs_has_feature_csum_seed(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "checksum-seed", buf, sizeof(buf),
> +			"0x%08x", sb->s_checksum_seed);
> +}
> diff --git a/lib/e2p/pe.c b/lib/e2p/pe.c
> index 1f24545d..9426e743 100644
> --- a/lib/e2p/pe.c
> +++ b/lib/e2p/pe.c
> @@ -38,3 +38,21 @@ void print_fs_errors (FILE * f, unsigned short errors)
>  			fprintf (f, "Unknown (continue)");
>  	}
>  }
> +
> +void snprint_fs_errors (char *buf, size_t size, unsigned short errors)
> +{
> +	switch (errors)
> +	{
> +		case EXT2_ERRORS_CONTINUE:
> +			snprintf (buf, size, "Continue");
> +			break;
> +		case EXT2_ERRORS_RO:
> +			snprintf (buf, size, "Remount read-only");
> +			break;
> +		case EXT2_ERRORS_PANIC:
> +			snprintf (buf, size, "Panic");
> +			break;
> +		default:
> +			snprintf (buf, size, "Unknown");
> +	}
> +}
> diff --git a/lib/e2p/ps.c b/lib/e2p/ps.c
> index 757f8a66..19eb6559 100644
> --- a/lib/e2p/ps.c
> +++ b/lib/e2p/ps.c
> @@ -30,3 +30,13 @@ void print_fs_state (FILE * f, unsigned short state)
>  	if (state & EXT2_ERROR_FS)
>  		fprintf (f, " with errors");
>  }
> +
> +void snprint_fs_state (char *str, size_t size, unsigned short state)
> +{
> +	if (state & EXT2_VALID_FS)
> +		snprintf (str, size, "clean");
> +	else
> +		snprintf (str, size, "not clean");
> +	if (state & EXT2_ERROR_FS)
> +		snprintf (str, size, "with errors");
> +}
> diff --git a/misc/Makefile.in b/misc/Makefile.in
> index 6f631eb1..a863ef62 100644
> --- a/misc/Makefile.in
> +++ b/misc/Makefile.in
> @@ -170,15 +170,15 @@ e2initrd_helper: e2initrd_helper.o $(DEPLIBS) $(DEPLIBBLKID) $(LIBEXT2FS)
>  tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS) $(DEPLIBS_E2P) $(DEPLIBBLKID) \
>  		$(DEPLIBUUID) $(LIBEXT2FS)
>  	$(E) "	LD $@"
> -	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) $(LIBS) \
> -		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) \
> +	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) \
> +		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) $(LIBS) \
>  		$(LIBINTL) $(SYSLIBS) $(LIBBLKID) $(LIBMAGIC)
>  
>  tune2fs.static: $(TUNE2FS_OBJS) $(STATIC_DEPLIBS) $(STATIC_LIBE2P) $(DEPSTATIC_LIBBLKID)
>  	$(E) "	LD $@"
>  	$(Q) $(CC) $(LDFLAGS_STATIC) -o tune2fs.static $(TUNE2FS_OBJS) \
> -		$(STATIC_LIBS) $(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
> -		$(STATIC_LIBE2P) $(LIBINTL) $(SYSLIBS) \
> +		$(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
> +		$(STATIC_LIBE2P) $(STATIC_LIBS) $(LIBINTL) $(SYSLIBS) \
>  		$(STATIC_LIBBLKID) $(LIBMAGIC)
>  
>  tune2fs.profiled: $(TUNE2FS_OBJS) $(PROFILED_DEPLIBS) \
> diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> index 319c296b..93e4b2ff 100644
> --- a/misc/dumpe2fs.c
> +++ b/misc/dumpe2fs.c
> @@ -863,7 +863,10 @@ try_open_again:
>  	} else {
>  		if (grp_only)
>  			goto just_descriptors;
> -		list_super (fs->super);
> +		if (json)
> +			fill_json_super(dump_obj, fs->super);
> +		else
> +			list_super (fs->super);
>  		if (ext2fs_has_feature_journal_dev(fs->super)) {
>  			print_journal_information(fs, dump_obj);
>  			if (json) {
> -- 
> 2.14.1
Viktor Prutyanov April 5, 2018, 3:24 p.m. | #2
On Tue, 20 Feb 2018 12:59:48 +0300
Viktor Prutyanov <viktor.prutyanov@virtuozzo.com> wrote:

ping

> This patch adds JSON output of superblock information
> 
> Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
> ---
>  debugfs/Makefile.in |   4 +-
>  lib/e2p/e2p.h       |   6 +
>  lib/e2p/ls.c        | 362
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/e2p/pe.c        |  18 +++ lib/e2p/ps.c        |  10 ++
>  misc/Makefile.in    |   8 +-
>  misc/dumpe2fs.c     |   5 +-
>  7 files changed, 406 insertions(+), 7 deletions(-)
> 
> diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in
> index 57940cde..92a3552a 100644
> --- a/debugfs/Makefile.in
> +++ b/debugfs/Makefile.in
> @@ -34,9 +34,9 @@ SRCS= debug_cmds.c $(srcdir)/debugfs.c
> $(srcdir)/util.c $(srcdir)/ls.c \ $(srcdir)/journal.c
> $(srcdir)/../e2fsck/revoke.c \ $(srcdir)/../e2fsck/recovery.c
> $(srcdir)/do_journal.c 
> -LIBS= $(LIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR)
> $(LIBBLKID) \ +LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT) $(LIBSS)
> $(LIBCOM_ERR) $(LIBBLKID) \ $(LIBUUID) $(LIBMAGIC) $(SYSLIBS)
> -DEPLIBS= $(DEPLIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(DEPLIBSS)
> $(DEPLIBCOM_ERR) \ +DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT)
> $(DEPLIBSS) $(DEPLIBCOM_ERR) \ $(DEPLIBBLKID) $(DEPLIBUUID)
>  
>  STATIC_LIBS= $(STATIC_LIBSUPPORT) $(STATIC_LIBEXT2FS)
> $(STATIC_LIBSS) \ diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
> index e96cdec8..88c08d73 100644
> --- a/lib/e2p/e2p.h
> +++ b/lib/e2p/e2p.h
> @@ -13,6 +13,8 @@
>  
>  #include <ext2fs/ext2_fs.h>
>  
> +#include "support/json-out.h"
> +
>  #define E2P_FEATURE_COMPAT	0
>  #define E2P_FEATURE_INCOMPAT	1
>  #define E2P_FEATURE_RO_INCOMPAT	2
> @@ -42,8 +44,10 @@ int iterate_on_dir (const char * dir_name,
>  void list_super(struct ext2_super_block * s);
>  void list_super2(struct ext2_super_block * s, FILE *f);
>  void print_fs_errors (FILE * f, unsigned short errors);
> +void snprint_fs_errors (char *str, size_t size, unsigned short
> errors); void print_flags (FILE * f, unsigned long flags, unsigned
> options); void print_fs_state (FILE * f, unsigned short state);
> +void snprint_fs_state (char *str, size_t size, unsigned short state);
>  int setflags (int fd, unsigned long flags);
>  int setversion (int fd, unsigned long version);
>  
> @@ -77,3 +81,5 @@ char *e2p_os2string(int os_type);
>  int e2p_string2os(char *str);
>  
>  unsigned int e2p_percent(int percent, unsigned int base);
> +
> +void fill_json_super(struct json_obj *obj, struct ext2_super_block *
> s); diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
> index a7586e09..b690960b 100644
> --- a/lib/e2p/ls.c
> +++ b/lib/e2p/ls.c
> @@ -37,6 +37,15 @@ static void print_user (unsigned short uid, FILE
> *f) fprintf(f, "(user %s)\n", pw->pw_name);
>  }
>  
> +static void fill_json_user(struct json_obj *obj, unsigned short uid)
> +{
> +	struct passwd *pw = getpwuid(uid);
> +
> +	json_obj_add_fmt_str(obj, "uid", 32, "%u", uid);
> +	if (pw)
> +		json_obj_add_str(obj, "user", pw->pw_name);
> +}
> +
>  static void print_group (unsigned short gid, FILE *f)
>  {
>  	struct group *gr;
> @@ -49,6 +58,15 @@ static void print_group (unsigned short gid, FILE
> *f) fprintf(f, "(group %s)\n", gr->gr_name);
>  }
>  
> +static void fill_json_group(struct json_obj *obj, unsigned short gid)
> +{
> +	struct group *gr = getgrgid(gid);
> +
> +	json_obj_add_fmt_str(obj, "gid", 32, "%u", gid);
> +	if (gr)
> +		json_obj_add_str(obj, "group", gr->gr_name);
> +}
> +
>  #define MONTH_INT (86400 * 30)
>  #define WEEK_INT (86400 * 7)
>  #define DAY_INT	(86400)
> @@ -117,6 +135,24 @@ static void print_features(struct
> ext2_super_block * s, FILE *f) #endif
>  }
>  
> +static void fill_json_features(struct json_obj *obj,
> +							   struct
> ext2_super_block *s) +{
> +#ifdef EXT2_DYNAMIC_REV
> +	int	i, j;
> +	__u32	*mask = &s->s_feature_compat, m;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"filesystem-features",
> JSON_VAL_STRING); +
> +	for (i=0; i <3; i++,mask++) {
> +		for (j=0,m=1; j < 32; j++, m<<=1) {
> +			if (*mask & m)
> +				json_list_add_str(list,
> e2p_feature2string(i, m));
> +		}
> +	}
> +#endif
> +}
> +
>  static void print_mntopts(struct ext2_super_block * s, FILE *f)
>  {
>  #ifdef EXT2_DYNAMIC_REV
> @@ -142,6 +178,26 @@ static void print_mntopts(struct
> ext2_super_block * s, FILE *f) #endif
>  }
>  
> +static void fill_json_mntopts(struct json_obj *obj,
> +							  struct
> ext2_super_block *s) +{
> +#ifdef EXT2_DYNAMIC_REV
> +	int	i;
> +	__u32	mask = s->s_default_mount_opts, m;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"default-mount-options",
> JSON_VAL_STRING); +
> +	if (mask & EXT3_DEFM_JMODE)
> +		json_list_add_str(list, e2p_mntopt2string(mask &
> EXT3_DEFM_JMODE));
> +	for (i=0,m=1; i < 32; i++, m<<=1) {
> +		if (m & EXT3_DEFM_JMODE)
> +			continue;
> +		if (mask & m)
> +			json_list_add_str(list,
> e2p_mntopt2string(m));
> +	}
> +#endif
> +}
> +
>  static void print_super_flags(struct ext2_super_block * s, FILE *f)
>  {
>  	int	flags_found = 0;
> @@ -168,6 +224,25 @@ static void print_super_flags(struct
> ext2_super_block * s, FILE *f) fputs("(none)\n", f);
>  }
>  
> +static void fill_json_super_flags(struct json_obj *obj,
> +
> struct ext2_super_block *s) +{
> +	int	flags_found = 0;
> +	struct json_list *list = json_list_create_in_obj(obj,
> +								"filesystem-flags",
> JSON_VAL_STRING); +
> +	if (s->s_flags == 0)
> +		return;
> +
> +	if (s->s_flags & EXT2_FLAGS_SIGNED_HASH)
> +		json_list_add_str(list, "signed_directory_hash");
> +	if (s->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
> +		json_list_add_str(list, "unsigned_directory_hash");
> +	if (s->s_flags & EXT2_FLAGS_TEST_FILESYS)
> +		json_list_add_str(list, "test_filesystem");
> +}
> +
> +
>  static __u64 e2p_blocks_count(struct ext2_super_block *super)
>  {
>  	return super->s_blocks_count |
> @@ -213,6 +288,12 @@ static const char *quota_prefix[MAXQUOTAS] = {
>  	[PRJQUOTA] = "Project quota inode:",
>  };
>  
> +static const char *json_quota_prefix[MAXQUOTAS] = {
> +	[USRQUOTA] = "user-quota-inode",
> +	[GRPQUOTA] = "group-quota-inode",
> +	[PRJQUOTA] = "project-quota-inode",
> +};
> +
>  /**
>   * Convert type of quota to written representation
>   */
> @@ -477,3 +558,284 @@ void list_super (struct ext2_super_block * s)
>  	list_super2(s, stdout);
>  }
>  
> +static void fill_json_time(struct json_obj *obj, const char *key,
> char *buf,
> +						   time_t tm)
> +{
> +	char *str;
> +
> +	if (tm && (ctime_r(&tm, buf))) {
> +		if (str = strchr(buf, '\n'))
> +			*str = '\0';
> +		json_obj_add_str(obj, key, buf);
> +	}
> +}
> +
> +void fill_json_super(struct json_obj *obj, struct ext2_super_block *
> sb) +{
> +	int inode_blocks_per_group;
> +	char buf[80], *str;
> +	time_t	tm;
> +	enum quota_type qtype;
> +	struct json_obj *super_obj = json_obj_create_in_obj(obj,
> "super"); +
> +	inode_blocks_per_group = (((sb->s_inodes_per_group *
> +				    EXT2_INODE_SIZE(sb)) +
> +				   EXT2_BLOCK_SIZE(sb) - 1) /
> +				  EXT2_BLOCK_SIZE(sb));
> +	if (sb->s_volume_name[0]) {
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, sb->s_volume_name,
> sizeof(sb->s_volume_name));
> +	} else
> +		strcpy(buf, "<none>");
> +	json_obj_add_str(super_obj, "fs-volume-name", buf);
> +	if (sb->s_last_mounted[0]) {
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, sb->s_last_mounted,
> sizeof(sb->s_last_mounted));
> +	} else
> +		strcpy(buf, "<not available>");
> +	json_obj_add_str(super_obj, "last-mounted-on", buf);
> +	json_obj_add_str(super_obj, "fs-uuid",
> e2p_uuid2str(sb->s_uuid));
> +	json_obj_add_fmt_buf_str(super_obj, "fs-magic-number", buf,
> sizeof(buf),
> +		"0x%04X", sb->s_magic);
> +	json_obj_add_fmt_buf_str(super_obj, "fs-revision-level",
> buf, sizeof(buf),
> +		"%d", sb->s_rev_level);
> +	fill_json_features(super_obj, sb);
> +	fill_json_super_flags(super_obj, sb);
> +	fill_json_mntopts(super_obj, sb);
> +	if (sb->s_mount_opts[0])
> +		json_obj_add_str(super_obj, "mount-options",
> sb->s_mount_opts);
> +	snprint_fs_state(buf, sizeof(buf), sb->s_state);
> +	json_obj_add_str(super_obj, "fs-state", buf);
> +	snprint_fs_errors(buf, sizeof(buf), sb->s_errors);
> +	json_obj_add_str(super_obj, "errors-behaviour", buf);
> +	str = e2p_os2string(sb->s_creator_os);
> +	json_obj_add_str(super_obj, "fs-os-type", str);
> +	free(str);
> +	json_obj_add_fmt_buf_str(super_obj, "inode-count", buf,
> sizeof(buf), "%u",
> +		sb->s_inodes_count);
> +	json_obj_add_fmt_buf_str(super_obj, "block-count", buf,
> sizeof(buf),
> +		"%llu", e2p_blocks_count(sb));
> +	json_obj_add_fmt_buf_str(super_obj, "reserved-block-count",
> buf,
> +		sizeof(buf), "%llu", e2p_r_blocks_count(sb));
> +	if (sb->s_overhead_blocks)
> +		json_obj_add_fmt_buf_str(super_obj,
> "overhead-blocks", buf,
> +			sizeof(buf), "%u", sb->s_overhead_blocks);
> +	json_obj_add_fmt_buf_str(super_obj, "free-blocks", buf,
> sizeof(buf),
> +		"%llu", e2p_free_blocks_count(sb));
> +	json_obj_add_fmt_buf_str(super_obj, "free-inodes", buf,
> sizeof(buf), "%u",
> +		sb->s_free_inodes_count);
> +	json_obj_add_fmt_buf_str(super_obj, "first-block", buf,
> sizeof(buf), "%u",
> +		sb->s_first_data_block);
> +	json_obj_add_fmt_buf_str(super_obj, "block-size", buf,
> sizeof(buf), "%u",
> +		EXT2_BLOCK_SIZE(sb));
> +	if (ext2fs_has_feature_bigalloc(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "cluster-size",
> buf, sizeof(buf),
> +			"%u", EXT2_CLUSTER_SIZE(sb));
> +	else
> +		json_obj_add_fmt_buf_str(super_obj, "fragment-size",
> buf, sizeof(buf),
> +			"%u", EXT2_CLUSTER_SIZE(sb));
> +
> +	if (ext2fs_has_feature_64bit(sb))
> +		json_obj_add_fmt_buf_str(super_obj,
> "group-descriptor-size", buf,
> +			sizeof(buf), "%u", sb->s_desc_size);
> +	if (sb->s_reserved_gdt_blocks)
> +		json_obj_add_fmt_buf_str(super_obj,
> "reserved-gdt-blocks", buf,
> +			sizeof(buf), "%u",
> sb->s_reserved_gdt_blocks);
> +	json_obj_add_fmt_buf_str(super_obj, "blocks-per-group", buf,
> sizeof(buf),
> +		"%u", sb->s_blocks_per_group);
> +	if (ext2fs_has_feature_bigalloc(sb))
> +		json_obj_add_fmt_buf_str(super_obj,
> "clusters-per-group", buf,
> +			sizeof(buf), "%u", sb->s_clusters_per_group);
> +	else
> +		json_obj_add_fmt_buf_str(super_obj,
> "fragments-per-group", buf,
> +			sizeof(buf), "%u", sb->s_clusters_per_group);
> +	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf,
> sizeof(buf),
> +		"%u", sb->s_inodes_per_group);
> +	json_obj_add_fmt_buf_str(super_obj,
> "inode-blocks-per-group", buf,
> +		sizeof(buf), "%u", inode_blocks_per_group);
> +	if (sb->s_raid_stride)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stride",
> buf, sizeof(buf),
> +			"%u", sb->s_raid_stride);
> +	if (sb->s_raid_stripe_width)
> +		json_obj_add_fmt_buf_str(super_obj,
> "raid-stripe-width", buf,
> +			sizeof(buf), "%u", sb->s_raid_stripe_width);
> +	if (sb->s_first_meta_bg)
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-meta-block-group", buf,
> +			sizeof(buf), "%u", sb->s_first_meta_bg);
> +	if (sb->s_log_groups_per_flex)
> +		json_obj_add_fmt_buf_str(super_obj,
> "flex-block-group-size", buf,
> +			sizeof(buf), "%u", 1 <<
> sb->s_log_groups_per_flex); +
> +	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf,
> sizeof(buf),
> +		"%u", sb->s_inodes_per_group);
> +	json_obj_add_fmt_buf_str(super_obj,
> "inode-blocks-per-group", buf,
> +		sizeof(buf), "%u", inode_blocks_per_group);
> +	if (sb->s_raid_stride)
> +		json_obj_add_fmt_buf_str(super_obj, "raid-stride",
> buf, sizeof(buf),
> +			"%u", sb->s_raid_stride);
> +	if (sb->s_raid_stripe_width)
> +		json_obj_add_fmt_buf_str(super_obj,
> "raid-stripe-width", buf,
> +			sizeof(buf), "%u", sb->s_raid_stripe_width);
> +	if (sb->s_first_meta_bg)
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-meta-block-group", buf,
> +			sizeof(buf), "%u", sb->s_first_meta_bg);
> +	if (sb->s_log_groups_per_flex)
> +		json_obj_add_fmt_buf_str(super_obj,
> "flex-block-group-size", buf,
> +			sizeof(buf), "%u", 1 <<
> sb->s_log_groups_per_flex);
> +	fill_json_time(super_obj, "filesystem-created", buf,
> sb->s_mkfs_time);
> +	fill_json_time(super_obj, "last-mount-time", buf,
> sb->s_mtime);
> +	fill_json_time(super_obj, "last-write-time", buf,
> sb->s_wtime);
> +	json_obj_add_fmt_buf_str(super_obj, "mount-count", buf,
> sizeof(buf),
> +		"%u", sb->s_mnt_count);
> +	json_obj_add_fmt_buf_str(super_obj, "maximum-mount-count",
> buf,
> +		sizeof(buf), "%d", sb->s_max_mnt_count);
> +	fill_json_time(super_obj, "last-checked", buf,
> sb->s_lastcheck);
> +	json_obj_add_fmt_buf_str(super_obj, "check-interval", buf,
> sizeof(buf),
> +		"%u", sb->s_checkinterval);
> +	json_obj_add_str(super_obj, "check-interval-str",
> +		interval_string(sb->s_checkinterval));
> +	if (sb->s_checkinterval)
> +	{
> +		time_t next = sb->s_lastcheck + sb->s_checkinterval;
> +
> +		fill_json_time(super_obj, "next-check-after", buf,
> +			sb->s_lastcheck + sb->s_checkinterval);
> +	}
> +#define POW2(x) ((__u64) 1 << (x))
> +	if (sb->s_kbytes_written) {
> +		if (sb->s_kbytes_written < POW2(13))
> +			json_obj_add_fmt_buf_str(super_obj,
> "lifetime-writes", buf,
> +				sizeof(buf), "%llu kB",
> sb->s_kbytes_written);
> +		else if (sb->s_kbytes_written < POW2(23))
> +			json_obj_add_fmt_buf_str(super_obj,
> "lifetime-writes", buf,
> +				sizeof(buf), "%llu MB",
> +				(sb->s_kbytes_written + POW2(9)) >>
> 10);
> +		else if (sb->s_kbytes_written < POW2(33))
> +			json_obj_add_fmt_buf_str(super_obj,
> "lifetime-writes", buf,
> +				sizeof(buf), "%llu GB",
> +				(sb->s_kbytes_written + POW2(19)) >>
> 20);
> +		else if (sb->s_kbytes_written < POW2(43))
> +			json_obj_add_fmt_buf_str(super_obj,
> "lifetime-writes", buf,
> +				sizeof(buf), "%llu TB",
> +				(sb->s_kbytes_written + POW2(29)) >>
> 30);
> +		else
> +			json_obj_add_fmt_buf_str(super_obj,
> "lifetime-writes", buf,
> +				sizeof(buf), "%llu PB",
> +				(sb->s_kbytes_written + POW2(39)) >>
> 40);
> +	}
> +	fill_json_user(super_obj, sb->s_def_resuid);
> +	fill_json_group(super_obj, sb->s_def_resgid);
> +	if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
> +		json_obj_add_fmt_buf_str(super_obj, "first-inode",
> buf, sizeof(buf),
> +				"%d", sb->s_first_ino);
> +		json_obj_add_fmt_buf_str(super_obj, "inode-size",
> buf, sizeof(buf),
> +				"%d", sb->s_inode_size);
> +		if (sb->s_min_extra_isize)
> +			json_obj_add_fmt_buf_str(super_obj,
> "required-extra-isize", buf,
> +				sizeof(buf), "%d",
> sb->s_min_extra_isize);
> +		if (sb->s_want_extra_isize)
> +			json_obj_add_fmt_buf_str(super_obj,
> "desired-extra-isize", buf,
> +				sizeof(buf), "%d",
> sb->s_want_extra_isize);
> +	}
> +	if (!e2p_is_null_uuid(sb->s_journal_uuid))
> +		json_obj_add_str(super_obj, "journal-uuid",
> +			e2p_uuid2str(sb->s_journal_uuid));
> +	if (sb->s_journal_inum)
> +		json_obj_add_fmt_buf_str(super_obj, "journal-inode",
> buf,
> +			sizeof(buf), "%u", sb->s_journal_inum);
> +	if (sb->s_journal_dev)
> +		json_obj_add_fmt_buf_str(super_obj,
> "journal-device", buf,
> +			sizeof(buf), "0x%04x", sb->s_journal_dev);
> +	if (sb->s_last_orphan)
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-orphan-inode", buf,
> +			sizeof(buf), "%u", sb->s_last_orphan);
> +	if (ext2fs_has_feature_dir_index(sb) ||
> +	    sb->s_def_hash_version)
> +		json_obj_add_str(super_obj, "default-directory-hash",
> +			e2p_hash2string(sb->s_def_hash_version));
> +	if (!e2p_is_null_uuid(sb->s_hash_seed))
> +		json_obj_add_str(super_obj, "directory-hash-seed",
> +			e2p_uuid2str(sb->s_hash_seed));
> +	if (sb->s_jnl_backup_type) {
> +		switch (sb->s_jnl_backup_type) {
> +		case 1:
> +			json_obj_add_str(super_obj,
> "journal-backup", "inode blocks");
> +			break;
> +		default:
> +			json_obj_add_fmt_buf_str(super_obj,
> "journal-backup", buf,
> +				sizeof(buf), "type %u",
> sb->s_jnl_backup_type);
> +		}
> +	}
> +	if (sb->s_backup_bgs[0])
> +		json_obj_add_fmt_buf_str(super_obj,
> "backup-block-group-0", buf,
> +			sizeof(buf), "%u", sb->s_backup_bgs[0]);
> +	if (sb->s_backup_bgs[1])
> +		json_obj_add_fmt_buf_str(super_obj,
> "backup-block-group-1", buf,
> +			sizeof(buf), "%u", sb->s_backup_bgs[1]);
> +	if (sb->s_snapshot_inum) {
> +		json_obj_add_fmt_buf_str(super_obj,
> "snapshot-inode", buf, sizeof(buf),
> +			"%u", sb->s_snapshot_inum);
> +		json_obj_add_fmt_buf_str(super_obj, "snapshot-id",
> buf, sizeof(buf),
> +			"%u", sb->s_snapshot_id);
> +		json_obj_add_fmt_buf_str(super_obj,
> "snapshot-reserved-blocks", buf,
> +			sizeof(buf), "%llu",
> sb->s_snapshot_r_blocks_count);
> +	}
> +	if (sb->s_snapshot_list)
> +		json_obj_add_fmt_buf_str(super_obj,
> "snapshot-list-head", buf,
> +			sizeof(buf), "%u", sb->s_snapshot_list);
> +	if (sb->s_error_count)
> +		json_obj_add_fmt_buf_str(super_obj,
> "fs-error-count", buf, sizeof(buf),
> +			"%u", sb->s_error_count);
> +	if (sb->s_first_error_time) {
> +		fill_json_time(super_obj, "first-error-time", buf,
> +			sb->s_first_error_time);
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, (char *)sb->s_first_error_func,
> +			sizeof(sb->s_first_error_func));
> +		json_obj_add_str(super_obj, "first-error-function",
> buf);
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-error-line-num", buf,
> +			sizeof(buf), "%u", sb->s_first_error_line);
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-error-inode-num", buf,
> +			sizeof(buf), "%u", sb->s_first_error_ino);
> +		json_obj_add_fmt_buf_str(super_obj,
> "first-error-block-num", buf,
> +			sizeof(buf), "%llu",
> sb->s_first_error_block);
> +	}
> +	if (sb->s_last_error_time) {
> +		fill_json_time(super_obj, "last-error-time", buf,
> +			sb->s_last_error_time);
> +		memset(buf, 0, sizeof(buf));
> +		strncpy(buf, (char *)sb->s_last_error_func,
> +			sizeof(sb->s_last_error_func));
> +		json_obj_add_str(super_obj, "last-error-function",
> buf);
> +		json_obj_add_fmt_buf_str(super_obj,
> "last-error-line-num", buf,
> +			sizeof(buf), "%u", sb->s_last_error_line);
> +		json_obj_add_fmt_buf_str(super_obj,
> "last-error-inode-num", buf,
> +			sizeof(buf), "%u", sb->s_last_error_ino);
> +		json_obj_add_fmt_buf_str(super_obj,
> "last-error-block-num", buf,
> +			sizeof(buf), "%llu", sb->s_last_error_block);
> +	}
> +	if (ext2fs_has_feature_mmp(sb)) {
> +		json_obj_add_fmt_buf_str(super_obj,
> "mmp-block-number", buf,
> +			sizeof(buf), "%llu", (long
> long)sb->s_mmp_block);
> +		json_obj_add_fmt_buf_str(super_obj,
> "mmp-update-interval", buf,
> +			sizeof(buf), "%u",
> sb->s_mmp_update_interval);
> +	}
> +	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
> +		if (*quota_sb_inump(sb, qtype) != 0)
> +			json_obj_add_fmt_buf_str(super_obj,
> json_quota_prefix[qtype], buf,
> +				sizeof(buf), "%u",
> *quota_sb_inump(sb, qtype));
> +	}
> +	if (ext2fs_has_feature_metadata_csum(sb)) {
> +		json_obj_add_str(super_obj, "checksum-type",
> +			checksum_type(sb->s_checksum_type));
> +		json_obj_add_fmt_buf_str(super_obj, "checksum", buf,
> sizeof(buf),
> +			"0x%08x", sb->s_checksum);
> +	}
> +	if (!e2p_is_null_uuid(sb->s_encrypt_pw_salt))
> +		json_obj_add_str(super_obj, "encryption-pw-salt",
> +			e2p_uuid2str(sb->s_encrypt_pw_salt));
> +
> +	if (ext2fs_has_feature_csum_seed(sb))
> +		json_obj_add_fmt_buf_str(super_obj, "checksum-seed",
> buf, sizeof(buf),
> +			"0x%08x", sb->s_checksum_seed);
> +}
> diff --git a/lib/e2p/pe.c b/lib/e2p/pe.c
> index 1f24545d..9426e743 100644
> --- a/lib/e2p/pe.c
> +++ b/lib/e2p/pe.c
> @@ -38,3 +38,21 @@ void print_fs_errors (FILE * f, unsigned short
> errors) fprintf (f, "Unknown (continue)");
>  	}
>  }
> +
> +void snprint_fs_errors (char *buf, size_t size, unsigned short
> errors) +{
> +	switch (errors)
> +	{
> +		case EXT2_ERRORS_CONTINUE:
> +			snprintf (buf, size, "Continue");
> +			break;
> +		case EXT2_ERRORS_RO:
> +			snprintf (buf, size, "Remount read-only");
> +			break;
> +		case EXT2_ERRORS_PANIC:
> +			snprintf (buf, size, "Panic");
> +			break;
> +		default:
> +			snprintf (buf, size, "Unknown");
> +	}
> +}
> diff --git a/lib/e2p/ps.c b/lib/e2p/ps.c
> index 757f8a66..19eb6559 100644
> --- a/lib/e2p/ps.c
> +++ b/lib/e2p/ps.c
> @@ -30,3 +30,13 @@ void print_fs_state (FILE * f, unsigned short
> state) if (state & EXT2_ERROR_FS)
>  		fprintf (f, " with errors");
>  }
> +
> +void snprint_fs_state (char *str, size_t size, unsigned short state)
> +{
> +	if (state & EXT2_VALID_FS)
> +		snprintf (str, size, "clean");
> +	else
> +		snprintf (str, size, "not clean");
> +	if (state & EXT2_ERROR_FS)
> +		snprintf (str, size, "with errors");
> +}
> diff --git a/misc/Makefile.in b/misc/Makefile.in
> index 6f631eb1..a863ef62 100644
> --- a/misc/Makefile.in
> +++ b/misc/Makefile.in
> @@ -170,15 +170,15 @@ e2initrd_helper: e2initrd_helper.o $(DEPLIBS)
> $(DEPLIBBLKID) $(LIBEXT2FS) tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS)
> $(DEPLIBS_E2P) $(DEPLIBBLKID) \ $(DEPLIBUUID) $(LIBEXT2FS)
>  	$(E) "	LD $@"
> -	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) $(LIBS)
> \
> -		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) \
> +	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) \
> +		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P)
> $(LIBS) \ $(LIBINTL) $(SYSLIBS) $(LIBBLKID) $(LIBMAGIC)
>  
>  tune2fs.static: $(TUNE2FS_OBJS) $(STATIC_DEPLIBS) $(STATIC_LIBE2P)
> $(DEPSTATIC_LIBBLKID) $(E) "	LD $@"
>  	$(Q) $(CC) $(LDFLAGS_STATIC) -o tune2fs.static
> $(TUNE2FS_OBJS) \
> -		$(STATIC_LIBS) $(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
> -		$(STATIC_LIBE2P) $(LIBINTL) $(SYSLIBS) \
> +		$(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
> +		$(STATIC_LIBE2P) $(STATIC_LIBS) $(LIBINTL)
> $(SYSLIBS) \ $(STATIC_LIBBLKID) $(LIBMAGIC)
>  
>  tune2fs.profiled: $(TUNE2FS_OBJS) $(PROFILED_DEPLIBS) \
> diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> index 319c296b..93e4b2ff 100644
> --- a/misc/dumpe2fs.c
> +++ b/misc/dumpe2fs.c
> @@ -863,7 +863,10 @@ try_open_again:
>  	} else {
>  		if (grp_only)
>  			goto just_descriptors;
> -		list_super (fs->super);
> +		if (json)
> +			fill_json_super(dump_obj, fs->super);
> +		else
> +			list_super (fs->super);
>  		if (ext2fs_has_feature_journal_dev(fs->super)) {
>  			print_journal_information(fs, dump_obj);
>  			if (json) {
Theodore Y. Ts'o April 19, 2018, 3:16 p.m. | #3
On Tue, Feb 20, 2018 at 12:59:48PM +0300, Viktor Prutyanov wrote:
> This patch adds JSON output of superblock information
> 
> Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
> ---
>  debugfs/Makefile.in |   4 +-
>  lib/e2p/e2p.h       |   6 +
>  lib/e2p/ls.c        | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/e2p/pe.c        |  18 +++
>  lib/e2p/ps.c        |  10 ++
>  misc/Makefile.in    |   8 +-
>  misc/dumpe2fs.c     |   5 +-

This patch is causing some problems when building with shared
libraries because there are now calls from libe2p (which can be built
shared) to libsupport (which is statically linked).

In general it's a bad idea for shared libraries to call a library
which is statically linked.  It doesn't necessarily work on all
operating systems, and it causes problems if the shared library is
dlopen'ed, etc.

I'd like to solve this problem by moving the json functions into
libe2p.  This would imply that the function signatures are fixed (we
don't want to change or delete function signatures in as shared
library since it could break already-linked binaries).  It would also
require that we change the license on your contributed json functions
currently in libsupport from GPL to LGPL.

Is this something you would be OK with?

thanks,

						- Ted

Patch

diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in
index 57940cde..92a3552a 100644
--- a/debugfs/Makefile.in
+++ b/debugfs/Makefile.in
@@ -34,9 +34,9 @@  SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \
 	$(srcdir)/journal.c $(srcdir)/../e2fsck/revoke.c \
 	$(srcdir)/../e2fsck/recovery.c $(srcdir)/do_journal.c
 
-LIBS= $(LIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \
+LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \
 	$(LIBUUID) $(LIBMAGIC) $(SYSLIBS)
-DEPLIBS= $(DEPLIBSUPPORT) $(LIBEXT2FS) $(LIBE2P) $(DEPLIBSS) $(DEPLIBCOM_ERR) \
+DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSUPPORT) $(DEPLIBSS) $(DEPLIBCOM_ERR) \
 	$(DEPLIBBLKID) $(DEPLIBUUID)
 
 STATIC_LIBS= $(STATIC_LIBSUPPORT) $(STATIC_LIBEXT2FS) $(STATIC_LIBSS) \
diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index e96cdec8..88c08d73 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -13,6 +13,8 @@ 
 
 #include <ext2fs/ext2_fs.h>
 
+#include "support/json-out.h"
+
 #define E2P_FEATURE_COMPAT	0
 #define E2P_FEATURE_INCOMPAT	1
 #define E2P_FEATURE_RO_INCOMPAT	2
@@ -42,8 +44,10 @@  int iterate_on_dir (const char * dir_name,
 void list_super(struct ext2_super_block * s);
 void list_super2(struct ext2_super_block * s, FILE *f);
 void print_fs_errors (FILE * f, unsigned short errors);
+void snprint_fs_errors (char *str, size_t size, unsigned short errors);
 void print_flags (FILE * f, unsigned long flags, unsigned options);
 void print_fs_state (FILE * f, unsigned short state);
+void snprint_fs_state (char *str, size_t size, unsigned short state);
 int setflags (int fd, unsigned long flags);
 int setversion (int fd, unsigned long version);
 
@@ -77,3 +81,5 @@  char *e2p_os2string(int os_type);
 int e2p_string2os(char *str);
 
 unsigned int e2p_percent(int percent, unsigned int base);
+
+void fill_json_super(struct json_obj *obj, struct ext2_super_block * s);
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index a7586e09..b690960b 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -37,6 +37,15 @@  static void print_user (unsigned short uid, FILE *f)
 		fprintf(f, "(user %s)\n", pw->pw_name);
 }
 
+static void fill_json_user(struct json_obj *obj, unsigned short uid)
+{
+	struct passwd *pw = getpwuid(uid);
+
+	json_obj_add_fmt_str(obj, "uid", 32, "%u", uid);
+	if (pw)
+		json_obj_add_str(obj, "user", pw->pw_name);
+}
+
 static void print_group (unsigned short gid, FILE *f)
 {
 	struct group *gr;
@@ -49,6 +58,15 @@  static void print_group (unsigned short gid, FILE *f)
 		fprintf(f, "(group %s)\n", gr->gr_name);
 }
 
+static void fill_json_group(struct json_obj *obj, unsigned short gid)
+{
+	struct group *gr = getgrgid(gid);
+
+	json_obj_add_fmt_str(obj, "gid", 32, "%u", gid);
+	if (gr)
+		json_obj_add_str(obj, "group", gr->gr_name);
+}
+
 #define MONTH_INT (86400 * 30)
 #define WEEK_INT (86400 * 7)
 #define DAY_INT	(86400)
@@ -117,6 +135,24 @@  static void print_features(struct ext2_super_block * s, FILE *f)
 #endif
 }
 
+static void fill_json_features(struct json_obj *obj,
+							   struct ext2_super_block *s)
+{
+#ifdef EXT2_DYNAMIC_REV
+	int	i, j;
+	__u32	*mask = &s->s_feature_compat, m;
+	struct json_list *list = json_list_create_in_obj(obj,
+								"filesystem-features", JSON_VAL_STRING);
+
+	for (i=0; i <3; i++,mask++) {
+		for (j=0,m=1; j < 32; j++, m<<=1) {
+			if (*mask & m)
+				json_list_add_str(list, e2p_feature2string(i, m));
+		}
+	}
+#endif
+}
+
 static void print_mntopts(struct ext2_super_block * s, FILE *f)
 {
 #ifdef EXT2_DYNAMIC_REV
@@ -142,6 +178,26 @@  static void print_mntopts(struct ext2_super_block * s, FILE *f)
 #endif
 }
 
+static void fill_json_mntopts(struct json_obj *obj,
+							  struct ext2_super_block *s)
+{
+#ifdef EXT2_DYNAMIC_REV
+	int	i;
+	__u32	mask = s->s_default_mount_opts, m;
+	struct json_list *list = json_list_create_in_obj(obj,
+								"default-mount-options", JSON_VAL_STRING);
+
+	if (mask & EXT3_DEFM_JMODE)
+		json_list_add_str(list, e2p_mntopt2string(mask & EXT3_DEFM_JMODE));
+	for (i=0,m=1; i < 32; i++, m<<=1) {
+		if (m & EXT3_DEFM_JMODE)
+			continue;
+		if (mask & m)
+			json_list_add_str(list, e2p_mntopt2string(m));
+	}
+#endif
+}
+
 static void print_super_flags(struct ext2_super_block * s, FILE *f)
 {
 	int	flags_found = 0;
@@ -168,6 +224,25 @@  static void print_super_flags(struct ext2_super_block * s, FILE *f)
 		fputs("(none)\n", f);
 }
 
+static void fill_json_super_flags(struct json_obj *obj,
+								  struct ext2_super_block *s)
+{
+	int	flags_found = 0;
+	struct json_list *list = json_list_create_in_obj(obj,
+								"filesystem-flags", JSON_VAL_STRING);
+
+	if (s->s_flags == 0)
+		return;
+
+	if (s->s_flags & EXT2_FLAGS_SIGNED_HASH)
+		json_list_add_str(list, "signed_directory_hash");
+	if (s->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
+		json_list_add_str(list, "unsigned_directory_hash");
+	if (s->s_flags & EXT2_FLAGS_TEST_FILESYS)
+		json_list_add_str(list, "test_filesystem");
+}
+
+
 static __u64 e2p_blocks_count(struct ext2_super_block *super)
 {
 	return super->s_blocks_count |
@@ -213,6 +288,12 @@  static const char *quota_prefix[MAXQUOTAS] = {
 	[PRJQUOTA] = "Project quota inode:",
 };
 
+static const char *json_quota_prefix[MAXQUOTAS] = {
+	[USRQUOTA] = "user-quota-inode",
+	[GRPQUOTA] = "group-quota-inode",
+	[PRJQUOTA] = "project-quota-inode",
+};
+
 /**
  * Convert type of quota to written representation
  */
@@ -477,3 +558,284 @@  void list_super (struct ext2_super_block * s)
 	list_super2(s, stdout);
 }
 
+static void fill_json_time(struct json_obj *obj, const char *key, char *buf,
+						   time_t tm)
+{
+	char *str;
+
+	if (tm && (ctime_r(&tm, buf))) {
+		if (str = strchr(buf, '\n'))
+			*str = '\0';
+		json_obj_add_str(obj, key, buf);
+	}
+}
+
+void fill_json_super(struct json_obj *obj, struct ext2_super_block * sb)
+{
+	int inode_blocks_per_group;
+	char buf[80], *str;
+	time_t	tm;
+	enum quota_type qtype;
+	struct json_obj *super_obj = json_obj_create_in_obj(obj, "super");
+
+	inode_blocks_per_group = (((sb->s_inodes_per_group *
+				    EXT2_INODE_SIZE(sb)) +
+				   EXT2_BLOCK_SIZE(sb) - 1) /
+				  EXT2_BLOCK_SIZE(sb));
+	if (sb->s_volume_name[0]) {
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name));
+	} else
+		strcpy(buf, "<none>");
+	json_obj_add_str(super_obj, "fs-volume-name", buf);
+	if (sb->s_last_mounted[0]) {
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted));
+	} else
+		strcpy(buf, "<not available>");
+	json_obj_add_str(super_obj, "last-mounted-on", buf);
+	json_obj_add_str(super_obj, "fs-uuid", e2p_uuid2str(sb->s_uuid));
+	json_obj_add_fmt_buf_str(super_obj, "fs-magic-number", buf, sizeof(buf),
+		"0x%04X", sb->s_magic);
+	json_obj_add_fmt_buf_str(super_obj, "fs-revision-level", buf, sizeof(buf),
+		"%d", sb->s_rev_level);
+	fill_json_features(super_obj, sb);
+	fill_json_super_flags(super_obj, sb);
+	fill_json_mntopts(super_obj, sb);
+	if (sb->s_mount_opts[0])
+		json_obj_add_str(super_obj, "mount-options", sb->s_mount_opts);
+	snprint_fs_state(buf, sizeof(buf), sb->s_state);
+	json_obj_add_str(super_obj, "fs-state", buf);
+	snprint_fs_errors(buf, sizeof(buf), sb->s_errors);
+	json_obj_add_str(super_obj, "errors-behaviour", buf);
+	str = e2p_os2string(sb->s_creator_os);
+	json_obj_add_str(super_obj, "fs-os-type", str);
+	free(str);
+	json_obj_add_fmt_buf_str(super_obj, "inode-count", buf, sizeof(buf), "%u",
+		sb->s_inodes_count);
+	json_obj_add_fmt_buf_str(super_obj, "block-count", buf, sizeof(buf),
+		"%llu", e2p_blocks_count(sb));
+	json_obj_add_fmt_buf_str(super_obj, "reserved-block-count", buf,
+		sizeof(buf), "%llu", e2p_r_blocks_count(sb));
+	if (sb->s_overhead_blocks)
+		json_obj_add_fmt_buf_str(super_obj, "overhead-blocks", buf,
+			sizeof(buf), "%u", sb->s_overhead_blocks);
+	json_obj_add_fmt_buf_str(super_obj, "free-blocks", buf, sizeof(buf),
+		"%llu", e2p_free_blocks_count(sb));
+	json_obj_add_fmt_buf_str(super_obj, "free-inodes", buf, sizeof(buf), "%u",
+		sb->s_free_inodes_count);
+	json_obj_add_fmt_buf_str(super_obj, "first-block", buf, sizeof(buf), "%u",
+		sb->s_first_data_block);
+	json_obj_add_fmt_buf_str(super_obj, "block-size", buf, sizeof(buf), "%u",
+		EXT2_BLOCK_SIZE(sb));
+	if (ext2fs_has_feature_bigalloc(sb))
+		json_obj_add_fmt_buf_str(super_obj, "cluster-size", buf, sizeof(buf),
+			"%u", EXT2_CLUSTER_SIZE(sb));
+	else
+		json_obj_add_fmt_buf_str(super_obj, "fragment-size", buf, sizeof(buf),
+			"%u", EXT2_CLUSTER_SIZE(sb));
+
+	if (ext2fs_has_feature_64bit(sb))
+		json_obj_add_fmt_buf_str(super_obj, "group-descriptor-size", buf,
+			sizeof(buf), "%u", sb->s_desc_size);
+	if (sb->s_reserved_gdt_blocks)
+		json_obj_add_fmt_buf_str(super_obj, "reserved-gdt-blocks", buf,
+			sizeof(buf), "%u", sb->s_reserved_gdt_blocks);
+	json_obj_add_fmt_buf_str(super_obj, "blocks-per-group", buf, sizeof(buf),
+		"%u", sb->s_blocks_per_group);
+	if (ext2fs_has_feature_bigalloc(sb))
+		json_obj_add_fmt_buf_str(super_obj, "clusters-per-group", buf,
+			sizeof(buf), "%u", sb->s_clusters_per_group);
+	else
+		json_obj_add_fmt_buf_str(super_obj, "fragments-per-group", buf,
+			sizeof(buf), "%u", sb->s_clusters_per_group);
+	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf, sizeof(buf),
+		"%u", sb->s_inodes_per_group);
+	json_obj_add_fmt_buf_str(super_obj, "inode-blocks-per-group", buf,
+		sizeof(buf), "%u", inode_blocks_per_group);
+	if (sb->s_raid_stride)
+		json_obj_add_fmt_buf_str(super_obj, "raid-stride", buf, sizeof(buf),
+			"%u", sb->s_raid_stride);
+	if (sb->s_raid_stripe_width)
+		json_obj_add_fmt_buf_str(super_obj, "raid-stripe-width", buf,
+			sizeof(buf), "%u", sb->s_raid_stripe_width);
+	if (sb->s_first_meta_bg)
+		json_obj_add_fmt_buf_str(super_obj, "first-meta-block-group", buf,
+			sizeof(buf), "%u", sb->s_first_meta_bg);
+	if (sb->s_log_groups_per_flex)
+		json_obj_add_fmt_buf_str(super_obj, "flex-block-group-size", buf,
+			sizeof(buf), "%u", 1 << sb->s_log_groups_per_flex);
+
+	json_obj_add_fmt_buf_str(super_obj, "inodes-per-group", buf, sizeof(buf),
+		"%u", sb->s_inodes_per_group);
+	json_obj_add_fmt_buf_str(super_obj, "inode-blocks-per-group", buf,
+		sizeof(buf), "%u", inode_blocks_per_group);
+	if (sb->s_raid_stride)
+		json_obj_add_fmt_buf_str(super_obj, "raid-stride", buf, sizeof(buf),
+			"%u", sb->s_raid_stride);
+	if (sb->s_raid_stripe_width)
+		json_obj_add_fmt_buf_str(super_obj, "raid-stripe-width", buf,
+			sizeof(buf), "%u", sb->s_raid_stripe_width);
+	if (sb->s_first_meta_bg)
+		json_obj_add_fmt_buf_str(super_obj, "first-meta-block-group", buf,
+			sizeof(buf), "%u", sb->s_first_meta_bg);
+	if (sb->s_log_groups_per_flex)
+		json_obj_add_fmt_buf_str(super_obj, "flex-block-group-size", buf,
+			sizeof(buf), "%u", 1 << sb->s_log_groups_per_flex);
+	fill_json_time(super_obj, "filesystem-created", buf, sb->s_mkfs_time);
+	fill_json_time(super_obj, "last-mount-time", buf, sb->s_mtime);
+	fill_json_time(super_obj, "last-write-time", buf, sb->s_wtime);
+	json_obj_add_fmt_buf_str(super_obj, "mount-count", buf, sizeof(buf),
+		"%u", sb->s_mnt_count);
+	json_obj_add_fmt_buf_str(super_obj, "maximum-mount-count", buf,
+		sizeof(buf), "%d", sb->s_max_mnt_count);
+	fill_json_time(super_obj, "last-checked", buf, sb->s_lastcheck);
+	json_obj_add_fmt_buf_str(super_obj, "check-interval", buf, sizeof(buf),
+		"%u", sb->s_checkinterval);
+	json_obj_add_str(super_obj, "check-interval-str",
+		interval_string(sb->s_checkinterval));
+	if (sb->s_checkinterval)
+	{
+		time_t next = sb->s_lastcheck + sb->s_checkinterval;
+
+		fill_json_time(super_obj, "next-check-after", buf,
+			sb->s_lastcheck + sb->s_checkinterval);
+	}
+#define POW2(x) ((__u64) 1 << (x))
+	if (sb->s_kbytes_written) {
+		if (sb->s_kbytes_written < POW2(13))
+			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
+				sizeof(buf), "%llu kB", sb->s_kbytes_written);
+		else if (sb->s_kbytes_written < POW2(23))
+			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
+				sizeof(buf), "%llu MB",
+				(sb->s_kbytes_written + POW2(9)) >> 10);
+		else if (sb->s_kbytes_written < POW2(33))
+			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
+				sizeof(buf), "%llu GB",
+				(sb->s_kbytes_written + POW2(19)) >> 20);
+		else if (sb->s_kbytes_written < POW2(43))
+			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
+				sizeof(buf), "%llu TB",
+				(sb->s_kbytes_written + POW2(29)) >> 30);
+		else
+			json_obj_add_fmt_buf_str(super_obj, "lifetime-writes", buf,
+				sizeof(buf), "%llu PB",
+				(sb->s_kbytes_written + POW2(39)) >> 40);
+	}
+	fill_json_user(super_obj, sb->s_def_resuid);
+	fill_json_group(super_obj, sb->s_def_resgid);
+	if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
+		json_obj_add_fmt_buf_str(super_obj, "first-inode", buf, sizeof(buf),
+				"%d", sb->s_first_ino);
+		json_obj_add_fmt_buf_str(super_obj, "inode-size", buf, sizeof(buf),
+				"%d", sb->s_inode_size);
+		if (sb->s_min_extra_isize)
+			json_obj_add_fmt_buf_str(super_obj, "required-extra-isize", buf,
+				sizeof(buf), "%d", sb->s_min_extra_isize);
+		if (sb->s_want_extra_isize)
+			json_obj_add_fmt_buf_str(super_obj, "desired-extra-isize", buf,
+				sizeof(buf), "%d", sb->s_want_extra_isize);
+	}
+	if (!e2p_is_null_uuid(sb->s_journal_uuid))
+		json_obj_add_str(super_obj, "journal-uuid",
+			e2p_uuid2str(sb->s_journal_uuid));
+	if (sb->s_journal_inum)
+		json_obj_add_fmt_buf_str(super_obj, "journal-inode", buf,
+			sizeof(buf), "%u", sb->s_journal_inum);
+	if (sb->s_journal_dev)
+		json_obj_add_fmt_buf_str(super_obj, "journal-device", buf,
+			sizeof(buf), "0x%04x", sb->s_journal_dev);
+	if (sb->s_last_orphan)
+		json_obj_add_fmt_buf_str(super_obj, "first-orphan-inode", buf,
+			sizeof(buf), "%u", sb->s_last_orphan);
+	if (ext2fs_has_feature_dir_index(sb) ||
+	    sb->s_def_hash_version)
+		json_obj_add_str(super_obj, "default-directory-hash",
+			e2p_hash2string(sb->s_def_hash_version));
+	if (!e2p_is_null_uuid(sb->s_hash_seed))
+		json_obj_add_str(super_obj, "directory-hash-seed",
+			e2p_uuid2str(sb->s_hash_seed));
+	if (sb->s_jnl_backup_type) {
+		switch (sb->s_jnl_backup_type) {
+		case 1:
+			json_obj_add_str(super_obj, "journal-backup", "inode blocks");
+			break;
+		default:
+			json_obj_add_fmt_buf_str(super_obj, "journal-backup", buf,
+				sizeof(buf), "type %u", sb->s_jnl_backup_type);
+		}
+	}
+	if (sb->s_backup_bgs[0])
+		json_obj_add_fmt_buf_str(super_obj, "backup-block-group-0", buf,
+			sizeof(buf), "%u", sb->s_backup_bgs[0]);
+	if (sb->s_backup_bgs[1])
+		json_obj_add_fmt_buf_str(super_obj, "backup-block-group-1", buf,
+			sizeof(buf), "%u", sb->s_backup_bgs[1]);
+	if (sb->s_snapshot_inum) {
+		json_obj_add_fmt_buf_str(super_obj, "snapshot-inode", buf, sizeof(buf),
+			"%u", sb->s_snapshot_inum);
+		json_obj_add_fmt_buf_str(super_obj, "snapshot-id", buf, sizeof(buf),
+			"%u", sb->s_snapshot_id);
+		json_obj_add_fmt_buf_str(super_obj, "snapshot-reserved-blocks", buf,
+			sizeof(buf), "%llu", sb->s_snapshot_r_blocks_count);
+	}
+	if (sb->s_snapshot_list)
+		json_obj_add_fmt_buf_str(super_obj, "snapshot-list-head", buf,
+			sizeof(buf), "%u", sb->s_snapshot_list);
+	if (sb->s_error_count)
+		json_obj_add_fmt_buf_str(super_obj, "fs-error-count", buf, sizeof(buf),
+			"%u", sb->s_error_count);
+	if (sb->s_first_error_time) {
+		fill_json_time(super_obj, "first-error-time", buf,
+			sb->s_first_error_time);
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, (char *)sb->s_first_error_func,
+			sizeof(sb->s_first_error_func));
+		json_obj_add_str(super_obj, "first-error-function", buf);
+		json_obj_add_fmt_buf_str(super_obj, "first-error-line-num", buf,
+			sizeof(buf), "%u", sb->s_first_error_line);
+		json_obj_add_fmt_buf_str(super_obj, "first-error-inode-num", buf,
+			sizeof(buf), "%u", sb->s_first_error_ino);
+		json_obj_add_fmt_buf_str(super_obj, "first-error-block-num", buf,
+			sizeof(buf), "%llu", sb->s_first_error_block);
+	}
+	if (sb->s_last_error_time) {
+		fill_json_time(super_obj, "last-error-time", buf,
+			sb->s_last_error_time);
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, (char *)sb->s_last_error_func,
+			sizeof(sb->s_last_error_func));
+		json_obj_add_str(super_obj, "last-error-function", buf);
+		json_obj_add_fmt_buf_str(super_obj, "last-error-line-num", buf,
+			sizeof(buf), "%u", sb->s_last_error_line);
+		json_obj_add_fmt_buf_str(super_obj, "last-error-inode-num", buf,
+			sizeof(buf), "%u", sb->s_last_error_ino);
+		json_obj_add_fmt_buf_str(super_obj, "last-error-block-num", buf,
+			sizeof(buf), "%llu", sb->s_last_error_block);
+	}
+	if (ext2fs_has_feature_mmp(sb)) {
+		json_obj_add_fmt_buf_str(super_obj, "mmp-block-number", buf,
+			sizeof(buf), "%llu", (long long)sb->s_mmp_block);
+		json_obj_add_fmt_buf_str(super_obj, "mmp-update-interval", buf,
+			sizeof(buf), "%u", sb->s_mmp_update_interval);
+	}
+	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+		if (*quota_sb_inump(sb, qtype) != 0)
+			json_obj_add_fmt_buf_str(super_obj, json_quota_prefix[qtype], buf,
+				sizeof(buf), "%u", *quota_sb_inump(sb, qtype));
+	}
+	if (ext2fs_has_feature_metadata_csum(sb)) {
+		json_obj_add_str(super_obj, "checksum-type",
+			checksum_type(sb->s_checksum_type));
+		json_obj_add_fmt_buf_str(super_obj, "checksum", buf, sizeof(buf),
+			"0x%08x", sb->s_checksum);
+	}
+	if (!e2p_is_null_uuid(sb->s_encrypt_pw_salt))
+		json_obj_add_str(super_obj, "encryption-pw-salt",
+			e2p_uuid2str(sb->s_encrypt_pw_salt));
+
+	if (ext2fs_has_feature_csum_seed(sb))
+		json_obj_add_fmt_buf_str(super_obj, "checksum-seed", buf, sizeof(buf),
+			"0x%08x", sb->s_checksum_seed);
+}
diff --git a/lib/e2p/pe.c b/lib/e2p/pe.c
index 1f24545d..9426e743 100644
--- a/lib/e2p/pe.c
+++ b/lib/e2p/pe.c
@@ -38,3 +38,21 @@  void print_fs_errors (FILE * f, unsigned short errors)
 			fprintf (f, "Unknown (continue)");
 	}
 }
+
+void snprint_fs_errors (char *buf, size_t size, unsigned short errors)
+{
+	switch (errors)
+	{
+		case EXT2_ERRORS_CONTINUE:
+			snprintf (buf, size, "Continue");
+			break;
+		case EXT2_ERRORS_RO:
+			snprintf (buf, size, "Remount read-only");
+			break;
+		case EXT2_ERRORS_PANIC:
+			snprintf (buf, size, "Panic");
+			break;
+		default:
+			snprintf (buf, size, "Unknown");
+	}
+}
diff --git a/lib/e2p/ps.c b/lib/e2p/ps.c
index 757f8a66..19eb6559 100644
--- a/lib/e2p/ps.c
+++ b/lib/e2p/ps.c
@@ -30,3 +30,13 @@  void print_fs_state (FILE * f, unsigned short state)
 	if (state & EXT2_ERROR_FS)
 		fprintf (f, " with errors");
 }
+
+void snprint_fs_state (char *str, size_t size, unsigned short state)
+{
+	if (state & EXT2_VALID_FS)
+		snprintf (str, size, "clean");
+	else
+		snprintf (str, size, "not clean");
+	if (state & EXT2_ERROR_FS)
+		snprintf (str, size, "with errors");
+}
diff --git a/misc/Makefile.in b/misc/Makefile.in
index 6f631eb1..a863ef62 100644
--- a/misc/Makefile.in
+++ b/misc/Makefile.in
@@ -170,15 +170,15 @@  e2initrd_helper: e2initrd_helper.o $(DEPLIBS) $(DEPLIBBLKID) $(LIBEXT2FS)
 tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS) $(DEPLIBS_E2P) $(DEPLIBBLKID) \
 		$(DEPLIBUUID) $(LIBEXT2FS)
 	$(E) "	LD $@"
-	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) $(LIBS) \
-		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) \
+	$(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) \
+		$(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) $(LIBS) \
 		$(LIBINTL) $(SYSLIBS) $(LIBBLKID) $(LIBMAGIC)
 
 tune2fs.static: $(TUNE2FS_OBJS) $(STATIC_DEPLIBS) $(STATIC_LIBE2P) $(DEPSTATIC_LIBBLKID)
 	$(E) "	LD $@"
 	$(Q) $(CC) $(LDFLAGS_STATIC) -o tune2fs.static $(TUNE2FS_OBJS) \
-		$(STATIC_LIBS) $(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
-		$(STATIC_LIBE2P) $(LIBINTL) $(SYSLIBS) \
+		$(STATIC_LIBBLKID) $(STATIC_LIBUUID) \
+		$(STATIC_LIBE2P) $(STATIC_LIBS) $(LIBINTL) $(SYSLIBS) \
 		$(STATIC_LIBBLKID) $(LIBMAGIC)
 
 tune2fs.profiled: $(TUNE2FS_OBJS) $(PROFILED_DEPLIBS) \
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 319c296b..93e4b2ff 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -863,7 +863,10 @@  try_open_again:
 	} else {
 		if (grp_only)
 			goto just_descriptors;
-		list_super (fs->super);
+		if (json)
+			fill_json_super(dump_obj, fs->super);
+		else
+			list_super (fs->super);
 		if (ext2fs_has_feature_journal_dev(fs->super)) {
 			print_journal_information(fs, dump_obj);
 			if (json) {