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);
162 error("target path already exists: %s", dst);
166 dupname = strdup(dst);
167 newname = basename(dupname);
168 dupdir = strdup(dst);
169 dstdir = dirname(dupdir);
171 if (!test_issubvolname(newname)) {
172 error("invalid subvolume name: %s", newname);
176 len = strlen(newname);
177 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
178 error("subvolume name too long: %s", newname);
182 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
186 printf("Create subvolume '%s/%s'\n", dstdir, newname);
188 struct btrfs_ioctl_vol_args_v2 args;
190 memset(&args, 0, sizeof(args));
191 strncpy_null(args.name, newname);
192 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
193 args.size = qgroup_inherit_size(inherit);
194 args.qgroup_inherit = inherit;
196 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
198 struct btrfs_ioctl_vol_args args;
200 memset(&args, 0, sizeof(args));
201 strncpy_null(args.name, newname);
203 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
207 error("cannot create subvolume: %s", strerror(errno));
211 retval = 0; /* success */
213 close_file_or_dir(fddst, dirstream);
222 * test if path is a subvolume:
223 * this function return
224 * 0-> path exists but it is not a subvolume
225 * 1-> path exists and it is a subvolume
226 * -1 -> path is unaccessible
228 int test_issubvolume(char *path)
233 res = stat(path, &st);
237 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
240 static int wait_for_commit(int fd)
244 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
247 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
250 static const char * const cmd_subvol_delete_usage[] = {
251 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
252 "Delete subvolume(s)",
253 "Delete subvolumes from the filesystem. The corresponding directory",
254 "is removed instantly but the data blocks are removed later.",
255 "The deletion does not involve full commit by default due to",
256 "performance reasons (as a consequence, the subvolume may appear again",
257 "after a crash). Use one of the --commit options to wait until the",
258 "operation is safely stored on the media.",
260 "-c|--commit-after wait for transaction commit at the end of the operation",
261 "-C|--commit-each wait for transaction commit after deleting each subvolume",
265 static int cmd_subvol_delete(int argc, char **argv)
270 struct btrfs_ioctl_vol_args args;
271 char *dname, *vname, *cpath;
272 char *dupdname = NULL;
273 char *dupvname = NULL;
275 DIR *dirstream = NULL;
282 static const struct option long_options[] = {
283 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
284 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
288 c = getopt_long(argc, argv, "cC", long_options, NULL);
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 == 1 ? "at the end" : "after each");
321 res = test_issubvolume(path);
323 error("cannot access subvolume %s", path);
328 error("not a subvolume: %s", path);
333 cpath = realpath(path, NULL);
336 error("cannot find real path for '%s': %s",
337 path, strerror(errno));
340 dupdname = strdup(cpath);
341 dname = dirname(dupdname);
342 dupvname = strdup(cpath);
343 vname = basename(dupvname);
346 fd = btrfs_open_dir(dname, &dirstream, 1);
352 printf("Delete subvolume (%s): '%s/%s'\n",
353 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
354 ? "commit" : "no-commit", dname, vname);
355 memset(&args, 0, sizeof(args));
356 strncpy_null(args.name, vname);
357 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
361 error("cannot delete '%s/%s': %s", dname, vname, strerror(e));
366 if (commit_mode == 1) {
367 res = wait_for_commit(fd);
369 error("unable to wait for commit after '%s': %s",
370 path, strerror(errno));
382 close_file_or_dir(fd, dirstream);
383 /* avoid double free */
389 if (commit_mode == 2 && fd != -1) {
390 res = wait_for_commit(fd);
392 error("unable to do final sync after deletion: %s",
397 close_file_or_dir(fd, dirstream);
404 * - uppercase for filters and sort options
405 * - lowercase for enabling specific items in the output
407 static const char * const cmd_subvol_list_usage[] = {
408 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
409 "[--sort=gen,ogen,rootid,path] <path>",
410 "List subvolumes (and snapshots)",
412 "-p print parent ID",
413 "-a print all the subvolumes in the filesystem and",
414 " distinguish absolute and relative path with respect",
415 " to the given <path>",
416 "-c print the ogeneration of the subvolume",
417 "-g print the generation of the subvolume",
418 "-o print only subvolumes below specified path",
419 "-u print the uuid of subvolumes (and snapshots)",
420 "-q print the parent uuid of the snapshots",
421 "-R print the uuid of the received snapshots",
422 "-t print the result as a table",
423 "-s list snapshots only in the filesystem",
424 "-r list readonly subvolumes (including snapshots)",
425 "-d list deleted subvolumes that are not yet cleaned",
427 " filter the subvolumes by generation",
428 " (+value: >= value; -value: <= value; value: = value)",
430 " filter the subvolumes by ogeneration",
431 " (+value: >= value; -value: <= value; value: = value)",
432 "--sort=gen,ogen,rootid,path",
433 " list the subvolume in order of gen, ogen, rootid or path",
434 " you also can add '+' or '-' in front of each items.",
435 " (+:ascending, -:descending, ascending default)",
439 static int cmd_subvol_list(int argc, char **argv)
441 struct btrfs_list_filter_set *filter_set;
442 struct btrfs_list_comparer_set *comparer_set;
446 int ret = -1, uerr = 0;
448 int is_tab_result = 0;
450 int is_only_in_path = 0;
451 DIR *dirstream = NULL;
453 filter_set = btrfs_list_alloc_filter_set();
454 comparer_set = btrfs_list_alloc_comparer_set();
459 static const struct option long_options[] = {
460 {"sort", required_argument, NULL, 'S'},
464 c = getopt_long(argc, argv,
465 "acdgopqsurRG:C:t", long_options, NULL);
471 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
477 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
480 btrfs_list_setup_filter(&filter_set,
481 BTRFS_LIST_FILTER_DELETED,
485 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
494 btrfs_list_setup_filter(&filter_set,
495 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
497 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
498 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
501 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
504 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
507 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
510 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
513 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
514 ret = btrfs_list_parse_filter_string(optarg,
516 BTRFS_LIST_FILTER_GEN);
524 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
525 ret = btrfs_list_parse_filter_string(optarg,
527 BTRFS_LIST_FILTER_CGEN);
534 ret = btrfs_list_parse_sort_string(optarg,
549 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
552 if (check_argc_exact(argc - optind, 1)) {
557 subvol = argv[optind];
558 fd = btrfs_open_dir(subvol, &dirstream, 1);
561 error("can't access '%s'", subvol);
565 ret = btrfs_list_get_path_rootid(fd, &top_id);
567 error("can't get rootid for '%s'", subvol);
572 btrfs_list_setup_filter(&filter_set,
573 BTRFS_LIST_FILTER_FULL_PATH,
575 else if (is_only_in_path)
576 btrfs_list_setup_filter(&filter_set,
577 BTRFS_LIST_FILTER_TOPID_EQUAL,
580 /* by default we shall print the following columns*/
581 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
582 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
583 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
584 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
587 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
588 BTRFS_LIST_LAYOUT_TABLE,
589 !is_list_all && !is_only_in_path, NULL);
591 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
592 BTRFS_LIST_LAYOUT_DEFAULT,
593 !is_list_all && !is_only_in_path, NULL);
596 close_file_or_dir(fd, dirstream);
598 btrfs_list_free_filter_set(filter_set);
600 btrfs_list_free_comparer_set(comparer_set);
602 usage(cmd_subvol_list_usage);
606 static const char * const cmd_subvol_snapshot_usage[] = {
607 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
608 "Create a snapshot of the subvolume",
609 "Create a writable/readonly snapshot of the subvolume <source> with",
610 "the name <name> in the <dest> directory. If only <dest> is given,",
611 "the subvolume will be named the basename of <source>.",
613 "-r create a readonly snapshot",
614 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
615 " option can be given multiple times.",
619 static int cmd_subvol_snapshot(int argc, char **argv)
623 int fd = -1, fddst = -1;
624 int len, readonly = 0;
625 char *dupname = NULL;
629 struct btrfs_ioctl_vol_args_v2 args;
630 struct btrfs_qgroup_inherit *inherit = NULL;
631 DIR *dirstream1 = NULL, *dirstream2 = NULL;
634 memset(&args, 0, sizeof(args));
636 int c = getopt(argc, argv, "c:i:r");
642 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
649 res = qgroup_inherit_add_group(&inherit, optarg);
659 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
666 usage(cmd_subvol_snapshot_usage);
670 if (check_argc_exact(argc - optind, 2))
671 usage(cmd_subvol_snapshot_usage);
673 subvol = argv[optind];
674 dst = argv[optind + 1];
676 retval = 1; /* failure */
677 res = test_issubvolume(subvol);
679 error("cannot access subvolume %s", subvol);
683 error("not a subvolume: %s", subvol);
687 res = test_isdir(dst);
689 error("'%s' exists and it is not a directory", dst);
694 dupname = strdup(subvol);
695 newname = basename(dupname);
698 dupname = strdup(dst);
699 newname = basename(dupname);
700 dupdir = strdup(dst);
701 dstdir = dirname(dupdir);
704 if (!test_issubvolname(newname)) {
705 error("invalid snapshot name '%s'", newname);
709 len = strlen(newname);
710 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
711 error("snapshot name too long '%s'", newname);
715 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
719 fd = btrfs_open_dir(subvol, &dirstream2, 1);
724 args.flags |= BTRFS_SUBVOL_RDONLY;
725 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
726 subvol, dstdir, newname);
728 printf("Create a snapshot of '%s' in '%s/%s'\n",
729 subvol, dstdir, newname);
734 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
735 args.size = qgroup_inherit_size(inherit);
736 args.qgroup_inherit = inherit;
738 strncpy_null(args.name, newname);
740 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
743 error("cannot snapshot '%s': %s", subvol, strerror(errno));
747 retval = 0; /* success */
750 close_file_or_dir(fddst, dirstream1);
751 close_file_or_dir(fd, dirstream2);
759 static const char * const cmd_subvol_get_default_usage[] = {
760 "btrfs subvolume get-default <path>",
761 "Get the default subvolume of a filesystem",
765 static int cmd_subvol_get_default(int argc, char **argv)
770 struct btrfs_list_filter_set *filter_set;
772 DIR *dirstream = NULL;
774 if (check_argc_exact(argc, 2))
775 usage(cmd_subvol_get_default_usage);
778 fd = btrfs_open_dir(subvol, &dirstream, 1);
782 ret = btrfs_list_get_default_subvolume(fd, &default_id);
784 error("failed to look up default subvolume: %s",
790 if (default_id == 0) {
791 error("'default' dir item not found");
795 /* no need to resolve roots if FS_TREE is default */
796 if (default_id == BTRFS_FS_TREE_OBJECTID) {
797 printf("ID 5 (FS_TREE)\n");
802 filter_set = btrfs_list_alloc_filter_set();
803 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
806 /* by default we shall print the following columns*/
807 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
808 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
809 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
810 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
812 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
813 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
816 btrfs_list_free_filter_set(filter_set);
818 close_file_or_dir(fd, dirstream);
822 static const char * const cmd_subvol_set_default_usage[] = {
823 "btrfs subvolume set-default <subvolid> <path>",
824 "Set the default subvolume of a filesystem",
828 static int cmd_subvol_set_default(int argc, char **argv)
834 DIR *dirstream = NULL;
836 if (check_argc_exact(argc, 3))
837 usage(cmd_subvol_set_default_usage);
842 objectid = arg_strtou64(subvolid);
844 fd = btrfs_open_dir(path, &dirstream, 1);
848 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
850 close_file_or_dir(fd, dirstream);
852 error("unable to set a new default subvolume: %s",
859 static const char * const cmd_subvol_find_new_usage[] = {
860 "btrfs subvolume find-new <path> <lastgen>",
861 "List the recently modified files in a filesystem",
865 static int cmd_subvol_find_new(int argc, char **argv)
871 DIR *dirstream = NULL;
873 if (check_argc_exact(argc, 3))
874 usage(cmd_subvol_find_new_usage);
877 last_gen = arg_strtou64(argv[2]);
879 ret = test_issubvolume(subvol);
881 error("cannot access subvolume %s", subvol);
885 error("not a subvolume: %s", subvol);
889 fd = btrfs_open_dir(subvol, &dirstream, 1);
893 ret = ioctl(fd, BTRFS_IOC_SYNC);
895 error("sync ioctl failed on '%s': %s",
896 subvol, strerror(errno));
897 close_file_or_dir(fd, dirstream);
901 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
902 close_file_or_dir(fd, dirstream);
906 static const char * const cmd_subvol_show_usage[] = {
907 "btrfs subvolume show <subvol-path>",
908 "Show more information of the subvolume",
912 static int cmd_subvol_show(int argc, char **argv)
914 struct root_info get_ri;
915 struct btrfs_list_filter_set *filter_set;
917 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
918 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
919 char raw_prefix[] = "\t\t\t\t";
921 int fd = -1, mntfd = -1;
923 DIR *dirstream1 = NULL, *dirstream2 = NULL;
925 if (check_argc_exact(argc, 2))
926 usage(cmd_subvol_show_usage);
928 fullpath = realpath(argv[1], NULL);
930 error("cannot find real path for '%s': %s",
931 argv[1], strerror(errno));
935 ret = test_issubvolume(fullpath);
937 error("cannot access subvolume %s", fullpath);
941 error("not a subvolume: %s", fullpath);
946 ret = find_mount_root(fullpath, &mnt);
948 error("find_mount_root failed on '%s': %s",
949 fullpath, strerror(-ret));
953 error("%s doesn't belong to btrfs mount point", fullpath);
957 svpath = get_subvol_name(mnt, fullpath);
959 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
963 ret = btrfs_list_get_path_rootid(fd, &sv_id);
965 error("can't get rootid for '%s'", fullpath);
969 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
973 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
974 printf("%s is toplevel subvolume\n", fullpath);
978 memset(&get_ri, 0, sizeof(get_ri));
979 get_ri.root_id = sv_id;
981 ret = btrfs_get_subvol(mntfd, &get_ri);
983 error("can't find '%s'", svpath);
988 printf("%s\n", fullpath);
989 printf("\tName: \t\t\t%s\n", get_ri.name);
991 if (uuid_is_null(get_ri.uuid))
992 strcpy(uuidparse, "-");
994 uuid_unparse(get_ri.uuid, uuidparse);
995 printf("\tUUID: \t\t\t%s\n", uuidparse);
997 if (uuid_is_null(get_ri.puuid))
998 strcpy(uuidparse, "-");
1000 uuid_unparse(get_ri.puuid, uuidparse);
1001 printf("\tParent UUID: \t\t%s\n", uuidparse);
1003 if (uuid_is_null(get_ri.ruuid))
1004 strcpy(uuidparse, "-");
1006 uuid_unparse(get_ri.ruuid, uuidparse);
1007 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1012 localtime_r(&get_ri.otime, &tm);
1013 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1016 printf("\tCreation time: \t\t%s\n", tstr);
1018 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1019 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1020 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1021 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1022 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1024 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1025 printf("\tFlags: \t\t\treadonly\n");
1027 printf("\tFlags: \t\t\t-\n");
1029 /* print the snapshots of the given subvol if any*/
1030 printf("\tSnapshot(s):\n");
1031 filter_set = btrfs_list_alloc_filter_set();
1032 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1033 (u64)(unsigned long)get_ri.uuid);
1034 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1035 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1041 free(get_ri.full_path);
1042 btrfs_list_free_filter_set(filter_set);
1045 close_file_or_dir(fd, dirstream1);
1046 close_file_or_dir(mntfd, dirstream2);
1052 static const char * const cmd_subvol_sync_usage[] = {
1053 "btrfs subvolume sync <path> [<subvol-id>...]",
1054 "Wait until given subvolume(s) are completely removed from the filesystem.",
1055 "Wait until given subvolume(s) are completely removed from the filesystem",
1057 "If no subvolume id is given, wait until all current deletion requests",
1058 "are completed, but do not wait for subvolumes deleted meanwhile.",
1059 "The status of subvolume ids is checked periodically.",
1061 "-s <N> sleep N seconds between checks (default: 1)",
1067 * If we're looking for any dead subvolume, take a shortcut and look
1068 * for any ORPHAN_ITEMs in the tree root
1070 static int fs_has_dead_subvolumes(int fd)
1073 struct btrfs_ioctl_search_args args;
1074 struct btrfs_ioctl_search_key *sk = &args.key;
1075 struct btrfs_ioctl_search_header sh;
1076 u64 min_subvolid = 0;
1079 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1080 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->min_offset = min_subvolid;
1085 sk->max_offset = (u64)-1;
1086 sk->min_transid = 0;
1087 sk->max_transid = (u64)-1;
1090 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1097 memcpy(&sh, args.buf, sizeof(sh));
1098 min_subvolid = sh.offset;
1101 * Verify that the root item is really there and we haven't hit
1104 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1105 sk->min_objectid = min_subvolid;
1106 sk->max_objectid = min_subvolid;
1107 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1108 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1110 sk->max_offset = (u64)-1;
1111 sk->min_transid = 0;
1112 sk->max_transid = (u64)-1;
1115 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1120 * Stale orphan, try the next one
1122 if (!sk->nr_items) {
1131 #define SUBVOL_ID_BATCH 1024
1134 * Enumerate all dead subvolumes that exist in the filesystem.
1135 * Fill @ids and reallocate to bigger size if needed.
1137 static int enumerate_dead_subvols(int fd, u64 **ids)
1140 struct btrfs_ioctl_search_args args;
1141 struct btrfs_ioctl_search_key *sk = &args.key;
1145 memset(&args, 0, sizeof(args));
1147 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1148 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1149 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1150 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1151 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1153 sk->max_offset = (u64)-1;
1154 sk->min_transid = 0;
1155 sk->max_transid = (u64)-1;
1156 sk->nr_items = 4096;
1160 struct btrfs_ioctl_search_header *sh;
1164 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1172 for (i = 0; i < sk->nr_items; i++) {
1173 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1176 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1180 count += SUBVOL_ID_BATCH;
1181 newids = (u64*)realloc(*ids, count);
1186 (*ids)[idx] = sh->offset;
1191 sk->min_objectid = sh->objectid;
1192 sk->min_type = sh->type;
1193 sk->min_offset = sh->offset;
1195 if (sk->min_offset < (u64)-1)
1199 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1201 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1208 static int cmd_subvol_sync(int argc, char **argv)
1213 DIR *dirstream = NULL;
1216 int sleep_interval = 1;
1220 int c = getopt(argc, argv, "s:");
1227 sleep_interval = atoi(argv[optind]);
1228 if (sleep_interval < 1) {
1229 error("invalid sleep interval %s",
1236 usage(cmd_subvol_sync_usage);
1240 if (check_argc_min(argc - optind, 1))
1241 usage(cmd_subvol_sync_usage);
1243 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1250 id_count = argc - optind;
1252 id_count = enumerate_dead_subvols(fd, &ids);
1254 error("can't enumerate dead subvolumes: %s",
1255 strerror(-id_count));
1259 if (id_count == 0) {
1264 ids = (u64*)malloc(id_count * sizeof(u64));
1266 error("not enough memory");
1271 for (i = 0; i < id_count; i++) {
1275 arg = argv[optind + i];
1277 id = strtoull(arg, NULL, 10);
1279 error("unrecognized subvolume id %s", arg);
1283 if (id < BTRFS_FIRST_FREE_OBJECTID
1284 || id > BTRFS_LAST_FREE_OBJECTID) {
1285 error("subvolume id %s out of range\n", arg);
1293 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1297 close_file_or_dir(fd, dirstream);
1302 static const char subvolume_cmd_group_info[] =
1303 "manage subvolumes: create, delete, list, etc";
1305 const struct cmd_group subvolume_cmd_group = {
1306 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1307 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1308 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1309 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1310 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1312 { "get-default", cmd_subvol_get_default,
1313 cmd_subvol_get_default_usage, NULL, 0 },
1314 { "set-default", cmd_subvol_set_default,
1315 cmd_subvol_set_default_usage, NULL, 0 },
1316 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1318 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1319 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1324 int cmd_subvolume(int argc, char **argv)
1326 return handle_command_group(&subvolume_cmd_group, argc, argv);