"Fossies" - the Fresh Open Source Software Archive

Member "btrfs-progs-v5.4/cmds/filesystem-usage.c" (3 Dec 2019, 27420 Bytes) of package /linux/misc/btrfs-progs-v5.4.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filesystem-usage.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: v5.3_vs_v5.4.

    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 #include <getopt.h>
   25 #include <fcntl.h>
   26 
   27 #include "common/utils.h"
   28 #include "kerncompat.h"
   29 #include "ctree.h"
   30 #include "common/string-table.h"
   31 #include "cmds/filesystem-usage.h"
   32 #include "cmds/commands.h"
   33 #include "disk-io.h"
   34 
   35 #include "version.h"
   36 #include "common/help.h"
   37 #include "common/device-utils.h"
   38 
   39 /*
   40  * Add the chunk info to the chunk_info list
   41  */
   42 static int add_info_to_list(struct chunk_info **info_ptr,
   43             int *info_count,
   44             struct btrfs_chunk *chunk)
   45 {
   46 
   47     u64 type = btrfs_stack_chunk_type(chunk);
   48     u64 size = btrfs_stack_chunk_length(chunk);
   49     int num_stripes = btrfs_stack_chunk_num_stripes(chunk);
   50     int j;
   51 
   52     for (j = 0 ; j < num_stripes ; j++) {
   53         int i;
   54         struct chunk_info *p = NULL;
   55         struct btrfs_stripe *stripe;
   56         u64    devid;
   57 
   58         stripe = btrfs_stripe_nr(chunk, j);
   59         devid = btrfs_stack_stripe_devid(stripe);
   60 
   61         for (i = 0 ; i < *info_count ; i++)
   62             if ((*info_ptr)[i].type == type &&
   63                 (*info_ptr)[i].devid == devid &&
   64                 (*info_ptr)[i].num_stripes == num_stripes ) {
   65                 p = (*info_ptr) + i;
   66                 break;
   67             }
   68 
   69         if (!p) {
   70             int tmp = sizeof(struct btrfs_chunk) * (*info_count + 1);
   71             struct chunk_info *res = realloc(*info_ptr, tmp);
   72 
   73             if (!res) {
   74                 free(*info_ptr);
   75                 error("not enough memory");
   76                 return -ENOMEM;
   77             }
   78 
   79             *info_ptr = res;
   80             p = res + *info_count;
   81             (*info_count)++;
   82 
   83             p->devid = devid;
   84             p->type = type;
   85             p->size = 0;
   86             p->num_stripes = num_stripes;
   87         }
   88 
   89         p->size += size;
   90 
   91     }
   92 
   93     return 0;
   94 
   95 }
   96 
   97 /*
   98  *  Helper to sort the chunk type
   99  */
  100 static int cmp_chunk_block_group(u64 f1, u64 f2)
  101 {
  102 
  103     u64 mask;
  104 
  105     if ((f1 & BTRFS_BLOCK_GROUP_TYPE_MASK) ==
  106         (f2 & BTRFS_BLOCK_GROUP_TYPE_MASK))
  107             mask = BTRFS_BLOCK_GROUP_PROFILE_MASK;
  108     else if (f2 & BTRFS_BLOCK_GROUP_SYSTEM)
  109             return -1;
  110     else if (f1 & BTRFS_BLOCK_GROUP_SYSTEM)
  111             return +1;
  112     else
  113             mask = BTRFS_BLOCK_GROUP_TYPE_MASK;
  114 
  115     if ((f1 & mask) > (f2 & mask))
  116         return +1;
  117     else if ((f1 & mask) < (f2 & mask))
  118         return -1;
  119     else
  120         return 0;
  121 }
  122 
  123 /*
  124  * Helper to sort the chunk
  125  */
  126 static int cmp_chunk_info(const void *a, const void *b)
  127 {
  128     return cmp_chunk_block_group(
  129         ((struct chunk_info *)a)->type,
  130         ((struct chunk_info *)b)->type);
  131 }
  132 
  133 static int load_chunk_info(int fd, struct chunk_info **info_ptr, int *info_count)
  134 {
  135     int ret;
  136     struct btrfs_ioctl_search_args args;
  137     struct btrfs_ioctl_search_key *sk = &args.key;
  138     struct btrfs_ioctl_search_header *sh;
  139     unsigned long off = 0;
  140     int i, e;
  141 
  142     memset(&args, 0, sizeof(args));
  143 
  144     /*
  145      * there may be more than one ROOT_ITEM key if there are
  146      * snapshots pending deletion, we have to loop through
  147      * them.
  148      */
  149     sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID;
  150 
  151     sk->min_objectid = 0;
  152     sk->max_objectid = (u64)-1;
  153     sk->max_type = 0;
  154     sk->min_type = (u8)-1;
  155     sk->min_offset = 0;
  156     sk->max_offset = (u64)-1;
  157     sk->min_transid = 0;
  158     sk->max_transid = (u64)-1;
  159     sk->nr_items = 4096;
  160 
  161     while (1) {
  162         ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
  163         e = errno;
  164         if (e == EPERM)
  165             return -e;
  166 
  167         if (ret < 0) {
  168             error("cannot look up chunk tree info: %m");
  169             return 1;
  170         }
  171         /* the ioctl returns the number of item it found in nr_items */
  172 
  173         if (sk->nr_items == 0)
  174             break;
  175 
  176         off = 0;
  177         for (i = 0; i < sk->nr_items; i++) {
  178             struct btrfs_chunk *item;
  179             sh = (struct btrfs_ioctl_search_header *)(args.buf +
  180                                   off);
  181 
  182             off += sizeof(*sh);
  183             item = (struct btrfs_chunk *)(args.buf + off);
  184 
  185             ret = add_info_to_list(info_ptr, info_count, item);
  186             if (ret) {
  187                 *info_ptr = NULL;
  188                 return 1;
  189             }
  190 
  191             off += btrfs_search_header_len(sh);
  192 
  193             sk->min_objectid = btrfs_search_header_objectid(sh);
  194             sk->min_type = btrfs_search_header_type(sh);
  195             sk->min_offset = btrfs_search_header_offset(sh)+1;
  196 
  197         }
  198         if (!sk->min_offset)    /* overflow */
  199             sk->min_type++;
  200         else
  201             continue;
  202 
  203         if (!sk->min_type)
  204             sk->min_objectid++;
  205          else
  206             continue;
  207 
  208         if (!sk->min_objectid)
  209             break;
  210     }
  211 
  212     qsort(*info_ptr, *info_count, sizeof(struct chunk_info),
  213         cmp_chunk_info);
  214 
  215     return 0;
  216 }
  217 
  218 /*
  219  * Helper to sort the struct btrfs_ioctl_space_info
  220  */
  221 static int cmp_btrfs_ioctl_space_info(const void *a, const void *b)
  222 {
  223     return cmp_chunk_block_group(
  224         ((struct btrfs_ioctl_space_info *)a)->flags,
  225         ((struct btrfs_ioctl_space_info *)b)->flags);
  226 }
  227 
  228 /*
  229  * This function load all the information about the space usage
  230  */
  231 static struct btrfs_ioctl_space_args *load_space_info(int fd, const char *path)
  232 {
  233     struct btrfs_ioctl_space_args *sargs = NULL, *sargs_orig = NULL;
  234     int ret, count;
  235 
  236     sargs_orig = sargs = calloc(1, sizeof(struct btrfs_ioctl_space_args));
  237     if (!sargs) {
  238         error("not enough memory");
  239         return NULL;
  240     }
  241 
  242     sargs->space_slots = 0;
  243     sargs->total_spaces = 0;
  244 
  245     ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
  246     if (ret < 0) {
  247         error("cannot get space info on '%s': %m", path);
  248         free(sargs);
  249         return NULL;
  250     }
  251     if (!sargs->total_spaces) {
  252         free(sargs);
  253         printf("No chunks found\n");
  254         return NULL;
  255     }
  256 
  257     count = sargs->total_spaces;
  258 
  259     sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) +
  260             (count * sizeof(struct btrfs_ioctl_space_info)));
  261     if (!sargs) {
  262         free(sargs_orig);
  263         error("not enough memory");
  264         return NULL;
  265     }
  266 
  267     sargs->space_slots = count;
  268     sargs->total_spaces = 0;
  269 
  270     ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
  271     if (ret < 0) {
  272         error("cannot get space info with %u slots: %m",
  273             count);
  274         free(sargs);
  275         return NULL;
  276     }
  277 
  278     qsort(&(sargs->spaces), count, sizeof(struct btrfs_ioctl_space_info),
  279         cmp_btrfs_ioctl_space_info);
  280 
  281     return sargs;
  282 }
  283 
  284 /*
  285  * This function computes the space occupied by a *single* RAID5/RAID6 chunk.
  286  * The computation is performed on the basis of the number of stripes
  287  * which compose the chunk, which could be different from the number of devices
  288  * if a disk is added later.
  289  */
  290 static void get_raid56_used(struct chunk_info *chunks, int chunkcount,
  291         u64 *raid5_used, u64 *raid6_used)
  292 {
  293     struct chunk_info *info_ptr = chunks;
  294     *raid5_used = 0;
  295     *raid6_used = 0;
  296 
  297     while (chunkcount-- > 0) {
  298         if (info_ptr->type & BTRFS_BLOCK_GROUP_RAID5)
  299             (*raid5_used) += info_ptr->size / (info_ptr->num_stripes - 1);
  300         if (info_ptr->type & BTRFS_BLOCK_GROUP_RAID6)
  301             (*raid6_used) += info_ptr->size / (info_ptr->num_stripes - 2);
  302         info_ptr++;
  303     }
  304 }
  305 
  306 #define MIN_UNALOCATED_THRESH   SZ_16M
  307 static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
  308         int chunkcount, struct device_info *devinfo, int devcount,
  309         const char *path, unsigned unit_mode)
  310 {
  311     struct btrfs_ioctl_space_args *sargs = NULL;
  312     int i;
  313     int ret = 0;
  314     int width = 10;     /* default 10 for human units */
  315     /*
  316      * r_* prefix is for raw data
  317      * l_* is for logical
  318      */
  319     u64 r_total_size = 0;   /* filesystem size, sum of device sizes */
  320     u64 r_total_chunks = 0; /* sum of chunks sizes on disk(s) */
  321     u64 r_total_used = 0;
  322     u64 r_total_unused = 0;
  323     u64 r_total_missing = 0;    /* sum of missing devices size */
  324     u64 r_data_used = 0;
  325     u64 r_data_chunks = 0;
  326     u64 l_data_chunks = 0;
  327     u64 r_metadata_used = 0;
  328     u64 r_metadata_chunks = 0;
  329     u64 l_metadata_chunks = 0;
  330     u64 r_system_used = 0;
  331     u64 r_system_chunks = 0;
  332     double data_ratio;
  333     double metadata_ratio;
  334     /* logical */
  335     u64 raid5_used = 0;
  336     u64 raid6_used = 0;
  337     u64 l_global_reserve = 0;
  338     u64 l_global_reserve_used = 0;
  339     u64 free_estimated = 0;
  340     u64 free_min = 0;
  341     int max_data_ratio = 1;
  342     int mixed = 0;
  343 
  344     sargs = load_space_info(fd, path);
  345     if (!sargs) {
  346         ret = 1;
  347         goto exit;
  348     }
  349 
  350     r_total_size = 0;
  351     for (i = 0; i < devcount; i++) {
  352         r_total_size += devinfo[i].size;
  353         if (!devinfo[i].device_size)
  354             r_total_missing += devinfo[i].size;
  355     }
  356 
  357     if (r_total_size == 0) {
  358         error("cannot get space info on '%s': %m", path);
  359 
  360         ret = 1;
  361         goto exit;
  362     }
  363     get_raid56_used(chunkinfo, chunkcount, &raid5_used, &raid6_used);
  364 
  365     for (i = 0; i < sargs->total_spaces; i++) {
  366         int ratio;
  367         u64 flags = sargs->spaces[i].flags;
  368 
  369         /*
  370          * The raid5/raid6 ratio depends by the stripes number
  371          * used by every chunk. It is computed separately
  372          */
  373         if (flags & BTRFS_BLOCK_GROUP_RAID0)
  374             ratio = 1;
  375         else if (flags & BTRFS_BLOCK_GROUP_RAID1)
  376             ratio = 2;
  377         else if (flags & BTRFS_BLOCK_GROUP_RAID1C3)
  378             ratio = 3;
  379         else if (flags & BTRFS_BLOCK_GROUP_RAID1C4)
  380             ratio = 4;
  381         else if (flags & BTRFS_BLOCK_GROUP_RAID5)
  382             ratio = 0;
  383         else if (flags & BTRFS_BLOCK_GROUP_RAID6)
  384             ratio = 0;
  385         else if (flags & BTRFS_BLOCK_GROUP_DUP)
  386             ratio = 2;
  387         else if (flags & BTRFS_BLOCK_GROUP_RAID10)
  388             ratio = 2;
  389         else
  390             ratio = 1;
  391 
  392         if (!ratio)
  393             warning("RAID56 detected, not implemented");
  394 
  395         if (ratio > max_data_ratio)
  396             max_data_ratio = ratio;
  397 
  398         if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV) {
  399             l_global_reserve = sargs->spaces[i].total_bytes;
  400             l_global_reserve_used = sargs->spaces[i].used_bytes;
  401         }
  402         if ((flags & (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA))
  403             == (BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA)) {
  404             mixed = 1;
  405         }
  406         if (flags & BTRFS_BLOCK_GROUP_DATA) {
  407             r_data_used += sargs->spaces[i].used_bytes * ratio;
  408             r_data_chunks += sargs->spaces[i].total_bytes * ratio;
  409             l_data_chunks += sargs->spaces[i].total_bytes;
  410         }
  411         if (flags & BTRFS_BLOCK_GROUP_METADATA) {
  412             r_metadata_used += sargs->spaces[i].used_bytes * ratio;
  413             r_metadata_chunks += sargs->spaces[i].total_bytes * ratio;
  414             l_metadata_chunks += sargs->spaces[i].total_bytes;
  415         }
  416         if (flags & BTRFS_BLOCK_GROUP_SYSTEM) {
  417             r_system_used += sargs->spaces[i].used_bytes * ratio;
  418             r_system_chunks += sargs->spaces[i].total_bytes * ratio;
  419         }
  420     }
  421 
  422     r_total_chunks = r_data_chunks + r_system_chunks;
  423     r_total_used = r_data_used + r_system_used;
  424     if (!mixed) {
  425         r_total_chunks += r_metadata_chunks;
  426         r_total_used += r_metadata_used;
  427     }
  428     r_total_unused = r_total_size - r_total_chunks;
  429 
  430     /* Raw / Logical = raid factor, >= 1 */
  431     data_ratio = (double)r_data_chunks / l_data_chunks;
  432     if (mixed)
  433         metadata_ratio = data_ratio;
  434     else
  435         metadata_ratio = (double)r_metadata_chunks / l_metadata_chunks;
  436 
  437 #if 0
  438     /* add the raid5/6 allocated space */
  439     total_chunks += raid5_used + raid6_used;
  440 #endif
  441 
  442     /*
  443      * We're able to fill at least DATA for the unused space
  444      *
  445      * With mixed raid levels, this gives a rough estimate but more
  446      * accurate than just counting the logical free space
  447      * (l_data_chunks - l_data_used)
  448      *
  449      * In non-mixed case there's no difference.
  450      */
  451     free_estimated = (r_data_chunks - r_data_used) / data_ratio;
  452     /*
  453      * For mixed-bg the metadata are left out in calculations thus global
  454      * reserve would be lost. Part of it could be permanently allocated,
  455      * we have to subtract the used bytes so we don't go under zero free.
  456      */
  457     if (mixed)
  458         free_estimated -= l_global_reserve - l_global_reserve_used;
  459     free_min = free_estimated;
  460 
  461     /* Chop unallocatable space */
  462     /* FIXME: must be applied per device */
  463     if (r_total_unused >= MIN_UNALOCATED_THRESH) {
  464         free_estimated += r_total_unused / data_ratio;
  465         /* Match the calculation of 'df', use the highest raid ratio */
  466         free_min += r_total_unused / max_data_ratio;
  467     }
  468 
  469     if (unit_mode != UNITS_HUMAN)
  470         width = 18;
  471 
  472     printf("Overall:\n");
  473 
  474     printf("    Device size:\t\t%*s\n", width,
  475         pretty_size_mode(r_total_size, unit_mode));
  476     printf("    Device allocated:\t\t%*s\n", width,
  477         pretty_size_mode(r_total_chunks, unit_mode));
  478     printf("    Device unallocated:\t\t%*s\n", width,
  479         pretty_size_mode(r_total_unused, unit_mode | UNITS_NEGATIVE));
  480     printf("    Device missing:\t\t%*s\n", width,
  481         pretty_size_mode(r_total_missing, unit_mode));
  482     printf("    Used:\t\t\t%*s\n", width,
  483         pretty_size_mode(r_total_used, unit_mode));
  484     printf("    Free (estimated):\t\t%*s\t(",
  485         width,
  486         pretty_size_mode(free_estimated, unit_mode));
  487     printf("min: %s)\n", pretty_size_mode(free_min, unit_mode));
  488     printf("    Data ratio:\t\t\t%*.2f\n",
  489         width, data_ratio);
  490     printf("    Metadata ratio:\t\t%*.2f\n",
  491         width, metadata_ratio);
  492     printf("    Global reserve:\t\t%*s\t(used: %s)\n", width,
  493         pretty_size_mode(l_global_reserve, unit_mode),
  494         pretty_size_mode(l_global_reserve_used, unit_mode));
  495 
  496 exit:
  497 
  498     if (sargs)
  499         free(sargs);
  500 
  501     return ret;
  502 }
  503 
  504 /*
  505  *  Helper to sort the device_info structure
  506  */
  507 static int cmp_device_info(const void *a, const void *b)
  508 {
  509     const struct device_info *deva = a;
  510     const struct device_info *devb = b;
  511 
  512     if (deva->devid < devb->devid)
  513         return -1;
  514     if (deva->devid > devb->devid)
  515         return 1;
  516 
  517     return 0;
  518 }
  519 
  520 int dev_to_fsid(const char *dev, u8 *fsid)
  521 {
  522     struct btrfs_super_block *disk_super;
  523     char buf[BTRFS_SUPER_INFO_SIZE];
  524     int ret;
  525     int fd;
  526 
  527     fd = open(dev, O_RDONLY);
  528     if (fd < 0) {
  529         ret = -errno;
  530         return ret;
  531     }
  532 
  533     disk_super = (struct btrfs_super_block *)buf;
  534     ret = btrfs_read_dev_super(fd, disk_super,
  535                    BTRFS_SUPER_INFO_OFFSET, SBREAD_DEFAULT);
  536     if (ret)
  537         goto out;
  538 
  539     memcpy(fsid, disk_super->fsid, BTRFS_FSID_SIZE);
  540     ret = 0;
  541 
  542 out:
  543     close(fd);
  544     return ret;
  545 }
  546 
  547 /*
  548  *  This function loads the device_info structure and put them in an array
  549  */
  550 static int load_device_info(int fd, struct device_info **device_info_ptr,
  551                int *device_info_count)
  552 {
  553     int ret, i, ndevs;
  554     struct btrfs_ioctl_fs_info_args fi_args;
  555     struct btrfs_ioctl_dev_info_args dev_info;
  556     struct device_info *info;
  557     u8 fsid[BTRFS_UUID_SIZE];
  558 
  559     *device_info_count = 0;
  560     *device_info_ptr = NULL;
  561 
  562     ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
  563     if (ret < 0) {
  564         if (errno == EPERM)
  565             return -errno;
  566         error("cannot get filesystem info: %m");
  567         return 1;
  568     }
  569 
  570     info = calloc(fi_args.num_devices, sizeof(struct device_info));
  571     if (!info) {
  572         error("not enough memory");
  573         return 1;
  574     }
  575 
  576     for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) {
  577         if (ndevs >= fi_args.num_devices) {
  578             error("unexpected number of devices: %d >= %llu", ndevs,
  579                 (unsigned long long)fi_args.num_devices);
  580             error(
  581         "if seed device is used, try running this command as root");
  582             goto out;
  583         }
  584         memset(&dev_info, 0, sizeof(dev_info));
  585         ret = get_device_info(fd, i, &dev_info);
  586 
  587         if (ret == -ENODEV)
  588             continue;
  589         if (ret) {
  590             error("cannot get info about device devid=%d", i);
  591             goto out;
  592         }
  593 
  594         /*
  595          * Skip seed device by checking device's fsid (requires root).
  596          * And we will skip only if dev_to_fsid is successful and dev
  597          * is a seed device.
  598          * Ignore any other error including -EACCES, which is seen when
  599          * a non-root process calls dev_to_fsid(path)->open(path).
  600          */
  601         ret = dev_to_fsid((const char *)dev_info.path, fsid);
  602         if (!ret && memcmp(fi_args.fsid, fsid, BTRFS_FSID_SIZE) != 0)
  603             continue;
  604 
  605         info[ndevs].devid = dev_info.devid;
  606         if (!dev_info.path[0]) {
  607             strcpy(info[ndevs].path, "missing");
  608         } else {
  609             strcpy(info[ndevs].path, (char *)dev_info.path);
  610             info[ndevs].device_size =
  611                 get_partition_size((char *)dev_info.path);
  612         }
  613         info[ndevs].size = dev_info.total_bytes;
  614         ++ndevs;
  615     }
  616 
  617     if (ndevs != fi_args.num_devices) {
  618         error("unexpected number of devices: %d != %llu", ndevs,
  619                 (unsigned long long)fi_args.num_devices);
  620         goto out;
  621     }
  622 
  623     qsort(info, fi_args.num_devices,
  624         sizeof(struct device_info), cmp_device_info);
  625 
  626     *device_info_count = fi_args.num_devices;
  627     *device_info_ptr = info;
  628 
  629     return 0;
  630 
  631 out:
  632     free(info);
  633     return ret;
  634 }
  635 
  636 int load_chunk_and_device_info(int fd, struct chunk_info **chunkinfo,
  637         int *chunkcount, struct device_info **devinfo, int *devcount)
  638 {
  639     int ret;
  640 
  641     ret = load_chunk_info(fd, chunkinfo, chunkcount);
  642     if (ret == -EPERM) {
  643         warning(
  644 "cannot read detailed chunk info, per-device usage will not be shown, run as root");
  645     } else if (ret) {
  646         return ret;
  647     }
  648 
  649     ret = load_device_info(fd, devinfo, devcount);
  650     if (ret == -EPERM) {
  651         warning(
  652         "cannot get filesystem info from ioctl(FS_INFO), run as root");
  653         ret = 0;
  654     }
  655 
  656     return ret;
  657 }
  658 
  659 /*
  660  *  This function computes the size of a chunk in a disk
  661  */
  662 static u64 calc_chunk_size(struct chunk_info *ci)
  663 {
  664     if (ci->type & BTRFS_BLOCK_GROUP_RAID0)
  665         return ci->size / ci->num_stripes;
  666     else if (ci->type & BTRFS_BLOCK_GROUP_RAID1)
  667         return ci->size ;
  668     else if (ci->type & BTRFS_BLOCK_GROUP_RAID1C3)
  669         return ci->size;
  670     else if (ci->type & BTRFS_BLOCK_GROUP_RAID1C4)
  671         return ci->size;
  672     else if (ci->type & BTRFS_BLOCK_GROUP_DUP)
  673         return ci->size ;
  674     else if (ci->type & BTRFS_BLOCK_GROUP_RAID5)
  675         return ci->size / (ci->num_stripes -1);
  676     else if (ci->type & BTRFS_BLOCK_GROUP_RAID6)
  677         return ci->size / (ci->num_stripes -2);
  678     else if (ci->type & BTRFS_BLOCK_GROUP_RAID10)
  679         return ci->size / (ci->num_stripes / 2);
  680     return ci->size;
  681 }
  682 
  683 /*
  684  *  This function print the results of the command "btrfs fi usage"
  685  *  in tabular format
  686  */
  687 static void _cmd_filesystem_usage_tabular(unsigned unit_mode,
  688                     struct btrfs_ioctl_space_args *sargs,
  689                     struct chunk_info *chunks_info_ptr,
  690                     int chunks_info_count,
  691                     struct device_info *device_info_ptr,
  692                     int device_info_count)
  693 {
  694     int i;
  695     u64 total_unused = 0;
  696     struct string_table *matrix = NULL;
  697     int  ncols, nrows;
  698     int col;
  699     int unallocated_col;
  700     int spaceinfos_col;
  701     const int vhdr_skip = 3;    /* amount of vertical header space */
  702 
  703     /* id, path, unallocated */
  704     ncols = 3;
  705     spaceinfos_col = 2;
  706     /* Properly count the real space infos */
  707     for (i = 0; i < sargs->total_spaces; i++) {
  708         if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  709             continue;
  710         ncols++;
  711     }
  712 
  713     /* 2 for header, empty line, devices, ===, total, used */
  714     nrows = vhdr_skip + device_info_count + 1 + 2;
  715 
  716     matrix = table_create(ncols, nrows);
  717     if (!matrix) {
  718         error("not enough memory");
  719         return;
  720     }
  721 
  722     /*
  723      * We have to skip the global block reserve everywhere as it's an
  724      * artificial blockgroup
  725      */
  726 
  727     /* header */
  728     for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
  729         u64 flags = sargs->spaces[i].flags;
  730 
  731         if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  732             continue;
  733 
  734         table_printf(matrix, col, 0, "<%s",
  735                 btrfs_group_type_str(flags));
  736         table_printf(matrix, col, 1, "<%s",
  737                 btrfs_group_profile_str(flags));
  738         col++;
  739     }
  740     unallocated_col = col;
  741 
  742     table_printf(matrix, 0, 1, "<Id");
  743     table_printf(matrix, 1, 1, "<Path");
  744     table_printf(matrix, unallocated_col, 1, "<Unallocated");
  745 
  746     /* body */
  747     for (i = 0; i < device_info_count; i++) {
  748         int k;
  749         char *p;
  750 
  751         u64  total_allocated = 0, unused;
  752 
  753         p = strrchr(device_info_ptr[i].path, '/');
  754         if (!p)
  755             p = device_info_ptr[i].path;
  756         else
  757             p++;
  758 
  759         table_printf(matrix, 0, vhdr_skip + i, ">%llu",
  760                 device_info_ptr[i].devid);
  761         table_printf(matrix, 1, vhdr_skip + i, "<%s",
  762                 device_info_ptr[i].path);
  763 
  764         for (col = spaceinfos_col, k = 0; k < sargs->total_spaces; k++) {
  765             u64 flags = sargs->spaces[k].flags;
  766             u64 devid = device_info_ptr[i].devid;
  767             int j;
  768             u64 size = 0;
  769 
  770             if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  771                 continue;
  772 
  773             for (j = 0 ; j < chunks_info_count ; j++) {
  774                 if (chunks_info_ptr[j].type != flags )
  775                         continue;
  776                 if (chunks_info_ptr[j].devid != devid)
  777                         continue;
  778 
  779                 size += calc_chunk_size(chunks_info_ptr+j);
  780             }
  781 
  782             if (size)
  783                 table_printf(matrix, col, vhdr_skip+ i,
  784                     ">%s", pretty_size_mode(size, unit_mode));
  785             else
  786                 table_printf(matrix, col, vhdr_skip + i, ">-");
  787 
  788             total_allocated += size;
  789             col++;
  790         }
  791 
  792         unused = get_partition_size(device_info_ptr[i].path)
  793                 - total_allocated;
  794 
  795         table_printf(matrix, unallocated_col, vhdr_skip + i, ">%s",
  796             pretty_size_mode(unused, unit_mode | UNITS_NEGATIVE));
  797         total_unused += unused;
  798 
  799     }
  800 
  801     for (i = 0; i < spaceinfos_col; i++) {
  802         table_printf(matrix, i, vhdr_skip - 1, "*-");
  803         table_printf(matrix, i, vhdr_skip + device_info_count, "*-");
  804     }
  805 
  806     for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
  807         if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  808             continue;
  809 
  810         table_printf(matrix, col, vhdr_skip - 1, "*-");
  811         table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
  812         col++;
  813     }
  814     /* One for Unallocated */
  815     table_printf(matrix, col, vhdr_skip - 1, "*-");
  816     table_printf(matrix, col, vhdr_skip + device_info_count, "*-");
  817 
  818     /* footer */
  819     table_printf(matrix, 1, vhdr_skip + device_info_count + 1, "<Total");
  820     for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
  821         if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  822             continue;
  823 
  824         table_printf(matrix, col++, vhdr_skip + device_info_count + 1,
  825             ">%s",
  826             pretty_size_mode(sargs->spaces[i].total_bytes, unit_mode));
  827     }
  828 
  829     table_printf(matrix, unallocated_col, vhdr_skip + device_info_count + 1,
  830         ">%s",
  831         pretty_size_mode(total_unused, unit_mode | UNITS_NEGATIVE));
  832 
  833     table_printf(matrix, 1, vhdr_skip + device_info_count + 2, "<Used");
  834     for (i = 0, col = spaceinfos_col; i < sargs->total_spaces; i++) {
  835         if (sargs->spaces[i].flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  836             continue;
  837 
  838         table_printf(matrix, col++, vhdr_skip + device_info_count + 2,
  839             ">%s",
  840             pretty_size_mode(sargs->spaces[i].used_bytes, unit_mode));
  841     }
  842 
  843     table_dump(matrix);
  844     table_free(matrix);
  845 }
  846 
  847 /*
  848  *  This function prints the unused space per every disk
  849  */
  850 static void print_unused(struct chunk_info *info_ptr,
  851               int info_count,
  852               struct device_info *device_info_ptr,
  853               int device_info_count,
  854               unsigned unit_mode)
  855 {
  856     int i;
  857     for (i = 0; i < device_info_count; i++) {
  858         int j;
  859         u64 total = 0;
  860 
  861         for (j = 0; j < info_count; j++)
  862             if (info_ptr[j].devid == device_info_ptr[i].devid)
  863                 total += calc_chunk_size(info_ptr+j);
  864 
  865         printf("   %s\t%10s\n",
  866             device_info_ptr[i].path,
  867             pretty_size_mode(device_info_ptr[i].size - total,
  868                 unit_mode));
  869     }
  870 }
  871 
  872 /*
  873  *  This function prints the allocated chunk per every disk
  874  */
  875 static void print_chunk_device(u64 chunk_type,
  876                 struct chunk_info *chunks_info_ptr,
  877                 int chunks_info_count,
  878                 struct device_info *device_info_ptr,
  879                 int device_info_count,
  880                 unsigned unit_mode)
  881 {
  882     int i;
  883 
  884     for (i = 0; i < device_info_count; i++) {
  885         int j;
  886         u64 total = 0;
  887 
  888         for (j = 0; j < chunks_info_count; j++) {
  889 
  890             if (chunks_info_ptr[j].type != chunk_type)
  891                 continue;
  892             if (chunks_info_ptr[j].devid != device_info_ptr[i].devid)
  893                 continue;
  894 
  895             total += calc_chunk_size(&(chunks_info_ptr[j]));
  896             //total += chunks_info_ptr[j].size;
  897         }
  898 
  899         if (total > 0)
  900             printf("   %s\t%10s\n",
  901                 device_info_ptr[i].path,
  902                 pretty_size_mode(total, unit_mode));
  903     }
  904 }
  905 
  906 /*
  907  *  This function print the results of the command "btrfs fi usage"
  908  *  in linear format
  909  */
  910 static void _cmd_filesystem_usage_linear(unsigned unit_mode,
  911                     struct btrfs_ioctl_space_args *sargs,
  912                     struct chunk_info *info_ptr,
  913                     int info_count,
  914                     struct device_info *device_info_ptr,
  915                     int device_info_count)
  916 {
  917     int i;
  918 
  919     for (i = 0; i < sargs->total_spaces; i++) {
  920         const char *description;
  921         const char *r_mode;
  922         u64 flags = sargs->spaces[i].flags;
  923 
  924         if (flags & BTRFS_SPACE_INFO_GLOBAL_RSV)
  925             continue;
  926 
  927         description = btrfs_group_type_str(flags);
  928         r_mode = btrfs_group_profile_str(flags);
  929 
  930         printf("%s,%s: Size:%s, ",
  931             description,
  932             r_mode,
  933             pretty_size_mode(sargs->spaces[i].total_bytes,
  934                 unit_mode));
  935         printf("Used:%s (%.2f%%)\n",
  936             pretty_size_mode(sargs->spaces[i].used_bytes, unit_mode),
  937             100.0f * sargs->spaces[i].used_bytes /
  938             (sargs->spaces[i].total_bytes + 1));
  939         print_chunk_device(flags, info_ptr, info_count,
  940                 device_info_ptr, device_info_count, unit_mode);
  941         printf("\n");
  942     }
  943 
  944     if (info_count) {
  945         printf("Unallocated:\n");
  946         print_unused(info_ptr, info_count, device_info_ptr,
  947                 device_info_count, unit_mode | UNITS_NEGATIVE);
  948     }
  949 }
  950 
  951 static int print_filesystem_usage_by_chunk(int fd,
  952         struct chunk_info *chunkinfo, int chunkcount,
  953         struct device_info *devinfo, int devcount,
  954         const char *path, unsigned unit_mode, int tabular)
  955 {
  956     struct btrfs_ioctl_space_args *sargs;
  957     int ret = 0;
  958 
  959     sargs = load_space_info(fd, path);
  960     if (!sargs) {
  961         ret = 1;
  962         goto out;
  963     }
  964 
  965     if (tabular)
  966         _cmd_filesystem_usage_tabular(unit_mode, sargs, chunkinfo,
  967                 chunkcount, devinfo, devcount);
  968     else
  969         _cmd_filesystem_usage_linear(unit_mode, sargs, chunkinfo,
  970                 chunkcount, devinfo, devcount);
  971 
  972     free(sargs);
  973 out:
  974     return ret;
  975 }
  976 
  977 static const char * const cmd_filesystem_usage_usage[] = {
  978     "btrfs filesystem usage [options] <path> [<path>..]",
  979     "Show detailed information about internal filesystem usage .",
  980     "",
  981     HELPINFO_UNITS_SHORT_LONG,
  982     "-T                 show data in tabular format",
  983     NULL
  984 };
  985 
  986 static int cmd_filesystem_usage(const struct cmd_struct *cmd,
  987                 int argc, char **argv)
  988 {
  989     int ret = 0;
  990     unsigned unit_mode;
  991     int i;
  992     int more_than_one = 0;
  993     int tabular = 0;
  994 
  995     unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
  996 
  997     optind = 0;
  998     while (1) {
  999         int c;
 1000 
 1001         c = getopt(argc, argv, "T");
 1002         if (c < 0)
 1003             break;
 1004 
 1005         switch (c) {
 1006         case 'T':
 1007             tabular = 1;
 1008             break;
 1009         default:
 1010             usage_unknown_option(cmd, argv);
 1011         }
 1012     }
 1013 
 1014     if (check_argc_min(argc - optind, 1))
 1015         return 1;
 1016 
 1017     for (i = optind; i < argc; i++) {
 1018         int fd;
 1019         DIR *dirstream = NULL;
 1020         struct chunk_info *chunkinfo = NULL;
 1021         struct device_info *devinfo = NULL;
 1022         int chunkcount = 0;
 1023         int devcount = 0;
 1024 
 1025         fd = btrfs_open_dir(argv[i], &dirstream, 1);
 1026         if (fd < 0) {
 1027             ret = 1;
 1028             goto out;
 1029         }
 1030         if (more_than_one)
 1031             printf("\n");
 1032 
 1033         ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount,
 1034                 &devinfo, &devcount);
 1035         if (ret)
 1036             goto cleanup;
 1037 
 1038         ret = print_filesystem_usage_overall(fd, chunkinfo, chunkcount,
 1039                 devinfo, devcount, argv[i], unit_mode);
 1040         if (ret)
 1041             goto cleanup;
 1042         printf("\n");
 1043         ret = print_filesystem_usage_by_chunk(fd, chunkinfo, chunkcount,
 1044                 devinfo, devcount, argv[i], unit_mode, tabular);
 1045 cleanup:
 1046         close_file_or_dir(fd, dirstream);
 1047         free(chunkinfo);
 1048         free(devinfo);
 1049 
 1050         if (ret)
 1051             goto out;
 1052         more_than_one = 1;
 1053     }
 1054 
 1055 out:
 1056     return !!ret;
 1057 }
 1058 DEFINE_SIMPLE_COMMAND(filesystem_usage, "usage");
 1059 
 1060 void print_device_chunks(struct device_info *devinfo,
 1061         struct chunk_info *chunks_info_ptr,
 1062         int chunks_info_count, unsigned unit_mode)
 1063 {
 1064     int i;
 1065     u64 allocated = 0;
 1066 
 1067     for (i = 0 ; i < chunks_info_count ; i++) {
 1068         const char *description;
 1069         const char *r_mode;
 1070         u64 flags;
 1071         u64 size;
 1072 
 1073         if (chunks_info_ptr[i].devid != devinfo->devid)
 1074             continue;
 1075 
 1076         flags = chunks_info_ptr[i].type;
 1077 
 1078         description = btrfs_group_type_str(flags);
 1079         r_mode = btrfs_group_profile_str(flags);
 1080         size = calc_chunk_size(chunks_info_ptr+i);
 1081         printf("   %s,%s:%*s%10s\n",
 1082             description,
 1083             r_mode,
 1084             (int)(20 - strlen(description) - strlen(r_mode)), "",
 1085             pretty_size_mode(size, unit_mode));
 1086 
 1087         allocated += size;
 1088 
 1089     }
 1090     printf("   Unallocated: %*s%10s\n",
 1091         (int)(20 - strlen("Unallocated")), "",
 1092         pretty_size_mode(devinfo->size - allocated,
 1093             unit_mode | UNITS_NEGATIVE));
 1094 }
 1095 
 1096 void print_device_sizes(struct device_info *devinfo, unsigned unit_mode)
 1097 {
 1098     printf("   Device size: %*s%10s\n",
 1099         (int)(20 - strlen("Device size")), "",
 1100         pretty_size_mode(devinfo->device_size, unit_mode));
 1101     printf("   Device slack: %*s%10s\n",
 1102         (int)(20 - strlen("Device slack")), "",
 1103         pretty_size_mode(devinfo->device_size > 0 ?
 1104             devinfo->device_size - devinfo->size : 0,
 1105             unit_mode));
 1106 }