diff --git a/cmds/qgroup.c b/cmds/qgroup.c index f841c9d4..aed37685 100644 --- a/cmds/qgroup.c +++ b/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 = ""; + fputs(path, stdout); + } else + printf("%llu/%llu", level, sid); + } + count++; + } + if (!count) + fputs("", stdout); + else if (!verbose) + printf("<%u member qgroup%c>", count, + count != 1 ? 's' : '\0'); + } else if (qgroup->path) + printf("%s%s", *qgroup->path ? "/" : "", + qgroup->path); + else + fputs("", 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); diff --git a/kerncompat.h b/kerncompat.h index 15595500..f7598627 100644 --- a/kerncompat.h +++ b/kerncompat.h @@ -35,7 +35,7 @@ #include #include #include - +#include #include /*