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.
22 #include <sys/ioctl.h>
29 #include <uuid/uuid.h>
30 #include <linux/magic.h>
32 #include <btrfsutil.h>
34 #include "kerncompat.h"
41 #include "btrfs-list.h"
45 static int is_subvolume_cleaned(int fd, u64 subvolid)
48 struct btrfs_ioctl_search_args args;
49 struct btrfs_ioctl_search_key *sk = &args.key;
51 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
52 sk->min_objectid = subvolid;
53 sk->max_objectid = subvolid;
54 sk->min_type = BTRFS_ROOT_ITEM_KEY;
55 sk->max_type = BTRFS_ROOT_ITEM_KEY;
57 sk->max_offset = (u64)-1;
59 sk->max_transid = (u64)-1;
62 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
66 if (sk->nr_items == 0)
72 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
81 for (i = 0; i < count; i++) {
84 ret = is_subvolume_cleaned(fd, ids[i]);
87 "cannot read status of dead subvolume %llu: %s",
88 (unsigned long long)ids[i], strerror(-ret));
92 printf("Subvolume id %llu is gone\n", ids[i]);
100 sleep(sleep_interval);
106 static const char * const subvolume_cmd_group_usage[] = {
107 "btrfs subvolume <command> <args>",
111 static const char * const cmd_subvol_create_usage[] = {
112 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
113 "Create a subvolume",
114 "Create a subvolume <name> in <dest>. If <dest> is not given",
115 "subvolume <name> will be created in the current directory.",
117 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
118 " option can be given multiple times.",
122 static int cmd_subvol_create(int argc, char **argv)
124 int retval, res, len;
126 char *dupname = NULL;
131 struct btrfs_qgroup_inherit *inherit = NULL;
132 DIR *dirstream = NULL;
135 int c = getopt(argc, argv, "c:i:");
141 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
148 res = qgroup_inherit_add_group(&inherit, optarg);
155 usage(cmd_subvol_create_usage);
159 if (check_argc_exact(argc - optind, 1))
160 usage(cmd_subvol_create_usage);
164 retval = 1; /* failure */
165 res = test_isdir(dst);
166 if (res < 0 && res != -ENOENT) {
167 error("cannot access %s: %s", dst, strerror(-res));
171 error("target path already exists: %s", dst);
175 dupname = strdup(dst);
176 newname = basename(dupname);
177 dupdir = strdup(dst);
178 dstdir = dirname(dupdir);
180 if (!test_issubvolname(newname)) {
181 error("invalid subvolume name: %s", newname);
185 len = strlen(newname);
186 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
187 error("subvolume name too long: %s", newname);
191 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
195 printf("Create subvolume '%s/%s'\n", dstdir, newname);
197 struct btrfs_ioctl_vol_args_v2 args;
199 memset(&args, 0, sizeof(args));
200 strncpy_null(args.name, newname);
201 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
202 args.size = qgroup_inherit_size(inherit);
203 args.qgroup_inherit = inherit;
205 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
207 struct btrfs_ioctl_vol_args args;
209 memset(&args, 0, sizeof(args));
210 strncpy_null(args.name, newname);
212 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
216 error("cannot create subvolume: %m");
220 retval = 0; /* success */
222 close_file_or_dir(fddst, dirstream);
230 static int wait_for_commit(int fd)
232 enum btrfs_util_error err;
235 err = btrfs_util_start_sync_fd(fd, &transid);
239 err = btrfs_util_wait_sync_fd(fd, transid);
246 static const char * const cmd_subvol_delete_usage[] = {
247 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
248 "Delete subvolume(s)",
249 "Delete subvolumes from the filesystem. The corresponding directory",
250 "is removed instantly but the data blocks are removed later.",
251 "The deletion does not involve full commit by default due to",
252 "performance reasons (as a consequence, the subvolume may appear again",
253 "after a crash). Use one of the --commit options to wait until the",
254 "operation is safely stored on the media.",
256 "-c|--commit-after wait for transaction commit at the end of the operation",
257 "-C|--commit-each wait for transaction commit after deleting each subvolume",
258 "-v|--verbose verbose output of operations",
262 static int cmd_subvol_delete(int argc, char **argv)
267 char *dname, *vname, *cpath;
268 char *dupdname = NULL;
269 char *dupvname = NULL;
271 DIR *dirstream = NULL;
274 u8 fsid[BTRFS_FSID_SIZE];
275 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
276 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
277 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
278 enum btrfs_util_error err;
282 static const struct option long_options[] = {
283 {"commit-after", no_argument, NULL, 'c'},
284 {"commit-each", no_argument, NULL, 'C'},
285 {"verbose", no_argument, NULL, 'v'},
289 c = getopt_long(argc, argv, "cCv", long_options, NULL);
295 commit_mode = COMMIT_AFTER;
298 commit_mode = COMMIT_EACH;
304 usage(cmd_subvol_delete_usage);
308 if (check_argc_min(argc - optind, 1))
309 usage(cmd_subvol_delete_usage);
312 printf("Transaction commit: %s\n",
313 !commit_mode ? "none (default)" :
314 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
322 err = btrfs_util_is_subvolume(path);
324 error_btrfs_util(err);
329 cpath = realpath(path, NULL);
332 error("cannot find real path for '%s': %m", path);
335 dupdname = strdup(cpath);
336 dname = dirname(dupdname);
337 dupvname = strdup(cpath);
338 vname = basename(dupvname);
341 fd = btrfs_open_dir(dname, &dirstream, 1);
347 printf("Delete subvolume (%s): '%s/%s'\n",
348 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
349 ? "commit" : "no-commit", dname, vname);
351 err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
353 error_btrfs_util(err);
358 if (commit_mode == COMMIT_EACH) {
359 res = wait_for_commit(fd);
361 error("unable to wait for commit after '%s': %m", path);
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: %m, fsid: %s",
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] <path>",
443 "List subvolumes and snapshots in the filesystem.",
446 "-o print only subvolumes below specified path",
447 "-a print all the subvolumes in the filesystem and",
448 " distinguish absolute and relative path with respect",
449 " to the given <path>",
452 "-p print parent ID",
453 "-c print the ogeneration of the subvolume",
454 "-g print the generation of the subvolume",
455 "-u print the uuid of subvolumes (and snapshots)",
456 "-q print the parent uuid of the snapshots",
457 "-R print the uuid of the received snapshots",
460 "-s list only snapshots",
461 "-r list readonly subvolumes (including snapshots)",
462 "-d list deleted subvolumes that are not yet cleaned",
465 "-t print the result as a table",
469 " filter the subvolumes by generation",
470 " (+value: >= value; -value: <= value; value: = value)",
472 " filter the subvolumes by ogeneration",
473 " (+value: >= value; -value: <= value; value: = value)",
474 "--sort=gen,ogen,rootid,path",
475 " list the subvolume in order of gen, ogen, rootid or path",
476 " you also can add '+' or '-' in front of each items.",
477 " (+:ascending, -:descending, ascending default)",
481 static int cmd_subvol_list(int argc, char **argv)
483 struct btrfs_list_filter_set *filter_set;
484 struct btrfs_list_comparer_set *comparer_set;
488 int ret = -1, uerr = 0;
491 int is_only_in_path = 0;
492 DIR *dirstream = NULL;
493 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
495 filter_set = btrfs_list_alloc_filter_set();
496 comparer_set = btrfs_list_alloc_comparer_set();
500 static const struct option long_options[] = {
501 {"sort", required_argument, NULL, 'S'},
505 c = getopt_long(argc, argv,
506 "acdgopqsurRG:C:t", long_options, NULL);
512 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
518 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
521 btrfs_list_setup_filter(&filter_set,
522 BTRFS_LIST_FILTER_DELETED,
526 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
532 layout = BTRFS_LIST_LAYOUT_TABLE;
535 btrfs_list_setup_filter(&filter_set,
536 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
538 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
539 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
542 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
545 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
548 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
551 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
554 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
555 ret = btrfs_list_parse_filter_string(optarg,
557 BTRFS_LIST_FILTER_GEN);
565 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
566 ret = btrfs_list_parse_filter_string(optarg,
568 BTRFS_LIST_FILTER_CGEN);
575 ret = btrfs_list_parse_sort_string(optarg,
589 if (check_argc_exact(argc - optind, 1)) {
594 subvol = argv[optind];
595 fd = btrfs_open_dir(subvol, &dirstream, 1);
598 error("can't access '%s'", subvol);
603 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
606 ret = btrfs_list_get_path_rootid(fd, &top_id);
611 btrfs_list_setup_filter(&filter_set,
612 BTRFS_LIST_FILTER_FULL_PATH,
614 else if (is_only_in_path)
615 btrfs_list_setup_filter(&filter_set,
616 BTRFS_LIST_FILTER_TOPID_EQUAL,
619 /* by default we shall print the following columns*/
620 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
621 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
622 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
623 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
625 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
626 layout, !is_list_all && !is_only_in_path, NULL);
629 close_file_or_dir(fd, dirstream);
635 usage(cmd_subvol_list_usage);
639 static const char * const cmd_subvol_snapshot_usage[] = {
640 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
641 "Create a snapshot of the subvolume",
642 "Create a writable/readonly snapshot of the subvolume <source> with",
643 "the name <name> in the <dest> directory. If only <dest> is given,",
644 "the subvolume will be named the basename of <source>.",
646 "-r create a readonly snapshot",
647 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
648 " option can be given multiple times.",
652 static int cmd_subvol_snapshot(int argc, char **argv)
656 int fd = -1, fddst = -1;
657 int len, readonly = 0;
658 char *dupname = NULL;
662 struct btrfs_ioctl_vol_args_v2 args;
663 struct btrfs_qgroup_inherit *inherit = NULL;
664 DIR *dirstream1 = NULL, *dirstream2 = NULL;
666 memset(&args, 0, sizeof(args));
668 int c = getopt(argc, argv, "c:i:r");
674 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
681 res = qgroup_inherit_add_group(&inherit, optarg);
691 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
698 usage(cmd_subvol_snapshot_usage);
702 if (check_argc_exact(argc - optind, 2))
703 usage(cmd_subvol_snapshot_usage);
705 subvol = argv[optind];
706 dst = argv[optind + 1];
708 retval = 1; /* failure */
709 res = test_issubvolume(subvol);
711 error("cannot access subvolume %s: %s", subvol, strerror(-res));
715 error("not a subvolume: %s", subvol);
719 res = test_isdir(dst);
720 if (res < 0 && res != -ENOENT) {
721 error("cannot access %s: %s", dst, strerror(-res));
725 error("'%s' exists and it is not a directory", dst);
730 dupname = strdup(subvol);
731 newname = basename(dupname);
734 dupname = strdup(dst);
735 newname = basename(dupname);
736 dupdir = strdup(dst);
737 dstdir = dirname(dupdir);
740 if (!test_issubvolname(newname)) {
741 error("invalid snapshot name '%s'", newname);
745 len = strlen(newname);
746 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
747 error("snapshot name too long '%s'", newname);
751 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
755 fd = btrfs_open_dir(subvol, &dirstream2, 1);
760 args.flags |= BTRFS_SUBVOL_RDONLY;
761 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
762 subvol, dstdir, newname);
764 printf("Create a snapshot of '%s' in '%s/%s'\n",
765 subvol, dstdir, newname);
770 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
771 args.size = qgroup_inherit_size(inherit);
772 args.qgroup_inherit = inherit;
774 strncpy_null(args.name, newname);
776 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
779 error("cannot snapshot '%s': %m", subvol);
783 retval = 0; /* success */
786 close_file_or_dir(fddst, dirstream1);
787 close_file_or_dir(fd, dirstream2);
795 static const char * const cmd_subvol_get_default_usage[] = {
796 "btrfs subvolume get-default <path>",
797 "Get the default subvolume of a filesystem",
801 static int cmd_subvol_get_default(int argc, char **argv)
806 DIR *dirstream = NULL;
807 enum btrfs_util_error err;
808 struct btrfs_util_subvolume_info subvol;
811 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
813 if (check_argc_exact(argc - optind, 1))
814 usage(cmd_subvol_get_default_usage);
816 fd = btrfs_open_dir(argv[1], &dirstream, 1);
820 err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
822 error_btrfs_util(err);
826 /* no need to resolve roots if FS_TREE is default */
827 if (default_id == BTRFS_FS_TREE_OBJECTID) {
828 printf("ID 5 (FS_TREE)\n");
833 err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
835 error_btrfs_util(err);
839 err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
841 error_btrfs_util(err);
845 printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
846 subvol.id, subvol.generation, subvol.parent_id, path);
852 close_file_or_dir(fd, dirstream);
856 static const char * const cmd_subvol_set_default_usage[] = {
857 "btrfs subvolume set-default <subvolume>\n"
858 "btrfs subvolume set-default <subvolid> <path>",
859 "Set the default subvolume of the filesystem mounted as default.",
860 "The subvolume can be specified by its path,",
861 "or the pair of subvolume id and path to the filesystem.",
865 static int cmd_subvol_set_default(int argc, char **argv)
869 enum btrfs_util_error err;
871 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
873 if (check_argc_min(argc - optind, 1) ||
874 check_argc_max(argc - optind, 2))
875 usage(cmd_subvol_set_default_usage);
877 if (argc - optind == 1) {
878 /* path to the subvolume is specified */
882 /* subvol id and path to the filesystem are specified */
883 objectid = arg_strtou64(argv[optind]);
884 path = argv[optind + 1];
887 err = btrfs_util_set_default_subvolume(path, objectid);
889 error_btrfs_util(err);
895 static const char * const cmd_subvol_find_new_usage[] = {
896 "btrfs subvolume find-new <path> <lastgen>",
897 "List the recently modified files in a filesystem",
901 static int cmd_subvol_find_new(int argc, char **argv)
907 DIR *dirstream = NULL;
908 enum btrfs_util_error err;
910 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
912 if (check_argc_exact(argc - optind, 2))
913 usage(cmd_subvol_find_new_usage);
915 subvol = argv[optind];
916 last_gen = arg_strtou64(argv[optind + 1]);
918 ret = test_issubvolume(subvol);
920 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
924 error("not a subvolume: %s", subvol);
928 fd = btrfs_open_dir(subvol, &dirstream, 1);
932 err = btrfs_util_sync_fd(fd);
934 error_btrfs_util(err);
935 close_file_or_dir(fd, dirstream);
939 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
940 close_file_or_dir(fd, dirstream);
944 static const char * const cmd_subvol_show_usage[] = {
945 "btrfs subvolume show [options] <subvol-path>|<mnt>",
946 "Show more information about the subvolume",
947 "-r|--rootid rootid of the subvolume",
948 "-u|--uuid uuid of the subvolume",
950 "If no option is specified, <subvol-path> will be shown, otherwise",
951 "the rootid or uuid are resolved relative to the <mnt> path.",
955 static int cmd_subvol_show(int argc, char **argv)
957 struct root_info get_ri;
958 struct btrfs_list_filter_set *filter_set = NULL;
960 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
961 char *fullpath = NULL;
962 char raw_prefix[] = "\t\t\t\t";
965 DIR *dirstream1 = NULL;
969 u8 uuid_arg[BTRFS_UUID_SIZE];
973 static const struct option long_options[] = {
974 { "rootid", required_argument, NULL, 'r'},
975 { "uuid", required_argument, NULL, 'u'},
979 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
985 rootid_arg = arg_strtou64(optarg);
989 uuid_parse(optarg, uuid_arg);
993 usage(cmd_subvol_show_usage);
997 if (check_argc_exact(argc - optind, 1))
998 usage(cmd_subvol_show_usage);
1000 if (by_rootid && by_uuid) {
1002 "options --rootid and --uuid cannot be used at the same time");
1003 usage(cmd_subvol_show_usage);
1006 memset(&get_ri, 0, sizeof(get_ri));
1007 fullpath = realpath(argv[optind], NULL);
1009 error("cannot find real path for '%s': %m", argv[optind]);
1014 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1015 } else if (by_uuid) {
1016 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1018 ret = get_subvol_info(fullpath, &get_ri);
1023 error("Failed to get subvol info %s: %s",
1024 fullpath, strerror(-ret));
1026 error("Failed to get subvol info %s: %d",
1032 /* print the info */
1033 printf("%s\n", get_ri.full_path);
1034 printf("\tName: \t\t\t%s\n", get_ri.name);
1036 if (uuid_is_null(get_ri.uuid))
1037 strcpy(uuidparse, "-");
1039 uuid_unparse(get_ri.uuid, uuidparse);
1040 printf("\tUUID: \t\t\t%s\n", uuidparse);
1042 if (uuid_is_null(get_ri.puuid))
1043 strcpy(uuidparse, "-");
1045 uuid_unparse(get_ri.puuid, uuidparse);
1046 printf("\tParent UUID: \t\t%s\n", uuidparse);
1048 if (uuid_is_null(get_ri.ruuid))
1049 strcpy(uuidparse, "-");
1051 uuid_unparse(get_ri.ruuid, uuidparse);
1052 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1057 localtime_r(&get_ri.otime, &tm);
1058 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1061 printf("\tCreation time: \t\t%s\n", tstr);
1063 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1064 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1065 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1066 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1067 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1069 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1070 printf("\tFlags: \t\t\treadonly\n");
1072 printf("\tFlags: \t\t\t-\n");
1074 /* print the snapshots of the given subvol if any*/
1075 printf("\tSnapshot(s):\n");
1076 filter_set = btrfs_list_alloc_filter_set();
1077 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1078 (u64)(unsigned long)get_ri.uuid);
1079 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1081 fd = open_file_or_dir(fullpath, &dirstream1);
1083 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1086 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1093 free(get_ri.full_path);
1096 close_file_or_dir(fd, dirstream1);
1101 static const char * const cmd_subvol_sync_usage[] = {
1102 "btrfs subvolume sync <path> [<subvol-id>...]",
1103 "Wait until given subvolume(s) are completely removed from the filesystem.",
1104 "Wait until given subvolume(s) are completely removed from the filesystem",
1106 "If no subvolume id is given, wait until all current deletion requests",
1107 "are completed, but do not wait for subvolumes deleted meanwhile.",
1108 "The status of subvolume ids is checked periodically.",
1110 "-s <N> sleep N seconds between checks (default: 1)",
1116 * If we're looking for any dead subvolume, take a shortcut and look
1117 * for any ORPHAN_ITEMs in the tree root
1119 static int fs_has_dead_subvolumes(int fd)
1122 struct btrfs_ioctl_search_args args;
1123 struct btrfs_ioctl_search_key *sk = &args.key;
1124 struct btrfs_ioctl_search_header sh;
1125 u64 min_subvolid = 0;
1128 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1129 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1130 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1131 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1132 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1133 sk->min_offset = min_subvolid;
1134 sk->max_offset = (u64)-1;
1135 sk->min_transid = 0;
1136 sk->max_transid = (u64)-1;
1139 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1146 memcpy(&sh, args.buf, sizeof(sh));
1147 min_subvolid = sh.offset;
1150 * Verify that the root item is really there and we haven't hit
1153 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1154 sk->min_objectid = min_subvolid;
1155 sk->max_objectid = min_subvolid;
1156 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1157 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1159 sk->max_offset = (u64)-1;
1160 sk->min_transid = 0;
1161 sk->max_transid = (u64)-1;
1164 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1169 * Stale orphan, try the next one
1171 if (!sk->nr_items) {
1180 #define SUBVOL_ID_BATCH 1024
1183 * Enumerate all dead subvolumes that exist in the filesystem.
1184 * Fill @ids and reallocate to bigger size if needed.
1186 static int enumerate_dead_subvols(int fd, u64 **ids)
1189 struct btrfs_ioctl_search_args args;
1190 struct btrfs_ioctl_search_key *sk = &args.key;
1194 memset(&args, 0, sizeof(args));
1196 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1197 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1198 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1199 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1200 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1202 sk->max_offset = (u64)-1;
1203 sk->min_transid = 0;
1204 sk->max_transid = (u64)-1;
1205 sk->nr_items = 4096;
1209 struct btrfs_ioctl_search_header *sh;
1213 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1221 for (i = 0; i < sk->nr_items; i++) {
1222 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1225 if (btrfs_search_header_type(sh)
1226 == BTRFS_ORPHAN_ITEM_KEY) {
1230 count += SUBVOL_ID_BATCH;
1231 newids = (u64*)realloc(*ids,
1232 count * sizeof(u64));
1237 (*ids)[idx] = btrfs_search_header_offset(sh);
1240 off += btrfs_search_header_len(sh);
1242 sk->min_objectid = btrfs_search_header_objectid(sh);
1243 sk->min_type = btrfs_search_header_type(sh);
1244 sk->min_offset = btrfs_search_header_offset(sh);
1246 if (sk->min_offset < (u64)-1)
1250 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1252 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1259 static int cmd_subvol_sync(int argc, char **argv)
1264 DIR *dirstream = NULL;
1267 int sleep_interval = 1;
1270 int c = getopt(argc, argv, "s:");
1277 sleep_interval = atoi(optarg);
1278 if (sleep_interval < 1) {
1279 error("invalid sleep interval %s", optarg);
1285 usage(cmd_subvol_sync_usage);
1289 if (check_argc_min(argc - optind, 1))
1290 usage(cmd_subvol_sync_usage);
1292 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1299 id_count = argc - optind;
1301 id_count = enumerate_dead_subvols(fd, &ids);
1303 error("can't enumerate dead subvolumes: %s",
1304 strerror(-id_count));
1308 if (id_count == 0) {
1313 ids = (u64*)malloc(id_count * sizeof(u64));
1315 error("not enough memory");
1320 for (i = 0; i < id_count; i++) {
1324 arg = argv[optind + i];
1326 id = strtoull(arg, NULL, 10);
1328 error("unrecognized subvolume id %s", arg);
1332 if (id < BTRFS_FIRST_FREE_OBJECTID
1333 || id > BTRFS_LAST_FREE_OBJECTID) {
1334 error("subvolume id %s out of range", arg);
1342 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1346 close_file_or_dir(fd, dirstream);
1351 static const char subvolume_cmd_group_info[] =
1352 "manage subvolumes: create, delete, list, etc";
1354 const struct cmd_group subvolume_cmd_group = {
1355 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1356 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1357 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1358 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1359 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1361 { "get-default", cmd_subvol_get_default,
1362 cmd_subvol_get_default_usage, NULL, 0 },
1363 { "set-default", cmd_subvol_set_default,
1364 cmd_subvol_set_default_usage, NULL, 0 },
1365 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1367 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1368 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1373 int cmd_subvolume(int argc, char **argv)
1375 return handle_command_group(&subvolume_cmd_group, argc, argv);