mirror of
https://github.com/kdave/btrfs-progs
synced 2024-12-25 23:52:17 +00:00
btrfs-progs: qgroup: add path to show output
The 'btrfs qgroup show' command currently only prints qgroup IDs, forcing the user to resolve which subvolume each corresponds to. Adds subvolume path resolution to 'qgroup show' so that when the -P option is used, the last column contains the pathname of the root of the subvolume it describes. In the case of nested qgroups, it will show the number of member qgroups or the paths of the members if the -v option is used. Path can also be used as a sort parameter. Sample output: qgroupid rfer excl path -------- ---- ---- ---- 0/5 16.00KiB 16.00KiB <FS_ROOT> 0/256 16.00KiB 16.00KiB <FS_ROOT>/subv1 0/257 16.00KiB 16.00KiB <missing> 0/258 16.00KiB 16.00KiB <FS_ROOT>/subv3 0/259 16.00KiB 16.00KiB <FS_ROOT>/snap1 Pull-request: #139 Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Jeff Mahoney <jeffm@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
f80a6b40b9
commit
8d1c854094
165
cmds/qgroup.c
165
cmds/qgroup.c
@ -68,6 +68,9 @@ struct btrfs_qgroup {
|
||||
struct rb_node all_parent_node;
|
||||
u64 qgroupid;
|
||||
|
||||
/* NULL for qgroups with level > 0 */
|
||||
const char *path;
|
||||
|
||||
/*
|
||||
* info_item
|
||||
*/
|
||||
@ -118,11 +121,13 @@ enum btrfs_qgroup_column_enum {
|
||||
BTRFS_QGROUP_MAX_EXCL,
|
||||
BTRFS_QGROUP_PARENT,
|
||||
BTRFS_QGROUP_CHILD,
|
||||
BTRFS_QGROUP_PATH,
|
||||
BTRFS_QGROUP_ALL,
|
||||
};
|
||||
|
||||
enum btrfs_qgroup_comp_enum {
|
||||
BTRFS_QGROUP_COMP_QGROUPID,
|
||||
BTRFS_QGROUP_COMP_PATH,
|
||||
BTRFS_QGROUP_COMP_RFER,
|
||||
BTRFS_QGROUP_COMP_EXCL,
|
||||
BTRFS_QGROUP_COMP_MAX_RFER,
|
||||
@ -194,6 +199,13 @@ static struct {
|
||||
.unit_mode = 0,
|
||||
.max_len = 5,
|
||||
},
|
||||
{
|
||||
.name = "path",
|
||||
.column_name = "Path",
|
||||
.need_print = 0,
|
||||
.unit_mode = 0,
|
||||
.max_len = 10,
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
.column_name = NULL,
|
||||
@ -276,8 +288,50 @@ static void print_qgroup_column_add_blank(enum btrfs_qgroup_column_enum column,
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
void print_path_column(struct btrfs_qgroup *qgroup, bool verbose)
|
||||
{
|
||||
struct btrfs_qgroup_list *list = NULL;
|
||||
|
||||
fputs(" ", stdout);
|
||||
if (btrfs_qgroup_level(qgroup->qgroupid) > 0) {
|
||||
int count = 0;
|
||||
|
||||
list_for_each_entry(list, &qgroup->qgroups,
|
||||
next_qgroup) {
|
||||
if (verbose) {
|
||||
struct btrfs_qgroup *member = list->qgroup;
|
||||
u64 qgroupid = member->qgroupid;
|
||||
u64 level = btrfs_qgroup_level(qgroupid);
|
||||
u64 sid = btrfs_qgroup_subvid(qgroupid);
|
||||
|
||||
if (count)
|
||||
fputs(" ", stdout);
|
||||
if (level == 0) {
|
||||
const char *path = member->path;
|
||||
|
||||
if (!path)
|
||||
path = "<missing>";
|
||||
fputs(path, stdout);
|
||||
} else
|
||||
printf("%llu/%llu", level, sid);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!count)
|
||||
fputs("<empty>", stdout);
|
||||
else if (!verbose)
|
||||
printf("<%u member qgroup%c>", count,
|
||||
count != 1 ? 's' : '\0');
|
||||
} else if (qgroup->path)
|
||||
printf("<FS_ROOT>%s%s", *qgroup->path ? "/" : "",
|
||||
qgroup->path);
|
||||
else
|
||||
fputs("<missing>", stdout);
|
||||
}
|
||||
|
||||
static void print_qgroup_column(struct btrfs_qgroup *qgroup,
|
||||
enum btrfs_qgroup_column_enum column)
|
||||
enum btrfs_qgroup_column_enum column,
|
||||
bool verbose)
|
||||
{
|
||||
int len;
|
||||
int unit_mode = btrfs_qgroup_columns[column].unit_mode;
|
||||
@ -321,19 +375,22 @@ static void print_qgroup_column(struct btrfs_qgroup *qgroup,
|
||||
len = print_child_column(qgroup);
|
||||
print_qgroup_column_add_blank(BTRFS_QGROUP_CHILD, len);
|
||||
break;
|
||||
case BTRFS_QGROUP_PATH:
|
||||
print_path_column(qgroup, verbose);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_single_qgroup_table(struct btrfs_qgroup *qgroup)
|
||||
static void print_single_qgroup_table(struct btrfs_qgroup *qgroup, bool verbose)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
|
||||
if (!btrfs_qgroup_columns[i].need_print)
|
||||
continue;
|
||||
print_qgroup_column(qgroup, i);
|
||||
print_qgroup_column(qgroup, i, verbose);
|
||||
|
||||
if (i != BTRFS_QGROUP_ALL - 1)
|
||||
printf(" ");
|
||||
@ -406,6 +463,46 @@ static int comp_entry_with_qgroupid(struct btrfs_qgroup *entry1,
|
||||
return is_descending ? -ret : ret;
|
||||
}
|
||||
|
||||
/* Sorts first-level qgroups by path and nested qgroups by qgroupid */
|
||||
static int comp_entry_with_path(struct btrfs_qgroup *entry1,
|
||||
struct btrfs_qgroup *entry2,
|
||||
int is_descending)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *p1 = entry1->path;
|
||||
const char *p2 = entry2->path;
|
||||
u64 level1 = btrfs_qgroup_level(entry1->qgroupid);
|
||||
u64 level2 = btrfs_qgroup_level(entry2->qgroupid);
|
||||
|
||||
if (level1 != level2) {
|
||||
if (entry1->qgroupid > entry2->qgroupid)
|
||||
ret = 1;
|
||||
else if (entry1->qgroupid < entry2->qgroupid)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
while (*p1 && *p2) {
|
||||
if (*p1 != *p2)
|
||||
break;
|
||||
p1++;
|
||||
p2++;
|
||||
}
|
||||
|
||||
if (*p1 == '/')
|
||||
ret = 1;
|
||||
else if (*p2 == '/')
|
||||
ret = -1;
|
||||
else if (*p1 > *p2)
|
||||
ret = 1;
|
||||
else if (*p1 < *p2)
|
||||
ret = -1;
|
||||
out:
|
||||
return (is_descending ? -ret : ret);
|
||||
}
|
||||
|
||||
static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
|
||||
struct btrfs_qgroup *entry2,
|
||||
int is_descending)
|
||||
@ -472,6 +569,7 @@ static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
|
||||
|
||||
static btrfs_qgroup_comp_func all_comp_funcs[] = {
|
||||
[BTRFS_QGROUP_COMP_QGROUPID] = comp_entry_with_qgroupid,
|
||||
[BTRFS_QGROUP_COMP_PATH] = comp_entry_with_path,
|
||||
[BTRFS_QGROUP_COMP_RFER] = comp_entry_with_rfer,
|
||||
[BTRFS_QGROUP_COMP_EXCL] = comp_entry_with_excl,
|
||||
[BTRFS_QGROUP_COMP_MAX_RFER] = comp_entry_with_max_rfer,
|
||||
@ -480,6 +578,7 @@ static btrfs_qgroup_comp_func all_comp_funcs[] = {
|
||||
|
||||
static char *all_sort_items[] = {
|
||||
[BTRFS_QGROUP_COMP_QGROUPID] = "qgroupid",
|
||||
[BTRFS_QGROUP_COMP_PATH] = "path",
|
||||
[BTRFS_QGROUP_COMP_RFER] = "rfer",
|
||||
[BTRFS_QGROUP_COMP_EXCL] = "excl",
|
||||
[BTRFS_QGROUP_COMP_MAX_RFER] = "max_rfer",
|
||||
@ -655,7 +754,7 @@ static struct btrfs_qgroup *qgroup_tree_search(struct qgroup_lookup *root_tree,
|
||||
* Return the pointer to the btrfs_qgroup if found or if inserted successfully.
|
||||
* Return ERR_PTR if any error occurred.
|
||||
*/
|
||||
static struct btrfs_qgroup *get_or_add_qgroup(
|
||||
static struct btrfs_qgroup *get_or_add_qgroup(int fd,
|
||||
struct qgroup_lookup *qgroup_lookup, u64 qgroupid)
|
||||
{
|
||||
struct btrfs_qgroup *bq;
|
||||
@ -675,6 +774,23 @@ static struct btrfs_qgroup *get_or_add_qgroup(
|
||||
INIT_LIST_HEAD(&bq->qgroups);
|
||||
INIT_LIST_HEAD(&bq->members);
|
||||
|
||||
if (btrfs_qgroup_level(qgroupid) == 0) {
|
||||
enum btrfs_util_error uret;
|
||||
char *path;
|
||||
|
||||
uret = btrfs_util_subvolume_path_fd(fd, qgroupid, &path);
|
||||
if (uret == BTRFS_UTIL_OK)
|
||||
bq->path = path;
|
||||
/* Ignore stale qgroup items */
|
||||
else if (uret != BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
|
||||
error("%s", btrfs_util_strerror(uret));
|
||||
if (uret == BTRFS_UTIL_ERROR_NO_MEMORY)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
else
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
}
|
||||
|
||||
ret = qgroup_tree_insert(qgroup_lookup, bq);
|
||||
if (ret) {
|
||||
errno = -ret;
|
||||
@ -686,12 +802,12 @@ static struct btrfs_qgroup *get_or_add_qgroup(
|
||||
return bq;
|
||||
}
|
||||
|
||||
static int update_qgroup_info(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
|
||||
static int update_qgroup_info(int fd, struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
|
||||
struct btrfs_qgroup_info_item *info)
|
||||
{
|
||||
struct btrfs_qgroup *bq;
|
||||
|
||||
bq = get_or_add_qgroup(qgroup_lookup, qgroupid);
|
||||
bq = get_or_add_qgroup(fd, qgroup_lookup, qgroupid);
|
||||
if (IS_ERR_OR_NULL(bq))
|
||||
return PTR_ERR(bq);
|
||||
|
||||
@ -706,13 +822,13 @@ static int update_qgroup_info(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_qgroup_limit(struct qgroup_lookup *qgroup_lookup,
|
||||
static int update_qgroup_limit(int fd, struct qgroup_lookup *qgroup_lookup,
|
||||
u64 qgroupid,
|
||||
struct btrfs_qgroup_limit_item *limit)
|
||||
{
|
||||
struct btrfs_qgroup *bq;
|
||||
|
||||
bq = get_or_add_qgroup(qgroup_lookup, qgroupid);
|
||||
bq = get_or_add_qgroup(fd, qgroup_lookup, qgroupid);
|
||||
if (IS_ERR_OR_NULL(bq))
|
||||
return PTR_ERR(bq);
|
||||
|
||||
@ -784,6 +900,8 @@ static void __free_btrfs_qgroup(struct btrfs_qgroup *bq)
|
||||
list_del(&list->next_member);
|
||||
free(list);
|
||||
}
|
||||
if (bq->path)
|
||||
free((void *)bq->path);
|
||||
free(bq);
|
||||
}
|
||||
|
||||
@ -1194,7 +1312,7 @@ static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
|
||||
info = (struct btrfs_qgroup_info_item *)
|
||||
(args->buf + off);
|
||||
|
||||
ret = update_qgroup_info(qgroup_lookup,
|
||||
ret = update_qgroup_info(fd, qgroup_lookup,
|
||||
qgroupid, info);
|
||||
break;
|
||||
case BTRFS_QGROUP_LIMIT_KEY:
|
||||
@ -1202,7 +1320,7 @@ static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
|
||||
limit = (struct btrfs_qgroup_limit_item *)
|
||||
(args->buf + off);
|
||||
|
||||
ret = update_qgroup_limit(qgroup_lookup,
|
||||
ret = update_qgroup_limit(fd, qgroup_lookup,
|
||||
qgroupid, limit);
|
||||
break;
|
||||
case BTRFS_QGROUP_RELATION_KEY:
|
||||
@ -1308,7 +1426,7 @@ int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
|
||||
static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
|
||||
{
|
||||
|
||||
struct rb_node *n;
|
||||
@ -1319,14 +1437,15 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
|
||||
n = rb_first(&qgroup_lookup->root);
|
||||
while (n) {
|
||||
entry = rb_entry(n, struct btrfs_qgroup, sort_node);
|
||||
print_single_qgroup_table(entry);
|
||||
print_single_qgroup_table(entry, verbose);
|
||||
n = rb_next(n);
|
||||
}
|
||||
}
|
||||
|
||||
static int show_qgroups(int fd,
|
||||
struct btrfs_qgroup_filter_set *filter_set,
|
||||
struct btrfs_qgroup_comparer_set *comp_set)
|
||||
struct btrfs_qgroup_comparer_set *comp_set,
|
||||
bool verbose)
|
||||
{
|
||||
|
||||
struct qgroup_lookup qgroup_lookup;
|
||||
@ -1338,7 +1457,7 @@ static int show_qgroups(int fd,
|
||||
return ret;
|
||||
__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
|
||||
filter_set, comp_set);
|
||||
print_all_qgroups(&sort_tree);
|
||||
print_all_qgroups(&sort_tree, verbose);
|
||||
|
||||
__free_all_qgroups(&qgroup_lookup);
|
||||
return ret;
|
||||
@ -1731,8 +1850,11 @@ static const char * const cmd_qgroup_show_usage[] = {
|
||||
" (including ancestral qgroups)",
|
||||
"-f list all qgroups which impact the given path",
|
||||
" (excluding ancestral qgroups)",
|
||||
"-P print first-level qgroups subvolume path",
|
||||
" - nested qgroups will be reported as a count",
|
||||
"-v verbose, prints pathn for all nested qgroups",
|
||||
HELPINFO_UNITS_LONG,
|
||||
"--sort=qgroupid,rfer,excl,max_rfer,max_excl",
|
||||
"--sort=qgroupid,rfer,excl,max_rfer,max_excl,path",
|
||||
" list qgroups sorted by specified items",
|
||||
" you can use '+' or '-' in front of each item.",
|
||||
" (+:ascending, -:descending, ascending default)",
|
||||
@ -1751,11 +1873,11 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
|
||||
unsigned unit_mode;
|
||||
bool sync = false;
|
||||
enum btrfs_util_error err;
|
||||
|
||||
struct btrfs_qgroup_comparer_set *comparer_set;
|
||||
struct btrfs_qgroup_filter_set *filter_set;
|
||||
filter_set = qgroup_alloc_filter_set();
|
||||
comparer_set = qgroup_alloc_comparer_set();
|
||||
bool verbose = false;
|
||||
|
||||
unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
|
||||
|
||||
@ -1769,10 +1891,11 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
|
||||
static const struct option long_options[] = {
|
||||
{"sort", required_argument, NULL, GETOPT_VAL_SORT},
|
||||
{"sync", no_argument, NULL, GETOPT_VAL_SYNC},
|
||||
{"verbose", no_argument, NULL, 'v'},
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "pcreFf", long_options, NULL);
|
||||
c = getopt_long(argc, argv, "pPcreFfv", long_options, NULL);
|
||||
if (c < 0)
|
||||
break;
|
||||
switch (c) {
|
||||
@ -1794,6 +1917,12 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
|
||||
case 'f':
|
||||
filter_flag |= 0x2;
|
||||
break;
|
||||
case 'P':
|
||||
qgroup_setup_print_column(BTRFS_QGROUP_PATH);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case GETOPT_VAL_SORT:
|
||||
ret = qgroup_parse_sort_string(optarg, &comparer_set);
|
||||
if (ret < 0) {
|
||||
@ -1849,7 +1978,7 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
|
||||
BTRFS_QGROUP_FILTER_PARENT,
|
||||
qgroupid);
|
||||
}
|
||||
ret = show_qgroups(fd, filter_set, comparer_set);
|
||||
ret = show_qgroups(fd, filter_set, comparer_set, verbose);
|
||||
close_file_or_dir(fd, dirstream);
|
||||
free(filter_set);
|
||||
free(comparer_set);
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/const.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <features.h>
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user