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 wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids,
49 enum btrfs_util_error err;
54 for (i = 0; i < count; i++) {
57 err = btrfs_util_subvolume_info_fd(fd, ids[i], NULL);
58 if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
59 printf("Subvolume id %" PRIu64 " is gone\n",
63 error_btrfs_util(err);
71 sleep(sleep_interval);
77 static const char * const subvolume_cmd_group_usage[] = {
78 "btrfs subvolume <command> <args>",
82 static const char * const cmd_subvol_create_usage[] = {
83 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
85 "Create a subvolume <name> in <dest>. If <dest> is not given",
86 "subvolume <name> will be created in the current directory.",
88 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
89 " option can be given multiple times.",
93 static int cmd_subvol_create(int argc, char **argv)
102 struct btrfs_qgroup_inherit *inherit = NULL;
103 DIR *dirstream = NULL;
106 int c = getopt(argc, argv, "c:i:");
112 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
119 res = qgroup_inherit_add_group(&inherit, optarg);
126 usage(cmd_subvol_create_usage);
130 if (check_argc_exact(argc - optind, 1))
131 usage(cmd_subvol_create_usage);
135 retval = 1; /* failure */
136 res = test_isdir(dst);
137 if (res < 0 && res != -ENOENT) {
138 error("cannot access %s: %s", dst, strerror(-res));
142 error("target path already exists: %s", dst);
146 dupname = strdup(dst);
147 newname = basename(dupname);
148 dupdir = strdup(dst);
149 dstdir = dirname(dupdir);
151 if (!test_issubvolname(newname)) {
152 error("invalid subvolume name: %s", newname);
156 len = strlen(newname);
157 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
158 error("subvolume name too long: %s", newname);
162 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
166 printf("Create subvolume '%s/%s'\n", dstdir, newname);
168 struct btrfs_ioctl_vol_args_v2 args;
170 memset(&args, 0, sizeof(args));
171 strncpy_null(args.name, newname);
172 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
173 args.size = qgroup_inherit_size(inherit);
174 args.qgroup_inherit = inherit;
176 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
178 struct btrfs_ioctl_vol_args args;
180 memset(&args, 0, sizeof(args));
181 strncpy_null(args.name, newname);
183 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
187 error("cannot create subvolume: %m");
191 retval = 0; /* success */
193 close_file_or_dir(fddst, dirstream);
201 static int wait_for_commit(int fd)
203 enum btrfs_util_error err;
206 err = btrfs_util_start_sync_fd(fd, &transid);
210 err = btrfs_util_wait_sync_fd(fd, transid);
217 static const char * const cmd_subvol_delete_usage[] = {
218 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
219 "Delete subvolume(s)",
220 "Delete subvolumes from the filesystem. The corresponding directory",
221 "is removed instantly but the data blocks are removed later.",
222 "The deletion does not involve full commit by default due to",
223 "performance reasons (as a consequence, the subvolume may appear again",
224 "after a crash). Use one of the --commit options to wait until the",
225 "operation is safely stored on the media.",
227 "-c|--commit-after wait for transaction commit at the end of the operation",
228 "-C|--commit-each wait for transaction commit after deleting each subvolume",
229 "-v|--verbose verbose output of operations",
233 static int cmd_subvol_delete(int argc, char **argv)
238 char *dname, *vname, *cpath;
239 char *dupdname = NULL;
240 char *dupvname = NULL;
242 DIR *dirstream = NULL;
245 u8 fsid[BTRFS_FSID_SIZE];
246 char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
247 struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, };
248 enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 };
249 enum btrfs_util_error err;
253 static const struct option long_options[] = {
254 {"commit-after", no_argument, NULL, 'c'},
255 {"commit-each", no_argument, NULL, 'C'},
256 {"verbose", no_argument, NULL, 'v'},
260 c = getopt_long(argc, argv, "cCv", long_options, NULL);
266 commit_mode = COMMIT_AFTER;
269 commit_mode = COMMIT_EACH;
275 usage(cmd_subvol_delete_usage);
279 if (check_argc_min(argc - optind, 1))
280 usage(cmd_subvol_delete_usage);
283 printf("Transaction commit: %s\n",
284 !commit_mode ? "none (default)" :
285 commit_mode == COMMIT_AFTER ? "at the end" : "after each");
293 err = btrfs_util_is_subvolume(path);
295 error_btrfs_util(err);
300 cpath = realpath(path, NULL);
303 error("cannot find real path for '%s': %m", path);
306 dupdname = strdup(cpath);
307 dname = dirname(dupdname);
308 dupvname = strdup(cpath);
309 vname = basename(dupvname);
312 fd = btrfs_open_dir(dname, &dirstream, 1);
318 printf("Delete subvolume (%s): '%s/%s'\n",
319 commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc)
320 ? "commit" : "no-commit", dname, vname);
322 err = btrfs_util_delete_subvolume_fd(fd, vname, 0);
324 error_btrfs_util(err);
329 if (commit_mode == COMMIT_EACH) {
330 res = wait_for_commit(fd);
332 error("unable to wait for commit after '%s': %m", path);
335 } else if (commit_mode == COMMIT_AFTER) {
336 res = get_fsid(dname, fsid, 0);
338 error("unable to get fsid for '%s': %s",
339 path, strerror(-res));
341 "delete suceeded but commit may not be done in the end");
346 if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) {
348 uuid_unparse(fsid, uuidbuf);
349 printf(" new fs is found for '%s', fsid: %s\n",
353 * This is the first time a subvolume on this
354 * filesystem is deleted, keep fd in order to issue
355 * SYNC ioctl in the end
362 close_file_or_dir(fd, dirstream);
374 if (commit_mode == COMMIT_AFTER) {
378 * Traverse seen_fsid_hash and issue SYNC ioctl on each
381 for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) {
382 struct seen_fsid *seen = seen_fsid_hash[slot];
385 res = wait_for_commit(seen->fd);
387 uuid_unparse(seen->fsid, uuidbuf);
389 "unable to do final sync after deletion: %m, fsid: %s",
392 } else if (verbose > 0) {
393 uuid_unparse(seen->fsid, uuidbuf);
394 printf("final sync is done for fsid: %s\n",
400 /* fd will also be closed in free_seen_fsid */
401 free_seen_fsid(seen_fsid_hash);
409 * - uppercase for filters and sort options
410 * - lowercase for enabling specific items in the output
412 static const char * const cmd_subvol_list_usage[] = {
413 "btrfs subvolume list [options] <path>",
414 "List subvolumes and snapshots in the filesystem.",
417 "-o print only subvolumes below specified path",
418 "-a print all the subvolumes in the filesystem and",
419 " distinguish absolute and relative path with respect",
420 " to the given <path>",
423 "-p print parent ID",
424 "-c print the ogeneration of the subvolume",
425 "-g print the generation of the subvolume",
426 "-u print the uuid of subvolumes (and snapshots)",
427 "-q print the parent uuid of the snapshots",
428 "-R print the uuid of the received snapshots",
431 "-s list only snapshots",
432 "-r list readonly subvolumes (including snapshots)",
433 "-d list deleted subvolumes that are not yet cleaned",
436 "-t print the result as a table",
440 " filter the subvolumes by generation",
441 " (+value: >= value; -value: <= value; value: = value)",
443 " filter the subvolumes by ogeneration",
444 " (+value: >= value; -value: <= value; value: = value)",
445 "--sort=gen,ogen,rootid,path",
446 " list the subvolume in order of gen, ogen, rootid or path",
447 " you also can add '+' or '-' in front of each items.",
448 " (+:ascending, -:descending, ascending default)",
452 static int cmd_subvol_list(int argc, char **argv)
454 struct btrfs_list_filter_set *filter_set;
455 struct btrfs_list_comparer_set *comparer_set;
459 int ret = -1, uerr = 0;
462 int is_only_in_path = 0;
463 DIR *dirstream = NULL;
464 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
466 filter_set = btrfs_list_alloc_filter_set();
467 comparer_set = btrfs_list_alloc_comparer_set();
471 static const struct option long_options[] = {
472 {"sort", required_argument, NULL, 'S'},
476 c = getopt_long(argc, argv,
477 "acdgopqsurRG:C:t", long_options, NULL);
483 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
489 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
492 btrfs_list_setup_filter(&filter_set,
493 BTRFS_LIST_FILTER_DELETED,
497 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
503 layout = BTRFS_LIST_LAYOUT_TABLE;
506 btrfs_list_setup_filter(&filter_set,
507 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
509 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
510 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
513 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
516 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
519 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
522 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
525 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
526 ret = btrfs_list_parse_filter_string(optarg,
528 BTRFS_LIST_FILTER_GEN);
536 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
537 ret = btrfs_list_parse_filter_string(optarg,
539 BTRFS_LIST_FILTER_CGEN);
546 ret = btrfs_list_parse_sort_string(optarg,
560 if (check_argc_exact(argc - optind, 1)) {
565 subvol = argv[optind];
566 fd = btrfs_open_dir(subvol, &dirstream, 1);
569 error("can't access '%s'", subvol);
574 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
577 ret = btrfs_list_get_path_rootid(fd, &top_id);
582 btrfs_list_setup_filter(&filter_set,
583 BTRFS_LIST_FILTER_FULL_PATH,
585 else if (is_only_in_path)
586 btrfs_list_setup_filter(&filter_set,
587 BTRFS_LIST_FILTER_TOPID_EQUAL,
590 /* by default we shall print the following columns*/
591 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
592 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
593 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
594 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
596 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
597 layout, !is_list_all && !is_only_in_path, NULL);
600 close_file_or_dir(fd, dirstream);
606 usage(cmd_subvol_list_usage);
610 static const char * const cmd_subvol_snapshot_usage[] = {
611 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
612 "Create a snapshot of the subvolume",
613 "Create a writable/readonly snapshot of the subvolume <source> with",
614 "the name <name> in the <dest> directory. If only <dest> is given,",
615 "the subvolume will be named the basename of <source>.",
617 "-r create a readonly snapshot",
618 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
619 " option can be given multiple times.",
623 static int cmd_subvol_snapshot(int argc, char **argv)
627 int fd = -1, fddst = -1;
628 int len, readonly = 0;
629 char *dupname = NULL;
633 struct btrfs_ioctl_vol_args_v2 args;
634 struct btrfs_qgroup_inherit *inherit = NULL;
635 DIR *dirstream1 = NULL, *dirstream2 = NULL;
637 memset(&args, 0, sizeof(args));
639 int c = getopt(argc, argv, "c:i:r");
645 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
652 res = qgroup_inherit_add_group(&inherit, optarg);
662 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
669 usage(cmd_subvol_snapshot_usage);
673 if (check_argc_exact(argc - optind, 2))
674 usage(cmd_subvol_snapshot_usage);
676 subvol = argv[optind];
677 dst = argv[optind + 1];
679 retval = 1; /* failure */
680 res = test_issubvolume(subvol);
682 error("cannot access subvolume %s: %s", subvol, strerror(-res));
686 error("not a subvolume: %s", subvol);
690 res = test_isdir(dst);
691 if (res < 0 && res != -ENOENT) {
692 error("cannot access %s: %s", dst, strerror(-res));
696 error("'%s' exists and it is not a directory", dst);
701 dupname = strdup(subvol);
702 newname = basename(dupname);
705 dupname = strdup(dst);
706 newname = basename(dupname);
707 dupdir = strdup(dst);
708 dstdir = dirname(dupdir);
711 if (!test_issubvolname(newname)) {
712 error("invalid snapshot name '%s'", newname);
716 len = strlen(newname);
717 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
718 error("snapshot name too long '%s'", newname);
722 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
726 fd = btrfs_open_dir(subvol, &dirstream2, 1);
731 args.flags |= BTRFS_SUBVOL_RDONLY;
732 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
733 subvol, dstdir, newname);
735 printf("Create a snapshot of '%s' in '%s/%s'\n",
736 subvol, dstdir, newname);
741 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
742 args.size = qgroup_inherit_size(inherit);
743 args.qgroup_inherit = inherit;
745 strncpy_null(args.name, newname);
747 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
750 error("cannot snapshot '%s': %m", subvol);
754 retval = 0; /* success */
757 close_file_or_dir(fddst, dirstream1);
758 close_file_or_dir(fd, dirstream2);
766 static const char * const cmd_subvol_get_default_usage[] = {
767 "btrfs subvolume get-default <path>",
768 "Get the default subvolume of a filesystem",
772 static int cmd_subvol_get_default(int argc, char **argv)
777 DIR *dirstream = NULL;
778 enum btrfs_util_error err;
779 struct btrfs_util_subvolume_info subvol;
782 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
784 if (check_argc_exact(argc - optind, 1))
785 usage(cmd_subvol_get_default_usage);
787 fd = btrfs_open_dir(argv[1], &dirstream, 1);
791 err = btrfs_util_get_default_subvolume_fd(fd, &default_id);
793 error_btrfs_util(err);
797 /* no need to resolve roots if FS_TREE is default */
798 if (default_id == BTRFS_FS_TREE_OBJECTID) {
799 printf("ID 5 (FS_TREE)\n");
804 err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol);
806 error_btrfs_util(err);
810 err = btrfs_util_subvolume_path_fd(fd, default_id, &path);
812 error_btrfs_util(err);
816 printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n",
817 subvol.id, subvol.generation, subvol.parent_id, path);
823 close_file_or_dir(fd, dirstream);
827 static const char * const cmd_subvol_set_default_usage[] = {
828 "btrfs subvolume set-default <subvolume>\n"
829 "btrfs subvolume set-default <subvolid> <path>",
830 "Set the default subvolume of the filesystem mounted as default.",
831 "The subvolume can be specified by its path,",
832 "or the pair of subvolume id and path to the filesystem.",
836 static int cmd_subvol_set_default(int argc, char **argv)
840 enum btrfs_util_error err;
842 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
844 if (check_argc_min(argc - optind, 1) ||
845 check_argc_max(argc - optind, 2))
846 usage(cmd_subvol_set_default_usage);
848 if (argc - optind == 1) {
849 /* path to the subvolume is specified */
853 /* subvol id and path to the filesystem are specified */
854 objectid = arg_strtou64(argv[optind]);
855 path = argv[optind + 1];
858 err = btrfs_util_set_default_subvolume(path, objectid);
860 error_btrfs_util(err);
866 static const char * const cmd_subvol_find_new_usage[] = {
867 "btrfs subvolume find-new <path> <lastgen>",
868 "List the recently modified files in a filesystem",
872 static int cmd_subvol_find_new(int argc, char **argv)
878 DIR *dirstream = NULL;
879 enum btrfs_util_error err;
881 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
883 if (check_argc_exact(argc - optind, 2))
884 usage(cmd_subvol_find_new_usage);
886 subvol = argv[optind];
887 last_gen = arg_strtou64(argv[optind + 1]);
889 ret = test_issubvolume(subvol);
891 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
895 error("not a subvolume: %s", subvol);
899 fd = btrfs_open_dir(subvol, &dirstream, 1);
903 err = btrfs_util_sync_fd(fd);
905 error_btrfs_util(err);
906 close_file_or_dir(fd, dirstream);
910 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
911 close_file_or_dir(fd, dirstream);
915 static const char * const cmd_subvol_show_usage[] = {
916 "btrfs subvolume show [options] <subvol-path>|<mnt>",
917 "Show more information about the subvolume",
918 "-r|--rootid rootid of the subvolume",
919 "-u|--uuid uuid of the subvolume",
921 "If no option is specified, <subvol-path> will be shown, otherwise",
922 "the rootid or uuid are resolved relative to the <mnt> path.",
926 static int cmd_subvol_show(int argc, char **argv)
929 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
930 char *fullpath = NULL;
933 DIR *dirstream1 = NULL;
937 u8 uuid_arg[BTRFS_UUID_SIZE];
938 struct btrfs_util_subvolume_iterator *iter;
939 struct btrfs_util_subvolume_info subvol;
940 char *subvol_path = NULL;
941 enum btrfs_util_error err;
945 static const struct option long_options[] = {
946 { "rootid", required_argument, NULL, 'r'},
947 { "uuid", required_argument, NULL, 'u'},
951 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
957 rootid_arg = arg_strtou64(optarg);
961 uuid_parse(optarg, uuid_arg);
965 usage(cmd_subvol_show_usage);
969 if (check_argc_exact(argc - optind, 1))
970 usage(cmd_subvol_show_usage);
972 if (by_rootid && by_uuid) {
974 "options --rootid and --uuid cannot be used at the same time");
975 usage(cmd_subvol_show_usage);
978 fullpath = realpath(argv[optind], NULL);
980 error("cannot find real path for '%s': %m", argv[optind]);
984 fd = open_file_or_dir(fullpath, &dirstream1);
986 error("can't access '%s'", fullpath);
991 err = btrfs_util_create_subvolume_iterator_fd(fd,
992 BTRFS_FS_TREE_OBJECTID,
995 error_btrfs_util(err);
1000 err = btrfs_util_subvolume_iterator_next_info(iter,
1003 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1004 uuid_unparse(uuid_arg, uuidparse);
1005 error("can't find uuid '%s' on '%s'", uuidparse,
1007 btrfs_util_destroy_subvolume_iterator(iter);
1010 error_btrfs_util(err);
1011 btrfs_util_destroy_subvolume_iterator(iter);
1015 if (uuid_compare(subvol.uuid, uuid_arg) == 0)
1020 btrfs_util_destroy_subvolume_iterator(iter);
1023 * If !by_rootid, rootid_arg = 0, which means find the
1024 * subvolume ID of the fd and use that.
1026 err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
1028 error_btrfs_util(err);
1032 err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
1034 error_btrfs_util(err);
1040 /* print the info */
1041 printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
1042 printf("\tName: \t\t\t%s\n",
1043 (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
1044 basename(subvol_path)));
1046 if (uuid_is_null(subvol.uuid))
1047 strcpy(uuidparse, "-");
1049 uuid_unparse(subvol.uuid, uuidparse);
1050 printf("\tUUID: \t\t\t%s\n", uuidparse);
1052 if (uuid_is_null(subvol.parent_uuid))
1053 strcpy(uuidparse, "-");
1055 uuid_unparse(subvol.parent_uuid, uuidparse);
1056 printf("\tParent UUID: \t\t%s\n", uuidparse);
1058 if (uuid_is_null(subvol.received_uuid))
1059 strcpy(uuidparse, "-");
1061 uuid_unparse(subvol.received_uuid, uuidparse);
1062 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1064 if (subvol.otime.tv_sec) {
1067 localtime_r(&subvol.otime.tv_sec, &tm);
1068 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1071 printf("\tCreation time: \t\t%s\n", tstr);
1073 printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
1074 printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
1075 printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
1076 printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1077 printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
1079 if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1080 printf("\tFlags: \t\t\treadonly\n");
1082 printf("\tFlags: \t\t\t-\n");
1084 /* print the snapshots of the given subvol if any*/
1085 printf("\tSnapshot(s):\n");
1087 err = btrfs_util_create_subvolume_iterator_fd(fd,
1088 BTRFS_FS_TREE_OBJECTID, 0,
1092 struct btrfs_util_subvolume_info subvol2;
1095 err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
1096 if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
1099 error_btrfs_util(err);
1100 btrfs_util_destroy_subvolume_iterator(iter);
1104 if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
1105 printf("\t\t\t\t%s\n", path);
1109 btrfs_util_destroy_subvolume_iterator(iter);
1114 close_file_or_dir(fd, dirstream1);
1119 static const char * const cmd_subvol_sync_usage[] = {
1120 "btrfs subvolume sync <path> [<subvol-id>...]",
1121 "Wait until given subvolume(s) are completely removed from the filesystem.",
1122 "Wait until given subvolume(s) are completely removed from the filesystem",
1124 "If no subvolume id is given, wait until all current deletion requests",
1125 "are completed, but do not wait for subvolumes deleted meanwhile.",
1126 "The status of subvolume ids is checked periodically.",
1128 "-s <N> sleep N seconds between checks (default: 1)",
1132 static int cmd_subvol_sync(int argc, char **argv)
1136 DIR *dirstream = NULL;
1137 uint64_t *ids = NULL;
1139 int sleep_interval = 1;
1140 enum btrfs_util_error err;
1143 int c = getopt(argc, argv, "s:");
1150 sleep_interval = atoi(optarg);
1151 if (sleep_interval < 1) {
1152 error("invalid sleep interval %s", optarg);
1158 usage(cmd_subvol_sync_usage);
1162 if (check_argc_min(argc - optind, 1))
1163 usage(cmd_subvol_sync_usage);
1165 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1172 id_count = argc - optind;
1174 err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
1176 error_btrfs_util(err);
1180 if (id_count == 0) {
1185 ids = malloc(id_count * sizeof(uint64_t));
1187 error("not enough memory");
1192 for (i = 0; i < id_count; i++) {
1196 arg = argv[optind + i];
1198 id = strtoull(arg, NULL, 10);
1200 error("unrecognized subvolume id %s", arg);
1204 if (id < BTRFS_FIRST_FREE_OBJECTID ||
1205 id > BTRFS_LAST_FREE_OBJECTID) {
1206 error("subvolume id %s out of range", arg);
1214 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1218 close_file_or_dir(fd, dirstream);
1223 static const char subvolume_cmd_group_info[] =
1224 "manage subvolumes: create, delete, list, etc";
1226 const struct cmd_group subvolume_cmd_group = {
1227 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1228 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1229 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1230 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1231 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1233 { "get-default", cmd_subvol_get_default,
1234 cmd_subvol_get_default_usage, NULL, 0 },
1235 { "set-default", cmd_subvol_set_default,
1236 cmd_subvol_set_default_usage, NULL, 0 },
1237 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1239 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1240 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1245 int cmd_subvolume(int argc, char **argv)
1247 return handle_command_group(&subvolume_cmd_group, argc, argv);