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", path);
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", subvol);
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", subvol);
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", fullpath);
964 error("not a subvolume: %s", fullpath);
969 ret = find_mount_root(fullpath, &mnt);
971 error("find_mount_root failed on '%s': %s",
972 fullpath, strerror(-ret));
976 error("%s doesn't belong to btrfs mount point", fullpath);
980 svpath = get_subvol_name(mnt, fullpath);
982 fd = btrfs_open_dir(fullpath, &dirstream1, 1);
986 ret = btrfs_list_get_path_rootid(fd, &sv_id);
988 error("can't get rootid for '%s'", fullpath);
992 mntfd = btrfs_open_dir(mnt, &dirstream2, 1);
996 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
997 printf("%s is toplevel subvolume\n", fullpath);
1001 memset(&get_ri, 0, sizeof(get_ri));
1002 get_ri.root_id = sv_id;
1004 ret = btrfs_get_subvol(mntfd, &get_ri);
1006 error("can't find '%s'", svpath);
1010 /* print the info */
1011 printf("%s\n", fullpath);
1012 printf("\tName: \t\t\t%s\n", get_ri.name);
1014 if (uuid_is_null(get_ri.uuid))
1015 strcpy(uuidparse, "-");
1017 uuid_unparse(get_ri.uuid, uuidparse);
1018 printf("\tUUID: \t\t\t%s\n", uuidparse);
1020 if (uuid_is_null(get_ri.puuid))
1021 strcpy(uuidparse, "-");
1023 uuid_unparse(get_ri.puuid, uuidparse);
1024 printf("\tParent UUID: \t\t%s\n", uuidparse);
1026 if (uuid_is_null(get_ri.ruuid))
1027 strcpy(uuidparse, "-");
1029 uuid_unparse(get_ri.ruuid, uuidparse);
1030 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1035 localtime_r(&get_ri.otime, &tm);
1036 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1039 printf("\tCreation time: \t\t%s\n", tstr);
1041 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1042 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1043 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1044 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1045 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1047 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1048 printf("\tFlags: \t\t\treadonly\n");
1050 printf("\tFlags: \t\t\t-\n");
1052 /* print the snapshots of the given subvol if any*/
1053 printf("\tSnapshot(s):\n");
1054 filter_set = btrfs_list_alloc_filter_set();
1055 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1056 (u64)(unsigned long)get_ri.uuid);
1057 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1058 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1064 free(get_ri.full_path);
1065 btrfs_list_free_filter_set(filter_set);
1068 close_file_or_dir(fd, dirstream1);
1069 close_file_or_dir(mntfd, dirstream2);
1075 static const char * const cmd_subvol_sync_usage[] = {
1076 "btrfs subvolume sync <path> [<subvol-id>...]",
1077 "Wait until given subvolume(s) are completely removed from the filesystem.",
1078 "Wait until given subvolume(s) are completely removed from the filesystem",
1080 "If no subvolume id is given, wait until all current deletion requests",
1081 "are completed, but do not wait for subvolumes deleted meanwhile.",
1082 "The status of subvolume ids is checked periodically.",
1084 "-s <N> sleep N seconds between checks (default: 1)",
1090 * If we're looking for any dead subvolume, take a shortcut and look
1091 * for any ORPHAN_ITEMs in the tree root
1093 static int fs_has_dead_subvolumes(int fd)
1096 struct btrfs_ioctl_search_args args;
1097 struct btrfs_ioctl_search_key *sk = &args.key;
1098 struct btrfs_ioctl_search_header sh;
1099 u64 min_subvolid = 0;
1102 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1103 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1104 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1105 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1106 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1107 sk->min_offset = min_subvolid;
1108 sk->max_offset = (u64)-1;
1109 sk->min_transid = 0;
1110 sk->max_transid = (u64)-1;
1113 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1120 memcpy(&sh, args.buf, sizeof(sh));
1121 min_subvolid = sh.offset;
1124 * Verify that the root item is really there and we haven't hit
1127 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1128 sk->min_objectid = min_subvolid;
1129 sk->max_objectid = min_subvolid;
1130 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1131 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1133 sk->max_offset = (u64)-1;
1134 sk->min_transid = 0;
1135 sk->max_transid = (u64)-1;
1138 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1143 * Stale orphan, try the next one
1145 if (!sk->nr_items) {
1154 #define SUBVOL_ID_BATCH 1024
1157 * Enumerate all dead subvolumes that exist in the filesystem.
1158 * Fill @ids and reallocate to bigger size if needed.
1160 static int enumerate_dead_subvols(int fd, u64 **ids)
1163 struct btrfs_ioctl_search_args args;
1164 struct btrfs_ioctl_search_key *sk = &args.key;
1168 memset(&args, 0, sizeof(args));
1170 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1171 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1172 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1173 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1174 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1176 sk->max_offset = (u64)-1;
1177 sk->min_transid = 0;
1178 sk->max_transid = (u64)-1;
1179 sk->nr_items = 4096;
1183 struct btrfs_ioctl_search_header *sh;
1187 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1195 for (i = 0; i < sk->nr_items; i++) {
1196 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1199 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1203 count += SUBVOL_ID_BATCH;
1204 newids = (u64*)realloc(*ids, count);
1209 (*ids)[idx] = sh->offset;
1214 sk->min_objectid = sh->objectid;
1215 sk->min_type = sh->type;
1216 sk->min_offset = sh->offset;
1218 if (sk->min_offset < (u64)-1)
1222 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1224 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1231 static int cmd_subvol_sync(int argc, char **argv)
1236 DIR *dirstream = NULL;
1239 int sleep_interval = 1;
1243 int c = getopt(argc, argv, "s:");
1250 sleep_interval = atoi(argv[optind]);
1251 if (sleep_interval < 1) {
1252 error("invalid sleep interval %s",
1259 usage(cmd_subvol_sync_usage);
1263 if (check_argc_min(argc - optind, 1))
1264 usage(cmd_subvol_sync_usage);
1266 fd = btrfs_open_dir(argv[optind], &dirstream, 1);
1273 id_count = argc - optind;
1275 id_count = enumerate_dead_subvols(fd, &ids);
1277 error("can't enumerate dead subvolumes: %s",
1278 strerror(-id_count));
1282 if (id_count == 0) {
1287 ids = (u64*)malloc(id_count * sizeof(u64));
1289 error("not enough memory");
1294 for (i = 0; i < id_count; i++) {
1298 arg = argv[optind + i];
1300 id = strtoull(arg, NULL, 10);
1302 error("unrecognized subvolume id %s", arg);
1306 if (id < BTRFS_FIRST_FREE_OBJECTID
1307 || id > BTRFS_LAST_FREE_OBJECTID) {
1308 error("subvolume id %s out of range\n", arg);
1316 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1320 close_file_or_dir(fd, dirstream);
1325 static const char subvolume_cmd_group_info[] =
1326 "manage subvolumes: create, delete, list, etc";
1328 const struct cmd_group subvolume_cmd_group = {
1329 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1330 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1331 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1332 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1333 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1335 { "get-default", cmd_subvol_get_default,
1336 cmd_subvol_get_default_usage, NULL, 0 },
1337 { "set-default", cmd_subvol_set_default,
1338 cmd_subvol_set_default_usage, NULL, 0 },
1339 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1341 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1342 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1347 int cmd_subvolume(int argc, char **argv)
1349 return handle_command_group(&subvolume_cmd_group, argc, argv);