From 084d2c9f6a441c037cd9cec17728362374325d04 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 7 Dec 2022 02:00:27 +0100 Subject: [PATCH] btrfs-progs: subvolume: add new command for listing Work in progress, experimental build. Listing command that utilizes unprivileged ioctls to list subvolumes accessible and visible to the user. If run as root it's using the search tree ioctl and has complete information. Reduced tabular output by default. Issue: #515 Signed-off-by: David Sterba --- cmds/commands.h | 1 + cmds/subvolume-list.c | 117 ++++++++++++++++++++++++++++++++++++++++++ cmds/subvolume.c | 3 ++ 3 files changed, 121 insertions(+) diff --git a/cmds/commands.h b/cmds/commands.h index 5ab7c881..91c58757 100644 --- a/cmds/commands.h +++ b/cmds/commands.h @@ -131,6 +131,7 @@ extern const char * const generic_cmd_help_usage[]; DECLARE_COMMAND(subvolume); DECLARE_COMMAND(subvolume_list); +DECLARE_COMMAND(subvolume_ls); DECLARE_COMMAND(filesystem); DECLARE_COMMAND(filesystem_du); DECLARE_COMMAND(filesystem_usage); diff --git a/cmds/subvolume-list.c b/cmds/subvolume-list.c index 6d5ef509..bcb09469 100644 --- a/cmds/subvolume-list.c +++ b/cmds/subvolume-list.c @@ -27,12 +27,14 @@ #include "kernel-lib/rbtree.h" #include "kernel-lib/rbtree_types.h" #include "kernel-shared/ctree.h" +#include "libbtrfsutil/btrfsutil.h" #include "common/defs.h" #include "common/rbtree-utils.h" #include "common/help.h" #include "common/messages.h" #include "common/open-utils.h" #include "common/string-utils.h" +#include "common/string-table.h" #include "common/utils.h" #include "cmds/commands.h" #include "ioctl.h" @@ -1642,3 +1644,118 @@ out: return !!ret; } DEFINE_SIMPLE_COMMAND(subvolume_list, "list"); + +static const char * const cmd_subvolume_ls_usage[] = { + "btrfs subvolume ls [options] ", + "New interface for subvolume listing.", + "New interface for subvolume listing.", + NULL +}; + +static int cmd_subvolume_ls(const struct cmd_struct *cmd, int argc, char **argv) +{ + int fd = -1; + int ret = -1; + char *subvol; + DIR *dirstream = NULL; + struct btrfs_util_subvolume_iterator *iter; + enum btrfs_util_error err; + struct string_table *tab = NULL; + unsigned int row; + int i; + enum { + C_SUBVOLID, C_GENERATION, C_FLAGS, C_PATH, + C_COLUMN_COUNT + }; + + optind = 0; + while (1) { + int c; + static const struct option long_options[] = { + {NULL, 0, NULL, 0} + }; + + c = getopt_long(argc, argv, "", long_options, NULL); + if (c < 0) + break; + + switch (c) { + default: + usage_unknown_option(cmd, argv); + } + } + + if (check_argc_exact(argc - optind, 1)) + goto out; + + subvol = argv[optind]; + fd = btrfs_open_dir(subvol, &dirstream, 1); + if (fd < 0) { + ret = -1; + error("can't access '%s'", subvol); + goto out; + } + + pr_verbose(LOG_DEBUG, "Create iterator\n"); + err = btrfs_util_create_subvolume_iterator_fd(fd, 0, 0, &iter); + if (err) { + error_btrfs_util(err); + goto out; + } + + tab = table_create(C_COLUMN_COUNT, 20); + if (!tab) { + error("not enough memory"); + goto out; + } + tab->hrows = 2; + row = tab->hrows; + table_printf(tab, C_SUBVOLID, 0, "ncols; i++) + table_printf(tab, i, 1, "*-"); + + while (1) { + struct btrfs_util_subvolume_info subvol; + char *path; + char str_flags[16] = { 0 }; + + err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol); + if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) { + table_dump(tab); + goto out; + } else if (err) { + error_btrfs_util(err); + btrfs_util_destroy_subvolume_iterator(iter); + goto out; + } + if (!uuid_is_null(subvol.parent_uuid)) + str_flags[0] = 's'; + else + str_flags[0] = 'u'; + if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY) + str_flags[1] = 'r'; + else + str_flags[1] = 'w'; + if (!uuid_is_null(subvol.received_uuid)) + str_flags[2] = 'R'; + table_printf(tab, C_SUBVOLID, row, ">%llu", subvol.id); + table_printf(tab, C_GENERATION, row, ">%llu", subvol.generation); + table_printf(tab, C_FLAGS, row, ">%s", str_flags); + table_printf(tab, C_PATH, row, "<%s", path); + free(path); + row++; + if (row >= tab->nrows) + break; + } + btrfs_util_destroy_subvolume_iterator(iter); + table_dump(tab); + +out: + table_free(tab); + close_file_or_dir(fd, dirstream); + return !!ret; +} +DEFINE_SIMPLE_COMMAND(subvolume_ls, "ls"); diff --git a/cmds/subvolume.c b/cmds/subvolume.c index e427d3c3..aa834120 100644 --- a/cmds/subvolume.c +++ b/cmds/subvolume.c @@ -1615,6 +1615,9 @@ static const struct cmd_group subvolume_cmd_group = { &cmd_struct_subvolume_create, &cmd_struct_subvolume_delete, &cmd_struct_subvolume_list, +#if EXPERIMENTAL + &cmd_struct_subvolume_ls, +#endif &cmd_struct_subvolume_snapshot, &cmd_struct_subvolume_get_default, &cmd_struct_subvolume_set_default,