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;
435 int is_only_in_path = 0;
436 DIR *dirstream = NULL;
437 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT;
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);
476 layout = BTRFS_LIST_LAYOUT_TABLE;
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,
533 if (check_argc_exact(argc - optind, 1)) {
538 subvol = argv[optind];
539 fd = btrfs_open_dir(subvol, &dirstream, 1);
542 error("can't access '%s'", subvol);
547 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
550 ret = btrfs_list_get_path_rootid(fd, &top_id);
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);
569 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
570 layout, !is_list_all && !is_only_in_path, NULL);
573 close_file_or_dir(fd, dirstream);
579 usage(cmd_subvol_list_usage);
583 static const char * const cmd_subvol_snapshot_usage[] = {
584 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
585 "Create a snapshot of the subvolume",
586 "Create a writable/readonly snapshot of the subvolume <source> with",
587 "the name <name> in the <dest> directory. If only <dest> is given,",
588 "the subvolume will be named the basename of <source>.",
590 "-r create a readonly snapshot",
591 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
592 " option can be given multiple times.",
596 static int cmd_subvol_snapshot(int argc, char **argv)
600 int fd = -1, fddst = -1;
601 int len, readonly = 0;
602 char *dupname = NULL;
606 struct btrfs_ioctl_vol_args_v2 args;
607 struct btrfs_qgroup_inherit *inherit = NULL;
608 DIR *dirstream1 = NULL, *dirstream2 = NULL;
610 memset(&args, 0, sizeof(args));
612 int c = getopt(argc, argv, "c:i:r");
618 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
625 res = qgroup_inherit_add_group(&inherit, optarg);
635 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
642 usage(cmd_subvol_snapshot_usage);
646 if (check_argc_exact(argc - optind, 2))
647 usage(cmd_subvol_snapshot_usage);
649 subvol = argv[optind];
650 dst = argv[optind + 1];
652 retval = 1; /* failure */
653 res = test_issubvolume(subvol);
655 error("cannot access subvolume %s: %s", subvol, strerror(-res));
659 error("not a subvolume: %s", subvol);
663 res = test_isdir(dst);
664 if (res < 0 && res != -ENOENT) {
665 error("cannot access %s: %s", dst, strerror(-res));
669 error("'%s' exists and it is not a directory", dst);
674 dupname = strdup(subvol);
675 newname = basename(dupname);
678 dupname = strdup(dst);
679 newname = basename(dupname);
680 dupdir = strdup(dst);
681 dstdir = dirname(dupdir);
684 if (!test_issubvolname(newname)) {
685 error("invalid snapshot name '%s'", newname);
689 len = strlen(newname);
690 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
691 error("snapshot name too long '%s'", newname);
695 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
699 fd = btrfs_open_dir(subvol, &dirstream2, 1);
704 args.flags |= BTRFS_SUBVOL_RDONLY;
705 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
706 subvol, dstdir, newname);
708 printf("Create a snapshot of '%s' in '%s/%s'\n",
709 subvol, dstdir, newname);
714 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
715 args.size = qgroup_inherit_size(inherit);
716 args.qgroup_inherit = inherit;
718 strncpy_null(args.name, newname);
720 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
723 error("cannot snapshot '%s': %s", subvol, strerror(errno));
727 retval = 0; /* success */
730 close_file_or_dir(fddst, dirstream1);
731 close_file_or_dir(fd, dirstream2);
739 static const char * const cmd_subvol_get_default_usage[] = {
740 "btrfs subvolume get-default <path>",
741 "Get the default subvolume of a filesystem",
745 static int cmd_subvol_get_default(int argc, char **argv)
750 struct btrfs_list_filter_set *filter_set;
752 DIR *dirstream = NULL;
754 clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
756 if (check_argc_exact(argc - optind, 1))
757 usage(cmd_subvol_get_default_usage);
760 fd = btrfs_open_dir(subvol, &dirstream, 1);
764 ret = btrfs_list_get_default_subvolume(fd, &default_id);
766 error("failed to look up default subvolume: %s",
772 if (default_id == 0) {
773 error("'default' dir item not found");
777 /* no need to resolve roots if FS_TREE is default */
778 if (default_id == BTRFS_FS_TREE_OBJECTID) {
779 printf("ID 5 (FS_TREE)\n");
784 filter_set = btrfs_list_alloc_filter_set();
785 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
788 /* by default we shall print the following columns*/
789 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
790 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
791 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
792 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
794 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
795 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
800 close_file_or_dir(fd, dirstream);
804 static const char * const cmd_subvol_set_default_usage[] = {
805 "btrfs subvolume set-default <subvolid> <path>",
806 "Set the default subvolume of a filesystem",
810 static int cmd_subvol_set_default(int argc, char **argv)
816 DIR *dirstream = NULL;
818 clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
820 if (check_argc_exact(argc - optind, 2))
821 usage(cmd_subvol_set_default_usage);
823 subvolid = argv[optind];
824 path = argv[optind + 1];
826 objectid = arg_strtou64(subvolid);
828 fd = btrfs_open_dir(path, &dirstream, 1);
832 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
834 close_file_or_dir(fd, dirstream);
836 error("unable to set a new default subvolume: %s",
843 static const char * const cmd_subvol_find_new_usage[] = {
844 "btrfs subvolume find-new <path> <lastgen>",
845 "List the recently modified files in a filesystem",
849 static int cmd_subvol_find_new(int argc, char **argv)
855 DIR *dirstream = NULL;
857 clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
859 if (check_argc_exact(argc - optind, 2))
860 usage(cmd_subvol_find_new_usage);
862 subvol = argv[optind];
863 last_gen = arg_strtou64(argv[optind + 1]);
865 ret = test_issubvolume(subvol);
867 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
871 error("not a subvolume: %s", subvol);
875 fd = btrfs_open_dir(subvol, &dirstream, 1);
879 ret = ioctl(fd, BTRFS_IOC_SYNC);
881 error("sync ioctl failed on '%s': %s",
882 subvol, strerror(errno));
883 close_file_or_dir(fd, dirstream);
887 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
888 close_file_or_dir(fd, dirstream);
892 static const char * const cmd_subvol_show_usage[] = {
893 "btrfs subvolume show <subvol-path>",
894 "Show more information of the subvolume",
898 static int cmd_subvol_show(int argc, char **argv)
900 struct root_info get_ri;
901 struct btrfs_list_filter_set *filter_set = NULL;
903 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
904 char *fullpath = NULL;
905 char raw_prefix[] = "\t\t\t\t";
908 DIR *dirstream1 = NULL;
910 clean_args_no_options(argc, argv, cmd_subvol_show_usage);
912 if (check_argc_exact(argc - optind, 1))
913 usage(cmd_subvol_show_usage);
915 memset(&get_ri, 0, sizeof(get_ri));
916 fullpath = realpath(argv[optind], NULL);
918 error("cannot find real path for '%s': %s",
919 argv[optind], strerror(errno));
923 ret = get_subvol_info(fullpath, &get_ri);
926 * Since the top level btrfs was given don't
929 printf("%s is toplevel subvolume\n", fullpath);
935 error("Failed to get subvol info %s: %s\n",
936 fullpath, strerror(-ret));
938 error("Failed to get subvol info %s: %d\n",
945 printf("%s\n", fullpath);
946 printf("\tName: \t\t\t%s\n", get_ri.name);
948 if (uuid_is_null(get_ri.uuid))
949 strcpy(uuidparse, "-");
951 uuid_unparse(get_ri.uuid, uuidparse);
952 printf("\tUUID: \t\t\t%s\n", uuidparse);
954 if (uuid_is_null(get_ri.puuid))
955 strcpy(uuidparse, "-");
957 uuid_unparse(get_ri.puuid, uuidparse);
958 printf("\tParent UUID: \t\t%s\n", uuidparse);
960 if (uuid_is_null(get_ri.ruuid))
961 strcpy(uuidparse, "-");
963 uuid_unparse(get_ri.ruuid, uuidparse);
964 printf("\tReceived UUID: \t\t%s\n", uuidparse);
969 localtime_r(&get_ri.otime, &tm);
970 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
973 printf("\tCreation time: \t\t%s\n", tstr);
975 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
976 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
977 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
978 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
979 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
981 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
982 printf("\tFlags: \t\t\treadonly\n");
984 printf("\tFlags: \t\t\t-\n");
986 /* print the snapshots of the given subvol if any*/
987 printf("\tSnapshot(s):\n");
988 filter_set = btrfs_list_alloc_filter_set();
989 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
990 (u64)(unsigned long)get_ri.uuid);
991 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
993 fd = open_file_or_dir(fullpath, &dirstream1);
995 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
998 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1005 free(get_ri.full_path);
1008 close_file_or_dir(fd, dirstream1);
1013 static const char * const cmd_subvol_sync_usage[] = {
1014 "btrfs subvolume sync <path> [<subvol-id>...]",
1015 "Wait until given subvolume(s) are completely removed from the filesystem.",
1016 "Wait until given subvolume(s) are completely removed from the filesystem",
1018 "If no subvolume id is given, wait until all current deletion requests",
1019 "are completed, but do not wait for subvolumes deleted meanwhile.",
1020 "The status of subvolume ids is checked periodically.",
1022 "-s <N> sleep N seconds between checks (default: 1)",
1028 * If we're looking for any dead subvolume, take a shortcut and look
1029 * for any ORPHAN_ITEMs in the tree root
1031 static int fs_has_dead_subvolumes(int fd)
1034 struct btrfs_ioctl_search_args args;
1035 struct btrfs_ioctl_search_key *sk = &args.key;
1036 struct btrfs_ioctl_search_header sh;
1037 u64 min_subvolid = 0;
1040 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1041 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1042 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1043 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1044 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1045 sk->min_offset = min_subvolid;
1046 sk->max_offset = (u64)-1;
1047 sk->min_transid = 0;
1048 sk->max_transid = (u64)-1;
1051 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1058 memcpy(&sh, args.buf, sizeof(sh));
1059 min_subvolid = sh.offset;
1062 * Verify that the root item is really there and we haven't hit
1065 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1066 sk->min_objectid = min_subvolid;
1067 sk->max_objectid = min_subvolid;
1068 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1069 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1071 sk->max_offset = (u64)-1;
1072 sk->min_transid = 0;
1073 sk->max_transid = (u64)-1;
1076 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1081 * Stale orphan, try the next one
1083 if (!sk->nr_items) {
1092 #define SUBVOL_ID_BATCH 1024
1095 * Enumerate all dead subvolumes that exist in the filesystem.
1096 * Fill @ids and reallocate to bigger size if needed.
1098 static int enumerate_dead_subvols(int fd, u64 **ids)
1101 struct btrfs_ioctl_search_args args;
1102 struct btrfs_ioctl_search_key *sk = &args.key;
1106 memset(&args, 0, sizeof(args));
1108 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1109 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1110 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1111 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1112 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1114 sk->max_offset = (u64)-1;
1115 sk->min_transid = 0;
1116 sk->max_transid = (u64)-1;
1117 sk->nr_items = 4096;
1121 struct btrfs_ioctl_search_header *sh;
1125 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1133 for (i = 0; i < sk->nr_items; i++) {
1134 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1137 if (btrfs_search_header_type(sh)
1138 == BTRFS_ORPHAN_ITEM_KEY) {
1142 count += SUBVOL_ID_BATCH;
1143 newids = (u64*)realloc(*ids,
1144 count * sizeof(u64));
1149 (*ids)[idx] = btrfs_search_header_offset(sh);
1152 off += btrfs_search_header_len(sh);
1154 sk->min_objectid = btrfs_search_header_objectid(sh);
1155 sk->min_type = btrfs_search_header_type(sh);
1156 sk->min_offset = btrfs_search_header_offset(sh);
1158 if (sk->min_offset < (u64)-1)
1162 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1164 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1171 static int cmd_subvol_sync(int argc, char **argv)
1176 DIR *dirstream = NULL;
1179 int sleep_interval = 1;
1182 int c = getopt(argc, argv, "s:");
1189 sleep_interval = atoi(optarg);
1190 if (sleep_interval < 1) {
1191 error("invalid sleep interval %s", optarg);
1197 usage(cmd_subvol_sync_usage);
1201 if (check_argc_min(argc - optind, 1))
1202 usage(cmd_subvol_sync_usage);
1204 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1211 id_count = argc - optind;
1213 id_count = enumerate_dead_subvols(fd, &ids);
1215 error("can't enumerate dead subvolumes: %s",
1216 strerror(-id_count));
1220 if (id_count == 0) {
1225 ids = (u64*)malloc(id_count * sizeof(u64));
1227 error("not enough memory");
1232 for (i = 0; i < id_count; i++) {
1236 arg = argv[optind + i];
1238 id = strtoull(arg, NULL, 10);
1240 error("unrecognized subvolume id %s", arg);
1244 if (id < BTRFS_FIRST_FREE_OBJECTID
1245 || id > BTRFS_LAST_FREE_OBJECTID) {
1246 error("subvolume id %s out of range\n", arg);
1254 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1258 close_file_or_dir(fd, dirstream);
1263 static const char subvolume_cmd_group_info[] =
1264 "manage subvolumes: create, delete, list, etc";
1266 const struct cmd_group subvolume_cmd_group = {
1267 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1268 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1269 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1270 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1271 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1273 { "get-default", cmd_subvol_get_default,
1274 cmd_subvol_get_default_usage, NULL, 0 },
1275 { "set-default", cmd_subvol_set_default,
1276 cmd_subvol_set_default_usage, NULL, 0 },
1277 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1279 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1280 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1285 int cmd_subvolume(int argc, char **argv)
1287 return handle_command_group(&subvolume_cmd_group, argc, argv);