Btrfs-progs: fix double free when deleting subvolumes
[btrfs-progs-unstable/devel.git] / qgroup.c
blob94d1febf4b07f679a0fadec0ce30f471a051421c
1 /*
2 * Copyright (C) 2012 STRATO. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
19 #include "qgroup.h"
20 #include <sys/ioctl.h>
21 #include "ctree.h"
22 #include "ioctl.h"
24 #define BTRFS_QGROUP_NFILTERS_INCREASE (2 * BTRFS_QGROUP_FILTER_MAX)
25 #define BTRFS_QGROUP_NCOMPS_INCREASE (2 * BTRFS_QGROUP_COMP_MAX)
27 struct qgroup_lookup {
28 struct rb_root root;
31 struct btrfs_qgroup {
32 struct rb_node rb_node;
33 struct rb_node sort_node;
35 *all_parent_node is used to
36 *filter a qgroup's all parent
38 struct rb_node all_parent_node;
39 u64 qgroupid;
42 * info_item
44 u64 generation;
45 u64 rfer; /*referenced*/
46 u64 rfer_cmpr; /*referenced compressed*/
47 u64 excl; /*exclusive*/
48 u64 excl_cmpr; /*exclusive compressed*/
51 *limit_item
53 u64 flags; /*which limits are set*/
54 u64 max_rfer;
55 u64 max_excl;
56 u64 rsv_rfer;
57 u64 rsv_excl;
59 /*qgroups this group is member of*/
60 struct list_head qgroups;
61 /*qgroups that are members of this group*/
62 struct list_head members;
66 * glue structure to represent the relations
67 * between qgroups
69 struct btrfs_qgroup_list {
70 struct list_head next_qgroup;
71 struct list_head next_member;
72 struct btrfs_qgroup *qgroup;
73 struct btrfs_qgroup *member;
77 * qgroupid,rfer,excl default to set
79 static struct {
80 char *name;
81 char *column_name;
82 int need_print;
83 int max_len;
84 } btrfs_qgroup_columns[] = {
86 .name = "qgroupid",
87 .column_name = "Qgroupid",
88 .need_print = 1,
89 .max_len = 8,
92 .name = "rfer",
93 .column_name = "Rfer",
94 .need_print = 1,
95 .max_len = 4,
98 .name = "excl",
99 .column_name = "Excl",
100 .need_print = 1,
101 .max_len = 4,
103 { .name = "max_rfer",
104 .column_name = "Max_rfer",
105 .need_print = 0,
106 .max_len = 8,
109 .name = "max_excl",
110 .column_name = "Max_excl",
111 .need_print = 0,
112 .max_len = 8,
115 .name = "parent",
116 .column_name = "Parent",
117 .need_print = 0,
118 .max_len = 7,
121 .name = "child",
122 .column_name = "Child",
123 .need_print = 0,
124 .max_len = 5,
127 .name = NULL,
128 .column_name = NULL,
129 .need_print = 0,
133 static btrfs_qgroup_filter_func all_filter_funcs[];
134 static btrfs_qgroup_comp_func all_comp_funcs[];
136 void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column)
138 int i;
140 BUG_ON(column < 0 || column > BTRFS_QGROUP_ALL);
142 if (column < BTRFS_QGROUP_ALL) {
143 btrfs_qgroup_columns[column].need_print = 1;
144 return;
146 for (i = 0; i < BTRFS_QGROUP_ALL; i++)
147 btrfs_qgroup_columns[i].need_print = 1;
150 static int print_parent_column(struct btrfs_qgroup *qgroup)
152 struct btrfs_qgroup_list *list = NULL;
153 int len = 0;
155 list_for_each_entry(list, &qgroup->qgroups, next_qgroup) {
156 len += printf("%llu/%llu", (list->qgroup)->qgroupid >> 48,
157 ((1ll << 48) - 1) & (list->qgroup)->qgroupid);
158 if (!list_is_last(&list->next_qgroup, &qgroup->qgroups))
159 len += printf(",");
161 if (list_empty(&qgroup->qgroups))
162 len += printf("---");
164 return len;
167 static int print_child_column(struct btrfs_qgroup *qgroup)
169 struct btrfs_qgroup_list *list = NULL;
170 int len = 0;
172 list_for_each_entry(list, &qgroup->members, next_member) {
173 len += printf("%llu/%llu", (list->member)->qgroupid >> 48,
174 ((1ll << 48) - 1) & (list->member)->qgroupid);
175 if (!list_is_last(&list->next_member, &qgroup->members))
176 len += printf(",");
178 if (list_empty(&qgroup->members))
179 len += printf("---");
181 return len;
184 static void print_qgroup_column_add_blank(enum btrfs_qgroup_column_enum column,
185 int len)
187 len = btrfs_qgroup_columns[column].max_len - len;
188 while (len--)
189 printf(" ");
192 static void print_qgroup_column(struct btrfs_qgroup *qgroup,
193 enum btrfs_qgroup_column_enum column)
195 BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0);
196 int len;
198 switch (column) {
200 case BTRFS_QGROUP_QGROUPID:
201 len = printf("%llu/%llu", qgroup->qgroupid >> 48,
202 ((1ll << 48) - 1) & qgroup->qgroupid);
203 print_qgroup_column_add_blank(BTRFS_QGROUP_QGROUPID, len);
204 break;
205 case BTRFS_QGROUP_RFER:
206 len = printf("%lld", qgroup->rfer);
207 print_qgroup_column_add_blank(BTRFS_QGROUP_RFER, len);
208 break;
209 case BTRFS_QGROUP_EXCL:
210 len = printf("%lld", qgroup->excl);
211 print_qgroup_column_add_blank(BTRFS_QGROUP_EXCL, len);
212 break;
213 case BTRFS_QGROUP_PARENT:
214 len = print_parent_column(qgroup);
215 print_qgroup_column_add_blank(BTRFS_QGROUP_PARENT, len);
216 break;
217 case BTRFS_QGROUP_MAX_RFER:
218 len = printf("%llu", qgroup->max_rfer);
219 print_qgroup_column_add_blank(BTRFS_QGROUP_MAX_RFER, len);
220 break;
221 case BTRFS_QGROUP_MAX_EXCL:
222 len = printf("%llu", qgroup->max_excl);
223 print_qgroup_column_add_blank(BTRFS_QGROUP_MAX_EXCL, len);
224 break;
225 case BTRFS_QGROUP_CHILD:
226 len = print_child_column(qgroup);
227 print_qgroup_column_add_blank(BTRFS_QGROUP_CHILD, len);
228 break;
229 default:
230 break;
234 static void print_single_qgroup_table(struct btrfs_qgroup *qgroup)
236 int i;
238 for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
239 if (!btrfs_qgroup_columns[i].need_print)
240 continue;
241 print_qgroup_column(qgroup, i);
243 if (i != BTRFS_QGROUP_CHILD)
244 printf(" ");
246 printf("\n");
249 static void print_table_head()
251 int i;
252 int len;
254 for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
255 if (!btrfs_qgroup_columns[i].need_print)
256 continue;
257 printf("%s", btrfs_qgroup_columns[i].name);
258 len = btrfs_qgroup_columns[i].max_len -
259 strlen(btrfs_qgroup_columns[i].name);
260 while (len--)
261 printf(" ");
262 printf(" ");
264 printf("\n");
265 for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
266 if (!btrfs_qgroup_columns[i].need_print)
267 continue;
269 len = strlen(btrfs_qgroup_columns[i].name);
270 while (len--)
271 printf("-");
272 len = btrfs_qgroup_columns[i].max_len -
273 strlen(btrfs_qgroup_columns[i].name);
274 printf(" ");
275 while (len--)
276 printf(" ");
278 printf("\n");
281 static void qgroup_lookup_init(struct qgroup_lookup *tree)
283 tree->root.rb_node = NULL;
286 static int comp_entry_with_qgroupid(struct btrfs_qgroup *entry1,
287 struct btrfs_qgroup *entry2,
288 int is_descending)
291 int ret;
293 if (entry1->qgroupid > entry2->qgroupid)
294 ret = 1;
295 else if (entry1->qgroupid < entry2->qgroupid)
296 ret = -1;
297 else
298 ret = 0;
300 return is_descending ? -ret : ret;
303 static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
304 struct btrfs_qgroup *entry2,
305 int is_descending)
307 int ret;
309 if (entry1->rfer > entry2->rfer)
310 ret = 1;
311 else if (entry1->rfer < entry2->rfer)
312 ret = -1;
313 else
314 ret = 0;
316 return is_descending ? -ret : ret;
319 static int comp_entry_with_excl(struct btrfs_qgroup *entry1,
320 struct btrfs_qgroup *entry2,
321 int is_descending)
323 int ret;
325 if (entry1->excl > entry2->excl)
326 ret = 1;
327 else if (entry1->excl < entry2->excl)
328 ret = -1;
329 else
330 ret = 0;
332 return is_descending ? -ret : ret;
335 static int comp_entry_with_max_rfer(struct btrfs_qgroup *entry1,
336 struct btrfs_qgroup *entry2,
337 int is_descending)
339 int ret;
341 if (entry1->max_rfer > entry2->max_rfer)
342 ret = 1;
343 else if (entry1->max_rfer < entry2->max_rfer)
344 ret = -1;
345 else
346 ret = 0;
348 return is_descending ? -ret : ret;
351 static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
352 struct btrfs_qgroup *entry2,
353 int is_descending)
355 int ret;
357 if (entry1->max_excl > entry2->max_excl)
358 ret = 1;
359 else if (entry1->max_excl < entry2->max_excl)
360 ret = -1;
361 else
362 ret = 0;
364 return is_descending ? -ret : ret;
367 static btrfs_qgroup_comp_func all_comp_funcs[] = {
368 [BTRFS_QGROUP_COMP_QGROUPID] = comp_entry_with_qgroupid,
369 [BTRFS_QGROUP_COMP_RFER] = comp_entry_with_rfer,
370 [BTRFS_QGROUP_COMP_EXCL] = comp_entry_with_excl,
371 [BTRFS_QGROUP_COMP_MAX_RFER] = comp_entry_with_max_rfer,
372 [BTRFS_QGROUP_COMP_MAX_EXCL] = comp_entry_with_max_excl
375 static char *all_sort_items[] = {
376 [BTRFS_QGROUP_COMP_QGROUPID] = "qgroupid",
377 [BTRFS_QGROUP_COMP_RFER] = "rfer",
378 [BTRFS_QGROUP_COMP_EXCL] = "excl",
379 [BTRFS_QGROUP_COMP_MAX_RFER] = "max_rfer",
380 [BTRFS_QGROUP_COMP_MAX_EXCL] = "max_excl",
381 [BTRFS_QGROUP_COMP_MAX] = NULL,
384 static int btrfs_qgroup_get_sort_item(char *sort_name)
386 int i;
388 for (i = 0; i < BTRFS_QGROUP_COMP_MAX; i++) {
389 if (strcmp(sort_name, all_sort_items[i]) == 0)
390 return i;
392 return -1;
395 struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void)
397 struct btrfs_qgroup_comparer_set *set;
398 int size;
399 size = sizeof(struct btrfs_qgroup_comparer_set) +
400 BTRFS_QGROUP_NCOMPS_INCREASE *
401 sizeof(struct btrfs_qgroup_comparer);
402 set = malloc(size);
403 if (!set) {
404 fprintf(stderr, "memory allocation failed\n");
405 exit(1);
408 memset(set, 0, size);
409 set->total = BTRFS_QGROUP_NCOMPS_INCREASE;
411 return set;
414 void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set)
416 free(comp_set);
419 int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
420 enum btrfs_qgroup_comp_enum comparer,
421 int is_descending)
423 struct btrfs_qgroup_comparer_set *set = *comp_set;
424 int size;
426 BUG_ON(!set);
427 BUG_ON(comparer >= BTRFS_QGROUP_COMP_MAX);
428 BUG_ON(set->ncomps > set->total);
430 if (set->ncomps == set->total) {
431 size = set->total + BTRFS_QGROUP_NCOMPS_INCREASE;
432 size = sizeof(*set) +
433 size * sizeof(struct btrfs_qgroup_comparer);
434 set = realloc(set, size);
435 if (!set) {
436 fprintf(stderr, "memory allocation failed\n");
437 exit(1);
440 memset(&set->comps[set->total], 0,
441 BTRFS_QGROUP_NCOMPS_INCREASE *
442 sizeof(struct btrfs_qgroup_comparer));
443 set->total += BTRFS_QGROUP_NCOMPS_INCREASE;
444 *comp_set = set;
447 BUG_ON(set->comps[set->ncomps].comp_func);
449 set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
450 set->comps[set->ncomps].is_descending = is_descending;
451 set->ncomps++;
452 return 0;
455 static int sort_comp(struct btrfs_qgroup *entry1, struct btrfs_qgroup *entry2,
456 struct btrfs_qgroup_comparer_set *set)
458 int qgroupid_compared = 0;
459 int i, ret = 0;
461 if (!set || !set->ncomps)
462 goto comp_qgroupid;
464 for (i = 0; i < set->ncomps; i++) {
465 if (!set->comps[i].comp_func)
466 break;
468 ret = set->comps[i].comp_func(entry1, entry2,
469 set->comps[i].is_descending);
470 if (ret)
471 return ret;
473 if (set->comps[i].comp_func == comp_entry_with_qgroupid)
474 qgroupid_compared = 1;
477 if (!qgroupid_compared) {
478 comp_qgroupid:
479 ret = comp_entry_with_qgroupid(entry1, entry2, 0);
482 return ret;
486 * insert a new root into the tree. returns the existing root entry
487 * if one is already there. qgroupid is used
488 * as the key
490 static int qgroup_tree_insert(struct qgroup_lookup *root_tree,
491 struct btrfs_qgroup *ins)
494 struct rb_node **p = &root_tree->root.rb_node;
495 struct rb_node *parent = NULL;
496 struct btrfs_qgroup *curr;
497 int ret;
499 while (*p) {
500 parent = *p;
501 curr = rb_entry(parent, struct btrfs_qgroup, rb_node);
503 ret = comp_entry_with_qgroupid(ins, curr, 0);
504 if (ret < 0)
505 p = &(*p)->rb_left;
506 else if (ret > 0)
507 p = &(*p)->rb_right;
508 else
509 return -EEXIST;
511 rb_link_node(&ins->rb_node, parent, p);
512 rb_insert_color(&ins->rb_node, &root_tree->root);
513 return 0;
517 *find a given qgroupid in the tree. We return the smallest one,
518 *rb_next can be used to move forward looking for more if required
520 static struct btrfs_qgroup *qgroup_tree_search(struct qgroup_lookup *root_tree,
521 u64 qgroupid)
523 struct rb_node *n = root_tree->root.rb_node;
524 struct btrfs_qgroup *entry;
525 struct btrfs_qgroup tmp;
526 int ret;
528 tmp.qgroupid = qgroupid;
530 while (n) {
531 entry = rb_entry(n, struct btrfs_qgroup, rb_node);
533 ret = comp_entry_with_qgroupid(&tmp, entry, 0);
534 if (ret < 0)
535 n = n->rb_left;
536 else if (ret > 0)
537 n = n->rb_right;
538 else
539 return entry;
542 return NULL;
545 static int update_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
546 u64 generation, u64 rfer, u64 rfer_cmpr, u64 excl,
547 u64 excl_cmpr, u64 flags, u64 max_rfer, u64 max_excl,
548 u64 rsv_rfer, u64 rsv_excl, struct btrfs_qgroup *pa,
549 struct btrfs_qgroup *child)
551 struct btrfs_qgroup *bq;
552 struct btrfs_qgroup_list *list;
554 bq = qgroup_tree_search(qgroup_lookup, qgroupid);
555 if (!bq || bq->qgroupid != qgroupid)
556 return -ENOENT;
558 if (generation)
559 bq->generation = generation;
560 if (rfer)
561 bq->rfer = rfer;
562 if (rfer_cmpr)
563 bq->rfer_cmpr = rfer_cmpr;
564 if (excl)
565 bq->excl = excl;
566 if (excl_cmpr)
567 bq->excl_cmpr = excl_cmpr;
568 if (flags)
569 bq->flags = flags;
570 if (max_rfer)
571 bq->max_rfer = max_rfer;
572 if (max_excl)
573 bq->max_excl = max_excl;
574 if (rsv_rfer)
575 bq->rsv_rfer = rsv_rfer;
576 if (pa && child) {
577 list = malloc(sizeof(*list));
578 if (!list) {
579 fprintf(stderr, "memory allocation failed\n");
580 exit(1);
582 list->qgroup = pa;
583 list->member = child;
584 list_add_tail(&list->next_qgroup, &child->qgroups);
585 list_add_tail(&list->next_member, &pa->members);
587 return 0;
590 static int add_qgroup(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
591 u64 generation, u64 rfer, u64 rfer_cmpr, u64 excl,
592 u64 excl_cmpr, u64 flags, u64 max_rfer, u64 max_excl,
593 u64 rsv_rfer, u64 rsv_excl, struct btrfs_qgroup *parent,
594 struct btrfs_qgroup *child)
596 struct btrfs_qgroup *bq;
597 struct btrfs_qgroup_list *list;
598 int ret;
600 ret = update_qgroup(qgroup_lookup, qgroupid, generation, rfer,
601 rfer_cmpr, excl, excl_cmpr, flags, max_rfer,
602 max_excl, rsv_rfer, rsv_excl, parent, child);
603 if (!ret)
604 return 0;
606 bq = malloc(sizeof(*bq));
607 if (!bq) {
608 printf("memory allocation failed\n");
609 exit(1);
611 memset(bq, 0, sizeof(*bq));
612 if (qgroupid) {
613 bq->qgroupid = qgroupid;
614 INIT_LIST_HEAD(&bq->qgroups);
615 INIT_LIST_HEAD(&bq->members);
617 if (generation)
618 bq->generation = generation;
619 if (rfer)
620 bq->rfer = rfer;
621 if (rfer_cmpr)
622 bq->rfer_cmpr = rfer_cmpr;
623 if (excl)
624 bq->excl = excl;
625 if (excl_cmpr)
626 bq->excl_cmpr = excl_cmpr;
627 if (flags)
628 bq->flags = flags;
629 if (max_rfer)
630 bq->max_rfer = max_rfer;
631 if (max_excl)
632 bq->max_excl = max_excl;
633 if (rsv_rfer)
634 bq->rsv_rfer = rsv_rfer;
635 if (parent && child) {
636 list = malloc(sizeof(*list));
637 if (!list) {
638 fprintf(stderr, "memory allocation failed\n");
639 exit(1);
641 list->qgroup = parent;
642 list->member = child;
643 list_add_tail(&list->next_qgroup, &child->qgroups);
644 list_add_tail(&list->next_member, &parent->members);
646 ret = qgroup_tree_insert(qgroup_lookup, bq);
647 if (ret) {
648 printf("failed to insert tree %llu\n",
649 bq->qgroupid);
650 exit(1);
652 return ret;
655 static void __free_btrfs_qgroup(struct btrfs_qgroup *bq)
657 struct btrfs_qgroup_list *list;
658 while (!list_empty(&bq->qgroups)) {
659 list = list_entry((&bq->qgroups)->next,
660 struct btrfs_qgroup_list,
661 next_qgroup);
662 list_del(&list->next_qgroup);
663 list_del(&list->next_member);
664 free(list);
666 while (!list_empty(&bq->members)) {
667 list = list_entry((&bq->members)->next,
668 struct btrfs_qgroup_list,
669 next_member);
670 list_del(&list->next_qgroup);
671 list_del(&list->next_member);
672 free(list);
674 free(bq);
677 static void __free_all_qgroups(struct qgroup_lookup *root_tree)
679 struct btrfs_qgroup *entry;
680 struct rb_node *n;
682 n = rb_first(&root_tree->root);
683 while (n) {
684 entry = rb_entry(n, struct btrfs_qgroup, rb_node);
685 rb_erase(n, &root_tree->root);
686 __free_btrfs_qgroup(entry);
688 n = rb_first(&root_tree->root);
692 static int filter_all_parent_insert(struct qgroup_lookup *sort_tree,
693 struct btrfs_qgroup *bq)
695 struct rb_node **p = &sort_tree->root.rb_node;
696 struct rb_node *parent = NULL;
697 struct btrfs_qgroup *curr;
698 int ret;
700 while (*p) {
701 parent = *p;
702 curr = rb_entry(parent, struct btrfs_qgroup, all_parent_node);
704 ret = comp_entry_with_qgroupid(bq, curr, 0);
705 if (ret < 0)
706 p = &(*p)->rb_left;
707 else if (ret > 0)
708 p = &(*p)->rb_right;
709 else
710 return -EEXIST;
712 rb_link_node(&bq->all_parent_node, parent, p);
713 rb_insert_color(&bq->all_parent_node, &sort_tree->root);
714 return 0;
717 static int filter_by_parent(struct btrfs_qgroup *bq, u64 data)
719 struct btrfs_qgroup *qgroup =
720 (struct btrfs_qgroup *)(unsigned long)data;
722 if (data == 0)
723 return 0;
724 if (qgroup->qgroupid == bq->qgroupid)
725 return 1;
726 return 0;
729 static int filter_by_all_parent(struct btrfs_qgroup *bq, u64 data)
731 struct qgroup_lookup lookup;
732 struct qgroup_lookup *ql = &lookup;
733 struct btrfs_qgroup_list *list;
734 struct rb_node *n;
735 struct btrfs_qgroup *qgroup =
736 (struct btrfs_qgroup *)(unsigned long)data;
738 if (data == 0)
739 return 0;
740 if (bq->qgroupid == qgroup->qgroupid)
741 return 1;
743 qgroup_lookup_init(ql);
744 filter_all_parent_insert(ql, qgroup);
745 n = rb_first(&ql->root);
746 while (n) {
747 qgroup = rb_entry(n, struct btrfs_qgroup, all_parent_node);
748 if (!list_empty(&qgroup->qgroups)) {
749 list_for_each_entry(list, &qgroup->qgroups,
750 next_qgroup) {
751 if ((list->qgroup)->qgroupid == bq->qgroupid)
752 return 1;
753 filter_all_parent_insert(ql, list->qgroup);
756 rb_erase(n, &ql->root);
757 n = rb_first(&ql->root);
759 return 0;
762 static btrfs_qgroup_filter_func all_filter_funcs[] = {
763 [BTRFS_QGROUP_FILTER_PARENT] = filter_by_parent,
764 [BTRFS_QGROUP_FILTER_ALL_PARENT] = filter_by_all_parent,
767 struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void)
769 struct btrfs_qgroup_filter_set *set;
770 int size;
772 size = sizeof(struct btrfs_qgroup_filter_set) +
773 BTRFS_QGROUP_NFILTERS_INCREASE *
774 sizeof(struct btrfs_qgroup_filter);
775 set = malloc(size);
776 if (!set) {
777 fprintf(stderr, "memory allocation failed\n");
778 exit(1);
780 memset(set, 0, size);
781 set->total = BTRFS_QGROUP_NFILTERS_INCREASE;
783 return set;
786 void btrfs_qgroup_free_filter_set(struct btrfs_qgroup_filter_set *filter_set)
788 free(filter_set);
791 int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
792 enum btrfs_qgroup_filter_enum filter, u64 data)
794 struct btrfs_qgroup_filter_set *set = *filter_set;
795 int size;
797 BUG_ON(!set);
798 BUG_ON(filter >= BTRFS_QGROUP_FILTER_MAX);
799 BUG_ON(set->nfilters > set->total);
801 if (set->nfilters == set->total) {
802 size = set->total + BTRFS_QGROUP_NFILTERS_INCREASE;
803 size = sizeof(*set) + size * sizeof(struct btrfs_qgroup_filter);
805 set = realloc(set, size);
806 if (!set) {
807 fprintf(stderr, "memory allocation failed\n");
808 exit(1);
810 memset(&set->filters[set->total], 0,
811 BTRFS_QGROUP_NFILTERS_INCREASE *
812 sizeof(struct btrfs_qgroup_filter));
813 set->total += BTRFS_QGROUP_NFILTERS_INCREASE;
814 *filter_set = set;
816 BUG_ON(set->filters[set->nfilters].filter_func);
817 set->filters[set->nfilters].filter_func = all_filter_funcs[filter];
818 set->filters[set->nfilters].data = data;
819 set->nfilters++;
820 return 0;
823 static int filter_qgroup(struct btrfs_qgroup *bq,
824 struct btrfs_qgroup_filter_set *set)
826 int i, ret;
828 if (!set || !set->nfilters)
829 return 1;
830 for (i = 0; i < set->nfilters; i++) {
831 if (!set->filters[i].filter_func)
832 break;
833 ret = set->filters[i].filter_func(bq, set->filters[i].data);
834 if (!ret)
835 return 0;
837 return 1;
840 static void pre_process_filter_set(struct qgroup_lookup *lookup,
841 struct btrfs_qgroup_filter_set *set)
843 int i;
844 struct btrfs_qgroup *qgroup_for_filter = NULL;
846 for (i = 0; i < set->nfilters; i++) {
848 if (set->filters[i].filter_func == filter_by_all_parent
849 || set->filters[i].filter_func == filter_by_parent) {
850 qgroup_for_filter = qgroup_tree_search(lookup,
851 set->filters[i].data);
852 set->filters[i].data =
853 (u64)(unsigned long)qgroup_for_filter;
858 static int sort_tree_insert(struct qgroup_lookup *sort_tree,
859 struct btrfs_qgroup *bq,
860 struct btrfs_qgroup_comparer_set *comp_set)
862 struct rb_node **p = &sort_tree->root.rb_node;
863 struct rb_node *parent = NULL;
864 struct btrfs_qgroup *curr;
865 int ret;
867 while (*p) {
868 parent = *p;
869 curr = rb_entry(parent, struct btrfs_qgroup, sort_node);
871 ret = sort_comp(bq, curr, comp_set);
872 if (ret < 0)
873 p = &(*p)->rb_left;
874 else if (ret > 0)
875 p = &(*p)->rb_right;
876 else
877 return -EEXIST;
879 rb_link_node(&bq->sort_node, parent, p);
880 rb_insert_color(&bq->sort_node, &sort_tree->root);
881 return 0;
884 static void __update_columns_max_len(struct btrfs_qgroup *bq,
885 enum btrfs_qgroup_column_enum column)
887 BUG_ON(column >= BTRFS_QGROUP_ALL || column < 0);
888 struct btrfs_qgroup_list *list = NULL;
889 char tmp[100];
890 int len;
892 switch (column) {
894 case BTRFS_QGROUP_QGROUPID:
895 sprintf(tmp, "%llu/%llu", (bq->qgroupid >> 48),
896 bq->qgroupid & ((1ll << 48) - 1));
897 len = strlen(tmp);
898 if (btrfs_qgroup_columns[column].max_len < len)
899 btrfs_qgroup_columns[column].max_len = len;
900 break;
901 case BTRFS_QGROUP_RFER:
902 sprintf(tmp, "%llu", bq->rfer);
903 len = strlen(tmp);
904 if (btrfs_qgroup_columns[column].max_len < len)
905 btrfs_qgroup_columns[column].max_len = len;
906 break;
907 case BTRFS_QGROUP_EXCL:
908 sprintf(tmp, "%llu", bq->excl);
909 len = strlen(tmp);
910 if (btrfs_qgroup_columns[column].max_len < len)
911 btrfs_qgroup_columns[column].max_len = len;
912 break;
913 case BTRFS_QGROUP_MAX_RFER:
914 sprintf(tmp, "%llu", bq->max_rfer);
915 len = strlen(tmp);
916 if (btrfs_qgroup_columns[column].max_len < len)
917 btrfs_qgroup_columns[column].max_len = len;
918 break;
919 case BTRFS_QGROUP_MAX_EXCL:
920 sprintf(tmp, "%llu", bq->max_excl);
921 len = strlen(tmp);
922 if (btrfs_qgroup_columns[column].max_len < len)
923 btrfs_qgroup_columns[column].max_len = len;
924 break;
925 case BTRFS_QGROUP_PARENT:
926 len = 0;
927 list_for_each_entry(list, &bq->qgroups, next_qgroup) {
928 len += sprintf(tmp, "%llu/%llu",
929 (list->qgroup)->qgroupid >> 48,
930 ((1ll << 48) - 1) & (list->qgroup)->qgroupid);
931 if (!list_is_last(&list->next_qgroup, &bq->qgroups))
932 len += 1;
934 if (btrfs_qgroup_columns[column].max_len < len)
935 btrfs_qgroup_columns[column].max_len = len;
936 break;
937 case BTRFS_QGROUP_CHILD:
938 len = 0;
939 list_for_each_entry(list, &bq->members, next_member) {
940 len += sprintf(tmp, "%llu/%llu",
941 (list->member)->qgroupid >> 48,
942 ((1ll << 48) - 1) & (list->member)->qgroupid);
943 if (!list_is_last(&list->next_member, &bq->members))
944 len += 1;
946 if (btrfs_qgroup_columns[column].max_len < len)
947 btrfs_qgroup_columns[column].max_len = len;
948 break;
949 default:
950 break;
955 static void update_columns_max_len(struct btrfs_qgroup *bq)
957 int i;
959 for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
960 if (!btrfs_qgroup_columns[i].need_print)
961 continue;
962 __update_columns_max_len(bq, i);
966 static void __filter_and_sort_qgroups(struct qgroup_lookup *all_qgroups,
967 struct qgroup_lookup *sort_tree,
968 struct btrfs_qgroup_filter_set *filter_set,
969 struct btrfs_qgroup_comparer_set *comp_set)
971 struct rb_node *n;
972 struct btrfs_qgroup *entry;
973 int ret;
975 qgroup_lookup_init(sort_tree);
976 pre_process_filter_set(all_qgroups, filter_set);
978 n = rb_last(&all_qgroups->root);
979 while (n) {
980 entry = rb_entry(n, struct btrfs_qgroup, rb_node);
982 ret = filter_qgroup(entry, filter_set);
983 if (ret) {
984 sort_tree_insert(sort_tree, entry, comp_set);
986 update_columns_max_len(entry);
988 n = rb_prev(n);
991 static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
993 int ret;
994 struct btrfs_ioctl_search_args args;
995 struct btrfs_ioctl_search_key *sk = &args.key;
996 struct btrfs_ioctl_search_header *sh;
997 unsigned long off = 0;
998 unsigned int i;
999 int e;
1000 struct btrfs_qgroup_info_item *info;
1001 struct btrfs_qgroup_limit_item *limit;
1002 struct btrfs_qgroup *bq;
1003 struct btrfs_qgroup *bq1;
1004 u64 a1;
1005 u64 a2;
1006 u64 a3;
1007 u64 a4;
1008 u64 a5;
1010 memset(&args, 0, sizeof(args));
1012 sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
1013 sk->max_type = BTRFS_QGROUP_RELATION_KEY;
1014 sk->min_type = BTRFS_QGROUP_INFO_KEY;
1015 sk->max_objectid = (u64)-1;
1016 sk->max_offset = (u64)-1;
1017 sk->max_transid = (u64)-1;
1018 sk->nr_items = 4096;
1020 qgroup_lookup_init(qgroup_lookup);
1022 while (1) {
1023 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1024 e = errno;
1025 if (ret < 0) {
1026 fprintf(stderr,
1027 "ERROR: can't perform the search - %s\n",
1028 strerror(e));
1029 return ret;
1031 /* the ioctl returns the number of item it found in nr_items */
1032 if (sk->nr_items == 0)
1033 break;
1035 off = 0;
1037 * for each item, pull the key out of the header and then
1038 * read the root_ref item it contains
1040 for (i = 0; i < sk->nr_items; i++) {
1041 sh = (struct btrfs_ioctl_search_header *)(args.buf +
1042 off);
1043 off += sizeof(*sh);
1045 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
1046 info = (struct btrfs_qgroup_info_item *)
1047 (args.buf + off);
1048 a1 = btrfs_stack_qgroup_info_generation(info);
1049 a2 = btrfs_stack_qgroup_info_referenced(info);
1050 a3 =
1051 btrfs_stack_qgroup_info_referenced_compressed
1052 (info);
1053 a4 = btrfs_stack_qgroup_info_exclusive(info);
1054 a5 =
1055 btrfs_stack_qgroup_info_exclusive_compressed
1056 (info);
1057 add_qgroup(qgroup_lookup, sh->offset, a1, a2,
1058 a3, a4, a5, 0, 0, 0, 0, 0, 0, 0);
1059 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
1060 limit = (struct btrfs_qgroup_limit_item *)
1061 (args.buf + off);
1063 a1 = btrfs_stack_qgroup_limit_flags(limit);
1064 a2 = btrfs_stack_qgroup_limit_max_referenced
1065 (limit);
1066 a3 = btrfs_stack_qgroup_limit_max_exclusive
1067 (limit);
1068 a4 = btrfs_stack_qgroup_limit_rsv_referenced
1069 (limit);
1070 a5 = btrfs_stack_qgroup_limit_rsv_exclusive
1071 (limit);
1072 add_qgroup(qgroup_lookup, sh->offset, 0, 0,
1073 0, 0, 0, a1, a2, a3, a4, a5, 0, 0);
1074 } else if (sh->type == BTRFS_QGROUP_RELATION_KEY) {
1075 if (sh->offset < sh->objectid)
1076 goto skip;
1077 bq = qgroup_tree_search(qgroup_lookup,
1078 sh->offset);
1079 if (!bq)
1080 goto skip;
1081 bq1 = qgroup_tree_search(qgroup_lookup,
1082 sh->objectid);
1083 if (!bq1)
1084 goto skip;
1085 add_qgroup(qgroup_lookup, sh->offset, 0, 0,
1086 0, 0, 0, 0, 0, 0, 0, 0, bq, bq1);
1087 } else
1088 goto done;
1089 skip:
1090 off += sh->len;
1093 * record the mins in sk so we can make sure the
1094 * next search doesn't repeat this root
1096 sk->min_type = sh->type;
1097 sk->min_offset = sh->offset;
1098 sk->min_objectid = sh->objectid;
1100 sk->nr_items = 4096;
1102 * this iteration is done, step forward one qgroup for the next
1103 * ioctl
1105 if (sk->min_offset < (u64)-1)
1106 sk->min_offset++;
1107 else
1108 break;
1111 done:
1112 return ret;
1115 static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
1118 struct rb_node *n;
1119 struct btrfs_qgroup *entry;
1121 print_table_head();
1123 n = rb_first(&qgroup_lookup->root);
1124 while (n) {
1125 entry = rb_entry(n, struct btrfs_qgroup, sort_node);
1126 print_single_qgroup_table(entry);
1127 n = rb_next(n);
1131 int btrfs_show_qgroups(int fd,
1132 struct btrfs_qgroup_filter_set *filter_set,
1133 struct btrfs_qgroup_comparer_set *comp_set)
1136 struct qgroup_lookup qgroup_lookup;
1137 struct qgroup_lookup sort_tree;
1138 int ret;
1140 ret = __qgroups_search(fd, &qgroup_lookup);
1141 if (ret)
1142 return ret;
1143 __filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
1144 filter_set, comp_set);
1145 print_all_qgroups(&sort_tree);
1147 __free_all_qgroups(&qgroup_lookup);
1148 btrfs_qgroup_free_filter_set(filter_set);
1149 return ret;
1152 u64 btrfs_get_path_rootid(int fd)
1154 int ret;
1155 struct btrfs_ioctl_ino_lookup_args args;
1157 memset(&args, 0, sizeof(args));
1158 args.objectid = BTRFS_FIRST_FREE_OBJECTID;
1160 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
1161 if (ret < 0) {
1162 fprintf(stderr,
1163 "ERROR: can't perform the search -%s\n",
1164 strerror(errno));
1165 return ret;
1167 return args.treeid;
1170 int btrfs_qgroup_parse_sort_string(char *opt_arg,
1171 struct btrfs_qgroup_comparer_set **comps)
1173 int order;
1174 int flag;
1175 char *p;
1176 char **ptr_argv;
1177 int what_to_sort;
1179 while ((p = strtok(opt_arg, ",")) != NULL) {
1180 flag = 0;
1181 ptr_argv = all_sort_items;
1183 while (*ptr_argv) {
1184 if (strcmp(*ptr_argv, p) == 0) {
1185 flag = 1;
1186 break;
1187 } else {
1188 p++;
1189 if (strcmp(*ptr_argv, p) == 0) {
1190 flag = 1;
1191 p--;
1192 break;
1194 p--;
1196 ptr_argv++;
1199 if (flag == 0)
1200 return -1;
1202 else {
1203 if (*p == '+') {
1204 order = 0;
1205 p++;
1206 } else if (*p == '-') {
1207 order = 1;
1208 p++;
1209 } else
1210 order = 0;
1212 what_to_sort = btrfs_qgroup_get_sort_item(p);
1213 if (what_to_sort < 0)
1214 return -1;
1215 btrfs_qgroup_setup_comparer(comps, what_to_sort, order);
1217 opt_arg = NULL;
1220 return 0;
1223 u64 parse_qgroupid(char *p)
1225 char *s = strchr(p, '/');
1226 char *ptr_src_end = p + strlen(p);
1227 char *ptr_parse_end = NULL;
1228 u64 level;
1229 u64 id;
1231 if (!s) {
1232 id = strtoull(p, &ptr_parse_end, 10);
1233 if (ptr_parse_end != ptr_src_end)
1234 goto err;
1235 return id;
1237 level = strtoull(p, &ptr_parse_end, 10);
1238 if (ptr_parse_end != s)
1239 goto err;
1241 id = strtoull(s+1, &ptr_parse_end, 10);
1242 if (ptr_parse_end != ptr_src_end)
1243 goto err;
1245 return (level << 48) | id;
1246 err:
1247 fprintf(stderr, "ERROR:invalid qgroupid\n");
1248 exit(-1);
1251 int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)
1253 return sizeof(*p) + sizeof(p->qgroups[0]) *
1254 (p->num_qgroups + 2 * p->num_ref_copies +
1255 2 * p->num_excl_copies);
1258 static int
1259 qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit, int n, int pos)
1261 struct btrfs_qgroup_inherit *out;
1262 int nitems = 0;
1264 if (*inherit) {
1265 nitems = (*inherit)->num_qgroups +
1266 (*inherit)->num_ref_copies +
1267 (*inherit)->num_excl_copies;
1270 out = calloc(sizeof(*out) + sizeof(out->qgroups[0]) * (nitems + n), 1);
1271 if (out == NULL) {
1272 fprintf(stderr, "ERROR: Not enough memory\n");
1273 return 13;
1276 if (*inherit) {
1277 struct btrfs_qgroup_inherit *i = *inherit;
1278 int s = sizeof(out->qgroups[0]);
1280 out->num_qgroups = i->num_qgroups;
1281 out->num_ref_copies = i->num_ref_copies;
1282 out->num_excl_copies = i->num_excl_copies;
1283 memcpy(out->qgroups, i->qgroups, pos * s);
1284 memcpy(out->qgroups + pos + n, i->qgroups + pos,
1285 (nitems - pos) * s);
1287 free(*inherit);
1288 *inherit = out;
1290 return 0;
1293 int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
1295 int ret;
1296 u64 qgroupid = parse_qgroupid(arg);
1297 int pos = 0;
1299 if (qgroupid == 0) {
1300 fprintf(stderr, "ERROR: bad qgroup specification\n");
1301 return 12;
1304 if (*inherit)
1305 pos = (*inherit)->num_qgroups;
1306 ret = qgroup_inherit_realloc(inherit, 1, pos);
1307 if (ret)
1308 return ret;
1310 (*inherit)->qgroups[(*inherit)->num_qgroups++] = qgroupid;
1312 return 0;
1315 int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
1316 int type)
1318 int ret;
1319 u64 qgroup_src;
1320 u64 qgroup_dst;
1321 char *p;
1322 int pos = 0;
1324 p = strchr(arg, ':');
1325 if (!p) {
1326 bad:
1327 fprintf(stderr, "ERROR: bad copy specification\n");
1328 return 12;
1330 *p = 0;
1331 qgroup_src = parse_qgroupid(arg);
1332 qgroup_dst = parse_qgroupid(p + 1);
1333 *p = ':';
1335 if (!qgroup_src || !qgroup_dst)
1336 goto bad;
1338 if (*inherit)
1339 pos = (*inherit)->num_qgroups +
1340 (*inherit)->num_ref_copies * 2 * type;
1342 ret = qgroup_inherit_realloc(inherit, 2, pos);
1343 if (ret)
1344 return ret;
1346 (*inherit)->qgroups[pos++] = qgroup_src;
1347 (*inherit)->qgroups[pos++] = qgroup_dst;
1349 if (!type)
1350 ++(*inherit)->num_ref_copies;
1351 else
1352 ++(*inherit)->num_excl_copies;
1354 return 0;