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:v");
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",
251 static int cmd_subvol_delete(int argc, char **argv)
256 struct btrfs_ioctl_vol_args args;
257 char *dname, *vname, *cpath;
258 char *dupdname = NULL;
259 char *dupvname = NULL;
261 DIR *dirstream = NULL;
267 static const struct option long_options[] = {
268 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
269 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
273 c = getopt_long(argc, argv, "cC", long_options, NULL);
288 usage(cmd_subvol_delete_usage);
292 if (check_argc_min(argc - optind, 1))
293 usage(cmd_subvol_delete_usage);
296 printf("Transaction commit: %s\n",
297 !commit_mode ? "none (default)" :
298 commit_mode == 1 ? "at the end" : "after each");
306 res = test_issubvolume(path);
308 error("cannot access subvolume %s: %s", path, strerror(-res));
313 error("not a subvolume: %s", path);
318 cpath = realpath(path, NULL);
321 error("cannot find real path for '%s': %s",
322 path, strerror(errno));
325 dupdname = strdup(cpath);
326 dname = dirname(dupdname);
327 dupvname = strdup(cpath);
328 vname = basename(dupvname);
331 fd = btrfs_open_dir(dname, &dirstream, 1);
337 printf("Delete subvolume (%s): '%s/%s'\n",
338 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
339 ? "commit" : "no-commit", dname, vname);
340 memset(&args, 0, sizeof(args));
341 strncpy_null(args.name, vname);
342 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
344 error("cannot delete '%s/%s': %s", dname, vname,
350 if (commit_mode == 1) {
351 res = wait_for_commit(fd);
353 error("unable to wait for commit after '%s': %s",
354 path, strerror(errno));
366 close_file_or_dir(fd, dirstream);
367 /* avoid double free */
373 if (commit_mode == 2 && fd != -1) {
374 res = wait_for_commit(fd);
376 error("unable to do final sync after deletion: %s",
381 close_file_or_dir(fd, dirstream);
388 * - uppercase for filters and sort options
389 * - lowercase for enabling specific items in the output
391 static const char * const cmd_subvol_list_usage[] = {
392 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
393 "[--sort=gen,ogen,rootid,path] <path>",
394 "List subvolumes (and snapshots)",
396 "-p print parent ID",
397 "-a print all the subvolumes in the filesystem and",
398 " distinguish absolute and relative path with respect",
399 " to the given <path>",
400 "-c print the ogeneration of the subvolume",
401 "-g print the generation of the subvolume",
402 "-o print only subvolumes below specified path",
403 "-u print the uuid of subvolumes (and snapshots)",
404 "-q print the parent uuid of the snapshots",
405 "-R print the uuid of the received snapshots",
406 "-t print the result as a table",
407 "-s list snapshots only in the filesystem",
408 "-r list readonly subvolumes (including snapshots)",
409 "-d list deleted subvolumes that are not yet cleaned",
411 " filter the subvolumes by generation",
412 " (+value: >= value; -value: <= value; value: = value)",
414 " filter the subvolumes by ogeneration",
415 " (+value: >= value; -value: <= value; value: = value)",
416 "--sort=gen,ogen,rootid,path",
417 " list the subvolume in order of gen, ogen, rootid or path",
418 " you also can add '+' or '-' in front of each items.",
419 " (+:ascending, -:descending, ascending default)",
423 static int cmd_subvol_list(int argc, char **argv)
425 struct btrfs_list_filter_set *filter_set;
426 struct btrfs_list_comparer_set *comparer_set;
430 int ret = -1, uerr = 0;
432 int is_tab_result = 0;
434 int is_only_in_path = 0;
435 DIR *dirstream = NULL;
437 filter_set = btrfs_list_alloc_filter_set();
438 comparer_set = btrfs_list_alloc_comparer_set();
442 static const struct option long_options[] = {
443 {"sort", required_argument, NULL, 'S'},
447 c = getopt_long(argc, argv,
448 "acdgopqsurRG:C:t", long_options, NULL);
454 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
460 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
463 btrfs_list_setup_filter(&filter_set,
464 BTRFS_LIST_FILTER_DELETED,
468 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
477 btrfs_list_setup_filter(&filter_set,
478 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
480 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
481 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
484 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
487 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
490 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
493 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
496 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
497 ret = btrfs_list_parse_filter_string(optarg,
499 BTRFS_LIST_FILTER_GEN);
507 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
508 ret = btrfs_list_parse_filter_string(optarg,
510 BTRFS_LIST_FILTER_CGEN);
517 ret = btrfs_list_parse_sort_string(optarg,
532 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
535 if (check_argc_exact(argc - optind, 1)) {
540 subvol = argv[optind];
541 fd = btrfs_open_dir(subvol, &dirstream, 1);
544 error("can't access '%s'", subvol);
548 ret = btrfs_list_get_path_rootid(fd, &top_id);
550 error("can't get rootid for '%s'", subvol);
555 btrfs_list_setup_filter(&filter_set,
556 BTRFS_LIST_FILTER_FULL_PATH,
558 else if (is_only_in_path)
559 btrfs_list_setup_filter(&filter_set,
560 BTRFS_LIST_FILTER_TOPID_EQUAL,
563 /* by default we shall print the following columns*/
564 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
565 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
566 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
567 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
570 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
571 BTRFS_LIST_LAYOUT_TABLE,
572 !is_list_all && !is_only_in_path, NULL);
574 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
575 BTRFS_LIST_LAYOUT_DEFAULT,
576 !is_list_all && !is_only_in_path, NULL);
579 close_file_or_dir(fd, dirstream);
581 btrfs_list_free_filter_set(filter_set);
583 btrfs_list_free_comparer_set(comparer_set);
585 usage(cmd_subvol_list_usage);
589 static const char * const cmd_subvol_snapshot_usage[] = {
590 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
591 "Create a snapshot of the subvolume",
592 "Create a writable/readonly snapshot of the subvolume <source> with",
593 "the name <name> in the <dest> directory. If only <dest> is given,",
594 "the subvolume will be named the basename of <source>.",
596 "-r create a readonly snapshot",
597 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
598 " option can be given multiple times.",
602 static int cmd_subvol_snapshot(int argc, char **argv)
606 int fd = -1, fddst = -1;
607 int len, readonly = 0;
608 char *dupname = NULL;
612 struct btrfs_ioctl_vol_args_v2 args;
613 struct btrfs_qgroup_inherit *inherit = NULL;
614 DIR *dirstream1 = NULL, *dirstream2 = NULL;
616 memset(&args, 0, sizeof(args));
618 int c = getopt(argc, argv, "c:i:r");
624 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
631 res = qgroup_inherit_add_group(&inherit, optarg);
641 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
648 usage(cmd_subvol_snapshot_usage);
652 if (check_argc_exact(argc - optind, 2))
653 usage(cmd_subvol_snapshot_usage);
655 subvol = argv[optind];
656 dst = argv[optind + 1];
658 retval = 1; /* failure */
659 res = test_issubvolume(subvol);
661 error("cannot access subvolume %s: %s", subvol, strerror(-res));
665 error("not a subvolume: %s", subvol);
669 res = test_isdir(dst);
670 if (res < 0 && res != -ENOENT) {
671 error("cannot access %s: %s", dst, strerror(-res));
675 error("'%s' exists and it is not a directory", dst);
680 dupname = strdup(subvol);
681 newname = basename(dupname);
684 dupname = strdup(dst);
685 newname = basename(dupname);
686 dupdir = strdup(dst);
687 dstdir = dirname(dupdir);
690 if (!test_issubvolname(newname)) {
691 error("invalid snapshot name '%s'", newname);
695 len = strlen(newname);
696 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
697 error("snapshot name too long '%s'", newname);
701 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
705 fd = btrfs_open_dir(subvol, &dirstream2, 1);
710 args.flags |= BTRFS_SUBVOL_RDONLY;
711 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
712 subvol, dstdir, newname);
714 printf("Create a snapshot of '%s' in '%s/%s'\n",
715 subvol, dstdir, newname);
720 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
721 args.size = qgroup_inherit_size(inherit);
722 args.qgroup_inherit = inherit;
724 strncpy_null(args.name, newname);
726 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
729 error("cannot snapshot '%s': %s", subvol, strerror(errno));
733 retval = 0; /* success */
736 close_file_or_dir(fddst, dirstream1);
737 close_file_or_dir(fd, dirstream2);
745 static const char * const cmd_subvol_get_default_usage[] = {
746 "btrfs subvolume get-default <path>",
747 "Get the default subvolume of a filesystem",
751 static int cmd_subvol_get_default(int argc, char **argv)
756 struct btrfs_list_filter_set *filter_set;
758 DIR *dirstream = NULL;
760 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
762 if (check_argc_exact(argc - optind, 1))
763 usage(cmd_subvol_get_default_usage);
766 fd = btrfs_open_dir(subvol, &dirstream, 1);
770 ret = btrfs_list_get_default_subvolume(fd, &default_id);
772 error("failed to look up default subvolume: %s",
778 if (default_id == 0) {
779 error("'default' dir item not found");
783 /* no need to resolve roots if FS_TREE is default */
784 if (default_id == BTRFS_FS_TREE_OBJECTID) {
785 printf("ID 5 (FS_TREE)\n");
790 filter_set = btrfs_list_alloc_filter_set();
791 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
794 /* by default we shall print the following columns*/
795 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
796 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
797 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
798 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
800 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
801 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
804 btrfs_list_free_filter_set(filter_set);
806 close_file_or_dir(fd, dirstream);
810 static const char * const cmd_subvol_set_default_usage[] = {
811 "btrfs subvolume set-default <subvolid> <path>",
812 "Set the default subvolume of a filesystem",
816 static int cmd_subvol_set_default(int argc, char **argv)
822 DIR *dirstream = NULL;
824 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
826 if (check_argc_exact(argc - optind, 2))
827 usage(cmd_subvol_set_default_usage);
829 subvolid = argv[optind];
830 path = argv[optind + 1];
832 objectid = arg_strtou64(subvolid);
834 fd = btrfs_open_dir(path, &dirstream, 1);
838 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
840 close_file_or_dir(fd, dirstream);
842 error("unable to set a new default subvolume: %s",
849 static const char * const cmd_subvol_find_new_usage[] = {
850 "btrfs subvolume find-new <path> <lastgen>",
851 "List the recently modified files in a filesystem",
855 static int cmd_subvol_find_new(int argc, char **argv)
861 DIR *dirstream = NULL;
863 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
865 if (check_argc_exact(argc - optind, 2))
866 usage(cmd_subvol_find_new_usage);
868 subvol = argv[optind];
869 last_gen = arg_strtou64(argv[optind + 1]);
871 ret = test_issubvolume(subvol);
873 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
877 error("not a subvolume: %s", subvol);
881 fd = btrfs_open_dir(subvol, &dirstream, 1);
885 ret = ioctl(fd, BTRFS_IOC_SYNC);
887 error("sync ioctl failed on '%s': %s",
888 subvol, strerror(errno));
889 close_file_or_dir(fd, dirstream);
893 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
894 close_file_or_dir(fd, dirstream);
898 static const char * const cmd_subvol_show_usage[] = {
899 "btrfs subvolume show <subvol-path>",
900 "Show more information of the subvolume",
904 static int cmd_subvol_show(int argc, char **argv)
906 struct root_info get_ri;
907 struct btrfs_list_filter_set *filter_set = NULL;
909 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
910 char *fullpath = NULL;
911 char raw_prefix[] = "\t\t\t\t";
914 DIR *dirstream1 = NULL;
916 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
918 if (check_argc_exact(argc - optind, 1))
919 usage(cmd_subvol_show_usage);
921 memset(&get_ri, 0, sizeof(get_ri));
922 fullpath = realpath(argv[optind], NULL);
924 error("cannot find real path for '%s': %s",
925 argv[optind], strerror(errno));
929 ret = get_subvol_info(fullpath, &get_ri);
932 * Since the top level btrfs was given don't
935 printf("%s is toplevel subvolume\n", fullpath);
941 error("Failed to get subvol info %s: %s\n",
942 fullpath, strerror(-ret));
944 error("Failed to get subvol info %s: %d\n",
951 printf("%s\n", fullpath);
952 printf("\tName: \t\t\t%s\n", get_ri.name);
954 if (uuid_is_null(get_ri.uuid))
955 strcpy(uuidparse, "-");
957 uuid_unparse(get_ri.uuid, uuidparse);
958 printf("\tUUID: \t\t\t%s\n", uuidparse);
960 if (uuid_is_null(get_ri.puuid))
961 strcpy(uuidparse, "-");
963 uuid_unparse(get_ri.puuid, uuidparse);
964 printf("\tParent UUID: \t\t%s\n", uuidparse);
966 if (uuid_is_null(get_ri.ruuid))
967 strcpy(uuidparse, "-");
969 uuid_unparse(get_ri.ruuid, uuidparse);
970 printf("\tReceived UUID: \t\t%s\n", uuidparse);
975 localtime_r(&get_ri.otime, &tm);
976 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
979 printf("\tCreation time: \t\t%s\n", tstr);
981 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
982 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
983 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
984 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
985 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
987 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
988 printf("\tFlags: \t\t\treadonly\n");
990 printf("\tFlags: \t\t\t-\n");
992 /* print the snapshots of the given subvol if any*/
993 printf("\tSnapshot(s):\n");
994 filter_set = btrfs_list_alloc_filter_set();
995 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
996 (u64)(unsigned long)get_ri.uuid);
997 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
999 fd = open_file_or_dir(fullpath, &dirstream1);
1001 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
1004 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1011 free(get_ri.full_path);
1012 btrfs_list_free_filter_set(filter_set);
1014 close_file_or_dir(fd, dirstream1);
1019 static const char * const cmd_subvol_sync_usage[] = {
1020 "btrfs subvolume sync <path> [<subvol-id>...]",
1021 "Wait until given subvolume(s) are completely removed from the filesystem.",
1022 "Wait until given subvolume(s) are completely removed from the filesystem",
1024 "If no subvolume id is given, wait until all current deletion requests",
1025 "are completed, but do not wait for subvolumes deleted meanwhile.",
1026 "The status of subvolume ids is checked periodically.",
1028 "-s <N> sleep N seconds between checks (default: 1)",
1034 * If we're looking for any dead subvolume, take a shortcut and look
1035 * for any ORPHAN_ITEMs in the tree root
1037 static int fs_has_dead_subvolumes(int fd)
1040 struct btrfs_ioctl_search_args args;
1041 struct btrfs_ioctl_search_key *sk = &args.key;
1042 struct btrfs_ioctl_search_header sh;
1043 u64 min_subvolid = 0;
1046 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1047 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1048 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1049 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1050 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1051 sk->min_offset = min_subvolid;
1052 sk->max_offset = (u64)-1;
1053 sk->min_transid = 0;
1054 sk->max_transid = (u64)-1;
1057 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1064 memcpy(&sh, args.buf, sizeof(sh));
1065 min_subvolid = sh.offset;
1068 * Verify that the root item is really there and we haven't hit
1071 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1072 sk->min_objectid = min_subvolid;
1073 sk->max_objectid = min_subvolid;
1074 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1075 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1077 sk->max_offset = (u64)-1;
1078 sk->min_transid = 0;
1079 sk->max_transid = (u64)-1;
1082 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1087 * Stale orphan, try the next one
1089 if (!sk->nr_items) {
1098 #define SUBVOL_ID_BATCH 1024
1101 * Enumerate all dead subvolumes that exist in the filesystem.
1102 * Fill @ids and reallocate to bigger size if needed.
1104 static int enumerate_dead_subvols(int fd, u64 **ids)
1107 struct btrfs_ioctl_search_args args;
1108 struct btrfs_ioctl_search_key *sk = &args.key;
1112 memset(&args, 0, sizeof(args));
1114 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1115 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1116 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1117 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1118 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1120 sk->max_offset = (u64)-1;
1121 sk->min_transid = 0;
1122 sk->max_transid = (u64)-1;
1123 sk->nr_items = 4096;
1127 struct btrfs_ioctl_search_header *sh;
1131 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1139 for (i = 0; i < sk->nr_items; i++) {
1140 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1143 if (btrfs_search_header_type(sh)
1144 == BTRFS_ORPHAN_ITEM_KEY) {
1148 count += SUBVOL_ID_BATCH;
1149 newids = (u64*)realloc(*ids,
1150 count * sizeof(u64));
1155 (*ids)[idx] = btrfs_search_header_offset(sh);
1158 off += btrfs_search_header_len(sh);
1160 sk->min_objectid = btrfs_search_header_objectid(sh);
1161 sk->min_type = btrfs_search_header_type(sh);
1162 sk->min_offset = btrfs_search_header_offset(sh);
1164 if (sk->min_offset < (u64)-1)
1168 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1170 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1177 static int cmd_subvol_sync(int argc, char **argv)
1182 DIR *dirstream = NULL;
1185 int sleep_interval = 1;
1188 int c = getopt(argc, argv, "s:");
1195 sleep_interval = atoi(optarg);
1196 if (sleep_interval < 1) {
1197 error("invalid sleep interval %s", optarg);
1203 usage(cmd_subvol_sync_usage);
1207 if (check_argc_min(argc - optind, 1))
1208 usage(cmd_subvol_sync_usage);
1210 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1217 id_count = argc - optind;
1219 id_count = enumerate_dead_subvols(fd, &ids);
1221 error("can't enumerate dead subvolumes: %s",
1222 strerror(-id_count));
1226 if (id_count == 0) {
1231 ids = (u64*)malloc(id_count * sizeof(u64));
1233 error("not enough memory");
1238 for (i = 0; i < id_count; i++) {
1242 arg = argv[optind + i];
1244 id = strtoull(arg, NULL, 10);
1246 error("unrecognized subvolume id %s", arg);
1250 if (id < BTRFS_FIRST_FREE_OBJECTID
1251 || id > BTRFS_LAST_FREE_OBJECTID) {
1252 error("subvolume id %s out of range\n", arg);
1260 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1264 close_file_or_dir(fd, dirstream);
1269 static const char subvolume_cmd_group_info[] =
1270 "manage subvolumes: create, delete, list, etc";
1272 const struct cmd_group subvolume_cmd_group = {
1273 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1274 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1275 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1276 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1277 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1279 { "get-default", cmd_subvol_get_default,
1280 cmd_subvol_get_default_usage, NULL, 0 },
1281 { "set-default", cmd_subvol_set_default,
1282 cmd_subvol_set_default_usage, NULL, 0 },
1283 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1285 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1286 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1291 int cmd_subvolume(int argc, char **argv)
1293 return handle_command_group(&subvolume_cmd_group, argc, argv);