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>
27 #include <uuid/uuid.h>
29 #include "kerncompat.h"
36 #include "btrfs-list.h"
39 static int is_subvolume_cleaned(int fd, u64 subvolid)
42 struct btrfs_ioctl_search_args args;
43 struct btrfs_ioctl_search_key *sk = &args.key;
45 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
46 sk->min_objectid = subvolid;
47 sk->max_objectid = subvolid;
48 sk->min_type = BTRFS_ROOT_ITEM_KEY;
49 sk->max_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_offset = (u64)-1;
53 sk->max_transid = (u64)-1;
56 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
60 if (sk->nr_items == 0)
66 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
75 for (i = 0; i < count; i++) {
78 ret = is_subvolume_cleaned(fd, ids[i]);
81 "cannot read status of dead subvolume %llu: %s",
82 (unsigned long long)ids[i], strerror(-ret));
86 printf("Subvolume id %llu is gone\n", ids[i]);
94 sleep(sleep_interval);
100 static const char * const subvolume_cmd_group_usage[] = {
101 "btrfs subvolume <command> <args>",
105 static const char * const cmd_subvol_create_usage[] = {
106 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
107 "Create a subvolume",
108 "Create a subvolume <name> in <dest>. If <dest> is not given",
109 "subvolume <name> will be created in the current directory.",
111 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
112 " option can be given multiple times.",
116 static int cmd_subvol_create(int argc, char **argv)
118 int retval, res, len;
120 char *dupname = NULL;
125 struct btrfs_qgroup_inherit *inherit = NULL;
126 DIR *dirstream = NULL;
130 int c = getopt(argc, argv, "c:i:v");
136 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
143 res = qgroup_inherit_add_group(&inherit, optarg);
150 usage(cmd_subvol_create_usage);
154 if (check_argc_exact(argc - optind, 1))
155 usage(cmd_subvol_create_usage);
159 retval = 1; /* failure */
160 res = test_isdir(dst);
161 if (res < 0 && res != -ENOENT) {
162 error("cannot access %s: %s", dst, strerror(-res));
166 error("target path already exists: %s", dst);
170 dupname = strdup(dst);
171 newname = basename(dupname);
172 dupdir = strdup(dst);
173 dstdir = dirname(dupdir);
175 if (!test_issubvolname(newname)) {
176 error("invalid subvolume name: %s", newname);
180 len = strlen(newname);
181 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
182 error("subvolume name too long: %s", newname);
186 fddst = btrfs_open_dir(dstdir, &dirstream, 1);
190 printf("Create subvolume '%s/%s'\n", dstdir, newname);
192 struct btrfs_ioctl_vol_args_v2 args;
194 memset(&args, 0, sizeof(args));
195 strncpy_null(args.name, newname);
196 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
197 args.size = qgroup_inherit_size(inherit);
198 args.qgroup_inherit = inherit;
200 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
202 struct btrfs_ioctl_vol_args args;
204 memset(&args, 0, sizeof(args));
205 strncpy_null(args.name, newname);
207 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
211 error("cannot create subvolume: %s", strerror(errno));
215 retval = 0; /* success */
217 close_file_or_dir(fddst, dirstream);
226 * Test if path is a subvolume
228 * 0 - path exists but it is not a subvolume
229 * 1 - path exists and it is a subvolume
232 int test_issubvolume(const char *path)
237 res = stat(path, &st);
241 return (st.st_ino == BTRFS_FIRST_FREE_OBJECTID)
242 && S_ISDIR(st.st_mode);
245 static int wait_for_commit(int fd)
249 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
252 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
255 static const char * const cmd_subvol_delete_usage[] = {
256 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
257 "Delete subvolume(s)",
258 "Delete subvolumes from the filesystem. The corresponding directory",
259 "is removed instantly but the data blocks are removed later.",
260 "The deletion does not involve full commit by default due to",
261 "performance reasons (as a consequence, the subvolume may appear again",
262 "after a crash). Use one of the --commit options to wait until the",
263 "operation is safely stored on the media.",
265 "-c|--commit-after wait for transaction commit at the end of the operation",
266 "-C|--commit-each wait for transaction commit after deleting each subvolume",
270 static int cmd_subvol_delete(int argc, char **argv)
275 struct btrfs_ioctl_vol_args args;
276 char *dname, *vname, *cpath;
277 char *dupdname = NULL;
278 char *dupvname = NULL;
280 DIR *dirstream = NULL;
287 static const struct option long_options[] = {
288 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
289 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
293 c = getopt_long(argc, argv, "cC", long_options, NULL);
308 usage(cmd_subvol_delete_usage);
312 if (check_argc_min(argc - optind, 1))
313 usage(cmd_subvol_delete_usage);
316 printf("Transaction commit: %s\n",
317 !commit_mode ? "none (default)" :
318 commit_mode == 1 ? "at the end" : "after each");
326 res = test_issubvolume(path);
328 error("cannot access subvolume %s: %s", path, strerror(-res));
333 error("not a subvolume: %s", path);
338 cpath = realpath(path, NULL);
341 error("cannot find real path for '%s': %s",
342 path, strerror(errno));
345 dupdname = strdup(cpath);
346 dname = dirname(dupdname);
347 dupvname = strdup(cpath);
348 vname = basename(dupvname);
351 fd = btrfs_open_dir(dname, &dirstream, 1);
357 printf("Delete subvolume (%s): '%s/%s'\n",
358 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
359 ? "commit" : "no-commit", dname, vname);
360 memset(&args, 0, sizeof(args));
361 strncpy_null(args.name, vname);
362 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
364 error("cannot delete '%s/%s': %s", dname, vname,
370 if (commit_mode == 1) {
371 res = wait_for_commit(fd);
373 error("unable to wait for commit after '%s': %s",
374 path, strerror(errno));
386 close_file_or_dir(fd, dirstream);
387 /* avoid double free */
393 if (commit_mode == 2 && fd != -1) {
394 res = wait_for_commit(fd);
396 error("unable to do final sync after deletion: %s",
401 close_file_or_dir(fd, dirstream);
408 * - uppercase for filters and sort options
409 * - lowercase for enabling specific items in the output
411 static const char * const cmd_subvol_list_usage[] = {
412 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
413 "[--sort=gen,ogen,rootid,path] <path>",
414 "List subvolumes (and snapshots)",
416 "-p print parent ID",
417 "-a print all the subvolumes in the filesystem and",
418 " distinguish absolute and relative path with respect",
419 " to the given <path>",
420 "-c print the ogeneration of the subvolume",
421 "-g print the generation of the subvolume",
422 "-o print only subvolumes below specified path",
423 "-u print the uuid of subvolumes (and snapshots)",
424 "-q print the parent uuid of the snapshots",
425 "-R print the uuid of the received snapshots",
426 "-t print the result as a table",
427 "-s list snapshots only in the filesystem",
428 "-r list readonly subvolumes (including snapshots)",
429 "-d list deleted subvolumes that are not yet cleaned",
431 " filter the subvolumes by generation",
432 " (+value: >= value; -value: <= value; value: = value)",
434 " filter the subvolumes by ogeneration",
435 " (+value: >= value; -value: <= value; value: = value)",
436 "--sort=gen,ogen,rootid,path",
437 " list the subvolume in order of gen, ogen, rootid or path",
438 " you also can add '+' or '-' in front of each items.",
439 " (+:ascending, -:descending, ascending default)",
443 static int cmd_subvol_list(int argc, char **argv)
445 struct btrfs_list_filter_set *filter_set;
446 struct btrfs_list_comparer_set *comparer_set;
450 int ret = -1, uerr = 0;
452 int is_tab_result = 0;
454 int is_only_in_path = 0;
455 DIR *dirstream = NULL;
457 filter_set = btrfs_list_alloc_filter_set();
458 comparer_set = btrfs_list_alloc_comparer_set();
463 static const struct option long_options[] = {
464 {"sort", required_argument, NULL, 'S'},
468 c = getopt_long(argc, argv,
469 "acdgopqsurRG:C:t", long_options, NULL);
475 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
481 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
484 btrfs_list_setup_filter(&filter_set,
485 BTRFS_LIST_FILTER_DELETED,
489 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
498 btrfs_list_setup_filter(&filter_set,
499 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
501 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
502 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
505 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
508 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
511 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
514 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
517 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
518 ret = btrfs_list_parse_filter_string(optarg,
520 BTRFS_LIST_FILTER_GEN);
528 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
529 ret = btrfs_list_parse_filter_string(optarg,
531 BTRFS_LIST_FILTER_CGEN);
538 ret = btrfs_list_parse_sort_string(optarg,
553 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
556 if (check_argc_exact(argc - optind, 1)) {
561 subvol = argv[optind];
562 fd = btrfs_open_dir(subvol, &dirstream, 1);
565 error("can't access '%s'", subvol);
569 ret = btrfs_list_get_path_rootid(fd, &top_id);
571 error("can't get rootid for '%s'", subvol);
576 btrfs_list_setup_filter(&filter_set,
577 BTRFS_LIST_FILTER_FULL_PATH,
579 else if (is_only_in_path)
580 btrfs_list_setup_filter(&filter_set,
581 BTRFS_LIST_FILTER_TOPID_EQUAL,
584 /* by default we shall print the following columns*/
585 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
586 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
587 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
588 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
591 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
592 BTRFS_LIST_LAYOUT_TABLE,
593 !is_list_all && !is_only_in_path, NULL);
595 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
596 BTRFS_LIST_LAYOUT_DEFAULT,
597 !is_list_all && !is_only_in_path, NULL);
600 close_file_or_dir(fd, dirstream);
602 btrfs_list_free_filter_set(filter_set);
604 btrfs_list_free_comparer_set(comparer_set);
606 usage(cmd_subvol_list_usage);
610 static const char * const cmd_subvol_snapshot_usage[] = {
611 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
612 "Create a snapshot of the subvolume",
613 "Create a writable/readonly snapshot of the subvolume <source> with",
614 "the name <name> in the <dest> directory. If only <dest> is given,",
615 "the subvolume will be named the basename of <source>.",
617 "-r create a readonly snapshot",
618 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
619 " option can be given multiple times.",
623 static int cmd_subvol_snapshot(int argc, char **argv)
627 int fd = -1, fddst = -1;
628 int len, readonly = 0;
629 char *dupname = NULL;
633 struct btrfs_ioctl_vol_args_v2 args;
634 struct btrfs_qgroup_inherit *inherit = NULL;
635 DIR *dirstream1 = NULL, *dirstream2 = NULL;
638 memset(&args, 0, sizeof(args));
640 int c = getopt(argc, argv, "c:i:r");
646 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
653 res = qgroup_inherit_add_group(&inherit, optarg);
663 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
670 usage(cmd_subvol_snapshot_usage);
674 if (check_argc_exact(argc - optind, 2))
675 usage(cmd_subvol_snapshot_usage);
677 subvol = argv[optind];
678 dst = argv[optind + 1];
680 retval = 1; /* failure */
681 res = test_issubvolume(subvol);
683 error("cannot access subvolume %s: %s", subvol, strerror(-res));
687 error("not a subvolume: %s", subvol);
691 res = test_isdir(dst);
692 if (res < 0 && res != -ENOENT) {
693 error("cannot access %s: %s", dst, strerror(-res));
697 error("'%s' exists and it is not a directory", dst);
702 dupname = strdup(subvol);
703 newname = basename(dupname);
706 dupname = strdup(dst);
707 newname = basename(dupname);
708 dupdir = strdup(dst);
709 dstdir = dirname(dupdir);
712 if (!test_issubvolname(newname)) {
713 error("invalid snapshot name '%s'", newname);
717 len = strlen(newname);
718 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
719 error("snapshot name too long '%s'", newname);
723 fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
727 fd = btrfs_open_dir(subvol, &dirstream2, 1);
732 args.flags |= BTRFS_SUBVOL_RDONLY;
733 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
734 subvol, dstdir, newname);
736 printf("Create a snapshot of '%s' in '%s/%s'\n",
737 subvol, dstdir, newname);
742 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
743 args.size = qgroup_inherit_size(inherit);
744 args.qgroup_inherit = inherit;
746 strncpy_null(args.name, newname);
748 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
751 error("cannot snapshot '%s': %s", subvol, strerror(errno));
755 retval = 0; /* success */
758 close_file_or_dir(fddst, dirstream1);
759 close_file_or_dir(fd, dirstream2);
767 static const char * const cmd_subvol_get_default_usage[] = {
768 "btrfs subvolume get-default <path>",
769 "Get the default subvolume of a filesystem",
773 static int cmd_subvol_get_default(int argc, char **argv)
778 struct btrfs_list_filter_set *filter_set;
780 DIR *dirstream = NULL;
782 if (check_argc_exact(argc, 2))
783 usage(cmd_subvol_get_default_usage);
786 fd = btrfs_open_dir(subvol, &dirstream, 1);
790 ret = btrfs_list_get_default_subvolume(fd, &default_id);
792 error("failed to look up default subvolume: %s",
798 if (default_id == 0) {
799 error("'default' dir item not found");
803 /* no need to resolve roots if FS_TREE is default */
804 if (default_id == BTRFS_FS_TREE_OBJECTID) {
805 printf("ID 5 (FS_TREE)\n");
810 filter_set = btrfs_list_alloc_filter_set();
811 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
814 /* by default we shall print the following columns*/
815 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
816 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
817 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
818 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
820 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
821 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
824 btrfs_list_free_filter_set(filter_set);
826 close_file_or_dir(fd, dirstream);
830 static const char * const cmd_subvol_set_default_usage[] = {
831 "btrfs subvolume set-default <subvolid> <path>",
832 "Set the default subvolume of a filesystem",
836 static int cmd_subvol_set_default(int argc, char **argv)
842 DIR *dirstream = NULL;
844 if (check_argc_exact(argc, 3))
845 usage(cmd_subvol_set_default_usage);
850 objectid = arg_strtou64(subvolid);
852 fd = btrfs_open_dir(path, &dirstream, 1);
856 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
858 close_file_or_dir(fd, dirstream);
860 error("unable to set a new default subvolume: %s",
867 static const char * const cmd_subvol_find_new_usage[] = {
868 "btrfs subvolume find-new <path> <lastgen>",
869 "List the recently modified files in a filesystem",
873 static int cmd_subvol_find_new(int argc, char **argv)
879 DIR *dirstream = NULL;
881 if (check_argc_exact(argc, 3))
882 usage(cmd_subvol_find_new_usage);
885 last_gen = arg_strtou64(argv[2]);
887 ret = test_issubvolume(subvol);
889 error("cannot access subvolume %s: %s", subvol, strerror(-ret));
893 error("not a subvolume: %s", subvol);
897 fd = btrfs_open_dir(subvol, &dirstream, 1);
901 ret = ioctl(fd, BTRFS_IOC_SYNC);
903 error("sync ioctl failed on '%s': %s",
904 subvol, strerror(errno));
905 close_file_or_dir(fd, dirstream);
909 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
910 close_file_or_dir(fd, dirstream);
914 static const char * const cmd_subvol_show_usage[] = {
915 "btrfs subvolume show <subvol-path>",
916 "Show more information of the subvolume",
920 static int cmd_subvol_show(int argc, char **argv)
922 struct root_info get_ri;
923 struct btrfs_list_filter_set *filter_set;
925 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
926 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
927 char raw_prefix[] = "\t\t\t\t";
929 int fd = -1, mntfd = -1;
931 DIR *dirstream1 = NULL, *dirstream2 = NULL;
934 static const struct option long_options[] = {
937 int c = getopt_long(argc, argv, "", long_options, NULL);
944 usage(cmd_subvol_show_usage);
948 if (check_argc_exact(argc - optind, 1))
949 usage(cmd_subvol_show_usage);
951 fullpath = realpath(argv[optind], NULL);
953 error("cannot find real path for '%s': %s",
954 argv[optind], strerror(errno));
958 ret = test_issubvolume(fullpath);
960 error("cannot access subvolume %s: %s", fullpath,
965 error("not a subvolume: %s", fullpath);
970 ret = find_mount_root(fullpath, &mnt);
972 error("find_mount_root failed on '%s': %s",
973 fullpath, strerror(-ret));
977 error("%s doesn't belong to btrfs mount point", fullpath);
981 svpath = get_subvol_name(mnt, fullpath);
983 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
987 ret = btrfs_list_get_path_rootid(fd, &sv_id);
989 error("can't get rootid for '%s'", fullpath);
993 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
997 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
998 printf("%s is toplevel subvolume\n", fullpath);
1002 memset(&get_ri, 0, sizeof(get_ri));
1003 get_ri.root_id = sv_id;
1005 ret = btrfs_get_subvol(mntfd, &get_ri);
1007 error("can't find '%s'", svpath);
1011 /* print the info */
1012 printf("%s\n", fullpath);
1013 printf("\tName: \t\t\t%s\n", get_ri.name);
1015 if (uuid_is_null(get_ri.uuid))
1016 strcpy(uuidparse, "-");
1018 uuid_unparse(get_ri.uuid, uuidparse);
1019 printf("\tUUID: \t\t\t%s\n", uuidparse);
1021 if (uuid_is_null(get_ri.puuid))
1022 strcpy(uuidparse, "-");
1024 uuid_unparse(get_ri.puuid, uuidparse);
1025 printf("\tParent UUID: \t\t%s\n", uuidparse);
1027 if (uuid_is_null(get_ri.ruuid))
1028 strcpy(uuidparse, "-");
1030 uuid_unparse(get_ri.ruuid, uuidparse);
1031 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1036 localtime_r(&get_ri.otime, &tm);
1037 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1040 printf("\tCreation time: \t\t%s\n", tstr);
1042 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1043 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1044 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1045 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1046 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1048 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1049 printf("\tFlags: \t\t\treadonly\n");
1051 printf("\tFlags: \t\t\t-\n");
1053 /* print the snapshots of the given subvol if any*/
1054 printf("\tSnapshot(s):\n");
1055 filter_set = btrfs_list_alloc_filter_set();
1056 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1057 (u64)(unsigned long)get_ri.uuid);
1058 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1059 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1065 free(get_ri.full_path);
1066 btrfs_list_free_filter_set(filter_set);
1069 close_file_or_dir(fd, dirstream1);
1070 close_file_or_dir(mntfd, dirstream2);
1076 static const char * const cmd_subvol_sync_usage[] = {
1077 "btrfs subvolume sync <path> [<subvol-id>...]",
1078 "Wait until given subvolume(s) are completely removed from the filesystem.",
1079 "Wait until given subvolume(s) are completely removed from the filesystem",
1081 "If no subvolume id is given, wait until all current deletion requests",
1082 "are completed, but do not wait for subvolumes deleted meanwhile.",
1083 "The status of subvolume ids is checked periodically.",
1085 "-s <N> sleep N seconds between checks (default: 1)",
1091 * If we're looking for any dead subvolume, take a shortcut and look
1092 * for any ORPHAN_ITEMs in the tree root
1094 static int fs_has_dead_subvolumes(int fd)
1097 struct btrfs_ioctl_search_args args;
1098 struct btrfs_ioctl_search_key *sk = &args.key;
1099 struct btrfs_ioctl_search_header sh;
1100 u64 min_subvolid = 0;
1103 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1104 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1105 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1106 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1107 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1108 sk->min_offset = min_subvolid;
1109 sk->max_offset = (u64)-1;
1110 sk->min_transid = 0;
1111 sk->max_transid = (u64)-1;
1114 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1121 memcpy(&sh, args.buf, sizeof(sh));
1122 min_subvolid = sh.offset;
1125 * Verify that the root item is really there and we haven't hit
1128 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1129 sk->min_objectid = min_subvolid;
1130 sk->max_objectid = min_subvolid;
1131 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1132 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1134 sk->max_offset = (u64)-1;
1135 sk->min_transid = 0;
1136 sk->max_transid = (u64)-1;
1139 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1144 * Stale orphan, try the next one
1146 if (!sk->nr_items) {
1155 #define SUBVOL_ID_BATCH 1024
1158 * Enumerate all dead subvolumes that exist in the filesystem.
1159 * Fill @ids and reallocate to bigger size if needed.
1161 static int enumerate_dead_subvols(int fd, u64 **ids)
1164 struct btrfs_ioctl_search_args args;
1165 struct btrfs_ioctl_search_key *sk = &args.key;
1169 memset(&args, 0, sizeof(args));
1171 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1172 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1173 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1174 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1175 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1177 sk->max_offset = (u64)-1;
1178 sk->min_transid = 0;
1179 sk->max_transid = (u64)-1;
1180 sk->nr_items = 4096;
1184 struct btrfs_ioctl_search_header *sh;
1188 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1196 for (i = 0; i < sk->nr_items; i++) {
1197 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1200 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1204 count += SUBVOL_ID_BATCH;
1205 newids = (u64*)realloc(*ids, count);
1210 (*ids)[idx] = sh->offset;
1215 sk->min_objectid = sh->objectid;
1216 sk->min_type = sh->type;
1217 sk->min_offset = sh->offset;
1219 if (sk->min_offset < (u64)-1)
1223 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1225 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1232 static int cmd_subvol_sync(int argc, char **argv)
1237 DIR *dirstream = NULL;
1240 int sleep_interval = 1;
1244 int c = getopt(argc, argv, "s:");
1251 sleep_interval = atoi(argv[optind]);
1252 if (sleep_interval < 1) {
1253 error("invalid sleep interval %s",
1260 usage(cmd_subvol_sync_usage);
1264 if (check_argc_min(argc - optind, 1))
1265 usage(cmd_subvol_sync_usage);
1267 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1274 id_count = argc - optind;
1276 id_count = enumerate_dead_subvols(fd, &ids);
1278 error("can't enumerate dead subvolumes: %s",
1279 strerror(-id_count));
1283 if (id_count == 0) {
1288 ids = (u64*)malloc(id_count * sizeof(u64));
1290 error("not enough memory");
1295 for (i = 0; i < id_count; i++) {
1299 arg = argv[optind + i];
1301 id = strtoull(arg, NULL, 10);
1303 error("unrecognized subvolume id %s", arg);
1307 if (id < BTRFS_FIRST_FREE_OBJECTID
1308 || id > BTRFS_LAST_FREE_OBJECTID) {
1309 error("subvolume id %s out of range\n", arg);
1317 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1321 close_file_or_dir(fd, dirstream);
1326 static const char subvolume_cmd_group_info[] =
1327 "manage subvolumes: create, delete, list, etc";
1329 const struct cmd_group subvolume_cmd_group = {
1330 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1331 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1332 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1333 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1334 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1336 { "get-default", cmd_subvol_get_default,
1337 cmd_subvol_get_default_usage, NULL, 0 },
1338 { "set-default", cmd_subvol_set_default,
1339 cmd_subvol_set_default_usage, NULL, 0 },
1340 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1342 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1343 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1348 int cmd_subvolume(int argc, char **argv)
1350 return handle_command_group(&subvolume_cmd_group, argc, argv);