"Fossies" - the Fresh Open Source Software Archive

Member "btrfs-progs-v5.4/cmds/filesystem.c" (3 Dec 2019, 29605 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.c" see the Fossies "Dox" file reference documentation.

    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 <uuid/uuid.h>
   24 #include <ctype.h>
   25 #include <fcntl.h>
   26 #include <ftw.h>
   27 #include <mntent.h>
   28 #include <linux/limits.h>
   29 #include <linux/version.h>
   30 #include <getopt.h>
   31 
   32 #include <btrfsutil.h>
   33 
   34 #include "kerncompat.h"
   35 #include "ctree.h"
   36 #include "common/utils.h"
   37 #include "volumes.h"
   38 #include "cmds/commands.h"
   39 #include "cmds/filesystem-usage.h"
   40 #include "kernel-lib/list_sort.h"
   41 #include "disk-io.h"
   42 #include "common/help.h"
   43 #include "common/fsfeatures.h"
   44 #include "common/path-utils.h"
   45 #include "common/device-scan.h"
   46 
   47 /*
   48  * for btrfs fi show, we maintain a hash of fsids we've already printed.
   49  * This way we don't print dups if a given FS is mounted more than once.
   50  */
   51 static struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = {NULL,};
   52 static mode_t defrag_open_mode = O_RDONLY;
   53 
   54 static const char * const filesystem_cmd_group_usage[] = {
   55     "btrfs filesystem [<group>] <command> [<args>]",
   56     NULL
   57 };
   58 
   59 static const char * const cmd_filesystem_df_usage[] = {
   60     "btrfs filesystem df [options] <path>",
   61     "Show space usage information for a mount point",
   62     "",
   63     HELPINFO_UNITS_SHORT_LONG,
   64     NULL
   65 };
   66 
   67 static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
   68 {
   69     u64 i;
   70     struct btrfs_ioctl_space_info *sp = sargs->spaces;
   71 
   72     for (i = 0; i < sargs->total_spaces; i++, sp++) {
   73         printf("%s, %s: total=%s, used=%s\n",
   74             btrfs_group_type_str(sp->flags),
   75             btrfs_group_profile_str(sp->flags),
   76             pretty_size_mode(sp->total_bytes, unit_mode),
   77             pretty_size_mode(sp->used_bytes, unit_mode));
   78     }
   79 }
   80 
   81 static int cmd_filesystem_df(const struct cmd_struct *cmd,
   82                  int argc, char **argv)
   83 {
   84     struct btrfs_ioctl_space_args *sargs = NULL;
   85     int ret;
   86     int fd;
   87     char *path;
   88     DIR *dirstream = NULL;
   89     unsigned unit_mode;
   90 
   91     unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
   92 
   93     clean_args_no_options(cmd, argc, argv);
   94 
   95     if (check_argc_exact(argc - optind, 1))
   96         return 1;
   97 
   98     path = argv[optind];
   99 
  100     fd = btrfs_open_dir(path, &dirstream, 1);
  101     if (fd < 0)
  102         return 1;
  103 
  104     ret = get_df(fd, &sargs);
  105 
  106     if (ret == 0) {
  107         print_df(sargs, unit_mode);
  108         free(sargs);
  109     } else {
  110         errno = -ret;
  111         error("get_df failed: %m");
  112     }
  113 
  114     close_file_or_dir(fd, dirstream);
  115     return !!ret;
  116 }
  117 static DEFINE_SIMPLE_COMMAND(filesystem_df, "df");
  118 
  119 static int match_search_item_kernel(u8 *fsid, char *mnt, char *label,
  120                     char *search)
  121 {
  122     char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
  123     int search_len = strlen(search);
  124 
  125     search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE);
  126     uuid_unparse(fsid, uuidbuf);
  127     if (!strncmp(uuidbuf, search, search_len))
  128         return 1;
  129 
  130     if (*label && strcmp(label, search) == 0)
  131         return 1;
  132 
  133     if (strcmp(mnt, search) == 0)
  134         return 1;
  135 
  136     return 0;
  137 }
  138 
  139 /* Search for user visible uuid 'search' in registered filesystems */
  140 static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search)
  141 {
  142     char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
  143     struct btrfs_device *device;
  144     int search_len = strlen(search);
  145 
  146     search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE);
  147     uuid_unparse(fs_devices->fsid, uuidbuf);
  148     if (!strncmp(uuidbuf, search, search_len))
  149         return 1;
  150 
  151     list_for_each_entry(device, &fs_devices->devices, dev_list) {
  152         if ((device->label && strcmp(device->label, search) == 0) ||
  153             strcmp(device->name, search) == 0)
  154             return 1;
  155     }
  156     return 0;
  157 }
  158 
  159 /*
  160  * Sort devices by devid, ascending
  161  */
  162 static int cmp_device_id(void *priv, struct list_head *a,
  163         struct list_head *b)
  164 {
  165     const struct btrfs_device *da = list_entry(a, struct btrfs_device,
  166             dev_list);
  167     const struct btrfs_device *db = list_entry(b, struct btrfs_device,
  168             dev_list);
  169 
  170     return da->devid < db->devid ? -1 :
  171         da->devid > db->devid ? 1 : 0;
  172 }
  173 
  174 static void splice_device_list(struct list_head *seed_devices,
  175                    struct list_head *all_devices)
  176 {
  177     struct btrfs_device *in_all, *next_all;
  178     struct btrfs_device *in_seed, *next_seed;
  179 
  180     list_for_each_entry_safe(in_all, next_all, all_devices, dev_list) {
  181         list_for_each_entry_safe(in_seed, next_seed, seed_devices,
  182                                 dev_list) {
  183             if (in_all->devid == in_seed->devid) {
  184                 /*
  185                  * When do dev replace in a sprout fs
  186                  * to a dev in its seed fs, the replacing
  187                  * dev will reside in the sprout fs and
  188                  * the replaced dev will still exist
  189                  * in the seed fs.
  190                  * So pick the latest one when showing
  191                  * the sprout fs.
  192                  */
  193                 if (in_all->generation
  194                         < in_seed->generation) {
  195                     list_del(&in_all->dev_list);
  196                     free(in_all);
  197                 } else if (in_all->generation
  198                         > in_seed->generation) {
  199                     list_del(&in_seed->dev_list);
  200                     free(in_seed);
  201                 }
  202                 break;
  203             }
  204         }
  205     }
  206 
  207     list_splice(seed_devices, all_devices);
  208 }
  209 
  210 static void print_devices(struct btrfs_fs_devices *fs_devices,
  211               u64 *devs_found, unsigned unit_mode)
  212 {
  213     struct btrfs_device *device;
  214     struct btrfs_fs_devices *cur_fs;
  215     struct list_head *all_devices;
  216 
  217     all_devices = &fs_devices->devices;
  218     cur_fs = fs_devices->seed;
  219     /* add all devices of seed fs to the fs to be printed */
  220     while (cur_fs) {
  221         splice_device_list(&cur_fs->devices, all_devices);
  222         cur_fs = cur_fs->seed;
  223     }
  224 
  225     list_sort(NULL, all_devices, cmp_device_id);
  226     list_for_each_entry(device, all_devices, dev_list) {
  227         printf("\tdevid %4llu size %s used %s path %s\n",
  228                (unsigned long long)device->devid,
  229                pretty_size_mode(device->total_bytes, unit_mode),
  230                pretty_size_mode(device->bytes_used, unit_mode),
  231                device->name);
  232 
  233         (*devs_found)++;
  234     }
  235 }
  236 
  237 static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
  238                unsigned unit_mode)
  239 {
  240     char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
  241     struct btrfs_device *device;
  242     u64 devs_found = 0;
  243     u64 total;
  244 
  245     if (add_seen_fsid(fs_devices->fsid, seen_fsid_hash, -1, NULL))
  246         return;
  247 
  248     uuid_unparse(fs_devices->fsid, uuidbuf);
  249     device = list_entry(fs_devices->devices.next, struct btrfs_device,
  250                 dev_list);
  251     if (device->label && device->label[0])
  252         printf("Label: '%s' ", device->label);
  253     else
  254         printf("Label: none ");
  255 
  256     total = device->total_devs;
  257     printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
  258            (unsigned long long)total,
  259            pretty_size_mode(device->super_bytes_used, unit_mode));
  260 
  261     print_devices(fs_devices, &devs_found, unit_mode);
  262 
  263     if (devs_found < total) {
  264         printf("\t*** Some devices missing\n");
  265     }
  266     printf("\n");
  267 }
  268 
  269 /* adds up all the used spaces as reported by the space info ioctl
  270  */
  271 static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
  272 {
  273     u64 ret = 0;
  274     int i;
  275     for (i = 0; i < si->total_spaces; i++)
  276         ret += si->spaces[i].used_bytes;
  277     return ret;
  278 }
  279 
  280 static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
  281         struct btrfs_ioctl_dev_info_args *dev_info,
  282         struct btrfs_ioctl_space_args *space_info,
  283         char *label, unsigned unit_mode)
  284 {
  285     int i;
  286     int fd;
  287     int missing = 0;
  288     char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
  289     struct btrfs_ioctl_dev_info_args *tmp_dev_info;
  290     int ret;
  291 
  292     ret = add_seen_fsid(fs_info->fsid, seen_fsid_hash, -1, NULL);
  293     if (ret == -EEXIST)
  294         return 0;
  295     else if (ret)
  296         return ret;
  297 
  298     uuid_unparse(fs_info->fsid, uuidbuf);
  299     if (label && *label)
  300         printf("Label: '%s' ", label);
  301     else
  302         printf("Label: none ");
  303 
  304     printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
  305             fs_info->num_devices,
  306             pretty_size_mode(calc_used_bytes(space_info),
  307                      unit_mode));
  308 
  309     for (i = 0; i < fs_info->num_devices; i++) {
  310         char *canonical_path;
  311 
  312         tmp_dev_info = (struct btrfs_ioctl_dev_info_args *)&dev_info[i];
  313 
  314         /* Add check for missing devices even mounted */
  315         fd = open((char *)tmp_dev_info->path, O_RDONLY);
  316         if (fd < 0) {
  317             missing = 1;
  318             continue;
  319         }
  320         close(fd);
  321         canonical_path = canonicalize_path((char *)tmp_dev_info->path);
  322         printf("\tdevid %4llu size %s used %s path %s\n",
  323             tmp_dev_info->devid,
  324             pretty_size_mode(tmp_dev_info->total_bytes, unit_mode),
  325             pretty_size_mode(tmp_dev_info->bytes_used, unit_mode),
  326             canonical_path);
  327 
  328         free(canonical_path);
  329     }
  330 
  331     if (missing)
  332         printf("\t*** Some devices missing\n");
  333     printf("\n");
  334     return 0;
  335 }
  336 
  337 static int btrfs_scan_kernel(void *search, unsigned unit_mode)
  338 {
  339     int ret = 0, fd;
  340     int found = 0;
  341     FILE *f;
  342     struct mntent *mnt;
  343     struct btrfs_ioctl_fs_info_args fs_info_arg;
  344     struct btrfs_ioctl_dev_info_args *dev_info_arg = NULL;
  345     struct btrfs_ioctl_space_args *space_info_arg = NULL;
  346     char label[BTRFS_LABEL_SIZE];
  347 
  348     f = setmntent("/proc/self/mounts", "r");
  349     if (f == NULL)
  350         return 1;
  351 
  352     memset(label, 0, sizeof(label));
  353     while ((mnt = getmntent(f)) != NULL) {
  354         free(dev_info_arg);
  355         dev_info_arg = NULL;
  356         if (strcmp(mnt->mnt_type, "btrfs"))
  357             continue;
  358         ret = get_fs_info(mnt->mnt_dir, &fs_info_arg,
  359                 &dev_info_arg);
  360         if (ret)
  361             goto out;
  362 
  363         /* skip all fs already shown as mounted fs */
  364         if (is_seen_fsid(fs_info_arg.fsid, seen_fsid_hash))
  365             continue;
  366 
  367         ret = get_label_mounted(mnt->mnt_dir, label);
  368         /* provide backward kernel compatibility */
  369         if (ret == -ENOTTY)
  370             ret = get_label_unmounted(
  371                 (const char *)dev_info_arg->path, label);
  372 
  373         if (ret)
  374             goto out;
  375 
  376         if (search && !match_search_item_kernel(fs_info_arg.fsid,
  377                     mnt->mnt_dir, label, search)) {
  378             continue;
  379         }
  380 
  381         fd = open(mnt->mnt_dir, O_RDONLY);
  382         if ((fd != -1) && !get_df(fd, &space_info_arg)) {
  383             print_one_fs(&fs_info_arg, dev_info_arg,
  384                      space_info_arg, label, unit_mode);
  385             free(space_info_arg);
  386             memset(label, 0, sizeof(label));
  387             found = 1;
  388         }
  389         if (fd != -1)
  390             close(fd);
  391     }
  392 
  393 out:
  394     free(dev_info_arg);
  395     endmntent(f);
  396     return !found;
  397 }
  398 
  399 static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
  400 {
  401     struct btrfs_fs_devices *cur_seed, *next_seed;
  402     struct btrfs_device *device;
  403 
  404     while (!list_empty(&fs_devices->devices)) {
  405         device = list_entry(fs_devices->devices.next,
  406                     struct btrfs_device, dev_list);
  407         list_del(&device->dev_list);
  408 
  409         free(device->name);
  410         free(device->label);
  411         free(device);
  412     }
  413 
  414     /* free seed fs chain */
  415     cur_seed = fs_devices->seed;
  416     fs_devices->seed = NULL;
  417     while (cur_seed) {
  418         next_seed = cur_seed->seed;
  419         free(cur_seed);
  420 
  421         cur_seed = next_seed;
  422     }
  423 
  424     list_del(&fs_devices->list);
  425     free(fs_devices);
  426 }
  427 
  428 static int copy_device(struct btrfs_device *dst,
  429                struct btrfs_device *src)
  430 {
  431     dst->devid = src->devid;
  432     memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE);
  433     if (src->name == NULL)
  434         dst->name = NULL;
  435     else {
  436         dst->name = strdup(src->name);
  437         if (!dst->name)
  438             return -ENOMEM;
  439     }
  440     if (src->label == NULL)
  441         dst->label = NULL;
  442     else {
  443         dst->label = strdup(src->label);
  444         if (!dst->label) {
  445             free(dst->name);
  446             return -ENOMEM;
  447         }
  448     }
  449     dst->total_devs = src->total_devs;
  450     dst->super_bytes_used = src->super_bytes_used;
  451     dst->total_bytes = src->total_bytes;
  452     dst->bytes_used = src->bytes_used;
  453     dst->generation = src->generation;
  454 
  455     return 0;
  456 }
  457 
  458 static int copy_fs_devices(struct btrfs_fs_devices *dst,
  459                struct btrfs_fs_devices *src)
  460 {
  461     struct btrfs_device *cur_dev, *dev_copy;
  462     int ret = 0;
  463 
  464     memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE);
  465     memcpy(dst->metadata_uuid, src->metadata_uuid, BTRFS_FSID_SIZE);
  466     INIT_LIST_HEAD(&dst->devices);
  467     dst->seed = NULL;
  468 
  469     list_for_each_entry(cur_dev, &src->devices, dev_list) {
  470         dev_copy = malloc(sizeof(*dev_copy));
  471         if (!dev_copy) {
  472             ret = -ENOMEM;
  473             break;
  474         }
  475 
  476         ret = copy_device(dev_copy, cur_dev);
  477         if (ret) {
  478             free(dev_copy);
  479             break;
  480         }
  481 
  482         list_add(&dev_copy->dev_list, &dst->devices);
  483         dev_copy->fs_devices = dst;
  484     }
  485 
  486     return ret;
  487 }
  488 
  489 static int find_and_copy_seed(struct btrfs_fs_devices *seed,
  490                   struct btrfs_fs_devices *copy,
  491                   struct list_head *fs_uuids) {
  492     struct btrfs_fs_devices *cur_fs;
  493 
  494     list_for_each_entry(cur_fs, fs_uuids, list)
  495         if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE))
  496             return copy_fs_devices(copy, cur_fs);
  497 
  498     return 1;
  499 }
  500 
  501 static int has_seed_devices(struct btrfs_fs_devices *fs_devices)
  502 {
  503     struct btrfs_device *device;
  504     int dev_cnt_total, dev_cnt = 0;
  505 
  506     device = list_first_entry(&fs_devices->devices, struct btrfs_device,
  507                   dev_list);
  508 
  509     dev_cnt_total = device->total_devs;
  510 
  511     list_for_each_entry(device, &fs_devices->devices, dev_list)
  512         dev_cnt++;
  513 
  514     return dev_cnt_total != dev_cnt;
  515 }
  516 
  517 static int search_umounted_fs_uuids(struct list_head *all_uuids,
  518                     char *search, int *found)
  519 {
  520     struct btrfs_fs_devices *cur_fs, *fs_copy;
  521     struct list_head *fs_uuids;
  522     int ret = 0;
  523 
  524     fs_uuids = btrfs_scanned_uuids();
  525 
  526     /*
  527      * The fs_uuids list is global, and open_ctree_* will
  528      * modify it, make a private copy here
  529      */
  530     list_for_each_entry(cur_fs, fs_uuids, list) {
  531         /* don't bother handle all fs, if search target specified */
  532         if (search) {
  533             if (uuid_search(cur_fs, search) == 0)
  534                 continue;
  535             if (found)
  536                 *found = 1;
  537         }
  538 
  539         /* skip all fs already shown as mounted fs */
  540         if (is_seen_fsid(cur_fs->fsid, seen_fsid_hash))
  541             continue;
  542 
  543         fs_copy = calloc(1, sizeof(*fs_copy));
  544         if (!fs_copy) {
  545             ret = -ENOMEM;
  546             goto out;
  547         }
  548 
  549         ret = copy_fs_devices(fs_copy, cur_fs);
  550         if (ret) {
  551             free(fs_copy);
  552             goto out;
  553         }
  554 
  555         list_add(&fs_copy->list, all_uuids);
  556     }
  557 
  558 out:
  559     return ret;
  560 }
  561 
  562 static int map_seed_devices(struct list_head *all_uuids)
  563 {
  564     struct btrfs_fs_devices *cur_fs, *cur_seed;
  565     struct btrfs_fs_devices *seed_copy;
  566     struct btrfs_fs_devices *opened_fs;
  567     struct btrfs_device *device;
  568     struct btrfs_fs_info *fs_info;
  569     struct list_head *fs_uuids;
  570     int ret = 0;
  571 
  572     fs_uuids = btrfs_scanned_uuids();
  573 
  574     list_for_each_entry(cur_fs, all_uuids, list) {
  575         device = list_first_entry(&cur_fs->devices,
  576                         struct btrfs_device, dev_list);
  577         if (!device)
  578             continue;
  579 
  580         /* skip fs without seeds */
  581         if (!has_seed_devices(cur_fs))
  582             continue;
  583 
  584         /*
  585          * open_ctree_* detects seed/sprout mapping
  586          */
  587         fs_info = open_ctree_fs_info(device->name, 0, 0, 0,
  588                         OPEN_CTREE_PARTIAL);
  589         if (!fs_info)
  590             continue;
  591 
  592         /*
  593          * copy the seed chain under the opened fs
  594          */
  595         opened_fs = fs_info->fs_devices;
  596         cur_seed = cur_fs;
  597         while (opened_fs->seed) {
  598             seed_copy = malloc(sizeof(*seed_copy));
  599             if (!seed_copy) {
  600                 ret = -ENOMEM;
  601                 goto fail_out;
  602             }
  603             ret = find_and_copy_seed(opened_fs->seed, seed_copy,
  604                          fs_uuids);
  605             if (ret) {
  606                 free(seed_copy);
  607                 goto fail_out;
  608             }
  609 
  610             cur_seed->seed = seed_copy;
  611 
  612             opened_fs = opened_fs->seed;
  613             cur_seed = cur_seed->seed;
  614         }
  615 
  616         close_ctree(fs_info->chunk_root);
  617     }
  618 
  619 out:
  620     return ret;
  621 fail_out:
  622     close_ctree(fs_info->chunk_root);
  623     goto out;
  624 }
  625 
  626 static const char * const cmd_filesystem_show_usage[] = {
  627     "btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
  628     "Show the structure of a filesystem",
  629     "",
  630     "-d|--all-devices   show only disks under /dev containing btrfs filesystem",
  631     "-m|--mounted       show only mounted btrfs",
  632     HELPINFO_UNITS_LONG,
  633     "If no argument is given, structure of all present filesystems is shown.",
  634     NULL
  635 };
  636 
  637 static int cmd_filesystem_show(const struct cmd_struct *cmd,
  638                    int argc, char **argv)
  639 {
  640     LIST_HEAD(all_uuids);
  641     struct btrfs_fs_devices *fs_devices;
  642     char *search = NULL;
  643     int ret;
  644     /* default, search both kernel and udev */
  645     int where = -1;
  646     int type = 0;
  647     char mp[PATH_MAX];
  648     char path[PATH_MAX];
  649     u8 fsid[BTRFS_FSID_SIZE];
  650     char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
  651     unsigned unit_mode;
  652     int found = 0;
  653 
  654     unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
  655 
  656     optind = 0;
  657     while (1) {
  658         int c;
  659         static const struct option long_options[] = {
  660             { "all-devices", no_argument, NULL, 'd'},
  661             { "mounted", no_argument, NULL, 'm'},
  662             { NULL, 0, NULL, 0 }
  663         };
  664 
  665         c = getopt_long(argc, argv, "dm", long_options, NULL);
  666         if (c < 0)
  667             break;
  668         switch (c) {
  669         case 'd':
  670             where = BTRFS_SCAN_LBLKID;
  671             break;
  672         case 'm':
  673             where = BTRFS_SCAN_MOUNTED;
  674             break;
  675         default:
  676             usage_unknown_option(cmd, argv);
  677         }
  678     }
  679 
  680     if (check_argc_max(argc, optind + 1))
  681         return 1;
  682 
  683     if (argc > optind) {
  684         search = argv[optind];
  685         if (*search == 0)
  686             usage(cmd);
  687         type = check_arg_type(search);
  688 
  689         /*
  690          * For search is a device:
  691          *     realpath do /dev/mapper/XX => /dev/dm-X
  692          *     which is required by BTRFS_SCAN_DEV
  693          * For search is a mountpoint:
  694          *     realpath do  /mnt/btrfs/  => /mnt/btrfs
  695          *     which shall be recognized by btrfs_scan_kernel()
  696          */
  697         if (realpath(search, path))
  698             search = path;
  699 
  700         /*
  701          * Needs special handling if input arg is block dev And if
  702          * input arg is mount-point just print it right away
  703          */
  704         if (type == BTRFS_ARG_BLKDEV && where != BTRFS_SCAN_LBLKID) {
  705             ret = get_btrfs_mount(search, mp, sizeof(mp));
  706             if (!ret) {
  707                 /* given block dev is mounted */
  708                 search = mp;
  709                 type = BTRFS_ARG_MNTPOINT;
  710             } else {
  711                 ret = dev_to_fsid(search, fsid);
  712                 if (ret) {
  713                     error("no btrfs on %s", search);
  714                     return 1;
  715                 }
  716                 uuid_unparse(fsid, uuid_buf);
  717                 search = uuid_buf;
  718                 type = BTRFS_ARG_UUID;
  719                 goto devs_only;
  720             }
  721         }
  722     }
  723 
  724     if (where == BTRFS_SCAN_LBLKID)
  725         goto devs_only;
  726 
  727     /* show mounted btrfs */
  728     ret = btrfs_scan_kernel(search, unit_mode);
  729     if (search && !ret) {
  730         /* since search is found we are done */
  731         goto out;
  732     }
  733 
  734     /* shows mounted only */
  735     if (where == BTRFS_SCAN_MOUNTED)
  736         goto out;
  737 
  738 devs_only:
  739     if (type == BTRFS_ARG_REG) {
  740         /*
  741          * We don't close the fs_info because it will free the device,
  742          * this is not a long-running process so it's fine
  743          */
  744         if (open_ctree(search, btrfs_sb_offset(0), 0))
  745             ret = 0;
  746         else
  747             ret = 1;
  748     } else {
  749         ret = btrfs_scan_devices();
  750     }
  751 
  752     if (ret) {
  753         error("blkid device scan returned %d", ret);
  754         return 1;
  755     }
  756 
  757     ret = search_umounted_fs_uuids(&all_uuids, search, &found);
  758     if (ret < 0) {
  759         error("searching target device returned error %d", ret);
  760         return 1;
  761     }
  762 
  763     /*
  764      * The seed/sprout mapping are not detected yet,
  765      * do mapping build for all umounted fs
  766      */
  767     ret = map_seed_devices(&all_uuids);
  768     if (ret) {
  769         error("mapping seed devices returned error %d", ret);
  770         return 1;
  771     }
  772 
  773     list_for_each_entry(fs_devices, &all_uuids, list)
  774         print_one_uuid(fs_devices, unit_mode);
  775 
  776     if (search && !found) {
  777         error("not a valid btrfs filesystem: %s", search);
  778         ret = 1;
  779     }
  780     while (!list_empty(&all_uuids)) {
  781         fs_devices = list_entry(all_uuids.next,
  782                     struct btrfs_fs_devices, list);
  783         free_fs_devices(fs_devices);
  784     }
  785 out:
  786     free_seen_fsid(seen_fsid_hash);
  787     return ret;
  788 }
  789 static DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
  790 
  791 static const char * const cmd_filesystem_sync_usage[] = {
  792     "btrfs filesystem sync <path>",
  793     "Force a sync on a filesystem",
  794     NULL
  795 };
  796 
  797 static int cmd_filesystem_sync(const struct cmd_struct *cmd,
  798                    int argc, char **argv)
  799 {
  800     enum btrfs_util_error err;
  801 
  802     clean_args_no_options(cmd, argc, argv);
  803 
  804     if (check_argc_exact(argc - optind, 1))
  805         return 1;
  806 
  807     err = btrfs_util_sync(argv[optind]);
  808     if (err) {
  809         error_btrfs_util(err);
  810         return 1;
  811     }
  812 
  813     return 0;
  814 }
  815 static DEFINE_SIMPLE_COMMAND(filesystem_sync, "sync");
  816 
  817 static int parse_compress_type(char *s)
  818 {
  819     if (strcmp(optarg, "zlib") == 0)
  820         return BTRFS_COMPRESS_ZLIB;
  821     else if (strcmp(optarg, "lzo") == 0)
  822         return BTRFS_COMPRESS_LZO;
  823     else if (strcmp(optarg, "zstd") == 0)
  824         return BTRFS_COMPRESS_ZSTD;
  825     else {
  826         error("unknown compression type %s", s);
  827         exit(1);
  828     };
  829 }
  830 
  831 static const char * const cmd_filesystem_defrag_usage[] = {
  832     "btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
  833     "Defragment a file or a directory",
  834     "",
  835     "-v                  be verbose",
  836     "-r                  defragment files recursively",
  837     "-c[zlib,lzo,zstd]   compress the file while defragmenting",
  838     "-f                  flush data to disk immediately after defragmenting",
  839     "-s start            defragment only from byte onward",
  840     "-l len              defragment only up to len bytes",
  841     "-t size             target extent size hint (default: 32M)",
  842     "",
  843     "Warning: most Linux kernels will break up the ref-links of COW data",
  844     "(e.g., files copied with 'cp --reflink', snapshots) which may cause",
  845     "considerable increase of space usage. See btrfs-filesystem(8) for",
  846     "more information.",
  847     NULL
  848 };
  849 
  850 static struct btrfs_ioctl_defrag_range_args defrag_global_range;
  851 static int defrag_global_verbose;
  852 static int defrag_global_errors;
  853 static int defrag_callback(const char *fpath, const struct stat *sb,
  854         int typeflag, struct FTW *ftwbuf)
  855 {
  856     int ret = 0;
  857     int fd = 0;
  858 
  859     if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) {
  860         if (defrag_global_verbose)
  861             printf("%s\n", fpath);
  862         fd = open(fpath, defrag_open_mode);
  863         if (fd < 0) {
  864             goto error;
  865         }
  866         ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
  867         close(fd);
  868         if (ret && errno == ENOTTY) {
  869             error(
  870 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
  871             defrag_global_errors++;
  872             return ENOTTY;
  873         }
  874         if (ret) {
  875             goto error;
  876         }
  877     }
  878     return 0;
  879 
  880 error:
  881     error("defrag failed on %s: %m", fpath);
  882     defrag_global_errors++;
  883     return 0;
  884 }
  885 
  886 static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
  887                  int argc, char **argv)
  888 {
  889     int fd;
  890     int flush = 0;
  891     u64 start = 0;
  892     u64 len = (u64)-1;
  893     u64 thresh;
  894     int i;
  895     int recursive = 0;
  896     int ret = 0;
  897     int compress_type = BTRFS_COMPRESS_NONE;
  898     DIR *dirstream;
  899 
  900     /*
  901      * Kernel 4.19+ supports defragmention of files open read-only,
  902      * otherwise it's an ETXTBSY error
  903      */
  904     if (get_running_kernel_version() < KERNEL_VERSION(4,19,0))
  905         defrag_open_mode = O_RDWR;
  906 
  907     /*
  908      * Kernel has a different default (256K) that is supposed to be safe,
  909      * but it does not defragment very well. The 32M will likely lead to
  910      * better results and is independent of the kernel default. We have to
  911      * use the v2 defrag ioctl.
  912      */
  913     thresh = SZ_32M;
  914 
  915     defrag_global_errors = 0;
  916     defrag_global_verbose = 0;
  917     defrag_global_errors = 0;
  918     optind = 0;
  919     while(1) {
  920         int c = getopt(argc, argv, "vrc::fs:l:t:");
  921         if (c < 0)
  922             break;
  923 
  924         switch(c) {
  925         case 'c':
  926             compress_type = BTRFS_COMPRESS_ZLIB;
  927             if (optarg)
  928                 compress_type = parse_compress_type(optarg);
  929             break;
  930         case 'f':
  931             flush = 1;
  932             break;
  933         case 'v':
  934             defrag_global_verbose = 1;
  935             break;
  936         case 's':
  937             start = parse_size(optarg);
  938             break;
  939         case 'l':
  940             len = parse_size(optarg);
  941             break;
  942         case 't':
  943             thresh = parse_size(optarg);
  944             if (thresh > (u32)-1) {
  945                 warning(
  946                 "target extent size %llu too big, trimmed to %u",
  947                     thresh, (u32)-1);
  948                 thresh = (u32)-1;
  949             }
  950             break;
  951         case 'r':
  952             recursive = 1;
  953             break;
  954         default:
  955             usage_unknown_option(cmd, argv);
  956         }
  957     }
  958 
  959     if (check_argc_min(argc - optind, 1))
  960         return 1;
  961 
  962     memset(&defrag_global_range, 0, sizeof(defrag_global_range));
  963     defrag_global_range.start = start;
  964     defrag_global_range.len = len;
  965     defrag_global_range.extent_thresh = (u32)thresh;
  966     if (compress_type) {
  967         defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
  968         defrag_global_range.compress_type = compress_type;
  969     }
  970     if (flush)
  971         defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
  972 
  973     /*
  974      * Look for directory arguments and warn if the recursive mode is not
  975      * requested, as this is not implemented as recursive defragmentation
  976      * in kernel. The stat errors are silent here as we check them below.
  977      */
  978     if (!recursive) {
  979         int found = 0;
  980 
  981         for (i = optind; i < argc; i++) {
  982             struct stat st;
  983 
  984             if (stat(argv[i], &st))
  985                 continue;
  986 
  987             if (S_ISDIR(st.st_mode)) {
  988                 warning(
  989             "directory specified but recursive mode not requested: %s",
  990                     argv[i]);
  991                 found = 1;
  992             }
  993         }
  994         if (found) {
  995             warning(
  996 "a directory passed to the defrag ioctl will not process the files\n"
  997 "recursively but will defragment the subvolume tree and the extent tree.\n"
  998 "If this is not intended, please use option -r .");
  999         }
 1000     }
 1001 
 1002     for (i = optind; i < argc; i++) {
 1003         struct stat st;
 1004         int defrag_err = 0;
 1005 
 1006         dirstream = NULL;
 1007         fd = open_file_or_dir3(argv[i], &dirstream, defrag_open_mode);
 1008         if (fd < 0) {
 1009             error("cannot open %s: %m", argv[i]);
 1010             ret = -errno;
 1011             goto next;
 1012         }
 1013 
 1014         ret = fstat(fd, &st);
 1015         if (ret) {
 1016             error("failed to stat %s: %m", argv[i]);
 1017             ret = -errno;
 1018             goto next;
 1019         }
 1020         if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
 1021             error("%s is not a directory or a regular file",
 1022                     argv[i]);
 1023             ret = -EINVAL;
 1024             goto next;
 1025         }
 1026         if (recursive && S_ISDIR(st.st_mode)) {
 1027             ret = nftw(argv[i], defrag_callback, 10,
 1028                         FTW_MOUNT | FTW_PHYS);
 1029             if (ret == ENOTTY)
 1030                 exit(1);
 1031             /* errors are handled in the callback */
 1032             ret = 0;
 1033         } else {
 1034             if (defrag_global_verbose)
 1035                 printf("%s\n", argv[i]);
 1036             ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE,
 1037                     &defrag_global_range);
 1038             defrag_err = errno;
 1039             if (ret && defrag_err == ENOTTY) {
 1040                 error(
 1041 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
 1042                 defrag_global_errors++;
 1043                 close_file_or_dir(fd, dirstream);
 1044                 break;
 1045             }
 1046             if (ret) {
 1047                 errno = defrag_err;
 1048                 error("defrag failed on %s: %m", argv[i]);
 1049                 goto next;
 1050             }
 1051         }
 1052 next:
 1053         if (ret)
 1054             defrag_global_errors++;
 1055         close_file_or_dir(fd, dirstream);
 1056     }
 1057 
 1058     if (defrag_global_errors)
 1059         fprintf(stderr, "total %d failures\n", defrag_global_errors);
 1060 
 1061     return !!defrag_global_errors;
 1062 }
 1063 static DEFINE_SIMPLE_COMMAND(filesystem_defrag, "defragment");
 1064 
 1065 static const char * const cmd_filesystem_resize_usage[] = {
 1066     "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
 1067     "Resize a filesystem",
 1068     "If 'max' is passed, the filesystem will occupy all available space",
 1069     "on the device 'devid'.",
 1070     "[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
 1071     NULL
 1072 };
 1073 
 1074 static int cmd_filesystem_resize(const struct cmd_struct *cmd,
 1075                  int argc, char **argv)
 1076 {
 1077     struct btrfs_ioctl_vol_args args;
 1078     int fd, res, len, e;
 1079     char    *amount, *path;
 1080     DIR *dirstream = NULL;
 1081     struct stat st;
 1082 
 1083     clean_args_no_options_relaxed(cmd, argc, argv);
 1084 
 1085     if (check_argc_exact(argc - optind, 2))
 1086         return 1;
 1087 
 1088     amount = argv[optind];
 1089     path = argv[optind + 1];
 1090 
 1091     len = strlen(amount);
 1092     if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
 1093         error("resize value too long (%s)", amount);
 1094         return 1;
 1095     }
 1096 
 1097     res = stat(path, &st);
 1098     if (res < 0) {
 1099         error("resize: cannot stat %s: %m", path);
 1100         return 1;
 1101     }
 1102     if (!S_ISDIR(st.st_mode)) {
 1103         error("resize works on mounted filesystems and accepts only\n"
 1104             "directories as argument. Passing file containing a btrfs image\n"
 1105             "would resize the underlying filesystem instead of the image.\n");
 1106         return 1;
 1107     }
 1108 
 1109     fd = btrfs_open_dir(path, &dirstream, 1);
 1110     if (fd < 0)
 1111         return 1;
 1112 
 1113     printf("Resize '%s' of '%s'\n", path, amount);
 1114     memset(&args, 0, sizeof(args));
 1115     strncpy_null(args.name, amount);
 1116     res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
 1117     e = errno;
 1118     close_file_or_dir(fd, dirstream);
 1119     if( res < 0 ){
 1120         switch (e) {
 1121         case EFBIG:
 1122             error("unable to resize '%s': no enough free space",
 1123                 path);
 1124             break;
 1125         default:
 1126             error("unable to resize '%s': %m", path);
 1127             break;
 1128         }
 1129         return 1;
 1130     } else if (res > 0) {
 1131         const char *err_str = btrfs_err_str(res);
 1132 
 1133         if (err_str) {
 1134             error("resizing of '%s' failed: %s", path, err_str);
 1135         } else {
 1136             error("resizing of '%s' failed: unknown error %d",
 1137                 path, res);
 1138         }
 1139         return 1;
 1140     }
 1141     return 0;
 1142 }
 1143 static DEFINE_SIMPLE_COMMAND(filesystem_resize, "resize");
 1144 
 1145 static const char * const cmd_filesystem_label_usage[] = {
 1146     "btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
 1147     "Get or change the label of a filesystem",
 1148     "With one argument, get the label of filesystem on <device>.",
 1149     "If <newlabel> is passed, set the filesystem label to <newlabel>.",
 1150     NULL
 1151 };
 1152 
 1153 static int cmd_filesystem_label(const struct cmd_struct *cmd,
 1154                 int argc, char **argv)
 1155 {
 1156     clean_args_no_options(cmd, argc, argv);
 1157 
 1158     if (check_argc_min(argc - optind, 1) ||
 1159             check_argc_max(argc - optind, 2))
 1160         return 1;
 1161 
 1162     if (argc - optind > 1) {
 1163         return set_label(argv[optind], argv[optind + 1]);
 1164     } else {
 1165         char label[BTRFS_LABEL_SIZE];
 1166         int ret;
 1167 
 1168         ret = get_label(argv[optind], label);
 1169         if (!ret)
 1170             fprintf(stdout, "%s\n", label);
 1171 
 1172         return ret;
 1173     }
 1174 }
 1175 static DEFINE_SIMPLE_COMMAND(filesystem_label, "label");
 1176 
 1177 static const char * const cmd_filesystem_balance_usage[] = {
 1178     "btrfs filesystem balance [args...] (alias of \"btrfs balance\")",
 1179     "Please see \"btrfs balance --help\" for more information.",
 1180     NULL
 1181 };
 1182 
 1183 static int cmd_filesystem_balance(const struct cmd_struct *unused,
 1184                   int argc, char **argv)
 1185 {
 1186     return cmd_execute(&cmd_struct_balance, argc, argv);
 1187 }
 1188 
 1189 /*
 1190  * Compatible old "btrfs filesystem balance" command
 1191  *
 1192  * We can't use cmd_struct_balance directly here since this alias is
 1193  * for historical compatibility and is hidden.
 1194  */
 1195 static DEFINE_COMMAND(filesystem_balance, "balance", cmd_filesystem_balance,
 1196               cmd_filesystem_balance_usage, NULL, CMD_HIDDEN);
 1197 
 1198 static const char filesystem_cmd_group_info[] =
 1199 "overall filesystem tasks and information";
 1200 
 1201 static const struct cmd_group filesystem_cmd_group = {
 1202     filesystem_cmd_group_usage, filesystem_cmd_group_info, {
 1203         &cmd_struct_filesystem_df,
 1204         &cmd_struct_filesystem_du,
 1205         &cmd_struct_filesystem_show,
 1206         &cmd_struct_filesystem_sync,
 1207         &cmd_struct_filesystem_defrag,
 1208         &cmd_struct_filesystem_balance,
 1209         &cmd_struct_filesystem_resize,
 1210         &cmd_struct_filesystem_label,
 1211         &cmd_struct_filesystem_usage,
 1212         NULL
 1213     }
 1214 };
 1215 
 1216 DEFINE_GROUP_COMMAND_TOKEN(filesystem);