btrfs-progs: Print more info about device sizes
[platform/upstream/btrfs-progs.git] / cmds-fi-disk_usage.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <stdarg.h>
24
25 #include "utils.h"
26 #include "kerncompat.h"
27 #include "ctree.h"
28 #include "string-table.h"
29 #include "cmds-fi-disk_usage.h"
30 #include "commands.h"
31
32 #include "version.h"
33
34 /*
35  * Pretty print the size
36  * PAY ATTENTION: it return a statically buffer
37  */
38 char *df_pretty_sizes(u64 size, int mode)
39 {
40         static char buf[30];
41
42         if (mode & DF_HUMAN_UNIT)
43                 (void)pretty_size_snprintf(size, buf, sizeof(buf), UNITS_DEFAULT);
44         else
45                 sprintf(buf, "%llu", size);
46
47         return buf;
48 }
49
50 /*
51  * Add the chunk info to the chunk_info list
52  */
53 static int add_info_to_list(struct chunk_info **info_ptr,
54                         int *info_count,
55                         struct btrfs_chunk *chunk)
56 {
57
58         u64 type = btrfs_stack_chunk_type(chunk);
59         u64 size = btrfs_stack_chunk_length(chunk);
60         int num_stripes = btrfs_stack_chunk_num_stripes(chunk);
61         int j;
62
63         for (j = 0 ; j < num_stripes ; j++) {
64                 int i;
65                 struct chunk_info *p = 0;
66                 struct btrfs_stripe *stripe;
67                 u64    devid;
68
69                 stripe = btrfs_stripe_nr(chunk, j);
70                 devid = btrfs_stack_stripe_devid(stripe);
71
72                 for (i = 0 ; i < *info_count ; i++)
73                         if ((*info_ptr)[i].type == type &&
74                             (*info_ptr)[i].devid == devid &&
75                             (*info_ptr)[i].num_stripes == num_stripes ) {
76                                 p = (*info_ptr) + i;
77                                 break;
78                         }
79
80                 if (!p) {
81                         int size = sizeof(struct btrfs_chunk) * (*info_count+1);
82                         struct chunk_info *res = realloc(*info_ptr, size);
83
84                         if (!res) {
85                                 free(*info_ptr);
86                                 fprintf(stderr, "ERROR: not enough memory\n");
87                                 return -1;
88                         }
89
90                         *info_ptr = res;
91                         p = res + *info_count;
92                         (*info_count)++;
93
94                         p->devid = devid;
95                         p->type = type;
96                         p->size = 0;
97                         p->num_stripes = num_stripes;
98                 }
99
100                 p->size += size;
101
102         }
103
104         return 0;
105
106 }
107
108 /*
109  *  Helper to sort the chunk type
110  */
111 static int cmp_chunk_block_group(u64 f1, u64 f2)
112 {
113
114         u64 mask;
115
116         if ((f1 & BTRFS_BLOCK_GROUP_TYPE_MASK) ==
117                 (f2 & BTRFS_BLOCK_GROUP_TYPE_MASK))
118                         mask = BTRFS_BLOCK_GROUP_PROFILE_MASK;
119         else if (f2 & BTRFS_BLOCK_GROUP_SYSTEM)
120                         return -1;
121         else if (f1 & BTRFS_BLOCK_GROUP_SYSTEM)
122                         return +1;
123         else
124                         mask = BTRFS_BLOCK_GROUP_TYPE_MASK;
125
126         if ((f1 & mask) > (f2 & mask))
127                 return +1;
128         else if ((f1 & mask) < (f2 & mask))
129                 return -1;
130         else
131                 return 0;
132 }
133
134 /*
135  * Helper to sort the chunk
136  */
137 static int cmp_chunk_info(const void *a, const void *b)
138 {
139         return cmp_chunk_block_group(
140                 ((struct chunk_info *)a)->type,
141                 ((struct chunk_info *)b)->type);
142 }
143
144 int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count)
145 {
146         int ret;
147         struct btrfs_ioctl_search_args args;
148         struct btrfs_ioctl_search_key *sk = &args.key;
149         struct btrfs_ioctl_search_header *sh;
150         unsigned long off = 0;
151         int i, e;
152
153         memset(&args, 0, sizeof(args));
154
155         /*
156          * there may be more than one ROOT_ITEM key if there are
157          * snapshots pending deletion, we have to loop through
158          * them.
159          */
160         sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID;
161
162         sk->min_objectid = 0;
163         sk->max_objectid = (u64)-1;
164         sk->max_type = 0;
165         sk->min_type = (u8)-1;
166         sk->min_offset = 0;
167         sk->max_offset = (u64)-1;
168         sk->min_transid = 0;
169         sk->max_transid = (u64)-1;
170         sk->nr_items = 4096;
171
172         while (1) {
173                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
174                 e = errno;
175                 if (ret == -EPERM) {
176                         fprintf(stderr,
177                                 "ERROR: can't read detailed chunk info from ioctl(TREE_SEARCH), run as root\n");
178                         return 0;
179                 }
180
181                 if (ret < 0) {
182                         fprintf(stderr,
183                                 "ERROR: can't perform the search - %s\n",
184                                 strerror(e));
185                         return -99;
186                 }
187                 /* the ioctl returns the number of item it found in nr_items */
188
189                 if (sk->nr_items == 0)
190                         break;
191
192                 off = 0;
193                 for (i = 0; i < sk->nr_items; i++) {
194                         struct btrfs_chunk *item;
195                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
196                                                                   off);
197
198                         off += sizeof(*sh);
199                         item = (struct btrfs_chunk *)(args.buf + off);
200
201                         if (add_info_to_list(info_ptr, info_count, item)) {
202                                 *info_ptr = 0;
203                                 return -100;
204                         }
205
206                         off += sh->len;
207
208                         sk->min_objectid = sh->objectid;
209                         sk->min_type = sh->type;
210                         sk->min_offset = sh->offset+1;
211
212                 }
213                 if (!sk->min_offset)    /* overflow */
214                         sk->min_type++;
215                 else
216                         continue;
217
218                 if (!sk->min_type)
219                         sk->min_objectid++;
220                  else
221                         continue;
222
223                 if (!sk->min_objectid)
224                         break;
225         }
226
227         qsort(*info_ptr, *info_count, sizeof(struct chunk_info),
228                 cmp_chunk_info);
229
230         return 0;
231 }
232
233 /*
234  * Helper to sort the struct btrfs_ioctl_space_info
235  */
236 static int cmp_btrfs_ioctl_space_info(const void *a, const void *b)
237 {
238         return cmp_chunk_block_group(
239                 ((struct btrfs_ioctl_space_info *)a)->flags,
240                 ((struct btrfs_ioctl_space_info *)b)->flags);
241 }
242
243 /*
244  * This function load all the information about the space usage
245  */
246 static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
247 {
248         struct btrfs_ioctl_space_args *sargs = 0, *sargs_orig = 0;
249         int e, ret, count;
250
251         sargs_orig = sargs = calloc(1, sizeof(struct btrfs_ioctl_space_args));
252         if (!sargs) {
253                 fprintf(stderr, "ERROR: not enough memory\n");
254                 return NULL;
255         }
256
257         sargs->space_slots = 0;
258         sargs->total_spaces = 0;
259
260         ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
261         e = errno;
262         if (ret) {
263                 fprintf(stderr,
264                         "ERROR: couldn't get space info on '%s' - %s\n",
265                         path, strerror(e));
266                 free(sargs);
267                 return NULL;
268         }
269         if (!sargs->total_spaces) {
270                 free(sargs);
271                 printf("No chunks found\n");
272                 return NULL;
273         }
274
275         count = sargs->total_spaces;
276
277         sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) +
278                         (count * sizeof(struct btrfs_ioctl_space_info)));
279         if (!sargs) {
280                 free(sargs_orig);
281                 fprintf(stderr, "ERROR: not enough memory\n");
282                 return NULL;
283         }
284
285         sargs->space_slots = count;
286         sargs->total_spaces = 0;
287
288         ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
289         e = errno;
290
291         if (ret) {
292                 fprintf(stderr,
293                         "ERROR: couldn't get space info on '%s' - %s\n",
294                         path, strerror(e));
295                 free(sargs);
296                 return NULL;
297         }
298
299         qsort(&(sargs->spaces), count, sizeof(struct btrfs_ioctl_space_info),
300                 cmp_btrfs_ioctl_space_info);
301
302         return sargs;
303 }
304
305 /* Not used, keep for later */
306 #if 0
307 /*
308  * This function computes the space occuped by a *single* RAID5/RAID6 chunk.
309  * The computation is performed on the basis of the number of stripes
310  * which compose the chunk, which could be different from the number of devices
311  * if a disk is added later.
312  */
313 static int get_raid56_used(int fd, u64 *raid5_used, u64 *raid6_used)
314 {
315         struct chunk_info *info_ptr=0, *p;
316         int info_count=0;
317         int ret;
318
319         *raid5_used = *raid6_used =0;
320
321         ret = load_chunk_info(fd, &info_ptr, &info_count);
322         if( ret < 0)
323                 return ret;
324
325         for ( p = info_ptr; info_count ; info_count--, p++ ) {
326                 if (p->type & BTRFS_BLOCK_GROUP_RAID5)
327                         (*raid5_used) += p->size / (p->num_stripes -1);
328                 if (p->type & BTRFS_BLOCK_GROUP_RAID6)
329                         (*raid6_used) += p->size / (p->num_stripes -2);
330         }
331         free(info_ptr);
332
333         return 0;
334
335 }
336
337 static int _cmd_disk_free(int fd, char *path, int mode)
338 {
339         struct btrfs_ioctl_space_args *sargs = 0;
340         int i;
341         int ret = 0;
342         int e, width;
343         u64 total_disk;         /* filesystem size == sum of
344                                    device sizes */
345         u64 total_chunks;       /* sum of chunks sizes on disk(s) */
346         u64 total_used;         /* logical space used */
347         u64 total_free;         /* logical space un-used */
348         double K;
349         u64 raid5_used, raid6_used;
350
351         if ((sargs = load_space_info(fd, path)) == NULL) {
352                 ret = -1;
353                 goto exit;
354         }
355
356         total_disk = disk_size(path);
357         e = errno;
358         if (total_disk == 0) {
359                 fprintf(stderr,
360                         "ERROR: couldn't get space info on '%s' - %s\n",
361                         path, strerror(e));
362
363                 ret = 19;
364                 goto exit;
365         }
366         if (get_raid56_used(fd, &raid5_used, &raid6_used) < 0) {
367                 fprintf(stderr,
368                         "ERROR: couldn't get space info on '%s'\n",
369                         path );
370                 ret = 20;
371                 goto exit;
372         }
373
374         total_chunks = total_used = total_free = 0;
375
376         for (i = 0; i < sargs->total_spaces; i++) {
377                 float ratio = 1;
378                 u64 allocated;
379                 u64 flags = sargs->spaces[i].flags;
380
381                 /*
382                  * The raid5/raid6 ratio depends by the stripes number
383                  * used by every chunk. It is computed separately
384                  */
385                 if (flags & BTRFS_BLOCK_GROUP_RAID0)
386                         ratio = 1;
387                 else if (flags & BTRFS_BLOCK_GROUP_RAID1)
388                         ratio = 2;
389                 else if (flags & BTRFS_BLOCK_GROUP_RAID5)
390                         ratio = 0;
391                 else if (flags & BTRFS_BLOCK_GROUP_RAID6)
392                         ratio = 0;
393                 else if (flags & BTRFS_BLOCK_GROUP_DUP)
394                         ratio = 2;
395                 else if (flags & BTRFS_BLOCK_GROUP_RAID10)
396                         ratio = 2;
397                 else
398                         ratio = 1;
399
400                 allocated = sargs->spaces[i].total_bytes * ratio;
401
402                 total_chunks += allocated;
403                 total_used += sargs->spaces[i].used_bytes;
404                 total_free += (sargs->spaces[i].total_bytes -
405                                         sargs->spaces[i].used_bytes);
406
407         }
408
409         /* add the raid5/6 allocated space */
410         total_chunks += raid5_used + raid6_used;
411
412         K = ((double)total_used + (double)total_free) / (double)total_chunks;
413
414         if (mode & DF_HUMAN_UNIT)
415                 width = 10;
416         else
417                 width = 18;
418
419         printf("Disk size:\t\t%*s\n", width,
420                 df_pretty_sizes(total_disk, mode));
421         printf("Disk allocated:\t\t%*s\n", width,
422                 df_pretty_sizes(total_chunks, mode));
423         printf("Disk unallocated:\t%*s\n", width,
424                 df_pretty_sizes(total_disk-total_chunks, mode));
425         printf("Used:\t\t\t%*s\n", width,
426                 df_pretty_sizes(total_used, mode));
427         printf("Free (Estimated):\t%*s\t(",
428                 width,
429                 df_pretty_sizes((u64)(K*total_disk-total_used), mode));
430         printf("Max: %s, ",
431                 df_pretty_sizes(total_disk-total_chunks+total_free, mode));
432         printf("min: %s)\n",
433                 df_pretty_sizes((total_disk-total_chunks)/2+total_free, mode));
434         printf("Data to disk ratio:\t%*.0f %%\n",
435                 width-2, K*100);
436
437 exit:
438
439         if (sargs)
440                 free(sargs);
441
442         return ret;
443 }
444 #endif
445
446 /*
447  *  Helper to sort the device_info structure
448  */
449 static int cmp_device_info(const void *a, const void *b)
450 {
451         return strcmp(((struct device_info *)a)->path,
452                         ((struct device_info *)b)->path);
453 }
454
455 /*
456  *  This function loads the device_info structure and put them in an array
457  */
458 int load_device_info(int fd, struct device_info **device_info_ptr,
459                            int *device_info_count)
460 {
461         int ret, i, ndevs;
462         struct btrfs_ioctl_fs_info_args fi_args;
463         struct btrfs_ioctl_dev_info_args dev_info;
464         struct device_info *info;
465
466         *device_info_count = 0;
467         *device_info_ptr = 0;
468
469         ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
470         if (ret == -EPERM) {
471                 fprintf(stderr, "ERROR: can't get filesystem info from ioctl(FS_INFO), run as root\n");
472                 return -1;
473         }
474         if (ret < 0) {
475                 fprintf(stderr, "ERROR: cannot get filesystem info\n");
476                 return -1;
477         }
478
479         info = calloc(fi_args.num_devices, sizeof(struct device_info));
480         if (!info) {
481                 fprintf(stderr, "ERROR: not enough memory\n");
482                 return -1;
483         }
484
485         for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) {
486                 BUG_ON(ndevs >= fi_args.num_devices);
487                 memset(&dev_info, 0, sizeof(dev_info));
488                 ret = get_device_info(fd, i, &dev_info);
489
490                 if (ret == -ENODEV)
491                         continue;
492                 if (ret) {
493                         fprintf(stderr,
494                             "ERROR: cannot get info about device devid=%d\n",
495                             i);
496                         free(info);
497                         return -1;
498                 }
499
500                 info[ndevs].devid = dev_info.devid;
501                 strcpy(info[ndevs].path, (char *)dev_info.path);
502                 info[ndevs].device_size = get_partition_size((char *)dev_info.path);
503                 info[ndevs].size = dev_info.total_bytes;
504                 ++ndevs;
505         }
506
507         BUG_ON(ndevs != fi_args.num_devices);
508         qsort(info, fi_args.num_devices,
509                 sizeof(struct device_info), cmp_device_info);
510
511         *device_info_count = fi_args.num_devices;
512         *device_info_ptr = info;
513
514         return 0;
515 }
516
517 /*
518  *  This function computes the size of a chunk in a disk
519  */
520 static u64 calc_chunk_size(struct chunk_info *ci)
521 {
522         if (ci->type & BTRFS_BLOCK_GROUP_RAID0)
523                 return ci->size / ci->num_stripes;
524         else if (ci->type & BTRFS_BLOCK_GROUP_RAID1)
525                 return ci->size ;
526         else if (ci->type & BTRFS_BLOCK_GROUP_DUP)
527                 return ci->size ;
528         else if (ci->type & BTRFS_BLOCK_GROUP_RAID5)
529                 return ci->size / (ci->num_stripes -1);
530         else if (ci->type & BTRFS_BLOCK_GROUP_RAID6)
531                 return ci->size / (ci->num_stripes -2);
532         else if (ci->type & BTRFS_BLOCK_GROUP_RAID10)
533                 return ci->size / ci->num_stripes;
534         return ci->size;
535 }
536
537 /*
538  *  This function print the results of the command "btrfs fi usage"
539  *  in tabular format
540  */
541 static void _cmd_filesystem_usage_tabular(int mode,
542                                         struct btrfs_ioctl_space_args *sargs,
543                                         struct chunk_info *chunks_info_ptr,
544                                         int chunks_info_count,
545                                         struct device_info *device_info_ptr,
546                                         int device_info_count)
547 {
548         int i;
549         u64 total_unused = 0;
550         struct string_table *matrix = 0;
551         int  ncols, nrows;
552
553         ncols = sargs->total_spaces + 2;
554         nrows = 2 + 1 + device_info_count + 1 + 2;
555
556         matrix = table_create(ncols, nrows);
557         if (!matrix) {
558                 fprintf(stderr, "ERROR: not enough memory\n");
559                 return;
560         }
561
562         /* header */
563         for (i = 0; i < sargs->total_spaces; i++) {
564                 const char *description;
565
566                 u64 flags = sargs->spaces[i].flags;
567                 description = btrfs_group_type_str(flags);
568
569                 table_printf(matrix, 1+i, 0, "<%s", description);
570         }
571
572         for (i = 0; i < sargs->total_spaces; i++) {
573                 const char *r_mode;
574
575                 u64 flags = sargs->spaces[i].flags;
576                 r_mode = btrfs_group_profile_str(flags);
577
578                 table_printf(matrix, 1+i, 1, "<%s", r_mode);
579         }
580
581         table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated");
582
583         /* body */
584         for (i = 0; i < device_info_count; i++) {
585                 int k, col;
586                 char *p;
587
588                 u64  total_allocated = 0, unused;
589
590                 p = strrchr(device_info_ptr[i].path, '/');
591                 if (!p)
592                         p = device_info_ptr[i].path;
593                 else
594                         p++;
595
596                 table_printf(matrix, 0, i + 3, "<%s", device_info_ptr[i].path);
597
598                 for (col = 1, k = 0 ; k < sargs->total_spaces ; k++)  {
599                         u64     flags = sargs->spaces[k].flags;
600                         u64 devid = device_info_ptr[i].devid;
601                         int     j;
602                         u64 size = 0;
603
604                         for (j = 0 ; j < chunks_info_count ; j++) {
605                                 if (chunks_info_ptr[j].type != flags )
606                                                 continue;
607                                 if (chunks_info_ptr[j].devid != devid)
608                                                 continue;
609
610                                 size += calc_chunk_size(chunks_info_ptr+j);
611                         }
612
613                         if (size)
614                                 table_printf(matrix, col, i+3,
615                                         ">%s", df_pretty_sizes(size, mode));
616                         else
617                                 table_printf(matrix, col, i+3, ">-");
618
619                         total_allocated += size;
620                         col++;
621                 }
622
623                 unused = get_partition_size(device_info_ptr[i].path)
624                                 - total_allocated;
625
626                 table_printf(matrix, sargs->total_spaces + 1, i + 3,
627                                ">%s", df_pretty_sizes(unused, mode));
628                 total_unused += unused;
629
630         }
631
632         for (i = 0; i <= sargs->total_spaces; i++)
633                 table_printf(matrix, i + 1, device_info_count + 3, "=");
634
635         /* footer */
636         table_printf(matrix, 0, device_info_count + 4, "<Total");
637         for (i = 0; i < sargs->total_spaces; i++)
638                 table_printf(matrix, 1 + i, device_info_count + 4, ">%s",
639                         df_pretty_sizes(sargs->spaces[i].total_bytes, mode));
640
641         table_printf(matrix, sargs->total_spaces + 1, device_info_count + 4,
642                         ">%s", df_pretty_sizes(total_unused, mode));
643
644         table_printf(matrix, 0, device_info_count + 5, "<Used");
645         for (i = 0; i < sargs->total_spaces; i++)
646                 table_printf(matrix, 1 + i, device_info_count+5, ">%s",
647                         df_pretty_sizes(sargs->spaces[i].used_bytes, mode));
648
649         table_dump(matrix);
650         table_free(matrix);
651 }
652
653 /*
654  *  This function prints the unused space per every disk
655  */
656 static void print_unused(struct chunk_info *info_ptr,
657                           int info_count,
658                           struct device_info *device_info_ptr,
659                           int device_info_count,
660                           int mode)
661 {
662         int i;
663         for (i = 0; i < device_info_count; i++) {
664                 int     j;
665                 u64     total = 0;
666
667                 for (j = 0; j < info_count; j++)
668                         if (info_ptr[j].devid == device_info_ptr[i].devid)
669                                 total += calc_chunk_size(info_ptr+j);
670
671                 printf("   %s\t%10s\n",
672                         device_info_ptr[i].path,
673                         df_pretty_sizes(device_info_ptr[i].size - total, mode));
674         }
675 }
676
677 /*
678  *  This function prints the allocated chunk per every disk
679  */
680 static void print_chunk_device(u64 chunk_type,
681                                 struct chunk_info *chunks_info_ptr,
682                                 int chunks_info_count,
683                                 struct device_info *device_info_ptr,
684                                 int device_info_count,
685                                 int mode)
686 {
687         int i;
688
689         for (i = 0; i < device_info_count; i++) {
690                 int     j;
691                 u64     total = 0;
692
693                 for (j = 0; j < chunks_info_count; j++) {
694
695                         if (chunks_info_ptr[j].type != chunk_type)
696                                 continue;
697                         if (chunks_info_ptr[j].devid != device_info_ptr[i].devid)
698                                 continue;
699
700                         total += calc_chunk_size(&(chunks_info_ptr[j]));
701                         //total += chunks_info_ptr[j].size;
702                 }
703
704                 if (total > 0)
705                         printf("   %s\t%10s\n",
706                                 device_info_ptr[i].path,
707                                 df_pretty_sizes(total, mode));
708         }
709 }
710
711 /*
712  *  This function print the results of the command "btrfs fi usage"
713  *  in linear format
714  */
715 static void _cmd_filesystem_usage_linear(int mode,
716                                         struct btrfs_ioctl_space_args *sargs,
717                                         struct chunk_info *info_ptr,
718                                         int info_count,
719                                         struct device_info *device_info_ptr,
720                                         int device_info_count)
721 {
722         int i;
723
724         for (i = 0; i < sargs->total_spaces; i++) {
725                 const char *description;
726                 const char *r_mode;
727
728                 u64 flags = sargs->spaces[i].flags;
729                 description = btrfs_group_type_str(flags);
730                 r_mode = btrfs_group_profile_str(flags);
731
732                 printf("%s,%s: Size:%s, ",
733                         description,
734                         r_mode,
735                         df_pretty_sizes(sargs->spaces[i].total_bytes ,
736                             mode));
737                 printf("Used:%s\n",
738                         df_pretty_sizes(sargs->spaces[i].used_bytes, mode));
739                 print_chunk_device(flags, info_ptr, info_count,
740                                 device_info_ptr, device_info_count, mode);
741                 printf("\n");
742         }
743
744         printf("Unallocated:\n");
745         print_unused(info_ptr, info_count, device_info_ptr, device_info_count,
746                         mode);
747 }
748
749 static int _cmd_filesystem_usage(int fd, char *path, int mode, int tabular)
750 {
751         struct btrfs_ioctl_space_args *sargs = 0;
752         int info_count = 0;
753         struct chunk_info *info_ptr = 0;
754         struct device_info *device_info_ptr = 0;
755         int device_info_count = 0;
756         int ret = 0;
757
758         if (load_chunk_info(fd, &info_ptr, &info_count) ||
759             load_device_info(fd, &device_info_ptr, &device_info_count)) {
760                 ret = -1;
761                 goto exit;
762         }
763
764         if ((sargs = load_space_info(fd, path)) == NULL) {
765                 ret = -1;
766                 goto exit;
767         }
768
769         if (tabular)
770                 _cmd_filesystem_usage_tabular(mode, sargs,
771                                         info_ptr, info_count,
772                                         device_info_ptr, device_info_count);
773         else
774                 _cmd_filesystem_usage_linear(mode, sargs,
775                                         info_ptr, info_count,
776                                         device_info_ptr, device_info_count);
777
778 exit:
779
780         if (sargs)
781                 free(sargs);
782         if (device_info_ptr)
783                 free(device_info_ptr);
784         if (info_ptr)
785                 free(info_ptr);
786
787         return ret;
788 }
789
790 const char * const cmd_filesystem_usage_usage[] = {
791         "btrfs filesystem usage [-b][-t] <path> [<path>..]",
792         "Show in which disk the chunks are allocated.",
793         "",
794         "-b\tSet byte as unit",
795         "-t\tShow data in tabular format",
796         NULL
797 };
798
799 int cmd_filesystem_usage(int argc, char **argv)
800 {
801
802         int     flags = DF_HUMAN_UNIT;
803         int     i, more_than_one = 0;
804         int     tabular = 0;
805
806         optind = 1;
807         while (1) {
808                 char    c = getopt(argc, argv, "bt");
809                 if (c < 0)
810                         break;
811                 switch (c) {
812                 case 'b':
813                         flags &= ~DF_HUMAN_UNIT;
814                         break;
815                 case 't':
816                         tabular = 1;
817                         break;
818                 default:
819                         usage(cmd_filesystem_usage_usage);
820                 }
821         }
822
823         if (check_argc_min(argc - optind, 1))
824                 usage(cmd_filesystem_usage_usage);
825
826         for (i = optind; i < argc ; i++) {
827                 int r, fd;
828                 DIR     *dirstream = NULL;
829                 if (more_than_one)
830                         printf("\n");
831
832                 fd = open_file_or_dir(argv[i], &dirstream);
833                 if (fd < 0) {
834                         fprintf(stderr, "ERROR: can't access to '%s'\n",
835                                 argv[1]);
836                         return 12;
837                 }
838                 r = _cmd_filesystem_usage(fd, argv[i], flags, tabular);
839                 close_file_or_dir(fd, dirstream);
840
841                 if (r)
842                         return r;
843                 more_than_one = 1;
844
845         }
846
847         return 0;
848 }
849
850 void print_device_chunks(int fd, u64 devid, u64 total_size,
851                 struct chunk_info *chunks_info_ptr,
852                 int chunks_info_count, int mode)
853 {
854         int i;
855         u64 allocated = 0;
856
857         for (i = 0 ; i < chunks_info_count ; i++) {
858                 const char *description;
859                 const char *r_mode;
860                 u64 flags;
861                 u64 size;
862
863                 if (chunks_info_ptr[i].devid != devid)
864                         continue;
865
866                 flags = chunks_info_ptr[i].type;
867
868                 description = btrfs_group_type_str(flags);
869                 r_mode = btrfs_group_profile_str(flags);
870                 size = calc_chunk_size(chunks_info_ptr+i);
871                 printf("   %s,%s:%*s%10s\n",
872                         description,
873                         r_mode,
874                         (int)(20 - strlen(description) - strlen(r_mode)), "",
875                         df_pretty_sizes(size, mode));
876
877                 allocated += size;
878
879         }
880         printf("   Unallocated: %*s%10s\n",
881                 (int)(20 - strlen("Unallocated")), "",
882                 df_pretty_sizes(total_size - allocated, mode));
883 }
884
885 void print_device_sizes(int fd, struct device_info *devinfo, int mode)
886 {
887         printf("   Device size: %*s%10s\n",
888                 (int)(20 - strlen("Device size")), "",
889                 df_pretty_sizes(devinfo->device_size, mode));
890         printf("   FS occupied: %*s%10s\n",
891                 (int)(20 - strlen("FS occupied")), "",
892                 df_pretty_sizes(devinfo->size, mode));
893 }