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] <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': %s", subvol, strerror(errno));
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 struct btrfs_list_filter_set *filter_set;
808 DIR *dirstream = NULL;
810 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
812 if (check_argc_exact(argc - optind, 1))
813 usage(cmd_subvol_get_default_usage);
816 fd = btrfs_open_dir(subvol, &dirstream, 1);
820 ret = btrfs_list_get_default_subvolume(fd, &default_id);
822 error("failed to look up default subvolume: %s",
828 if (default_id == 0) {
829 error("'default' dir item not found");
833 /* no need to resolve roots if FS_TREE is default */
834 if (default_id == BTRFS_FS_TREE_OBJECTID) {
835 printf("ID 5 (FS_TREE)\n");
840 filter_set = btrfs_list_alloc_filter_set();
841 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
844 /* by default we shall print the following columns*/
845 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
846 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
847 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
848 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
850 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
851 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
856 close_file_or_dir(fd, dirstream);
860 static const char * const cmd_subvol_set_default_usage[] = {
861 "btrfs subvolume set-default <subvolume>\n"
862 "btrfs subvolume set-default <subvolid> <path>",
863 "Set the default subvolume of the filesystem mounted as default.",
864 "The subvolume can be specified by its path,",
865 "or the pair of subvolume id and path to the filesystem.",
869 static int cmd_subvol_set_default(int argc, char **argv)
875 DIR *dirstream = NULL;
877 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
879 if (check_argc_min(argc - optind, 1) ||
880 check_argc_max(argc - optind, 2))
881 usage(cmd_subvol_set_default_usage);
883 if (argc - optind == 1) {
884 /* path to the subvolume is specified */
887 ret = test_issubvolume(path);
889 error("stat error: %s", strerror(-ret));
892 error("'%s' is not a subvolume", path);
896 fd = btrfs_open_dir(path, &dirstream, 1);
900 ret = lookup_path_rootid(fd, &objectid);
902 error("unable to get subvol id: %s", strerror(-ret));
903 close_file_or_dir(fd, dirstream);
907 /* subvol id and path to the filesystem are specified */
908 subvolid = argv[optind];
909 path = argv[optind + 1];
910 objectid = arg_strtou64(subvolid);
912 fd = btrfs_open_dir(path, &dirstream, 1);
917 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
919 close_file_or_dir(fd, dirstream);
921 error("unable to set a new default subvolume: %s",
928 static const char * const cmd_subvol_find_new_usage[] = {
929 "btrfs subvolume find-new <path> <lastgen>",
930 "List the recently modified files in a filesystem",
934 static int cmd_subvol_find_new(int argc, char **argv)
940 DIR *dirstream = NULL;
942 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
944 if (check_argc_exact(argc - optind, 2))
945 usage(cmd_subvol_find_new_usage);
947 subvol = argv[optind];
948 last_gen = arg_strtou64(argv[optind + 1]);
950 ret = test_issubvolume(subvol);
952 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
956 error("not a subvolume: %s", subvol);
960 fd = btrfs_open_dir(subvol, &dirstream, 1);
964 ret = ioctl(fd, BTRFS_IOC_SYNC);
966 error("sync ioctl failed on '%s': %s",
967 subvol, strerror(errno));
968 close_file_or_dir(fd, dirstream);
972 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
973 close_file_or_dir(fd, dirstream);
977 static const char * const cmd_subvol_show_usage[] = {
978 "btrfs subvolume show [options] <subvol-path>|<mnt>",
979 "Show more information about the subvolume",
980 "-r|--rootid rootid of the subvolume",
981 "-u|--uuid uuid of the subvolume",
983 "If no option is specified, <subvol-path> will be shown, otherwise",
984 "the rootid or uuid are resolved relative to the <mnt> path.",
988 static int cmd_subvol_show(int argc, char **argv)
990 struct root_info get_ri;
991 struct btrfs_list_filter_set *filter_set = NULL;
993 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
994 char *fullpath = NULL;
995 char raw_prefix[] = "\t\t\t\t";
998 DIR *dirstream1 = NULL;
1002 u8 uuid_arg[BTRFS_UUID_SIZE];
1006 static const struct option long_options[] = {
1007 { "rootid", required_argument, NULL, 'r'},
1008 { "uuid", required_argument, NULL, 'u'},
1009 { NULL, 0, NULL, 0 }
1012 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
1018 rootid_arg = arg_strtou64(optarg);
1022 uuid_parse(optarg, uuid_arg);
1026 usage(cmd_subvol_show_usage);
1030 if (check_argc_exact(argc - optind, 1))
1031 usage(cmd_subvol_show_usage);
1033 if (by_rootid && by_uuid) {
1035 "options --rootid and --uuid cannot be used at the same time");
1036 usage(cmd_subvol_show_usage);
1039 memset(&get_ri, 0, sizeof(get_ri));
1040 fullpath = realpath(argv[optind], NULL);
1042 error("cannot find real path for '%s': %s",
1043 argv[optind], strerror(errno));
1048 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1049 } else if (by_uuid) {
1050 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1052 ret = get_subvol_info(fullpath, &get_ri);
1057 error("Failed to get subvol info %s: %s",
1058 fullpath, strerror(-ret));
1060 error("Failed to get subvol info %s: %d",
1066 /* print the info */
1067 printf("%s\n", get_ri.full_path);
1068 printf("\tName: \t\t\t%s\n", get_ri.name);
1070 if (uuid_is_null(get_ri.uuid))
1071 strcpy(uuidparse, "-");
1073 uuid_unparse(get_ri.uuid, uuidparse);
1074 printf("\tUUID: \t\t\t%s\n", uuidparse);
1076 if (uuid_is_null(get_ri.puuid))
1077 strcpy(uuidparse, "-");
1079 uuid_unparse(get_ri.puuid, uuidparse);
1080 printf("\tParent UUID: \t\t%s\n", uuidparse);
1082 if (uuid_is_null(get_ri.ruuid))
1083 strcpy(uuidparse, "-");
1085 uuid_unparse(get_ri.ruuid, uuidparse);
1086 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1091 localtime_r(&get_ri.otime, &tm);
1092 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1095 printf("\tCreation time: \t\t%s\n", tstr);
1097 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1098 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1099 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1100 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1101 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1103 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1104 printf("\tFlags: \t\t\treadonly\n");
1106 printf("\tFlags: \t\t\t-\n");
1108 /* print the snapshots of the given subvol if any*/
1109 printf("\tSnapshot(s):\n");
1110 filter_set = btrfs_list_alloc_filter_set();
1111 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1112 (u64)(unsigned long)get_ri.uuid);
1113 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1115 fd = open_file_or_dir(fullpath, &dirstream1);
1117 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1120 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1127 free(get_ri.full_path);
1130 close_file_or_dir(fd, dirstream1);
1135 static const char * const cmd_subvol_sync_usage[] = {
1136 "btrfs subvolume sync <path> [<subvol-id>...]",
1137 "Wait until given subvolume(s) are completely removed from the filesystem.",
1138 "Wait until given subvolume(s) are completely removed from the filesystem",
1140 "If no subvolume id is given, wait until all current deletion requests",
1141 "are completed, but do not wait for subvolumes deleted meanwhile.",
1142 "The status of subvolume ids is checked periodically.",
1144 "-s <N> sleep N seconds between checks (default: 1)",
1150 * If we're looking for any dead subvolume, take a shortcut and look
1151 * for any ORPHAN_ITEMs in the tree root
1153 static int fs_has_dead_subvolumes(int fd)
1156 struct btrfs_ioctl_search_args args;
1157 struct btrfs_ioctl_search_key *sk = &args.key;
1158 struct btrfs_ioctl_search_header sh;
1159 u64 min_subvolid = 0;
1162 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1163 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1164 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1165 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1166 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1167 sk->min_offset = min_subvolid;
1168 sk->max_offset = (u64)-1;
1169 sk->min_transid = 0;
1170 sk->max_transid = (u64)-1;
1173 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1180 memcpy(&sh, args.buf, sizeof(sh));
1181 min_subvolid = sh.offset;
1184 * Verify that the root item is really there and we haven't hit
1187 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1188 sk->min_objectid = min_subvolid;
1189 sk->max_objectid = min_subvolid;
1190 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1191 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1193 sk->max_offset = (u64)-1;
1194 sk->min_transid = 0;
1195 sk->max_transid = (u64)-1;
1198 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1203 * Stale orphan, try the next one
1205 if (!sk->nr_items) {
1214 #define SUBVOL_ID_BATCH 1024
1217 * Enumerate all dead subvolumes that exist in the filesystem.
1218 * Fill @ids and reallocate to bigger size if needed.
1220 static int enumerate_dead_subvols(int fd, u64 **ids)
1223 struct btrfs_ioctl_search_args args;
1224 struct btrfs_ioctl_search_key *sk = &args.key;
1228 memset(&args, 0, sizeof(args));
1230 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1231 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1232 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1233 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1234 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1236 sk->max_offset = (u64)-1;
1237 sk->min_transid = 0;
1238 sk->max_transid = (u64)-1;
1239 sk->nr_items = 4096;
1243 struct btrfs_ioctl_search_header *sh;
1247 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1255 for (i = 0; i < sk->nr_items; i++) {
1256 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1259 if (btrfs_search_header_type(sh)
1260 == BTRFS_ORPHAN_ITEM_KEY) {
1264 count += SUBVOL_ID_BATCH;
1265 newids = (u64*)realloc(*ids,
1266 count * sizeof(u64));
1271 (*ids)[idx] = btrfs_search_header_offset(sh);
1274 off += btrfs_search_header_len(sh);
1276 sk->min_objectid = btrfs_search_header_objectid(sh);
1277 sk->min_type = btrfs_search_header_type(sh);
1278 sk->min_offset = btrfs_search_header_offset(sh);
1280 if (sk->min_offset < (u64)-1)
1284 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1286 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1293 static int cmd_subvol_sync(int argc, char **argv)
1298 DIR *dirstream = NULL;
1301 int sleep_interval = 1;
1304 int c = getopt(argc, argv, "s:");
1311 sleep_interval = atoi(optarg);
1312 if (sleep_interval < 1) {
1313 error("invalid sleep interval %s", optarg);
1319 usage(cmd_subvol_sync_usage);
1323 if (check_argc_min(argc - optind, 1))
1324 usage(cmd_subvol_sync_usage);
1326 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1333 id_count = argc - optind;
1335 id_count = enumerate_dead_subvols(fd, &ids);
1337 error("can't enumerate dead subvolumes: %s",
1338 strerror(-id_count));
1342 if (id_count == 0) {
1347 ids = (u64*)malloc(id_count * sizeof(u64));
1349 error("not enough memory");
1354 for (i = 0; i < id_count; i++) {
1358 arg = argv[optind + i];
1360 id = strtoull(arg, NULL, 10);
1362 error("unrecognized subvolume id %s", arg);
1366 if (id < BTRFS_FIRST_FREE_OBJECTID
1367 || id > BTRFS_LAST_FREE_OBJECTID) {
1368 error("subvolume id %s out of range", arg);
1376 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1380 close_file_or_dir(fd, dirstream);
1385 static const char subvolume_cmd_group_info[] =
1386 "manage subvolumes: create, delete, list, etc";
1388 const struct cmd_group subvolume_cmd_group = {
1389 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1390 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1391 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1392 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1393 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1395 { "get-default", cmd_subvol_get_default,
1396 cmd_subvol_get_default_usage, NULL, 0 },
1397 { "set-default", cmd_subvol_set_default,
1398 cmd_subvol_set_default_usage, NULL, 0 },
1399 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1401 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1402 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1407 int cmd_subvolume(int argc, char **argv)
1409 return handle_command_group(&subvolume_cmd_group, argc, argv);