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 int is_subvolume_cleaned(int fd, u64 subvolid)
42 struct btrfs_ioctl_search_args args;
43 struct btrfs_ioctl_search_key *sk = &args.key;
45 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
46 sk->min_objectid = subvolid;
47 sk->max_objectid = subvolid;
48 sk->min_type = BTRFS_ROOT_ITEM_KEY;
49 sk->max_type = BTRFS_ROOT_ITEM_KEY;
51 sk->max_offset = (u64)-1;
53 sk->max_transid = (u64)-1;
56 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
60 if (sk->nr_items == 0)
66 static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
75 for (i = 0; i < count; i++) {
78 ret = is_subvolume_cleaned(fd, ids[i]);
81 "ERROR: can't perform the search - %s\n",
86 printf("Subvolume id %llu is gone\n", ids[i]);
94 sleep(sleep_interval);
100 static const char * const subvolume_cmd_group_usage[] = {
101 "btrfs subvolume <command> <args>",
105 static const char * const cmd_subvol_create_usage[] = {
106 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
107 "Create a subvolume",
108 "Create a subvolume <name> in <dest>. If <dest> is not given",
109 "subvolume <name> will be created in the current directory.",
111 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
112 " option can be given multiple times.",
116 static int cmd_subvol_create(int argc, char **argv)
118 int retval, res, len;
120 char *dupname = NULL;
125 struct btrfs_qgroup_inherit *inherit = NULL;
126 DIR *dirstream = NULL;
130 int c = getopt(argc, argv, "c:i:v");
136 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
143 res = qgroup_inherit_add_group(&inherit, optarg);
150 usage(cmd_subvol_create_usage);
154 if (check_argc_exact(argc - optind, 1))
155 usage(cmd_subvol_create_usage);
159 retval = 1; /* failure */
160 res = test_isdir(dst);
162 fprintf(stderr, "ERROR: '%s' exists\n", dst);
166 dupname = strdup(dst);
167 newname = basename(dupname);
168 dupdir = strdup(dst);
169 dstdir = dirname(dupdir);
171 if (!test_issubvolname(newname)) {
172 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
177 len = strlen(newname);
178 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
179 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
184 fddst = open_file_or_dir(dstdir, &dirstream);
186 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
190 printf("Create subvolume '%s/%s'\n", dstdir, newname);
192 struct btrfs_ioctl_vol_args_v2 args;
194 memset(&args, 0, sizeof(args));
195 strncpy_null(args.name, newname);
196 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
197 args.size = qgroup_inherit_size(inherit);
198 args.qgroup_inherit = inherit;
200 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
202 struct btrfs_ioctl_vol_args args;
204 memset(&args, 0, sizeof(args));
205 strncpy_null(args.name, newname);
207 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
211 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
216 retval = 0; /* success */
218 close_file_or_dir(fddst, dirstream);
227 * test if path is a subvolume:
228 * this function return
229 * 0-> path exists but it is not a subvolume
230 * 1-> path exists and it is a subvolume
231 * -1 -> path is unaccessible
233 int test_issubvolume(char *path)
238 res = stat(path, &st);
242 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
245 static int wait_for_commit(int fd)
249 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
252 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
255 static const char * const cmd_subvol_delete_usage[] = {
256 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
257 "Delete subvolume(s)",
258 "Delete subvolumes from the filesystem. The corresponding directory",
259 "is removed instantly but the data blocks are removed later.",
260 "The deletion does not involve full commit by default due to",
261 "performance reasons (as a consequence, the subvolume may appear again",
262 "after a crash). Use one of the --commit options to wait until the",
263 "operation is safely stored on the media.",
265 "-c|--commit-after wait for transaction commit at the end of the operation",
266 "-C|--commit-each wait for transaction commit after deleting each subvolume",
270 static int cmd_subvol_delete(int argc, char **argv)
275 struct btrfs_ioctl_vol_args args;
276 char *dname, *vname, *cpath;
277 char *dupdname = NULL;
278 char *dupvname = NULL;
280 DIR *dirstream = NULL;
287 static const struct option long_options[] = {
288 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
289 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
293 c = getopt_long(argc, argv, "cC", long_options, NULL);
308 usage(cmd_subvol_delete_usage);
312 if (check_argc_min(argc - optind, 1))
313 usage(cmd_subvol_delete_usage);
316 printf("Transaction commit: %s\n",
317 !commit_mode ? "none (default)" :
318 commit_mode == 1 ? "at the end" : "after each");
326 res = test_issubvolume(path);
328 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
333 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
338 cpath = realpath(path, NULL);
341 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
342 path, strerror(errno));
345 dupdname = strdup(cpath);
346 dname = dirname(dupdname);
347 dupvname = strdup(cpath);
348 vname = basename(dupvname);
351 fd = open_file_or_dir(dname, &dirstream);
353 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
358 printf("Delete subvolume (%s): '%s/%s'\n",
359 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
360 ? "commit" : "no-commit", dname, vname);
361 memset(&args, 0, sizeof(args));
362 strncpy_null(args.name, vname);
363 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
367 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
368 dname, vname, strerror(e));
373 if (commit_mode == 1) {
374 res = wait_for_commit(fd);
377 "ERROR: unable to wait for commit after '%s': %s\n",
378 path, strerror(errno));
390 close_file_or_dir(fd, dirstream);
391 /* avoid double free */
397 if (commit_mode == 2 && fd != -1) {
398 res = wait_for_commit(fd);
401 "ERROR: unable to do final sync: %s\n",
406 close_file_or_dir(fd, dirstream);
413 * - uppercase for filters and sort options
414 * - lowercase for enabling specific items in the output
416 static const char * const cmd_subvol_list_usage[] = {
417 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
418 "[--sort=gen,ogen,rootid,path] <path>",
419 "List subvolumes (and snapshots)",
421 "-p print parent ID",
422 "-a print all the subvolumes in the filesystem and",
423 " distinguish absolute and relative path with respect",
424 " to the given <path>",
425 "-c print the ogeneration of the subvolume",
426 "-g print the generation of the subvolume",
427 "-o print only subvolumes below specified path",
428 "-u print the uuid of subvolumes (and snapshots)",
429 "-q print the parent uuid of the snapshots",
430 "-R print the uuid of the received snapshots",
431 "-t print the result as a table",
432 "-s list snapshots only in the filesystem",
433 "-r list readonly subvolumes (including snapshots)",
434 "-d list deleted subvolumes that are not yet cleaned",
436 " filter the subvolumes by generation",
437 " (+value: >= value; -value: <= value; value: = value)",
439 " filter the subvolumes by ogeneration",
440 " (+value: >= value; -value: <= value; value: = value)",
441 "--sort=gen,ogen,rootid,path",
442 " list the subvolume in order of gen, ogen, rootid or path",
443 " you also can add '+' or '-' in front of each items.",
444 " (+:ascending, -:descending, ascending default)",
448 static int cmd_subvol_list(int argc, char **argv)
450 struct btrfs_list_filter_set *filter_set;
451 struct btrfs_list_comparer_set *comparer_set;
455 int ret = -1, uerr = 0;
457 int is_tab_result = 0;
459 int is_only_in_path = 0;
460 DIR *dirstream = NULL;
462 filter_set = btrfs_list_alloc_filter_set();
463 comparer_set = btrfs_list_alloc_comparer_set();
468 static const struct option long_options[] = {
469 {"sort", required_argument, NULL, 'S'},
473 c = getopt_long(argc, argv,
474 "acdgopqsurRG:C:t", long_options, NULL);
480 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
486 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
489 btrfs_list_setup_filter(&filter_set,
490 BTRFS_LIST_FILTER_DELETED,
494 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
503 btrfs_list_setup_filter(&filter_set,
504 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
506 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
507 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
510 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
513 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
516 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
519 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
522 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
523 ret = btrfs_list_parse_filter_string(optarg,
525 BTRFS_LIST_FILTER_GEN);
533 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
534 ret = btrfs_list_parse_filter_string(optarg,
536 BTRFS_LIST_FILTER_CGEN);
543 ret = btrfs_list_parse_sort_string(optarg,
558 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
561 if (check_argc_exact(argc - optind, 1)) {
566 subvol = argv[optind];
567 fd = open_file_or_dir(subvol, &dirstream);
570 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
574 ret = btrfs_list_get_path_rootid(fd, &top_id);
576 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
581 btrfs_list_setup_filter(&filter_set,
582 BTRFS_LIST_FILTER_FULL_PATH,
584 else if (is_only_in_path)
585 btrfs_list_setup_filter(&filter_set,
586 BTRFS_LIST_FILTER_TOPID_EQUAL,
589 /* by default we shall print the following columns*/
590 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
591 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
592 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
593 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
596 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
597 BTRFS_LIST_LAYOUT_TABLE,
598 !is_list_all && !is_only_in_path, NULL);
600 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
601 BTRFS_LIST_LAYOUT_DEFAULT,
602 !is_list_all && !is_only_in_path, NULL);
605 close_file_or_dir(fd, dirstream);
607 btrfs_list_free_filter_set(filter_set);
609 btrfs_list_free_comparer_set(comparer_set);
611 usage(cmd_subvol_list_usage);
615 static const char * const cmd_subvol_snapshot_usage[] = {
616 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
617 "Create a snapshot of the subvolume",
618 "Create a writable/readonly snapshot of the subvolume <source> with",
619 "the name <name> in the <dest> directory. If only <dest> is given,",
620 "the subvolume will be named the basename of <source>.",
622 "-r create a readonly snapshot",
623 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
624 " option can be given multiple times.",
628 static int cmd_subvol_snapshot(int argc, char **argv)
632 int fd = -1, fddst = -1;
633 int len, readonly = 0;
634 char *dupname = NULL;
638 struct btrfs_ioctl_vol_args_v2 args;
639 struct btrfs_qgroup_inherit *inherit = NULL;
640 DIR *dirstream1 = NULL, *dirstream2 = NULL;
643 memset(&args, 0, sizeof(args));
645 int c = getopt(argc, argv, "c:i:r");
651 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
658 res = qgroup_inherit_add_group(&inherit, optarg);
668 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
675 usage(cmd_subvol_snapshot_usage);
679 if (check_argc_exact(argc - optind, 2))
680 usage(cmd_subvol_snapshot_usage);
682 subvol = argv[optind];
683 dst = argv[optind + 1];
685 retval = 1; /* failure */
686 res = test_issubvolume(subvol);
688 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
692 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
696 res = test_isdir(dst);
698 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
703 dupname = strdup(subvol);
704 newname = basename(dupname);
707 dupname = strdup(dst);
708 newname = basename(dupname);
709 dupdir = strdup(dst);
710 dstdir = dirname(dupdir);
713 if (!test_issubvolname(newname)) {
714 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
719 len = strlen(newname);
720 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
721 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
726 fddst = open_file_or_dir(dstdir, &dirstream1);
728 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
732 fd = open_file_or_dir(subvol, &dirstream2);
734 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
739 args.flags |= BTRFS_SUBVOL_RDONLY;
740 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
741 subvol, dstdir, newname);
743 printf("Create a snapshot of '%s' in '%s/%s'\n",
744 subvol, dstdir, newname);
749 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
750 args.size = qgroup_inherit_size(inherit);
751 args.qgroup_inherit = inherit;
753 strncpy_null(args.name, newname);
755 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
758 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
759 subvol, strerror(errno));
763 retval = 0; /* success */
766 close_file_or_dir(fddst, dirstream1);
767 close_file_or_dir(fd, dirstream2);
775 static const char * const cmd_subvol_get_default_usage[] = {
776 "btrfs subvolume get-default <path>",
777 "Get the default subvolume of a filesystem",
781 static int cmd_subvol_get_default(int argc, char **argv)
786 struct btrfs_list_filter_set *filter_set;
788 DIR *dirstream = NULL;
790 if (check_argc_exact(argc, 2))
791 usage(cmd_subvol_get_default_usage);
794 fd = open_file_or_dir(subvol, &dirstream);
796 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
800 ret = btrfs_list_get_default_subvolume(fd, &default_id);
802 fprintf(stderr, "ERROR: can't perform the search - %s\n",
808 if (default_id == 0) {
809 fprintf(stderr, "ERROR: 'default' dir item not found\n");
813 /* no need to resolve roots if FS_TREE is default */
814 if (default_id == BTRFS_FS_TREE_OBJECTID) {
815 printf("ID 5 (FS_TREE)\n");
820 filter_set = btrfs_list_alloc_filter_set();
821 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
824 /* by default we shall print the following columns*/
825 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
826 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
827 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
828 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
830 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
831 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
834 btrfs_list_free_filter_set(filter_set);
836 close_file_or_dir(fd, dirstream);
840 static const char * const cmd_subvol_set_default_usage[] = {
841 "btrfs subvolume set-default <subvolid> <path>",
842 "Set the default subvolume of a filesystem",
846 static int cmd_subvol_set_default(int argc, char **argv)
852 DIR *dirstream = NULL;
854 if (check_argc_exact(argc, 3))
855 usage(cmd_subvol_set_default_usage);
860 objectid = arg_strtou64(subvolid);
862 fd = open_file_or_dir(path, &dirstream);
864 fprintf(stderr, "ERROR: can't access '%s'\n", path);
868 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
870 close_file_or_dir(fd, dirstream);
872 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
879 static const char * const cmd_subvol_find_new_usage[] = {
880 "btrfs subvolume find-new <path> <lastgen>",
881 "List the recently modified files in a filesystem",
885 static int cmd_subvol_find_new(int argc, char **argv)
891 DIR *dirstream = NULL;
893 if (check_argc_exact(argc, 3))
894 usage(cmd_subvol_find_new_usage);
897 last_gen = arg_strtou64(argv[2]);
899 ret = test_issubvolume(subvol);
901 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
905 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
909 fd = open_file_or_dir(subvol, &dirstream);
911 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
915 ret = ioctl(fd, BTRFS_IOC_SYNC);
917 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
918 subvol, strerror(errno));
919 close_file_or_dir(fd, dirstream);
923 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
924 close_file_or_dir(fd, dirstream);
928 static const char * const cmd_subvol_show_usage[] = {
929 "btrfs subvolume show <subvol-path>",
930 "Show more information of the subvolume",
934 static int cmd_subvol_show(int argc, char **argv)
936 struct root_info get_ri;
937 struct btrfs_list_filter_set *filter_set;
939 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
940 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
941 char raw_prefix[] = "\t\t\t\t";
943 int fd = -1, mntfd = -1;
945 DIR *dirstream1 = NULL, *dirstream2 = NULL;
947 if (check_argc_exact(argc, 2))
948 usage(cmd_subvol_show_usage);
950 fullpath = realpath(argv[1], NULL);
952 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
953 argv[1], strerror(errno));
957 ret = test_issubvolume(fullpath);
959 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
963 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
968 ret = find_mount_root(fullpath, &mnt);
970 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
971 "%s\n", fullpath, strerror(-ret));
976 "ERROR: %s doesn't belong to btrfs mount point\n",
981 svpath = get_subvol_name(mnt, fullpath);
983 fd = open_file_or_dir(fullpath, &dirstream1);
985 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
989 ret = btrfs_list_get_path_rootid(fd, &sv_id);
991 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
996 mntfd = open_file_or_dir(mnt, &dirstream2);
998 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
1002 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1003 printf("%s is btrfs root\n", fullpath);
1007 memset(&get_ri, 0, sizeof(get_ri));
1008 get_ri.root_id = sv_id;
1010 ret = btrfs_get_subvol(mntfd, &get_ri);
1012 fprintf(stderr, "ERROR: can't find '%s'\n",
1017 /* print the info */
1018 printf("%s\n", fullpath);
1019 printf("\tName: \t\t\t%s\n", get_ri.name);
1021 if (uuid_is_null(get_ri.uuid))
1022 strcpy(uuidparse, "-");
1024 uuid_unparse(get_ri.uuid, uuidparse);
1025 printf("\tUUID: \t\t\t%s\n", uuidparse);
1027 if (uuid_is_null(get_ri.puuid))
1028 strcpy(uuidparse, "-");
1030 uuid_unparse(get_ri.puuid, uuidparse);
1031 printf("\tParent UUID: \t\t%s\n", uuidparse);
1033 if (uuid_is_null(get_ri.ruuid))
1034 strcpy(uuidparse, "-");
1036 uuid_unparse(get_ri.ruuid, uuidparse);
1037 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1042 localtime_r(&get_ri.otime, &tm);
1043 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1046 printf("\tCreation time: \t\t%s\n", tstr);
1048 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1049 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1050 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1051 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1052 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1054 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1055 printf("\tFlags: \t\t\treadonly\n");
1057 printf("\tFlags: \t\t\t-\n");
1059 /* print the snapshots of the given subvol if any*/
1060 printf("\tSnapshot(s):\n");
1061 filter_set = btrfs_list_alloc_filter_set();
1062 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1063 (u64)(unsigned long)get_ri.uuid);
1064 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1065 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1071 free(get_ri.full_path);
1072 btrfs_list_free_filter_set(filter_set);
1075 close_file_or_dir(fd, dirstream1);
1076 close_file_or_dir(mntfd, dirstream2);
1082 static const char * const cmd_subvol_sync_usage[] = {
1083 "btrfs subvolume sync <path> [<subvol-id>...]",
1084 "Wait until given subvolume(s) are completely removed from the filesystem.",
1085 "Wait until given subvolume(s) are completely removed from the filesystem",
1087 "If no subvolume id is given, wait until all current deletion requests",
1088 "are completed, but do not wait for subvolumes deleted meanwhile.",
1089 "The status of subvolume ids is checked periodically.",
1091 "-s <N> sleep N seconds between checks (default: 1)",
1097 * If we're looking for any dead subvolume, take a shortcut and look
1098 * for any ORPHAN_ITEMs in the tree root
1100 static int fs_has_dead_subvolumes(int fd)
1103 struct btrfs_ioctl_search_args args;
1104 struct btrfs_ioctl_search_key *sk = &args.key;
1105 struct btrfs_ioctl_search_header sh;
1106 u64 min_subvolid = 0;
1109 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1110 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1111 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1112 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1113 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1114 sk->min_offset = min_subvolid;
1115 sk->max_offset = (u64)-1;
1116 sk->min_transid = 0;
1117 sk->max_transid = (u64)-1;
1120 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1127 memcpy(&sh, args.buf, sizeof(sh));
1128 min_subvolid = sh.offset;
1131 * Verify that the root item is really there and we haven't hit
1134 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1135 sk->min_objectid = min_subvolid;
1136 sk->max_objectid = min_subvolid;
1137 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1138 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1140 sk->max_offset = (u64)-1;
1141 sk->min_transid = 0;
1142 sk->max_transid = (u64)-1;
1145 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1150 * Stale orphan, try the next one
1152 if (!sk->nr_items) {
1161 #define SUBVOL_ID_BATCH 1024
1164 * Enumerate all dead subvolumes that exist in the filesystem.
1165 * Fill @ids and reallocate to bigger size if needed.
1167 static int enumerate_dead_subvols(int fd, u64 **ids)
1170 struct btrfs_ioctl_search_args args;
1171 struct btrfs_ioctl_search_key *sk = &args.key;
1175 memset(&args, 0, sizeof(args));
1177 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1178 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1179 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1180 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1181 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1183 sk->max_offset = (u64)-1;
1184 sk->min_transid = 0;
1185 sk->max_transid = (u64)-1;
1186 sk->nr_items = 4096;
1190 struct btrfs_ioctl_search_header *sh;
1194 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1202 for (i = 0; i < sk->nr_items; i++) {
1203 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1206 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1210 count += SUBVOL_ID_BATCH;
1211 newids = (u64*)realloc(*ids, count);
1216 (*ids)[idx] = sh->offset;
1221 sk->min_objectid = sh->objectid;
1222 sk->min_type = sh->type;
1223 sk->min_offset = sh->offset;
1225 if (sk->min_offset < (u64)-1)
1229 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1231 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1238 static int cmd_subvol_sync(int argc, char **argv)
1243 DIR *dirstream = NULL;
1246 int sleep_interval = 1;
1250 int c = getopt(argc, argv, "s:");
1257 sleep_interval = atoi(argv[optind]);
1258 if (sleep_interval < 1) {
1260 "ERROR: invalid sleep interval %s\n",
1267 usage(cmd_subvol_sync_usage);
1271 if (check_argc_min(argc - optind, 1))
1272 usage(cmd_subvol_sync_usage);
1274 fd = open_file_or_dir(argv[optind], &dirstream);
1276 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1282 id_count = argc - optind;
1284 id_count = enumerate_dead_subvols(fd, &ids);
1286 fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
1287 strerror(-id_count));
1291 if (id_count == 0) {
1296 ids = (u64*)malloc(id_count * sizeof(u64));
1298 fprintf(stderr, "ERROR: not enough memory\n");
1303 for (i = 0; i < id_count; i++) {
1307 arg = argv[optind + i];
1309 id = strtoull(arg, NULL, 10);
1312 "ERROR: unrecognized subvolume id %s\n",
1317 if (id < BTRFS_FIRST_FREE_OBJECTID
1318 || id > BTRFS_LAST_FREE_OBJECTID) {
1320 "ERROR: subvolume id %s out of range\n",
1329 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1333 close_file_or_dir(fd, dirstream);
1338 static const char subvolume_cmd_group_info[] =
1339 "manage subvolumes: create, delete, list, etc";
1341 const struct cmd_group subvolume_cmd_group = {
1342 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1343 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1344 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1345 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1346 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1348 { "get-default", cmd_subvol_get_default,
1349 cmd_subvol_get_default_usage, NULL, 0 },
1350 { "set-default", cmd_subvol_set_default,
1351 cmd_subvol_set_default_usage, NULL, 0 },
1352 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1354 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1355 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1360 int cmd_subvolume(int argc, char **argv)
1362 return handle_command_group(&subvolume_cmd_group, argc, argv);