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);
980 localtime_r(&get_ri.otime, &tm);
981 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
984 printf("\tCreation time: \t\t%s\n", tstr);
986 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
987 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
988 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
989 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
990 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
992 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
993 printf("\tFlags: \t\t\treadonly\n");
995 printf("\tFlags: \t\t\t-\n");
997 /* print the snapshots of the given subvol if any*/
998 printf("\tSnapshot(s):\n");
999 filter_set = btrfs_list_alloc_filter_set();
1000 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1001 (u64)(unsigned long)get_ri.uuid);
1002 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1003 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1009 free(get_ri.full_path);
1010 btrfs_list_free_filter_set(filter_set);
1013 close_file_or_dir(fd, dirstream1);
1014 close_file_or_dir(mntfd, dirstream2);
1020 static const char * const cmd_subvol_sync_usage[] = {
1021 "btrfs subvolume sync <path> [<subvol-id>...]",
1022 "Wait until given subvolume(s) are completely removed from the filesystem.",
1023 "Wait until given subvolume(s) are completely removed from the filesystem",
1025 "If no subvolume id is given, wait until all ongoing deletion requests",
1026 "are complete. This may take long if new deleted subvolumes appear during",
1027 "the sleep interval.",
1029 "-s <N> sleep N seconds between checks (default: 1)",
1033 static int is_subvolume_cleaned(int fd, u64 subvolid)
1036 struct btrfs_ioctl_search_args args;
1037 struct btrfs_ioctl_search_key *sk = &args.key;
1039 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1040 sk->min_objectid = subvolid;
1041 sk->max_objectid = subvolid;
1042 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1043 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1045 sk->max_offset = (u64)-1;
1046 sk->min_transid = 0;
1047 sk->max_transid = (u64)-1;
1050 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1054 if (sk->nr_items == 0)
1061 * If we're looking for any dead subvolume, take a shortcut and look
1062 * for any ORPHAN_ITEMs in the tree root
1064 static int fs_has_dead_subvolumes(int fd)
1067 struct btrfs_ioctl_search_args args;
1068 struct btrfs_ioctl_search_key *sk = &args.key;
1069 struct btrfs_ioctl_search_header sh;
1070 u64 min_subvolid = 0;
1073 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1074 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1075 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1076 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1077 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1078 sk->min_offset = min_subvolid;
1079 sk->max_offset = (u64)-1;
1080 sk->min_transid = 0;
1081 sk->max_transid = (u64)-1;
1084 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1091 memcpy(&sh, args.buf, sizeof(sh));
1092 min_subvolid = sh.offset;
1095 * Verify that the root item is really there and we haven't hit
1098 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1099 sk->min_objectid = min_subvolid;
1100 sk->max_objectid = min_subvolid;
1101 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1102 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1104 sk->max_offset = (u64)-1;
1105 sk->min_transid = 0;
1106 sk->max_transid = (u64)-1;
1109 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1114 * Stale orphan, try the next one
1116 if (!sk->nr_items) {
1124 static int cmd_subvol_sync(int argc, char **argv)
1129 DIR *dirstream = NULL;
1133 int sleep_interval = 1;
1137 int c = getopt(argc, argv, "s:");
1144 sleep_interval = atoi(argv[optind]);
1145 if (sleep_interval < 1) {
1147 "ERROR: invalid sleep interval %s\n",
1154 usage(cmd_subvol_sync_usage);
1158 if (check_argc_min(argc - optind, 1))
1159 usage(cmd_subvol_sync_usage);
1161 fd = open_file_or_dir(argv[optind], &dirstream);
1163 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1169 id_count = argc - optind;
1176 ret = fs_has_dead_subvolumes(fd);
1178 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1185 sleep(sleep_interval);
1190 * Wait only for the requested ones
1192 ids = (u64*)malloc(sizeof(u64) * id_count);
1195 fprintf(stderr, "ERROR: not enough memory\n");
1200 for (i = 0; i < id_count; i++) {
1204 arg = argv[optind + i];
1206 id = strtoull(arg, NULL, 10);
1208 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1213 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1214 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1222 remaining = id_count;
1224 for (i = 0; i < id_count; i++) {
1227 ret = is_subvolume_cleaned(fd, ids[i]);
1229 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1234 printf("Subvolume id %llu is gone\n", ids[i]);
1241 sleep(sleep_interval);
1246 close_file_or_dir(fd, dirstream);
1251 const struct cmd_group subvolume_cmd_group = {
1252 subvolume_cmd_group_usage, NULL, {
1253 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1254 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1255 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1256 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1257 { "get-default", cmd_subvol_get_default,
1258 cmd_subvol_get_default_usage, NULL, 0 },
1259 { "set-default", cmd_subvol_set_default,
1260 cmd_subvol_set_default_usage, NULL, 0 },
1261 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1262 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1263 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1268 int cmd_subvolume(int argc, char **argv)
1270 return handle_command_group(&subvolume_cmd_group, argc, argv);