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]);
93 sleep(sleep_interval);
101 static const char * const subvolume_cmd_group_usage[] = {
102 "btrfs subvolume <command> <args>",
106 static const char * const cmd_subvol_create_usage[] = {
107 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
108 "Create a subvolume",
109 "Create a subvolume <name> in <dest>. If <dest> is not given",
110 "subvolume <name> will be created in the current directory.",
112 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
113 " option can be given multiple times.",
117 static int cmd_subvol_create(int argc, char **argv)
119 int retval, res, len;
121 char *dupname = NULL;
126 struct btrfs_qgroup_inherit *inherit = NULL;
127 DIR *dirstream = NULL;
131 int c = getopt(argc, argv, "c:i:v");
137 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
144 res = qgroup_inherit_add_group(&inherit, optarg);
151 usage(cmd_subvol_create_usage);
155 if (check_argc_exact(argc - optind, 1))
156 usage(cmd_subvol_create_usage);
160 retval = 1; /* failure */
161 res = test_isdir(dst);
163 fprintf(stderr, "ERROR: '%s' exists\n", dst);
167 dupname = strdup(dst);
168 newname = basename(dupname);
169 dupdir = strdup(dst);
170 dstdir = dirname(dupdir);
172 if (!test_issubvolname(newname)) {
173 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
178 len = strlen(newname);
179 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
180 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
185 fddst = open_file_or_dir(dstdir, &dirstream);
187 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
191 printf("Create subvolume '%s/%s'\n", dstdir, newname);
193 struct btrfs_ioctl_vol_args_v2 args;
195 memset(&args, 0, sizeof(args));
196 strncpy_null(args.name, newname);
197 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
198 args.size = qgroup_inherit_size(inherit);
199 args.qgroup_inherit = inherit;
201 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
203 struct btrfs_ioctl_vol_args args;
205 memset(&args, 0, sizeof(args));
206 strncpy_null(args.name, newname);
208 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
212 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
217 retval = 0; /* success */
219 close_file_or_dir(fddst, dirstream);
228 * test if path is a subvolume:
229 * this function return
230 * 0-> path exists but it is not a subvolume
231 * 1-> path exists and it is a subvolume
232 * -1 -> path is unaccessible
234 int test_issubvolume(char *path)
239 res = stat(path, &st);
243 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
246 static int wait_for_commit(int fd)
250 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
253 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
256 static const char * const cmd_subvol_delete_usage[] = {
257 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
258 "Delete subvolume(s)",
259 "Delete subvolumes from the filesystem. The corresponding directory",
260 "is removed instantly but the data blocks are removed later.",
261 "The deletion does not involve full commit by default due to",
262 "performance reasons (as a consequence, the subvolume may appear again",
263 "after a crash). Use one of the --commit options to wait until the",
264 "operation is safely stored on the media.",
266 "-c|--commit-after wait for transaction commit at the end of the operation",
267 "-C|--commit-each wait for transaction commit after deleting each subvolume",
271 static int cmd_subvol_delete(int argc, char **argv)
276 struct btrfs_ioctl_vol_args args;
277 char *dname, *vname, *cpath;
278 char *dupdname = NULL;
279 char *dupvname = NULL;
281 DIR *dirstream = NULL;
288 static const struct option long_options[] = {
289 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
290 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
294 c = getopt_long(argc, argv, "cC", long_options, NULL);
309 usage(cmd_subvol_delete_usage);
313 if (check_argc_min(argc - optind, 1))
314 usage(cmd_subvol_delete_usage);
317 printf("Transaction commit: %s\n",
318 !commit_mode ? "none (default)" :
319 commit_mode == 1 ? "at the end" : "after each");
327 res = test_issubvolume(path);
329 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
334 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
339 cpath = realpath(path, NULL);
342 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
343 path, strerror(errno));
346 dupdname = strdup(cpath);
347 dname = dirname(dupdname);
348 dupvname = strdup(cpath);
349 vname = basename(dupvname);
352 fd = open_file_or_dir(dname, &dirstream);
354 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
359 printf("Delete subvolume (%s): '%s/%s'\n",
360 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
361 ? "commit" : "no-commit", dname, vname);
362 memset(&args, 0, sizeof(args));
363 strncpy_null(args.name, vname);
364 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
368 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
369 dname, vname, strerror(e));
374 if (commit_mode == 1) {
375 res = wait_for_commit(fd);
378 "ERROR: unable to wait for commit after '%s': %s\n",
379 path, strerror(errno));
391 close_file_or_dir(fd, dirstream);
392 /* avoid double free */
398 if (commit_mode == 2 && fd != -1) {
399 res = wait_for_commit(fd);
402 "ERROR: unable to do final sync: %s\n",
407 close_file_or_dir(fd, dirstream);
414 * - uppercase for filters and sort options
415 * - lowercase for enabling specific items in the output
417 static const char * const cmd_subvol_list_usage[] = {
418 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
419 "[--sort=gen,ogen,rootid,path] <path>",
420 "List subvolumes (and snapshots)",
422 "-p print parent ID",
423 "-a print all the subvolumes in the filesystem and",
424 " distinguish absolute and relative path with respect",
425 " to the given <path>",
426 "-c print the ogeneration of the subvolume",
427 "-g print the generation of the subvolume",
428 "-o print only subvolumes below specified path",
429 "-u print the uuid of subvolumes (and snapshots)",
430 "-q print the parent uuid of the snapshots",
431 "-R print the uuid of the received snapshots",
432 "-t print the result as a table",
433 "-s list snapshots only in the filesystem",
434 "-r list readonly subvolumes (including snapshots)",
435 "-d list deleted subvolumes that are not yet cleaned",
437 " filter the subvolumes by generation",
438 " (+value: >= value; -value: <= value; value: = value)",
440 " filter the subvolumes by ogeneration",
441 " (+value: >= value; -value: <= value; value: = value)",
442 "--sort=gen,ogen,rootid,path",
443 " list the subvolume in order of gen, ogen, rootid or path",
444 " you also can add '+' or '-' in front of each items.",
445 " (+:ascending, -:descending, ascending default)",
449 static int cmd_subvol_list(int argc, char **argv)
451 struct btrfs_list_filter_set *filter_set;
452 struct btrfs_list_comparer_set *comparer_set;
456 int ret = -1, uerr = 0;
458 int is_tab_result = 0;
460 int is_only_in_path = 0;
461 DIR *dirstream = NULL;
463 filter_set = btrfs_list_alloc_filter_set();
464 comparer_set = btrfs_list_alloc_comparer_set();
469 static const struct option long_options[] = {
470 {"sort", required_argument, NULL, 'S'},
474 c = getopt_long(argc, argv,
475 "acdgopqsurRG:C:t", long_options, NULL);
481 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
487 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
490 btrfs_list_setup_filter(&filter_set,
491 BTRFS_LIST_FILTER_DELETED,
495 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
504 btrfs_list_setup_filter(&filter_set,
505 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
507 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
508 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
511 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
514 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
517 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
520 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
523 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
524 ret = btrfs_list_parse_filter_string(optarg,
526 BTRFS_LIST_FILTER_GEN);
534 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
535 ret = btrfs_list_parse_filter_string(optarg,
537 BTRFS_LIST_FILTER_CGEN);
544 ret = btrfs_list_parse_sort_string(optarg,
559 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
562 if (check_argc_exact(argc - optind, 1)) {
567 subvol = argv[optind];
568 fd = open_file_or_dir(subvol, &dirstream);
571 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
575 ret = btrfs_list_get_path_rootid(fd, &top_id);
577 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
582 btrfs_list_setup_filter(&filter_set,
583 BTRFS_LIST_FILTER_FULL_PATH,
585 else if (is_only_in_path)
586 btrfs_list_setup_filter(&filter_set,
587 BTRFS_LIST_FILTER_TOPID_EQUAL,
590 /* by default we shall print the following columns*/
591 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
592 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
593 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
594 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
597 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
598 BTRFS_LIST_LAYOUT_TABLE,
599 !is_list_all && !is_only_in_path, NULL);
601 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
602 BTRFS_LIST_LAYOUT_DEFAULT,
603 !is_list_all && !is_only_in_path, NULL);
606 close_file_or_dir(fd, dirstream);
608 btrfs_list_free_filter_set(filter_set);
610 btrfs_list_free_comparer_set(comparer_set);
612 usage(cmd_subvol_list_usage);
616 static const char * const cmd_subvol_snapshot_usage[] = {
617 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
618 "Create a snapshot of the subvolume",
619 "Create a writable/readonly snapshot of the subvolume <source> with",
620 "the name <name> in the <dest> directory. If only <dest> is given,",
621 "the subvolume will be named the basename of <source>.",
623 "-r create a readonly snapshot",
624 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
625 " option can be given multiple times.",
629 static int cmd_subvol_snapshot(int argc, char **argv)
633 int fd = -1, fddst = -1;
634 int len, readonly = 0;
635 char *dupname = NULL;
639 struct btrfs_ioctl_vol_args_v2 args;
640 struct btrfs_qgroup_inherit *inherit = NULL;
641 DIR *dirstream1 = NULL, *dirstream2 = NULL;
644 memset(&args, 0, sizeof(args));
646 int c = getopt(argc, argv, "c:i:r");
652 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
659 res = qgroup_inherit_add_group(&inherit, optarg);
669 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
676 usage(cmd_subvol_snapshot_usage);
680 if (check_argc_exact(argc - optind, 2))
681 usage(cmd_subvol_snapshot_usage);
683 subvol = argv[optind];
684 dst = argv[optind + 1];
686 retval = 1; /* failure */
687 res = test_issubvolume(subvol);
689 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
693 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
697 res = test_isdir(dst);
699 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
704 dupname = strdup(subvol);
705 newname = basename(dupname);
708 dupname = strdup(dst);
709 newname = basename(dupname);
710 dupdir = strdup(dst);
711 dstdir = dirname(dupdir);
714 if (!test_issubvolname(newname)) {
715 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
720 len = strlen(newname);
721 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
722 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
727 fddst = open_file_or_dir(dstdir, &dirstream1);
729 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
733 fd = open_file_or_dir(subvol, &dirstream2);
735 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
740 args.flags |= BTRFS_SUBVOL_RDONLY;
741 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
742 subvol, dstdir, newname);
744 printf("Create a snapshot of '%s' in '%s/%s'\n",
745 subvol, dstdir, newname);
750 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
751 args.size = qgroup_inherit_size(inherit);
752 args.qgroup_inherit = inherit;
754 strncpy_null(args.name, newname);
756 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
759 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
760 subvol, strerror(errno));
764 retval = 0; /* success */
767 close_file_or_dir(fddst, dirstream1);
768 close_file_or_dir(fd, dirstream2);
776 static const char * const cmd_subvol_get_default_usage[] = {
777 "btrfs subvolume get-default <path>",
778 "Get the default subvolume of a filesystem",
782 static int cmd_subvol_get_default(int argc, char **argv)
787 struct btrfs_list_filter_set *filter_set;
789 DIR *dirstream = NULL;
791 if (check_argc_exact(argc, 2))
792 usage(cmd_subvol_get_default_usage);
795 fd = open_file_or_dir(subvol, &dirstream);
797 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
801 ret = btrfs_list_get_default_subvolume(fd, &default_id);
803 fprintf(stderr, "ERROR: can't perform the search - %s\n",
809 if (default_id == 0) {
810 fprintf(stderr, "ERROR: 'default' dir item not found\n");
814 /* no need to resolve roots if FS_TREE is default */
815 if (default_id == BTRFS_FS_TREE_OBJECTID) {
816 printf("ID 5 (FS_TREE)\n");
821 filter_set = btrfs_list_alloc_filter_set();
822 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
825 /* by default we shall print the following columns*/
826 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
827 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
828 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
829 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
831 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
832 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
835 btrfs_list_free_filter_set(filter_set);
837 close_file_or_dir(fd, dirstream);
841 static const char * const cmd_subvol_set_default_usage[] = {
842 "btrfs subvolume set-default <subvolid> <path>",
843 "Set the default subvolume of a filesystem",
847 static int cmd_subvol_set_default(int argc, char **argv)
853 DIR *dirstream = NULL;
855 if (check_argc_exact(argc, 3))
856 usage(cmd_subvol_set_default_usage);
861 objectid = arg_strtou64(subvolid);
863 fd = open_file_or_dir(path, &dirstream);
865 fprintf(stderr, "ERROR: can't access '%s'\n", path);
869 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
871 close_file_or_dir(fd, dirstream);
873 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
880 static const char * const cmd_subvol_find_new_usage[] = {
881 "btrfs subvolume find-new <path> <lastgen>",
882 "List the recently modified files in a filesystem",
886 static int cmd_subvol_find_new(int argc, char **argv)
892 DIR *dirstream = NULL;
894 if (check_argc_exact(argc, 3))
895 usage(cmd_subvol_find_new_usage);
898 last_gen = arg_strtou64(argv[2]);
900 ret = test_issubvolume(subvol);
902 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
906 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
910 fd = open_file_or_dir(subvol, &dirstream);
912 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
916 ret = ioctl(fd, BTRFS_IOC_SYNC);
918 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
919 subvol, strerror(errno));
920 close_file_or_dir(fd, dirstream);
924 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
925 close_file_or_dir(fd, dirstream);
929 static const char * const cmd_subvol_show_usage[] = {
930 "btrfs subvolume show <subvol-path>",
931 "Show more information of the subvolume",
935 static int cmd_subvol_show(int argc, char **argv)
937 struct root_info get_ri;
938 struct btrfs_list_filter_set *filter_set;
940 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
941 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
942 char raw_prefix[] = "\t\t\t\t";
944 int fd = -1, mntfd = -1;
946 DIR *dirstream1 = NULL, *dirstream2 = NULL;
948 if (check_argc_exact(argc, 2))
949 usage(cmd_subvol_show_usage);
951 fullpath = realpath(argv[1], NULL);
953 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
954 argv[1], strerror(errno));
958 ret = test_issubvolume(fullpath);
960 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
964 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
969 ret = find_mount_root(fullpath, &mnt);
971 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
972 "%s\n", fullpath, strerror(-ret));
977 "ERROR: %s doesn't belong to btrfs mount point\n",
982 svpath = get_subvol_name(mnt, fullpath);
984 fd = open_file_or_dir(fullpath, &dirstream1);
986 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
990 ret = btrfs_list_get_path_rootid(fd, &sv_id);
992 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
997 mntfd = open_file_or_dir(mnt, &dirstream2);
999 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
1003 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1004 printf("%s is btrfs root\n", fullpath);
1008 memset(&get_ri, 0, sizeof(get_ri));
1009 get_ri.root_id = sv_id;
1011 ret = btrfs_get_subvol(mntfd, &get_ri);
1013 fprintf(stderr, "ERROR: can't find '%s'\n",
1018 /* print the info */
1019 printf("%s\n", fullpath);
1020 printf("\tName: \t\t\t%s\n", get_ri.name);
1022 if (uuid_is_null(get_ri.uuid))
1023 strcpy(uuidparse, "-");
1025 uuid_unparse(get_ri.uuid, uuidparse);
1026 printf("\tUUID: \t\t\t%s\n", uuidparse);
1028 if (uuid_is_null(get_ri.puuid))
1029 strcpy(uuidparse, "-");
1031 uuid_unparse(get_ri.puuid, uuidparse);
1032 printf("\tParent UUID: \t\t%s\n", uuidparse);
1034 if (uuid_is_null(get_ri.ruuid))
1035 strcpy(uuidparse, "-");
1037 uuid_unparse(get_ri.ruuid, uuidparse);
1038 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1043 localtime_r(&get_ri.otime, &tm);
1044 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1047 printf("\tCreation time: \t\t%s\n", tstr);
1049 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1050 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1051 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1052 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1053 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1055 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1056 printf("\tFlags: \t\t\treadonly\n");
1058 printf("\tFlags: \t\t\t-\n");
1060 /* print the snapshots of the given subvol if any*/
1061 printf("\tSnapshot(s):\n");
1062 filter_set = btrfs_list_alloc_filter_set();
1063 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1064 (u64)(unsigned long)get_ri.uuid);
1065 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1066 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1072 free(get_ri.full_path);
1073 btrfs_list_free_filter_set(filter_set);
1076 close_file_or_dir(fd, dirstream1);
1077 close_file_or_dir(mntfd, dirstream2);
1083 static const char * const cmd_subvol_sync_usage[] = {
1084 "btrfs subvolume sync <path> [<subvol-id>...]",
1085 "Wait until given subvolume(s) are completely removed from the filesystem.",
1086 "Wait until given subvolume(s) are completely removed from the filesystem",
1088 "If no subvolume id is given, wait until all current deletion requests",
1089 "are completed, but do not wait for subvolumes deleted meanwhile.",
1090 "The status of subvolume ids is checked periodically.",
1092 "-s <N> sleep N seconds between checks (default: 1)",
1098 * If we're looking for any dead subvolume, take a shortcut and look
1099 * for any ORPHAN_ITEMs in the tree root
1101 static int fs_has_dead_subvolumes(int fd)
1104 struct btrfs_ioctl_search_args args;
1105 struct btrfs_ioctl_search_key *sk = &args.key;
1106 struct btrfs_ioctl_search_header sh;
1107 u64 min_subvolid = 0;
1110 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1111 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1112 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1113 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1114 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1115 sk->min_offset = min_subvolid;
1116 sk->max_offset = (u64)-1;
1117 sk->min_transid = 0;
1118 sk->max_transid = (u64)-1;
1121 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1128 memcpy(&sh, args.buf, sizeof(sh));
1129 min_subvolid = sh.offset;
1132 * Verify that the root item is really there and we haven't hit
1135 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1136 sk->min_objectid = min_subvolid;
1137 sk->max_objectid = min_subvolid;
1138 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1139 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1141 sk->max_offset = (u64)-1;
1142 sk->min_transid = 0;
1143 sk->max_transid = (u64)-1;
1146 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1151 * Stale orphan, try the next one
1153 if (!sk->nr_items) {
1162 #define SUBVOL_ID_BATCH 1024
1165 * Enumerate all dead subvolumes that exist in the filesystem.
1166 * Fill @ids and reallocate to bigger size if needed.
1168 static int enumerate_dead_subvols(int fd, u64 **ids)
1171 struct btrfs_ioctl_search_args args;
1172 struct btrfs_ioctl_search_key *sk = &args.key;
1176 memset(&args, 0, sizeof(args));
1178 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1179 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1180 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1181 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1182 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1184 sk->max_offset = (u64)-1;
1185 sk->min_transid = 0;
1186 sk->max_transid = (u64)-1;
1187 sk->nr_items = 4096;
1191 struct btrfs_ioctl_search_header *sh;
1195 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1203 for (i = 0; i < sk->nr_items; i++) {
1204 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1207 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1211 count += SUBVOL_ID_BATCH;
1212 newids = (u64*)realloc(*ids, count);
1217 (*ids)[idx] = sh->offset;
1222 sk->min_objectid = sh->objectid;
1223 sk->min_type = sh->type;
1224 sk->min_offset = sh->offset;
1226 if (sk->min_offset < (u64)-1)
1230 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1232 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1239 static int cmd_subvol_sync(int argc, char **argv)
1244 DIR *dirstream = NULL;
1247 int sleep_interval = 1;
1251 int c = getopt(argc, argv, "s:");
1258 sleep_interval = atoi(argv[optind]);
1259 if (sleep_interval < 1) {
1261 "ERROR: invalid sleep interval %s\n",
1268 usage(cmd_subvol_sync_usage);
1272 if (check_argc_min(argc - optind, 1))
1273 usage(cmd_subvol_sync_usage);
1275 fd = open_file_or_dir(argv[optind], &dirstream);
1277 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1283 id_count = argc - optind;
1285 id_count = enumerate_dead_subvols(fd, &ids);
1287 fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
1288 strerror(-id_count));
1292 if (id_count == 0) {
1297 ids = (u64*)malloc(id_count * sizeof(u64));
1299 fprintf(stderr, "ERROR: not enough memory\n");
1304 for (i = 0; i < id_count; i++) {
1308 arg = argv[optind + i];
1310 id = strtoull(arg, NULL, 10);
1313 "ERROR: unrecognized subvolume id %s\n",
1318 if (id < BTRFS_FIRST_FREE_OBJECTID
1319 || id > BTRFS_LAST_FREE_OBJECTID) {
1321 "ERROR: subvolume id %s out of range\n",
1330 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1334 close_file_or_dir(fd, dirstream);
1339 static const char subvolume_cmd_group_info[] =
1340 "manage subvolumes: create, delete, list, etc";
1342 const struct cmd_group subvolume_cmd_group = {
1343 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1344 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1345 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1346 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1347 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1349 { "get-default", cmd_subvol_get_default,
1350 cmd_subvol_get_default_usage, NULL, 0 },
1351 { "set-default", cmd_subvol_set_default,
1352 cmd_subvol_set_default_usage, NULL, 0 },
1353 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1355 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1356 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1361 int cmd_subvolume(int argc, char **argv)
1363 return handle_command_group(&subvolume_cmd_group, argc, argv);