btrfs-progs: subvol list: implement json format output

Implements JSON-formatted output for the `subvolume list` command using
the `--format json` global option, much like it is implemented for other
commands.

Re-uses the `btrfs_list_layout` infrastructure to nicely fit it into the
existing formatting code.

A notable difference to the normal, text-based output is that in the
JSON output, timestamps include the timezone offset as well.

Signed-off-by: Christoph Heiss <christoph@c8h4.io>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Christoph Heiss 2023-08-13 11:45:00 +02:00 committed by David Sterba
parent da440e66fb
commit ee233db769
1 changed files with 88 additions and 3 deletions

View File

@ -35,7 +35,9 @@
#include "common/open-utils.h"
#include "common/string-utils.h"
#include "common/utils.h"
#include "common/format-output.h"
#include "cmds/commands.h"
#include "cmds/subvolume.h"
/*
* Naming of options:
@ -75,6 +77,8 @@ static const char * const cmd_subvolume_list_usage[] = {
OPTLINE("--sort=gen,ogen,rootid,path", "list the subvolume in order of gen, ogen, rootid or path "
"you also can add '+' or '-' in front of each items. "
"(+:ascending, -:descending, ascending default)"),
HELPINFO_INSERT_GLOBALS,
HELPINFO_INSERT_FORMAT,
NULL,
};
@ -84,7 +88,8 @@ static const char * const cmd_subvolume_list_usage[] = {
enum btrfs_list_layout {
BTRFS_LIST_LAYOUT_DEFAULT = 0,
BTRFS_LIST_LAYOUT_TABLE,
BTRFS_LIST_LAYOUT_RAW
BTRFS_LIST_LAYOUT_RAW,
BTRFS_LIST_LAYOUT_JSON
};
/*
@ -1269,14 +1274,83 @@ static void print_all_subvol_info_tab_head(void)
}
}
static void print_subvol_json_key(struct format_ctx *fctx,
const struct root_info *subv,
const enum btrfs_list_column_enum column)
{
const char *column_name;
UASSERT(0 <= column && column < BTRFS_LIST_ALL);
column_name = btrfs_list_columns[column].name;
switch (column) {
case BTRFS_LIST_OBJECTID:
fmt_print(fctx, column_name, subv->root_id);
break;
case BTRFS_LIST_GENERATION:
fmt_print(fctx, column_name, subv->gen);
break;
case BTRFS_LIST_OGENERATION:
fmt_print(fctx, column_name, subv->ogen);
break;
case BTRFS_LIST_PARENT:
fmt_print(fctx, column_name, subv->ref_tree);
break;
case BTRFS_LIST_TOP_LEVEL:
fmt_print(fctx, column_name, subv->top_id);
break;
case BTRFS_LIST_OTIME:
fmt_print(fctx, column_name, subv->otime);
break;
case BTRFS_LIST_UUID:
fmt_print(fctx, column_name, subv->uuid);
break;
case BTRFS_LIST_PUUID:
fmt_print(fctx, column_name, subv->puuid);
break;
case BTRFS_LIST_RUUID:
fmt_print(fctx, column_name, subv->ruuid);
break;
case BTRFS_LIST_PATH:
BUG_ON(!subv->full_path);
fmt_print(fctx, column_name, subv->full_path);
break;
default:
break;
}
}
static void print_one_subvol_info_json(struct format_ctx *fctx,
struct root_info *subv)
{
int i;
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
for (i = 0; i < BTRFS_LIST_ALL; i++) {
if (!btrfs_list_columns[i].need_print)
continue;
print_subvol_json_key(fctx, subv, i);
}
fmt_print_end_group(fctx, NULL);
}
static void print_all_subvol_info(struct rb_root *sorted_tree,
enum btrfs_list_layout layout, const char *raw_prefix)
{
struct rb_node *n;
struct root_info *entry;
struct format_ctx fctx;
if (layout == BTRFS_LIST_LAYOUT_TABLE)
if (layout == BTRFS_LIST_LAYOUT_TABLE) {
print_all_subvol_info_tab_head();
} else if (layout == BTRFS_LIST_LAYOUT_JSON) {
fmt_start(&fctx, btrfs_subvolume_rowspec, 1, 0);
fmt_print_start_group(&fctx, "subvolume-list", JSON_TYPE_ARRAY);
}
n = rb_first(sorted_tree);
while (n) {
@ -1296,10 +1370,18 @@ static void print_all_subvol_info(struct rb_root *sorted_tree,
case BTRFS_LIST_LAYOUT_RAW:
print_one_subvol_info_raw(entry, raw_prefix);
break;
case BTRFS_LIST_LAYOUT_JSON:
print_one_subvol_info_json(&fctx, entry);
break;
}
next:
n = rb_next(n);
}
if (layout == BTRFS_LIST_LAYOUT_JSON) {
fmt_print_end_group(&fctx, "subvolume-list");
fmt_end(&fctx);
}
}
static int btrfs_list_subvols(int fd, struct rb_root *root_lookup)
@ -1631,6 +1713,9 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
if (bconf.output_format == CMD_FORMAT_JSON)
layout = BTRFS_LIST_LAYOUT_JSON;
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
layout, !is_list_all && !is_only_in_path, NULL);
@ -1644,4 +1729,4 @@ out:
usage(cmd, 1);
return !!ret;
}
DEFINE_SIMPLE_COMMAND(subvolume_list, "list");
DEFINE_COMMAND_WITH_FLAGS(subvolume_list, "list", CMD_FORMAT_JSON);