btrfs-progs: docs: add note about mount option applicability
[btrfs-progs-unstable/devel.git] / cmds-filesystem.c
blob7728430f16a1d6d8cc6fab1f47b219db13e97ade
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 void free_fs_devices(struct btrfs_fs_devices *fs_devices)
436 struct btrfs_fs_devices *cur_seed, *next_seed;
437 struct btrfs_device *device;
439 while (!list_empty(&fs_devices->devices)) {
440 device = list_entry(fs_devices->devices.next,
441 struct btrfs_device, dev_list);
442 list_del(&device->dev_list);
444 free(device->name);
445 free(device->label);
446 free(device);
449 /* free seed fs chain */
450 cur_seed = fs_devices->seed;
451 fs_devices->seed = NULL;
452 while (cur_seed) {
453 next_seed = cur_seed->seed;
454 free(cur_seed);
456 cur_seed = next_seed;
459 list_del(&fs_devices->list);
460 free(fs_devices);
463 static int copy_device(struct btrfs_device *dst,
464 struct btrfs_device *src)
466 dst->devid = src->devid;
467 memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE);
468 if (src->name == NULL)
469 dst->name = NULL;
470 else {
471 dst->name = strdup(src->name);
472 if (!dst->name)
473 return -ENOMEM;
475 if (src->label == NULL)
476 dst->label = NULL;
477 else {
478 dst->label = strdup(src->label);
479 if (!dst->label) {
480 free(dst->name);
481 return -ENOMEM;
484 dst->total_devs = src->total_devs;
485 dst->super_bytes_used = src->super_bytes_used;
486 dst->total_bytes = src->total_bytes;
487 dst->bytes_used = src->bytes_used;
488 dst->generation = src->generation;
490 return 0;
493 static int copy_fs_devices(struct btrfs_fs_devices *dst,
494 struct btrfs_fs_devices *src)
496 struct btrfs_device *cur_dev, *dev_copy;
497 int ret = 0;
499 memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE);
500 INIT_LIST_HEAD(&dst->devices);
501 dst->seed = NULL;
503 list_for_each_entry(cur_dev, &src->devices, dev_list) {
504 dev_copy = malloc(sizeof(*dev_copy));
505 if (!dev_copy) {
506 ret = -ENOMEM;
507 break;
510 ret = copy_device(dev_copy, cur_dev);
511 if (ret) {
512 free(dev_copy);
513 break;
516 list_add(&dev_copy->dev_list, &dst->devices);
517 dev_copy->fs_devices = dst;
520 return ret;
523 static int find_and_copy_seed(struct btrfs_fs_devices *seed,
524 struct btrfs_fs_devices *copy,
525 struct list_head *fs_uuids) {
526 struct btrfs_fs_devices *cur_fs;
528 list_for_each_entry(cur_fs, fs_uuids, list)
529 if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE))
530 return copy_fs_devices(copy, cur_fs);
532 return 1;
535 static int has_seed_devices(struct btrfs_fs_devices *fs_devices)
537 struct btrfs_device *device;
538 int dev_cnt_total, dev_cnt = 0;
540 device = list_first_entry(&fs_devices->devices, struct btrfs_device,
541 dev_list);
543 dev_cnt_total = device->total_devs;
545 list_for_each_entry(device, &fs_devices->devices, dev_list)
546 dev_cnt++;
548 return dev_cnt_total != dev_cnt;
551 static int search_umounted_fs_uuids(struct list_head *all_uuids,
552 char *search, int *found)
554 struct btrfs_fs_devices *cur_fs, *fs_copy;
555 struct list_head *fs_uuids;
556 int ret = 0;
558 fs_uuids = btrfs_scanned_uuids();
561 * The fs_uuids list is global, and open_ctree_* will
562 * modify it, make a private copy here
564 list_for_each_entry(cur_fs, fs_uuids, list) {
565 /* don't bother handle all fs, if search target specified */
566 if (search) {
567 if (uuid_search(cur_fs, search) == 0)
568 continue;
569 if (found)
570 *found = 1;
573 /* skip all fs already shown as mounted fs */
574 if (is_seen_fsid(cur_fs->fsid, seen_fsid_hash))
575 continue;
577 fs_copy = calloc(1, sizeof(*fs_copy));
578 if (!fs_copy) {
579 ret = -ENOMEM;
580 goto out;
583 ret = copy_fs_devices(fs_copy, cur_fs);
584 if (ret) {
585 free(fs_copy);
586 goto out;
589 list_add(&fs_copy->list, all_uuids);
592 out:
593 return ret;
596 static int map_seed_devices(struct list_head *all_uuids)
598 struct btrfs_fs_devices *cur_fs, *cur_seed;
599 struct btrfs_fs_devices *seed_copy;
600 struct btrfs_fs_devices *opened_fs;
601 struct btrfs_device *device;
602 struct btrfs_fs_info *fs_info;
603 struct list_head *fs_uuids;
604 int ret = 0;
606 fs_uuids = btrfs_scanned_uuids();
608 list_for_each_entry(cur_fs, all_uuids, list) {
609 device = list_first_entry(&cur_fs->devices,
610 struct btrfs_device, dev_list);
611 if (!device)
612 continue;
614 /* skip fs without seeds */
615 if (!has_seed_devices(cur_fs))
616 continue;
619 * open_ctree_* detects seed/sprout mapping
621 fs_info = open_ctree_fs_info(device->name, 0, 0, 0,
622 OPEN_CTREE_PARTIAL);
623 if (!fs_info)
624 continue;
627 * copy the seed chain under the opened fs
629 opened_fs = fs_info->fs_devices;
630 cur_seed = cur_fs;
631 while (opened_fs->seed) {
632 seed_copy = malloc(sizeof(*seed_copy));
633 if (!seed_copy) {
634 ret = -ENOMEM;
635 goto fail_out;
637 ret = find_and_copy_seed(opened_fs->seed, seed_copy,
638 fs_uuids);
639 if (ret) {
640 free(seed_copy);
641 goto fail_out;
644 cur_seed->seed = seed_copy;
646 opened_fs = opened_fs->seed;
647 cur_seed = cur_seed->seed;
650 close_ctree(fs_info->chunk_root);
653 out:
654 return ret;
655 fail_out:
656 close_ctree(fs_info->chunk_root);
657 goto out;
660 static const char * const cmd_filesystem_show_usage[] = {
661 "btrfs filesystem show [options] [<path>|<uuid>|<device>|label]",
662 "Show the structure of a filesystem",
663 "-d|--all-devices show only disks under /dev containing btrfs filesystem",
664 "-m|--mounted show only mounted btrfs",
665 HELPINFO_UNITS_LONG,
666 "If no argument is given, structure of all present filesystems is shown.",
667 NULL
670 static int cmd_filesystem_show(int argc, char **argv)
672 LIST_HEAD(all_uuids);
673 struct btrfs_fs_devices *fs_devices;
674 char *search = NULL;
675 int ret;
676 /* default, search both kernel and udev */
677 int where = -1;
678 int type = 0;
679 char mp[PATH_MAX];
680 char path[PATH_MAX];
681 u8 fsid[BTRFS_FSID_SIZE];
682 char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
683 unsigned unit_mode;
684 int found = 0;
686 unit_mode = get_unit_mode_from_arg(&argc, argv, 0);
688 while (1) {
689 int c;
690 static const struct option long_options[] = {
691 { "all-devices", no_argument, NULL, 'd'},
692 { "mounted", no_argument, NULL, 'm'},
693 { NULL, 0, NULL, 0 }
696 c = getopt_long(argc, argv, "dm", long_options, NULL);
697 if (c < 0)
698 break;
699 switch (c) {
700 case 'd':
701 where = BTRFS_SCAN_LBLKID;
702 break;
703 case 'm':
704 where = BTRFS_SCAN_MOUNTED;
705 break;
706 default:
707 usage(cmd_filesystem_show_usage);
711 if (check_argc_max(argc, optind + 1))
712 usage(cmd_filesystem_show_usage);
714 if (argc > optind) {
715 search = argv[optind];
716 if (*search == 0)
717 usage(cmd_filesystem_show_usage);
718 type = check_arg_type(search);
721 * For search is a device:
722 * realpath do /dev/mapper/XX => /dev/dm-X
723 * which is required by BTRFS_SCAN_DEV
724 * For search is a mountpoint:
725 * realpath do /mnt/btrfs/ => /mnt/btrfs
726 * which shall be recognized by btrfs_scan_kernel()
728 if (realpath(search, path))
729 search = path;
732 * Needs special handling if input arg is block dev And if
733 * input arg is mount-point just print it right away
735 if (type == BTRFS_ARG_BLKDEV && where != BTRFS_SCAN_LBLKID) {
736 ret = get_btrfs_mount(search, mp, sizeof(mp));
737 if (!ret) {
738 /* given block dev is mounted */
739 search = mp;
740 type = BTRFS_ARG_MNTPOINT;
741 } else {
742 ret = dev_to_fsid(search, fsid);
743 if (ret) {
744 error("no btrfs on %s", search);
745 return 1;
747 uuid_unparse(fsid, uuid_buf);
748 search = uuid_buf;
749 type = BTRFS_ARG_UUID;
750 goto devs_only;
755 if (where == BTRFS_SCAN_LBLKID)
756 goto devs_only;
758 /* show mounted btrfs */
759 ret = btrfs_scan_kernel(search, unit_mode);
760 if (search && !ret) {
761 /* since search is found we are done */
762 goto out;
765 /* shows mounted only */
766 if (where == BTRFS_SCAN_MOUNTED)
767 goto out;
769 devs_only:
770 ret = btrfs_scan_devices();
772 if (ret) {
773 error("blkid device scan returned %d", ret);
774 return 1;
777 ret = search_umounted_fs_uuids(&all_uuids, search, &found);
778 if (ret < 0) {
779 error("searching target device returned error %d", ret);
780 return 1;
784 * The seed/sprout mapping are not detected yet,
785 * do mapping build for all umounted fs
787 ret = map_seed_devices(&all_uuids);
788 if (ret) {
789 error("mapping seed devices returned error %d", ret);
790 return 1;
793 list_for_each_entry(fs_devices, &all_uuids, list)
794 print_one_uuid(fs_devices, unit_mode);
796 if (search && !found) {
797 error("not a valid btrfs filesystem: %s", search);
798 ret = 1;
800 while (!list_empty(&all_uuids)) {
801 fs_devices = list_entry(all_uuids.next,
802 struct btrfs_fs_devices, list);
803 free_fs_devices(fs_devices);
805 out:
806 free_seen_fsid(seen_fsid_hash);
807 return ret;
810 static const char * const cmd_filesystem_sync_usage[] = {
811 "btrfs filesystem sync <path>",
812 "Force a sync on a filesystem",
813 NULL
816 static int cmd_filesystem_sync(int argc, char **argv)
818 int fd, res, e;
819 char *path;
820 DIR *dirstream = NULL;
822 clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
824 if (check_argc_exact(argc - optind, 1))
825 usage(cmd_filesystem_sync_usage);
827 path = argv[optind];
829 fd = btrfs_open_dir(path, &dirstream, 1);
830 if (fd < 0)
831 return 1;
833 res = ioctl(fd, BTRFS_IOC_SYNC);
834 e = errno;
835 close_file_or_dir(fd, dirstream);
836 if( res < 0 ){
837 error("sync ioctl failed on '%s': %s", path, strerror(e));
838 return 1;
841 return 0;
844 static int parse_compress_type(char *s)
846 if (strcmp(optarg, "zlib") == 0)
847 return BTRFS_COMPRESS_ZLIB;
848 else if (strcmp(optarg, "lzo") == 0)
849 return BTRFS_COMPRESS_LZO;
850 else if (strcmp(optarg, "zstd") == 0)
851 return BTRFS_COMPRESS_ZSTD;
852 else {
853 error("unknown compression type %s", s);
854 exit(1);
858 static const char * const cmd_filesystem_defrag_usage[] = {
859 "btrfs filesystem defragment [options] <file>|<dir> [<file>|<dir>...]",
860 "Defragment a file or a directory",
862 "-v be verbose",
863 "-r defragment files recursively",
864 "-c[zlib,lzo,zstd] compress the file while defragmenting",
865 "-f flush data to disk immediately after defragmenting",
866 "-s start defragment only from byte onward",
867 "-l len defragment only up to len bytes",
868 "-t size target extent size hint (default: 32M)",
870 "Warning: most Linux kernels will break up the ref-links of COW data",
871 "(e.g., files copied with 'cp --reflink', snapshots) which may cause",
872 "considerable increase of space usage. See btrfs-filesystem(8) for",
873 "more information.",
874 NULL
877 static struct btrfs_ioctl_defrag_range_args defrag_global_range;
878 static int defrag_global_verbose;
879 static int defrag_global_errors;
880 static int defrag_callback(const char *fpath, const struct stat *sb,
881 int typeflag, struct FTW *ftwbuf)
883 int ret = 0;
884 int err = 0;
885 int fd = 0;
887 if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) {
888 if (defrag_global_verbose)
889 printf("%s\n", fpath);
890 fd = open(fpath, O_RDWR);
891 if (fd < 0) {
892 err = errno;
893 goto error;
895 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &defrag_global_range);
896 close(fd);
897 if (ret && errno == ENOTTY) {
898 error(
899 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
900 defrag_global_errors++;
901 return ENOTTY;
903 if (ret) {
904 err = errno;
905 goto error;
908 return 0;
910 error:
911 error("defrag failed on %s: %s", fpath, strerror(err));
912 defrag_global_errors++;
913 return 0;
916 static int cmd_filesystem_defrag(int argc, char **argv)
918 int fd;
919 int flush = 0;
920 u64 start = 0;
921 u64 len = (u64)-1;
922 u64 thresh;
923 int i;
924 int recursive = 0;
925 int ret = 0;
926 int compress_type = BTRFS_COMPRESS_NONE;
927 DIR *dirstream;
930 * Kernel has a different default (256K) that is supposed to be safe,
931 * but it does not defragment very well. The 32M will likely lead to
932 * better results and is independent of the kernel default. We have to
933 * use the v2 defrag ioctl.
935 thresh = SZ_32M;
937 defrag_global_errors = 0;
938 defrag_global_verbose = 0;
939 defrag_global_errors = 0;
940 while(1) {
941 int c = getopt(argc, argv, "vrc::fs:l:t:");
942 if (c < 0)
943 break;
945 switch(c) {
946 case 'c':
947 compress_type = BTRFS_COMPRESS_ZLIB;
948 if (optarg)
949 compress_type = parse_compress_type(optarg);
950 break;
951 case 'f':
952 flush = 1;
953 break;
954 case 'v':
955 defrag_global_verbose = 1;
956 break;
957 case 's':
958 start = parse_size(optarg);
959 break;
960 case 'l':
961 len = parse_size(optarg);
962 break;
963 case 't':
964 thresh = parse_size(optarg);
965 if (thresh > (u32)-1) {
966 warning(
967 "target extent size %llu too big, trimmed to %u",
968 thresh, (u32)-1);
969 thresh = (u32)-1;
971 break;
972 case 'r':
973 recursive = 1;
974 break;
975 default:
976 usage(cmd_filesystem_defrag_usage);
980 if (check_argc_min(argc - optind, 1))
981 usage(cmd_filesystem_defrag_usage);
983 memset(&defrag_global_range, 0, sizeof(defrag_global_range));
984 defrag_global_range.start = start;
985 defrag_global_range.len = len;
986 defrag_global_range.extent_thresh = (u32)thresh;
987 if (compress_type) {
988 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
989 defrag_global_range.compress_type = compress_type;
991 if (flush)
992 defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
995 * Look for directory arguments and warn if the recursive mode is not
996 * requested, as this is not implemented as recursive defragmentation
997 * in kernel. The stat errors are silent here as we check them below.
999 if (!recursive) {
1000 int found = 0;
1002 for (i = optind; i < argc; i++) {
1003 struct stat st;
1005 if (stat(argv[i], &st))
1006 continue;
1008 if (S_ISDIR(st.st_mode)) {
1009 warning(
1010 "directory specified but recursive mode not requested: %s",
1011 argv[i]);
1012 found = 1;
1015 if (found) {
1016 warning(
1017 "a directory passed to the defrag ioctl will not process the files\n"
1018 "recursively but will defragment the subvolume tree and the extent tree.\n"
1019 "If this is not intended, please use option -r .");
1023 for (i = optind; i < argc; i++) {
1024 struct stat st;
1025 int defrag_err = 0;
1027 dirstream = NULL;
1028 fd = open_file_or_dir(argv[i], &dirstream);
1029 if (fd < 0) {
1030 error("cannot open %s: %s", argv[i],
1031 strerror(errno));
1032 defrag_global_errors++;
1033 close_file_or_dir(fd, dirstream);
1034 continue;
1036 if (fstat(fd, &st)) {
1037 error("failed to stat %s: %s",
1038 argv[i], strerror(errno));
1039 defrag_global_errors++;
1040 close_file_or_dir(fd, dirstream);
1041 continue;
1043 if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
1044 error("%s is not a directory or a regular file",
1045 argv[i]);
1046 defrag_global_errors++;
1047 close_file_or_dir(fd, dirstream);
1048 continue;
1050 if (recursive && S_ISDIR(st.st_mode)) {
1051 ret = nftw(argv[i], defrag_callback, 10,
1052 FTW_MOUNT | FTW_PHYS);
1053 if (ret == ENOTTY)
1054 exit(1);
1055 /* errors are handled in the callback */
1056 ret = 0;
1057 } else {
1058 if (defrag_global_verbose)
1059 printf("%s\n", argv[i]);
1060 ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE,
1061 &defrag_global_range);
1062 defrag_err = errno;
1064 close_file_or_dir(fd, dirstream);
1065 if (ret && defrag_err == ENOTTY) {
1066 error(
1067 "defrag range ioctl not supported in this kernel version, 2.6.33 and newer is required");
1068 defrag_global_errors++;
1069 break;
1071 if (ret) {
1072 error("defrag failed on %s: %s", argv[i],
1073 strerror(defrag_err));
1074 defrag_global_errors++;
1077 if (defrag_global_errors)
1078 fprintf(stderr, "total %d failures\n", defrag_global_errors);
1080 return !!defrag_global_errors;
1083 static const char * const cmd_filesystem_resize_usage[] = {
1084 "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
1085 "Resize a filesystem",
1086 "If 'max' is passed, the filesystem will occupy all available space",
1087 "on the device 'devid'.",
1088 "[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
1089 NULL
1092 static int cmd_filesystem_resize(int argc, char **argv)
1094 struct btrfs_ioctl_vol_args args;
1095 int fd, res, len, e;
1096 char *amount, *path;
1097 DIR *dirstream = NULL;
1098 struct stat st;
1100 clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
1102 if (check_argc_exact(argc - optind, 2))
1103 usage(cmd_filesystem_resize_usage);
1105 amount = argv[optind];
1106 path = argv[optind + 1];
1108 len = strlen(amount);
1109 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
1110 error("resize value too long (%s)", amount);
1111 return 1;
1114 res = stat(path, &st);
1115 if (res < 0) {
1116 error("resize: cannot stat %s: %s", path, strerror(errno));
1117 return 1;
1119 if (!S_ISDIR(st.st_mode)) {
1120 error("resize works on mounted filesystems and accepts only\n"
1121 "directories as argument. Passing file containing a btrfs image\n"
1122 "would resize the underlying filesystem instead of the image.\n");
1123 return 1;
1126 fd = btrfs_open_dir(path, &dirstream, 1);
1127 if (fd < 0)
1128 return 1;
1130 printf("Resize '%s' of '%s'\n", path, amount);
1131 memset(&args, 0, sizeof(args));
1132 strncpy_null(args.name, amount);
1133 res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
1134 e = errno;
1135 close_file_or_dir(fd, dirstream);
1136 if( res < 0 ){
1137 switch (e) {
1138 case EFBIG:
1139 error("unable to resize '%s': no enough free space",
1140 path);
1141 break;
1142 default:
1143 error("unable to resize '%s': %s", path, strerror(e));
1144 break;
1146 return 1;
1147 } else if (res > 0) {
1148 const char *err_str = btrfs_err_str(res);
1150 if (err_str) {
1151 error("resizing of '%s' failed: %s", path, err_str);
1152 } else {
1153 error("resizing of '%s' failed: unknown error %d",
1154 path, res);
1156 return 1;
1158 return 0;
1161 static const char * const cmd_filesystem_label_usage[] = {
1162 "btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
1163 "Get or change the label of a filesystem",
1164 "With one argument, get the label of filesystem on <device>.",
1165 "If <newlabel> is passed, set the filesystem label to <newlabel>.",
1166 NULL
1169 static int cmd_filesystem_label(int argc, char **argv)
1171 clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
1173 if (check_argc_min(argc - optind, 1) ||
1174 check_argc_max(argc - optind, 2))
1175 usage(cmd_filesystem_label_usage);
1177 if (argc - optind > 1) {
1178 return set_label(argv[optind], argv[optind + 1]);
1179 } else {
1180 char label[BTRFS_LABEL_SIZE];
1181 int ret;
1183 ret = get_label(argv[optind], label);
1184 if (!ret)
1185 fprintf(stdout, "%s\n", label);
1187 return ret;
1191 static const char filesystem_cmd_group_info[] =
1192 "overall filesystem tasks and information";
1194 const struct cmd_group filesystem_cmd_group = {
1195 filesystem_cmd_group_usage, filesystem_cmd_group_info, {
1196 { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 },
1197 { "du", cmd_filesystem_du, cmd_filesystem_du_usage, NULL, 0 },
1198 { "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
1199 0 },
1200 { "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
1201 0 },
1202 { "defragment", cmd_filesystem_defrag,
1203 cmd_filesystem_defrag_usage, NULL, 0 },
1204 { "balance", cmd_balance, NULL, &balance_cmd_group,
1205 CMD_HIDDEN },
1206 { "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
1207 NULL, 0 },
1208 { "label", cmd_filesystem_label, cmd_filesystem_label_usage,
1209 NULL, 0 },
1210 { "usage", cmd_filesystem_usage,
1211 cmd_filesystem_usage_usage, NULL, 0 },
1213 NULL_CMD_STRUCT
1217 int cmd_filesystem(int argc, char **argv)
1219 return handle_command_group(&filesystem_cmd_group, argc, argv);