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:");
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;
221 struct option long_options[] = {
222 {"commit-after", no_argument, NULL, 'c'}, /* sync mode 1 */
223 {"commit-each", no_argument, NULL, 'C'}, /* sync mode 2 */
231 c = getopt_long(argc, argv, "cC", long_options, NULL);
243 usage(cmd_subvol_delete_usage);
247 if (check_argc_min(argc - optind, 1))
248 usage(cmd_subvol_delete_usage);
250 printf("Transaction commit: %s\n",
251 !sync_mode ? "none (default)" :
252 sync_mode == 1 ? "at the end" : "after each");
259 res = test_issubvolume(path);
261 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
266 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
271 cpath = realpath(path, NULL);
274 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
275 path, strerror(errno));
278 dupdname = strdup(cpath);
279 dname = dirname(dupdname);
280 dupvname = strdup(cpath);
281 vname = basename(dupvname);
284 if (!test_issubvolname(vname)) {
285 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
292 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
293 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
299 fd = open_file_or_dir(dname, &dirstream);
301 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
306 printf("Delete subvolume '%s/%s'\n", dname, vname);
307 strncpy_null(args.name, vname);
308 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
312 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
313 dname, vname, strerror(e));
318 if (sync_mode == 1) {
319 res = wait_for_commit(fd);
322 "ERROR: unable to wait for commit after '%s': %s\n",
323 path, strerror(errno));
335 close_file_or_dir(fd, dirstream);
336 /* avoid double free */
342 if (sync_mode == 2 && fd != -1) {
343 res = wait_for_commit(fd);
346 "ERROR: unable to do final sync: %s\n",
351 close_file_or_dir(fd, dirstream);
358 * - uppercase for filters and sort options
359 * - lowercase for enabling specific items in the output
361 static const char * const cmd_subvol_list_usage[] = {
362 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
363 "[--sort=gen,ogen,rootid,path] <path>",
364 "List subvolumes (and snapshots)",
366 "-p print parent ID",
367 "-a print all the subvolumes in the filesystem and",
368 " distinguish absolute and relative path with respect",
369 " to the given <path>",
370 "-c print the ogeneration of the subvolume",
371 "-g print the generation of the subvolume",
372 "-o print only subvolumes below specified path",
373 "-u print the uuid of subvolumes (and snapshots)",
374 "-q print the parent uuid of the snapshots",
375 "-R print the uuid of the received snapshots",
376 "-t print the result as a table",
377 "-s list snapshots only in the filesystem",
378 "-r list readonly subvolumes (including snapshots)",
379 "-d list deleted subvolumes that are not yet cleaned",
381 " filter the subvolumes by generation",
382 " (+value: >= value; -value: <= value; value: = value)",
384 " filter the subvolumes by ogeneration",
385 " (+value: >= value; -value: <= value; value: = value)",
386 "--sort=gen,ogen,rootid,path",
387 " list the subvolume in order of gen, ogen, rootid or path",
388 " you also can add '+' or '-' in front of each items.",
389 " (+:ascending, -:descending, ascending default)",
393 static int cmd_subvol_list(int argc, char **argv)
395 struct btrfs_list_filter_set *filter_set;
396 struct btrfs_list_comparer_set *comparer_set;
400 int ret = -1, uerr = 0;
403 int is_tab_result = 0;
405 int is_only_in_path = 0;
406 struct option long_options[] = {
407 {"sort", 1, NULL, 'S'},
410 DIR *dirstream = NULL;
412 filter_set = btrfs_list_alloc_filter_set();
413 comparer_set = btrfs_list_alloc_comparer_set();
417 c = getopt_long(argc, argv,
418 "acdgopqsurRG:C:t", long_options, NULL);
424 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
430 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
433 btrfs_list_setup_filter(&filter_set,
434 BTRFS_LIST_FILTER_DELETED,
438 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
447 btrfs_list_setup_filter(&filter_set,
448 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
450 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
451 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
454 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
457 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
460 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
463 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
466 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
467 ret = btrfs_list_parse_filter_string(optarg,
469 BTRFS_LIST_FILTER_GEN);
477 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
478 ret = btrfs_list_parse_filter_string(optarg,
480 BTRFS_LIST_FILTER_CGEN);
487 ret = btrfs_list_parse_sort_string(optarg,
502 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
505 if (check_argc_exact(argc - optind, 1)) {
510 subvol = argv[optind];
511 fd = open_file_or_dir(subvol, &dirstream);
514 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
518 ret = btrfs_list_get_path_rootid(fd, &top_id);
520 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
525 btrfs_list_setup_filter(&filter_set,
526 BTRFS_LIST_FILTER_FULL_PATH,
528 else if (is_only_in_path)
529 btrfs_list_setup_filter(&filter_set,
530 BTRFS_LIST_FILTER_TOPID_EQUAL,
533 /* by default we shall print the following columns*/
534 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
535 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
536 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
537 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
540 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
541 BTRFS_LIST_LAYOUT_TABLE,
542 !is_list_all && !is_only_in_path, NULL);
544 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
545 BTRFS_LIST_LAYOUT_DEFAULT,
546 !is_list_all && !is_only_in_path, NULL);
549 close_file_or_dir(fd, dirstream);
551 btrfs_list_free_filter_set(filter_set);
553 btrfs_list_free_comparer_set(comparer_set);
555 usage(cmd_subvol_list_usage);
559 static const char * const cmd_snapshot_usage[] = {
560 "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>",
561 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
562 "Create a snapshot of the subvolume",
563 "Create a writable/readonly snapshot of the subvolume <source> with",
564 "the name <name> in the <dest> directory. If only <dest> is given,",
565 "the subvolume will be named the basename of <source>.",
567 "-r create a readonly snapshot",
568 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
569 " option can be given multiple times.",
573 static int cmd_snapshot(int argc, char **argv)
577 int fd = -1, fddst = -1;
578 int len, readonly = 0;
579 char *dupname = NULL;
583 struct btrfs_ioctl_vol_args_v2 args;
584 struct btrfs_qgroup_inherit *inherit = NULL;
585 DIR *dirstream1 = NULL, *dirstream2 = NULL;
588 memset(&args, 0, sizeof(args));
590 int c = getopt(argc, argv, "c:i:r");
596 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
603 res = qgroup_inherit_add_group(&inherit, optarg);
613 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
620 usage(cmd_snapshot_usage);
624 if (check_argc_exact(argc - optind, 2))
625 usage(cmd_snapshot_usage);
627 subvol = argv[optind];
628 dst = argv[optind + 1];
630 retval = 1; /* failure */
631 res = test_issubvolume(subvol);
633 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
637 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
641 res = test_isdir(dst);
643 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
648 dupname = strdup(subvol);
649 newname = basename(dupname);
652 dupname = strdup(dst);
653 newname = basename(dupname);
654 dupdir = strdup(dst);
655 dstdir = dirname(dupdir);
658 if (!test_issubvolname(newname)) {
659 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
664 len = strlen(newname);
665 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
666 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
671 fddst = open_file_or_dir(dstdir, &dirstream1);
673 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
677 fd = open_file_or_dir(subvol, &dirstream2);
679 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
684 args.flags |= BTRFS_SUBVOL_RDONLY;
685 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
686 subvol, dstdir, newname);
688 printf("Create a snapshot of '%s' in '%s/%s'\n",
689 subvol, dstdir, newname);
694 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
695 args.size = qgroup_inherit_size(inherit);
696 args.qgroup_inherit = inherit;
698 strncpy_null(args.name, newname);
700 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
703 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
704 subvol, strerror(errno));
708 retval = 0; /* success */
711 close_file_or_dir(fddst, dirstream1);
712 close_file_or_dir(fd, dirstream2);
720 static const char * const cmd_subvol_get_default_usage[] = {
721 "btrfs subvolume get-default <path>",
722 "Get the default subvolume of a filesystem",
726 static int cmd_subvol_get_default(int argc, char **argv)
731 struct btrfs_list_filter_set *filter_set;
733 DIR *dirstream = NULL;
735 if (check_argc_exact(argc, 2))
736 usage(cmd_subvol_get_default_usage);
739 fd = open_file_or_dir(subvol, &dirstream);
741 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
745 ret = btrfs_list_get_default_subvolume(fd, &default_id);
747 fprintf(stderr, "ERROR: can't perform the search - %s\n",
753 if (default_id == 0) {
754 fprintf(stderr, "ERROR: 'default' dir item not found\n");
758 /* no need to resolve roots if FS_TREE is default */
759 if (default_id == BTRFS_FS_TREE_OBJECTID) {
760 printf("ID 5 (FS_TREE)\n");
765 filter_set = btrfs_list_alloc_filter_set();
766 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
769 /* by default we shall print the following columns*/
770 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
771 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
772 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
773 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
775 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
776 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
779 btrfs_list_free_filter_set(filter_set);
781 close_file_or_dir(fd, dirstream);
785 static const char * const cmd_subvol_set_default_usage[] = {
786 "btrfs subvolume set-default <subvolid> <path>",
787 "Set the default subvolume of a filesystem",
791 static int cmd_subvol_set_default(int argc, char **argv)
797 DIR *dirstream = NULL;
799 if (check_argc_exact(argc, 3))
800 usage(cmd_subvol_set_default_usage);
805 objectid = arg_strtou64(subvolid);
807 fd = open_file_or_dir(path, &dirstream);
809 fprintf(stderr, "ERROR: can't access '%s'\n", path);
813 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
815 close_file_or_dir(fd, dirstream);
817 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
824 static const char * const cmd_find_new_usage[] = {
825 "btrfs subvolume find-new <path> <lastgen>",
826 "List the recently modified files in a filesystem",
830 static int cmd_find_new(int argc, char **argv)
836 DIR *dirstream = NULL;
838 if (check_argc_exact(argc, 3))
839 usage(cmd_find_new_usage);
842 last_gen = arg_strtou64(argv[2]);
844 ret = test_issubvolume(subvol);
846 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
850 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
854 fd = open_file_or_dir(subvol, &dirstream);
856 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
860 ret = ioctl(fd, BTRFS_IOC_SYNC);
862 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
863 subvol, strerror(errno));
864 close_file_or_dir(fd, dirstream);
868 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
869 close_file_or_dir(fd, dirstream);
873 static const char * const cmd_subvol_show_usage[] = {
874 "btrfs subvolume show <subvol-path>",
875 "Show more information of the subvolume",
879 static int cmd_subvol_show(int argc, char **argv)
881 struct root_info get_ri;
882 struct btrfs_list_filter_set *filter_set;
884 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
885 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
886 char raw_prefix[] = "\t\t\t\t";
888 int fd = -1, mntfd = -1;
890 DIR *dirstream1 = NULL, *dirstream2 = NULL;
892 if (check_argc_exact(argc, 2))
893 usage(cmd_subvol_show_usage);
895 fullpath = realpath(argv[1], NULL);
897 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
898 argv[1], strerror(errno));
902 ret = test_issubvolume(fullpath);
904 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
908 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
913 ret = find_mount_root(fullpath, &mnt);
915 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
916 "%s\n", fullpath, strerror(-ret));
921 "ERROR: %s doesn't belong to btrfs mount point\n",
926 svpath = get_subvol_name(mnt, fullpath);
928 fd = open_file_or_dir(fullpath, &dirstream1);
930 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
934 ret = btrfs_list_get_path_rootid(fd, &sv_id);
936 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
941 mntfd = open_file_or_dir(mnt, &dirstream2);
943 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
947 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
949 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
953 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
954 printf("%s is btrfs root\n", fullpath);
958 memset(&get_ri, 0, sizeof(get_ri));
959 get_ri.root_id = sv_id;
961 ret = btrfs_get_subvol(mntfd, &get_ri);
963 fprintf(stderr, "ERROR: can't find '%s'\n",
969 printf("%s\n", fullpath);
970 printf("\tName: \t\t\t%s\n", get_ri.name);
972 if (uuid_is_null(get_ri.uuid))
973 strcpy(uuidparse, "-");
975 uuid_unparse(get_ri.uuid, uuidparse);
976 printf("\tuuid: \t\t\t%s\n", uuidparse);
978 if (uuid_is_null(get_ri.puuid))
979 strcpy(uuidparse, "-");
981 uuid_unparse(get_ri.puuid, uuidparse);
982 printf("\tParent uuid: \t\t%s\n", uuidparse);
987 localtime_r(&get_ri.otime, &tm);
988 strftime(tstr, 256, "%Y-%m-%d %X", &tm);
991 printf("\tCreation time: \t\t%s\n", tstr);
993 printf("\tObject ID: \t\t%llu\n", get_ri.root_id);
994 printf("\tGeneration (Gen): \t%llu\n", get_ri.gen);
995 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
996 printf("\tParent: \t\t%llu\n", get_ri.ref_tree);
997 printf("\tTop Level: \t\t%llu\n", get_ri.top_id);
999 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1000 printf("\tFlags: \t\t\treadonly\n");
1002 printf("\tFlags: \t\t\t-\n");
1004 /* print the snapshots of the given subvol if any*/
1005 printf("\tSnapshot(s):\n");
1006 filter_set = btrfs_list_alloc_filter_set();
1007 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1008 (u64)(unsigned long)get_ri.uuid);
1009 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1010 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1016 free(get_ri.full_path);
1017 btrfs_list_free_filter_set(filter_set);
1020 close_file_or_dir(fd, dirstream1);
1021 close_file_or_dir(mntfd, dirstream2);
1027 static const char * const cmd_subvol_sync_usage[] = {
1028 "btrfs subvolume sync <path> [<subvol-id>...]",
1029 "Wait until given subvolume(s) are completely removed from the filesystem.",
1030 "Wait until given subvolume(s) are completely removed from the filesystem",
1032 "If no subvolume id is given, wait until all ongoing deletion requests",
1033 "are complete. This may take long if new deleted subvolumes appear during",
1034 "the sleep interval.",
1036 "-s <N> sleep N seconds between checks (default: 1)",
1040 static int is_subvolume_cleaned(int fd, u64 subvolid)
1043 struct btrfs_ioctl_search_args args;
1044 struct btrfs_ioctl_search_key *sk = &args.key;
1046 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1047 sk->min_objectid = subvolid;
1048 sk->max_objectid = subvolid;
1049 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1050 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1052 sk->max_offset = (u64)-1;
1053 sk->min_transid = 0;
1054 sk->max_transid = (u64)-1;
1057 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1061 if (sk->nr_items == 0)
1068 * If we're looking for any dead subvolume, take a shortcut and look
1069 * for any ORPHAN_ITEMs in the tree root
1071 static int fs_has_dead_subvolumes(int fd)
1074 struct btrfs_ioctl_search_args args;
1075 struct btrfs_ioctl_search_key *sk = &args.key;
1076 struct btrfs_ioctl_search_header sh;
1077 u64 min_subvolid = 0;
1080 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1081 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1082 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1083 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1084 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1085 sk->min_offset = min_subvolid;
1086 sk->max_offset = (u64)-1;
1087 sk->min_transid = 0;
1088 sk->max_transid = (u64)-1;
1091 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1098 memcpy(&sh, args.buf, sizeof(sh));
1099 min_subvolid = sh.offset;
1102 * Verify that the root item is really there and we haven't hit
1105 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1106 sk->min_objectid = min_subvolid;
1107 sk->max_objectid = min_subvolid;
1108 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1109 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1111 sk->max_offset = (u64)-1;
1112 sk->min_transid = 0;
1113 sk->max_transid = (u64)-1;
1116 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1121 * Stale orphan, try the next one
1123 if (!sk->nr_items) {
1131 static int cmd_subvol_sync(int argc, char **argv)
1136 DIR *dirstream = NULL;
1140 int sleep_interval = 1;
1144 int c = getopt(argc, argv, "s:");
1151 sleep_interval = atoi(argv[optind]);
1152 if (sleep_interval < 1) {
1154 "ERROR: invalid sleep interval %s\n",
1161 usage(cmd_subvol_sync_usage);
1165 if (check_argc_min(argc - optind, 1))
1166 usage(cmd_subvol_sync_usage);
1168 fd = open_file_or_dir(argv[optind], &dirstream);
1170 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1176 id_count = argc - optind;
1183 ret = fs_has_dead_subvolumes(fd);
1185 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1192 sleep(sleep_interval);
1197 * Wait only for the requested ones
1199 ids = (u64*)malloc(sizeof(u64) * id_count);
1202 fprintf(stderr, "ERROR: not enough memory\n");
1207 for (i = 0; i < id_count; i++) {
1211 arg = argv[optind + i];
1213 id = strtoull(arg, NULL, 10);
1215 fprintf(stderr, "ERROR: unrecognized subvolume id %s\n",
1220 if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) {
1221 fprintf(stderr, "ERROR: subvolume id %s out of range\n",
1229 remaining = id_count;
1231 for (i = 0; i < id_count; i++) {
1234 ret = is_subvolume_cleaned(fd, ids[i]);
1236 fprintf(stderr, "ERROR: can't perform the search - %s\n",
1241 printf("Subvolume id %llu is gone\n", ids[i]);
1248 sleep(sleep_interval);
1253 close_file_or_dir(fd, dirstream);
1258 const struct cmd_group subvolume_cmd_group = {
1259 subvolume_cmd_group_usage, NULL, {
1260 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1261 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1262 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1263 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1264 { "get-default", cmd_subvol_get_default,
1265 cmd_subvol_get_default_usage, NULL, 0 },
1266 { "set-default", cmd_subvol_set_default,
1267 cmd_subvol_set_default_usage, NULL, 0 },
1268 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1269 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1270 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1275 int cmd_subvolume(int argc, char **argv)
1277 return handle_command_group(&subvolume_cmd_group, argc, argv);