diff --git a/qgroup.c b/qgroup.c index 11a592a7..e5e90fb0 100644 --- a/qgroup.c +++ b/qgroup.c @@ -1044,11 +1044,30 @@ static inline void print_status_flag_warning(u64 flags) warning("qgroup data inconsistent, rescan recommended"); } -static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup) +static bool key_in_range(const struct btrfs_key *key, + const struct btrfs_ioctl_search_key *sk) +{ + if (key->objectid < sk->min_objectid || + key->objectid > sk->max_objectid) + return false; + + if (key->type < sk->min_type || + key->type > sk->max_type) + return false; + + if (key->offset < sk->min_offset || + key->offset > sk->max_offset) + return false; + + return true; +} + +static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args, + struct qgroup_lookup *qgroup_lookup) { int ret; - struct btrfs_ioctl_search_args args; - struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_key *sk = &args->key; + struct btrfs_ioctl_search_key filter_key = args->key; struct btrfs_ioctl_search_header *sh; unsigned long off = 0; unsigned int i; @@ -1059,29 +1078,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup) u64 qgroupid; u64 child, parent; - memset(&args, 0, sizeof(args)); - - sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID; - sk->max_type = BTRFS_QGROUP_RELATION_KEY; - sk->min_type = BTRFS_QGROUP_STATUS_KEY; - sk->max_objectid = (u64)-1; - sk->max_offset = (u64)-1; - sk->max_transid = (u64)-1; - sk->nr_items = 4096; - qgroup_lookup_init(qgroup_lookup); while (1) { - ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args); if (ret < 0) { - if (errno == ENOENT) { - error("can't list qgroups: quotas not enabled"); + if (errno == ENOENT) ret = -ENOTTY; - } else { - error("can't list qgroups: %m"); + else ret = -errno; - } - break; } @@ -1095,37 +1100,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup) * read the root_ref item it contains */ for (i = 0; i < sk->nr_items; i++) { - sh = (struct btrfs_ioctl_search_header *)(args.buf + + struct btrfs_key key; + + sh = (struct btrfs_ioctl_search_header *)(args->buf + off); off += sizeof(*sh); - switch (btrfs_search_header_type(sh)) { + key.objectid = btrfs_search_header_objectid(sh); + key.type = btrfs_search_header_type(sh); + key.offset = btrfs_search_header_offset(sh); + + if (!key_in_range(&key, &filter_key)) + goto next; + + switch (key.type) { case BTRFS_QGROUP_STATUS_KEY: si = (struct btrfs_qgroup_status_item *) - (args.buf + off); + (args->buf + off); flags = btrfs_stack_qgroup_status_flags(si); print_status_flag_warning(flags); break; case BTRFS_QGROUP_INFO_KEY: - qgroupid = btrfs_search_header_offset(sh); + qgroupid = key.offset; info = (struct btrfs_qgroup_info_item *) - (args.buf + off); + (args->buf + off); ret = update_qgroup_info(qgroup_lookup, qgroupid, info); break; case BTRFS_QGROUP_LIMIT_KEY: - qgroupid = btrfs_search_header_offset(sh); + qgroupid = key.offset; limit = (struct btrfs_qgroup_limit_item *) - (args.buf + off); + (args->buf + off); ret = update_qgroup_limit(qgroup_lookup, qgroupid, limit); break; case BTRFS_QGROUP_RELATION_KEY: - child = btrfs_search_header_offset(sh); - parent = btrfs_search_header_objectid(sh); + child = key.offset; + parent = key.objectid; if (parent <= child) break; @@ -1140,15 +1154,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup) if (ret) return ret; +next: off += btrfs_search_header_len(sh); /* * record the mins in sk so we can make sure the * next search doesn't repeat this root */ - sk->min_type = btrfs_search_header_type(sh); - sk->min_offset = btrfs_search_header_offset(sh); - sk->min_objectid = btrfs_search_header_objectid(sh); + sk->min_type = key.type; + sk->min_offset = key.offset; + sk->min_objectid = key.objectid; } sk->nr_items = 4096; /* @@ -1164,6 +1179,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup) return ret; } +static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup) +{ + struct btrfs_ioctl_search_args args = { + .key = { + .tree_id = BTRFS_QUOTA_TREE_OBJECTID, + .max_type = BTRFS_QGROUP_RELATION_KEY, + .min_type = BTRFS_QGROUP_STATUS_KEY, + .max_objectid = (u64)-1, + .max_offset = (u64)-1, + .max_transid = (u64)-1, + .nr_items = 4096, + }, + }; + int ret; + + ret = __qgroups_search(fd, &args, qgroup_lookup); + if (ret == -ENOTTY) + error("can't list qgroups: quotas not enabled"); + else if (ret < 0) + error("can't list qgroups: %s", strerror(-ret)); + return ret; +} + +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats) +{ + struct btrfs_ioctl_search_args args = { + .key = { + .tree_id = BTRFS_QUOTA_TREE_OBJECTID, + .min_type = BTRFS_QGROUP_INFO_KEY, + .max_type = BTRFS_QGROUP_LIMIT_KEY, + .max_objectid = 0, + .min_offset = qgroupid, + .max_offset = qgroupid, + .max_transid = (u64)-1, + .nr_items = 4096, + }, + }; + struct qgroup_lookup qgroup_lookup; + struct btrfs_qgroup *qgroup; + struct rb_node *n; + int ret; + + ret = __qgroups_search(fd, &args, &qgroup_lookup); + if (ret < 0) + return ret; + + ret = -ENODATA; + n = rb_first(&qgroup_lookup.root); + if (n) { + qgroup = rb_entry(n, struct btrfs_qgroup, rb_node); + stats->qgroupid = qgroup->qgroupid; + stats->info = qgroup->info; + stats->limit = qgroup->limit; + + ret = 0; + } + + __free_all_qgroups(&qgroup_lookup); + return ret; +} + static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup) { @@ -1189,7 +1265,7 @@ int btrfs_show_qgroups(int fd, struct qgroup_lookup sort_tree; int ret; - ret = __qgroups_search(fd, &qgroup_lookup); + ret = qgroups_search_all(fd, &qgroup_lookup); if (ret) return ret; __filter_and_sort_qgroups(&qgroup_lookup, &sort_tree, diff --git a/qgroup.h b/qgroup.h index d0d0de2d..cbe21545 100644 --- a/qgroup.h +++ b/qgroup.h @@ -85,6 +85,12 @@ struct btrfs_qgroup_info { u64 exclusive_compressed; }; +struct btrfs_qgroup_stats { + u64 qgroupid; + struct btrfs_qgroup_info info; + struct btrfs_qgroup_limit limit; +}; + int btrfs_qgroup_parse_sort_string(const char *opt_arg, struct btrfs_qgroup_comparer_set **comps); int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *, @@ -103,4 +109,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg); int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg, int type); +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats); #endif