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 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
793 if (check_argc_exact(argc - optind, 2))
794 usage(cmd_subvol_get_default_usage);
797 fd = btrfs_open_dir(subvol, &dirstream, 1);
801 ret = btrfs_list_get_default_subvolume(fd, &default_id);
803 error("failed to look up default subvolume: %s",
809 if (default_id == 0) {
810 error("'default' dir item not found");
814 /* no need to resolve roots if FS_TREE is default */
815 if (default_id == BTRFS_FS_TREE_OBJECTID) {
816 printf("ID 5 (FS_TREE)\n");
821 filter_set = btrfs_list_alloc_filter_set();
822 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
825 /* by default we shall print the following columns*/
826 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
827 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
828 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
829 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
831 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
832 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
835 btrfs_list_free_filter_set(filter_set);
837 close_file_or_dir(fd, dirstream);
841 static const char * const cmd_subvol_set_default_usage[] = {
842 "btrfs subvolume set-default <subvolid> <path>",
843 "Set the default subvolume of a filesystem",
847 static int cmd_subvol_set_default(int argc, char **argv)
853 DIR *dirstream = NULL;
855 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
857 if (check_argc_exact(argc - optind, 2))
858 usage(cmd_subvol_set_default_usage);
860 subvolid = argv[optind];
861 path = argv[optind + 1];
863 objectid = arg_strtou64(subvolid);
865 fd = btrfs_open_dir(path, &dirstream, 1);
869 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
871 close_file_or_dir(fd, dirstream);
873 error("unable to set a new default subvolume: %s",
880 static const char * const cmd_subvol_find_new_usage[] = {
881 "btrfs subvolume find-new <path> <lastgen>",
882 "List the recently modified files in a filesystem",
886 static int cmd_subvol_find_new(int argc, char **argv)
892 DIR *dirstream = NULL;
894 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
896 if (check_argc_exact(argc - optind, 2))
897 usage(cmd_subvol_find_new_usage);
899 subvol = argv[optind];
900 last_gen = arg_strtou64(argv[optind + 1]);
902 ret = test_issubvolume(subvol);
904 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
908 error("not a subvolume: %s", subvol);
912 fd = btrfs_open_dir(subvol, &dirstream, 1);
916 ret = ioctl(fd, BTRFS_IOC_SYNC);
918 error("sync ioctl failed on '%s': %s",
919 subvol, strerror(errno));
920 close_file_or_dir(fd, dirstream);
924 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
925 close_file_or_dir(fd, dirstream);
929 static const char * const cmd_subvol_show_usage[] = {
930 "btrfs subvolume show <subvol-path>",
931 "Show more information of the subvolume",
935 static int cmd_subvol_show(int argc, char **argv)
937 struct root_info get_ri;
938 struct btrfs_list_filter_set *filter_set;
940 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
941 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
942 char raw_prefix[] = "\t\t\t\t";
944 int fd = -1, mntfd = -1;
946 DIR *dirstream1 = NULL, *dirstream2 = NULL;
948 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
950 if (check_argc_exact(argc - optind, 1))
951 usage(cmd_subvol_show_usage);
953 fullpath = realpath(argv[optind], NULL);
955 error("cannot find real path for '%s': %s",
956 argv[optind], strerror(errno));
960 ret = test_issubvolume(fullpath);
962 error("cannot access subvolume %s: %s", fullpath,
967 error("not a subvolume: %s", fullpath);
972 ret = find_mount_root(fullpath, &mnt);
974 error("find_mount_root failed on '%s': %s",
975 fullpath, strerror(-ret));
979 error("%s doesn't belong to btrfs mount point", fullpath);
983 svpath = get_subvol_name(mnt, fullpath);
985 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
989 ret = btrfs_list_get_path_rootid(fd, &sv_id);
991 error("can't get rootid for '%s'", fullpath);
995 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
999 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1000 printf("%s is toplevel subvolume\n", fullpath);
1004 memset(&get_ri, 0, sizeof(get_ri));
1005 get_ri.root_id = sv_id;
1007 ret = btrfs_get_subvol(mntfd, &get_ri);
1009 error("can't find '%s'", svpath);
1013 /* print the info */
1014 printf("%s\n", fullpath);
1015 printf("\tName: \t\t\t%s\n", get_ri.name);
1017 if (uuid_is_null(get_ri.uuid))
1018 strcpy(uuidparse, "-");
1020 uuid_unparse(get_ri.uuid, uuidparse);
1021 printf("\tUUID: \t\t\t%s\n", uuidparse);
1023 if (uuid_is_null(get_ri.puuid))
1024 strcpy(uuidparse, "-");
1026 uuid_unparse(get_ri.puuid, uuidparse);
1027 printf("\tParent UUID: \t\t%s\n", uuidparse);
1029 if (uuid_is_null(get_ri.ruuid))
1030 strcpy(uuidparse, "-");
1032 uuid_unparse(get_ri.ruuid, uuidparse);
1033 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1038 localtime_r(&get_ri.otime, &tm);
1039 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1042 printf("\tCreation time: \t\t%s\n", tstr);
1044 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1045 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1046 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1047 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1048 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1050 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1051 printf("\tFlags: \t\t\treadonly\n");
1053 printf("\tFlags: \t\t\t-\n");
1055 /* print the snapshots of the given subvol if any*/
1056 printf("\tSnapshot(s):\n");
1057 filter_set = btrfs_list_alloc_filter_set();
1058 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1059 (u64)(unsigned long)get_ri.uuid);
1060 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1061 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1067 free(get_ri.full_path);
1068 btrfs_list_free_filter_set(filter_set);
1071 close_file_or_dir(fd, dirstream1);
1072 close_file_or_dir(mntfd, dirstream2);
1078 static const char * const cmd_subvol_sync_usage[] = {
1079 "btrfs subvolume sync <path> [<subvol-id>...]",
1080 "Wait until given subvolume(s) are completely removed from the filesystem.",
1081 "Wait until given subvolume(s) are completely removed from the filesystem",
1083 "If no subvolume id is given, wait until all current deletion requests",
1084 "are completed, but do not wait for subvolumes deleted meanwhile.",
1085 "The status of subvolume ids is checked periodically.",
1087 "-s <N> sleep N seconds between checks (default: 1)",
1093 * If we're looking for any dead subvolume, take a shortcut and look
1094 * for any ORPHAN_ITEMs in the tree root
1096 static int fs_has_dead_subvolumes(int fd)
1099 struct btrfs_ioctl_search_args args;
1100 struct btrfs_ioctl_search_key *sk = &args.key;
1101 struct btrfs_ioctl_search_header sh;
1102 u64 min_subvolid = 0;
1105 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1106 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1107 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1108 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1109 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1110 sk->min_offset = min_subvolid;
1111 sk->max_offset = (u64)-1;
1112 sk->min_transid = 0;
1113 sk->max_transid = (u64)-1;
1116 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1123 memcpy(&sh, args.buf, sizeof(sh));
1124 min_subvolid = sh.offset;
1127 * Verify that the root item is really there and we haven't hit
1130 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1131 sk->min_objectid = min_subvolid;
1132 sk->max_objectid = min_subvolid;
1133 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1134 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1136 sk->max_offset = (u64)-1;
1137 sk->min_transid = 0;
1138 sk->max_transid = (u64)-1;
1141 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1146 * Stale orphan, try the next one
1148 if (!sk->nr_items) {
1157 #define SUBVOL_ID_BATCH 1024
1160 * Enumerate all dead subvolumes that exist in the filesystem.
1161 * Fill @ids and reallocate to bigger size if needed.
1163 static int enumerate_dead_subvols(int fd, u64 **ids)
1166 struct btrfs_ioctl_search_args args;
1167 struct btrfs_ioctl_search_key *sk = &args.key;
1171 memset(&args, 0, sizeof(args));
1173 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1174 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1175 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1176 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1177 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1179 sk->max_offset = (u64)-1;
1180 sk->min_transid = 0;
1181 sk->max_transid = (u64)-1;
1182 sk->nr_items = 4096;
1186 struct btrfs_ioctl_search_header *sh;
1190 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1198 for (i = 0; i < sk->nr_items; i++) {
1199 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1202 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1206 count += SUBVOL_ID_BATCH;
1207 newids = (u64*)realloc(*ids, count);
1212 (*ids)[idx] = sh->offset;
1217 sk->min_objectid = sh->objectid;
1218 sk->min_type = sh->type;
1219 sk->min_offset = sh->offset;
1221 if (sk->min_offset < (u64)-1)
1225 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1227 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1234 static int cmd_subvol_sync(int argc, char **argv)
1239 DIR *dirstream = NULL;
1242 int sleep_interval = 1;
1246 int c = getopt(argc, argv, "s:");
1253 sleep_interval = atoi(argv[optind]);
1254 if (sleep_interval < 1) {
1255 error("invalid sleep interval %s",
1262 usage(cmd_subvol_sync_usage);
1266 if (check_argc_min(argc - optind, 1))
1267 usage(cmd_subvol_sync_usage);
1269 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1276 id_count = argc - optind;
1278 id_count = enumerate_dead_subvols(fd, &ids);
1280 error("can't enumerate dead subvolumes: %s",
1281 strerror(-id_count));
1285 if (id_count == 0) {
1290 ids = (u64*)malloc(id_count * sizeof(u64));
1292 error("not enough memory");
1297 for (i = 0; i < id_count; i++) {
1301 arg = argv[optind + i];
1303 id = strtoull(arg, NULL, 10);
1305 error("unrecognized subvolume id %s", arg);
1309 if (id < BTRFS_FIRST_FREE_OBJECTID
1310 || id > BTRFS_LAST_FREE_OBJECTID) {
1311 error("subvolume id %s out of range\n", arg);
1319 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1323 close_file_or_dir(fd, dirstream);
1328 static const char subvolume_cmd_group_info[] =
1329 "manage subvolumes: create, delete, list, etc";
1331 const struct cmd_group subvolume_cmd_group = {
1332 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1333 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1334 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1335 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1336 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1338 { "get-default", cmd_subvol_get_default,
1339 cmd_subvol_get_default_usage, NULL, 0 },
1340 { "set-default", cmd_subvol_set_default,
1341 cmd_subvol_set_default_usage, NULL, 0 },
1342 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1344 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1345 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1350 int cmd_subvolume(int argc, char **argv)
1352 return handle_command_group(&subvolume_cmd_group, argc, argv);