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"
41 static int is_subvolume_cleaned(int fd, u64 subvolid)
44 struct btrfs_ioctl_search_args args;
45 struct btrfs_ioctl_search_key *sk = &args.key;
47 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
48 sk->min_objectid = subvolid;
49 sk->max_objectid = subvolid;
50 sk->min_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_type = BTRFS_ROOT_ITEM_KEY;
53 sk->max_offset = (u64)-1;
55 sk->max_transid = (u64)-1;
58 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
62 if (sk->nr_items == 0)
68 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
77 for (i = 0; i < count; i++) {
80 ret = is_subvolume_cleaned(fd, ids[i]);
83 "cannot read status of dead subvolume %llu: %s",
84 (unsigned long long)ids[i], strerror(-ret));
88 printf("Subvolume id %llu is gone\n", ids[i]);
96 sleep(sleep_interval);
102 static const char * const subvolume_cmd_group_usage[] = {
103 "btrfs subvolume <command> <args>",
107 static const char * const cmd_subvol_create_usage[] = {
108 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
109 "Create a subvolume",
110 "Create a subvolume <name> in <dest>. If <dest> is not given",
111 "subvolume <name> will be created in the current directory.",
113 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
114 " option can be given multiple times.",
118 static int cmd_subvol_create(int argc, char **argv)
120 int retval, res, len;
122 char *dupname = NULL;
127 struct btrfs_qgroup_inherit *inherit = NULL;
128 DIR *dirstream = NULL;
131 int c = getopt(argc, argv, "c:i:v");
137 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
144 res = qgroup_inherit_add_group(&inherit, optarg);
151 usage(cmd_subvol_create_usage);
155 if (check_argc_exact(argc - optind, 1))
156 usage(cmd_subvol_create_usage);
160 retval = 1; /* failure */
161 res = test_isdir(dst);
162 if (res < 0 && res != -ENOENT) {
163 error("cannot access %s: %s", dst, strerror(-res));
167 error("target path already exists: %s", dst);
171 dupname = strdup(dst);
172 newname = basename(dupname);
173 dupdir = strdup(dst);
174 dstdir = dirname(dupdir);
176 if (!test_issubvolname(newname)) {
177 error("invalid subvolume name: %s", newname);
181 len = strlen(newname);
182 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
183 error("subvolume name too long: %s", newname);
187 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
191 printf("Create subvolume '%s/%s'\n", dstdir, newname);
193 struct btrfs_ioctl_vol_args_v2 args;
195 memset(&args, 0, sizeof(args));
196 strncpy_null(args.name, newname);
197 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
198 args.size = qgroup_inherit_size(inherit);
199 args.qgroup_inherit = inherit;
201 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
203 struct btrfs_ioctl_vol_args args;
205 memset(&args, 0, sizeof(args));
206 strncpy_null(args.name, newname);
208 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
212 error("cannot create subvolume: %s", strerror(errno));
216 retval = 0; /* success */
218 close_file_or_dir(fddst, dirstream);
226 static int wait_for_commit(int fd)
230 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
233 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
236 static const char * const cmd_subvol_delete_usage[] = {
237 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
238 "Delete subvolume(s)",
239 "Delete subvolumes from the filesystem. The corresponding directory",
240 "is removed instantly but the data blocks are removed later.",
241 "The deletion does not involve full commit by default due to",
242 "performance reasons (as a consequence, the subvolume may appear again",
243 "after a crash). Use one of the --commit options to wait until the",
244 "operation is safely stored on the media.",
246 "-c|--commit-after wait for transaction commit at the end of the operation",
247 "-C|--commit-each wait for transaction commit after deleting each subvolume",
251 static int cmd_subvol_delete(int argc, char **argv)
256 struct btrfs_ioctl_vol_args args;
257 char *dname, *vname, *cpath;
258 char *dupdname = NULL;
259 char *dupvname = NULL;
261 DIR *dirstream = NULL;
267 static const struct option long_options[] = {
268 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
269 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
273 c = getopt_long(argc, argv, "cC", long_options, NULL);
288 usage(cmd_subvol_delete_usage);
292 if (check_argc_min(argc - optind, 1))
293 usage(cmd_subvol_delete_usage);
296 printf("Transaction commit: %s\n",
297 !commit_mode ? "none (default)" :
298 commit_mode == 1 ? "at the end" : "after each");
306 res = test_issubvolume(path);
308 error("cannot access subvolume %s: %s", path, strerror(-res));
313 error("not a subvolume: %s", path);
318 cpath = realpath(path, NULL);
321 error("cannot find real path for '%s': %s",
322 path, strerror(errno));
325 dupdname = strdup(cpath);
326 dname = dirname(dupdname);
327 dupvname = strdup(cpath);
328 vname = basename(dupvname);
331 fd = btrfs_open_dir(dname, &dirstream, 1);
337 printf("Delete subvolume (%s): '%s/%s'\n",
338 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
339 ? "commit" : "no-commit", dname, vname);
340 memset(&args, 0, sizeof(args));
341 strncpy_null(args.name, vname);
342 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
344 error("cannot delete '%s/%s': %s", dname, vname,
350 if (commit_mode == 1) {
351 res = wait_for_commit(fd);
353 error("unable to wait for commit after '%s': %s",
354 path, strerror(errno));
366 close_file_or_dir(fd, dirstream);
367 /* avoid double free */
373 if (commit_mode == 2 && fd != -1) {
374 res = wait_for_commit(fd);
376 error("unable to do final sync after deletion: %s",
381 close_file_or_dir(fd, dirstream);
388 * - uppercase for filters and sort options
389 * - lowercase for enabling specific items in the output
391 static const char * const cmd_subvol_list_usage[] = {
392 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
393 "[--sort=gen,ogen,rootid,path] <path>",
394 "List subvolumes (and snapshots)",
396 "-p print parent ID",
397 "-a print all the subvolumes in the filesystem and",
398 " distinguish absolute and relative path with respect",
399 " to the given <path>",
400 "-c print the ogeneration of the subvolume",
401 "-g print the generation of the subvolume",
402 "-o print only subvolumes below specified path",
403 "-u print the uuid of subvolumes (and snapshots)",
404 "-q print the parent uuid of the snapshots",
405 "-R print the uuid of the received snapshots",
406 "-t print the result as a table",
407 "-s list snapshots only in the filesystem",
408 "-r list readonly subvolumes (including snapshots)",
409 "-d list deleted subvolumes that are not yet cleaned",
411 " filter the subvolumes by generation",
412 " (+value: >= value; -value: <= value; value: = value)",
414 " filter the subvolumes by ogeneration",
415 " (+value: >= value; -value: <= value; value: = value)",
416 "--sort=gen,ogen,rootid,path",
417 " list the subvolume in order of gen, ogen, rootid or path",
418 " you also can add '+' or '-' in front of each items.",
419 " (+:ascending, -:descending, ascending default)",
423 static int cmd_subvol_list(int argc, char **argv)
425 struct btrfs_list_filter_set *filter_set;
426 struct btrfs_list_comparer_set *comparer_set;
430 int ret = -1, uerr = 0;
432 int is_tab_result = 0;
434 int is_only_in_path = 0;
435 DIR *dirstream = NULL;
437 filter_set = btrfs_list_alloc_filter_set();
438 comparer_set = btrfs_list_alloc_comparer_set();
442 static const struct option long_options[] = {
443 {"sort", required_argument, NULL, 'S'},
447 c = getopt_long(argc, argv,
448 "acdgopqsurRG:C:t", long_options, NULL);
454 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
460 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
463 btrfs_list_setup_filter(&filter_set,
464 BTRFS_LIST_FILTER_DELETED,
468 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
477 btrfs_list_setup_filter(&filter_set,
478 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
480 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
481 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
484 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
487 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
490 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
493 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
496 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
497 ret = btrfs_list_parse_filter_string(optarg,
499 BTRFS_LIST_FILTER_GEN);
507 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
508 ret = btrfs_list_parse_filter_string(optarg,
510 BTRFS_LIST_FILTER_CGEN);
517 ret = btrfs_list_parse_sort_string(optarg,
532 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
535 if (check_argc_exact(argc - optind, 1)) {
540 subvol = argv[optind];
541 fd = btrfs_open_dir(subvol, &dirstream, 1);
544 error("can't access '%s'", subvol);
548 ret = btrfs_list_get_path_rootid(fd, &top_id);
550 error("can't get rootid for '%s'", subvol);
555 btrfs_list_setup_filter(&filter_set,
556 BTRFS_LIST_FILTER_FULL_PATH,
558 else if (is_only_in_path)
559 btrfs_list_setup_filter(&filter_set,
560 BTRFS_LIST_FILTER_TOPID_EQUAL,
563 /* by default we shall print the following columns*/
564 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
565 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
566 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
567 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
570 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
571 BTRFS_LIST_LAYOUT_TABLE,
572 !is_list_all && !is_only_in_path, NULL);
574 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
575 BTRFS_LIST_LAYOUT_DEFAULT,
576 !is_list_all && !is_only_in_path, NULL);
579 close_file_or_dir(fd, dirstream);
581 btrfs_list_free_filter_set(filter_set);
583 btrfs_list_free_comparer_set(comparer_set);
585 usage(cmd_subvol_list_usage);
589 static const char * const cmd_subvol_snapshot_usage[] = {
590 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
591 "Create a snapshot of the subvolume",
592 "Create a writable/readonly snapshot of the subvolume <source> with",
593 "the name <name> in the <dest> directory. If only <dest> is given,",
594 "the subvolume will be named the basename of <source>.",
596 "-r create a readonly snapshot",
597 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
598 " option can be given multiple times.",
602 static int cmd_subvol_snapshot(int argc, char **argv)
606 int fd = -1, fddst = -1;
607 int len, readonly = 0;
608 char *dupname = NULL;
612 struct btrfs_ioctl_vol_args_v2 args;
613 struct btrfs_qgroup_inherit *inherit = NULL;
614 DIR *dirstream1 = NULL, *dirstream2 = NULL;
616 memset(&args, 0, sizeof(args));
618 int c = getopt(argc, argv, "c:i:r");
624 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
631 res = qgroup_inherit_add_group(&inherit, optarg);
641 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
648 usage(cmd_subvol_snapshot_usage);
652 if (check_argc_exact(argc - optind, 2))
653 usage(cmd_subvol_snapshot_usage);
655 subvol = argv[optind];
656 dst = argv[optind + 1];
658 retval = 1; /* failure */
659 res = test_issubvolume(subvol);
661 error("cannot access subvolume %s: %s", subvol, strerror(-res));
665 error("not a subvolume: %s", subvol);
669 res = test_isdir(dst);
670 if (res < 0 && res != -ENOENT) {
671 error("cannot access %s: %s", dst, strerror(-res));
675 error("'%s' exists and it is not a directory", dst);
680 dupname = strdup(subvol);
681 newname = basename(dupname);
684 dupname = strdup(dst);
685 newname = basename(dupname);
686 dupdir = strdup(dst);
687 dstdir = dirname(dupdir);
690 if (!test_issubvolname(newname)) {
691 error("invalid snapshot name '%s'", newname);
695 len = strlen(newname);
696 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
697 error("snapshot name too long '%s'", newname);
701 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
705 fd = btrfs_open_dir(subvol, &dirstream2, 1);
710 args.flags |= BTRFS_SUBVOL_RDONLY;
711 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
712 subvol, dstdir, newname);
714 printf("Create a snapshot of '%s' in '%s/%s'\n",
715 subvol, dstdir, newname);
720 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
721 args.size = qgroup_inherit_size(inherit);
722 args.qgroup_inherit = inherit;
724 strncpy_null(args.name, newname);
726 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
729 error("cannot snapshot '%s': %s", subvol, strerror(errno));
733 retval = 0; /* success */
736 close_file_or_dir(fddst, dirstream1);
737 close_file_or_dir(fd, dirstream2);
745 static const char * const cmd_subvol_get_default_usage[] = {
746 "btrfs subvolume get-default <path>",
747 "Get the default subvolume of a filesystem",
751 static int cmd_subvol_get_default(int argc, char **argv)
756 struct btrfs_list_filter_set *filter_set;
758 DIR *dirstream = NULL;
760 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
762 if (check_argc_exact(argc - optind, 1))
763 usage(cmd_subvol_get_default_usage);
766 fd = btrfs_open_dir(subvol, &dirstream, 1);
770 ret = btrfs_list_get_default_subvolume(fd, &default_id);
772 error("failed to look up default subvolume: %s",
778 if (default_id == 0) {
779 error("'default' dir item not found");
783 /* no need to resolve roots if FS_TREE is default */
784 if (default_id == BTRFS_FS_TREE_OBJECTID) {
785 printf("ID 5 (FS_TREE)\n");
790 filter_set = btrfs_list_alloc_filter_set();
791 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
794 /* by default we shall print the following columns*/
795 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
796 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
797 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
798 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
800 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
801 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
804 btrfs_list_free_filter_set(filter_set);
806 close_file_or_dir(fd, dirstream);
810 static const char * const cmd_subvol_set_default_usage[] = {
811 "btrfs subvolume set-default <subvolid> <path>",
812 "Set the default subvolume of a filesystem",
816 static int cmd_subvol_set_default(int argc, char **argv)
822 DIR *dirstream = NULL;
824 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
826 if (check_argc_exact(argc - optind, 2))
827 usage(cmd_subvol_set_default_usage);
829 subvolid = argv[optind];
830 path = argv[optind + 1];
832 objectid = arg_strtou64(subvolid);
834 fd = btrfs_open_dir(path, &dirstream, 1);
838 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
840 close_file_or_dir(fd, dirstream);
842 error("unable to set a new default subvolume: %s",
849 static const char * const cmd_subvol_find_new_usage[] = {
850 "btrfs subvolume find-new <path> <lastgen>",
851 "List the recently modified files in a filesystem",
855 static int cmd_subvol_find_new(int argc, char **argv)
861 DIR *dirstream = NULL;
863 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
865 if (check_argc_exact(argc - optind, 2))
866 usage(cmd_subvol_find_new_usage);
868 subvol = argv[optind];
869 last_gen = arg_strtou64(argv[optind + 1]);
871 ret = test_issubvolume(subvol);
873 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
877 error("not a subvolume: %s", subvol);
881 fd = btrfs_open_dir(subvol, &dirstream, 1);
885 ret = ioctl(fd, BTRFS_IOC_SYNC);
887 error("sync ioctl failed on '%s': %s",
888 subvol, strerror(errno));
889 close_file_or_dir(fd, dirstream);
893 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
894 close_file_or_dir(fd, dirstream);
898 static const char * const cmd_subvol_show_usage[] = {
899 "btrfs subvolume show <subvol-path>",
900 "Show more information of the subvolume",
904 static int cmd_subvol_show(int argc, char **argv)
906 struct root_info get_ri;
907 struct btrfs_list_filter_set *filter_set = NULL;
909 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
910 char *fullpath = NULL;
911 char raw_prefix[] = "\t\t\t\t";
914 DIR *dirstream1 = NULL;
916 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
918 if (check_argc_exact(argc - optind, 1))
919 usage(cmd_subvol_show_usage);
921 memset(&get_ri, 0, sizeof(get_ri));
922 fullpath = realpath(argv[optind], NULL);
924 error("cannot find real path for '%s': %s",
925 argv[optind], strerror(errno));
929 ret = get_subvol_info(fullpath, &get_ri);
932 * Since the top level btrfs was given don't
935 printf("%s is toplevel subvolume\n", fullpath);
941 error("Failed to get subvol info %s: %s\n",
942 fullpath, strerror(-ret)):
943 error("Failed to get subvol info %s: %d\n",
949 printf("%s\n", fullpath);
950 printf("\tName: \t\t\t%s\n", get_ri.name);
952 if (uuid_is_null(get_ri.uuid))
953 strcpy(uuidparse, "-");
955 uuid_unparse(get_ri.uuid, uuidparse);
956 printf("\tUUID: \t\t\t%s\n", uuidparse);
958 if (uuid_is_null(get_ri.puuid))
959 strcpy(uuidparse, "-");
961 uuid_unparse(get_ri.puuid, uuidparse);
962 printf("\tParent UUID: \t\t%s\n", uuidparse);
964 if (uuid_is_null(get_ri.ruuid))
965 strcpy(uuidparse, "-");
967 uuid_unparse(get_ri.ruuid, uuidparse);
968 printf("\tReceived UUID: \t\t%s\n", uuidparse);
973 localtime_r(&get_ri.otime, &tm);
974 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
977 printf("\tCreation time: \t\t%s\n", tstr);
979 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
980 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
981 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
982 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
983 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
985 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
986 printf("\tFlags: \t\t\treadonly\n");
988 printf("\tFlags: \t\t\t-\n");
990 /* print the snapshots of the given subvol if any*/
991 printf("\tSnapshot(s):\n");
992 filter_set = btrfs_list_alloc_filter_set();
993 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
994 (u64)(unsigned long)get_ri.uuid);
995 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
997 fd = open_file_or_dir(fullpath, &dirstream1);
999 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1002 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1009 free(get_ri.full_path);
1010 btrfs_list_free_filter_set(filter_set);
1012 close_file_or_dir(fd, dirstream1);
1017 static const char * const cmd_subvol_sync_usage[] = {
1018 "btrfs subvolume sync <path> [<subvol-id>...]",
1019 "Wait until given subvolume(s) are completely removed from the filesystem.",
1020 "Wait until given subvolume(s) are completely removed from the filesystem",
1022 "If no subvolume id is given, wait until all current deletion requests",
1023 "are completed, but do not wait for subvolumes deleted meanwhile.",
1024 "The status of subvolume ids is checked periodically.",
1026 "-s <N> sleep N seconds between checks (default: 1)",
1032 * If we're looking for any dead subvolume, take a shortcut and look
1033 * for any ORPHAN_ITEMs in the tree root
1035 static int fs_has_dead_subvolumes(int fd)
1038 struct btrfs_ioctl_search_args args;
1039 struct btrfs_ioctl_search_key *sk = &args.key;
1040 struct btrfs_ioctl_search_header sh;
1041 u64 min_subvolid = 0;
1044 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1045 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1046 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1047 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1048 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1049 sk->min_offset = min_subvolid;
1050 sk->max_offset = (u64)-1;
1051 sk->min_transid = 0;
1052 sk->max_transid = (u64)-1;
1055 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1062 memcpy(&sh, args.buf, sizeof(sh));
1063 min_subvolid = sh.offset;
1066 * Verify that the root item is really there and we haven't hit
1069 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1070 sk->min_objectid = min_subvolid;
1071 sk->max_objectid = min_subvolid;
1072 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1073 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1075 sk->max_offset = (u64)-1;
1076 sk->min_transid = 0;
1077 sk->max_transid = (u64)-1;
1080 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1085 * Stale orphan, try the next one
1087 if (!sk->nr_items) {
1096 #define SUBVOL_ID_BATCH 1024
1099 * Enumerate all dead subvolumes that exist in the filesystem.
1100 * Fill @ids and reallocate to bigger size if needed.
1102 static int enumerate_dead_subvols(int fd, u64 **ids)
1105 struct btrfs_ioctl_search_args args;
1106 struct btrfs_ioctl_search_key *sk = &args.key;
1110 memset(&args, 0, sizeof(args));
1112 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1113 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1114 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1115 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1116 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1118 sk->max_offset = (u64)-1;
1119 sk->min_transid = 0;
1120 sk->max_transid = (u64)-1;
1121 sk->nr_items = 4096;
1125 struct btrfs_ioctl_search_header *sh;
1129 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1137 for (i = 0; i < sk->nr_items; i++) {
1138 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1141 if (btrfs_search_header_type(sh)
1142 == BTRFS_ORPHAN_ITEM_KEY) {
1146 count += SUBVOL_ID_BATCH;
1147 newids = (u64*)realloc(*ids,
1148 count * sizeof(u64));
1153 (*ids)[idx] = btrfs_search_header_offset(sh);
1156 off += btrfs_search_header_len(sh);
1158 sk->min_objectid = btrfs_search_header_objectid(sh);
1159 sk->min_type = btrfs_search_header_type(sh);
1160 sk->min_offset = btrfs_search_header_offset(sh);
1162 if (sk->min_offset < (u64)-1)
1166 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1168 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1175 static int cmd_subvol_sync(int argc, char **argv)
1180 DIR *dirstream = NULL;
1183 int sleep_interval = 1;
1186 int c = getopt(argc, argv, "s:");
1193 sleep_interval = atoi(optarg);
1194 if (sleep_interval < 1) {
1195 error("invalid sleep interval %s", optarg);
1201 usage(cmd_subvol_sync_usage);
1205 if (check_argc_min(argc - optind, 1))
1206 usage(cmd_subvol_sync_usage);
1208 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1215 id_count = argc - optind;
1217 id_count = enumerate_dead_subvols(fd, &ids);
1219 error("can't enumerate dead subvolumes: %s",
1220 strerror(-id_count));
1224 if (id_count == 0) {
1229 ids = (u64*)malloc(id_count * sizeof(u64));
1231 error("not enough memory");
1236 for (i = 0; i < id_count; i++) {
1240 arg = argv[optind + i];
1242 id = strtoull(arg, NULL, 10);
1244 error("unrecognized subvolume id %s", arg);
1248 if (id < BTRFS_FIRST_FREE_OBJECTID
1249 || id > BTRFS_LAST_FREE_OBJECTID) {
1250 error("subvolume id %s out of range\n", arg);
1258 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1262 close_file_or_dir(fd, dirstream);
1267 static const char subvolume_cmd_group_info[] =
1268 "manage subvolumes: create, delete, list, etc";
1270 const struct cmd_group subvolume_cmd_group = {
1271 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1272 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1273 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1274 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1275 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1277 { "get-default", cmd_subvol_get_default,
1278 cmd_subvol_get_default_usage, NULL, 0 },
1279 { "set-default", cmd_subvol_set_default,
1280 cmd_subvol_set_default_usage, NULL, 0 },
1281 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1283 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1284 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1289 int cmd_subvolume(int argc, char **argv)
1291 return handle_command_group(&subvolume_cmd_group, argc, argv);