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;
131 int c = getopt(argc, argv, "c:i:");
137 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
144 res = qgroup_inherit_add_group(&inherit, optarg);
151 usage(cmd_subvol_create_usage);
155 if (check_argc_exact(argc - optind, 1))
156 usage(cmd_subvol_create_usage);
160 retval = 1; /* failure */
161 res = test_isdir(dst);
162 if (res < 0 && res != -ENOENT) {
163 error("cannot access %s: %s", dst, strerror(-res));
167 error("target path already exists: %s", dst);
171 dupname = strdup(dst);
172 newname = basename(dupname);
173 dupdir = strdup(dst);
174 dstdir = dirname(dupdir);
176 if (!test_issubvolname(newname)) {
177 error("invalid subvolume name: %s", newname);
181 len = strlen(newname);
182 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
183 error("subvolume name too long: %s", newname);
187 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
191 printf("Create subvolume '%s/%s'\n", dstdir, newname);
193 struct btrfs_ioctl_vol_args_v2 args;
195 memset(&args, 0, sizeof(args));
196 strncpy_null(args.name, newname);
197 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
198 args.size = qgroup_inherit_size(inherit);
199 args.qgroup_inherit = inherit;
201 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
203 struct btrfs_ioctl_vol_args args;
205 memset(&args, 0, sizeof(args));
206 strncpy_null(args.name, newname);
208 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
212 error("cannot create subvolume: %s", strerror(errno));
216 retval = 0; /* success */
218 close_file_or_dir(fddst, dirstream);
226 static int wait_for_commit(int fd)
230 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
233 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
236 static const char * const cmd_subvol_delete_usage[] = {
237 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
238 "Delete subvolume(s)",
239 "Delete subvolumes from the filesystem. The corresponding directory",
240 "is removed instantly but the data blocks are removed later.",
241 "The deletion does not involve full commit by default due to",
242 "performance reasons (as a consequence, the subvolume may appear again",
243 "after a crash). Use one of the --commit options to wait until the",
244 "operation is safely stored on the media.",
246 "-c|--commit-after wait for transaction commit at the end of the operation",
247 "-C|--commit-each wait for transaction commit after deleting each subvolume",
248 "-v|--verbose verbose output of operations",
252 static int cmd_subvol_delete(int argc, char **argv)
257 struct btrfs_ioctl_vol_args args;
258 char *dname, *vname, *cpath;
259 char *dupdname = NULL;
260 char *dupvname = NULL;
262 DIR *dirstream = NULL;
268 static const struct option long_options[] = {
269 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
270 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
271 {"verbose", no_argument, NULL, 'v'},
275 c = getopt_long(argc, argv, "cCv", long_options, NULL);
290 usage(cmd_subvol_delete_usage);
294 if (check_argc_min(argc - optind, 1))
295 usage(cmd_subvol_delete_usage);
298 printf("Transaction commit: %s\n",
299 !commit_mode ? "none (default)" :
300 commit_mode == 1 ? "at the end" : "after each");
308 res = test_issubvolume(path);
310 error("cannot access subvolume %s: %s", path, strerror(-res));
315 error("not a subvolume: %s", path);
320 cpath = realpath(path, NULL);
323 error("cannot find real path for '%s': %s",
324 path, strerror(errno));
327 dupdname = strdup(cpath);
328 dname = dirname(dupdname);
329 dupvname = strdup(cpath);
330 vname = basename(dupvname);
333 fd = btrfs_open_dir(dname, &dirstream, 1);
339 printf("Delete subvolume (%s): '%s/%s'\n",
340 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
341 ? "commit" : "no-commit", dname, vname);
342 memset(&args, 0, sizeof(args));
343 strncpy_null(args.name, vname);
344 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
346 error("cannot delete '%s/%s': %s", dname, vname,
352 if (commit_mode == 1) {
353 res = wait_for_commit(fd);
355 error("unable to wait for commit after '%s': %s",
356 path, strerror(errno));
368 close_file_or_dir(fd, dirstream);
369 /* avoid double free */
375 if (commit_mode == 2 && fd != -1) {
376 res = wait_for_commit(fd);
378 error("unable to do final sync after deletion: %s",
383 close_file_or_dir(fd, dirstream);
390 * - uppercase for filters and sort options
391 * - lowercase for enabling specific items in the output
393 static const char * const cmd_subvol_list_usage[] = {
394 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
395 "[--sort=gen,ogen,rootid,path] <path>",
396 "List subvolumes (and snapshots)",
398 "-p print parent ID",
399 "-a print all the subvolumes in the filesystem and",
400 " distinguish absolute and relative path with respect",
401 " to the given <path>",
402 "-c print the ogeneration of the subvolume",
403 "-g print the generation of the subvolume",
404 "-o print only subvolumes below specified path",
405 "-u print the uuid of subvolumes (and snapshots)",
406 "-q print the parent uuid of the snapshots",
407 "-R print the uuid of the received snapshots",
408 "-t print the result as a table",
409 "-s list snapshots only in the filesystem",
410 "-r list readonly subvolumes (including snapshots)",
411 "-d list deleted subvolumes that are not yet cleaned",
413 " filter the subvolumes by generation",
414 " (+value: >= value; -value: <= value; value: = value)",
416 " filter the subvolumes by ogeneration",
417 " (+value: >= value; -value: <= value; value: = value)",
418 "--sort=gen,ogen,rootid,path",
419 " list the subvolume in order of gen, ogen, rootid or path",
420 " you also can add '+' or '-' in front of each items.",
421 " (+:ascending, -:descending, ascending default)",
425 static int cmd_subvol_list(int argc, char **argv)
427 struct btrfs_list_filter_set *filter_set;
428 struct btrfs_list_comparer_set *comparer_set;
432 int ret = -1, uerr = 0;
434 int is_tab_result = 0;
436 int is_only_in_path = 0;
437 DIR *dirstream = NULL;
439 filter_set = btrfs_list_alloc_filter_set();
440 comparer_set = btrfs_list_alloc_comparer_set();
444 static const struct option long_options[] = {
445 {"sort", required_argument, NULL, 'S'},
449 c = getopt_long(argc, argv,
450 "acdgopqsurRG:C:t", long_options, NULL);
456 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
462 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
465 btrfs_list_setup_filter(&filter_set,
466 BTRFS_LIST_FILTER_DELETED,
470 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
479 btrfs_list_setup_filter(&filter_set,
480 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
482 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
483 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
486 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
489 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
492 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
495 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
498 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
499 ret = btrfs_list_parse_filter_string(optarg,
501 BTRFS_LIST_FILTER_GEN);
509 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
510 ret = btrfs_list_parse_filter_string(optarg,
512 BTRFS_LIST_FILTER_CGEN);
519 ret = btrfs_list_parse_sort_string(optarg,
534 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
537 if (check_argc_exact(argc - optind, 1)) {
542 subvol = argv[optind];
543 fd = btrfs_open_dir(subvol, &dirstream, 1);
546 error("can't access '%s'", subvol);
550 ret = btrfs_list_get_path_rootid(fd, &top_id);
552 error("can't get rootid for '%s'", subvol);
557 btrfs_list_setup_filter(&filter_set,
558 BTRFS_LIST_FILTER_FULL_PATH,
560 else if (is_only_in_path)
561 btrfs_list_setup_filter(&filter_set,
562 BTRFS_LIST_FILTER_TOPID_EQUAL,
565 /* by default we shall print the following columns*/
566 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
567 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
568 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
569 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
572 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
573 BTRFS_LIST_LAYOUT_TABLE,
574 !is_list_all && !is_only_in_path, NULL);
576 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
577 BTRFS_LIST_LAYOUT_DEFAULT,
578 !is_list_all && !is_only_in_path, NULL);
581 close_file_or_dir(fd, dirstream);
587 usage(cmd_subvol_list_usage);
591 static const char * const cmd_subvol_snapshot_usage[] = {
592 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
593 "Create a snapshot of the subvolume",
594 "Create a writable/readonly snapshot of the subvolume <source> with",
595 "the name <name> in the <dest> directory. If only <dest> is given,",
596 "the subvolume will be named the basename of <source>.",
598 "-r create a readonly snapshot",
599 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
600 " option can be given multiple times.",
604 static int cmd_subvol_snapshot(int argc, char **argv)
608 int fd = -1, fddst = -1;
609 int len, readonly = 0;
610 char *dupname = NULL;
614 struct btrfs_ioctl_vol_args_v2 args;
615 struct btrfs_qgroup_inherit *inherit = NULL;
616 DIR *dirstream1 = NULL, *dirstream2 = NULL;
618 memset(&args, 0, sizeof(args));
620 int c = getopt(argc, argv, "c:i:r");
626 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
633 res = qgroup_inherit_add_group(&inherit, optarg);
643 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
650 usage(cmd_subvol_snapshot_usage);
654 if (check_argc_exact(argc - optind, 2))
655 usage(cmd_subvol_snapshot_usage);
657 subvol = argv[optind];
658 dst = argv[optind + 1];
660 retval = 1; /* failure */
661 res = test_issubvolume(subvol);
663 error("cannot access subvolume %s: %s", subvol, strerror(-res));
667 error("not a subvolume: %s", subvol);
671 res = test_isdir(dst);
672 if (res < 0 && res != -ENOENT) {
673 error("cannot access %s: %s", dst, strerror(-res));
677 error("'%s' exists and it is not a directory", dst);
682 dupname = strdup(subvol);
683 newname = basename(dupname);
686 dupname = strdup(dst);
687 newname = basename(dupname);
688 dupdir = strdup(dst);
689 dstdir = dirname(dupdir);
692 if (!test_issubvolname(newname)) {
693 error("invalid snapshot name '%s'", newname);
697 len = strlen(newname);
698 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
699 error("snapshot name too long '%s'", newname);
703 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
707 fd = btrfs_open_dir(subvol, &dirstream2, 1);
712 args.flags |= BTRFS_SUBVOL_RDONLY;
713 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
714 subvol, dstdir, newname);
716 printf("Create a snapshot of '%s' in '%s/%s'\n",
717 subvol, dstdir, newname);
722 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
723 args.size = qgroup_inherit_size(inherit);
724 args.qgroup_inherit = inherit;
726 strncpy_null(args.name, newname);
728 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
731 error("cannot snapshot '%s': %s", subvol, strerror(errno));
735 retval = 0; /* success */
738 close_file_or_dir(fddst, dirstream1);
739 close_file_or_dir(fd, dirstream2);
747 static const char * const cmd_subvol_get_default_usage[] = {
748 "btrfs subvolume get-default <path>",
749 "Get the default subvolume of a filesystem",
753 static int cmd_subvol_get_default(int argc, char **argv)
758 struct btrfs_list_filter_set *filter_set;
760 DIR *dirstream = NULL;
762 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
764 if (check_argc_exact(argc - optind, 1))
765 usage(cmd_subvol_get_default_usage);
768 fd = btrfs_open_dir(subvol, &dirstream, 1);
772 ret = btrfs_list_get_default_subvolume(fd, &default_id);
774 error("failed to look up default subvolume: %s",
780 if (default_id == 0) {
781 error("'default' dir item not found");
785 /* no need to resolve roots if FS_TREE is default */
786 if (default_id == BTRFS_FS_TREE_OBJECTID) {
787 printf("ID 5 (FS_TREE)\n");
792 filter_set = btrfs_list_alloc_filter_set();
793 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
796 /* by default we shall print the following columns*/
797 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
798 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
799 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
800 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
802 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
803 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
808 close_file_or_dir(fd, dirstream);
812 static const char * const cmd_subvol_set_default_usage[] = {
813 "btrfs subvolume set-default <subvolid> <path>",
814 "Set the default subvolume of a filesystem",
818 static int cmd_subvol_set_default(int argc, char **argv)
824 DIR *dirstream = NULL;
826 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
828 if (check_argc_exact(argc - optind, 2))
829 usage(cmd_subvol_set_default_usage);
831 subvolid = argv[optind];
832 path = argv[optind + 1];
834 objectid = arg_strtou64(subvolid);
836 fd = btrfs_open_dir(path, &dirstream, 1);
840 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
842 close_file_or_dir(fd, dirstream);
844 error("unable to set a new default subvolume: %s",
851 static const char * const cmd_subvol_find_new_usage[] = {
852 "btrfs subvolume find-new <path> <lastgen>",
853 "List the recently modified files in a filesystem",
857 static int cmd_subvol_find_new(int argc, char **argv)
863 DIR *dirstream = NULL;
865 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
867 if (check_argc_exact(argc - optind, 2))
868 usage(cmd_subvol_find_new_usage);
870 subvol = argv[optind];
871 last_gen = arg_strtou64(argv[optind + 1]);
873 ret = test_issubvolume(subvol);
875 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
879 error("not a subvolume: %s", subvol);
883 fd = btrfs_open_dir(subvol, &dirstream, 1);
887 ret = ioctl(fd, BTRFS_IOC_SYNC);
889 error("sync ioctl failed on '%s': %s",
890 subvol, strerror(errno));
891 close_file_or_dir(fd, dirstream);
895 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
896 close_file_or_dir(fd, dirstream);
900 static const char * const cmd_subvol_show_usage[] = {
901 "btrfs subvolume show <subvol-path>",
902 "Show more information of the subvolume",
906 static int cmd_subvol_show(int argc, char **argv)
908 struct root_info get_ri;
909 struct btrfs_list_filter_set *filter_set = NULL;
911 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
912 char *fullpath = NULL;
913 char raw_prefix[] = "\t\t\t\t";
916 DIR *dirstream1 = NULL;
918 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
920 if (check_argc_exact(argc - optind, 1))
921 usage(cmd_subvol_show_usage);
923 memset(&get_ri, 0, sizeof(get_ri));
924 fullpath = realpath(argv[optind], NULL);
926 error("cannot find real path for '%s': %s",
927 argv[optind], strerror(errno));
931 ret = get_subvol_info(fullpath, &get_ri);
934 * Since the top level btrfs was given don't
937 printf("%s is toplevel subvolume\n", fullpath);
943 error("Failed to get subvol info %s: %s\n",
944 fullpath, strerror(-ret));
946 error("Failed to get subvol info %s: %d\n",
953 printf("%s\n", fullpath);
954 printf("\tName: \t\t\t%s\n", get_ri.name);
956 if (uuid_is_null(get_ri.uuid))
957 strcpy(uuidparse, "-");
959 uuid_unparse(get_ri.uuid, uuidparse);
960 printf("\tUUID: \t\t\t%s\n", uuidparse);
962 if (uuid_is_null(get_ri.puuid))
963 strcpy(uuidparse, "-");
965 uuid_unparse(get_ri.puuid, uuidparse);
966 printf("\tParent UUID: \t\t%s\n", uuidparse);
968 if (uuid_is_null(get_ri.ruuid))
969 strcpy(uuidparse, "-");
971 uuid_unparse(get_ri.ruuid, uuidparse);
972 printf("\tReceived UUID: \t\t%s\n", uuidparse);
977 localtime_r(&get_ri.otime, &tm);
978 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
981 printf("\tCreation time: \t\t%s\n", tstr);
983 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
984 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
985 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
986 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
987 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
989 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
990 printf("\tFlags: \t\t\treadonly\n");
992 printf("\tFlags: \t\t\t-\n");
994 /* print the snapshots of the given subvol if any*/
995 printf("\tSnapshot(s):\n");
996 filter_set = btrfs_list_alloc_filter_set();
997 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
998 (u64)(unsigned long)get_ri.uuid);
999 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1001 fd = open_file_or_dir(fullpath, &dirstream1);
1003 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1006 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1013 free(get_ri.full_path);
1016 close_file_or_dir(fd, dirstream1);
1021 static const char * const cmd_subvol_sync_usage[] = {
1022 "btrfs subvolume sync <path> [<subvol-id>...]",
1023 "Wait until given subvolume(s) are completely removed from the filesystem.",
1024 "Wait until given subvolume(s) are completely removed from the filesystem",
1026 "If no subvolume id is given, wait until all current deletion requests",
1027 "are completed, but do not wait for subvolumes deleted meanwhile.",
1028 "The status of subvolume ids is checked periodically.",
1030 "-s <N> sleep N seconds between checks (default: 1)",
1036 * If we're looking for any dead subvolume, take a shortcut and look
1037 * for any ORPHAN_ITEMs in the tree root
1039 static int fs_has_dead_subvolumes(int fd)
1042 struct btrfs_ioctl_search_args args;
1043 struct btrfs_ioctl_search_key *sk = &args.key;
1044 struct btrfs_ioctl_search_header sh;
1045 u64 min_subvolid = 0;
1048 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1049 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1050 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1051 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1052 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1053 sk->min_offset = min_subvolid;
1054 sk->max_offset = (u64)-1;
1055 sk->min_transid = 0;
1056 sk->max_transid = (u64)-1;
1059 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1066 memcpy(&sh, args.buf, sizeof(sh));
1067 min_subvolid = sh.offset;
1070 * Verify that the root item is really there and we haven't hit
1073 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1074 sk->min_objectid = min_subvolid;
1075 sk->max_objectid = min_subvolid;
1076 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1077 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1079 sk->max_offset = (u64)-1;
1080 sk->min_transid = 0;
1081 sk->max_transid = (u64)-1;
1084 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1089 * Stale orphan, try the next one
1091 if (!sk->nr_items) {
1100 #define SUBVOL_ID_BATCH 1024
1103 * Enumerate all dead subvolumes that exist in the filesystem.
1104 * Fill @ids and reallocate to bigger size if needed.
1106 static int enumerate_dead_subvols(int fd, u64 **ids)
1109 struct btrfs_ioctl_search_args args;
1110 struct btrfs_ioctl_search_key *sk = &args.key;
1114 memset(&args, 0, sizeof(args));
1116 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1117 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1118 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1119 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1120 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1122 sk->max_offset = (u64)-1;
1123 sk->min_transid = 0;
1124 sk->max_transid = (u64)-1;
1125 sk->nr_items = 4096;
1129 struct btrfs_ioctl_search_header *sh;
1133 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1141 for (i = 0; i < sk->nr_items; i++) {
1142 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1145 if (btrfs_search_header_type(sh)
1146 == BTRFS_ORPHAN_ITEM_KEY) {
1150 count += SUBVOL_ID_BATCH;
1151 newids = (u64*)realloc(*ids,
1152 count * sizeof(u64));
1157 (*ids)[idx] = btrfs_search_header_offset(sh);
1160 off += btrfs_search_header_len(sh);
1162 sk->min_objectid = btrfs_search_header_objectid(sh);
1163 sk->min_type = btrfs_search_header_type(sh);
1164 sk->min_offset = btrfs_search_header_offset(sh);
1166 if (sk->min_offset < (u64)-1)
1170 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1172 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1179 static int cmd_subvol_sync(int argc, char **argv)
1184 DIR *dirstream = NULL;
1187 int sleep_interval = 1;
1190 int c = getopt(argc, argv, "s:");
1197 sleep_interval = atoi(optarg);
1198 if (sleep_interval < 1) {
1199 error("invalid sleep interval %s", optarg);
1205 usage(cmd_subvol_sync_usage);
1209 if (check_argc_min(argc - optind, 1))
1210 usage(cmd_subvol_sync_usage);
1212 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1219 id_count = argc - optind;
1221 id_count = enumerate_dead_subvols(fd, &ids);
1223 error("can't enumerate dead subvolumes: %s",
1224 strerror(-id_count));
1228 if (id_count == 0) {
1233 ids = (u64*)malloc(id_count * sizeof(u64));
1235 error("not enough memory");
1240 for (i = 0; i < id_count; i++) {
1244 arg = argv[optind + i];
1246 id = strtoull(arg, NULL, 10);
1248 error("unrecognized subvolume id %s", arg);
1252 if (id < BTRFS_FIRST_FREE_OBJECTID
1253 || id > BTRFS_LAST_FREE_OBJECTID) {
1254 error("subvolume id %s out of range\n", arg);
1262 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1266 close_file_or_dir(fd, dirstream);
1271 static const char subvolume_cmd_group_info[] =
1272 "manage subvolumes: create, delete, list, etc";
1274 const struct cmd_group subvolume_cmd_group = {
1275 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1276 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1277 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1278 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1279 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1281 { "get-default", cmd_subvol_get_default,
1282 cmd_subvol_get_default_usage, NULL, 0 },
1283 { "set-default", cmd_subvol_set_default,
1284 cmd_subvol_set_default_usage, NULL, 0 },
1285 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1287 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1288 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1293 int cmd_subvolume(int argc, char **argv)
1295 return handle_command_group(&subvolume_cmd_group, argc, argv);