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)
958 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
959 char *fullpath = NULL;
962 DIR *dirstream1 = NULL;
966 u8 uuid_arg[BTRFS_UUID_SIZE];
967 struct btrfs_util_subvolume_iterator *iter;
968 struct btrfs_util_subvolume_info subvol;
969 char *subvol_path = NULL;
970 enum btrfs_util_error err;
974 static const struct option long_options[] = {
975 { "rootid", required_argument, NULL, 'r'},
976 { "uuid", required_argument, NULL, 'u'},
980 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
986 rootid_arg = arg_strtou64(optarg);
990 uuid_parse(optarg, uuid_arg);
994 usage(cmd_subvol_show_usage);
998 if (check_argc_exact(argc - optind, 1))
999 usage(cmd_subvol_show_usage);
1001 if (by_rootid && by_uuid) {
1003 "options --rootid and --uuid cannot be used at the same time");
1004 usage(cmd_subvol_show_usage);
1007 fullpath = realpath(argv[optind], NULL);
1009 error("cannot find real path for '%s': %m", argv[optind]);
1013 fd = open_file_or_dir(fullpath, &dirstream1);
1015 error("can't access '%s'", fullpath);
1020 err = btrfs_util_create_subvolume_iterator_fd(fd,
1021 BTRFS_FS_TREE_OBJECTID,
1024 error_btrfs_util(err);
1029 err = btrfs_util_subvolume_iterator_next_info(iter,
1032 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1033 uuid_unparse(uuid_arg, uuidparse);
1034 error("can't find uuid '%s' on '%s'", uuidparse,
1036 btrfs_util_destroy_subvolume_iterator(iter);
1039 error_btrfs_util(err);
1040 btrfs_util_destroy_subvolume_iterator(iter);
1044 if (uuid_compare(subvol.uuid, uuid_arg) == 0)
1049 btrfs_util_destroy_subvolume_iterator(iter);
1052 * If !by_rootid, rootid_arg = 0, which means find the
1053 * subvolume ID of the fd and use that.
1055 err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
1057 error_btrfs_util(err);
1061 err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
1063 error_btrfs_util(err);
1069 /* print the info */
1070 printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
1071 printf("\tName: \t\t\t%s\n",
1072 (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
1073 basename(subvol_path)));
1075 if (uuid_is_null(subvol.uuid))
1076 strcpy(uuidparse, "-");
1078 uuid_unparse(subvol.uuid, uuidparse);
1079 printf("\tUUID: \t\t\t%s\n", uuidparse);
1081 if (uuid_is_null(subvol.parent_uuid))
1082 strcpy(uuidparse, "-");
1084 uuid_unparse(subvol.parent_uuid, uuidparse);
1085 printf("\tParent UUID: \t\t%s\n", uuidparse);
1087 if (uuid_is_null(subvol.received_uuid))
1088 strcpy(uuidparse, "-");
1090 uuid_unparse(subvol.received_uuid, uuidparse);
1091 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1093 if (subvol.otime.tv_sec) {
1096 localtime_r(&subvol.otime.tv_sec, &tm);
1097 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1100 printf("\tCreation time: \t\t%s\n", tstr);
1102 printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
1103 printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
1104 printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
1105 printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1106 printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1108 if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1109 printf("\tFlags: \t\t\treadonly\n");
1111 printf("\tFlags: \t\t\t-\n");
1113 /* print the snapshots of the given subvol if any*/
1114 printf("\tSnapshot(s):\n");
1116 err = btrfs_util_create_subvolume_iterator_fd(fd,
1117 BTRFS_FS_TREE_OBJECTID, 0,
1121 struct btrfs_util_subvolume_info subvol2;
1124 err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
1125 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1128 error_btrfs_util(err);
1129 btrfs_util_destroy_subvolume_iterator(iter);
1133 if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
1134 printf("\t\t\t\t%s\n", path);
1138 btrfs_util_destroy_subvolume_iterator(iter);
1143 close_file_or_dir(fd, dirstream1);
1148 static const char * const cmd_subvol_sync_usage[] = {
1149 "btrfs subvolume sync <path> [<subvol-id>...]",
1150 "Wait until given subvolume(s) are completely removed from the filesystem.",
1151 "Wait until given subvolume(s) are completely removed from the filesystem",
1153 "If no subvolume id is given, wait until all current deletion requests",
1154 "are completed, but do not wait for subvolumes deleted meanwhile.",
1155 "The status of subvolume ids is checked periodically.",
1157 "-s <N> sleep N seconds between checks (default: 1)",
1163 * If we're looking for any dead subvolume, take a shortcut and look
1164 * for any ORPHAN_ITEMs in the tree root
1166 static int fs_has_dead_subvolumes(int fd)
1169 struct btrfs_ioctl_search_args args;
1170 struct btrfs_ioctl_search_key *sk = &args.key;
1171 struct btrfs_ioctl_search_header sh;
1172 u64 min_subvolid = 0;
1175 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1176 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1177 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1178 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1179 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1180 sk->min_offset = min_subvolid;
1181 sk->max_offset = (u64)-1;
1182 sk->min_transid = 0;
1183 sk->max_transid = (u64)-1;
1186 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1193 memcpy(&sh, args.buf, sizeof(sh));
1194 min_subvolid = sh.offset;
1197 * Verify that the root item is really there and we haven't hit
1200 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1201 sk->min_objectid = min_subvolid;
1202 sk->max_objectid = min_subvolid;
1203 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1204 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1206 sk->max_offset = (u64)-1;
1207 sk->min_transid = 0;
1208 sk->max_transid = (u64)-1;
1211 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1216 * Stale orphan, try the next one
1218 if (!sk->nr_items) {
1227 #define SUBVOL_ID_BATCH 1024
1230 * Enumerate all dead subvolumes that exist in the filesystem.
1231 * Fill @ids and reallocate to bigger size if needed.
1233 static int enumerate_dead_subvols(int fd, u64 **ids)
1236 struct btrfs_ioctl_search_args args;
1237 struct btrfs_ioctl_search_key *sk = &args.key;
1241 memset(&args, 0, sizeof(args));
1243 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1244 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1245 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1246 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1247 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1249 sk->max_offset = (u64)-1;
1250 sk->min_transid = 0;
1251 sk->max_transid = (u64)-1;
1252 sk->nr_items = 4096;
1256 struct btrfs_ioctl_search_header *sh;
1260 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1268 for (i = 0; i < sk->nr_items; i++) {
1269 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1272 if (btrfs_search_header_type(sh)
1273 == BTRFS_ORPHAN_ITEM_KEY) {
1277 count += SUBVOL_ID_BATCH;
1278 newids = (u64*)realloc(*ids,
1279 count * sizeof(u64));
1284 (*ids)[idx] = btrfs_search_header_offset(sh);
1287 off += btrfs_search_header_len(sh);
1289 sk->min_objectid = btrfs_search_header_objectid(sh);
1290 sk->min_type = btrfs_search_header_type(sh);
1291 sk->min_offset = btrfs_search_header_offset(sh);
1293 if (sk->min_offset < (u64)-1)
1297 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1299 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1306 static int cmd_subvol_sync(int argc, char **argv)
1311 DIR *dirstream = NULL;
1314 int sleep_interval = 1;
1317 int c = getopt(argc, argv, "s:");
1324 sleep_interval = atoi(optarg);
1325 if (sleep_interval < 1) {
1326 error("invalid sleep interval %s", optarg);
1332 usage(cmd_subvol_sync_usage);
1336 if (check_argc_min(argc - optind, 1))
1337 usage(cmd_subvol_sync_usage);
1339 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1346 id_count = argc - optind;
1348 id_count = enumerate_dead_subvols(fd, &ids);
1350 error("can't enumerate dead subvolumes: %s",
1351 strerror(-id_count));
1355 if (id_count == 0) {
1360 ids = (u64*)malloc(id_count * sizeof(u64));
1362 error("not enough memory");
1367 for (i = 0; i < id_count; i++) {
1371 arg = argv[optind + i];
1373 id = strtoull(arg, NULL, 10);
1375 error("unrecognized subvolume id %s", arg);
1379 if (id < BTRFS_FIRST_FREE_OBJECTID
1380 || id > BTRFS_LAST_FREE_OBJECTID) {
1381 error("subvolume id %s out of range", arg);
1389 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1393 close_file_or_dir(fd, dirstream);
1398 static const char subvolume_cmd_group_info[] =
1399 "manage subvolumes: create, delete, list, etc";
1401 const struct cmd_group subvolume_cmd_group = {
1402 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1403 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1404 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1405 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1406 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1408 { "get-default", cmd_subvol_get_default,
1409 cmd_subvol_get_default_usage, NULL, 0 },
1410 { "set-default", cmd_subvol_set_default,
1411 cmd_subvol_set_default_usage, NULL, 0 },
1412 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1414 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1415 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1420 int cmd_subvolume(int argc, char **argv)
1422 return handle_command_group(&subvolume_cmd_group, argc, argv);