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