btrfs-progs: image: move metadump definitions to own header
[btrfs-progs-unstable/devel.git] / cmds-filesystem.c
blobc39f2d15a917f9f4440c1e62c8c25757c2d88992
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.
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.
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.
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>
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"
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.
45 static struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = {NULL,};
47 static const char * const filesystem_cmd_group_usage[] = {
48 "btrfs filesystem [<group>] <command> [<args>]",
49 NULL
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
59 static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret)
61 u64 count = 0;
62 int ret;
63 struct btrfs_ioctl_space_args *sargs;
65 sargs = malloc(sizeof(struct btrfs_ioctl_space_args));
66 if (!sargs)
67 return -ENOMEM;
69 sargs->space_slots = 0;
70 sargs->total_spaces = 0;
72 ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs);
73 if (ret < 0) {
74 error("cannot get space info: %s", strerror(errno));
75 free(sargs);
76 return -errno;
78 /* This really should never happen */
79 if (!sargs->total_spaces) {
80 free(sargs);
81 return -ENOENT;
83 count = sargs->total_spaces;
84 free(sargs);
86 sargs = malloc(sizeof(struct btrfs_ioctl_space_args) +
87 (count * sizeof(struct btrfs_ioctl_space_info)));
88 if (!sargs)
89 return -ENOMEM;
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: %s",
96 count, strerror(errno));
97 free(sargs);
98 return -errno;
100 *sargs_ret = sargs;
101 return 0;
104 static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
106 u64 i;
107 struct btrfs_ioctl_space_info *sp = sargs->spaces;
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));
118 static int cmd_filesystem_df(int argc, char **argv)
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;
127 unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
129 clean_args_no_options(argc, argv, cmd_filesystem_df_usage);
131 if (check_argc_exact(argc - optind, 1))
132 usage(cmd_filesystem_df_usage);
134 path = argv[optind];
136 fd = btrfs_open_dir(path, &dirstream, 1);
137 if (fd < 0)
138 return 1;
140 ret = get_df(fd, &sargs);
142 if (ret == 0) {
143 print_df(sargs, unit_mode);
144 free(sargs);
145 } else {
146 error("get_df failed %s", strerror(-ret));
149 close_file_or_dir(fd, dirstream);
150 return !!ret;
153 static int match_search_item_kernel(__u8 *fsid, char *mnt, char *label,
154 char *search)
156 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
157 int search_len = strlen(search);
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;
164 if (*label && strcmp(label, search) == 0)
165 return 1;
167 if (strcmp(mnt, search) == 0)
168 return 1;
170 return 0;
173 static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search)
175 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
176 struct list_head *cur;
177 struct btrfs_device *device;
178 int search_len = strlen(search);
180 search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE);
181 uuid_unparse(fs_devices->fsid, uuidbuf);
182 if (!strncmp(uuidbuf, search, search_len))
183 return 1;
185 list_for_each(cur, &fs_devices->devices) {
186 device = list_entry(cur, struct btrfs_device, dev_list);
187 if ((device->label && strcmp(device->label, search) == 0) ||
188 strcmp(device->name, search) == 0)
189 return 1;
191 return 0;
195 * Sort devices by devid, ascending
197 static int cmp_device_id(void *priv, struct list_head *a,
198 struct list_head *b)
200 const struct btrfs_device *da = list_entry(a, struct btrfs_device,
201 dev_list);
202 const struct btrfs_device *db = list_entry(b, struct btrfs_device,
203 dev_list);
205 return da->devid < db->devid ? -1 :
206 da->devid > db->devid ? 1 : 0;
209 static void splice_device_list(struct list_head *seed_devices,
210 struct list_head *all_devices)
212 struct btrfs_device *in_all, *next_all;
213 struct btrfs_device *in_seed, *next_seed;
215 list_for_each_entry_safe(in_all, next_all, all_devices, dev_list) {
216 list_for_each_entry_safe(in_seed, next_seed, seed_devices,
217 dev_list) {
218 if (in_all->devid == in_seed->devid) {
220 * When do dev replace in a sprout fs
221 * to a dev in its seed fs, the replacing
222 * dev will reside in the sprout fs and
223 * the replaced dev will still exist
224 * in the seed fs.
225 * So pick the latest one when showing
226 * the sprout fs.
228 if (in_all->generation
229 < in_seed->generation) {
230 list_del(&in_all->dev_list);
231 free(in_all);
232 } else if (in_all->generation
233 > in_seed->generation) {
234 list_del(&in_seed->dev_list);
235 free(in_seed);
237 break;
242 list_splice(seed_devices, all_devices);
245 static void print_devices(struct btrfs_fs_devices *fs_devices,
246 u64 *devs_found, unsigned unit_mode)
248 struct btrfs_device *device;
249 struct btrfs_fs_devices *cur_fs;
250 struct list_head *all_devices;
252 all_devices = &fs_devices->devices;
253 cur_fs = fs_devices->seed;
254 /* add all devices of seed fs to the fs to be printed */
255 while (cur_fs) {
256 splice_device_list(&cur_fs->devices, all_devices);
257 cur_fs = cur_fs->seed;
260 list_sort(NULL, all_devices, cmp_device_id);
261 list_for_each_entry(device, all_devices, dev_list) {
262 printf("\tdevid %4llu size %s used %s path %s\n",
263 (unsigned long long)device->devid,
264 pretty_size_mode(device->total_bytes, unit_mode),
265 pretty_size_mode(device->bytes_used, unit_mode),
266 device->name);
268 (*devs_found)++;
272 static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
273 unsigned unit_mode)
275 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
276 struct btrfs_device *device;
277 u64 devs_found = 0;
278 u64 total;
280 if (add_seen_fsid(fs_devices->fsid, seen_fsid_hash, -1, NULL))
281 return;
283 uuid_unparse(fs_devices->fsid, uuidbuf);
284 device = list_entry(fs_devices->devices.next, struct btrfs_device,
285 dev_list);
286 if (device->label && device->label[0])
287 printf("Label: '%s' ", device->label);
288 else
289 printf("Label: none ");
291 total = device->total_devs;
292 printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
293 (unsigned long long)total,
294 pretty_size_mode(device->super_bytes_used, unit_mode));
296 print_devices(fs_devices, &devs_found, unit_mode);
298 if (devs_found < total) {
299 printf("\t*** Some devices missing\n");
301 printf("\n");
304 /* adds up all the used spaces as reported by the space info ioctl
306 static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
308 u64 ret = 0;
309 int i;
310 for (i = 0; i < si->total_spaces; i++)
311 ret += si->spaces[i].used_bytes;
312 return ret;
315 static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
316 struct btrfs_ioctl_dev_info_args *dev_info,
317 struct btrfs_ioctl_space_args *space_info,
318 char *label, unsigned unit_mode)
320 int i;
321 int fd;
322 int missing = 0;
323 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
324 struct btrfs_ioctl_dev_info_args *tmp_dev_info;
325 int ret;
327 ret = add_seen_fsid(fs_info->fsid, seen_fsid_hash, -1, NULL);
328 if (ret == -EEXIST)
329 return 0;
330 else if (ret)
331 return ret;
333 uuid_unparse(fs_info->fsid, uuidbuf);
334 if (label && *label)
335 printf("Label: '%s' ", label);
336 else
337 printf("Label: none ");
339 printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
340 fs_info->num_devices,
341 pretty_size_mode(calc_used_bytes(space_info),
342 unit_mode));
344 for (i = 0; i < fs_info->num_devices; i++) {
345 char *canonical_path;
347 tmp_dev_info = (struct btrfs_ioctl_dev_info_args *)&dev_info[i];
349 /* Add check for missing devices even mounted */
350 fd = open((char *)tmp_dev_info->path, O_RDONLY);
351 if (fd < 0) {
352 missing = 1;
353 continue;
355 close(fd);
356 canonical_path = canonicalize_path((char *)tmp_dev_info->path);
357 printf("\tdevid %4llu size %s used %s path %s\n",
358 tmp_dev_info->devid,
359 pretty_size_mode(tmp_dev_info->total_bytes, unit_mode),
360 pretty_size_mode(tmp_dev_info->bytes_used, unit_mode),
361 canonical_path);
363 free(canonical_path);
366 if (missing)
367 printf("\t*** Some devices missing\n");
368 printf("\n");
369 return 0;
372 static int btrfs_scan_kernel(void *search, unsigned unit_mode)
374 int ret = 0, fd;
375 int found = 0;
376 FILE *f;
377 struct mntent *mnt;
378 struct btrfs_ioctl_fs_info_args fs_info_arg;
379 struct btrfs_ioctl_dev_info_args *dev_info_arg = NULL;
380 struct btrfs_ioctl_space_args *space_info_arg = NULL;
381 char label[BTRFS_LABEL_SIZE];
383 f = setmntent("/proc/self/mounts", "r");
384 if (f == NULL)
385 return 1;
387 memset(label, 0, sizeof(label));
388 while ((mnt = getmntent(f)) != NULL) {
389 free(dev_info_arg);
390 dev_info_arg = NULL;
391 if (strcmp(mnt->mnt_type, "btrfs"))
392 continue;
393 ret = get_fs_info(mnt->mnt_dir, &fs_info_arg,
394 &dev_info_arg);
395 if (ret)
396 goto out;
398 /* skip all fs already shown as mounted fs */
399 if (is_seen_fsid(fs_info_arg.fsid, seen_fsid_hash))
400 continue;
402 ret = get_label_mounted(mnt->mnt_dir, label);
403 /* provide backward kernel compatibility */
404 if (ret == -ENOTTY)
405 ret = get_label_unmounted(
406 (const char *)dev_info_arg->path, label);
408 if (ret)
409 goto out;
411 if (search && !match_search_item_kernel(fs_info_arg.fsid,
412 mnt->mnt_dir, label, search)) {
413 continue;
416 fd = open(mnt->mnt_dir, O_RDONLY);
417 if ((fd != -1) && !get_df(fd, &space_info_arg)) {
418 print_one_fs(&fs_info_arg, dev_info_arg,
419 space_info_arg, label, unit_mode);
420 free(space_info_arg);
421 memset(label, 0, sizeof(label));
422 found = 1;
424 if (fd != -1)
425 close(fd);
428 out:
429 free(dev_info_arg);
430 endmntent(f);
431 return !found;
434 static int dev_to_fsid(const char *dev, __u8 *fsid)
436 struct btrfs_super_block *disk_super;
437 char buf[BTRFS_SUPER_INFO_SIZE];
438 int ret;
439 int fd;
441 fd = open(dev, O_RDONLY);
442 if (fd < 0) {
443 ret = -errno;
444 return ret;
447 disk_super = (struct btrfs_super_block *)buf;
448 ret = btrfs_read_dev_super(fd, disk_super,
449 BTRFS_SUPER_INFO_OFFSET, SBREAD_DEFAULT);
450 if (ret)
451 goto out;
453 memcpy(fsid, disk_super->fsid, BTRFS_FSID_SIZE);
454 ret = 0;
456 out:
457 close(fd);
458 return ret;
461 static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
463 struct btrfs_fs_devices *cur_seed, *next_seed;
464 struct btrfs_device *device;
466 while (!list_empty(&fs_devices->devices)) {
467 device = list_entry(fs_devices->devices.next,
468 struct btrfs_device, dev_list);
469 list_del(&device->dev_list);
471 free(device->name);
472 free(device->label);
473 free(device);
476 /* free seed fs chain */
477 cur_seed = fs_devices->seed;
478 fs_devices->seed = NULL;
479 while (cur_seed) {
480 next_seed = cur_seed->seed;
481 free(cur_seed);
483 cur_seed = next_seed;
486 list_del(&fs_devices->list);
487 free(fs_devices);
490 static int copy_device(struct btrfs_device *dst,
491 struct btrfs_device *src)
493 dst->devid = src->devid;
494 memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE);
495 if (src->name == NULL)
496 dst->name = NULL;
497 else {
498 dst->name = strdup(src->name);
499 if (!dst->name)
500 return -ENOMEM;
502 if (src->label == NULL)
503 dst->label = NULL;
504 else {
505 dst->label = strdup(src->label);
506 if (!dst->label) {
507 free(dst->name);
508 return -ENOMEM;
511 dst->total_devs = src->total_devs;
512 dst->super_bytes_used = src->super_bytes_used;
513 dst->total_bytes = src->total_bytes;
514 dst->bytes_used = src->bytes_used;
515 dst->generation = src->generation;
517 return 0;
520 static int copy_fs_devices(struct btrfs_fs_devices *dst,
521 struct btrfs_fs_devices *src)
523 struct btrfs_device *cur_dev, *dev_copy;
524 int ret = 0;
526 memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE);
527 INIT_LIST_HEAD(&dst->devices);
528 dst->seed = NULL;
530 list_for_each_entry(cur_dev, &src->devices, dev_list) {
531 dev_copy = malloc(sizeof(*dev_copy));
532 if (!dev_copy) {
533 ret = -ENOMEM;
534 break;
537 ret = copy_device(dev_copy, cur_dev);
538 if (ret) {
539 free(dev_copy);
540 break;
543 list_add(&dev_copy->dev_list, &dst->devices);
544 dev_copy->fs_devices = dst;
547 return ret;
550 static int find_and_copy_seed(struct btrfs_fs_devices *seed,
551 struct btrfs_fs_devices *copy,
552 struct list_head *fs_uuids) {
553 struct btrfs_fs_devices *cur_fs;
555 list_for_each_entry(cur_fs, fs_uuids, list)
556 if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE))
557 return copy_fs_devices(copy, cur_fs);
559 return 1;
562 static int has_seed_devices(struct btrfs_fs_devices *fs_devices)
564 struct btrfs_device *device;
565 int dev_cnt_total, dev_cnt = 0;
567 device = list_first_entry(&fs_devices->devices, struct btrfs_device,
568 dev_list);
570 dev_cnt_total = device->total_devs;
572 list_for_each_entry(device, &fs_devices->devices, dev_list)
573 dev_cnt++;
575 return dev_cnt_total != dev_cnt;
578 static int search_umounted_fs_uuids(struct list_head *all_uuids,
579 char *search, int *found)
581 struct btrfs_fs_devices *cur_fs, *fs_copy;
582 struct list_head *fs_uuids;
583 int ret = 0;
585 fs_uuids = btrfs_scanned_uuids();
588 * The fs_uuids list is global, and open_ctree_* will
589 * modify it, make a private copy here
591 list_for_each_entry(cur_fs, fs_uuids, list) {
592 /* don't bother handle all fs, if search target specified */
593 if (search) {
594 if (uuid_search(cur_fs, search) == 0)
595 continue;
596 if (found)
597 *found = 1;
600 /* skip all fs already shown as mounted fs */
601 if (is_seen_fsid(cur_fs->fsid, seen_fsid_hash))
602 continue;
604 fs_copy = calloc(1, sizeof(*fs_copy));
605 if (!fs_copy) {
606 ret = -ENOMEM;
607 goto out;
610 ret = copy_fs_devices(fs_copy, cur_fs);
611 if (ret) {
612 free(fs_copy);
613 goto out;
616 list_add(&fs_copy->list, all_uuids);
619 out:
620 return ret;
623 static int map_seed_devices(struct list_head *all_uuids)
625 struct btrfs_fs_devices *cur_fs, *cur_seed;
626 struct btrfs_fs_devices *seed_copy;
627 struct btrfs_fs_devices *opened_fs;
628 struct btrfs_device *device;
629 struct btrfs_fs_info *fs_info;
630 struct list_head *fs_uuids;
631 int ret = 0;
633 fs_uuids = btrfs_scanned_uuids();
635 list_for_each_entry(cur_fs, all_uuids, list) {
636 device = list_first_entry(&cur_fs->devices,
637 struct btrfs_device, dev_list);
638 if (!device)
639 continue;
641 /* skip fs without seeds */
642 if (!has_seed_devices(cur_fs))
643 continue;
646 * open_ctree_* detects seed/sprout mapping
648 fs_info = open_ctree_fs_info(device->name, 0, 0, 0,
649 OPEN_CTREE_PARTIAL);
650 if (!fs_info)
651 continue;
654 * copy the seed chain under the opened fs
656 opened_fs = fs_info->fs_devices;
657 cur_seed = cur_fs;
658 while (opened_fs->seed) {
659 seed_copy = malloc(sizeof(*seed_copy));
660 if (!seed_copy) {
661 ret = -ENOMEM;
662 goto fail_out;
664 ret = find_and_copy_seed(opened_fs->seed, seed_copy,
665 fs_uuids);
666 if (ret) {
667 free(seed_copy);
668 goto fail_out;
671 cur_seed->seed = seed_copy;
673 opened_fs = opened_fs->seed;
674 cur_seed = cur_seed->seed;
677 close_ctree(fs_info->chunk_root);
680 out:
681 return ret;
682 fail_out:
683 close_ctree(fs_info->chunk_root);
684 goto out;
687 static const char * const cmd_filesystem_show_usage[] = {
688 "btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
689 "Show the structure of a filesystem",
690 "-d|--all-devices show only disks under /dev containing btrfs filesystem",
691 "-m|--mounted show only mounted btrfs",
692 HELPINFO_UNITS_LONG,
693 "If no argument is given, structure of all present filesystems is shown.",
694 NULL
697 static int cmd_filesystem_show(int argc, char **argv)
699 LIST_HEAD(all_uuids);
700 struct btrfs_fs_devices *fs_devices;
701 char *search = NULL;
702 int ret;
703 /* default, search both kernel and udev */
704 int where = -1;
705 int type = 0;
706 char mp[PATH_MAX];
707 char path[PATH_MAX];
708 __u8 fsid[BTRFS_FSID_SIZE];
709 char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
710 unsigned unit_mode;
711 int found = 0;
713 unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
715 while (1) {
716 int c;
717 static const struct option long_options[] = {
718 { "all-devices", no_argument, NULL, 'd'},
719 { "mounted", no_argument, NULL, 'm'},
720 { NULL, 0, NULL, 0 }
723 c = getopt_long(argc, argv, "dm", long_options, NULL);
724 if (c < 0)
725 break;
726 switch (c) {
727 case 'd':
728 where = BTRFS_SCAN_LBLKID;
729 break;
730 case 'm':
731 where = BTRFS_SCAN_MOUNTED;
732 break;
733 default:
734 usage(cmd_filesystem_show_usage);
738 if (check_argc_max(argc, optind + 1))
739 usage(cmd_filesystem_show_usage);
741 if (argc > optind) {
742 search = argv[optind];
743 if (*search == 0)
744 usage(cmd_filesystem_show_usage);
745 type = check_arg_type(search);
748 * For search is a device:
749 * realpath do /dev/mapper/XX => /dev/dm-X
750 * which is required by BTRFS_SCAN_DEV
751 * For search is a mountpoint:
752 * realpath do /mnt/btrfs/ => /mnt/btrfs
753 * which shall be recognized by btrfs_scan_kernel()
755 if (realpath(search, path))
756 search = path;
759 * Needs special handling if input arg is block dev And if
760 * input arg is mount-point just print it right away
762 if (type == BTRFS_ARG_BLKDEV && where != BTRFS_SCAN_LBLKID) {
763 ret = get_btrfs_mount(search, mp, sizeof(mp));
764 if (!ret) {
765 /* given block dev is mounted */
766 search = mp;
767 type = BTRFS_ARG_MNTPOINT;
768 } else {
769 ret = dev_to_fsid(search, fsid);
770 if (ret) {
771 error("no btrfs on %s", search);
772 return 1;
774 uuid_unparse(fsid, uuid_buf);
775 search = uuid_buf;
776 type = BTRFS_ARG_UUID;
777 goto devs_only;
782 if (where == BTRFS_SCAN_LBLKID)
783 goto devs_only;
785 /* show mounted btrfs */
786 ret = btrfs_scan_kernel(search, unit_mode);
787 if (search && !ret) {
788 /* since search is found we are done */
789 goto out;
792 /* shows mounted only */
793 if (where == BTRFS_SCAN_MOUNTED)
794 goto out;
796 devs_only:
797 ret = btrfs_scan_devices();
799 if (ret) {
800 error("blkid device scan returned %d", ret);
801 return 1;
804 ret = search_umounted_fs_uuids(&all_uuids, search, &found);
805 if (ret < 0) {
806 error("searching target device returned error %d", ret);
807 return 1;
811 * The seed/sprout mapping are not detected yet,
812 * do mapping build for all umounted fs
814 ret = map_seed_devices(&all_uuids);
815 if (ret) {
816 error("mapping seed devices returned error %d", ret);
817 return 1;
820 list_for_each_entry(fs_devices, &all_uuids, list)
821 print_one_uuid(fs_devices, unit_mode);
823 if (search && !found) {
824 error("not a valid btrfs filesystem: %s", search);
825 ret = 1;
827 while (!list_empty(&all_uuids)) {
828 fs_devices = list_entry(all_uuids.next,
829 struct btrfs_fs_devices, list);
830 free_fs_devices(fs_devices);
832 out:
833 free_seen_fsid(seen_fsid_hash);
834 return ret;
837 static const char * const cmd_filesystem_sync_usage[] = {
838 "btrfs filesystem sync <path>",
839 "Force a sync on a filesystem",
840 NULL
843 static int cmd_filesystem_sync(int argc, char **argv)
845 int fd, res, e;
846 char *path;
847 DIR *dirstream = NULL;
849 clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
851 if (check_argc_exact(argc - optind, 1))
852 usage(cmd_filesystem_sync_usage);
854 path = argv[optind];
856 fd = btrfs_open_dir(path, &dirstream, 1);
857 if (fd < 0)
858 return 1;
860 res = ioctl(fd, BTRFS_IOC_SYNC);
861 e = errno;
862 close_file_or_dir(fd, dirstream);
863 if( res < 0 ){
864 error("sync ioctl failed on '%s': %s", path, strerror(e));
865 return 1;
868 return 0;
871 static int parse_compress_type(char *s)
873 if (strcmp(optarg, "zlib") == 0)
874 return BTRFS_COMPRESS_ZLIB;
875 else if (strcmp(optarg, "lzo") == 0)
876 return BTRFS_COMPRESS_LZO;
877 else if (strcmp(optarg, "zstd") == 0)
878 return BTRFS_COMPRESS_ZSTD;
879 else {
880 error("unknown compression type %s", s);
881 exit(1);
885 static const char * const cmd_filesystem_defrag_usage[] = {
886 "btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
887 "Defragment a file or a directory",
889 "-v be verbose",
890 "-r defragment files recursively",
891 "-c[zlib,lzo,zstd] compress the file while defragmenting",
892 "-f flush data to disk immediately after defragmenting",
893 "-s start defragment only from byte onward",
894 "-l len defragment only up to len bytes",
895 "-t size target extent size hint (default: 32M)",
896 NULL
899 static struct btrfs_ioctl_defrag_range_args defrag_global_range;
900 static int defrag_global_verbose;
901 static int defrag_global_errors;
902 static int defrag_callback(const char *fpath, const struct stat *sb,
903 int typeflag, struct FTW *ftwbuf)
905 int ret = 0;
906 int err = 0;
907 int fd = 0;
909 if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) {
910 if (defrag_global_verbose)
911 printf("%s\n", fpath);
912 fd = open(fpath, O_RDWR);
913 if (fd < 0) {
914 err = errno;
915 goto error;
917 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
918 close(fd);
919 if (ret && errno == ENOTTY) {
920 error(
921 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
922 defrag_global_errors++;
923 return ENOTTY;
925 if (ret) {
926 err = errno;
927 goto error;
930 return 0;
932 error:
933 error("defrag failed on %s: %s", fpath, strerror(err));
934 defrag_global_errors++;
935 return 0;
938 static int cmd_filesystem_defrag(int argc, char **argv)
940 int fd;
941 int flush = 0;
942 u64 start = 0;
943 u64 len = (u64)-1;
944 u64 thresh;
945 int i;
946 int recursive = 0;
947 int ret = 0;
948 int compress_type = BTRFS_COMPRESS_NONE;
949 DIR *dirstream;
952 * Kernel has a different default (256K) that is supposed to be safe,
953 * but it does not defragment very well. The 32M will likely lead to
954 * better results and is independent of the kernel default. We have to
955 * use the v2 defrag ioctl.
957 thresh = SZ_32M;
959 defrag_global_errors = 0;
960 defrag_global_verbose = 0;
961 defrag_global_errors = 0;
962 while(1) {
963 int c = getopt(argc, argv, "vrc::fs:l:t:");
964 if (c < 0)
965 break;
967 switch(c) {
968 case 'c':
969 compress_type = BTRFS_COMPRESS_ZLIB;
970 if (optarg)
971 compress_type = parse_compress_type(optarg);
972 break;
973 case 'f':
974 flush = 1;
975 break;
976 case 'v':
977 defrag_global_verbose = 1;
978 break;
979 case 's':
980 start = parse_size(optarg);
981 break;
982 case 'l':
983 len = parse_size(optarg);
984 break;
985 case 't':
986 thresh = parse_size(optarg);
987 if (thresh > (u32)-1) {
988 warning(
989 "target extent size %llu too big, trimmed to %u",
990 thresh, (u32)-1);
991 thresh = (u32)-1;
993 break;
994 case 'r':
995 recursive = 1;
996 break;
997 default:
998 usage(cmd_filesystem_defrag_usage);
1002 if (check_argc_min(argc - optind, 1))
1003 usage(cmd_filesystem_defrag_usage);
1005 memset(&defrag_global_range, 0, sizeof(defrag_global_range));
1006 defrag_global_range.start = start;
1007 defrag_global_range.len = len;
1008 defrag_global_range.extent_thresh = (u32)thresh;
1009 if (compress_type) {
1010 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
1011 defrag_global_range.compress_type = compress_type;
1013 if (flush)
1014 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
1017 * Look for directory arguments and warn if the recursive mode is not
1018 * requested, as this is not implemented as recursive defragmentation
1019 * in kernel. The stat errors are silent here as we check them below.
1021 if (!recursive) {
1022 int found = 0;
1024 for (i = optind; i < argc; i++) {
1025 struct stat st;
1027 if (stat(argv[i], &st))
1028 continue;
1030 if (S_ISDIR(st.st_mode)) {
1031 warning(
1032 "directory specified but recursive mode not requested: %s",
1033 argv[i]);
1034 found = 1;
1037 if (found) {
1038 warning(
1039 "a directory passed to the defrag ioctl will not process the files\n"
1040 "recursively but will defragment the subvolume tree and the extent tree.\n"
1041 "If this is not intended, please use option -r .");
1045 for (i = optind; i < argc; i++) {
1046 struct stat st;
1047 int defrag_err = 0;
1049 dirstream = NULL;
1050 fd = open_file_or_dir(argv[i], &dirstream);
1051 if (fd < 0) {
1052 error("cannot open %s: %s", argv[i],
1053 strerror(errno));
1054 defrag_global_errors++;
1055 close_file_or_dir(fd, dirstream);
1056 continue;
1058 if (fstat(fd, &st)) {
1059 error("failed to stat %s: %s",
1060 argv[i], strerror(errno));
1061 defrag_global_errors++;
1062 close_file_or_dir(fd, dirstream);
1063 continue;
1065 if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
1066 error("%s is not a directory or a regular file",
1067 argv[i]);
1068 defrag_global_errors++;
1069 close_file_or_dir(fd, dirstream);
1070 continue;
1072 if (recursive && S_ISDIR(st.st_mode)) {
1073 ret = nftw(argv[i], defrag_callback, 10,
1074 FTW_MOUNT | FTW_PHYS);
1075 if (ret == ENOTTY)
1076 exit(1);
1077 /* errors are handled in the callback */
1078 ret = 0;
1079 } else {
1080 if (defrag_global_verbose)
1081 printf("%s\n", argv[i]);
1082 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE,
1083 &defrag_global_range);
1084 defrag_err = errno;
1086 close_file_or_dir(fd, dirstream);
1087 if (ret && defrag_err == ENOTTY) {
1088 error(
1089 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
1090 defrag_global_errors++;
1091 break;
1093 if (ret) {
1094 error("defrag failed on %s: %s", argv[i],
1095 strerror(defrag_err));
1096 defrag_global_errors++;
1099 if (defrag_global_errors)
1100 fprintf(stderr, "total %d failures\n", defrag_global_errors);
1102 return !!defrag_global_errors;
1105 static const char * const cmd_filesystem_resize_usage[] = {
1106 "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
1107 "Resize a filesystem",
1108 "If 'max' is passed, the filesystem will occupy all available space",
1109 "on the device 'devid'.",
1110 "[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
1111 NULL
1114 static int cmd_filesystem_resize(int argc, char **argv)
1116 struct btrfs_ioctl_vol_args args;
1117 int fd, res, len, e;
1118 char *amount, *path;
1119 DIR *dirstream = NULL;
1120 struct stat st;
1122 clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
1124 if (check_argc_exact(argc - optind, 2))
1125 usage(cmd_filesystem_resize_usage);
1127 amount = argv[optind];
1128 path = argv[optind + 1];
1130 len = strlen(amount);
1131 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
1132 error("resize value too long (%s)", amount);
1133 return 1;
1136 res = stat(path, &st);
1137 if (res < 0) {
1138 error("resize: cannot stat %s: %s", path, strerror(errno));
1139 return 1;
1141 if (!S_ISDIR(st.st_mode)) {
1142 error("resize works on mounted filesystems and accepts only\n"
1143 "directories as argument. Passing file containing a btrfs image\n"
1144 "would resize the underlying filesystem instead of the image.\n");
1145 return 1;
1148 fd = btrfs_open_dir(path, &dirstream, 1);
1149 if (fd < 0)
1150 return 1;
1152 printf("Resize '%s' of '%s'\n", path, amount);
1153 memset(&args, 0, sizeof(args));
1154 strncpy_null(args.name, amount);
1155 res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
1156 e = errno;
1157 close_file_or_dir(fd, dirstream);
1158 if( res < 0 ){
1159 switch (e) {
1160 case EFBIG:
1161 error("unable to resize '%s': no enough free space",
1162 path);
1163 break;
1164 default:
1165 error("unable to resize '%s': %s", path, strerror(e));
1166 break;
1168 return 1;
1169 } else if (res > 0) {
1170 const char *err_str = btrfs_err_str(res);
1172 if (err_str) {
1173 error("resizing of '%s' failed: %s", path, err_str);
1174 } else {
1175 error("resizing of '%s' failed: unknown error %d",
1176 path, res);
1178 return 1;
1180 return 0;
1183 static const char * const cmd_filesystem_label_usage[] = {
1184 "btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
1185 "Get or change the label of a filesystem",
1186 "With one argument, get the label of filesystem on <device>.",
1187 "If <newlabel> is passed, set the filesystem label to <newlabel>.",
1188 NULL
1191 static int cmd_filesystem_label(int argc, char **argv)
1193 clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
1195 if (check_argc_min(argc - optind, 1) ||
1196 check_argc_max(argc - optind, 2))
1197 usage(cmd_filesystem_label_usage);
1199 if (argc - optind > 1) {
1200 return set_label(argv[optind], argv[optind + 1]);
1201 } else {
1202 char label[BTRFS_LABEL_SIZE];
1203 int ret;
1205 ret = get_label(argv[optind], label);
1206 if (!ret)
1207 fprintf(stdout, "%s\n", label);
1209 return ret;
1213 static const char filesystem_cmd_group_info[] =
1214 "overall filesystem tasks and information";
1216 const struct cmd_group filesystem_cmd_group = {
1217 filesystem_cmd_group_usage, filesystem_cmd_group_info, {
1218 { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 },
1219 { "du", cmd_filesystem_du, cmd_filesystem_du_usage, NULL, 0 },
1220 { "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
1221 0 },
1222 { "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
1223 0 },
1224 { "defragment", cmd_filesystem_defrag,
1225 cmd_filesystem_defrag_usage, NULL, 0 },
1226 { "balance", cmd_balance, NULL, &balance_cmd_group,
1227 CMD_HIDDEN },
1228 { "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
1229 NULL, 0 },
1230 { "label", cmd_filesystem_label, cmd_filesystem_label_usage,
1231 NULL, 0 },
1232 { "usage", cmd_filesystem_usage,
1233 cmd_filesystem_usage_usage, NULL, 0 },
1235 NULL_CMD_STRUCT
1239 int cmd_filesystem(int argc, char **argv)
1241 return handle_command_group(&filesystem_cmd_group, argc, argv);