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.
21 #include <sys/ioctl.h>
28 #include <uuid/uuid.h>
29 #include <linux/magic.h>
31 #include "kerncompat.h"
38 #include "btrfs-list.h"
42 static int is_subvolume_cleaned(int fd, u64 subvolid)
45 struct btrfs_ioctl_search_args args;
46 struct btrfs_ioctl_search_key *sk = &args.key;
48 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
49 sk->min_objectid = subvolid;
50 sk->max_objectid = subvolid;
51 sk->min_type = BTRFS_ROOT_ITEM_KEY;
52 sk->max_type = BTRFS_ROOT_ITEM_KEY;
54 sk->max_offset = (u64)-1;
56 sk->max_transid = (u64)-1;
59 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
63 if (sk->nr_items == 0)
69 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
78 for (i = 0; i < count; i++) {
81 ret = is_subvolume_cleaned(fd, ids[i]);
84 "cannot read status of dead subvolume %llu: %s",
85 (unsigned long long)ids[i], strerror(-ret));
89 printf("Subvolume id %llu is gone\n", ids[i]);
97 sleep(sleep_interval);
103 static const char * const subvolume_cmd_group_usage[] = {
104 "btrfs subvolume <command> <args>",
108 static const char * const cmd_subvol_create_usage[] = {
109 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
110 "Create a subvolume",
111 "Create a subvolume <name> in <dest>. If <dest> is not given",
112 "subvolume <name> will be created in the current directory.",
114 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
115 " option can be given multiple times.",
119 static int cmd_subvol_create(int argc, char **argv)
121 int retval, res, len;
123 char *dupname = NULL;
128 struct btrfs_qgroup_inherit *inherit = NULL;
129 DIR *dirstream = NULL;
132 int c = getopt(argc, argv, "c:i:");
138 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
145 res = qgroup_inherit_add_group(&inherit, optarg);
152 usage(cmd_subvol_create_usage);
156 if (check_argc_exact(argc - optind, 1))
157 usage(cmd_subvol_create_usage);
161 retval = 1; /* failure */
162 res = test_isdir(dst);
163 if (res < 0 && res != -ENOENT) {
164 error("cannot access %s: %s", dst, strerror(-res));
168 error("target path already exists: %s", dst);
172 dupname = strdup(dst);
173 newname = basename(dupname);
174 dupdir = strdup(dst);
175 dstdir = dirname(dupdir);
177 if (!test_issubvolname(newname)) {
178 error("invalid subvolume name: %s", newname);
182 len = strlen(newname);
183 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
184 error("subvolume name too long: %s", newname);
188 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
192 printf("Create subvolume '%s/%s'\n", dstdir, newname);
194 struct btrfs_ioctl_vol_args_v2 args;
196 memset(&args, 0, sizeof(args));
197 strncpy_null(args.name, newname);
198 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
199 args.size = qgroup_inherit_size(inherit);
200 args.qgroup_inherit = inherit;
202 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
204 struct btrfs_ioctl_vol_args args;
206 memset(&args, 0, sizeof(args));
207 strncpy_null(args.name, newname);
209 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
213 error("cannot create subvolume: %s", strerror(errno));
217 retval = 0; /* success */
219 close_file_or_dir(fddst, dirstream);
227 static int wait_for_commit(int fd)
231 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
234 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
237 static const char * const cmd_subvol_delete_usage[] = {
238 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
239 "Delete subvolume(s)",
240 "Delete subvolumes from the filesystem. The corresponding directory",
241 "is removed instantly but the data blocks are removed later.",
242 "The deletion does not involve full commit by default due to",
243 "performance reasons (as a consequence, the subvolume may appear again",
244 "after a crash). Use one of the --commit options to wait until the",
245 "operation is safely stored on the media.",
247 "-c|--commit-after wait for transaction commit at the end of the operation",
248 "-C|--commit-each wait for transaction commit after deleting each subvolume",
249 "-v|--verbose verbose output of operations",
253 static int cmd_subvol_delete(int argc, char **argv)
258 struct btrfs_ioctl_vol_args args;
259 char *dname, *vname, *cpath;
260 char *dupdname = NULL;
261 char *dupvname = NULL;
263 DIR *dirstream = NULL;
266 u8 fsid[BTRFS_FSID_SIZE];
267 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
268 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
269 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
273 static const struct option long_options[] = {
274 {"commit-after", no_argument, NULL, 'c'},
275 {"commit-each", no_argument, NULL, 'C'},
276 {"verbose", no_argument, NULL, 'v'},
280 c = getopt_long(argc, argv, "cCv", long_options, NULL);
286 commit_mode = COMMIT_AFTER;
289 commit_mode = COMMIT_EACH;
295 usage(cmd_subvol_delete_usage);
299 if (check_argc_min(argc - optind, 1))
300 usage(cmd_subvol_delete_usage);
303 printf("Transaction commit: %s\n",
304 !commit_mode ? "none (default)" :
305 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
313 res = test_issubvolume(path);
315 error("cannot access subvolume %s: %s", path, strerror(-res));
320 error("not a subvolume: %s", path);
325 cpath = realpath(path, NULL);
328 error("cannot find real path for '%s': %s",
329 path, strerror(errno));
332 dupdname = strdup(cpath);
333 dname = dirname(dupdname);
334 dupvname = strdup(cpath);
335 vname = basename(dupvname);
338 fd = btrfs_open_dir(dname, &dirstream, 1);
344 printf("Delete subvolume (%s): '%s/%s'\n",
345 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
346 ? "commit" : "no-commit", dname, vname);
347 memset(&args, 0, sizeof(args));
348 strncpy_null(args.name, vname);
349 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
351 error("cannot delete '%s/%s': %s", dname, vname,
357 if (commit_mode == COMMIT_EACH) {
358 res = wait_for_commit(fd);
360 error("unable to wait for commit after '%s': %s",
361 path, strerror(errno));
364 } else if (commit_mode == COMMIT_AFTER) {
365 res = get_fsid(dname, fsid, 0);
367 error("unable to get fsid for '%s': %s",
368 path, strerror(-res));
370 "delete suceeded but commit may not be done in the end");
375 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
377 uuid_unparse(fsid, uuidbuf);
378 printf(" new fs is found for '%s', fsid: %s\n",
382 * This is the first time a subvolume on this
383 * filesystem is deleted, keep fd in order to issue
384 * SYNC ioctl in the end
391 close_file_or_dir(fd, dirstream);
403 if (commit_mode == COMMIT_AFTER) {
407 * Traverse seen_fsid_hash and issue SYNC ioctl on each
410 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
411 struct seen_fsid *seen = seen_fsid_hash[slot];
414 res = wait_for_commit(seen->fd);
416 uuid_unparse(seen->fsid, uuidbuf);
418 "unable to do final sync after deletion: %s, fsid: %s",
419 strerror(errno), uuidbuf);
421 } else if (verbose > 0) {
422 uuid_unparse(seen->fsid, uuidbuf);
423 printf("final sync is done for fsid: %s\n",
429 /* fd will also be closed in free_seen_fsid */
430 free_seen_fsid(seen_fsid_hash);
438 * - uppercase for filters and sort options
439 * - lowercase for enabling specific items in the output
441 static const char * const cmd_subvol_list_usage[] = {
442 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
443 "[--sort=gen,ogen,rootid,path] <path>",
444 "List subvolumes (and snapshots)",
446 "-p print parent ID",
447 "-a print all the subvolumes in the filesystem and",
448 " distinguish absolute and relative path with respect",
449 " to the given <path>",
450 "-c print the ogeneration of the subvolume",
451 "-g print the generation of the subvolume",
452 "-o print only subvolumes below specified path",
453 "-u print the uuid of subvolumes (and snapshots)",
454 "-q print the parent uuid of the snapshots",
455 "-R print the uuid of the received snapshots",
456 "-t print the result as a table",
457 "-s list snapshots only in the filesystem",
458 "-r list readonly subvolumes (including snapshots)",
459 "-d list deleted subvolumes that are not yet cleaned",
461 " filter the subvolumes by generation",
462 " (+value: >= value; -value: <= value; value: = value)",
464 " filter the subvolumes by ogeneration",
465 " (+value: >= value; -value: <= value; value: = value)",
466 "--sort=gen,ogen,rootid,path",
467 " list the subvolume in order of gen, ogen, rootid or path",
468 " you also can add '+' or '-' in front of each items.",
469 " (+:ascending, -:descending, ascending default)",
473 static int cmd_subvol_list(int argc, char **argv)
475 struct btrfs_list_filter_set *filter_set;
476 struct btrfs_list_comparer_set *comparer_set;
480 int ret = -1, uerr = 0;
483 int is_only_in_path = 0;
484 DIR *dirstream = NULL;
485 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
487 filter_set = btrfs_list_alloc_filter_set();
488 comparer_set = btrfs_list_alloc_comparer_set();
492 static const struct option long_options[] = {
493 {"sort", required_argument, NULL, 'S'},
497 c = getopt_long(argc, argv,
498 "acdgopqsurRG:C:t", long_options, NULL);
504 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
510 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
513 btrfs_list_setup_filter(&filter_set,
514 BTRFS_LIST_FILTER_DELETED,
518 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
524 layout = BTRFS_LIST_LAYOUT_TABLE;
527 btrfs_list_setup_filter(&filter_set,
528 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
530 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
531 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
534 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
537 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
540 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
543 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
546 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
547 ret = btrfs_list_parse_filter_string(optarg,
549 BTRFS_LIST_FILTER_GEN);
557 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
558 ret = btrfs_list_parse_filter_string(optarg,
560 BTRFS_LIST_FILTER_CGEN);
567 ret = btrfs_list_parse_sort_string(optarg,
581 if (check_argc_exact(argc - optind, 1)) {
586 subvol = argv[optind];
587 fd = btrfs_open_dir(subvol, &dirstream, 1);
590 error("can't access '%s'", subvol);
595 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
598 ret = btrfs_list_get_path_rootid(fd, &top_id);
603 btrfs_list_setup_filter(&filter_set,
604 BTRFS_LIST_FILTER_FULL_PATH,
606 else if (is_only_in_path)
607 btrfs_list_setup_filter(&filter_set,
608 BTRFS_LIST_FILTER_TOPID_EQUAL,
611 /* by default we shall print the following columns*/
612 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
613 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
614 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
615 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
617 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
618 layout, !is_list_all && !is_only_in_path, NULL);
621 close_file_or_dir(fd, dirstream);
627 usage(cmd_subvol_list_usage);
631 static const char * const cmd_subvol_snapshot_usage[] = {
632 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
633 "Create a snapshot of the subvolume",
634 "Create a writable/readonly snapshot of the subvolume <source> with",
635 "the name <name> in the <dest> directory. If only <dest> is given,",
636 "the subvolume will be named the basename of <source>.",
638 "-r create a readonly snapshot",
639 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
640 " option can be given multiple times.",
644 static int cmd_subvol_snapshot(int argc, char **argv)
648 int fd = -1, fddst = -1;
649 int len, readonly = 0;
650 char *dupname = NULL;
654 struct btrfs_ioctl_vol_args_v2 args;
655 struct btrfs_qgroup_inherit *inherit = NULL;
656 DIR *dirstream1 = NULL, *dirstream2 = NULL;
658 memset(&args, 0, sizeof(args));
660 int c = getopt(argc, argv, "c:i:r");
666 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
673 res = qgroup_inherit_add_group(&inherit, optarg);
683 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
690 usage(cmd_subvol_snapshot_usage);
694 if (check_argc_exact(argc - optind, 2))
695 usage(cmd_subvol_snapshot_usage);
697 subvol = argv[optind];
698 dst = argv[optind + 1];
700 retval = 1; /* failure */
701 res = test_issubvolume(subvol);
703 error("cannot access subvolume %s: %s", subvol, strerror(-res));
707 error("not a subvolume: %s", subvol);
711 res = test_isdir(dst);
712 if (res < 0 && res != -ENOENT) {
713 error("cannot access %s: %s", dst, strerror(-res));
717 error("'%s' exists and it is not a directory", dst);
722 dupname = strdup(subvol);
723 newname = basename(dupname);
726 dupname = strdup(dst);
727 newname = basename(dupname);
728 dupdir = strdup(dst);
729 dstdir = dirname(dupdir);
732 if (!test_issubvolname(newname)) {
733 error("invalid snapshot name '%s'", newname);
737 len = strlen(newname);
738 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
739 error("snapshot name too long '%s'", newname);
743 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
747 fd = btrfs_open_dir(subvol, &dirstream2, 1);
752 args.flags |= BTRFS_SUBVOL_RDONLY;
753 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
754 subvol, dstdir, newname);
756 printf("Create a snapshot of '%s' in '%s/%s'\n",
757 subvol, dstdir, newname);
762 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
763 args.size = qgroup_inherit_size(inherit);
764 args.qgroup_inherit = inherit;
766 strncpy_null(args.name, newname);
768 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
771 error("cannot snapshot '%s': %s", subvol, strerror(errno));
775 retval = 0; /* success */
778 close_file_or_dir(fddst, dirstream1);
779 close_file_or_dir(fd, dirstream2);
787 static const char * const cmd_subvol_get_default_usage[] = {
788 "btrfs subvolume get-default <path>",
789 "Get the default subvolume of a filesystem",
793 static int cmd_subvol_get_default(int argc, char **argv)
798 struct btrfs_list_filter_set *filter_set;
800 DIR *dirstream = NULL;
802 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
804 if (check_argc_exact(argc - optind, 1))
805 usage(cmd_subvol_get_default_usage);
808 fd = btrfs_open_dir(subvol, &dirstream, 1);
812 ret = btrfs_list_get_default_subvolume(fd, &default_id);
814 error("failed to look up default subvolume: %s",
820 if (default_id == 0) {
821 error("'default' dir item not found");
825 /* no need to resolve roots if FS_TREE is default */
826 if (default_id == BTRFS_FS_TREE_OBJECTID) {
827 printf("ID 5 (FS_TREE)\n");
832 filter_set = btrfs_list_alloc_filter_set();
833 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
836 /* by default we shall print the following columns*/
837 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
838 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
839 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
840 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
842 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
843 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
848 close_file_or_dir(fd, dirstream);
852 static const char * const cmd_subvol_set_default_usage[] = {
853 "btrfs subvolume set-default <subvolid> <path>",
854 "Set the default subvolume of a filesystem",
858 static int cmd_subvol_set_default(int argc, char **argv)
864 DIR *dirstream = NULL;
866 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
868 if (check_argc_exact(argc - optind, 2))
869 usage(cmd_subvol_set_default_usage);
871 subvolid = argv[optind];
872 path = argv[optind + 1];
874 objectid = arg_strtou64(subvolid);
876 fd = btrfs_open_dir(path, &dirstream, 1);
880 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
882 close_file_or_dir(fd, dirstream);
884 error("unable to set a new default subvolume: %s",
891 static const char * const cmd_subvol_find_new_usage[] = {
892 "btrfs subvolume find-new <path> <lastgen>",
893 "List the recently modified files in a filesystem",
897 static int cmd_subvol_find_new(int argc, char **argv)
903 DIR *dirstream = NULL;
905 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
907 if (check_argc_exact(argc - optind, 2))
908 usage(cmd_subvol_find_new_usage);
910 subvol = argv[optind];
911 last_gen = arg_strtou64(argv[optind + 1]);
913 ret = test_issubvolume(subvol);
915 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
919 error("not a subvolume: %s", subvol);
923 fd = btrfs_open_dir(subvol, &dirstream, 1);
927 ret = ioctl(fd, BTRFS_IOC_SYNC);
929 error("sync ioctl failed on '%s': %s",
930 subvol, strerror(errno));
931 close_file_or_dir(fd, dirstream);
935 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
936 close_file_or_dir(fd, dirstream);
940 static const char * const cmd_subvol_show_usage[] = {
941 "btrfs subvolume show [options] <subvol-path>|<mnt>",
942 "Show more information about the subvolume",
943 "-r|--rootid rootid of the subvolume",
944 "-u|--uuid uuid of the subvolume",
946 "If no option is specified, <subvol-path> will be shown, otherwise",
947 "the rootid or uuid are resolved relative to the <mnt> path.",
951 static int cmd_subvol_show(int argc, char **argv)
953 struct root_info get_ri;
954 struct btrfs_list_filter_set *filter_set = NULL;
956 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
957 char *fullpath = NULL;
958 char raw_prefix[] = "\t\t\t\t";
961 DIR *dirstream1 = NULL;
965 u8 uuid_arg[BTRFS_UUID_SIZE];
969 static const struct option long_options[] = {
970 { "rootid", required_argument, NULL, 'r'},
971 { "uuid", required_argument, NULL, 'u'},
975 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
981 rootid_arg = arg_strtou64(optarg);
985 uuid_parse(optarg, uuid_arg);
989 usage(cmd_subvol_show_usage);
993 if (check_argc_exact(argc - optind, 1))
994 usage(cmd_subvol_show_usage);
996 if (by_rootid && by_uuid) {
998 "options --rootid and --uuid cannot be used at the same time");
999 usage(cmd_subvol_show_usage);
1002 memset(&get_ri, 0, sizeof(get_ri));
1003 fullpath = realpath(argv[optind], NULL);
1005 error("cannot find real path for '%s': %s",
1006 argv[optind], strerror(errno));
1011 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1012 } else if (by_uuid) {
1013 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1015 ret = get_subvol_info(fullpath, &get_ri);
1020 error("Failed to get subvol info %s: %s",
1021 fullpath, strerror(-ret));
1023 error("Failed to get subvol info %s: %d",
1029 /* print the info */
1030 printf("%s\n", get_ri.full_path);
1031 printf("\tName: \t\t\t%s\n", get_ri.name);
1033 if (uuid_is_null(get_ri.uuid))
1034 strcpy(uuidparse, "-");
1036 uuid_unparse(get_ri.uuid, uuidparse);
1037 printf("\tUUID: \t\t\t%s\n", uuidparse);
1039 if (uuid_is_null(get_ri.puuid))
1040 strcpy(uuidparse, "-");
1042 uuid_unparse(get_ri.puuid, uuidparse);
1043 printf("\tParent UUID: \t\t%s\n", uuidparse);
1045 if (uuid_is_null(get_ri.ruuid))
1046 strcpy(uuidparse, "-");
1048 uuid_unparse(get_ri.ruuid, uuidparse);
1049 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1054 localtime_r(&get_ri.otime, &tm);
1055 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1058 printf("\tCreation time: \t\t%s\n", tstr);
1060 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1061 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1062 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1063 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1064 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1066 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1067 printf("\tFlags: \t\t\treadonly\n");
1069 printf("\tFlags: \t\t\t-\n");
1071 /* print the snapshots of the given subvol if any*/
1072 printf("\tSnapshot(s):\n");
1073 filter_set = btrfs_list_alloc_filter_set();
1074 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1075 (u64)(unsigned long)get_ri.uuid);
1076 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1078 fd = open_file_or_dir(fullpath, &dirstream1);
1080 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1083 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1090 free(get_ri.full_path);
1093 close_file_or_dir(fd, dirstream1);
1098 static const char * const cmd_subvol_sync_usage[] = {
1099 "btrfs subvolume sync <path> [<subvol-id>...]",
1100 "Wait until given subvolume(s) are completely removed from the filesystem.",
1101 "Wait until given subvolume(s) are completely removed from the filesystem",
1103 "If no subvolume id is given, wait until all current deletion requests",
1104 "are completed, but do not wait for subvolumes deleted meanwhile.",
1105 "The status of subvolume ids is checked periodically.",
1107 "-s <N> sleep N seconds between checks (default: 1)",
1113 * If we're looking for any dead subvolume, take a shortcut and look
1114 * for any ORPHAN_ITEMs in the tree root
1116 static int fs_has_dead_subvolumes(int fd)
1119 struct btrfs_ioctl_search_args args;
1120 struct btrfs_ioctl_search_key *sk = &args.key;
1121 struct btrfs_ioctl_search_header sh;
1122 u64 min_subvolid = 0;
1125 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1126 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1127 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1128 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1129 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1130 sk->min_offset = min_subvolid;
1131 sk->max_offset = (u64)-1;
1132 sk->min_transid = 0;
1133 sk->max_transid = (u64)-1;
1136 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1143 memcpy(&sh, args.buf, sizeof(sh));
1144 min_subvolid = sh.offset;
1147 * Verify that the root item is really there and we haven't hit
1150 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1151 sk->min_objectid = min_subvolid;
1152 sk->max_objectid = min_subvolid;
1153 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1154 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1156 sk->max_offset = (u64)-1;
1157 sk->min_transid = 0;
1158 sk->max_transid = (u64)-1;
1161 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1166 * Stale orphan, try the next one
1168 if (!sk->nr_items) {
1177 #define SUBVOL_ID_BATCH 1024
1180 * Enumerate all dead subvolumes that exist in the filesystem.
1181 * Fill @ids and reallocate to bigger size if needed.
1183 static int enumerate_dead_subvols(int fd, u64 **ids)
1186 struct btrfs_ioctl_search_args args;
1187 struct btrfs_ioctl_search_key *sk = &args.key;
1191 memset(&args, 0, sizeof(args));
1193 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1194 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1195 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1196 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1197 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1199 sk->max_offset = (u64)-1;
1200 sk->min_transid = 0;
1201 sk->max_transid = (u64)-1;
1202 sk->nr_items = 4096;
1206 struct btrfs_ioctl_search_header *sh;
1210 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1218 for (i = 0; i < sk->nr_items; i++) {
1219 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1222 if (btrfs_search_header_type(sh)
1223 == BTRFS_ORPHAN_ITEM_KEY) {
1227 count += SUBVOL_ID_BATCH;
1228 newids = (u64*)realloc(*ids,
1229 count * sizeof(u64));
1234 (*ids)[idx] = btrfs_search_header_offset(sh);
1237 off += btrfs_search_header_len(sh);
1239 sk->min_objectid = btrfs_search_header_objectid(sh);
1240 sk->min_type = btrfs_search_header_type(sh);
1241 sk->min_offset = btrfs_search_header_offset(sh);
1243 if (sk->min_offset < (u64)-1)
1247 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1249 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1256 static int cmd_subvol_sync(int argc, char **argv)
1261 DIR *dirstream = NULL;
1264 int sleep_interval = 1;
1267 int c = getopt(argc, argv, "s:");
1274 sleep_interval = atoi(optarg);
1275 if (sleep_interval < 1) {
1276 error("invalid sleep interval %s", optarg);
1282 usage(cmd_subvol_sync_usage);
1286 if (check_argc_min(argc - optind, 1))
1287 usage(cmd_subvol_sync_usage);
1289 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1296 id_count = argc - optind;
1298 id_count = enumerate_dead_subvols(fd, &ids);
1300 error("can't enumerate dead subvolumes: %s",
1301 strerror(-id_count));
1305 if (id_count == 0) {
1310 ids = (u64*)malloc(id_count * sizeof(u64));
1312 error("not enough memory");
1317 for (i = 0; i < id_count; i++) {
1321 arg = argv[optind + i];
1323 id = strtoull(arg, NULL, 10);
1325 error("unrecognized subvolume id %s", arg);
1329 if (id < BTRFS_FIRST_FREE_OBJECTID
1330 || id > BTRFS_LAST_FREE_OBJECTID) {
1331 error("subvolume id %s out of range", arg);
1339 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1343 close_file_or_dir(fd, dirstream);
1348 static const char subvolume_cmd_group_info[] =
1349 "manage subvolumes: create, delete, list, etc";
1351 const struct cmd_group subvolume_cmd_group = {
1352 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1353 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1354 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1355 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1356 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1358 { "get-default", cmd_subvol_get_default,
1359 cmd_subvol_get_default_usage, NULL, 0 },
1360 { "set-default", cmd_subvol_set_default,
1361 cmd_subvol_set_default_usage, NULL, 0 },
1362 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1364 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1365 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1370 int cmd_subvolume(int argc, char **argv)
1372 return handle_command_group(&subvolume_cmd_group, argc, argv);