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 <subvolid> <path>",
862 "Set the default subvolume of a filesystem",
866 static int cmd_subvol_set_default(int argc, char **argv)
872 DIR *dirstream = NULL;
874 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
876 if (check_argc_exact(argc - optind, 2))
877 usage(cmd_subvol_set_default_usage);
879 subvolid = argv[optind];
880 path = argv[optind + 1];
882 objectid = arg_strtou64(subvolid);
884 fd = btrfs_open_dir(path, &dirstream, 1);
888 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
890 close_file_or_dir(fd, dirstream);
892 error("unable to set a new default subvolume: %s",
899 static const char * const cmd_subvol_find_new_usage[] = {
900 "btrfs subvolume find-new <path> <lastgen>",
901 "List the recently modified files in a filesystem",
905 static int cmd_subvol_find_new(int argc, char **argv)
911 DIR *dirstream = NULL;
913 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
915 if (check_argc_exact(argc - optind, 2))
916 usage(cmd_subvol_find_new_usage);
918 subvol = argv[optind];
919 last_gen = arg_strtou64(argv[optind + 1]);
921 ret = test_issubvolume(subvol);
923 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
927 error("not a subvolume: %s", subvol);
931 fd = btrfs_open_dir(subvol, &dirstream, 1);
935 ret = ioctl(fd, BTRFS_IOC_SYNC);
937 error("sync ioctl failed on '%s': %s",
938 subvol, strerror(errno));
939 close_file_or_dir(fd, dirstream);
943 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
944 close_file_or_dir(fd, dirstream);
948 static const char * const cmd_subvol_show_usage[] = {
949 "btrfs subvolume show [options] <subvol-path>|<mnt>",
950 "Show more information about the subvolume",
951 "-r|--rootid rootid of the subvolume",
952 "-u|--uuid uuid of the subvolume",
954 "If no option is specified, <subvol-path> will be shown, otherwise",
955 "the rootid or uuid are resolved relative to the <mnt> path.",
959 static int cmd_subvol_show(int argc, char **argv)
961 struct root_info get_ri;
962 struct btrfs_list_filter_set *filter_set = NULL;
964 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
965 char *fullpath = NULL;
966 char raw_prefix[] = "\t\t\t\t";
969 DIR *dirstream1 = NULL;
973 u8 uuid_arg[BTRFS_UUID_SIZE];
977 static const struct option long_options[] = {
978 { "rootid", required_argument, NULL, 'r'},
979 { "uuid", required_argument, NULL, 'u'},
983 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
989 rootid_arg = arg_strtou64(optarg);
993 uuid_parse(optarg, uuid_arg);
997 usage(cmd_subvol_show_usage);
1001 if (check_argc_exact(argc - optind, 1))
1002 usage(cmd_subvol_show_usage);
1004 if (by_rootid && by_uuid) {
1006 "options --rootid and --uuid cannot be used at the same time");
1007 usage(cmd_subvol_show_usage);
1010 memset(&get_ri, 0, sizeof(get_ri));
1011 fullpath = realpath(argv[optind], NULL);
1013 error("cannot find real path for '%s': %s",
1014 argv[optind], strerror(errno));
1019 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1020 } else if (by_uuid) {
1021 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1023 ret = get_subvol_info(fullpath, &get_ri);
1028 error("Failed to get subvol info %s: %s",
1029 fullpath, strerror(-ret));
1031 error("Failed to get subvol info %s: %d",
1037 /* print the info */
1038 printf("%s\n", get_ri.full_path);
1039 printf("\tName: \t\t\t%s\n", get_ri.name);
1041 if (uuid_is_null(get_ri.uuid))
1042 strcpy(uuidparse, "-");
1044 uuid_unparse(get_ri.uuid, uuidparse);
1045 printf("\tUUID: \t\t\t%s\n", uuidparse);
1047 if (uuid_is_null(get_ri.puuid))
1048 strcpy(uuidparse, "-");
1050 uuid_unparse(get_ri.puuid, uuidparse);
1051 printf("\tParent UUID: \t\t%s\n", uuidparse);
1053 if (uuid_is_null(get_ri.ruuid))
1054 strcpy(uuidparse, "-");
1056 uuid_unparse(get_ri.ruuid, uuidparse);
1057 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1062 localtime_r(&get_ri.otime, &tm);
1063 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1066 printf("\tCreation time: \t\t%s\n", tstr);
1068 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1069 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1070 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1071 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1072 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1074 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1075 printf("\tFlags: \t\t\treadonly\n");
1077 printf("\tFlags: \t\t\t-\n");
1079 /* print the snapshots of the given subvol if any*/
1080 printf("\tSnapshot(s):\n");
1081 filter_set = btrfs_list_alloc_filter_set();
1082 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1083 (u64)(unsigned long)get_ri.uuid);
1084 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1086 fd = open_file_or_dir(fullpath, &dirstream1);
1088 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1091 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1098 free(get_ri.full_path);
1101 close_file_or_dir(fd, dirstream1);
1106 static const char * const cmd_subvol_sync_usage[] = {
1107 "btrfs subvolume sync <path> [<subvol-id>...]",
1108 "Wait until given subvolume(s) are completely removed from the filesystem.",
1109 "Wait until given subvolume(s) are completely removed from the filesystem",
1111 "If no subvolume id is given, wait until all current deletion requests",
1112 "are completed, but do not wait for subvolumes deleted meanwhile.",
1113 "The status of subvolume ids is checked periodically.",
1115 "-s <N> sleep N seconds between checks (default: 1)",
1121 * If we're looking for any dead subvolume, take a shortcut and look
1122 * for any ORPHAN_ITEMs in the tree root
1124 static int fs_has_dead_subvolumes(int fd)
1127 struct btrfs_ioctl_search_args args;
1128 struct btrfs_ioctl_search_key *sk = &args.key;
1129 struct btrfs_ioctl_search_header sh;
1130 u64 min_subvolid = 0;
1133 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1134 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1135 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1136 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1137 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1138 sk->min_offset = min_subvolid;
1139 sk->max_offset = (u64)-1;
1140 sk->min_transid = 0;
1141 sk->max_transid = (u64)-1;
1144 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1151 memcpy(&sh, args.buf, sizeof(sh));
1152 min_subvolid = sh.offset;
1155 * Verify that the root item is really there and we haven't hit
1158 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1159 sk->min_objectid = min_subvolid;
1160 sk->max_objectid = min_subvolid;
1161 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1162 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1164 sk->max_offset = (u64)-1;
1165 sk->min_transid = 0;
1166 sk->max_transid = (u64)-1;
1169 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1174 * Stale orphan, try the next one
1176 if (!sk->nr_items) {
1185 #define SUBVOL_ID_BATCH 1024
1188 * Enumerate all dead subvolumes that exist in the filesystem.
1189 * Fill @ids and reallocate to bigger size if needed.
1191 static int enumerate_dead_subvols(int fd, u64 **ids)
1194 struct btrfs_ioctl_search_args args;
1195 struct btrfs_ioctl_search_key *sk = &args.key;
1199 memset(&args, 0, sizeof(args));
1201 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1202 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1203 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1204 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1205 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1207 sk->max_offset = (u64)-1;
1208 sk->min_transid = 0;
1209 sk->max_transid = (u64)-1;
1210 sk->nr_items = 4096;
1214 struct btrfs_ioctl_search_header *sh;
1218 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1226 for (i = 0; i < sk->nr_items; i++) {
1227 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1230 if (btrfs_search_header_type(sh)
1231 == BTRFS_ORPHAN_ITEM_KEY) {
1235 count += SUBVOL_ID_BATCH;
1236 newids = (u64*)realloc(*ids,
1237 count * sizeof(u64));
1242 (*ids)[idx] = btrfs_search_header_offset(sh);
1245 off += btrfs_search_header_len(sh);
1247 sk->min_objectid = btrfs_search_header_objectid(sh);
1248 sk->min_type = btrfs_search_header_type(sh);
1249 sk->min_offset = btrfs_search_header_offset(sh);
1251 if (sk->min_offset < (u64)-1)
1255 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1257 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1264 static int cmd_subvol_sync(int argc, char **argv)
1269 DIR *dirstream = NULL;
1272 int sleep_interval = 1;
1275 int c = getopt(argc, argv, "s:");
1282 sleep_interval = atoi(optarg);
1283 if (sleep_interval < 1) {
1284 error("invalid sleep interval %s", optarg);
1290 usage(cmd_subvol_sync_usage);
1294 if (check_argc_min(argc - optind, 1))
1295 usage(cmd_subvol_sync_usage);
1297 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1304 id_count = argc - optind;
1306 id_count = enumerate_dead_subvols(fd, &ids);
1308 error("can't enumerate dead subvolumes: %s",
1309 strerror(-id_count));
1313 if (id_count == 0) {
1318 ids = (u64*)malloc(id_count * sizeof(u64));
1320 error("not enough memory");
1325 for (i = 0; i < id_count; i++) {
1329 arg = argv[optind + i];
1331 id = strtoull(arg, NULL, 10);
1333 error("unrecognized subvolume id %s", arg);
1337 if (id < BTRFS_FIRST_FREE_OBJECTID
1338 || id > BTRFS_LAST_FREE_OBJECTID) {
1339 error("subvolume id %s out of range", arg);
1347 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1351 close_file_or_dir(fd, dirstream);
1356 static const char subvolume_cmd_group_info[] =
1357 "manage subvolumes: create, delete, list, etc";
1359 const struct cmd_group subvolume_cmd_group = {
1360 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1361 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1362 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1363 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1364 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1366 { "get-default", cmd_subvol_get_default,
1367 cmd_subvol_get_default_usage, NULL, 0 },
1368 { "set-default", cmd_subvol_set_default,
1369 cmd_subvol_set_default_usage, NULL, 0 },
1370 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1372 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1373 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1378 int cmd_subvolume(int argc, char **argv)
1380 return handle_command_group(&subvolume_cmd_group, argc, argv);