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)
877 enum btrfs_util_error err;
879 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
881 if (check_argc_min(argc - optind, 1) ||
882 check_argc_max(argc - optind, 2))
883 usage(cmd_subvol_set_default_usage);
885 if (argc - optind == 1) {
886 /* path to the subvolume is specified */
890 /* subvol id and path to the filesystem are specified */
891 objectid = arg_strtou64(argv[optind]);
892 path = argv[optind + 1];
895 err = btrfs_util_set_default_subvolume(path, objectid);
897 error_btrfs_util(err);
903 static const char * const cmd_subvol_find_new_usage[] = {
904 "btrfs subvolume find-new <path> <lastgen>",
905 "List the recently modified files in a filesystem",
909 static int cmd_subvol_find_new(int argc, char **argv)
915 DIR *dirstream = NULL;
916 enum btrfs_util_error err;
918 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
920 if (check_argc_exact(argc - optind, 2))
921 usage(cmd_subvol_find_new_usage);
923 subvol = argv[optind];
924 last_gen = arg_strtou64(argv[optind + 1]);
926 ret = test_issubvolume(subvol);
928 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
932 error("not a subvolume: %s", subvol);
936 fd = btrfs_open_dir(subvol, &dirstream, 1);
940 err = btrfs_util_sync_fd(fd);
942 error_btrfs_util(err);
943 close_file_or_dir(fd, dirstream);
947 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
948 close_file_or_dir(fd, dirstream);
952 static const char * const cmd_subvol_show_usage[] = {
953 "btrfs subvolume show [options] <subvol-path>|<mnt>",
954 "Show more information about the subvolume",
955 "-r|--rootid rootid of the subvolume",
956 "-u|--uuid uuid of the subvolume",
958 "If no option is specified, <subvol-path> will be shown, otherwise",
959 "the rootid or uuid are resolved relative to the <mnt> path.",
963 static int cmd_subvol_show(int argc, char **argv)
965 struct root_info get_ri;
966 struct btrfs_list_filter_set *filter_set = NULL;
968 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
969 char *fullpath = NULL;
970 char raw_prefix[] = "\t\t\t\t";
973 DIR *dirstream1 = NULL;
977 u8 uuid_arg[BTRFS_UUID_SIZE];
981 static const struct option long_options[] = {
982 { "rootid", required_argument, NULL, 'r'},
983 { "uuid", required_argument, NULL, 'u'},
987 c = getopt_long(argc, argv, "r:u:", long_options, NULL);
993 rootid_arg = arg_strtou64(optarg);
997 uuid_parse(optarg, uuid_arg);
1001 usage(cmd_subvol_show_usage);
1005 if (check_argc_exact(argc - optind, 1))
1006 usage(cmd_subvol_show_usage);
1008 if (by_rootid && by_uuid) {
1010 "options --rootid and --uuid cannot be used at the same time");
1011 usage(cmd_subvol_show_usage);
1014 memset(&get_ri, 0, sizeof(get_ri));
1015 fullpath = realpath(argv[optind], NULL);
1017 error("cannot find real path for '%s': %m", argv[optind]);
1022 ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg);
1023 } else if (by_uuid) {
1024 ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg);
1026 ret = get_subvol_info(fullpath, &get_ri);
1031 error("Failed to get subvol info %s: %s",
1032 fullpath, strerror(-ret));
1034 error("Failed to get subvol info %s: %d",
1040 /* print the info */
1041 printf("%s\n", get_ri.full_path);
1042 printf("\tName: \t\t\t%s\n", get_ri.name);
1044 if (uuid_is_null(get_ri.uuid))
1045 strcpy(uuidparse, "-");
1047 uuid_unparse(get_ri.uuid, uuidparse);
1048 printf("\tUUID: \t\t\t%s\n", uuidparse);
1050 if (uuid_is_null(get_ri.puuid))
1051 strcpy(uuidparse, "-");
1053 uuid_unparse(get_ri.puuid, uuidparse);
1054 printf("\tParent UUID: \t\t%s\n", uuidparse);
1056 if (uuid_is_null(get_ri.ruuid))
1057 strcpy(uuidparse, "-");
1059 uuid_unparse(get_ri.ruuid, uuidparse);
1060 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1065 localtime_r(&get_ri.otime, &tm);
1066 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1069 printf("\tCreation time: \t\t%s\n", tstr);
1071 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1072 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1073 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1074 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1075 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1077 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1078 printf("\tFlags: \t\t\treadonly\n");
1080 printf("\tFlags: \t\t\t-\n");
1082 /* print the snapshots of the given subvol if any*/
1083 printf("\tSnapshot(s):\n");
1084 filter_set = btrfs_list_alloc_filter_set();
1085 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1086 (u64)(unsigned long)get_ri.uuid);
1087 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1089 fd = open_file_or_dir(fullpath, &dirstream1);
1091 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1094 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1101 free(get_ri.full_path);
1104 close_file_or_dir(fd, dirstream1);
1109 static const char * const cmd_subvol_sync_usage[] = {
1110 "btrfs subvolume sync <path> [<subvol-id>...]",
1111 "Wait until given subvolume(s) are completely removed from the filesystem.",
1112 "Wait until given subvolume(s) are completely removed from the filesystem",
1114 "If no subvolume id is given, wait until all current deletion requests",
1115 "are completed, but do not wait for subvolumes deleted meanwhile.",
1116 "The status of subvolume ids is checked periodically.",
1118 "-s <N> sleep N seconds between checks (default: 1)",
1124 * If we're looking for any dead subvolume, take a shortcut and look
1125 * for any ORPHAN_ITEMs in the tree root
1127 static int fs_has_dead_subvolumes(int fd)
1130 struct btrfs_ioctl_search_args args;
1131 struct btrfs_ioctl_search_key *sk = &args.key;
1132 struct btrfs_ioctl_search_header sh;
1133 u64 min_subvolid = 0;
1136 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1137 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1138 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1139 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1140 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1141 sk->min_offset = min_subvolid;
1142 sk->max_offset = (u64)-1;
1143 sk->min_transid = 0;
1144 sk->max_transid = (u64)-1;
1147 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1154 memcpy(&sh, args.buf, sizeof(sh));
1155 min_subvolid = sh.offset;
1158 * Verify that the root item is really there and we haven't hit
1161 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1162 sk->min_objectid = min_subvolid;
1163 sk->max_objectid = min_subvolid;
1164 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1165 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1167 sk->max_offset = (u64)-1;
1168 sk->min_transid = 0;
1169 sk->max_transid = (u64)-1;
1172 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1177 * Stale orphan, try the next one
1179 if (!sk->nr_items) {
1188 #define SUBVOL_ID_BATCH 1024
1191 * Enumerate all dead subvolumes that exist in the filesystem.
1192 * Fill @ids and reallocate to bigger size if needed.
1194 static int enumerate_dead_subvols(int fd, u64 **ids)
1197 struct btrfs_ioctl_search_args args;
1198 struct btrfs_ioctl_search_key *sk = &args.key;
1202 memset(&args, 0, sizeof(args));
1204 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1205 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1206 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1207 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1208 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1210 sk->max_offset = (u64)-1;
1211 sk->min_transid = 0;
1212 sk->max_transid = (u64)-1;
1213 sk->nr_items = 4096;
1217 struct btrfs_ioctl_search_header *sh;
1221 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1229 for (i = 0; i < sk->nr_items; i++) {
1230 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1233 if (btrfs_search_header_type(sh)
1234 == BTRFS_ORPHAN_ITEM_KEY) {
1238 count += SUBVOL_ID_BATCH;
1239 newids = (u64*)realloc(*ids,
1240 count * sizeof(u64));
1245 (*ids)[idx] = btrfs_search_header_offset(sh);
1248 off += btrfs_search_header_len(sh);
1250 sk->min_objectid = btrfs_search_header_objectid(sh);
1251 sk->min_type = btrfs_search_header_type(sh);
1252 sk->min_offset = btrfs_search_header_offset(sh);
1254 if (sk->min_offset < (u64)-1)
1258 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1260 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1267 static int cmd_subvol_sync(int argc, char **argv)
1272 DIR *dirstream = NULL;
1275 int sleep_interval = 1;
1278 int c = getopt(argc, argv, "s:");
1285 sleep_interval = atoi(optarg);
1286 if (sleep_interval < 1) {
1287 error("invalid sleep interval %s", optarg);
1293 usage(cmd_subvol_sync_usage);
1297 if (check_argc_min(argc - optind, 1))
1298 usage(cmd_subvol_sync_usage);
1300 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1307 id_count = argc - optind;
1309 id_count = enumerate_dead_subvols(fd, &ids);
1311 error("can't enumerate dead subvolumes: %s",
1312 strerror(-id_count));
1316 if (id_count == 0) {
1321 ids = (u64*)malloc(id_count * sizeof(u64));
1323 error("not enough memory");
1328 for (i = 0; i < id_count; i++) {
1332 arg = argv[optind + i];
1334 id = strtoull(arg, NULL, 10);
1336 error("unrecognized subvolume id %s", arg);
1340 if (id < BTRFS_FIRST_FREE_OBJECTID
1341 || id > BTRFS_LAST_FREE_OBJECTID) {
1342 error("subvolume id %s out of range", arg);
1350 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1354 close_file_or_dir(fd, dirstream);
1359 static const char subvolume_cmd_group_info[] =
1360 "manage subvolumes: create, delete, list, etc";
1362 const struct cmd_group subvolume_cmd_group = {
1363 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1364 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1365 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1366 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1367 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1369 { "get-default", cmd_subvol_get_default,
1370 cmd_subvol_get_default_usage, NULL, 0 },
1371 { "set-default", cmd_subvol_set_default,
1372 cmd_subvol_set_default_usage, NULL, 0 },
1373 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1375 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1376 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1381 int cmd_subvolume(int argc, char **argv)
1383 return handle_command_group(&subvolume_cmd_group, argc, argv);