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>
27 #include <uuid/uuid.h>
29 #include "kerncompat.h"
36 #include "btrfs-list.h"
39 static int is_subvolume_cleaned(int fd, u64 subvolid)
42 struct btrfs_ioctl_search_args args;
43 struct btrfs_ioctl_search_key *sk = &args.key;
45 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
46 sk->min_objectid = subvolid;
47 sk->max_objectid = subvolid;
48 sk->min_type = BTRFS_ROOT_ITEM_KEY;
49 sk->max_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_offset = (u64)-1;
53 sk->max_transid = (u64)-1;
56 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
60 if (sk->nr_items == 0)
66 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
75 for (i = 0; i < count; i++) {
78 ret = is_subvolume_cleaned(fd, ids[i]);
81 "cannot read status of dead subvolume %llu: %s",
82 (unsigned long long)ids[i], strerror(-ret));
86 printf("Subvolume id %llu is gone\n", ids[i]);
94 sleep(sleep_interval);
100 static const char * const subvolume_cmd_group_usage[] = {
101 "btrfs subvolume <command> <args>",
105 static const char * const cmd_subvol_create_usage[] = {
106 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
107 "Create a subvolume",
108 "Create a subvolume <name> in <dest>. If <dest> is not given",
109 "subvolume <name> will be created in the current directory.",
111 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
112 " option can be given multiple times.",
116 static int cmd_subvol_create(int argc, char **argv)
118 int retval, res, len;
120 char *dupname = NULL;
125 struct btrfs_qgroup_inherit *inherit = NULL;
126 DIR *dirstream = NULL;
130 int c = getopt(argc, argv, "c:i:v");
136 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
143 res = qgroup_inherit_add_group(&inherit, optarg);
150 usage(cmd_subvol_create_usage);
154 if (check_argc_exact(argc - optind, 1))
155 usage(cmd_subvol_create_usage);
159 retval = 1; /* failure */
160 res = test_isdir(dst);
161 if (res < 0 && res != -ENOENT) {
162 error("cannot access %s: %s", dst, strerror(-res));
166 error("target path already exists: %s", dst);
170 dupname = strdup(dst);
171 newname = basename(dupname);
172 dupdir = strdup(dst);
173 dstdir = dirname(dupdir);
175 if (!test_issubvolname(newname)) {
176 error("invalid subvolume name: %s", newname);
180 len = strlen(newname);
181 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
182 error("subvolume name too long: %s", newname);
186 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
190 printf("Create subvolume '%s/%s'\n", dstdir, newname);
192 struct btrfs_ioctl_vol_args_v2 args;
194 memset(&args, 0, sizeof(args));
195 strncpy_null(args.name, newname);
196 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
197 args.size = qgroup_inherit_size(inherit);
198 args.qgroup_inherit = inherit;
200 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
202 struct btrfs_ioctl_vol_args args;
204 memset(&args, 0, sizeof(args));
205 strncpy_null(args.name, newname);
207 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
211 error("cannot create subvolume: %s", strerror(errno));
215 retval = 0; /* success */
217 close_file_or_dir(fddst, dirstream);
226 * test if path is a subvolume:
227 * this function return
228 * 0-> path exists but it is not a subvolume
229 * 1-> path exists and it is a subvolume
230 * -1 -> path is unaccessible
232 int test_issubvolume(char *path)
237 res = stat(path, &st);
241 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
244 static int wait_for_commit(int fd)
248 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
251 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
254 static const char * const cmd_subvol_delete_usage[] = {
255 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
256 "Delete subvolume(s)",
257 "Delete subvolumes from the filesystem. The corresponding directory",
258 "is removed instantly but the data blocks are removed later.",
259 "The deletion does not involve full commit by default due to",
260 "performance reasons (as a consequence, the subvolume may appear again",
261 "after a crash). Use one of the --commit options to wait until the",
262 "operation is safely stored on the media.",
264 "-c|--commit-after wait for transaction commit at the end of the operation",
265 "-C|--commit-each wait for transaction commit after deleting each subvolume",
269 static int cmd_subvol_delete(int argc, char **argv)
274 struct btrfs_ioctl_vol_args args;
275 char *dname, *vname, *cpath;
276 char *dupdname = NULL;
277 char *dupvname = NULL;
279 DIR *dirstream = NULL;
286 static const struct option long_options[] = {
287 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
288 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
292 c = getopt_long(argc, argv, "cC", long_options, NULL);
307 usage(cmd_subvol_delete_usage);
311 if (check_argc_min(argc - optind, 1))
312 usage(cmd_subvol_delete_usage);
315 printf("Transaction commit: %s\n",
316 !commit_mode ? "none (default)" :
317 commit_mode == 1 ? "at the end" : "after each");
325 res = test_issubvolume(path);
327 error("cannot access subvolume %s", path);
332 error("not a subvolume: %s", path);
337 cpath = realpath(path, NULL);
340 error("cannot find real path for '%s': %s",
341 path, strerror(errno));
344 dupdname = strdup(cpath);
345 dname = dirname(dupdname);
346 dupvname = strdup(cpath);
347 vname = basename(dupvname);
350 fd = btrfs_open_dir(dname, &dirstream, 1);
356 printf("Delete subvolume (%s): '%s/%s'\n",
357 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
358 ? "commit" : "no-commit", dname, vname);
359 memset(&args, 0, sizeof(args));
360 strncpy_null(args.name, vname);
361 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
363 error("cannot delete '%s/%s': %s", dname, vname,
369 if (commit_mode == 1) {
370 res = wait_for_commit(fd);
372 error("unable to wait for commit after '%s': %s",
373 path, strerror(errno));
385 close_file_or_dir(fd, dirstream);
386 /* avoid double free */
392 if (commit_mode == 2 && fd != -1) {
393 res = wait_for_commit(fd);
395 error("unable to do final sync after deletion: %s",
400 close_file_or_dir(fd, dirstream);
407 * - uppercase for filters and sort options
408 * - lowercase for enabling specific items in the output
410 static const char * const cmd_subvol_list_usage[] = {
411 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
412 "[--sort=gen,ogen,rootid,path] <path>",
413 "List subvolumes (and snapshots)",
415 "-p print parent ID",
416 "-a print all the subvolumes in the filesystem and",
417 " distinguish absolute and relative path with respect",
418 " to the given <path>",
419 "-c print the ogeneration of the subvolume",
420 "-g print the generation of the subvolume",
421 "-o print only subvolumes below specified path",
422 "-u print the uuid of subvolumes (and snapshots)",
423 "-q print the parent uuid of the snapshots",
424 "-R print the uuid of the received snapshots",
425 "-t print the result as a table",
426 "-s list snapshots only in the filesystem",
427 "-r list readonly subvolumes (including snapshots)",
428 "-d list deleted subvolumes that are not yet cleaned",
430 " filter the subvolumes by generation",
431 " (+value: >= value; -value: <= value; value: = value)",
433 " filter the subvolumes by ogeneration",
434 " (+value: >= value; -value: <= value; value: = value)",
435 "--sort=gen,ogen,rootid,path",
436 " list the subvolume in order of gen, ogen, rootid or path",
437 " you also can add '+' or '-' in front of each items.",
438 " (+:ascending, -:descending, ascending default)",
442 static int cmd_subvol_list(int argc, char **argv)
444 struct btrfs_list_filter_set *filter_set;
445 struct btrfs_list_comparer_set *comparer_set;
449 int ret = -1, uerr = 0;
451 int is_tab_result = 0;
453 int is_only_in_path = 0;
454 DIR *dirstream = NULL;
456 filter_set = btrfs_list_alloc_filter_set();
457 comparer_set = btrfs_list_alloc_comparer_set();
462 static const struct option long_options[] = {
463 {"sort", required_argument, NULL, 'S'},
467 c = getopt_long(argc, argv,
468 "acdgopqsurRG:C:t", long_options, NULL);
474 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
480 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
483 btrfs_list_setup_filter(&filter_set,
484 BTRFS_LIST_FILTER_DELETED,
488 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
497 btrfs_list_setup_filter(&filter_set,
498 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
500 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
501 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
504 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
507 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
510 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
513 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
516 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
517 ret = btrfs_list_parse_filter_string(optarg,
519 BTRFS_LIST_FILTER_GEN);
527 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
528 ret = btrfs_list_parse_filter_string(optarg,
530 BTRFS_LIST_FILTER_CGEN);
537 ret = btrfs_list_parse_sort_string(optarg,
552 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
555 if (check_argc_exact(argc - optind, 1)) {
560 subvol = argv[optind];
561 fd = btrfs_open_dir(subvol, &dirstream, 1);
564 error("can't access '%s'", subvol);
568 ret = btrfs_list_get_path_rootid(fd, &top_id);
570 error("can't get rootid for '%s'", subvol);
575 btrfs_list_setup_filter(&filter_set,
576 BTRFS_LIST_FILTER_FULL_PATH,
578 else if (is_only_in_path)
579 btrfs_list_setup_filter(&filter_set,
580 BTRFS_LIST_FILTER_TOPID_EQUAL,
583 /* by default we shall print the following columns*/
584 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
585 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
586 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
587 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
590 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
591 BTRFS_LIST_LAYOUT_TABLE,
592 !is_list_all && !is_only_in_path, NULL);
594 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
595 BTRFS_LIST_LAYOUT_DEFAULT,
596 !is_list_all && !is_only_in_path, NULL);
599 close_file_or_dir(fd, dirstream);
601 btrfs_list_free_filter_set(filter_set);
603 btrfs_list_free_comparer_set(comparer_set);
605 usage(cmd_subvol_list_usage);
609 static const char * const cmd_subvol_snapshot_usage[] = {
610 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
611 "Create a snapshot of the subvolume",
612 "Create a writable/readonly snapshot of the subvolume <source> with",
613 "the name <name> in the <dest> directory. If only <dest> is given,",
614 "the subvolume will be named the basename of <source>.",
616 "-r create a readonly snapshot",
617 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
618 " option can be given multiple times.",
622 static int cmd_subvol_snapshot(int argc, char **argv)
626 int fd = -1, fddst = -1;
627 int len, readonly = 0;
628 char *dupname = NULL;
632 struct btrfs_ioctl_vol_args_v2 args;
633 struct btrfs_qgroup_inherit *inherit = NULL;
634 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", subvol);
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': %s", subvol, strerror(errno));
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 struct btrfs_list_filter_set *filter_set;
779 DIR *dirstream = NULL;
781 if (check_argc_exact(argc, 2))
782 usage(cmd_subvol_get_default_usage);
785 fd = btrfs_open_dir(subvol, &dirstream, 1);
789 ret = btrfs_list_get_default_subvolume(fd, &default_id);
791 error("failed to look up default subvolume: %s",
797 if (default_id == 0) {
798 error("'default' dir item not found");
802 /* no need to resolve roots if FS_TREE is default */
803 if (default_id == BTRFS_FS_TREE_OBJECTID) {
804 printf("ID 5 (FS_TREE)\n");
809 filter_set = btrfs_list_alloc_filter_set();
810 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
813 /* by default we shall print the following columns*/
814 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
815 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
816 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
817 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
819 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
820 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
823 btrfs_list_free_filter_set(filter_set);
825 close_file_or_dir(fd, dirstream);
829 static const char * const cmd_subvol_set_default_usage[] = {
830 "btrfs subvolume set-default <subvolid> <path>",
831 "Set the default subvolume of a filesystem",
835 static int cmd_subvol_set_default(int argc, char **argv)
841 DIR *dirstream = NULL;
843 if (check_argc_exact(argc, 3))
844 usage(cmd_subvol_set_default_usage);
849 objectid = arg_strtou64(subvolid);
851 fd = btrfs_open_dir(path, &dirstream, 1);
855 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
857 close_file_or_dir(fd, dirstream);
859 error("unable to set a new default subvolume: %s",
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;
880 if (check_argc_exact(argc, 3))
881 usage(cmd_subvol_find_new_usage);
884 last_gen = arg_strtou64(argv[2]);
886 ret = test_issubvolume(subvol);
888 error("cannot access subvolume %s", subvol);
892 error("not a subvolume: %s", subvol);
896 fd = btrfs_open_dir(subvol, &dirstream, 1);
900 ret = ioctl(fd, BTRFS_IOC_SYNC);
902 error("sync ioctl failed on '%s': %s",
903 subvol, strerror(errno));
904 close_file_or_dir(fd, dirstream);
908 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
909 close_file_or_dir(fd, dirstream);
913 static const char * const cmd_subvol_show_usage[] = {
914 "btrfs subvolume show <subvol-path>",
915 "Show more information of the subvolume",
919 static int cmd_subvol_show(int argc, char **argv)
921 struct root_info get_ri;
922 struct btrfs_list_filter_set *filter_set;
924 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
925 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
926 char raw_prefix[] = "\t\t\t\t";
928 int fd = -1, mntfd = -1;
930 DIR *dirstream1 = NULL, *dirstream2 = NULL;
933 static const struct option long_options[] = {
936 int c = getopt_long(argc, argv, "", long_options, NULL);
943 usage(cmd_subvol_show_usage);
947 if (check_argc_exact(argc - optind, 1))
948 usage(cmd_subvol_show_usage);
950 fullpath = realpath(argv[optind], NULL);
952 error("cannot find real path for '%s': %s",
953 argv[optind], strerror(errno));
957 ret = test_issubvolume(fullpath);
959 error("cannot access subvolume %s", fullpath);
963 error("not a subvolume: %s", fullpath);
968 ret = find_mount_root(fullpath, &mnt);
970 error("find_mount_root failed on '%s': %s",
971 fullpath, strerror(-ret));
975 error("%s doesn't belong to btrfs mount point", fullpath);
979 svpath = get_subvol_name(mnt, fullpath);
981 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
985 ret = btrfs_list_get_path_rootid(fd, &sv_id);
987 error("can't get rootid for '%s'", fullpath);
991 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
995 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
996 printf("%s is toplevel subvolume\n", fullpath);
1000 memset(&get_ri, 0, sizeof(get_ri));
1001 get_ri.root_id = sv_id;
1003 ret = btrfs_get_subvol(mntfd, &get_ri);
1005 error("can't find '%s'", svpath);
1009 /* print the info */
1010 printf("%s\n", fullpath);
1011 printf("\tName: \t\t\t%s\n", get_ri.name);
1013 if (uuid_is_null(get_ri.uuid))
1014 strcpy(uuidparse, "-");
1016 uuid_unparse(get_ri.uuid, uuidparse);
1017 printf("\tUUID: \t\t\t%s\n", uuidparse);
1019 if (uuid_is_null(get_ri.puuid))
1020 strcpy(uuidparse, "-");
1022 uuid_unparse(get_ri.puuid, uuidparse);
1023 printf("\tParent UUID: \t\t%s\n", uuidparse);
1025 if (uuid_is_null(get_ri.ruuid))
1026 strcpy(uuidparse, "-");
1028 uuid_unparse(get_ri.ruuid, uuidparse);
1029 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1034 localtime_r(&get_ri.otime, &tm);
1035 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1038 printf("\tCreation time: \t\t%s\n", tstr);
1040 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1041 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1042 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1043 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1044 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1046 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1047 printf("\tFlags: \t\t\treadonly\n");
1049 printf("\tFlags: \t\t\t-\n");
1051 /* print the snapshots of the given subvol if any*/
1052 printf("\tSnapshot(s):\n");
1053 filter_set = btrfs_list_alloc_filter_set();
1054 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1055 (u64)(unsigned long)get_ri.uuid);
1056 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1057 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1063 free(get_ri.full_path);
1064 btrfs_list_free_filter_set(filter_set);
1067 close_file_or_dir(fd, dirstream1);
1068 close_file_or_dir(mntfd, dirstream2);
1074 static const char * const cmd_subvol_sync_usage[] = {
1075 "btrfs subvolume sync <path> [<subvol-id>...]",
1076 "Wait until given subvolume(s) are completely removed from the filesystem.",
1077 "Wait until given subvolume(s) are completely removed from the filesystem",
1079 "If no subvolume id is given, wait until all current deletion requests",
1080 "are completed, but do not wait for subvolumes deleted meanwhile.",
1081 "The status of subvolume ids is checked periodically.",
1083 "-s <N> sleep N seconds between checks (default: 1)",
1089 * If we're looking for any dead subvolume, take a shortcut and look
1090 * for any ORPHAN_ITEMs in the tree root
1092 static int fs_has_dead_subvolumes(int fd)
1095 struct btrfs_ioctl_search_args args;
1096 struct btrfs_ioctl_search_key *sk = &args.key;
1097 struct btrfs_ioctl_search_header sh;
1098 u64 min_subvolid = 0;
1101 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1102 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1103 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1104 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1105 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1106 sk->min_offset = min_subvolid;
1107 sk->max_offset = (u64)-1;
1108 sk->min_transid = 0;
1109 sk->max_transid = (u64)-1;
1112 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1119 memcpy(&sh, args.buf, sizeof(sh));
1120 min_subvolid = sh.offset;
1123 * Verify that the root item is really there and we haven't hit
1126 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1127 sk->min_objectid = min_subvolid;
1128 sk->max_objectid = min_subvolid;
1129 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1130 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1132 sk->max_offset = (u64)-1;
1133 sk->min_transid = 0;
1134 sk->max_transid = (u64)-1;
1137 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1142 * Stale orphan, try the next one
1144 if (!sk->nr_items) {
1153 #define SUBVOL_ID_BATCH 1024
1156 * Enumerate all dead subvolumes that exist in the filesystem.
1157 * Fill @ids and reallocate to bigger size if needed.
1159 static int enumerate_dead_subvols(int fd, u64 **ids)
1162 struct btrfs_ioctl_search_args args;
1163 struct btrfs_ioctl_search_key *sk = &args.key;
1167 memset(&args, 0, sizeof(args));
1169 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1170 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1171 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1172 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1173 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1175 sk->max_offset = (u64)-1;
1176 sk->min_transid = 0;
1177 sk->max_transid = (u64)-1;
1178 sk->nr_items = 4096;
1182 struct btrfs_ioctl_search_header *sh;
1186 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1194 for (i = 0; i < sk->nr_items; i++) {
1195 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1198 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1202 count += SUBVOL_ID_BATCH;
1203 newids = (u64*)realloc(*ids, count);
1208 (*ids)[idx] = sh->offset;
1213 sk->min_objectid = sh->objectid;
1214 sk->min_type = sh->type;
1215 sk->min_offset = sh->offset;
1217 if (sk->min_offset < (u64)-1)
1221 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1223 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1230 static int cmd_subvol_sync(int argc, char **argv)
1235 DIR *dirstream = NULL;
1238 int sleep_interval = 1;
1242 int c = getopt(argc, argv, "s:");
1249 sleep_interval = atoi(argv[optind]);
1250 if (sleep_interval < 1) {
1251 error("invalid sleep interval %s",
1258 usage(cmd_subvol_sync_usage);
1262 if (check_argc_min(argc - optind, 1))
1263 usage(cmd_subvol_sync_usage);
1265 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1272 id_count = argc - optind;
1274 id_count = enumerate_dead_subvols(fd, &ids);
1276 error("can't enumerate dead subvolumes: %s",
1277 strerror(-id_count));
1281 if (id_count == 0) {
1286 ids = (u64*)malloc(id_count * sizeof(u64));
1288 error("not enough memory");
1293 for (i = 0; i < id_count; i++) {
1297 arg = argv[optind + i];
1299 id = strtoull(arg, NULL, 10);
1301 error("unrecognized subvolume id %s", arg);
1305 if (id < BTRFS_FIRST_FREE_OBJECTID
1306 || id > BTRFS_LAST_FREE_OBJECTID) {
1307 error("subvolume id %s out of range\n", arg);
1315 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1319 close_file_or_dir(fd, dirstream);
1324 static const char subvolume_cmd_group_info[] =
1325 "manage subvolumes: create, delete, list, etc";
1327 const struct cmd_group subvolume_cmd_group = {
1328 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1329 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1330 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1331 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1332 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1334 { "get-default", cmd_subvol_get_default,
1335 cmd_subvol_get_default_usage, NULL, 0 },
1336 { "set-default", cmd_subvol_set_default,
1337 cmd_subvol_set_default_usage, NULL, 0 },
1338 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1340 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1341 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1346 int cmd_subvolume(int argc, char **argv)
1348 return handle_command_group(&subvolume_cmd_group, argc, argv);