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;
942 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
944 if (check_argc_exact(argc - optind, 1))
945 usage(cmd_subvol_show_usage);
947 fullpath = realpath(argv[optind], NULL);
949 error("cannot find real path for '%s': %s",
950 argv[optind], strerror(errno));
954 ret = test_issubvolume(fullpath);
956 error("cannot access subvolume %s: %s", fullpath,
961 error("not a subvolume: %s", fullpath);
966 ret = find_mount_root(fullpath, &mnt);
968 error("find_mount_root failed on '%s': %s",
969 fullpath, strerror(-ret));
973 error("%s doesn't belong to btrfs mount point", fullpath);
977 svpath = get_subvol_name(mnt, fullpath);
979 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
983 ret = btrfs_list_get_path_rootid(fd, &sv_id);
985 error("can't get rootid for '%s'", fullpath);
989 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
993 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
994 printf("%s is toplevel subvolume\n", fullpath);
998 memset(&get_ri, 0, sizeof(get_ri));
999 get_ri.root_id = sv_id;
1001 ret = btrfs_get_subvol(mntfd, &get_ri);
1003 error("can't find '%s'", svpath);
1007 /* print the info */
1008 printf("%s\n", fullpath);
1009 printf("\tName: \t\t\t%s\n", get_ri.name);
1011 if (uuid_is_null(get_ri.uuid))
1012 strcpy(uuidparse, "-");
1014 uuid_unparse(get_ri.uuid, uuidparse);
1015 printf("\tUUID: \t\t\t%s\n", uuidparse);
1017 if (uuid_is_null(get_ri.puuid))
1018 strcpy(uuidparse, "-");
1020 uuid_unparse(get_ri.puuid, uuidparse);
1021 printf("\tParent UUID: \t\t%s\n", uuidparse);
1023 if (uuid_is_null(get_ri.ruuid))
1024 strcpy(uuidparse, "-");
1026 uuid_unparse(get_ri.ruuid, uuidparse);
1027 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1032 localtime_r(&get_ri.otime, &tm);
1033 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1036 printf("\tCreation time: \t\t%s\n", tstr);
1038 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1039 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1040 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1041 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1042 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1044 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1045 printf("\tFlags: \t\t\treadonly\n");
1047 printf("\tFlags: \t\t\t-\n");
1049 /* print the snapshots of the given subvol if any*/
1050 printf("\tSnapshot(s):\n");
1051 filter_set = btrfs_list_alloc_filter_set();
1052 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1053 (u64)(unsigned long)get_ri.uuid);
1054 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1055 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1061 free(get_ri.full_path);
1062 btrfs_list_free_filter_set(filter_set);
1065 close_file_or_dir(fd, dirstream1);
1066 close_file_or_dir(mntfd, dirstream2);
1072 static const char * const cmd_subvol_sync_usage[] = {
1073 "btrfs subvolume sync <path> [<subvol-id>...]",
1074 "Wait until given subvolume(s) are completely removed from the filesystem.",
1075 "Wait until given subvolume(s) are completely removed from the filesystem",
1077 "If no subvolume id is given, wait until all current deletion requests",
1078 "are completed, but do not wait for subvolumes deleted meanwhile.",
1079 "The status of subvolume ids is checked periodically.",
1081 "-s <N> sleep N seconds between checks (default: 1)",
1087 * If we're looking for any dead subvolume, take a shortcut and look
1088 * for any ORPHAN_ITEMs in the tree root
1090 static int fs_has_dead_subvolumes(int fd)
1093 struct btrfs_ioctl_search_args args;
1094 struct btrfs_ioctl_search_key *sk = &args.key;
1095 struct btrfs_ioctl_search_header sh;
1096 u64 min_subvolid = 0;
1099 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1100 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1101 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1102 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1103 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1104 sk->min_offset = min_subvolid;
1105 sk->max_offset = (u64)-1;
1106 sk->min_transid = 0;
1107 sk->max_transid = (u64)-1;
1110 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1117 memcpy(&sh, args.buf, sizeof(sh));
1118 min_subvolid = sh.offset;
1121 * Verify that the root item is really there and we haven't hit
1124 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1125 sk->min_objectid = min_subvolid;
1126 sk->max_objectid = min_subvolid;
1127 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1128 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1130 sk->max_offset = (u64)-1;
1131 sk->min_transid = 0;
1132 sk->max_transid = (u64)-1;
1135 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1140 * Stale orphan, try the next one
1142 if (!sk->nr_items) {
1151 #define SUBVOL_ID_BATCH 1024
1154 * Enumerate all dead subvolumes that exist in the filesystem.
1155 * Fill @ids and reallocate to bigger size if needed.
1157 static int enumerate_dead_subvols(int fd, u64 **ids)
1160 struct btrfs_ioctl_search_args args;
1161 struct btrfs_ioctl_search_key *sk = &args.key;
1165 memset(&args, 0, sizeof(args));
1167 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1168 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1169 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1170 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1171 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1173 sk->max_offset = (u64)-1;
1174 sk->min_transid = 0;
1175 sk->max_transid = (u64)-1;
1176 sk->nr_items = 4096;
1180 struct btrfs_ioctl_search_header *sh;
1184 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1192 for (i = 0; i < sk->nr_items; i++) {
1193 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1196 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1200 count += SUBVOL_ID_BATCH;
1201 newids = (u64*)realloc(*ids, count);
1206 (*ids)[idx] = sh->offset;
1211 sk->min_objectid = sh->objectid;
1212 sk->min_type = sh->type;
1213 sk->min_offset = sh->offset;
1215 if (sk->min_offset < (u64)-1)
1219 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1221 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1228 static int cmd_subvol_sync(int argc, char **argv)
1233 DIR *dirstream = NULL;
1236 int sleep_interval = 1;
1240 int c = getopt(argc, argv, "s:");
1247 sleep_interval = atoi(argv[optind]);
1248 if (sleep_interval < 1) {
1249 error("invalid sleep interval %s",
1256 usage(cmd_subvol_sync_usage);
1260 if (check_argc_min(argc - optind, 1))
1261 usage(cmd_subvol_sync_usage);
1263 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1270 id_count = argc - optind;
1272 id_count = enumerate_dead_subvols(fd, &ids);
1274 error("can't enumerate dead subvolumes: %s",
1275 strerror(-id_count));
1279 if (id_count == 0) {
1284 ids = (u64*)malloc(id_count * sizeof(u64));
1286 error("not enough memory");
1291 for (i = 0; i < id_count; i++) {
1295 arg = argv[optind + i];
1297 id = strtoull(arg, NULL, 10);
1299 error("unrecognized subvolume id %s", arg);
1303 if (id < BTRFS_FIRST_FREE_OBJECTID
1304 || id > BTRFS_LAST_FREE_OBJECTID) {
1305 error("subvolume id %s out of range\n", arg);
1313 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1317 close_file_or_dir(fd, dirstream);
1322 static const char subvolume_cmd_group_info[] =
1323 "manage subvolumes: create, delete, list, etc";
1325 const struct cmd_group subvolume_cmd_group = {
1326 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1327 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1328 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1329 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1330 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1332 { "get-default", cmd_subvol_get_default,
1333 cmd_subvol_get_default_usage, NULL, 0 },
1334 { "set-default", cmd_subvol_set_default,
1335 cmd_subvol_set_default_usage, NULL, 0 },
1336 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1338 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1339 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1344 int cmd_subvolume(int argc, char **argv)
1346 return handle_command_group(&subvolume_cmd_group, argc, argv);