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);
359 error("cannot delete '%s/%s': %s", dname, vname,
365 if (commit_mode == 1) {
366 res = wait_for_commit(fd);
368 error("unable to wait for commit after '%s': %s",
369 path, strerror(errno));
381 close_file_or_dir(fd, dirstream);
382 /* avoid double free */
388 if (commit_mode == 2 && fd != -1) {
389 res = wait_for_commit(fd);
391 error("unable to do final sync after deletion: %s",
396 close_file_or_dir(fd, dirstream);
403 * - uppercase for filters and sort options
404 * - lowercase for enabling specific items in the output
406 static const char * const cmd_subvol_list_usage[] = {
407 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
408 "[--sort=gen,ogen,rootid,path] <path>",
409 "List subvolumes (and snapshots)",
411 "-p print parent ID",
412 "-a print all the subvolumes in the filesystem and",
413 " distinguish absolute and relative path with respect",
414 " to the given <path>",
415 "-c print the ogeneration of the subvolume",
416 "-g print the generation of the subvolume",
417 "-o print only subvolumes below specified path",
418 "-u print the uuid of subvolumes (and snapshots)",
419 "-q print the parent uuid of the snapshots",
420 "-R print the uuid of the received snapshots",
421 "-t print the result as a table",
422 "-s list snapshots only in the filesystem",
423 "-r list readonly subvolumes (including snapshots)",
424 "-d list deleted subvolumes that are not yet cleaned",
426 " filter the subvolumes by generation",
427 " (+value: >= value; -value: <= value; value: = value)",
429 " filter the subvolumes by ogeneration",
430 " (+value: >= value; -value: <= value; value: = value)",
431 "--sort=gen,ogen,rootid,path",
432 " list the subvolume in order of gen, ogen, rootid or path",
433 " you also can add '+' or '-' in front of each items.",
434 " (+:ascending, -:descending, ascending default)",
438 static int cmd_subvol_list(int argc, char **argv)
440 struct btrfs_list_filter_set *filter_set;
441 struct btrfs_list_comparer_set *comparer_set;
445 int ret = -1, uerr = 0;
447 int is_tab_result = 0;
449 int is_only_in_path = 0;
450 DIR *dirstream = NULL;
452 filter_set = btrfs_list_alloc_filter_set();
453 comparer_set = btrfs_list_alloc_comparer_set();
458 static const struct option long_options[] = {
459 {"sort", required_argument, NULL, 'S'},
463 c = getopt_long(argc, argv,
464 "acdgopqsurRG:C:t", long_options, NULL);
470 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
476 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
479 btrfs_list_setup_filter(&filter_set,
480 BTRFS_LIST_FILTER_DELETED,
484 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
493 btrfs_list_setup_filter(&filter_set,
494 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
496 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
497 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
500 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
503 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
506 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
509 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
512 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
513 ret = btrfs_list_parse_filter_string(optarg,
515 BTRFS_LIST_FILTER_GEN);
523 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
524 ret = btrfs_list_parse_filter_string(optarg,
526 BTRFS_LIST_FILTER_CGEN);
533 ret = btrfs_list_parse_sort_string(optarg,
548 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
551 if (check_argc_exact(argc - optind, 1)) {
556 subvol = argv[optind];
557 fd = btrfs_open_dir(subvol, &dirstream, 1);
560 error("can't access '%s'", subvol);
564 ret = btrfs_list_get_path_rootid(fd, &top_id);
566 error("can't get rootid for '%s'", subvol);
571 btrfs_list_setup_filter(&filter_set,
572 BTRFS_LIST_FILTER_FULL_PATH,
574 else if (is_only_in_path)
575 btrfs_list_setup_filter(&filter_set,
576 BTRFS_LIST_FILTER_TOPID_EQUAL,
579 /* by default we shall print the following columns*/
580 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
581 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
582 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
583 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
586 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
587 BTRFS_LIST_LAYOUT_TABLE,
588 !is_list_all && !is_only_in_path, NULL);
590 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
591 BTRFS_LIST_LAYOUT_DEFAULT,
592 !is_list_all && !is_only_in_path, NULL);
595 close_file_or_dir(fd, dirstream);
597 btrfs_list_free_filter_set(filter_set);
599 btrfs_list_free_comparer_set(comparer_set);
601 usage(cmd_subvol_list_usage);
605 static const char * const cmd_subvol_snapshot_usage[] = {
606 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
607 "Create a snapshot of the subvolume",
608 "Create a writable/readonly snapshot of the subvolume <source> with",
609 "the name <name> in the <dest> directory. If only <dest> is given,",
610 "the subvolume will be named the basename of <source>.",
612 "-r create a readonly snapshot",
613 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
614 " option can be given multiple times.",
618 static int cmd_subvol_snapshot(int argc, char **argv)
622 int fd = -1, fddst = -1;
623 int len, readonly = 0;
624 char *dupname = NULL;
628 struct btrfs_ioctl_vol_args_v2 args;
629 struct btrfs_qgroup_inherit *inherit = NULL;
630 DIR *dirstream1 = NULL, *dirstream2 = NULL;
633 memset(&args, 0, sizeof(args));
635 int c = getopt(argc, argv, "c:i:r");
641 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
648 res = qgroup_inherit_add_group(&inherit, optarg);
658 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
665 usage(cmd_subvol_snapshot_usage);
669 if (check_argc_exact(argc - optind, 2))
670 usage(cmd_subvol_snapshot_usage);
672 subvol = argv[optind];
673 dst = argv[optind + 1];
675 retval = 1; /* failure */
676 res = test_issubvolume(subvol);
678 error("cannot access subvolume %s", subvol);
682 error("not a subvolume: %s", subvol);
686 res = test_isdir(dst);
688 error("'%s' exists and it is not a directory", dst);
693 dupname = strdup(subvol);
694 newname = basename(dupname);
697 dupname = strdup(dst);
698 newname = basename(dupname);
699 dupdir = strdup(dst);
700 dstdir = dirname(dupdir);
703 if (!test_issubvolname(newname)) {
704 error("invalid snapshot name '%s'", newname);
708 len = strlen(newname);
709 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
710 error("snapshot name too long '%s'", newname);
714 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
718 fd = btrfs_open_dir(subvol, &dirstream2, 1);
723 args.flags |= BTRFS_SUBVOL_RDONLY;
724 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
725 subvol, dstdir, newname);
727 printf("Create a snapshot of '%s' in '%s/%s'\n",
728 subvol, dstdir, newname);
733 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
734 args.size = qgroup_inherit_size(inherit);
735 args.qgroup_inherit = inherit;
737 strncpy_null(args.name, newname);
739 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
742 error("cannot snapshot '%s': %s", subvol, strerror(errno));
746 retval = 0; /* success */
749 close_file_or_dir(fddst, dirstream1);
750 close_file_or_dir(fd, dirstream2);
758 static const char * const cmd_subvol_get_default_usage[] = {
759 "btrfs subvolume get-default <path>",
760 "Get the default subvolume of a filesystem",
764 static int cmd_subvol_get_default(int argc, char **argv)
769 struct btrfs_list_filter_set *filter_set;
771 DIR *dirstream = NULL;
773 if (check_argc_exact(argc, 2))
774 usage(cmd_subvol_get_default_usage);
777 fd = btrfs_open_dir(subvol, &dirstream, 1);
781 ret = btrfs_list_get_default_subvolume(fd, &default_id);
783 error("failed to look up default subvolume: %s",
789 if (default_id == 0) {
790 error("'default' dir item not found");
794 /* no need to resolve roots if FS_TREE is default */
795 if (default_id == BTRFS_FS_TREE_OBJECTID) {
796 printf("ID 5 (FS_TREE)\n");
801 filter_set = btrfs_list_alloc_filter_set();
802 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
805 /* by default we shall print the following columns*/
806 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
807 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
808 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
809 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
811 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
812 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
815 btrfs_list_free_filter_set(filter_set);
817 close_file_or_dir(fd, dirstream);
821 static const char * const cmd_subvol_set_default_usage[] = {
822 "btrfs subvolume set-default <subvolid> <path>",
823 "Set the default subvolume of a filesystem",
827 static int cmd_subvol_set_default(int argc, char **argv)
833 DIR *dirstream = NULL;
835 if (check_argc_exact(argc, 3))
836 usage(cmd_subvol_set_default_usage);
841 objectid = arg_strtou64(subvolid);
843 fd = btrfs_open_dir(path, &dirstream, 1);
847 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
849 close_file_or_dir(fd, dirstream);
851 error("unable to set a new default subvolume: %s",
858 static const char * const cmd_subvol_find_new_usage[] = {
859 "btrfs subvolume find-new <path> <lastgen>",
860 "List the recently modified files in a filesystem",
864 static int cmd_subvol_find_new(int argc, char **argv)
870 DIR *dirstream = NULL;
872 if (check_argc_exact(argc, 3))
873 usage(cmd_subvol_find_new_usage);
876 last_gen = arg_strtou64(argv[2]);
878 ret = test_issubvolume(subvol);
880 error("cannot access subvolume %s", subvol);
884 error("not a subvolume: %s", subvol);
888 fd = btrfs_open_dir(subvol, &dirstream, 1);
892 ret = ioctl(fd, BTRFS_IOC_SYNC);
894 error("sync ioctl failed on '%s': %s",
895 subvol, strerror(errno));
896 close_file_or_dir(fd, dirstream);
900 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
901 close_file_or_dir(fd, dirstream);
905 static const char * const cmd_subvol_show_usage[] = {
906 "btrfs subvolume show <subvol-path>",
907 "Show more information of the subvolume",
911 static int cmd_subvol_show(int argc, char **argv)
913 struct root_info get_ri;
914 struct btrfs_list_filter_set *filter_set;
916 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
917 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
918 char raw_prefix[] = "\t\t\t\t";
920 int fd = -1, mntfd = -1;
922 DIR *dirstream1 = NULL, *dirstream2 = NULL;
925 static const struct option long_options[] = {
928 int c = getopt_long(argc, argv, "", long_options, NULL);
935 usage(cmd_subvol_show_usage);
939 if (check_argc_exact(argc - optind, 1))
940 usage(cmd_subvol_show_usage);
942 fullpath = realpath(argv[optind], NULL);
944 error("cannot find real path for '%s': %s",
945 argv[optind], strerror(errno));
949 ret = test_issubvolume(fullpath);
951 error("cannot access subvolume %s", fullpath);
955 error("not a subvolume: %s", fullpath);
960 ret = find_mount_root(fullpath, &mnt);
962 error("find_mount_root failed on '%s': %s",
963 fullpath, strerror(-ret));
967 error("%s doesn't belong to btrfs mount point", fullpath);
971 svpath = get_subvol_name(mnt, fullpath);
973 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
977 ret = btrfs_list_get_path_rootid(fd, &sv_id);
979 error("can't get rootid for '%s'", fullpath);
983 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
987 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
988 printf("%s is toplevel subvolume\n", fullpath);
992 memset(&get_ri, 0, sizeof(get_ri));
993 get_ri.root_id = sv_id;
995 ret = btrfs_get_subvol(mntfd, &get_ri);
997 error("can't find '%s'", svpath);
1001 /* print the info */
1002 printf("%s\n", fullpath);
1003 printf("\tName: \t\t\t%s\n", get_ri.name);
1005 if (uuid_is_null(get_ri.uuid))
1006 strcpy(uuidparse, "-");
1008 uuid_unparse(get_ri.uuid, uuidparse);
1009 printf("\tUUID: \t\t\t%s\n", uuidparse);
1011 if (uuid_is_null(get_ri.puuid))
1012 strcpy(uuidparse, "-");
1014 uuid_unparse(get_ri.puuid, uuidparse);
1015 printf("\tParent UUID: \t\t%s\n", uuidparse);
1017 if (uuid_is_null(get_ri.ruuid))
1018 strcpy(uuidparse, "-");
1020 uuid_unparse(get_ri.ruuid, uuidparse);
1021 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1026 localtime_r(&get_ri.otime, &tm);
1027 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1030 printf("\tCreation time: \t\t%s\n", tstr);
1032 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1033 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1034 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1035 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1036 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1038 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1039 printf("\tFlags: \t\t\treadonly\n");
1041 printf("\tFlags: \t\t\t-\n");
1043 /* print the snapshots of the given subvol if any*/
1044 printf("\tSnapshot(s):\n");
1045 filter_set = btrfs_list_alloc_filter_set();
1046 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1047 (u64)(unsigned long)get_ri.uuid);
1048 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1049 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1055 free(get_ri.full_path);
1056 btrfs_list_free_filter_set(filter_set);
1059 close_file_or_dir(fd, dirstream1);
1060 close_file_or_dir(mntfd, dirstream2);
1066 static const char * const cmd_subvol_sync_usage[] = {
1067 "btrfs subvolume sync <path> [<subvol-id>...]",
1068 "Wait until given subvolume(s) are completely removed from the filesystem.",
1069 "Wait until given subvolume(s) are completely removed from the filesystem",
1071 "If no subvolume id is given, wait until all current deletion requests",
1072 "are completed, but do not wait for subvolumes deleted meanwhile.",
1073 "The status of subvolume ids is checked periodically.",
1075 "-s <N> sleep N seconds between checks (default: 1)",
1081 * If we're looking for any dead subvolume, take a shortcut and look
1082 * for any ORPHAN_ITEMs in the tree root
1084 static int fs_has_dead_subvolumes(int fd)
1087 struct btrfs_ioctl_search_args args;
1088 struct btrfs_ioctl_search_key *sk = &args.key;
1089 struct btrfs_ioctl_search_header sh;
1090 u64 min_subvolid = 0;
1093 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1094 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1095 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1096 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1097 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1098 sk->min_offset = min_subvolid;
1099 sk->max_offset = (u64)-1;
1100 sk->min_transid = 0;
1101 sk->max_transid = (u64)-1;
1104 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1111 memcpy(&sh, args.buf, sizeof(sh));
1112 min_subvolid = sh.offset;
1115 * Verify that the root item is really there and we haven't hit
1118 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1119 sk->min_objectid = min_subvolid;
1120 sk->max_objectid = min_subvolid;
1121 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1122 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1124 sk->max_offset = (u64)-1;
1125 sk->min_transid = 0;
1126 sk->max_transid = (u64)-1;
1129 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1134 * Stale orphan, try the next one
1136 if (!sk->nr_items) {
1145 #define SUBVOL_ID_BATCH 1024
1148 * Enumerate all dead subvolumes that exist in the filesystem.
1149 * Fill @ids and reallocate to bigger size if needed.
1151 static int enumerate_dead_subvols(int fd, u64 **ids)
1154 struct btrfs_ioctl_search_args args;
1155 struct btrfs_ioctl_search_key *sk = &args.key;
1159 memset(&args, 0, sizeof(args));
1161 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1162 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1163 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1164 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1165 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1167 sk->max_offset = (u64)-1;
1168 sk->min_transid = 0;
1169 sk->max_transid = (u64)-1;
1170 sk->nr_items = 4096;
1174 struct btrfs_ioctl_search_header *sh;
1178 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1186 for (i = 0; i < sk->nr_items; i++) {
1187 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1190 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1194 count += SUBVOL_ID_BATCH;
1195 newids = (u64*)realloc(*ids, count);
1200 (*ids)[idx] = sh->offset;
1205 sk->min_objectid = sh->objectid;
1206 sk->min_type = sh->type;
1207 sk->min_offset = sh->offset;
1209 if (sk->min_offset < (u64)-1)
1213 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1215 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1222 static int cmd_subvol_sync(int argc, char **argv)
1227 DIR *dirstream = NULL;
1230 int sleep_interval = 1;
1234 int c = getopt(argc, argv, "s:");
1241 sleep_interval = atoi(argv[optind]);
1242 if (sleep_interval < 1) {
1243 error("invalid sleep interval %s",
1250 usage(cmd_subvol_sync_usage);
1254 if (check_argc_min(argc - optind, 1))
1255 usage(cmd_subvol_sync_usage);
1257 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1264 id_count = argc - optind;
1266 id_count = enumerate_dead_subvols(fd, &ids);
1268 error("can't enumerate dead subvolumes: %s",
1269 strerror(-id_count));
1273 if (id_count == 0) {
1278 ids = (u64*)malloc(id_count * sizeof(u64));
1280 error("not enough memory");
1285 for (i = 0; i < id_count; i++) {
1289 arg = argv[optind + i];
1291 id = strtoull(arg, NULL, 10);
1293 error("unrecognized subvolume id %s", arg);
1297 if (id < BTRFS_FIRST_FREE_OBJECTID
1298 || id > BTRFS_LAST_FREE_OBJECTID) {
1299 error("subvolume id %s out of range\n", arg);
1307 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1311 close_file_or_dir(fd, dirstream);
1316 static const char subvolume_cmd_group_info[] =
1317 "manage subvolumes: create, delete, list, etc";
1319 const struct cmd_group subvolume_cmd_group = {
1320 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1321 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1322 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1323 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1324 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1326 { "get-default", cmd_subvol_get_default,
1327 cmd_subvol_get_default_usage, NULL, 0 },
1328 { "set-default", cmd_subvol_set_default,
1329 cmd_subvol_set_default_usage, NULL, 0 },
1330 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1332 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1333 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1338 int cmd_subvolume(int argc, char **argv)
1340 return handle_command_group(&subvolume_cmd_group, argc, argv);