Skip to content

Commit

Permalink
btrfs-progs: filesystem show: implement json format output
Browse files Browse the repository at this point in the history
Implements JSON-formatted output for the `filesystem show` command using
the `--format json` global option. Devices are in a `device-list` array,
optionally showing if a device is missing.

Signed-off-by: Jelle van der Waa <[email protected]>
  • Loading branch information
jelly committed Jun 5, 2024
1 parent 945bacd commit 75d699b
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 49 deletions.
1 change: 1 addition & 0 deletions Documentation/dev/dev-json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Commands that support json output

* :command:`btrfs device stats`
* :command:`btrfs filesystem df`
* :command:`btrfs filesystem list`
* :command:`btrfs qgroup show`
* :command:`btrfs subvolume get-default`
* :command:`btrfs subvolume list`
Expand Down
177 changes: 128 additions & 49 deletions cmds/filesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,36 +272,75 @@ static void splice_device_list(struct list_head *seed_devices,
list_splice(seed_devices, all_devices);
}

static void print_filesystem_info(char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
u64 bytes_used, u64 num_devices,
unsigned unit_mode)
static const struct rowspec filesystem_show_data_rowspec[] = {
{ .key = "label", .fmt = "%s", .out_json = "label" },
{ .key = "uuid", .fmt = "%s", .out_json = "uuid" },
{ .key = "num_devices", .fmt = "%llu", .out_json = "total_devices" },
{ .key = "used", .fmt = "%llu", .out_json = "used" },
/* device list */
{ .key = "devid", .fmt = "%llu", .out_json = "devid" },
{ .key = "size", .fmt = "%llu", .out_json = "size" },
{ .key = "used", .fmt = "%llu", .out_json = "used" },
{ .key = "path", .fmt = "%s", .out_json = "path" },
{ .key = "missing", .fmt = "bool", .out_json = "missing" },
ROWSPEC_END
};

static void print_filesystem_info(struct format_ctx *fctx,
char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
u64 bytes_used, u64 num_devices,
unsigned unit_mode)
{
if (label)
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
else
pr_verbose(LOG_DEFAULT, "Label: none ");

pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
num_devices,
pretty_size_mode(bytes_used,
unit_mode));
if (bconf.output_format == CMD_FORMAT_JSON) {
if (label)
fmt_print(fctx, "label", label);
else
fmt_print(fctx, "label", "none");

fmt_print(fctx, "uuid", uuidbuf);
fmt_print(fctx, "num_devices", num_devices);
fmt_print(fctx, "used", bytes_used);
} else {
if (label)
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
else
pr_verbose(LOG_DEFAULT, "Label: none ");

pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
num_devices,
pretty_size_mode(bytes_used,
unit_mode));
}
}

static void print_filesystem_device(u64 devid, u64 total_bytes, u64 bytes_used,
static void print_filesystem_device(struct format_ctx *fctx,
u64 devid, u64 total_bytes, u64 bytes_used,
char *path,
bool missing,
unsigned unit_mode)
{
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s%s\n",
devid,
pretty_size_mode(total_bytes, unit_mode),
pretty_size_mode(bytes_used, unit_mode),
path,
missing ? " MISSING" : "");
if (bconf.output_format == CMD_FORMAT_JSON) {
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
fmt_print(fctx, "devid", devid);
fmt_print(fctx, "size", 0);
fmt_print(fctx, "used", 0);
fmt_print(fctx, "path", path);
if (missing)
fmt_print(fctx, "missing", 0);
fmt_print_end_group(fctx, NULL);
} else {
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s%s\n",
devid,
pretty_size_mode(total_bytes, unit_mode),
pretty_size_mode(bytes_used, unit_mode),
path,
missing ? " MISSING" : "");
}
}

static void print_devices(struct btrfs_fs_devices *fs_devices,
u64 *devs_found, unsigned unit_mode)
u64 *devs_found, unsigned unit_mode,
struct format_ctx *fctx)
{
struct btrfs_device *device;
struct btrfs_fs_devices *cur_fs;
Expand All @@ -317,16 +356,17 @@ static void print_devices(struct btrfs_fs_devices *fs_devices,

list_sort(NULL, all_devices, cmp_device_id);
list_for_each_entry(device, all_devices, dev_list) {
print_filesystem_device(device->devid,
device->total_bytes, device->bytes_used,
device->name,
false,
unit_mode);
print_filesystem_device(fctx, device->devid,
device->total_bytes, device->bytes_used,
device->name,
false,
unit_mode);
(*devs_found)++;
}
}

static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
static void print_one_uuid(struct format_ctx *fctx,
struct btrfs_fs_devices *fs_devices,
unsigned unit_mode)
{
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
Expand All @@ -342,16 +382,28 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
dev_list);
total = device->total_devs;

print_filesystem_info(device->label && device->label[0] ? device->label : NULL, uuidbuf,
device->super_bytes_used, total,
unit_mode);
if (bconf.output_format == CMD_FORMAT_JSON)
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);

print_filesystem_info(fctx, device->label && device->label[0] ? device->label : NULL, uuidbuf,
device->super_bytes_used, total,
unit_mode);

print_devices(fs_devices, &devs_found, unit_mode);
if (bconf.output_format == CMD_FORMAT_JSON)
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);

if (devs_found < total) {
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
print_devices(fs_devices, &devs_found, unit_mode, fctx);

// TODO: global missing option?
if (bconf.output_format == CMD_FORMAT_JSON) {
fmt_print_end_group(fctx, NULL);
fmt_print_end_group(fctx, "device-list");
} else {
if (devs_found < total) {
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
}
pr_verbose(LOG_DEFAULT, "\n");
}
pr_verbose(LOG_DEFAULT, "\n");
}

/* adds up all the used spaces as reported by the space info ioctl
Expand All @@ -365,7 +417,8 @@ static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
return ret;
}

static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
static int print_one_fs(struct format_ctx *fctx,
struct btrfs_ioctl_fs_info_args *fs_info,
struct btrfs_ioctl_dev_info_args *dev_info,
struct btrfs_ioctl_space_args *space_info,
char *label, unsigned unit_mode)
Expand All @@ -382,10 +435,16 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
else if (ret)
return ret;

if (bconf.output_format == CMD_FORMAT_JSON)
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);

uuid_unparse(fs_info->fsid, uuidbuf);
print_filesystem_info(label && *label ? label : NULL, uuidbuf,
calc_used_bytes(space_info), fs_info->num_devices,
unit_mode);
print_filesystem_info(fctx, label && *label ? label : NULL, uuidbuf,
calc_used_bytes(space_info), fs_info->num_devices,
unit_mode);

if (bconf.output_format == CMD_FORMAT_JSON)
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);

for (i = 0; i < fs_info->num_devices; i++) {
char *canonical_path;
Expand All @@ -395,7 +454,8 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
/* Add check for missing devices even mounted */
fd = open((char *)tmp_dev_info->path, O_RDONLY);
if (fd < 0) {
print_filesystem_device(tmp_dev_info->devid,
print_filesystem_device(fctx,
tmp_dev_info->devid,
0, 0,
(char *)tmp_dev_info->path,
true,
Expand All @@ -404,20 +464,25 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
}
close(fd);
canonical_path = path_canonicalize((char *)tmp_dev_info->path);
print_filesystem_device(tmp_dev_info->devid,
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
canonical_path,
false,
unit_mode);
print_filesystem_device(fctx, tmp_dev_info->devid,
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
canonical_path,
false,
unit_mode);

free(canonical_path);
}

pr_verbose(LOG_DEFAULT, "\n");
if (bconf.output_format == CMD_FORMAT_JSON) {
fmt_print_end_group(fctx, "device-list");
fmt_print_end_group(fctx, NULL);
} else {
pr_verbose(LOG_DEFAULT, "\n");
}
return 0;
}

static int btrfs_scan_kernel(void *search, unsigned unit_mode)
static int btrfs_scan_kernel(struct format_ctx *fctx, void *search, unsigned unit_mode)
{
int ret = 0, fd;
int found = 0;
Expand Down Expand Up @@ -463,7 +528,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)

fd = open(mnt->mnt_dir, O_RDONLY);
if ((fd != -1) && !get_df(fd, &space_info_arg)) {
print_one_fs(&fs_info_arg, dev_info_arg,
print_one_fs(fctx, &fs_info_arg, dev_info_arg,
space_info_arg, label, unit_mode);
free(space_info_arg);
memset(label, 0, sizeof(label));
Expand Down Expand Up @@ -727,6 +792,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
LIST_HEAD(all_uuids);
struct btrfs_fs_devices *fs_devices;
struct btrfs_root *root = NULL;
struct format_ctx fctx;
char *search = NULL;
char *canon_path = NULL;
int ret;
Expand Down Expand Up @@ -769,6 +835,11 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
if (check_argc_max(argc, optind + 1))
return 1;

if (bconf.output_format == CMD_FORMAT_JSON) {
fmt_start(&fctx, filesystem_show_data_rowspec, 1, 0);
fmt_print_start_group(&fctx, "filesystem-list", JSON_TYPE_ARRAY);
}

if (argc > optind) {
search = argv[optind];
if (*search == 0)
Expand Down Expand Up @@ -821,7 +892,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
}

/* show mounted btrfs */
ret = btrfs_scan_kernel(search, unit_mode);
ret = btrfs_scan_kernel(&fctx, search, unit_mode);
if (search && !ret) {
/* since search is found we are done */
goto out;
Expand Down Expand Up @@ -865,7 +936,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
}

list_for_each_entry(fs_devices, &all_uuids, fs_list)
print_one_uuid(fs_devices, unit_mode);
print_one_uuid(&fctx, fs_devices, unit_mode);

if (search && !found) {
error("not a valid btrfs filesystem: %s", search);
Expand All @@ -877,13 +948,21 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
free_fs_devices(fs_devices);
}
out:
if (bconf.output_format == CMD_FORMAT_JSON) {
fmt_print_end_group(&fctx, "filesystem-list");
fmt_end(&fctx);
}
free(canon_path);
if (root)
close_ctree(root);
free_seen_fsid(seen_fsid_hash);
return !!ret;
}
static DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
#if EXPERIMENTAL
static DEFINE_COMMAND_WITH_FLAGS(filesystem_show, "show", CMD_FORMAT_JSON);
#else
DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
#endif

static const char * const cmd_filesystem_sync_usage[] = {
"btrfs filesystem sync <path>",
Expand Down

0 comments on commit 75d699b

Please sign in to comment.