Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Btrfs filesystem show JSON format support #761

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
183 changes: 141 additions & 42 deletions cmds/filesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,75 @@ static void splice_device_list(struct list_head *seed_devices,
list_splice(seed_devices, all_devices);
}

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 (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(struct format_ctx *fctx,
u64 devid, u64 total_bytes, u64 bytes_used,
char *path,
bool missing,
unsigned unit_mode)
{
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 @@ -289,17 +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) {
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s\n",
device->devid,
pretty_size_mode(device->total_bytes, unit_mode),
pretty_size_mode(device->bytes_used, unit_mode),
device->name);

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 @@ -313,21 +380,30 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
uuid_unparse(fs_devices->fsid, uuidbuf);
device = list_entry(fs_devices->devices.next, struct btrfs_device,
dev_list);
if (device->label && device->label[0])
pr_verbose(LOG_DEFAULT, "Label: '%s' ", device->label);
else
pr_verbose(LOG_DEFAULT, "Label: none ");

total = device->total_devs;
pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
total, pretty_size_mode(device->super_bytes_used, unit_mode));

print_devices(fs_devices, &devs_found, 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);

if (devs_found < total) {
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
if (bconf.output_format == CMD_FORMAT_JSON)
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);

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 @@ -341,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 @@ -358,16 +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);
if (label && *label)
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
else
pr_verbose(LOG_DEFAULT, "Label: none ");
print_filesystem_info(fctx, label && *label ? label : NULL, uuidbuf,
calc_used_bytes(space_info), fs_info->num_devices,
unit_mode);

pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
fs_info->num_devices,
pretty_size_mode(calc_used_bytes(space_info),
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 @@ -377,27 +454,35 @@ 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) {
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size 0 used 0 path %s MISSING\n",
tmp_dev_info->devid, tmp_dev_info->path);
print_filesystem_device(fctx,
tmp_dev_info->devid,
0, 0,
(char *)tmp_dev_info->path,
true,
unit_mode);
continue;

}
close(fd);
canonical_path = path_canonicalize((char *)tmp_dev_info->path);
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s\n",
tmp_dev_info->devid,
pretty_size_mode(tmp_dev_info->total_bytes, unit_mode),
pretty_size_mode(tmp_dev_info->bytes_used, unit_mode),
canonical_path);
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 @@ -443,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 @@ -707,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 @@ -749,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 @@ -801,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 @@ -845,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 @@ -857,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
2 changes: 1 addition & 1 deletion kernel-shared/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2434,7 +2434,7 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
NULL);
if (!map->stripes[i].dev) {
map->stripes[i].dev = fill_missing_device(devid, uuid);
printf("warning, device %llu is missing\n",
warning("warning, device %llu is missing\n",
(unsigned long long)devid);
list_add(&map->stripes[i].dev->dev_list,
&fs_info->fs_devices->devices);
Expand Down