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;
226 static const struct option long_options[] = {
227 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
228 {"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;
395 int is_tab_result = 0;
397 int is_only_in_path = 0;
398 DIR *dirstream = NULL;
400 filter_set = btrfs_list_alloc_filter_set();
401 comparer_set = btrfs_list_alloc_comparer_set();
406 static const struct option long_options[] = {
407 {"sort", required_argument, NULL, 'S'},
411 c = getopt_long(argc, argv,
412 "acdgopqsurRG:C:t", long_options, NULL);
418 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
424 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
427 btrfs_list_setup_filter(&filter_set,
428 BTRFS_LIST_FILTER_DELETED,
432 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
441 btrfs_list_setup_filter(&filter_set,
442 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
444 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
445 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
448 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
451 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
454 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
457 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
460 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
461 ret = btrfs_list_parse_filter_string(optarg,
463 BTRFS_LIST_FILTER_GEN);
471 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
472 ret = btrfs_list_parse_filter_string(optarg,
474 BTRFS_LIST_FILTER_CGEN);
481 ret = btrfs_list_parse_sort_string(optarg,
496 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
499 if (check_argc_exact(argc - optind, 1)) {
504 subvol = argv[optind];
505 fd = open_file_or_dir(subvol, &dirstream);
508 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
512 ret = btrfs_list_get_path_rootid(fd, &top_id);
514 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
519 btrfs_list_setup_filter(&filter_set,
520 BTRFS_LIST_FILTER_FULL_PATH,
522 else if (is_only_in_path)
523 btrfs_list_setup_filter(&filter_set,
524 BTRFS_LIST_FILTER_TOPID_EQUAL,
527 /* by default we shall print the following columns*/
528 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
529 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
530 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
531 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
534 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
535 BTRFS_LIST_LAYOUT_TABLE,
536 !is_list_all && !is_only_in_path, NULL);
538 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
539 BTRFS_LIST_LAYOUT_DEFAULT,
540 !is_list_all && !is_only_in_path, NULL);
543 close_file_or_dir(fd, dirstream);
545 btrfs_list_free_filter_set(filter_set);
547 btrfs_list_free_comparer_set(comparer_set);
549 usage(cmd_subvol_list_usage);
553 static const char * const cmd_snapshot_usage[] = {
554 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
555 "Create a snapshot of the subvolume",
556 "Create a writable/readonly snapshot of the subvolume <source> with",
557 "the name <name> in the <dest> directory. If only <dest> is given,",
558 "the subvolume will be named the basename of <source>.",
560 "-r create a readonly snapshot",
561 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
562 " option can be given multiple times.",
566 static int cmd_snapshot(int argc, char **argv)
570 int fd = -1, fddst = -1;
571 int len, readonly = 0;
572 char *dupname = NULL;
576 struct btrfs_ioctl_vol_args_v2 args;
577 struct btrfs_qgroup_inherit *inherit = NULL;
578 DIR *dirstream1 = NULL, *dirstream2 = NULL;
581 memset(&args, 0, sizeof(args));
583 int c = getopt(argc, argv, "c:i:r");
589 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
596 res = qgroup_inherit_add_group(&inherit, optarg);
606 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
613 usage(cmd_snapshot_usage);
617 if (check_argc_exact(argc - optind, 2))
618 usage(cmd_snapshot_usage);
620 subvol = argv[optind];
621 dst = argv[optind + 1];
623 retval = 1; /* failure */
624 res = test_issubvolume(subvol);
626 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
630 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
634 res = test_isdir(dst);
636 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
641 dupname = strdup(subvol);
642 newname = basename(dupname);
645 dupname = strdup(dst);
646 newname = basename(dupname);
647 dupdir = strdup(dst);
648 dstdir = dirname(dupdir);
651 if (!test_issubvolname(newname)) {
652 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
657 len = strlen(newname);
658 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
659 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
664 fddst = open_file_or_dir(dstdir, &dirstream1);
666 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
670 fd = open_file_or_dir(subvol, &dirstream2);
672 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
677 args.flags |= BTRFS_SUBVOL_RDONLY;
678 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
679 subvol, dstdir, newname);
681 printf("Create a snapshot of '%s' in '%s/%s'\n",
682 subvol, dstdir, newname);
687 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
688 args.size = qgroup_inherit_size(inherit);
689 args.qgroup_inherit = inherit;
691 strncpy_null(args.name, newname);
693 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
696 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
697 subvol, strerror(errno));
701 retval = 0; /* success */
704 close_file_or_dir(fddst, dirstream1);
705 close_file_or_dir(fd, dirstream2);
713 static const char * const cmd_subvol_get_default_usage[] = {
714 "btrfs subvolume get-default <path>",
715 "Get the default subvolume of a filesystem",
719 static int cmd_subvol_get_default(int argc, char **argv)
724 struct btrfs_list_filter_set *filter_set;
726 DIR *dirstream = NULL;
728 if (check_argc_exact(argc, 2))
729 usage(cmd_subvol_get_default_usage);
732 fd = open_file_or_dir(subvol, &dirstream);
734 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
738 ret = btrfs_list_get_default_subvolume(fd, &default_id);
740 fprintf(stderr, "ERROR: can't perform the search - %s\n",
746 if (default_id == 0) {
747 fprintf(stderr, "ERROR: 'default' dir item not found\n");
751 /* no need to resolve roots if FS_TREE is default */
752 if (default_id == BTRFS_FS_TREE_OBJECTID) {
753 printf("ID 5 (FS_TREE)\n");
758 filter_set = btrfs_list_alloc_filter_set();
759 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
762 /* by default we shall print the following columns*/
763 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
764 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
765 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
766 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
768 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
769 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
772 btrfs_list_free_filter_set(filter_set);
774 close_file_or_dir(fd, dirstream);
778 static const char * const cmd_subvol_set_default_usage[] = {
779 "btrfs subvolume set-default <subvolid> <path>",
780 "Set the default subvolume of a filesystem",
784 static int cmd_subvol_set_default(int argc, char **argv)
790 DIR *dirstream = NULL;
792 if (check_argc_exact(argc, 3))
793 usage(cmd_subvol_set_default_usage);
798 objectid = arg_strtou64(subvolid);
800 fd = open_file_or_dir(path, &dirstream);
802 fprintf(stderr, "ERROR: can't access '%s'\n", path);
806 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
808 close_file_or_dir(fd, dirstream);
810 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
817 static const char * const cmd_find_new_usage[] = {
818 "btrfs subvolume find-new <path> <lastgen>",
819 "List the recently modified files in a filesystem",
823 static int cmd_find_new(int argc, char **argv)
829 DIR *dirstream = NULL;
831 if (check_argc_exact(argc, 3))
832 usage(cmd_find_new_usage);
835 last_gen = arg_strtou64(argv[2]);
837 ret = test_issubvolume(subvol);
839 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
843 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
847 fd = open_file_or_dir(subvol, &dirstream);
849 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
853 ret = ioctl(fd, BTRFS_IOC_SYNC);
855 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
856 subvol, strerror(errno));
857 close_file_or_dir(fd, dirstream);
861 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
862 close_file_or_dir(fd, dirstream);
866 static const char * const cmd_subvol_show_usage[] = {
867 "btrfs subvolume show <subvol-path>",
868 "Show more information of the subvolume",
872 static int cmd_subvol_show(int argc, char **argv)
874 struct root_info get_ri;
875 struct btrfs_list_filter_set *filter_set;
877 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
878 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
879 char raw_prefix[] = "\t\t\t\t";
881 int fd = -1, mntfd = -1;
883 DIR *dirstream1 = NULL, *dirstream2 = NULL;
885 if (check_argc_exact(argc, 2))
886 usage(cmd_subvol_show_usage);
888 fullpath = realpath(argv[1], NULL);
890 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
891 argv[1], strerror(errno));
895 ret = test_issubvolume(fullpath);
897 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
901 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
906 ret = find_mount_root(fullpath, &mnt);
908 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
909 "%s\n", fullpath, strerror(-ret));
914 "ERROR: %s doesn't belong to btrfs mount point\n",
919 svpath = get_subvol_name(mnt, fullpath);
921 fd = open_file_or_dir(fullpath, &dirstream1);
923 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
927 ret = btrfs_list_get_path_rootid(fd, &sv_id);
929 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
934 mntfd = open_file_or_dir(mnt, &dirstream2);
936 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
940 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
942 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
946 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
947 printf("%s is btrfs root\n", fullpath);
951 memset(&get_ri, 0, sizeof(get_ri));
952 get_ri.root_id = sv_id;
954 ret = btrfs_get_subvol(mntfd, &get_ri);
956 fprintf(stderr, "ERROR: can't find '%s'\n",
962 printf("%s\n", fullpath);
963 printf("\tName: \t\t\t%s\n", get_ri.name);
965 if (uuid_is_null(get_ri.uuid))
966 strcpy(uuidparse, "-");
968 uuid_unparse(get_ri.uuid, uuidparse);
969 printf("\tUUID: \t\t\t%s\n", uuidparse);
971 if (uuid_is_null(get_ri.puuid))
972 strcpy(uuidparse, "-");
974 uuid_unparse(get_ri.puuid, uuidparse);
975 printf("\tParent UUID: \t\t%s\n", uuidparse);
977 if (uuid_is_null(get_ri.ruuid))
978 strcpy(uuidparse, "-");
980 uuid_unparse(get_ri.ruuid, uuidparse);
981 printf("\tReceived UUID: \t\t%s\n", uuidparse);
986 localtime_r(&get_ri.otime, &tm);
987 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
990 printf("\tCreation time: \t\t%s\n", tstr);
992 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
993 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
994 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
995 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
996 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
998 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
999 printf("\tFlags: \t\t\treadonly\n");
1001 printf("\tFlags: \t\t\t-\n");
1003 /* print the snapshots of the given subvol if any*/
1004 printf("\tSnapshot(s):\n");
1005 filter_set = btrfs_list_alloc_filter_set();
1006 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1007 (u64)(unsigned long)get_ri.uuid);
1008 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1009 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1015 free(get_ri.full_path);
1016 btrfs_list_free_filter_set(filter_set);
1019 close_file_or_dir(fd, dirstream1);
1020 close_file_or_dir(mntfd, dirstream2);
1026 static const char * const cmd_subvol_sync_usage[] = {
1027 "btrfs subvolume sync <path> [<subvol-id>...]",
1028 "Wait until given subvolume(s) are completely removed from the filesystem.",
1029 "Wait until given subvolume(s) are completely removed from the filesystem",
1031 "If no subvolume id is given, wait until all ongoing deletion requests",
1032 "are complete. This may take long if new deleted subvolumes appear during",
1033 "the sleep interval.",
1035 "-s <N> sleep N seconds between checks (default: 1)",
1039 static int is_subvolume_cleaned(int fd, u64 subvolid)
1042 struct btrfs_ioctl_search_args args;
1043 struct btrfs_ioctl_search_key *sk = &args.key;
1045 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1046 sk->min_objectid = subvolid;
1047 sk->max_objectid = subvolid;
1048 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1049 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1051 sk->max_offset = (u64)-1;
1052 sk->min_transid = 0;
1053 sk->max_transid = (u64)-1;
1056 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1060 if (sk->nr_items == 0)
1067 * If we're looking for any dead subvolume, take a shortcut and look
1068 * for any ORPHAN_ITEMs in the tree root
1070 static int fs_has_dead_subvolumes(int fd)
1073 struct btrfs_ioctl_search_args args;
1074 struct btrfs_ioctl_search_key *sk = &args.key;
1075 struct btrfs_ioctl_search_header sh;
1076 u64 min_subvolid = 0;
1079 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1080 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1081 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1083 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->min_offset = min_subvolid;
1085 sk->max_offset = (u64)-1;
1086 sk->min_transid = 0;
1087 sk->max_transid = (u64)-1;
1090 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1097 memcpy(&sh, args.buf, sizeof(sh));
1098 min_subvolid = sh.offset;
1101 * Verify that the root item is really there and we haven't hit
1104 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1105 sk->min_objectid = min_subvolid;
1106 sk->max_objectid = min_subvolid;
1107 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1108 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1110 sk->max_offset = (u64)-1;
1111 sk->min_transid = 0;
1112 sk->max_transid = (u64)-1;
1115 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1120 * Stale orphan, try the next one
1122 if (!sk->nr_items) {
1130 static int cmd_subvol_sync(int argc, char **argv)
1135 DIR *dirstream = NULL;
1139 int sleep_interval = 1;
1143 int c = getopt(argc, argv, "s:");
1150 sleep_interval = atoi(argv[optind]);
1151 if (sleep_interval < 1) {
1153 "ERROR: invalid sleep interval %s\n",
1160 usage(cmd_subvol_sync_usage);
1164 if (check_argc_min(argc - optind, 1))
1165 usage(cmd_subvol_sync_usage);
1167 fd = open_file_or_dir(argv[optind], &dirstream);
1169 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1175 id_count = argc - optind;
1182 ret = fs_has_dead_subvolumes(fd);
1184 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1191 sleep(sleep_interval);
1196 * Wait only for the requested ones
1198 ids = (u64*)malloc(sizeof(u64) * id_count);
1201 fprintf(stderr, "ERROR: not enough memory\n");
1206 for (i = 0; i < id_count; i++) {
1210 arg = argv[optind + i];
1212 id = strtoull(arg, NULL, 10);
1214 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1219 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1220 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1228 remaining = id_count;
1230 for (i = 0; i < id_count; i++) {
1233 ret = is_subvolume_cleaned(fd, ids[i]);
1235 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1240 printf("Subvolume id %llu is gone\n", ids[i]);
1247 sleep(sleep_interval);
1252 close_file_or_dir(fd, dirstream);
1257 static const char subvolume_cmd_group_info[] =
1258 "manage subvolumes: create, delete, list, etc";
1260 const struct cmd_group subvolume_cmd_group = {
1261 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1262 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1263 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1264 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1265 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1266 { "get-default", cmd_subvol_get_default,
1267 cmd_subvol_get_default_usage, NULL, 0 },
1268 { "set-default", cmd_subvol_set_default,
1269 cmd_subvol_set_default_usage, NULL, 0 },
1270 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1271 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1272 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1277 int cmd_subvolume(int argc, char **argv)
1279 return handle_command_group(&subvolume_cmd_group, argc, argv);