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] <source> <dest>|[<dest>/]<name>",
569 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
570 "Create a snapshot of the subvolume",
571 "Create a writable/readonly snapshot of the subvolume <source> with",
572 "the name <name> in the <dest> directory. If only <dest> is given,",
573 "the subvolume will be named the basename of <source>.",
575 "-r create a readonly snapshot",
576 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
577 " option can be given multiple times.",
581 static int cmd_snapshot(int argc, char **argv)
585 int fd = -1, fddst = -1;
586 int len, readonly = 0;
587 char *dupname = NULL;
591 struct btrfs_ioctl_vol_args_v2 args;
592 struct btrfs_qgroup_inherit *inherit = NULL;
593 DIR *dirstream1 = NULL, *dirstream2 = NULL;
596 memset(&args, 0, sizeof(args));
598 int c = getopt(argc, argv, "c:i:r");
604 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
611 res = qgroup_inherit_add_group(&inherit, optarg);
621 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
628 usage(cmd_snapshot_usage);
632 if (check_argc_exact(argc - optind, 2))
633 usage(cmd_snapshot_usage);
635 subvol = argv[optind];
636 dst = argv[optind + 1];
638 retval = 1; /* failure */
639 res = test_issubvolume(subvol);
641 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
645 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
649 res = test_isdir(dst);
651 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
656 dupname = strdup(subvol);
657 newname = basename(dupname);
660 dupname = strdup(dst);
661 newname = basename(dupname);
662 dupdir = strdup(dst);
663 dstdir = dirname(dupdir);
666 if (!test_issubvolname(newname)) {
667 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
672 len = strlen(newname);
673 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
674 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
679 fddst = open_file_or_dir(dstdir, &dirstream1);
681 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
685 fd = open_file_or_dir(subvol, &dirstream2);
687 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
692 args.flags |= BTRFS_SUBVOL_RDONLY;
693 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
694 subvol, dstdir, newname);
696 printf("Create a snapshot of '%s' in '%s/%s'\n",
697 subvol, dstdir, newname);
702 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
703 args.size = qgroup_inherit_size(inherit);
704 args.qgroup_inherit = inherit;
706 strncpy_null(args.name, newname);
708 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
711 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
712 subvol, strerror(errno));
716 retval = 0; /* success */
719 close_file_or_dir(fddst, dirstream1);
720 close_file_or_dir(fd, dirstream2);
728 static const char * const cmd_subvol_get_default_usage[] = {
729 "btrfs subvolume get-default <path>",
730 "Get the default subvolume of a filesystem",
734 static int cmd_subvol_get_default(int argc, char **argv)
739 struct btrfs_list_filter_set *filter_set;
741 DIR *dirstream = NULL;
743 if (check_argc_exact(argc, 2))
744 usage(cmd_subvol_get_default_usage);
747 fd = open_file_or_dir(subvol, &dirstream);
749 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
753 ret = btrfs_list_get_default_subvolume(fd, &default_id);
755 fprintf(stderr, "ERROR: can't perform the search - %s\n",
761 if (default_id == 0) {
762 fprintf(stderr, "ERROR: 'default' dir item not found\n");
766 /* no need to resolve roots if FS_TREE is default */
767 if (default_id == BTRFS_FS_TREE_OBJECTID) {
768 printf("ID 5 (FS_TREE)\n");
773 filter_set = btrfs_list_alloc_filter_set();
774 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
777 /* by default we shall print the following columns*/
778 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
779 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
780 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
781 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
783 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
784 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
787 btrfs_list_free_filter_set(filter_set);
789 close_file_or_dir(fd, dirstream);
793 static const char * const cmd_subvol_set_default_usage[] = {
794 "btrfs subvolume set-default <subvolid> <path>",
795 "Set the default subvolume of a filesystem",
799 static int cmd_subvol_set_default(int argc, char **argv)
805 DIR *dirstream = NULL;
807 if (check_argc_exact(argc, 3))
808 usage(cmd_subvol_set_default_usage);
813 objectid = arg_strtou64(subvolid);
815 fd = open_file_or_dir(path, &dirstream);
817 fprintf(stderr, "ERROR: can't access '%s'\n", path);
821 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
823 close_file_or_dir(fd, dirstream);
825 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
832 static const char * const cmd_find_new_usage[] = {
833 "btrfs subvolume find-new <path> <lastgen>",
834 "List the recently modified files in a filesystem",
838 static int cmd_find_new(int argc, char **argv)
844 DIR *dirstream = NULL;
846 if (check_argc_exact(argc, 3))
847 usage(cmd_find_new_usage);
850 last_gen = arg_strtou64(argv[2]);
852 ret = test_issubvolume(subvol);
854 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
858 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
862 fd = open_file_or_dir(subvol, &dirstream);
864 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
868 ret = ioctl(fd, BTRFS_IOC_SYNC);
870 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
871 subvol, strerror(errno));
872 close_file_or_dir(fd, dirstream);
876 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
877 close_file_or_dir(fd, dirstream);
881 static const char * const cmd_subvol_show_usage[] = {
882 "btrfs subvolume show <subvol-path>",
883 "Show more information of the subvolume",
887 static int cmd_subvol_show(int argc, char **argv)
889 struct root_info get_ri;
890 struct btrfs_list_filter_set *filter_set;
892 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
893 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
894 char raw_prefix[] = "\t\t\t\t";
896 int fd = -1, mntfd = -1;
898 DIR *dirstream1 = NULL, *dirstream2 = NULL;
900 if (check_argc_exact(argc, 2))
901 usage(cmd_subvol_show_usage);
903 fullpath = realpath(argv[1], NULL);
905 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
906 argv[1], strerror(errno));
910 ret = test_issubvolume(fullpath);
912 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
916 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
921 ret = find_mount_root(fullpath, &mnt);
923 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
924 "%s\n", fullpath, strerror(-ret));
929 "ERROR: %s doesn't belong to btrfs mount point\n",
934 svpath = get_subvol_name(mnt, fullpath);
936 fd = open_file_or_dir(fullpath, &dirstream1);
938 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
942 ret = btrfs_list_get_path_rootid(fd, &sv_id);
944 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
949 mntfd = open_file_or_dir(mnt, &dirstream2);
951 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
955 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
957 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
961 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
962 printf("%s is btrfs root\n", fullpath);
966 memset(&get_ri, 0, sizeof(get_ri));
967 get_ri.root_id = sv_id;
969 ret = btrfs_get_subvol(mntfd, &get_ri);
971 fprintf(stderr, "ERROR: can't find '%s'\n",
977 printf("%s\n", fullpath);
978 printf("\tName: \t\t\t%s\n", get_ri.name);
980 if (uuid_is_null(get_ri.uuid))
981 strcpy(uuidparse, "-");
983 uuid_unparse(get_ri.uuid, uuidparse);
984 printf("\tuuid: \t\t\t%s\n", uuidparse);
986 if (uuid_is_null(get_ri.puuid))
987 strcpy(uuidparse, "-");
989 uuid_unparse(get_ri.puuid, uuidparse);
990 printf("\tParent uuid: \t\t%s\n", uuidparse);
995 localtime_r(&get_ri.otime, &tm);
996 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
999 printf("\tCreation time: \t\t%s\n", tstr);
1001 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
1002 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
1003 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1004 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
1005 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
1007 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1008 printf("\tFlags: \t\t\treadonly\n");
1010 printf("\tFlags: \t\t\t-\n");
1012 /* print the snapshots of the given subvol if any*/
1013 printf("\tSnapshot(s):\n");
1014 filter_set = btrfs_list_alloc_filter_set();
1015 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1016 (u64)(unsigned long)get_ri.uuid);
1017 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1018 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1024 free(get_ri.full_path);
1025 btrfs_list_free_filter_set(filter_set);
1028 close_file_or_dir(fd, dirstream1);
1029 close_file_or_dir(mntfd, dirstream2);
1035 static const char * const cmd_subvol_sync_usage[] = {
1036 "btrfs subvolume sync <path> [<subvol-id>...]",
1037 "Wait until given subvolume(s) are completely removed from the filesystem.",
1038 "Wait until given subvolume(s) are completely removed from the filesystem",
1040 "If no subvolume id is given, wait until all ongoing deletion requests",
1041 "are complete. This may take long if new deleted subvolumes appear during",
1042 "the sleep interval.",
1044 "-s <N> sleep N seconds between checks (default: 1)",
1048 static int is_subvolume_cleaned(int fd, u64 subvolid)
1051 struct btrfs_ioctl_search_args args;
1052 struct btrfs_ioctl_search_key *sk = &args.key;
1054 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1055 sk->min_objectid = subvolid;
1056 sk->max_objectid = subvolid;
1057 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1058 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1060 sk->max_offset = (u64)-1;
1061 sk->min_transid = 0;
1062 sk->max_transid = (u64)-1;
1065 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1069 if (sk->nr_items == 0)
1076 * If we're looking for any dead subvolume, take a shortcut and look
1077 * for any ORPHAN_ITEMs in the tree root
1079 static int fs_has_dead_subvolumes(int fd)
1082 struct btrfs_ioctl_search_args args;
1083 struct btrfs_ioctl_search_key *sk = &args.key;
1084 struct btrfs_ioctl_search_header sh;
1085 u64 min_subvolid = 0;
1088 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1089 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1090 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1091 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1092 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1093 sk->min_offset = min_subvolid;
1094 sk->max_offset = (u64)-1;
1095 sk->min_transid = 0;
1096 sk->max_transid = (u64)-1;
1099 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1106 memcpy(&sh, args.buf, sizeof(sh));
1107 min_subvolid = sh.offset;
1110 * Verify that the root item is really there and we haven't hit
1113 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1114 sk->min_objectid = min_subvolid;
1115 sk->max_objectid = min_subvolid;
1116 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1117 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1119 sk->max_offset = (u64)-1;
1120 sk->min_transid = 0;
1121 sk->max_transid = (u64)-1;
1124 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1129 * Stale orphan, try the next one
1131 if (!sk->nr_items) {
1139 static int cmd_subvol_sync(int argc, char **argv)
1144 DIR *dirstream = NULL;
1148 int sleep_interval = 1;
1152 int c = getopt(argc, argv, "s:");
1159 sleep_interval = atoi(argv[optind]);
1160 if (sleep_interval < 1) {
1162 "ERROR: invalid sleep interval %s\n",
1169 usage(cmd_subvol_sync_usage);
1173 if (check_argc_min(argc - optind, 1))
1174 usage(cmd_subvol_sync_usage);
1176 fd = open_file_or_dir(argv[optind], &dirstream);
1178 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1184 id_count = argc - optind;
1191 ret = fs_has_dead_subvolumes(fd);
1193 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1200 sleep(sleep_interval);
1205 * Wait only for the requested ones
1207 ids = (u64*)malloc(sizeof(u64) * id_count);
1210 fprintf(stderr, "ERROR: not enough memory\n");
1215 for (i = 0; i < id_count; i++) {
1219 arg = argv[optind + i];
1221 id = strtoull(arg, NULL, 10);
1223 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1228 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1229 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1237 remaining = id_count;
1239 for (i = 0; i < id_count; i++) {
1242 ret = is_subvolume_cleaned(fd, ids[i]);
1244 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1249 printf("Subvolume id %llu is gone\n", ids[i]);
1256 sleep(sleep_interval);
1261 close_file_or_dir(fd, dirstream);
1266 const struct cmd_group subvolume_cmd_group = {
1267 subvolume_cmd_group_usage, NULL, {
1268 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1269 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1270 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1271 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1272 { "get-default", cmd_subvol_get_default,
1273 cmd_subvol_get_default_usage, NULL, 0 },
1274 { "set-default", cmd_subvol_set_default,
1275 cmd_subvol_set_default_usage, NULL, 0 },
1276 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1277 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1278 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1283 int cmd_subvolume(int argc, char **argv)
1285 return handle_command_group(&subvolume_cmd_group, argc, argv);