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,