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