btrfs-progs: check: move reada_walk_down to check/common.c
[btrfs-progs-unstable/devel.git] / cmds-filesystem.c
blob467aff118e1511c1345a01814875aec5656adec5
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: %m");
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: %m",
96 count);
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 btrfs_device *device;
177 int search_len = strlen(search);
179 search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE);
180 uuid_unparse(fs_devices->fsid, uuidbuf);
181 if (!strncmp(uuidbuf, search, search_len))
182 return 1;
184 list_for_each_entry(device, &fs_devices->devices, dev_list) {
185 if ((device->label && strcmp(device->label, search) == 0) ||
186 strcmp(device->name, search) == 0)
187 return 1;
189 return 0;
193 * Sort devices by devid, ascending
195 static int cmp_device_id(void *priv, struct list_head *a,
196 struct list_head *b)
198 const struct btrfs_device *da = list_entry(a, struct btrfs_device,
199 dev_list);
200 const struct btrfs_device *db = list_entry(b, struct btrfs_device,
201 dev_list);
203 return da->devid < db->devid ? -1 :
204 da->devid > db->devid ? 1 : 0;
207 static void splice_device_list(struct list_head *seed_devices,
208 struct list_head *all_devices)
210 struct btrfs_device *in_all, *next_all;
211 struct btrfs_device *in_seed, *next_seed;
213 list_for_each_entry_safe(in_all, next_all, all_devices, dev_list) {
214 list_for_each_entry_safe(in_seed, next_seed, seed_devices,
215 dev_list) {
216 if (in_all->devid == in_seed->devid) {
218 * When do dev replace in a sprout fs
219 * to a dev in its seed fs, the replacing
220 * dev will reside in the sprout fs and
221 * the replaced dev will still exist
222 * in the seed fs.
223 * So pick the latest one when showing
224 * the sprout fs.
226 if (in_all->generation
227 < in_seed->generation) {
228 list_del(&in_all->dev_list);
229 free(in_all);
230 } else if (in_all->generation
231 > in_seed->generation) {
232 list_del(&in_seed->dev_list);
233 free(in_seed);
235 break;
240 list_splice(seed_devices, all_devices);
243 static void print_devices(struct btrfs_fs_devices *fs_devices,
244 u64 *devs_found, unsigned unit_mode)
246 struct btrfs_device *device;
247 struct btrfs_fs_devices *cur_fs;
248 struct list_head *all_devices;
250 all_devices = &fs_devices->devices;
251 cur_fs = fs_devices->seed;
252 /* add all devices of seed fs to the fs to be printed */
253 while (cur_fs) {
254 splice_device_list(&cur_fs->devices, all_devices);
255 cur_fs = cur_fs->seed;
258 list_sort(NULL, all_devices, cmp_device_id);
259 list_for_each_entry(device, all_devices, dev_list) {
260 printf("\tdevid %4llu size %s used %s path %s\n",
261 (unsigned long long)device->devid,
262 pretty_size_mode(device->total_bytes, unit_mode),
263 pretty_size_mode(device->bytes_used, unit_mode),
264 device->name);
266 (*devs_found)++;
270 static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
271 unsigned unit_mode)
273 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
274 struct btrfs_device *device;
275 u64 devs_found = 0;
276 u64 total;
278 if (add_seen_fsid(fs_devices->fsid, seen_fsid_hash, -1, NULL))
279 return;
281 uuid_unparse(fs_devices->fsid, uuidbuf);
282 device = list_entry(fs_devices->devices.next, struct btrfs_device,
283 dev_list);
284 if (device->label && device->label[0])
285 printf("Label: '%s' ", device->label);
286 else
287 printf("Label: none ");
289 total = device->total_devs;
290 printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
291 (unsigned long long)total,
292 pretty_size_mode(device->super_bytes_used, unit_mode));
294 print_devices(fs_devices, &devs_found, unit_mode);
296 if (devs_found < total) {
297 printf("\t*** Some devices missing\n");
299 printf("\n");
302 /* adds up all the used spaces as reported by the space info ioctl
304 static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
306 u64 ret = 0;
307 int i;
308 for (i = 0; i < si->total_spaces; i++)
309 ret += si->spaces[i].used_bytes;
310 return ret;
313 static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
314 struct btrfs_ioctl_dev_info_args *dev_info,
315 struct btrfs_ioctl_space_args *space_info,
316 char *label, unsigned unit_mode)
318 int i;
319 int fd;
320 int missing = 0;
321 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
322 struct btrfs_ioctl_dev_info_args *tmp_dev_info;
323 int ret;
325 ret = add_seen_fsid(fs_info->fsid, seen_fsid_hash, -1, NULL);
326 if (ret == -EEXIST)
327 return 0;
328 else if (ret)
329 return ret;
331 uuid_unparse(fs_info->fsid, uuidbuf);
332 if (label && *label)
333 printf("Label: '%s' ", label);
334 else
335 printf("Label: none ");
337 printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
338 fs_info->num_devices,
339 pretty_size_mode(calc_used_bytes(space_info),
340 unit_mode));
342 for (i = 0; i < fs_info->num_devices; i++) {
343 char *canonical_path;
345 tmp_dev_info = (struct btrfs_ioctl_dev_info_args *)&dev_info[i];
347 /* Add check for missing devices even mounted */
348 fd = open((char *)tmp_dev_info->path, O_RDONLY);
349 if (fd < 0) {
350 missing = 1;
351 continue;
353 close(fd);
354 canonical_path = canonicalize_path((char *)tmp_dev_info->path);
355 printf("\tdevid %4llu size %s used %s path %s\n",
356 tmp_dev_info->devid,
357 pretty_size_mode(tmp_dev_info->total_bytes, unit_mode),
358 pretty_size_mode(tmp_dev_info->bytes_used, unit_mode),
359 canonical_path);
361 free(canonical_path);
364 if (missing)
365 printf("\t*** Some devices missing\n");
366 printf("\n");
367 return 0;
370 static int btrfs_scan_kernel(void *search, unsigned unit_mode)
372 int ret = 0, fd;
373 int found = 0;
374 FILE *f;
375 struct mntent *mnt;
376 struct btrfs_ioctl_fs_info_args fs_info_arg;
377 struct btrfs_ioctl_dev_info_args *dev_info_arg = NULL;
378 struct btrfs_ioctl_space_args *space_info_arg = NULL;
379 char label[BTRFS_LABEL_SIZE];
381 f = setmntent("/proc/self/mounts", "r");
382 if (f == NULL)
383 return 1;
385 memset(label, 0, sizeof(label));
386 while ((mnt = getmntent(f)) != NULL) {
387 free(dev_info_arg);
388 dev_info_arg = NULL;
389 if (strcmp(mnt->mnt_type, "btrfs"))
390 continue;
391 ret = get_fs_info(mnt->mnt_dir, &fs_info_arg,
392 &dev_info_arg);
393 if (ret)
394 goto out;
396 /* skip all fs already shown as mounted fs */
397 if (is_seen_fsid(fs_info_arg.fsid, seen_fsid_hash))
398 continue;
400 ret = get_label_mounted(mnt->mnt_dir, label);
401 /* provide backward kernel compatibility */
402 if (ret == -ENOTTY)
403 ret = get_label_unmounted(
404 (const char *)dev_info_arg->path, label);
406 if (ret)
407 goto out;
409 if (search && !match_search_item_kernel(fs_info_arg.fsid,
410 mnt->mnt_dir, label, search)) {
411 continue;
414 fd = open(mnt->mnt_dir, O_RDONLY);
415 if ((fd != -1) && !get_df(fd, &space_info_arg)) {
416 print_one_fs(&fs_info_arg, dev_info_arg,
417 space_info_arg, label, unit_mode);
418 free(space_info_arg);
419 memset(label, 0, sizeof(label));
420 found = 1;
422 if (fd != -1)
423 close(fd);
426 out:
427 free(dev_info_arg);
428 endmntent(f);
429 return !found;
432 static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
434 struct btrfs_fs_devices *cur_seed, *next_seed;
435 struct btrfs_device *device;
437 while (!list_empty(&fs_devices->devices)) {
438 device = list_entry(fs_devices->devices.next,
439 struct btrfs_device, dev_list);
440 list_del(&device->dev_list);
442 free(device->name);
443 free(device->label);
444 free(device);
447 /* free seed fs chain */
448 cur_seed = fs_devices->seed;
449 fs_devices->seed = NULL;
450 while (cur_seed) {
451 next_seed = cur_seed->seed;
452 free(cur_seed);
454 cur_seed = next_seed;
457 list_del(&fs_devices->list);
458 free(fs_devices);
461 static int copy_device(struct btrfs_device *dst,
462 struct btrfs_device *src)
464 dst->devid = src->devid;
465 memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE);
466 if (src->name == NULL)
467 dst->name = NULL;
468 else {
469 dst->name = strdup(src->name);
470 if (!dst->name)
471 return -ENOMEM;
473 if (src->label == NULL)
474 dst->label = NULL;
475 else {
476 dst->label = strdup(src->label);
477 if (!dst->label) {
478 free(dst->name);
479 return -ENOMEM;
482 dst->total_devs = src->total_devs;
483 dst->super_bytes_used = src->super_bytes_used;
484 dst->total_bytes = src->total_bytes;
485 dst->bytes_used = src->bytes_used;
486 dst->generation = src->generation;
488 return 0;
491 static int copy_fs_devices(struct btrfs_fs_devices *dst,
492 struct btrfs_fs_devices *src)
494 struct btrfs_device *cur_dev, *dev_copy;
495 int ret = 0;
497 memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE);
498 INIT_LIST_HEAD(&dst->devices);
499 dst->seed = NULL;
501 list_for_each_entry(cur_dev, &src->devices, dev_list) {
502 dev_copy = malloc(sizeof(*dev_copy));
503 if (!dev_copy) {
504 ret = -ENOMEM;
505 break;
508 ret = copy_device(dev_copy, cur_dev);
509 if (ret) {
510 free(dev_copy);
511 break;
514 list_add(&dev_copy->dev_list, &dst->devices);
515 dev_copy->fs_devices = dst;
518 return ret;
521 static int find_and_copy_seed(struct btrfs_fs_devices *seed,
522 struct btrfs_fs_devices *copy,
523 struct list_head *fs_uuids) {
524 struct btrfs_fs_devices *cur_fs;
526 list_for_each_entry(cur_fs, fs_uuids, list)
527 if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE))
528 return copy_fs_devices(copy, cur_fs);
530 return 1;
533 static int has_seed_devices(struct btrfs_fs_devices *fs_devices)
535 struct btrfs_device *device;
536 int dev_cnt_total, dev_cnt = 0;
538 device = list_first_entry(&fs_devices->devices, struct btrfs_device,
539 dev_list);
541 dev_cnt_total = device->total_devs;
543 list_for_each_entry(device, &fs_devices->devices, dev_list)
544 dev_cnt++;
546 return dev_cnt_total != dev_cnt;
549 static int search_umounted_fs_uuids(struct list_head *all_uuids,
550 char *search, int *found)
552 struct btrfs_fs_devices *cur_fs, *fs_copy;
553 struct list_head *fs_uuids;
554 int ret = 0;
556 fs_uuids = btrfs_scanned_uuids();
559 * The fs_uuids list is global, and open_ctree_* will
560 * modify it, make a private copy here
562 list_for_each_entry(cur_fs, fs_uuids, list) {
563 /* don't bother handle all fs, if search target specified */
564 if (search) {
565 if (uuid_search(cur_fs, search) == 0)
566 continue;
567 if (found)
568 *found = 1;
571 /* skip all fs already shown as mounted fs */
572 if (is_seen_fsid(cur_fs->fsid, seen_fsid_hash))
573 continue;
575 fs_copy = calloc(1, sizeof(*fs_copy));
576 if (!fs_copy) {
577 ret = -ENOMEM;
578 goto out;
581 ret = copy_fs_devices(fs_copy, cur_fs);
582 if (ret) {
583 free(fs_copy);
584 goto out;
587 list_add(&fs_copy->list, all_uuids);
590 out:
591 return ret;
594 static int map_seed_devices(struct list_head *all_uuids)
596 struct btrfs_fs_devices *cur_fs, *cur_seed;
597 struct btrfs_fs_devices *seed_copy;
598 struct btrfs_fs_devices *opened_fs;
599 struct btrfs_device *device;
600 struct btrfs_fs_info *fs_info;
601 struct list_head *fs_uuids;
602 int ret = 0;
604 fs_uuids = btrfs_scanned_uuids();
606 list_for_each_entry(cur_fs, all_uuids, list) {
607 device = list_first_entry(&cur_fs->devices,
608 struct btrfs_device, dev_list);
609 if (!device)
610 continue;
612 /* skip fs without seeds */
613 if (!has_seed_devices(cur_fs))
614 continue;
617 * open_ctree_* detects seed/sprout mapping
619 fs_info = open_ctree_fs_info(device->name, 0, 0, 0,
620 OPEN_CTREE_PARTIAL);
621 if (!fs_info)
622 continue;
625 * copy the seed chain under the opened fs
627 opened_fs = fs_info->fs_devices;
628 cur_seed = cur_fs;
629 while (opened_fs->seed) {
630 seed_copy = malloc(sizeof(*seed_copy));
631 if (!seed_copy) {
632 ret = -ENOMEM;
633 goto fail_out;
635 ret = find_and_copy_seed(opened_fs->seed, seed_copy,
636 fs_uuids);
637 if (ret) {
638 free(seed_copy);
639 goto fail_out;
642 cur_seed->seed = seed_copy;
644 opened_fs = opened_fs->seed;
645 cur_seed = cur_seed->seed;
648 close_ctree(fs_info->chunk_root);
651 out:
652 return ret;
653 fail_out:
654 close_ctree(fs_info->chunk_root);
655 goto out;
658 static const char * const cmd_filesystem_show_usage[] = {
659 "btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
660 "Show the structure of a filesystem",
661 "-d|--all-devices show only disks under /dev containing btrfs filesystem",
662 "-m|--mounted show only mounted btrfs",
663 HELPINFO_UNITS_LONG,
664 "If no argument is given, structure of all present filesystems is shown.",
665 NULL
668 static int cmd_filesystem_show(int argc, char **argv)
670 LIST_HEAD(all_uuids);
671 struct btrfs_fs_devices *fs_devices;
672 char *search = NULL;
673 int ret;
674 /* default, search both kernel and udev */
675 int where = -1;
676 int type = 0;
677 char mp[PATH_MAX];
678 char path[PATH_MAX];
679 u8 fsid[BTRFS_FSID_SIZE];
680 char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
681 unsigned unit_mode;
682 int found = 0;
684 unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
686 while (1) {
687 int c;
688 static const struct option long_options[] = {
689 { "all-devices", no_argument, NULL, 'd'},
690 { "mounted", no_argument, NULL, 'm'},
691 { NULL, 0, NULL, 0 }
694 c = getopt_long(argc, argv, "dm", long_options, NULL);
695 if (c < 0)
696 break;
697 switch (c) {
698 case 'd':
699 where = BTRFS_SCAN_LBLKID;
700 break;
701 case 'm':
702 where = BTRFS_SCAN_MOUNTED;
703 break;
704 default:
705 usage(cmd_filesystem_show_usage);
709 if (check_argc_max(argc, optind + 1))
710 usage(cmd_filesystem_show_usage);
712 if (argc > optind) {
713 search = argv[optind];
714 if (*search == 0)
715 usage(cmd_filesystem_show_usage);
716 type = check_arg_type(search);
719 * For search is a device:
720 * realpath do /dev/mapper/XX => /dev/dm-X
721 * which is required by BTRFS_SCAN_DEV
722 * For search is a mountpoint:
723 * realpath do /mnt/btrfs/ => /mnt/btrfs
724 * which shall be recognized by btrfs_scan_kernel()
726 if (realpath(search, path))
727 search = path;
730 * Needs special handling if input arg is block dev And if
731 * input arg is mount-point just print it right away
733 if (type == BTRFS_ARG_BLKDEV && where != BTRFS_SCAN_LBLKID) {
734 ret = get_btrfs_mount(search, mp, sizeof(mp));
735 if (!ret) {
736 /* given block dev is mounted */
737 search = mp;
738 type = BTRFS_ARG_MNTPOINT;
739 } else {
740 ret = dev_to_fsid(search, fsid);
741 if (ret) {
742 error("no btrfs on %s", search);
743 return 1;
745 uuid_unparse(fsid, uuid_buf);
746 search = uuid_buf;
747 type = BTRFS_ARG_UUID;
748 goto devs_only;
753 if (where == BTRFS_SCAN_LBLKID)
754 goto devs_only;
756 /* show mounted btrfs */
757 ret = btrfs_scan_kernel(search, unit_mode);
758 if (search && !ret) {
759 /* since search is found we are done */
760 goto out;
763 /* shows mounted only */
764 if (where == BTRFS_SCAN_MOUNTED)
765 goto out;
767 devs_only:
768 ret = btrfs_scan_devices();
770 if (ret) {
771 error("blkid device scan returned %d", ret);
772 return 1;
775 ret = search_umounted_fs_uuids(&all_uuids, search, &found);
776 if (ret < 0) {
777 error("searching target device returned error %d", ret);
778 return 1;
782 * The seed/sprout mapping are not detected yet,
783 * do mapping build for all umounted fs
785 ret = map_seed_devices(&all_uuids);
786 if (ret) {
787 error("mapping seed devices returned error %d", ret);
788 return 1;
791 list_for_each_entry(fs_devices, &all_uuids, list)
792 print_one_uuid(fs_devices, unit_mode);
794 if (search && !found) {
795 error("not a valid btrfs filesystem: %s", search);
796 ret = 1;
798 while (!list_empty(&all_uuids)) {
799 fs_devices = list_entry(all_uuids.next,
800 struct btrfs_fs_devices, list);
801 free_fs_devices(fs_devices);
803 out:
804 free_seen_fsid(seen_fsid_hash);
805 return ret;
808 static const char * const cmd_filesystem_sync_usage[] = {
809 "btrfs filesystem sync <path>",
810 "Force a sync on a filesystem",
811 NULL
814 static int cmd_filesystem_sync(int argc, char **argv)
816 int fd, res;
817 char *path;
818 DIR *dirstream = NULL;
820 clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
822 if (check_argc_exact(argc - optind, 1))
823 usage(cmd_filesystem_sync_usage);
825 path = argv[optind];
827 fd = btrfs_open_dir(path, &dirstream, 1);
828 if (fd < 0)
829 return 1;
831 res = ioctl(fd, BTRFS_IOC_SYNC);
832 close_file_or_dir(fd, dirstream);
833 if( res < 0 ){
834 error("sync ioctl failed on '%s': %m", path);
835 return 1;
838 return 0;
841 static int parse_compress_type(char *s)
843 if (strcmp(optarg, "zlib") == 0)
844 return BTRFS_COMPRESS_ZLIB;
845 else if (strcmp(optarg, "lzo") == 0)
846 return BTRFS_COMPRESS_LZO;
847 else if (strcmp(optarg, "zstd") == 0)
848 return BTRFS_COMPRESS_ZSTD;
849 else {
850 error("unknown compression type %s", s);
851 exit(1);
855 static const char * const cmd_filesystem_defrag_usage[] = {
856 "btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
857 "Defragment a file or a directory",
859 "-v be verbose",
860 "-r defragment files recursively",
861 "-c[zlib,lzo,zstd] compress the file while defragmenting",
862 "-f flush data to disk immediately after defragmenting",
863 "-s start defragment only from byte onward",
864 "-l len defragment only up to len bytes",
865 "-t size target extent size hint (default: 32M)",
867 "Warning: most Linux kernels will break up the ref-links of COW data",
868 "(e.g., files copied with 'cp --reflink', snapshots) which may cause",
869 "considerable increase of space usage. See btrfs-filesystem(8) for",
870 "more information.",
871 NULL
874 static struct btrfs_ioctl_defrag_range_args defrag_global_range;
875 static int defrag_global_verbose;
876 static int defrag_global_errors;
877 static int defrag_callback(const char *fpath, const struct stat *sb,
878 int typeflag, struct FTW *ftwbuf)
880 int ret = 0;
881 int fd = 0;
883 if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) {
884 if (defrag_global_verbose)
885 printf("%s\n", fpath);
886 fd = open(fpath, O_RDWR);
887 if (fd < 0) {
888 goto error;
890 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
891 close(fd);
892 if (ret && errno == ENOTTY) {
893 error(
894 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
895 defrag_global_errors++;
896 return ENOTTY;
898 if (ret) {
899 goto error;
902 return 0;
904 error:
905 error("defrag failed on %s: %m", fpath);
906 defrag_global_errors++;
907 return 0;
910 static int cmd_filesystem_defrag(int argc, char **argv)
912 int fd;
913 int flush = 0;
914 u64 start = 0;
915 u64 len = (u64)-1;
916 u64 thresh;
917 int i;
918 int recursive = 0;
919 int ret = 0;
920 int compress_type = BTRFS_COMPRESS_NONE;
921 DIR *dirstream;
924 * Kernel has a different default (256K) that is supposed to be safe,
925 * but it does not defragment very well. The 32M will likely lead to
926 * better results and is independent of the kernel default. We have to
927 * use the v2 defrag ioctl.
929 thresh = SZ_32M;
931 defrag_global_errors = 0;
932 defrag_global_verbose = 0;
933 defrag_global_errors = 0;
934 while(1) {
935 int c = getopt(argc, argv, "vrc::fs:l:t:");
936 if (c < 0)
937 break;
939 switch(c) {
940 case 'c':
941 compress_type = BTRFS_COMPRESS_ZLIB;
942 if (optarg)
943 compress_type = parse_compress_type(optarg);
944 break;
945 case 'f':
946 flush = 1;
947 break;
948 case 'v':
949 defrag_global_verbose = 1;
950 break;
951 case 's':
952 start = parse_size(optarg);
953 break;
954 case 'l':
955 len = parse_size(optarg);
956 break;
957 case 't':
958 thresh = parse_size(optarg);
959 if (thresh > (u32)-1) {
960 warning(
961 "target extent size %llu too big, trimmed to %u",
962 thresh, (u32)-1);
963 thresh = (u32)-1;
965 break;
966 case 'r':
967 recursive = 1;
968 break;
969 default:
970 usage(cmd_filesystem_defrag_usage);
974 if (check_argc_min(argc - optind, 1))
975 usage(cmd_filesystem_defrag_usage);
977 memset(&defrag_global_range, 0, sizeof(defrag_global_range));
978 defrag_global_range.start = start;
979 defrag_global_range.len = len;
980 defrag_global_range.extent_thresh = (u32)thresh;
981 if (compress_type) {
982 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
983 defrag_global_range.compress_type = compress_type;
985 if (flush)
986 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
989 * Look for directory arguments and warn if the recursive mode is not
990 * requested, as this is not implemented as recursive defragmentation
991 * in kernel. The stat errors are silent here as we check them below.
993 if (!recursive) {
994 int found = 0;
996 for (i = optind; i < argc; i++) {
997 struct stat st;
999 if (stat(argv[i], &st))
1000 continue;
1002 if (S_ISDIR(st.st_mode)) {
1003 warning(
1004 "directory specified but recursive mode not requested: %s",
1005 argv[i]);
1006 found = 1;
1009 if (found) {
1010 warning(
1011 "a directory passed to the defrag ioctl will not process the files\n"
1012 "recursively but will defragment the subvolume tree and the extent tree.\n"
1013 "If this is not intended, please use option -r .");
1017 for (i = optind; i < argc; i++) {
1018 struct stat st;
1019 int defrag_err = 0;
1021 dirstream = NULL;
1022 fd = open_file_or_dir(argv[i], &dirstream);
1023 if (fd < 0) {
1024 error("cannot open %s: %m", argv[i]);
1025 ret = -errno;
1026 goto next;
1029 ret = fstat(fd, &st);
1030 if (ret) {
1031 error("failed to stat %s: %m", argv[i]);
1032 ret = -errno;
1033 goto next;
1035 if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
1036 error("%s is not a directory or a regular file",
1037 argv[i]);
1038 ret = -EINVAL;
1039 goto next;
1041 if (recursive && S_ISDIR(st.st_mode)) {
1042 ret = nftw(argv[i], defrag_callback, 10,
1043 FTW_MOUNT | FTW_PHYS);
1044 if (ret == ENOTTY)
1045 exit(1);
1046 /* errors are handled in the callback */
1047 ret = 0;
1048 } else {
1049 if (defrag_global_verbose)
1050 printf("%s\n", argv[i]);
1051 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE,
1052 &defrag_global_range);
1053 defrag_err = errno;
1054 if (ret && defrag_err == ENOTTY) {
1055 error(
1056 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
1057 defrag_global_errors++;
1058 close_file_or_dir(fd, dirstream);
1059 break;
1061 if (ret) {
1062 error("defrag failed on %s: %s", argv[i],
1063 strerror(defrag_err));
1064 goto next;
1067 next:
1068 if (ret)
1069 defrag_global_errors++;
1070 close_file_or_dir(fd, dirstream);
1073 if (defrag_global_errors)
1074 fprintf(stderr, "total %d failures\n", defrag_global_errors);
1076 return !!defrag_global_errors;
1079 static const char * const cmd_filesystem_resize_usage[] = {
1080 "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
1081 "Resize a filesystem",
1082 "If 'max' is passed, the filesystem will occupy all available space",
1083 "on the device 'devid'.",
1084 "[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
1085 NULL
1088 static int cmd_filesystem_resize(int argc, char **argv)
1090 struct btrfs_ioctl_vol_args args;
1091 int fd, res, len, e;
1092 char *amount, *path;
1093 DIR *dirstream = NULL;
1094 struct stat st;
1096 clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
1098 if (check_argc_exact(argc - optind, 2))
1099 usage(cmd_filesystem_resize_usage);
1101 amount = argv[optind];
1102 path = argv[optind + 1];
1104 len = strlen(amount);
1105 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
1106 error("resize value too long (%s)", amount);
1107 return 1;
1110 res = stat(path, &st);
1111 if (res < 0) {
1112 error("resize: cannot stat %s: %m", path);
1113 return 1;
1115 if (!S_ISDIR(st.st_mode)) {
1116 error("resize works on mounted filesystems and accepts only\n"
1117 "directories as argument. Passing file containing a btrfs image\n"
1118 "would resize the underlying filesystem instead of the image.\n");
1119 return 1;
1122 fd = btrfs_open_dir(path, &dirstream, 1);
1123 if (fd < 0)
1124 return 1;
1126 printf("Resize '%s' of '%s'\n", path, amount);
1127 memset(&args, 0, sizeof(args));
1128 strncpy_null(args.name, amount);
1129 res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
1130 e = errno;
1131 close_file_or_dir(fd, dirstream);
1132 if( res < 0 ){
1133 switch (e) {
1134 case EFBIG:
1135 error("unable to resize '%s': no enough free space",
1136 path);
1137 break;
1138 default:
1139 error("unable to resize '%s': %m", path);
1140 break;
1142 return 1;
1143 } else if (res > 0) {
1144 const char *err_str = btrfs_err_str(res);
1146 if (err_str) {
1147 error("resizing of '%s' failed: %s", path, err_str);
1148 } else {
1149 error("resizing of '%s' failed: unknown error %d",
1150 path, res);
1152 return 1;
1154 return 0;
1157 static const char * const cmd_filesystem_label_usage[] = {
1158 "btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
1159 "Get or change the label of a filesystem",
1160 "With one argument, get the label of filesystem on <device>.",
1161 "If <newlabel> is passed, set the filesystem label to <newlabel>.",
1162 NULL
1165 static int cmd_filesystem_label(int argc, char **argv)
1167 clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
1169 if (check_argc_min(argc - optind, 1) ||
1170 check_argc_max(argc - optind, 2))
1171 usage(cmd_filesystem_label_usage);
1173 if (argc - optind > 1) {
1174 return set_label(argv[optind], argv[optind + 1]);
1175 } else {
1176 char label[BTRFS_LABEL_SIZE];
1177 int ret;
1179 ret = get_label(argv[optind], label);
1180 if (!ret)
1181 fprintf(stdout, "%s\n", label);
1183 return ret;
1187 static const char filesystem_cmd_group_info[] =
1188 "overall filesystem tasks and information";
1190 const struct cmd_group filesystem_cmd_group = {
1191 filesystem_cmd_group_usage, filesystem_cmd_group_info, {
1192 { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 },
1193 { "du", cmd_filesystem_du, cmd_filesystem_du_usage, NULL, 0 },
1194 { "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
1195 0 },
1196 { "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
1197 0 },
1198 { "defragment", cmd_filesystem_defrag,
1199 cmd_filesystem_defrag_usage, NULL, 0 },
1200 { "balance", cmd_balance, NULL, &balance_cmd_group,
1201 CMD_HIDDEN },
1202 { "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
1203 NULL, 0 },
1204 { "label", cmd_filesystem_label, cmd_filesystem_label_usage,
1205 NULL, 0 },
1206 { "usage", cmd_filesystem_usage,
1207 cmd_filesystem_usage_usage, NULL, 0 },
1209 NULL_CMD_STRUCT
1213 int cmd_filesystem(int argc, char **argv)
1215 return handle_command_group(&filesystem_cmd_group, argc, argv);