2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
21 #include <sys/ioctl.h>
28 #include <uuid/uuid.h>
29 #include <linux/magic.h>
31 #include "kerncompat.h"
38 #include "btrfs-list.h"
41 static int is_subvolume_cleaned(int fd, u64 subvolid)
44 struct btrfs_ioctl_search_args args;
45 struct btrfs_ioctl_search_key *sk = &args.key;
47 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
48 sk->min_objectid = subvolid;
49 sk->max_objectid = subvolid;
50 sk->min_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_type = BTRFS_ROOT_ITEM_KEY;
53 sk->max_offset = (u64)-1;
55 sk->max_transid = (u64)-1;
58 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
62 if (sk->nr_items == 0)
68 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
77 for (i = 0; i < count; i++) {
80 ret = is_subvolume_cleaned(fd, ids[i]);
83 "cannot read status of dead subvolume %llu: %s",
84 (unsigned long long)ids[i], strerror(-ret));
88 printf("Subvolume id %llu is gone\n", ids[i]);
96 sleep(sleep_interval);
102 static const char * const subvolume_cmd_group_usage[] = {
103 "btrfs subvolume <command> <args>",
107 static const char * const cmd_subvol_create_usage[] = {
108 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
109 "Create a subvolume",
110 "Create a subvolume <name> in <dest>. If <dest> is not given",
111 "subvolume <name> will be created in the current directory.",
113 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
114 " option can be given multiple times.",
118 static int cmd_subvol_create(int argc, char **argv)
120 int retval, res, len;
122 char *dupname = NULL;
127 struct btrfs_qgroup_inherit *inherit = NULL;
128 DIR *dirstream = NULL;
132 int c = getopt(argc, argv, "c:i:v");
138 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
145 res = qgroup_inherit_add_group(&inherit, optarg);
152 usage(cmd_subvol_create_usage);
156 if (check_argc_exact(argc - optind, 1))
157 usage(cmd_subvol_create_usage);
161 retval = 1; /* failure */
162 res = test_isdir(dst);
163 if (res < 0 && res != -ENOENT) {
164 error("cannot access %s: %s", dst, strerror(-res));
168 error("target path already exists: %s", dst);
172 dupname = strdup(dst);
173 newname = basename(dupname);
174 dupdir = strdup(dst);
175 dstdir = dirname(dupdir);
177 if (!test_issubvolname(newname)) {
178 error("invalid subvolume name: %s", newname);
182 len = strlen(newname);
183 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
184 error("subvolume name too long: %s", newname);
188 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
192 printf("Create subvolume '%s/%s'\n", dstdir, newname);
194 struct btrfs_ioctl_vol_args_v2 args;
196 memset(&args, 0, sizeof(args));
197 strncpy_null(args.name, newname);
198 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
199 args.size = qgroup_inherit_size(inherit);
200 args.qgroup_inherit = inherit;
202 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
204 struct btrfs_ioctl_vol_args args;
206 memset(&args, 0, sizeof(args));
207 strncpy_null(args.name, newname);
209 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
213 error("cannot create subvolume: %s", strerror(errno));
217 retval = 0; /* success */
219 close_file_or_dir(fddst, dirstream);
228 * Test if path is a subvolume
230 * 0 - path exists but it is not a subvolume
231 * 1 - path exists and it is a subvolume
234 int test_issubvolume(const char *path)
240 res = stat(path, &st);
244 if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
247 res = statfs(path, &stfs);
251 return (int)stfs.f_type == BTRFS_SUPER_MAGIC;
254 static int wait_for_commit(int fd)
258 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
261 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
264 static const char * const cmd_subvol_delete_usage[] = {
265 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
266 "Delete subvolume(s)",
267 "Delete subvolumes from the filesystem. The corresponding directory",
268 "is removed instantly but the data blocks are removed later.",
269 "The deletion does not involve full commit by default due to",
270 "performance reasons (as a consequence, the subvolume may appear again",
271 "after a crash). Use one of the --commit options to wait until the",
272 "operation is safely stored on the media.",
274 "-c|--commit-after wait for transaction commit at the end of the operation",
275 "-C|--commit-each wait for transaction commit after deleting each subvolume",
279 static int cmd_subvol_delete(int argc, char **argv)
284 struct btrfs_ioctl_vol_args args;
285 char *dname, *vname, *cpath;
286 char *dupdname = NULL;
287 char *dupvname = NULL;
289 DIR *dirstream = NULL;
296 static const struct option long_options[] = {
297 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
298 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
302 c = getopt_long(argc, argv, "cC", long_options, NULL);
317 usage(cmd_subvol_delete_usage);
321 if (check_argc_min(argc - optind, 1))
322 usage(cmd_subvol_delete_usage);
325 printf("Transaction commit: %s\n",
326 !commit_mode ? "none (default)" :
327 commit_mode == 1 ? "at the end" : "after each");
335 res = test_issubvolume(path);
337 error("cannot access subvolume %s: %s", path, strerror(-res));
342 error("not a subvolume: %s", path);
347 cpath = realpath(path, NULL);
350 error("cannot find real path for '%s': %s",
351 path, strerror(errno));
354 dupdname = strdup(cpath);
355 dname = dirname(dupdname);
356 dupvname = strdup(cpath);
357 vname = basename(dupvname);
360 fd = btrfs_open_dir(dname, &dirstream, 1);
366 printf("Delete subvolume (%s): '%s/%s'\n",
367 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
368 ? "commit" : "no-commit", dname, vname);
369 memset(&args, 0, sizeof(args));
370 strncpy_null(args.name, vname);
371 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
373 error("cannot delete '%s/%s': %s", dname, vname,
379 if (commit_mode == 1) {
380 res = wait_for_commit(fd);
382 error("unable to wait for commit after '%s': %s",
383 path, strerror(errno));
395 close_file_or_dir(fd, dirstream);
396 /* avoid double free */
402 if (commit_mode == 2 && fd != -1) {
403 res = wait_for_commit(fd);
405 error("unable to do final sync after deletion: %s",
410 close_file_or_dir(fd, dirstream);
417 * - uppercase for filters and sort options
418 * - lowercase for enabling specific items in the output
420 static const char * const cmd_subvol_list_usage[] = {
421 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
422 "[--sort=gen,ogen,rootid,path] <path>",
423 "List subvolumes (and snapshots)",
425 "-p print parent ID",
426 "-a print all the subvolumes in the filesystem and",
427 " distinguish absolute and relative path with respect",
428 " to the given <path>",
429 "-c print the ogeneration of the subvolume",
430 "-g print the generation of the subvolume",
431 "-o print only subvolumes below specified path",
432 "-u print the uuid of subvolumes (and snapshots)",
433 "-q print the parent uuid of the snapshots",
434 "-R print the uuid of the received snapshots",
435 "-t print the result as a table",
436 "-s list snapshots only in the filesystem",
437 "-r list readonly subvolumes (including snapshots)",
438 "-d list deleted subvolumes that are not yet cleaned",
440 " filter the subvolumes by generation",
441 " (+value: >= value; -value: <= value; value: = value)",
443 " filter the subvolumes by ogeneration",
444 " (+value: >= value; -value: <= value; value: = value)",
445 "--sort=gen,ogen,rootid,path",
446 " list the subvolume in order of gen, ogen, rootid or path",
447 " you also can add '+' or '-' in front of each items.",
448 " (+:ascending, -:descending, ascending default)",
452 static int cmd_subvol_list(int argc, char **argv)
454 struct btrfs_list_filter_set *filter_set;
455 struct btrfs_list_comparer_set *comparer_set;
459 int ret = -1, uerr = 0;
461 int is_tab_result = 0;
463 int is_only_in_path = 0;
464 DIR *dirstream = NULL;
466 filter_set = btrfs_list_alloc_filter_set();
467 comparer_set = btrfs_list_alloc_comparer_set();
472 static const struct option long_options[] = {
473 {"sort", required_argument, NULL, 'S'},
477 c = getopt_long(argc, argv,
478 "acdgopqsurRG:C:t", long_options, NULL);
484 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
490 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
493 btrfs_list_setup_filter(&filter_set,
494 BTRFS_LIST_FILTER_DELETED,
498 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
507 btrfs_list_setup_filter(&filter_set,
508 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
510 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
511 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
514 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
517 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
520 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
523 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
526 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
527 ret = btrfs_list_parse_filter_string(optarg,
529 BTRFS_LIST_FILTER_GEN);
537 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
538 ret = btrfs_list_parse_filter_string(optarg,
540 BTRFS_LIST_FILTER_CGEN);
547 ret = btrfs_list_parse_sort_string(optarg,
562 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
565 if (check_argc_exact(argc - optind, 1)) {
570 subvol = argv[optind];
571 fd = btrfs_open_dir(subvol, &dirstream, 1);
574 error("can't access '%s'", subvol);
578 ret = btrfs_list_get_path_rootid(fd, &top_id);
580 error("can't get rootid for '%s'", subvol);
585 btrfs_list_setup_filter(&filter_set,
586 BTRFS_LIST_FILTER_FULL_PATH,
588 else if (is_only_in_path)
589 btrfs_list_setup_filter(&filter_set,
590 BTRFS_LIST_FILTER_TOPID_EQUAL,
593 /* by default we shall print the following columns*/
594 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
595 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
596 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
597 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
600 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
601 BTRFS_LIST_LAYOUT_TABLE,
602 !is_list_all && !is_only_in_path, NULL);
604 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
605 BTRFS_LIST_LAYOUT_DEFAULT,
606 !is_list_all && !is_only_in_path, NULL);
609 close_file_or_dir(fd, dirstream);
611 btrfs_list_free_filter_set(filter_set);
613 btrfs_list_free_comparer_set(comparer_set);
615 usage(cmd_subvol_list_usage);
619 static const char * const cmd_subvol_snapshot_usage[] = {
620 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
621 "Create a snapshot of the subvolume",
622 "Create a writable/readonly snapshot of the subvolume <source> with",
623 "the name <name> in the <dest> directory. If only <dest> is given,",
624 "the subvolume will be named the basename of <source>.",
626 "-r create a readonly snapshot",
627 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
628 " option can be given multiple times.",
632 static int cmd_subvol_snapshot(int argc, char **argv)
636 int fd = -1, fddst = -1;
637 int len, readonly = 0;
638 char *dupname = NULL;
642 struct btrfs_ioctl_vol_args_v2 args;
643 struct btrfs_qgroup_inherit *inherit = NULL;
644 DIR *dirstream1 = NULL, *dirstream2 = NULL;
647 memset(&args, 0, sizeof(args));
649 int c = getopt(argc, argv, "c:i:r");
655 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
662 res = qgroup_inherit_add_group(&inherit, optarg);
672 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
679 usage(cmd_subvol_snapshot_usage);
683 if (check_argc_exact(argc - optind, 2))
684 usage(cmd_subvol_snapshot_usage);
686 subvol = argv[optind];
687 dst = argv[optind + 1];
689 retval = 1; /* failure */
690 res = test_issubvolume(subvol);
692 error("cannot access subvolume %s: %s", subvol, strerror(-res));
696 error("not a subvolume: %s", subvol);
700 res = test_isdir(dst);
701 if (res < 0 && res != -ENOENT) {
702 error("cannot access %s: %s", dst, strerror(-res));
706 error("'%s' exists and it is not a directory", dst);
711 dupname = strdup(subvol);
712 newname = basename(dupname);
715 dupname = strdup(dst);
716 newname = basename(dupname);
717 dupdir = strdup(dst);
718 dstdir = dirname(dupdir);
721 if (!test_issubvolname(newname)) {
722 error("invalid snapshot name '%s'", newname);
726 len = strlen(newname);
727 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
728 error("snapshot name too long '%s'", newname);
732 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
736 fd = btrfs_open_dir(subvol, &dirstream2, 1);
741 args.flags |= BTRFS_SUBVOL_RDONLY;
742 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
743 subvol, dstdir, newname);
745 printf("Create a snapshot of '%s' in '%s/%s'\n",
746 subvol, dstdir, newname);
751 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
752 args.size = qgroup_inherit_size(inherit);
753 args.qgroup_inherit = inherit;
755 strncpy_null(args.name, newname);
757 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
760 error("cannot snapshot '%s': %s", subvol, strerror(errno));
764 retval = 0; /* success */
767 close_file_or_dir(fddst, dirstream1);
768 close_file_or_dir(fd, dirstream2);
776 static const char * const cmd_subvol_get_default_usage[] = {
777 "btrfs subvolume get-default <path>",
778 "Get the default subvolume of a filesystem",
782 static int cmd_subvol_get_default(int argc, char **argv)
787 struct btrfs_list_filter_set *filter_set;
789 DIR *dirstream = NULL;
791 if (check_argc_exact(argc, 2))
792 usage(cmd_subvol_get_default_usage);
795 fd = btrfs_open_dir(subvol, &dirstream, 1);
799 ret = btrfs_list_get_default_subvolume(fd, &default_id);
801 error("failed to look up default subvolume: %s",
807 if (default_id == 0) {
808 error("'default' dir item not found");
812 /* no need to resolve roots if FS_TREE is default */
813 if (default_id == BTRFS_FS_TREE_OBJECTID) {
814 printf("ID 5 (FS_TREE)\n");
819 filter_set = btrfs_list_alloc_filter_set();
820 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
823 /* by default we shall print the following columns*/
824 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
825 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
826 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
827 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
829 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
830 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
833 btrfs_list_free_filter_set(filter_set);
835 close_file_or_dir(fd, dirstream);
839 static const char * const cmd_subvol_set_default_usage[] = {
840 "btrfs subvolume set-default <subvolid> <path>",
841 "Set the default subvolume of a filesystem",
845 static int cmd_subvol_set_default(int argc, char **argv)
851 DIR *dirstream = NULL;
853 if (check_argc_exact(argc, 3))
854 usage(cmd_subvol_set_default_usage);
859 objectid = arg_strtou64(subvolid);
861 fd = btrfs_open_dir(path, &dirstream, 1);
865 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
867 close_file_or_dir(fd, dirstream);
869 error("unable to set a new default subvolume: %s",
876 static const char * const cmd_subvol_find_new_usage[] = {
877 "btrfs subvolume find-new <path> <lastgen>",
878 "List the recently modified files in a filesystem",
882 static int cmd_subvol_find_new(int argc, char **argv)
888 DIR *dirstream = NULL;
890 if (check_argc_exact(argc, 3))
891 usage(cmd_subvol_find_new_usage);
894 last_gen = arg_strtou64(argv[2]);
896 ret = test_issubvolume(subvol);
898 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
902 error("not a subvolume: %s", subvol);
906 fd = btrfs_open_dir(subvol, &dirstream, 1);
910 ret = ioctl(fd, BTRFS_IOC_SYNC);
912 error("sync ioctl failed on '%s': %s",
913 subvol, strerror(errno));
914 close_file_or_dir(fd, dirstream);
918 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
919 close_file_or_dir(fd, dirstream);
923 static const char * const cmd_subvol_show_usage[] = {
924 "btrfs subvolume show <subvol-path>",
925 "Show more information of the subvolume",
929 static int cmd_subvol_show(int argc, char **argv)
931 struct root_info get_ri;
932 struct btrfs_list_filter_set *filter_set;
934 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
935 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
936 char raw_prefix[] = "\t\t\t\t";
938 int fd = -1, mntfd = -1;
940 DIR *dirstream1 = NULL, *dirstream2 = NULL;
943 static const struct option long_options[] = {
946 int c = getopt_long(argc, argv, "", long_options, NULL);
953 usage(cmd_subvol_show_usage);
957 if (check_argc_exact(argc - optind, 1))
958 usage(cmd_subvol_show_usage);
960 fullpath = realpath(argv[optind], NULL);
962 error("cannot find real path for '%s': %s",
963 argv[optind], strerror(errno));
967 ret = test_issubvolume(fullpath);
969 error("cannot access subvolume %s: %s", fullpath,
974 error("not a subvolume: %s", fullpath);
979 ret = find_mount_root(fullpath, &mnt);
981 error("find_mount_root failed on '%s': %s",
982 fullpath, strerror(-ret));
986 error("%s doesn't belong to btrfs mount point", fullpath);
990 svpath = get_subvol_name(mnt, fullpath);
992 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
996 ret = btrfs_list_get_path_rootid(fd, &sv_id);
998 error("can't get rootid for '%s'", fullpath);
1002 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
1006 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1007 printf("%s is toplevel subvolume\n", fullpath);
1011 memset(&get_ri, 0, sizeof(get_ri));
1012 get_ri.root_id = sv_id;
1014 ret = btrfs_get_subvol(mntfd, &get_ri);
1016 error("can't find '%s'", svpath);
1020 /* print the info */
1021 printf("%s\n", fullpath);
1022 printf("\tName: \t\t\t%s\n", get_ri.name);
1024 if (uuid_is_null(get_ri.uuid))
1025 strcpy(uuidparse, "-");
1027 uuid_unparse(get_ri.uuid, uuidparse);
1028 printf("\tUUID: \t\t\t%s\n", uuidparse);
1030 if (uuid_is_null(get_ri.puuid))
1031 strcpy(uuidparse, "-");
1033 uuid_unparse(get_ri.puuid, uuidparse);
1034 printf("\tParent UUID: \t\t%s\n", uuidparse);
1036 if (uuid_is_null(get_ri.ruuid))
1037 strcpy(uuidparse, "-");
1039 uuid_unparse(get_ri.ruuid, uuidparse);
1040 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1045 localtime_r(&get_ri.otime, &tm);
1046 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1049 printf("\tCreation time: \t\t%s\n", tstr);
1051 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1052 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1053 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1054 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1055 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1057 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1058 printf("\tFlags: \t\t\treadonly\n");
1060 printf("\tFlags: \t\t\t-\n");
1062 /* print the snapshots of the given subvol if any*/
1063 printf("\tSnapshot(s):\n");
1064 filter_set = btrfs_list_alloc_filter_set();
1065 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1066 (u64)(unsigned long)get_ri.uuid);
1067 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1068 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1074 free(get_ri.full_path);
1075 btrfs_list_free_filter_set(filter_set);
1078 close_file_or_dir(fd, dirstream1);
1079 close_file_or_dir(mntfd, dirstream2);
1085 static const char * const cmd_subvol_sync_usage[] = {
1086 "btrfs subvolume sync <path> [<subvol-id>...]",
1087 "Wait until given subvolume(s) are completely removed from the filesystem.",
1088 "Wait until given subvolume(s) are completely removed from the filesystem",
1090 "If no subvolume id is given, wait until all current deletion requests",
1091 "are completed, but do not wait for subvolumes deleted meanwhile.",
1092 "The status of subvolume ids is checked periodically.",
1094 "-s <N> sleep N seconds between checks (default: 1)",
1100 * If we're looking for any dead subvolume, take a shortcut and look
1101 * for any ORPHAN_ITEMs in the tree root
1103 static int fs_has_dead_subvolumes(int fd)
1106 struct btrfs_ioctl_search_args args;
1107 struct btrfs_ioctl_search_key *sk = &args.key;
1108 struct btrfs_ioctl_search_header sh;
1109 u64 min_subvolid = 0;
1112 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1113 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1114 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1115 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1116 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1117 sk->min_offset = min_subvolid;
1118 sk->max_offset = (u64)-1;
1119 sk->min_transid = 0;
1120 sk->max_transid = (u64)-1;
1123 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1130 memcpy(&sh, args.buf, sizeof(sh));
1131 min_subvolid = sh.offset;
1134 * Verify that the root item is really there and we haven't hit
1137 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1138 sk->min_objectid = min_subvolid;
1139 sk->max_objectid = min_subvolid;
1140 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1141 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1143 sk->max_offset = (u64)-1;
1144 sk->min_transid = 0;
1145 sk->max_transid = (u64)-1;
1148 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1153 * Stale orphan, try the next one
1155 if (!sk->nr_items) {
1164 #define SUBVOL_ID_BATCH 1024
1167 * Enumerate all dead subvolumes that exist in the filesystem.
1168 * Fill @ids and reallocate to bigger size if needed.
1170 static int enumerate_dead_subvols(int fd, u64 **ids)
1173 struct btrfs_ioctl_search_args args;
1174 struct btrfs_ioctl_search_key *sk = &args.key;
1178 memset(&args, 0, sizeof(args));
1180 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1181 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1182 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1183 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1184 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1186 sk->max_offset = (u64)-1;
1187 sk->min_transid = 0;
1188 sk->max_transid = (u64)-1;
1189 sk->nr_items = 4096;
1193 struct btrfs_ioctl_search_header *sh;
1197 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1205 for (i = 0; i < sk->nr_items; i++) {
1206 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1209 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1213 count += SUBVOL_ID_BATCH;
1214 newids = (u64*)realloc(*ids, count);
1219 (*ids)[idx] = sh->offset;
1224 sk->min_objectid = sh->objectid;
1225 sk->min_type = sh->type;
1226 sk->min_offset = sh->offset;
1228 if (sk->min_offset < (u64)-1)
1232 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1234 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1241 static int cmd_subvol_sync(int argc, char **argv)
1246 DIR *dirstream = NULL;
1249 int sleep_interval = 1;
1253 int c = getopt(argc, argv, "s:");
1260 sleep_interval = atoi(argv[optind]);
1261 if (sleep_interval < 1) {
1262 error("invalid sleep interval %s",
1269 usage(cmd_subvol_sync_usage);
1273 if (check_argc_min(argc - optind, 1))
1274 usage(cmd_subvol_sync_usage);
1276 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1283 id_count = argc - optind;
1285 id_count = enumerate_dead_subvols(fd, &ids);
1287 error("can't enumerate dead subvolumes: %s",
1288 strerror(-id_count));
1292 if (id_count == 0) {
1297 ids = (u64*)malloc(id_count * sizeof(u64));
1299 error("not enough memory");
1304 for (i = 0; i < id_count; i++) {
1308 arg = argv[optind + i];
1310 id = strtoull(arg, NULL, 10);
1312 error("unrecognized subvolume id %s", arg);
1316 if (id < BTRFS_FIRST_FREE_OBJECTID
1317 || id > BTRFS_LAST_FREE_OBJECTID) {
1318 error("subvolume id %s out of range\n", arg);
1326 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1330 close_file_or_dir(fd, dirstream);
1335 static const char subvolume_cmd_group_info[] =
1336 "manage subvolumes: create, delete, list, etc";
1338 const struct cmd_group subvolume_cmd_group = {
1339 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1340 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1341 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1342 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1343 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1345 { "get-default", cmd_subvol_get_default,
1346 cmd_subvol_get_default_usage, NULL, 0 },
1347 { "set-default", cmd_subvol_set_default,
1348 cmd_subvol_set_default_usage, NULL, 0 },
1349 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1351 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1352 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1357 int cmd_subvolume(int argc, char **argv)
1359 return handle_command_group(&subvolume_cmd_group, argc, argv);