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 const char * const subvolume_cmd_group_usage[] = {
40 "btrfs subvolume <command> <args>",
44 static const char * const cmd_subvol_create_usage[] = {
45 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
47 "Create a subvolume <name> in <dest>. If <dest> is not given",
48 "subvolume <name> will be created in the current directory.",
50 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
51 " option can be given multiple times.",
55 static int cmd_subvol_create(int argc, char **argv)
64 struct btrfs_qgroup_inherit *inherit = NULL;
65 DIR *dirstream = NULL;
69 int c = getopt(argc, argv, "c:i:v");
75 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
82 res = qgroup_inherit_add_group(&inherit, optarg);
89 usage(cmd_subvol_create_usage);
93 if (check_argc_exact(argc - optind, 1))
94 usage(cmd_subvol_create_usage);
98 retval = 1; /* failure */
99 res = test_isdir(dst);
101 fprintf(stderr, "ERROR: '%s' exists\n", dst);
105 dupname = strdup(dst);
106 newname = basename(dupname);
107 dupdir = strdup(dst);
108 dstdir = dirname(dupdir);
110 if (!test_issubvolname(newname)) {
111 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
116 len = strlen(newname);
117 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
118 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
123 fddst = open_file_or_dir(dstdir, &dirstream);
125 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
129 printf("Create subvolume '%s/%s'\n", dstdir, newname);
131 struct btrfs_ioctl_vol_args_v2 args;
133 memset(&args, 0, sizeof(args));
134 strncpy_null(args.name, newname);
135 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
136 args.size = qgroup_inherit_size(inherit);
137 args.qgroup_inherit = inherit;
139 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
141 struct btrfs_ioctl_vol_args args;
143 memset(&args, 0, sizeof(args));
144 strncpy_null(args.name, newname);
146 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
150 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
155 retval = 0; /* success */
157 close_file_or_dir(fddst, dirstream);
166 * test if path is a subvolume:
167 * this function return
168 * 0-> path exists but it is not a subvolume
169 * 1-> path exists and it is a subvolume
170 * -1 -> path is unaccessible
172 int test_issubvolume(char *path)
177 res = stat(path, &st);
181 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
184 static int wait_for_commit(int fd)
188 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
191 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
194 static const char * const cmd_subvol_delete_usage[] = {
195 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
196 "Delete subvolume(s)",
197 "Delete subvolumes from the filesystem. The corresponding directory",
198 "is removed instantly but the data blocks are removed later.",
199 "The deletion does not involve full commit by default due to",
200 "performance reasons (as a consequence, the subvolume may appear again",
201 "after a crash). Use one of the --commit options to wait until the",
202 "operation is safely stored on the media.",
204 "-c|--commit-after wait for transaction commit at the end of the operation",
205 "-C|--commit-each wait for transaction commit after deleting each subvolume",
209 static int cmd_subvol_delete(int argc, char **argv)
214 struct btrfs_ioctl_vol_args args;
215 char *dname, *vname, *cpath;
216 char *dupdname = NULL;
217 char *dupvname = NULL;
219 DIR *dirstream = NULL;
222 struct option long_options[] = {
223 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
224 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
232 c = getopt_long(argc, argv, "cC", long_options, NULL);
247 usage(cmd_subvol_delete_usage);
251 if (check_argc_min(argc - optind, 1))
252 usage(cmd_subvol_delete_usage);
255 printf("Transaction commit: %s\n",
256 !commit_mode ? "none (default)" :
257 commit_mode == 1 ? "at the end" : "after each");
265 res = test_issubvolume(path);
267 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
272 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
277 cpath = realpath(path, NULL);
280 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
281 path, strerror(errno));
284 dupdname = strdup(cpath);
285 dname = dirname(dupdname);
286 dupvname = strdup(cpath);
287 vname = basename(dupvname);
290 fd = open_file_or_dir(dname, &dirstream);
292 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
297 printf("Delete subvolume (%s): '%s/%s'\n",
298 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
299 ? "commit" : "no-commit", dname, vname);
300 strncpy_null(args.name, vname);
301 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
305 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
306 dname, vname, strerror(e));
311 if (commit_mode == 1) {
312 res = wait_for_commit(fd);
315 "ERROR: unable to wait for commit after '%s': %s\n",
316 path, strerror(errno));
328 close_file_or_dir(fd, dirstream);
329 /* avoid double free */
335 if (commit_mode == 2 && fd != -1) {
336 res = wait_for_commit(fd);
339 "ERROR: unable to do final sync: %s\n",
344 close_file_or_dir(fd, dirstream);
351 * - uppercase for filters and sort options
352 * - lowercase for enabling specific items in the output
354 static const char * const cmd_subvol_list_usage[] = {
355 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
356 "[--sort=gen,ogen,rootid,path] <path>",
357 "List subvolumes (and snapshots)",
359 "-p print parent ID",
360 "-a print all the subvolumes in the filesystem and",
361 " distinguish absolute and relative path with respect",
362 " to the given <path>",
363 "-c print the ogeneration of the subvolume",
364 "-g print the generation of the subvolume",
365 "-o print only subvolumes below specified path",
366 "-u print the uuid of subvolumes (and snapshots)",
367 "-q print the parent uuid of the snapshots",
368 "-R print the uuid of the received snapshots",
369 "-t print the result as a table",
370 "-s list snapshots only in the filesystem",
371 "-r list readonly subvolumes (including snapshots)",
372 "-d list deleted subvolumes that are not yet cleaned",
374 " filter the subvolumes by generation",
375 " (+value: >= value; -value: <= value; value: = value)",
377 " filter the subvolumes by ogeneration",
378 " (+value: >= value; -value: <= value; value: = value)",
379 "--sort=gen,ogen,rootid,path",
380 " list the subvolume in order of gen, ogen, rootid or path",
381 " you also can add '+' or '-' in front of each items.",
382 " (+:ascending, -:descending, ascending default)",
386 static int cmd_subvol_list(int argc, char **argv)
388 struct btrfs_list_filter_set *filter_set;
389 struct btrfs_list_comparer_set *comparer_set;
393 int ret = -1, uerr = 0;
396 int is_tab_result = 0;
398 int is_only_in_path = 0;
399 struct option long_options[] = {
400 {"sort", 1, NULL, 'S'},
403 DIR *dirstream = NULL;
405 filter_set = btrfs_list_alloc_filter_set();
406 comparer_set = btrfs_list_alloc_comparer_set();
410 c = getopt_long(argc, argv,
411 "acdgopqsurRG:C:t", long_options, NULL);
417 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
423 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
426 btrfs_list_setup_filter(&filter_set,
427 BTRFS_LIST_FILTER_DELETED,
431 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
440 btrfs_list_setup_filter(&filter_set,
441 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
443 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
444 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
447 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
450 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
453 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
456 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
459 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
460 ret = btrfs_list_parse_filter_string(optarg,
462 BTRFS_LIST_FILTER_GEN);
470 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
471 ret = btrfs_list_parse_filter_string(optarg,
473 BTRFS_LIST_FILTER_CGEN);
480 ret = btrfs_list_parse_sort_string(optarg,
495 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
498 if (check_argc_exact(argc - optind, 1)) {
503 subvol = argv[optind];
504 fd = open_file_or_dir(subvol, &dirstream);
507 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
511 ret = btrfs_list_get_path_rootid(fd, &top_id);
513 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
518 btrfs_list_setup_filter(&filter_set,
519 BTRFS_LIST_FILTER_FULL_PATH,
521 else if (is_only_in_path)
522 btrfs_list_setup_filter(&filter_set,
523 BTRFS_LIST_FILTER_TOPID_EQUAL,
526 /* by default we shall print the following columns*/
527 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
528 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
529 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
530 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
533 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
534 BTRFS_LIST_LAYOUT_TABLE,
535 !is_list_all && !is_only_in_path, NULL);
537 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
538 BTRFS_LIST_LAYOUT_DEFAULT,
539 !is_list_all && !is_only_in_path, NULL);
542 close_file_or_dir(fd, dirstream);
544 btrfs_list_free_filter_set(filter_set);
546 btrfs_list_free_comparer_set(comparer_set);
548 usage(cmd_subvol_list_usage);
552 static const char * const cmd_snapshot_usage[] = {
553 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
554 "Create a snapshot of the subvolume",
555 "Create a writable/readonly snapshot of the subvolume <source> with",
556 "the name <name> in the <dest> directory. If only <dest> is given,",
557 "the subvolume will be named the basename of <source>.",
559 "-r create a readonly snapshot",
560 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
561 " option can be given multiple times.",
565 static int cmd_snapshot(int argc, char **argv)
569 int fd = -1, fddst = -1;
570 int len, readonly = 0;
571 char *dupname = NULL;
575 struct btrfs_ioctl_vol_args_v2 args;
576 struct btrfs_qgroup_inherit *inherit = NULL;
577 DIR *dirstream1 = NULL, *dirstream2 = NULL;
580 memset(&args, 0, sizeof(args));
582 int c = getopt(argc, argv, "c:i:r");
588 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
595 res = qgroup_inherit_add_group(&inherit, optarg);
605 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
612 usage(cmd_snapshot_usage);
616 if (check_argc_exact(argc - optind, 2))
617 usage(cmd_snapshot_usage);
619 subvol = argv[optind];
620 dst = argv[optind + 1];
622 retval = 1; /* failure */
623 res = test_issubvolume(subvol);
625 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
629 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
633 res = test_isdir(dst);
635 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
640 dupname = strdup(subvol);
641 newname = basename(dupname);
644 dupname = strdup(dst);
645 newname = basename(dupname);
646 dupdir = strdup(dst);
647 dstdir = dirname(dupdir);
650 if (!test_issubvolname(newname)) {
651 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
656 len = strlen(newname);
657 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
658 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
663 fddst = open_file_or_dir(dstdir, &dirstream1);
665 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
669 fd = open_file_or_dir(subvol, &dirstream2);
671 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
676 args.flags |= BTRFS_SUBVOL_RDONLY;
677 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
678 subvol, dstdir, newname);
680 printf("Create a snapshot of '%s' in '%s/%s'\n",
681 subvol, dstdir, newname);
686 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
687 args.size = qgroup_inherit_size(inherit);
688 args.qgroup_inherit = inherit;
690 strncpy_null(args.name, newname);
692 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
695 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
696 subvol, strerror(errno));
700 retval = 0; /* success */
703 close_file_or_dir(fddst, dirstream1);
704 close_file_or_dir(fd, dirstream2);
712 static const char * const cmd_subvol_get_default_usage[] = {
713 "btrfs subvolume get-default <path>",
714 "Get the default subvolume of a filesystem",
718 static int cmd_subvol_get_default(int argc, char **argv)
723 struct btrfs_list_filter_set *filter_set;
725 DIR *dirstream = NULL;
727 if (check_argc_exact(argc, 2))
728 usage(cmd_subvol_get_default_usage);
731 fd = open_file_or_dir(subvol, &dirstream);
733 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
737 ret = btrfs_list_get_default_subvolume(fd, &default_id);
739 fprintf(stderr, "ERROR: can't perform the search - %s\n",
745 if (default_id == 0) {
746 fprintf(stderr, "ERROR: 'default' dir item not found\n");
750 /* no need to resolve roots if FS_TREE is default */
751 if (default_id == BTRFS_FS_TREE_OBJECTID) {
752 printf("ID 5 (FS_TREE)\n");
757 filter_set = btrfs_list_alloc_filter_set();
758 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
761 /* by default we shall print the following columns*/
762 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
763 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
764 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
765 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
767 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
768 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
771 btrfs_list_free_filter_set(filter_set);
773 close_file_or_dir(fd, dirstream);
777 static const char * const cmd_subvol_set_default_usage[] = {
778 "btrfs subvolume set-default <subvolid> <path>",
779 "Set the default subvolume of a filesystem",
783 static int cmd_subvol_set_default(int argc, char **argv)
789 DIR *dirstream = NULL;
791 if (check_argc_exact(argc, 3))
792 usage(cmd_subvol_set_default_usage);
797 objectid = arg_strtou64(subvolid);
799 fd = open_file_or_dir(path, &dirstream);
801 fprintf(stderr, "ERROR: can't access '%s'\n", path);
805 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
807 close_file_or_dir(fd, dirstream);
809 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
816 static const char * const cmd_find_new_usage[] = {
817 "btrfs subvolume find-new <path> <lastgen>",
818 "List the recently modified files in a filesystem",
822 static int cmd_find_new(int argc, char **argv)
828 DIR *dirstream = NULL;
830 if (check_argc_exact(argc, 3))
831 usage(cmd_find_new_usage);
834 last_gen = arg_strtou64(argv[2]);
836 ret = test_issubvolume(subvol);
838 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
842 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
846 fd = open_file_or_dir(subvol, &dirstream);
848 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
852 ret = ioctl(fd, BTRFS_IOC_SYNC);
854 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
855 subvol, strerror(errno));
856 close_file_or_dir(fd, dirstream);
860 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
861 close_file_or_dir(fd, dirstream);
865 static const char * const cmd_subvol_show_usage[] = {
866 "btrfs subvolume show <subvol-path>",
867 "Show more information of the subvolume",
871 static int cmd_subvol_show(int argc, char **argv)
873 struct root_info get_ri;
874 struct btrfs_list_filter_set *filter_set;
876 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
877 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
878 char raw_prefix[] = "\t\t\t\t";
880 int fd = -1, mntfd = -1;
882 DIR *dirstream1 = NULL, *dirstream2 = NULL;
884 if (check_argc_exact(argc, 2))
885 usage(cmd_subvol_show_usage);
887 fullpath = realpath(argv[1], NULL);
889 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
890 argv[1], strerror(errno));
894 ret = test_issubvolume(fullpath);
896 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
900 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
905 ret = find_mount_root(fullpath, &mnt);
907 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
908 "%s\n", fullpath, strerror(-ret));
913 "ERROR: %s doesn't belong to btrfs mount point\n",
918 svpath = get_subvol_name(mnt, fullpath);
920 fd = open_file_or_dir(fullpath, &dirstream1);
922 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
926 ret = btrfs_list_get_path_rootid(fd, &sv_id);
928 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
933 mntfd = open_file_or_dir(mnt, &dirstream2);
935 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
939 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
941 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
945 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
946 printf("%s is btrfs root\n", fullpath);
950 memset(&get_ri, 0, sizeof(get_ri));
951 get_ri.root_id = sv_id;
953 ret = btrfs_get_subvol(mntfd, &get_ri);
955 fprintf(stderr, "ERROR: can't find '%s'\n",
961 printf("%s\n", fullpath);
962 printf("\tName: \t\t\t%s\n", get_ri.name);
964 if (uuid_is_null(get_ri.uuid))
965 strcpy(uuidparse, "-");
967 uuid_unparse(get_ri.uuid, uuidparse);
968 printf("\tuuid: \t\t\t%s\n", uuidparse);
970 if (uuid_is_null(get_ri.puuid))
971 strcpy(uuidparse, "-");
973 uuid_unparse(get_ri.puuid, uuidparse);
974 printf("\tParent uuid: \t\t%s\n", uuidparse);
979 localtime_r(&get_ri.otime, &tm);
980 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
983 printf("\tCreation time: \t\t%s\n", tstr);
985 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
986 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
987 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
988 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
989 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
991 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
992 printf("\tFlags: \t\t\treadonly\n");
994 printf("\tFlags: \t\t\t-\n");
996 /* print the snapshots of the given subvol if any*/
997 printf("\tSnapshot(s):\n");
998 filter_set = btrfs_list_alloc_filter_set();
999 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1000 (u64)(unsigned long)get_ri.uuid);
1001 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1002 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1008 free(get_ri.full_path);
1009 btrfs_list_free_filter_set(filter_set);
1012 close_file_or_dir(fd, dirstream1);
1013 close_file_or_dir(mntfd, dirstream2);
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 ongoing deletion requests",
1025 "are complete. This may take long if new deleted subvolumes appear during",
1026 "the sleep interval.",
1028 "-s <N> sleep N seconds between checks (default: 1)",
1032 static int is_subvolume_cleaned(int fd, u64 subvolid)
1035 struct btrfs_ioctl_search_args args;
1036 struct btrfs_ioctl_search_key *sk = &args.key;
1038 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1039 sk->min_objectid = subvolid;
1040 sk->max_objectid = subvolid;
1041 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1042 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1044 sk->max_offset = (u64)-1;
1045 sk->min_transid = 0;
1046 sk->max_transid = (u64)-1;
1049 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1053 if (sk->nr_items == 0)
1060 * If we're looking for any dead subvolume, take a shortcut and look
1061 * for any ORPHAN_ITEMs in the tree root
1063 static int fs_has_dead_subvolumes(int fd)
1066 struct btrfs_ioctl_search_args args;
1067 struct btrfs_ioctl_search_key *sk = &args.key;
1068 struct btrfs_ioctl_search_header sh;
1069 u64 min_subvolid = 0;
1072 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1073 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1074 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1075 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1076 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1077 sk->min_offset = min_subvolid;
1078 sk->max_offset = (u64)-1;
1079 sk->min_transid = 0;
1080 sk->max_transid = (u64)-1;
1083 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1090 memcpy(&sh, args.buf, sizeof(sh));
1091 min_subvolid = sh.offset;
1094 * Verify that the root item is really there and we haven't hit
1097 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1098 sk->min_objectid = min_subvolid;
1099 sk->max_objectid = min_subvolid;
1100 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1101 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1103 sk->max_offset = (u64)-1;
1104 sk->min_transid = 0;
1105 sk->max_transid = (u64)-1;
1108 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1113 * Stale orphan, try the next one
1115 if (!sk->nr_items) {
1123 static int cmd_subvol_sync(int argc, char **argv)
1128 DIR *dirstream = NULL;
1132 int sleep_interval = 1;
1136 int c = getopt(argc, argv, "s:");
1143 sleep_interval = atoi(argv[optind]);
1144 if (sleep_interval < 1) {
1146 "ERROR: invalid sleep interval %s\n",
1153 usage(cmd_subvol_sync_usage);
1157 if (check_argc_min(argc - optind, 1))
1158 usage(cmd_subvol_sync_usage);
1160 fd = open_file_or_dir(argv[optind], &dirstream);
1162 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1168 id_count = argc - optind;
1175 ret = fs_has_dead_subvolumes(fd);
1177 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1184 sleep(sleep_interval);
1189 * Wait only for the requested ones
1191 ids = (u64*)malloc(sizeof(u64) * id_count);
1194 fprintf(stderr, "ERROR: not enough memory\n");
1199 for (i = 0; i < id_count; i++) {
1203 arg = argv[optind + i];
1205 id = strtoull(arg, NULL, 10);
1207 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1212 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1213 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1221 remaining = id_count;
1223 for (i = 0; i < id_count; i++) {
1226 ret = is_subvolume_cleaned(fd, ids[i]);
1228 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1233 printf("Subvolume id %llu is gone\n", ids[i]);
1240 sleep(sleep_interval);
1245 close_file_or_dir(fd, dirstream);
1250 const struct cmd_group subvolume_cmd_group = {
1251 subvolume_cmd_group_usage, NULL, {
1252 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1253 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1254 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1255 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1256 { "get-default", cmd_subvol_get_default,
1257 cmd_subvol_get_default_usage, NULL, 0 },
1258 { "set-default", cmd_subvol_set_default,
1259 cmd_subvol_set_default_usage, NULL, 0 },
1260 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1261 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1262 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1267 int cmd_subvolume(int argc, char **argv)
1269 return handle_command_group(&subvolume_cmd_group, argc, argv);