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 <btrfsutil.h>
33 #include "kerncompat.h"
40 #include "btrfs-list.h"
44 static int is_subvolume_cleaned(int fd, u64 subvolid)
47 struct btrfs_ioctl_search_args args;
48 struct btrfs_ioctl_search_key *sk = &args.key;
50 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
51 sk->min_objectid = subvolid;
52 sk->max_objectid = subvolid;
53 sk->min_type = BTRFS_ROOT_ITEM_KEY;
54 sk->max_type = BTRFS_ROOT_ITEM_KEY;
56 sk->max_offset = (u64)-1;
58 sk->max_transid = (u64)-1;
61 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
65 if (sk->nr_items == 0)
71 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
80 for (i = 0; i < count; i++) {
83 ret = is_subvolume_cleaned(fd, ids[i]);
86 "cannot read status of dead subvolume %llu: %s",
87 (unsigned long long)ids[i], strerror(-ret));
91 printf("Subvolume id %llu is gone\n", ids[i]);
99 sleep(sleep_interval);
105 static const char * const subvolume_cmd_group_usage[] = {
106 "btrfs subvolume <command> <args>",
110 static const char * const cmd_subvol_create_usage[] = {
111 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
112 "Create a subvolume",
113 "Create a subvolume <name> in <dest>. If <dest> is not given",
114 "subvolume <name> will be created in the current directory.",
116 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
117 " option can be given multiple times.",
121 static int cmd_subvol_create(int argc, char **argv)
123 int retval, res, len;
125 char *dupname = NULL;
130 struct btrfs_qgroup_inherit *inherit = NULL;
131 DIR *dirstream = NULL;
134 int c = getopt(argc, argv, "c:i:");
140 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
147 res = qgroup_inherit_add_group(&inherit, optarg);
154 usage(cmd_subvol_create_usage);
158 if (check_argc_exact(argc - optind, 1))
159 usage(cmd_subvol_create_usage);
163 retval = 1; /* failure */
164 res = test_isdir(dst);
165 if (res < 0 && res != -ENOENT) {
166 error("cannot access %s: %s", dst, strerror(-res));
170 error("target path already exists: %s", dst);
174 dupname = strdup(dst);
175 newname = basename(dupname);
176 dupdir = strdup(dst);
177 dstdir = dirname(dupdir);
179 if (!test_issubvolname(newname)) {
180 error("invalid subvolume name: %s", newname);
184 len = strlen(newname);
185 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
186 error("subvolume name too long: %s", newname);
190 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
194 printf("Create subvolume '%s/%s'\n", dstdir, newname);
196 struct btrfs_ioctl_vol_args_v2 args;
198 memset(&args, 0, sizeof(args));
199 strncpy_null(args.name, newname);
200 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
201 args.size = qgroup_inherit_size(inherit);
202 args.qgroup_inherit = inherit;
204 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
206 struct btrfs_ioctl_vol_args args;
208 memset(&args, 0, sizeof(args));
209 strncpy_null(args.name, newname);
211 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
215 error("cannot create subvolume: %m");
219 retval = 0; /* success */
221 close_file_or_dir(fddst, dirstream);
229 static int wait_for_commit(int fd)
231 enum btrfs_util_error err;
234 err = btrfs_util_start_sync_fd(fd, &transid);
238 err = btrfs_util_wait_sync_fd(fd, transid);
245 static const char * const cmd_subvol_delete_usage[] = {
246 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
247 "Delete subvolume(s)",
248 "Delete subvolumes from the filesystem. The corresponding directory",
249 "is removed instantly but the data blocks are removed later.",
250 "The deletion does not involve full commit by default due to",
251 "performance reasons (as a consequence, the subvolume may appear again",
252 "after a crash). Use one of the --commit options to wait until the",
253 "operation is safely stored on the media.",
255 "-c|--commit-after wait for transaction commit at the end of the operation",
256 "-C|--commit-each wait for transaction commit after deleting each subvolume",
257 "-v|--verbose verbose output of operations",
261 static int cmd_subvol_delete(int argc, char **argv)
266 struct btrfs_ioctl_vol_args args;
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 };
281 static const struct option long_options[] = {
282 {"commit-after", no_argument, NULL, 'c'},
283 {"commit-each", no_argument, NULL, 'C'},
284 {"verbose", no_argument, NULL, 'v'},
288 c = getopt_long(argc, argv, "cCv", long_options, NULL);
294 commit_mode = COMMIT_AFTER;
297 commit_mode = COMMIT_EACH;
303 usage(cmd_subvol_delete_usage);
307 if (check_argc_min(argc - optind, 1))
308 usage(cmd_subvol_delete_usage);
311 printf("Transaction commit: %s\n",
312 !commit_mode ? "none (default)" :
313 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
321 res = test_issubvolume(path);
323 error("cannot access subvolume %s: %s", path, strerror(-res));
328 error("not a subvolume: %s", path);
333 cpath = realpath(path, NULL);
336 error("cannot find real path for '%s': %m", path);
339 dupdname = strdup(cpath);
340 dname = dirname(dupdname);
341 dupvname = strdup(cpath);
342 vname = basename(dupvname);
345 fd = btrfs_open_dir(dname, &dirstream, 1);
351 printf("Delete subvolume (%s): '%s/%s'\n",
352 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
353 ? "commit" : "no-commit", dname, vname);
354 memset(&args, 0, sizeof(args));
355 strncpy_null(args.name, vname);
356 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
358 error("cannot delete '%s/%s': %m", dname, vname);
363 if (commit_mode == COMMIT_EACH) {
364 res = wait_for_commit(fd);
366 error("unable to wait for commit after '%s': %m", path);
369 } else if (commit_mode == COMMIT_AFTER) {
370 res = get_fsid(dname, fsid, 0);
372 error("unable to get fsid for '%s': %s",
373 path, strerror(-res));
375 "delete suceeded but commit may not be done in the end");
380 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
382 uuid_unparse(fsid, uuidbuf);
383 printf(" new fs is found for '%s', fsid: %s\n",
387 * This is the first time a subvolume on this
388 * filesystem is deleted, keep fd in order to issue
389 * SYNC ioctl in the end
396 close_file_or_dir(fd, dirstream);
408 if (commit_mode == COMMIT_AFTER) {
412 * Traverse seen_fsid_hash and issue SYNC ioctl on each
415 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
416 struct seen_fsid *seen = seen_fsid_hash[slot];
419 res = wait_for_commit(seen->fd);
421 uuid_unparse(seen->fsid, uuidbuf);
423 "unable to do final sync after deletion: %m, fsid: %s",
426 } else if (verbose > 0) {
427 uuid_unparse(seen->fsid, uuidbuf);
428 printf("final sync is done for fsid: %s\n",
434 /* fd will also be closed in free_seen_fsid */
435 free_seen_fsid(seen_fsid_hash);
443 * - uppercase for filters and sort options
444 * - lowercase for enabling specific items in the output
446 static const char * const cmd_subvol_list_usage[] = {
447 "btrfs subvolume list [options] <path>",
448 "List subvolumes and snapshots in the filesystem.",
451 "-o print only subvolumes below specified path",
452 "-a print all the subvolumes in the filesystem and",
453 " distinguish absolute and relative path with respect",
454 " to the given <path>",
457 "-p print parent ID",
458 "-c print the ogeneration of the subvolume",
459 "-g print the generation of the subvolume",
460 "-u print the uuid of subvolumes (and snapshots)",
461 "-q print the parent uuid of the snapshots",
462 "-R print the uuid of the received snapshots",
465 "-s list only snapshots",
466 "-r list readonly subvolumes (including snapshots)",
467 "-d list deleted subvolumes that are not yet cleaned",
470 "-t print the result as a table",
474 " filter the subvolumes by generation",
475 " (+value: >= value; -value: <= value; value: = value)",
477 " filter the subvolumes by ogeneration",
478 " (+value: >= value; -value: <= value; value: = value)",
479 "--sort=gen,ogen,rootid,path",
480 " list the subvolume in order of gen, ogen, rootid or path",
481 " you also can add '+' or '-' in front of each items.",
482 " (+:ascending, -:descending, ascending default)",
486 static int cmd_subvol_list(int argc, char **argv)
488 struct btrfs_list_filter_set *filter_set;
489 struct btrfs_list_comparer_set *comparer_set;
493 int ret = -1, uerr = 0;
496 int is_only_in_path = 0;
497 DIR *dirstream = NULL;
498 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
500 filter_set = btrfs_list_alloc_filter_set();
501 comparer_set = btrfs_list_alloc_comparer_set();
505 static const struct option long_options[] = {
506 {"sort", required_argument, NULL, 'S'},
510 c = getopt_long(argc, argv,
511 "acdgopqsurRG:C:t", long_options, NULL);
517 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
523 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
526 btrfs_list_setup_filter(&filter_set,
527 BTRFS_LIST_FILTER_DELETED,
531 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
537 layout = BTRFS_LIST_LAYOUT_TABLE;
540 btrfs_list_setup_filter(&filter_set,
541 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
543 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
544 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
547 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
550 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
553 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
556 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
559 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
560 ret = btrfs_list_parse_filter_string(optarg,
562 BTRFS_LIST_FILTER_GEN);
570 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
571 ret = btrfs_list_parse_filter_string(optarg,
573 BTRFS_LIST_FILTER_CGEN);
580 ret = btrfs_list_parse_sort_string(optarg,
594 if (check_argc_exact(argc - optind, 1)) {
599 subvol = argv[optind];
600 fd = btrfs_open_dir(subvol, &dirstream, 1);
603 error("can't access '%s'", subvol);
608 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
611 ret = btrfs_list_get_path_rootid(fd, &top_id);
616 btrfs_list_setup_filter(&filter_set,
617 BTRFS_LIST_FILTER_FULL_PATH,
619 else if (is_only_in_path)
620 btrfs_list_setup_filter(&filter_set,
621 BTRFS_LIST_FILTER_TOPID_EQUAL,
624 /* by default we shall print the following columns*/
625 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
626 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
627 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
628 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
630 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
631 layout, !is_list_all && !is_only_in_path, NULL);
634 close_file_or_dir(fd, dirstream);
640 usage(cmd_subvol_list_usage);
644 static const char * const cmd_subvol_snapshot_usage[] = {
645 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
646 "Create a snapshot of the subvolume",
647 "Create a writable/readonly snapshot of the subvolume <source> with",
648 "the name <name> in the <dest> directory. If only <dest> is given,",
649 "the subvolume will be named the basename of <source>.",
651 "-r create a readonly snapshot",
652 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
653 " option can be given multiple times.",
657 static int cmd_subvol_snapshot(int argc, char **argv)
661 int fd = -1, fddst = -1;
662 int len, readonly = 0;
663 char *dupname = NULL;
667 struct btrfs_ioctl_vol_args_v2 args;
668 struct btrfs_qgroup_inherit *inherit = NULL;
669 DIR *dirstream1 = NULL, *dirstream2 = NULL;
671 memset(&args, 0, sizeof(args));
673 int c = getopt(argc, argv, "c:i:r");
679 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
686 res = qgroup_inherit_add_group(&inherit, optarg);
696 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
703 usage(cmd_subvol_snapshot_usage);
707 if (check_argc_exact(argc - optind, 2))
708 usage(cmd_subvol_snapshot_usage);
710 subvol = argv[optind];
711 dst = argv[optind + 1];
713 retval = 1; /* failure */
714 res = test_issubvolume(subvol);
716 error("cannot access subvolume %s: %s", subvol, strerror(-res));
720 error("not a subvolume: %s", subvol);
724 res = test_isdir(dst);
725 if (res < 0 && res != -ENOENT) {
726 error("cannot access %s: %s", dst, strerror(-res));
730 error("'%s' exists and it is not a directory", dst);
735 dupname = strdup(subvol);
736 newname = basename(dupname);
739 dupname = strdup(dst);
740 newname = basename(dupname);
741 dupdir = strdup(dst);
742 dstdir = dirname(dupdir);
745 if (!test_issubvolname(newname)) {
746 error("invalid snapshot name '%s'", newname);
750 len = strlen(newname);
751 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
752 error("snapshot name too long '%s'", newname);
756 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
760 fd = btrfs_open_dir(subvol, &dirstream2, 1);
765 args.flags |= BTRFS_SUBVOL_RDONLY;
766 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
767 subvol, dstdir, newname);
769 printf("Create a snapshot of '%s' in '%s/%s'\n",
770 subvol, dstdir, newname);
775 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
776 args.size = qgroup_inherit_size(inherit);
777 args.qgroup_inherit = inherit;
779 strncpy_null(args.name, newname);
781 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
784 error("cannot snapshot '%s': %m", subvol);
788 retval = 0; /* success */
791 close_file_or_dir(fddst, dirstream1);
792 close_file_or_dir(fd, dirstream2);
800 static const char * const cmd_subvol_get_default_usage[] = {
801 "btrfs subvolume get-default <path>",
802 "Get the default subvolume of a filesystem",
806 static int cmd_subvol_get_default(int argc, char **argv)
811 struct btrfs_list_filter_set *filter_set;
813 DIR *dirstream = NULL;
815 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
817 if (check_argc_exact(argc - optind, 1))
818 usage(cmd_subvol_get_default_usage);
821 fd = btrfs_open_dir(subvol, &dirstream, 1);
825 ret = btrfs_list_get_default_subvolume(fd, &default_id);
827 error("failed to look up default subvolume: %m");
832 if (default_id == 0) {
833 error("'default' dir item not found");
837 /* no need to resolve roots if FS_TREE is default */
838 if (default_id == BTRFS_FS_TREE_OBJECTID) {
839 printf("ID 5 (FS_TREE)\n");
844 filter_set = btrfs_list_alloc_filter_set();
845 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
848 /* by default we shall print the following columns*/
849 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
850 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
851 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
852 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
854 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
855 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
860 close_file_or_dir(fd, dirstream);
864 static const char * const cmd_subvol_set_default_usage[] = {
865 "btrfs subvolume set-default <subvolume>\n"
866 "btrfs subvolume set-default <subvolid> <path>",
867 "Set the default subvolume of the filesystem mounted as default.",
868 "The subvolume can be specified by its path,",
869 "or the pair of subvolume id and path to the filesystem.",
873 static int cmd_subvol_set_default(int argc, char **argv)
879 DIR *dirstream = NULL;
881 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
883 if (check_argc_min(argc - optind, 1) ||
884 check_argc_max(argc - optind, 2))
885 usage(cmd_subvol_set_default_usage);
887 if (argc - optind == 1) {
888 /* path to the subvolume is specified */
891 ret = test_issubvolume(path);
893 error("stat error: %s", strerror(-ret));
896 error("'%s' is not a subvolume", path);
900 fd = btrfs_open_dir(path, &dirstream, 1);
904 ret = lookup_path_rootid(fd, &objectid);
906 error("unable to get subvol id: %s", strerror(-ret));
907 close_file_or_dir(fd, dirstream);
911 /* subvol id and path to the filesystem are specified */
912 subvolid = argv[optind];
913 path = argv[optind + 1];
914 objectid = arg_strtou64(subvolid);
916 fd = btrfs_open_dir(path, &dirstream, 1);
921 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
922 close_file_or_dir(fd, dirstream);
924 error("unable to set a new default subvolume: %m");
930 static const char * const cmd_subvol_find_new_usage[] = {
931 "btrfs subvolume find-new <path> <lastgen>",
932 "List the recently modified files in a filesystem",
936 static int cmd_subvol_find_new(int argc, char **argv)
942 DIR *dirstream = NULL;
943 enum btrfs_util_error err;
945 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
947 if (check_argc_exact(argc - optind, 2))
948 usage(cmd_subvol_find_new_usage);
950 subvol = argv[optind];
951 last_gen = arg_strtou64(argv[optind + 1]);
953 ret = test_issubvolume(subvol);
955 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
959 error("not a subvolume: %s", subvol);
963 fd = btrfs_open_dir(subvol, &dirstream, 1);
967 err = btrfs_util_sync_fd(fd);
969 error_btrfs_util(err);
970 close_file_or_dir(fd, dirstream);
974 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
975 close_file_or_dir(fd, dirstream);
979 static const char * const cmd_subvol_show_usage[] = {
980 "btrfs subvolume show [options] <subvol-path>|<mnt>",
981 "Show more information about the subvolume",
982 "-r|--rootid rootid of the subvolume",
983 "-u|--uuid uuid of the subvolume",
985 "If no option is specified, <subvol-path> will be shown, otherwise",
986 "the rootid or uuid are resolved relative to the <mnt> path.",
990 static int cmd_subvol_show(int argc, char **argv)
992 struct root_info get_ri;
993 struct btrfs_list_filter_set *filter_set = NULL;
995 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
996 char *fullpath = NULL;
997 char raw_prefix[] = "\t\t\t\t";
1000 DIR *dirstream1 = NULL;
1004 u8 uuid_arg[BTRFS_UUID_SIZE];
1008 static const struct option long_options[] = {
1009 { "rootid", required_argument, NULL, 'r'},
1010 { "uuid", required_argument, NULL, 'u'},
1011 { NULL, 0, NULL, 0 }
1014 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
1020 rootid_arg = arg_strtou64(optarg);
1024 uuid_parse(optarg, uuid_arg);
1028 usage(cmd_subvol_show_usage);
1032 if (check_argc_exact(argc - optind, 1))
1033 usage(cmd_subvol_show_usage);
1035 if (by_rootid && by_uuid) {
1037 "options --rootid and --uuid cannot be used at the same time");
1038 usage(cmd_subvol_show_usage);
1041 memset(&get_ri, 0, sizeof(get_ri));
1042 fullpath = realpath(argv[optind], NULL);
1044 error("cannot find real path for '%s': %m", argv[optind]);
1049 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1050 } else if (by_uuid) {
1051 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1053 ret = get_subvol_info(fullpath, &get_ri);
1058 error("Failed to get subvol info %s: %s",
1059 fullpath, strerror(-ret));
1061 error("Failed to get subvol info %s: %d",
1067 /* print the info */
1068 printf("%s\n", get_ri.full_path);
1069 printf("\tName: \t\t\t%s\n", get_ri.name);
1071 if (uuid_is_null(get_ri.uuid))
1072 strcpy(uuidparse, "-");
1074 uuid_unparse(get_ri.uuid, uuidparse);
1075 printf("\tUUID: \t\t\t%s\n", uuidparse);
1077 if (uuid_is_null(get_ri.puuid))
1078 strcpy(uuidparse, "-");
1080 uuid_unparse(get_ri.puuid, uuidparse);
1081 printf("\tParent UUID: \t\t%s\n", uuidparse);
1083 if (uuid_is_null(get_ri.ruuid))
1084 strcpy(uuidparse, "-");
1086 uuid_unparse(get_ri.ruuid, uuidparse);
1087 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1092 localtime_r(&get_ri.otime, &tm);
1093 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1096 printf("\tCreation time: \t\t%s\n", tstr);
1098 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1099 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1100 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1101 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1102 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1104 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1105 printf("\tFlags: \t\t\treadonly\n");
1107 printf("\tFlags: \t\t\t-\n");
1109 /* print the snapshots of the given subvol if any*/
1110 printf("\tSnapshot(s):\n");
1111 filter_set = btrfs_list_alloc_filter_set();
1112 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1113 (u64)(unsigned long)get_ri.uuid);
1114 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1116 fd = open_file_or_dir(fullpath, &dirstream1);
1118 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1121 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1128 free(get_ri.full_path);
1131 close_file_or_dir(fd, dirstream1);
1136 static const char * const cmd_subvol_sync_usage[] = {
1137 "btrfs subvolume sync <path> [<subvol-id>...]",
1138 "Wait until given subvolume(s) are completely removed from the filesystem.",
1139 "Wait until given subvolume(s) are completely removed from the filesystem",
1141 "If no subvolume id is given, wait until all current deletion requests",
1142 "are completed, but do not wait for subvolumes deleted meanwhile.",
1143 "The status of subvolume ids is checked periodically.",
1145 "-s <N> sleep N seconds between checks (default: 1)",
1151 * If we're looking for any dead subvolume, take a shortcut and look
1152 * for any ORPHAN_ITEMs in the tree root
1154 static int fs_has_dead_subvolumes(int fd)
1157 struct btrfs_ioctl_search_args args;
1158 struct btrfs_ioctl_search_key *sk = &args.key;
1159 struct btrfs_ioctl_search_header sh;
1160 u64 min_subvolid = 0;
1163 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1164 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1165 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1166 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1167 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1168 sk->min_offset = min_subvolid;
1169 sk->max_offset = (u64)-1;
1170 sk->min_transid = 0;
1171 sk->max_transid = (u64)-1;
1174 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1181 memcpy(&sh, args.buf, sizeof(sh));
1182 min_subvolid = sh.offset;
1185 * Verify that the root item is really there and we haven't hit
1188 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1189 sk->min_objectid = min_subvolid;
1190 sk->max_objectid = min_subvolid;
1191 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1192 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1194 sk->max_offset = (u64)-1;
1195 sk->min_transid = 0;
1196 sk->max_transid = (u64)-1;
1199 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1204 * Stale orphan, try the next one
1206 if (!sk->nr_items) {
1215 #define SUBVOL_ID_BATCH 1024
1218 * Enumerate all dead subvolumes that exist in the filesystem.
1219 * Fill @ids and reallocate to bigger size if needed.
1221 static int enumerate_dead_subvols(int fd, u64 **ids)
1224 struct btrfs_ioctl_search_args args;
1225 struct btrfs_ioctl_search_key *sk = &args.key;
1229 memset(&args, 0, sizeof(args));
1231 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1232 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1233 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1234 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1235 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1237 sk->max_offset = (u64)-1;
1238 sk->min_transid = 0;
1239 sk->max_transid = (u64)-1;
1240 sk->nr_items = 4096;
1244 struct btrfs_ioctl_search_header *sh;
1248 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1256 for (i = 0; i < sk->nr_items; i++) {
1257 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1260 if (btrfs_search_header_type(sh)
1261 == BTRFS_ORPHAN_ITEM_KEY) {
1265 count += SUBVOL_ID_BATCH;
1266 newids = (u64*)realloc(*ids,
1267 count * sizeof(u64));
1272 (*ids)[idx] = btrfs_search_header_offset(sh);
1275 off += btrfs_search_header_len(sh);
1277 sk->min_objectid = btrfs_search_header_objectid(sh);
1278 sk->min_type = btrfs_search_header_type(sh);
1279 sk->min_offset = btrfs_search_header_offset(sh);
1281 if (sk->min_offset < (u64)-1)
1285 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1287 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1294 static int cmd_subvol_sync(int argc, char **argv)
1299 DIR *dirstream = NULL;
1302 int sleep_interval = 1;
1305 int c = getopt(argc, argv, "s:");
1312 sleep_interval = atoi(optarg);
1313 if (sleep_interval < 1) {
1314 error("invalid sleep interval %s", optarg);
1320 usage(cmd_subvol_sync_usage);
1324 if (check_argc_min(argc - optind, 1))
1325 usage(cmd_subvol_sync_usage);
1327 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1334 id_count = argc - optind;
1336 id_count = enumerate_dead_subvols(fd, &ids);
1338 error("can't enumerate dead subvolumes: %s",
1339 strerror(-id_count));
1343 if (id_count == 0) {
1348 ids = (u64*)malloc(id_count * sizeof(u64));
1350 error("not enough memory");
1355 for (i = 0; i < id_count; i++) {
1359 arg = argv[optind + i];
1361 id = strtoull(arg, NULL, 10);
1363 error("unrecognized subvolume id %s", arg);
1367 if (id < BTRFS_FIRST_FREE_OBJECTID
1368 || id > BTRFS_LAST_FREE_OBJECTID) {
1369 error("subvolume id %s out of range", arg);
1377 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1381 close_file_or_dir(fd, dirstream);
1386 static const char subvolume_cmd_group_info[] =
1387 "manage subvolumes: create, delete, list, etc";
1389 const struct cmd_group subvolume_cmd_group = {
1390 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1391 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1392 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1393 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1394 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1396 { "get-default", cmd_subvol_get_default,
1397 cmd_subvol_get_default_usage, NULL, 0 },
1398 { "set-default", cmd_subvol_set_default,
1399 cmd_subvol_set_default_usage, NULL, 0 },
1400 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1402 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1403 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1408 int cmd_subvolume(int argc, char **argv)
1410 return handle_command_group(&subvolume_cmd_group, argc, argv);