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)
211 int res, len, e, ret = 0;
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 if (!test_issubvolname(vname)) {
291 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
298 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
299 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
305 fd = open_file_or_dir(dname, &dirstream);
307 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
312 printf("Delete subvolume (%s): '%s/%s'\n",
313 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
314 ? "commit" : "no-commit", dname, vname);
315 strncpy_null(args.name, vname);
316 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
320 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
321 dname, vname, strerror(e));
326 if (commit_mode == 1) {
327 res = wait_for_commit(fd);
330 "ERROR: unable to wait for commit after '%s': %s\n",
331 path, strerror(errno));
343 close_file_or_dir(fd, dirstream);
344 /* avoid double free */
350 if (commit_mode == 2 && fd != -1) {
351 res = wait_for_commit(fd);
354 "ERROR: unable to do final sync: %s\n",
359 close_file_or_dir(fd, dirstream);
366 * - uppercase for filters and sort options
367 * - lowercase for enabling specific items in the output
369 static const char * const cmd_subvol_list_usage[] = {
370 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
371 "[--sort=gen,ogen,rootid,path] <path>",
372 "List subvolumes (and snapshots)",
374 "-p print parent ID",
375 "-a print all the subvolumes in the filesystem and",
376 " distinguish absolute and relative path with respect",
377 " to the given <path>",
378 "-c print the ogeneration of the subvolume",
379 "-g print the generation of the subvolume",
380 "-o print only subvolumes below specified path",
381 "-u print the uuid of subvolumes (and snapshots)",
382 "-q print the parent uuid of the snapshots",
383 "-R print the uuid of the received snapshots",
384 "-t print the result as a table",
385 "-s list snapshots only in the filesystem",
386 "-r list readonly subvolumes (including snapshots)",
387 "-d list deleted subvolumes that are not yet cleaned",
389 " filter the subvolumes by generation",
390 " (+value: >= value; -value: <= value; value: = value)",
392 " filter the subvolumes by ogeneration",
393 " (+value: >= value; -value: <= value; value: = value)",
394 "--sort=gen,ogen,rootid,path",
395 " list the subvolume in order of gen, ogen, rootid or path",
396 " you also can add '+' or '-' in front of each items.",
397 " (+:ascending, -:descending, ascending default)",
401 static int cmd_subvol_list(int argc, char **argv)
403 struct btrfs_list_filter_set *filter_set;
404 struct btrfs_list_comparer_set *comparer_set;
408 int ret = -1, uerr = 0;
411 int is_tab_result = 0;
413 int is_only_in_path = 0;
414 struct option long_options[] = {
415 {"sort", 1, NULL, 'S'},
418 DIR *dirstream = NULL;
420 filter_set = btrfs_list_alloc_filter_set();
421 comparer_set = btrfs_list_alloc_comparer_set();
425 c = getopt_long(argc, argv,
426 "acdgopqsurRG:C:t", long_options, NULL);
432 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
438 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
441 btrfs_list_setup_filter(&filter_set,
442 BTRFS_LIST_FILTER_DELETED,
446 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
455 btrfs_list_setup_filter(&filter_set,
456 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
458 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
459 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
462 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
465 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
468 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
471 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
474 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
475 ret = btrfs_list_parse_filter_string(optarg,
477 BTRFS_LIST_FILTER_GEN);
485 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
486 ret = btrfs_list_parse_filter_string(optarg,
488 BTRFS_LIST_FILTER_CGEN);
495 ret = btrfs_list_parse_sort_string(optarg,
510 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
513 if (check_argc_exact(argc - optind, 1)) {
518 subvol = argv[optind];
519 fd = open_file_or_dir(subvol, &dirstream);
522 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
526 ret = btrfs_list_get_path_rootid(fd, &top_id);
528 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
533 btrfs_list_setup_filter(&filter_set,
534 BTRFS_LIST_FILTER_FULL_PATH,
536 else if (is_only_in_path)
537 btrfs_list_setup_filter(&filter_set,
538 BTRFS_LIST_FILTER_TOPID_EQUAL,
541 /* by default we shall print the following columns*/
542 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
543 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
544 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
545 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
548 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
549 BTRFS_LIST_LAYOUT_TABLE,
550 !is_list_all && !is_only_in_path, NULL);
552 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
553 BTRFS_LIST_LAYOUT_DEFAULT,
554 !is_list_all && !is_only_in_path, NULL);
557 close_file_or_dir(fd, dirstream);
559 btrfs_list_free_filter_set(filter_set);
561 btrfs_list_free_comparer_set(comparer_set);
563 usage(cmd_subvol_list_usage);
567 static const char * const cmd_snapshot_usage[] = {
568 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
569 "Create a snapshot of the subvolume",
570 "Create a writable/readonly snapshot of the subvolume <source> with",
571 "the name <name> in the <dest> directory. If only <dest> is given,",
572 "the subvolume will be named the basename of <source>.",
574 "-r create a readonly snapshot",
575 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
576 " option can be given multiple times.",
580 static int cmd_snapshot(int argc, char **argv)
584 int fd = -1, fddst = -1;
585 int len, readonly = 0;
586 char *dupname = NULL;
590 struct btrfs_ioctl_vol_args_v2 args;
591 struct btrfs_qgroup_inherit *inherit = NULL;
592 DIR *dirstream1 = NULL, *dirstream2 = NULL;
595 memset(&args, 0, sizeof(args));
597 int c = getopt(argc, argv, "c:i:r");
603 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
610 res = qgroup_inherit_add_group(&inherit, optarg);
620 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
627 usage(cmd_snapshot_usage);
631 if (check_argc_exact(argc - optind, 2))
632 usage(cmd_snapshot_usage);
634 subvol = argv[optind];
635 dst = argv[optind + 1];
637 retval = 1; /* failure */
638 res = test_issubvolume(subvol);
640 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
644 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
648 res = test_isdir(dst);
650 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
655 dupname = strdup(subvol);
656 newname = basename(dupname);
659 dupname = strdup(dst);
660 newname = basename(dupname);
661 dupdir = strdup(dst);
662 dstdir = dirname(dupdir);
665 if (!test_issubvolname(newname)) {
666 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
671 len = strlen(newname);
672 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
673 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
678 fddst = open_file_or_dir(dstdir, &dirstream1);
680 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
684 fd = open_file_or_dir(subvol, &dirstream2);
686 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
691 args.flags |= BTRFS_SUBVOL_RDONLY;
692 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
693 subvol, dstdir, newname);
695 printf("Create a snapshot of '%s' in '%s/%s'\n",
696 subvol, dstdir, newname);
701 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
702 args.size = qgroup_inherit_size(inherit);
703 args.qgroup_inherit = inherit;
705 strncpy_null(args.name, newname);
707 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
710 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
711 subvol, strerror(errno));
715 retval = 0; /* success */
718 close_file_or_dir(fddst, dirstream1);
719 close_file_or_dir(fd, dirstream2);
727 static const char * const cmd_subvol_get_default_usage[] = {
728 "btrfs subvolume get-default <path>",
729 "Get the default subvolume of a filesystem",
733 static int cmd_subvol_get_default(int argc, char **argv)
738 struct btrfs_list_filter_set *filter_set;
740 DIR *dirstream = NULL;
742 if (check_argc_exact(argc, 2))
743 usage(cmd_subvol_get_default_usage);
746 fd = open_file_or_dir(subvol, &dirstream);
748 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
752 ret = btrfs_list_get_default_subvolume(fd, &default_id);
754 fprintf(stderr, "ERROR: can't perform the search - %s\n",
760 if (default_id == 0) {
761 fprintf(stderr, "ERROR: 'default' dir item not found\n");
765 /* no need to resolve roots if FS_TREE is default */
766 if (default_id == BTRFS_FS_TREE_OBJECTID) {
767 printf("ID 5 (FS_TREE)\n");
772 filter_set = btrfs_list_alloc_filter_set();
773 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
776 /* by default we shall print the following columns*/
777 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
778 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
779 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
780 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
782 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
783 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
786 btrfs_list_free_filter_set(filter_set);
788 close_file_or_dir(fd, dirstream);
792 static const char * const cmd_subvol_set_default_usage[] = {
793 "btrfs subvolume set-default <subvolid> <path>",
794 "Set the default subvolume of a filesystem",
798 static int cmd_subvol_set_default(int argc, char **argv)
804 DIR *dirstream = NULL;
806 if (check_argc_exact(argc, 3))
807 usage(cmd_subvol_set_default_usage);
812 objectid = arg_strtou64(subvolid);
814 fd = open_file_or_dir(path, &dirstream);
816 fprintf(stderr, "ERROR: can't access '%s'\n", path);
820 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
822 close_file_or_dir(fd, dirstream);
824 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
831 static const char * const cmd_find_new_usage[] = {
832 "btrfs subvolume find-new <path> <lastgen>",
833 "List the recently modified files in a filesystem",
837 static int cmd_find_new(int argc, char **argv)
843 DIR *dirstream = NULL;
845 if (check_argc_exact(argc, 3))
846 usage(cmd_find_new_usage);
849 last_gen = arg_strtou64(argv[2]);
851 ret = test_issubvolume(subvol);
853 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
857 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
861 fd = open_file_or_dir(subvol, &dirstream);
863 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
867 ret = ioctl(fd, BTRFS_IOC_SYNC);
869 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
870 subvol, strerror(errno));
871 close_file_or_dir(fd, dirstream);
875 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
876 close_file_or_dir(fd, dirstream);
880 static const char * const cmd_subvol_show_usage[] = {
881 "btrfs subvolume show <subvol-path>",
882 "Show more information of the subvolume",
886 static int cmd_subvol_show(int argc, char **argv)
888 struct root_info get_ri;
889 struct btrfs_list_filter_set *filter_set;
891 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
892 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
893 char raw_prefix[] = "\t\t\t\t";
895 int fd = -1, mntfd = -1;
897 DIR *dirstream1 = NULL, *dirstream2 = NULL;
899 if (check_argc_exact(argc, 2))
900 usage(cmd_subvol_show_usage);
902 fullpath = realpath(argv[1], NULL);
904 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
905 argv[1], strerror(errno));
909 ret = test_issubvolume(fullpath);
911 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
915 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
920 ret = find_mount_root(fullpath, &mnt);
922 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
923 "%s\n", fullpath, strerror(-ret));
928 "ERROR: %s doesn't belong to btrfs mount point\n",
933 svpath = get_subvol_name(mnt, fullpath);
935 fd = open_file_or_dir(fullpath, &dirstream1);
937 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
941 ret = btrfs_list_get_path_rootid(fd, &sv_id);
943 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
948 mntfd = open_file_or_dir(mnt, &dirstream2);
950 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
954 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
956 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
960 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
961 printf("%s is btrfs root\n", fullpath);
965 memset(&get_ri, 0, sizeof(get_ri));
966 get_ri.root_id = sv_id;
968 ret = btrfs_get_subvol(mntfd, &get_ri);
970 fprintf(stderr, "ERROR: can't find '%s'\n",
976 printf("%s\n", fullpath);
977 printf("\tName: \t\t\t%s\n", get_ri.name);
979 if (uuid_is_null(get_ri.uuid))
980 strcpy(uuidparse, "-");
982 uuid_unparse(get_ri.uuid, uuidparse);
983 printf("\tuuid: \t\t\t%s\n", uuidparse);
985 if (uuid_is_null(get_ri.puuid))
986 strcpy(uuidparse, "-");
988 uuid_unparse(get_ri.puuid, uuidparse);
989 printf("\tParent uuid: \t\t%s\n", uuidparse);
994 localtime_r(&get_ri.otime, &tm);
995 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
998 printf("\tCreation time: \t\t%s\n", tstr);
1000 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1001 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1002 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1003 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1004 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1006 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1007 printf("\tFlags: \t\t\treadonly\n");
1009 printf("\tFlags: \t\t\t-\n");
1011 /* print the snapshots of the given subvol if any*/
1012 printf("\tSnapshot(s):\n");
1013 filter_set = btrfs_list_alloc_filter_set();
1014 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1015 (u64)(unsigned long)get_ri.uuid);
1016 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1017 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1023 free(get_ri.full_path);
1024 btrfs_list_free_filter_set(filter_set);
1027 close_file_or_dir(fd, dirstream1);
1028 close_file_or_dir(mntfd, dirstream2);
1034 static const char * const cmd_subvol_sync_usage[] = {
1035 "btrfs subvolume sync <path> [<subvol-id>...]",
1036 "Wait until given subvolume(s) are completely removed from the filesystem.",
1037 "Wait until given subvolume(s) are completely removed from the filesystem",
1039 "If no subvolume id is given, wait until all ongoing deletion requests",
1040 "are complete. This may take long if new deleted subvolumes appear during",
1041 "the sleep interval.",
1043 "-s <N> sleep N seconds between checks (default: 1)",
1047 static int is_subvolume_cleaned(int fd, u64 subvolid)
1050 struct btrfs_ioctl_search_args args;
1051 struct btrfs_ioctl_search_key *sk = &args.key;
1053 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1054 sk->min_objectid = subvolid;
1055 sk->max_objectid = subvolid;
1056 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1057 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1059 sk->max_offset = (u64)-1;
1060 sk->min_transid = 0;
1061 sk->max_transid = (u64)-1;
1064 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1068 if (sk->nr_items == 0)
1075 * If we're looking for any dead subvolume, take a shortcut and look
1076 * for any ORPHAN_ITEMs in the tree root
1078 static int fs_has_dead_subvolumes(int fd)
1081 struct btrfs_ioctl_search_args args;
1082 struct btrfs_ioctl_search_key *sk = &args.key;
1083 struct btrfs_ioctl_search_header sh;
1084 u64 min_subvolid = 0;
1087 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1088 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1089 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1090 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1091 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1092 sk->min_offset = min_subvolid;
1093 sk->max_offset = (u64)-1;
1094 sk->min_transid = 0;
1095 sk->max_transid = (u64)-1;
1098 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1105 memcpy(&sh, args.buf, sizeof(sh));
1106 min_subvolid = sh.offset;
1109 * Verify that the root item is really there and we haven't hit
1112 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1113 sk->min_objectid = min_subvolid;
1114 sk->max_objectid = min_subvolid;
1115 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1116 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1118 sk->max_offset = (u64)-1;
1119 sk->min_transid = 0;
1120 sk->max_transid = (u64)-1;
1123 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1128 * Stale orphan, try the next one
1130 if (!sk->nr_items) {
1138 static int cmd_subvol_sync(int argc, char **argv)
1143 DIR *dirstream = NULL;
1147 int sleep_interval = 1;
1151 int c = getopt(argc, argv, "s:");
1158 sleep_interval = atoi(argv[optind]);
1159 if (sleep_interval < 1) {
1161 "ERROR: invalid sleep interval %s\n",
1168 usage(cmd_subvol_sync_usage);
1172 if (check_argc_min(argc - optind, 1))
1173 usage(cmd_subvol_sync_usage);
1175 fd = open_file_or_dir(argv[optind], &dirstream);
1177 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1183 id_count = argc - optind;
1190 ret = fs_has_dead_subvolumes(fd);
1192 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1199 sleep(sleep_interval);
1204 * Wait only for the requested ones
1206 ids = (u64*)malloc(sizeof(u64) * id_count);
1209 fprintf(stderr, "ERROR: not enough memory\n");
1214 for (i = 0; i < id_count; i++) {
1218 arg = argv[optind + i];
1220 id = strtoull(arg, NULL, 10);
1222 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1227 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1228 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1236 remaining = id_count;
1238 for (i = 0; i < id_count; i++) {
1241 ret = is_subvolume_cleaned(fd, ids[i]);
1243 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1248 printf("Subvolume id %llu is gone\n", ids[i]);
1255 sleep(sleep_interval);
1260 close_file_or_dir(fd, dirstream);
1265 const struct cmd_group subvolume_cmd_group = {
1266 subvolume_cmd_group_usage, NULL, {
1267 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1268 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1269 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1270 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1271 { "get-default", cmd_subvol_get_default,
1272 cmd_subvol_get_default_usage, NULL, 0 },
1273 { "set-default", cmd_subvol_set_default,
1274 cmd_subvol_set_default_usage, NULL, 0 },
1275 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1276 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1277 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1282 int cmd_subvolume(int argc, char **argv)
1284 return handle_command_group(&subvolume_cmd_group, argc, argv);