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;
924 if (check_argc_exact(argc, 2))
925 usage(cmd_subvol_show_usage);
927 fullpath = realpath(argv[1], NULL);
929 error("cannot find real path for '%s': %s",
930 argv[1], strerror(errno));
934 ret = test_issubvolume(fullpath);
936 error("cannot access subvolume %s", fullpath);
940 error("not a subvolume: %s", fullpath);
945 ret = find_mount_root(fullpath, &mnt);
947 error("find_mount_root failed on '%s': %s",
948 fullpath, strerror(-ret));
952 error("%s doesn't belong to btrfs mount point", fullpath);
956 svpath = get_subvol_name(mnt, fullpath);
958 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
962 ret = btrfs_list_get_path_rootid(fd, &sv_id);
964 error("can't get rootid for '%s'", fullpath);
968 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
972 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
973 printf("%s is toplevel subvolume\n", fullpath);
977 memset(&get_ri, 0, sizeof(get_ri));
978 get_ri.root_id = sv_id;
980 ret = btrfs_get_subvol(mntfd, &get_ri);
982 error("can't find '%s'", svpath);
987 printf("%s\n", fullpath);
988 printf("\tName: \t\t\t%s\n", get_ri.name);
990 if (uuid_is_null(get_ri.uuid))
991 strcpy(uuidparse, "-");
993 uuid_unparse(get_ri.uuid, uuidparse);
994 printf("\tUUID: \t\t\t%s\n", uuidparse);
996 if (uuid_is_null(get_ri.puuid))
997 strcpy(uuidparse, "-");
999 uuid_unparse(get_ri.puuid, uuidparse);
1000 printf("\tParent UUID: \t\t%s\n", uuidparse);
1002 if (uuid_is_null(get_ri.ruuid))
1003 strcpy(uuidparse, "-");
1005 uuid_unparse(get_ri.ruuid, uuidparse);
1006 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1011 localtime_r(&get_ri.otime, &tm);
1012 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1015 printf("\tCreation time: \t\t%s\n", tstr);
1017 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1018 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1019 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1020 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1021 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1023 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1024 printf("\tFlags: \t\t\treadonly\n");
1026 printf("\tFlags: \t\t\t-\n");
1028 /* print the snapshots of the given subvol if any*/
1029 printf("\tSnapshot(s):\n");
1030 filter_set = btrfs_list_alloc_filter_set();
1031 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1032 (u64)(unsigned long)get_ri.uuid);
1033 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1034 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1040 free(get_ri.full_path);
1041 btrfs_list_free_filter_set(filter_set);
1044 close_file_or_dir(fd, dirstream1);
1045 close_file_or_dir(mntfd, dirstream2);
1051 static const char * const cmd_subvol_sync_usage[] = {
1052 "btrfs subvolume sync <path> [<subvol-id>...]",
1053 "Wait until given subvolume(s) are completely removed from the filesystem.",
1054 "Wait until given subvolume(s) are completely removed from the filesystem",
1056 "If no subvolume id is given, wait until all current deletion requests",
1057 "are completed, but do not wait for subvolumes deleted meanwhile.",
1058 "The status of subvolume ids is checked periodically.",
1060 "-s <N> sleep N seconds between checks (default: 1)",
1066 * If we're looking for any dead subvolume, take a shortcut and look
1067 * for any ORPHAN_ITEMs in the tree root
1069 static int fs_has_dead_subvolumes(int fd)
1072 struct btrfs_ioctl_search_args args;
1073 struct btrfs_ioctl_search_key *sk = &args.key;
1074 struct btrfs_ioctl_search_header sh;
1075 u64 min_subvolid = 0;
1078 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1079 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1080 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1082 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->min_offset = min_subvolid;
1084 sk->max_offset = (u64)-1;
1085 sk->min_transid = 0;
1086 sk->max_transid = (u64)-1;
1089 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1096 memcpy(&sh, args.buf, sizeof(sh));
1097 min_subvolid = sh.offset;
1100 * Verify that the root item is really there and we haven't hit
1103 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1104 sk->min_objectid = min_subvolid;
1105 sk->max_objectid = min_subvolid;
1106 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1107 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1109 sk->max_offset = (u64)-1;
1110 sk->min_transid = 0;
1111 sk->max_transid = (u64)-1;
1114 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1119 * Stale orphan, try the next one
1121 if (!sk->nr_items) {
1130 #define SUBVOL_ID_BATCH 1024
1133 * Enumerate all dead subvolumes that exist in the filesystem.
1134 * Fill @ids and reallocate to bigger size if needed.
1136 static int enumerate_dead_subvols(int fd, u64 **ids)
1139 struct btrfs_ioctl_search_args args;
1140 struct btrfs_ioctl_search_key *sk = &args.key;
1144 memset(&args, 0, sizeof(args));
1146 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1147 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1148 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1149 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1150 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1152 sk->max_offset = (u64)-1;
1153 sk->min_transid = 0;
1154 sk->max_transid = (u64)-1;
1155 sk->nr_items = 4096;
1159 struct btrfs_ioctl_search_header *sh;
1163 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1171 for (i = 0; i < sk->nr_items; i++) {
1172 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1175 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1179 count += SUBVOL_ID_BATCH;
1180 newids = (u64*)realloc(*ids, count);
1185 (*ids)[idx] = sh->offset;
1190 sk->min_objectid = sh->objectid;
1191 sk->min_type = sh->type;
1192 sk->min_offset = sh->offset;
1194 if (sk->min_offset < (u64)-1)
1198 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1200 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1207 static int cmd_subvol_sync(int argc, char **argv)
1212 DIR *dirstream = NULL;
1215 int sleep_interval = 1;
1219 int c = getopt(argc, argv, "s:");
1226 sleep_interval = atoi(argv[optind]);
1227 if (sleep_interval < 1) {
1228 error("invalid sleep interval %s",
1235 usage(cmd_subvol_sync_usage);
1239 if (check_argc_min(argc - optind, 1))
1240 usage(cmd_subvol_sync_usage);
1242 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1249 id_count = argc - optind;
1251 id_count = enumerate_dead_subvols(fd, &ids);
1253 error("can't enumerate dead subvolumes: %s",
1254 strerror(-id_count));
1258 if (id_count == 0) {
1263 ids = (u64*)malloc(id_count * sizeof(u64));
1265 error("not enough memory");
1270 for (i = 0; i < id_count; i++) {
1274 arg = argv[optind + i];
1276 id = strtoull(arg, NULL, 10);
1278 error("unrecognized subvolume id %s", arg);
1282 if (id < BTRFS_FIRST_FREE_OBJECTID
1283 || id > BTRFS_LAST_FREE_OBJECTID) {
1284 error("subvolume id %s out of range\n", arg);
1292 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1296 close_file_or_dir(fd, dirstream);
1301 static const char subvolume_cmd_group_info[] =
1302 "manage subvolumes: create, delete, list, etc";
1304 const struct cmd_group subvolume_cmd_group = {
1305 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1306 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1307 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1308 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1309 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1311 { "get-default", cmd_subvol_get_default,
1312 cmd_subvol_get_default_usage, NULL, 0 },
1313 { "set-default", cmd_subvol_set_default,
1314 cmd_subvol_set_default_usage, NULL, 0 },
1315 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1317 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1318 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1323 int cmd_subvolume(int argc, char **argv)
1325 return handle_command_group(&subvolume_cmd_group, argc, argv);