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