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);
99 static const char * const subvolume_cmd_group_usage[] = {
100 "btrfs subvolume <command> <args>",
104 static const char * const cmd_subvol_create_usage[] = {
105 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
106 "Create a subvolume",
107 "Create a subvolume <name> in <dest>. If <dest> is not given",
108 "subvolume <name> will be created in the current directory.",
110 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
111 " option can be given multiple times.",
115 static int cmd_subvol_create(int argc, char **argv)
117 int retval, res, len;
119 char *dupname = NULL;
124 struct btrfs_qgroup_inherit *inherit = NULL;
125 DIR *dirstream = NULL;
129 int c = getopt(argc, argv, "c:i:v");
135 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
142 res = qgroup_inherit_add_group(&inherit, optarg);
149 usage(cmd_subvol_create_usage);
153 if (check_argc_exact(argc - optind, 1))
154 usage(cmd_subvol_create_usage);
158 retval = 1; /* failure */
159 res = test_isdir(dst);
161 fprintf(stderr, "ERROR: '%s' exists\n", dst);
165 dupname = strdup(dst);
166 newname = basename(dupname);
167 dupdir = strdup(dst);
168 dstdir = dirname(dupdir);
170 if (!test_issubvolname(newname)) {
171 fprintf(stderr, "ERROR: incorrect subvolume name '%s'\n",
176 len = strlen(newname);
177 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
178 fprintf(stderr, "ERROR: subvolume name too long '%s'\n",
183 fddst = open_file_or_dir(dstdir, &dirstream);
185 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
189 printf("Create subvolume '%s/%s'\n", dstdir, newname);
191 struct btrfs_ioctl_vol_args_v2 args;
193 memset(&args, 0, sizeof(args));
194 strncpy_null(args.name, newname);
195 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
196 args.size = qgroup_inherit_size(inherit);
197 args.qgroup_inherit = inherit;
199 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
201 struct btrfs_ioctl_vol_args args;
203 memset(&args, 0, sizeof(args));
204 strncpy_null(args.name, newname);
206 res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
210 fprintf(stderr, "ERROR: cannot create subvolume - %s\n",
215 retval = 0; /* success */
217 close_file_or_dir(fddst, dirstream);
226 * test if path is a subvolume:
227 * this function return
228 * 0-> path exists but it is not a subvolume
229 * 1-> path exists and it is a subvolume
230 * -1 -> path is unaccessible
232 int test_issubvolume(char *path)
237 res = stat(path, &st);
241 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
244 static int wait_for_commit(int fd)
248 ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
251 return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
254 static const char * const cmd_subvol_delete_usage[] = {
255 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
256 "Delete subvolume(s)",
257 "Delete subvolumes from the filesystem. The corresponding directory",
258 "is removed instantly but the data blocks are removed later.",
259 "The deletion does not involve full commit by default due to",
260 "performance reasons (as a consequence, the subvolume may appear again",
261 "after a crash). Use one of the --commit options to wait until the",
262 "operation is safely stored on the media.",
264 "-c|--commit-after wait for transaction commit at the end of the operation",
265 "-C|--commit-each wait for transaction commit after deleting each subvolume",
269 static int cmd_subvol_delete(int argc, char **argv)
274 struct btrfs_ioctl_vol_args args;
275 char *dname, *vname, *cpath;
276 char *dupdname = NULL;
277 char *dupvname = NULL;
279 DIR *dirstream = NULL;
286 static const struct option long_options[] = {
287 {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
288 {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
292 c = getopt_long(argc, argv, "cC", long_options, NULL);
307 usage(cmd_subvol_delete_usage);
311 if (check_argc_min(argc - optind, 1))
312 usage(cmd_subvol_delete_usage);
315 printf("Transaction commit: %s\n",
316 !commit_mode ? "none (default)" :
317 commit_mode == 1 ? "at the end" : "after each");
325 res = test_issubvolume(path);
327 fprintf(stderr, "ERROR: error accessing '%s'\n", path);
332 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", path);
337 cpath = realpath(path, NULL);
340 fprintf(stderr, "ERROR: finding real path for '%s': %s\n",
341 path, strerror(errno));
344 dupdname = strdup(cpath);
345 dname = dirname(dupdname);
346 dupvname = strdup(cpath);
347 vname = basename(dupvname);
350 fd = open_file_or_dir(dname, &dirstream);
352 fprintf(stderr, "ERROR: can't access '%s'\n", dname);
357 printf("Delete subvolume (%s): '%s/%s'\n",
358 commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
359 ? "commit" : "no-commit", dname, vname);
360 strncpy_null(args.name, vname);
361 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
365 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
366 dname, vname, strerror(e));
371 if (commit_mode == 1) {
372 res = wait_for_commit(fd);
375 "ERROR: unable to wait for commit after '%s': %s\n",
376 path, strerror(errno));
388 close_file_or_dir(fd, dirstream);
389 /* avoid double free */
395 if (commit_mode == 2 && fd != -1) {
396 res = wait_for_commit(fd);
399 "ERROR: unable to do final sync: %s\n",
404 close_file_or_dir(fd, dirstream);
411 * - uppercase for filters and sort options
412 * - lowercase for enabling specific items in the output
414 static const char * const cmd_subvol_list_usage[] = {
415 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
416 "[--sort=gen,ogen,rootid,path] <path>",
417 "List subvolumes (and snapshots)",
419 "-p print parent ID",
420 "-a print all the subvolumes in the filesystem and",
421 " distinguish absolute and relative path with respect",
422 " to the given <path>",
423 "-c print the ogeneration of the subvolume",
424 "-g print the generation of the subvolume",
425 "-o print only subvolumes below specified path",
426 "-u print the uuid of subvolumes (and snapshots)",
427 "-q print the parent uuid of the snapshots",
428 "-R print the uuid of the received snapshots",
429 "-t print the result as a table",
430 "-s list snapshots only in the filesystem",
431 "-r list readonly subvolumes (including snapshots)",
432 "-d list deleted subvolumes that are not yet cleaned",
434 " filter the subvolumes by generation",
435 " (+value: >= value; -value: <= value; value: = value)",
437 " filter the subvolumes by ogeneration",
438 " (+value: >= value; -value: <= value; value: = value)",
439 "--sort=gen,ogen,rootid,path",
440 " list the subvolume in order of gen, ogen, rootid or path",
441 " you also can add '+' or '-' in front of each items.",
442 " (+:ascending, -:descending, ascending default)",
446 static int cmd_subvol_list(int argc, char **argv)
448 struct btrfs_list_filter_set *filter_set;
449 struct btrfs_list_comparer_set *comparer_set;
453 int ret = -1, uerr = 0;
455 int is_tab_result = 0;
457 int is_only_in_path = 0;
458 DIR *dirstream = NULL;
460 filter_set = btrfs_list_alloc_filter_set();
461 comparer_set = btrfs_list_alloc_comparer_set();
466 static const struct option long_options[] = {
467 {"sort", required_argument, NULL, 'S'},
471 c = getopt_long(argc, argv,
472 "acdgopqsurRG:C:t", long_options, NULL);
478 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
484 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
487 btrfs_list_setup_filter(&filter_set,
488 BTRFS_LIST_FILTER_DELETED,
492 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
501 btrfs_list_setup_filter(&filter_set,
502 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
504 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
505 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
508 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
511 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
514 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
517 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
520 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
521 ret = btrfs_list_parse_filter_string(optarg,
523 BTRFS_LIST_FILTER_GEN);
531 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
532 ret = btrfs_list_parse_filter_string(optarg,
534 BTRFS_LIST_FILTER_CGEN);
541 ret = btrfs_list_parse_sort_string(optarg,
556 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
559 if (check_argc_exact(argc - optind, 1)) {
564 subvol = argv[optind];
565 fd = open_file_or_dir(subvol, &dirstream);
568 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
572 ret = btrfs_list_get_path_rootid(fd, &top_id);
574 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
579 btrfs_list_setup_filter(&filter_set,
580 BTRFS_LIST_FILTER_FULL_PATH,
582 else if (is_only_in_path)
583 btrfs_list_setup_filter(&filter_set,
584 BTRFS_LIST_FILTER_TOPID_EQUAL,
587 /* by default we shall print the following columns*/
588 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
589 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
590 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
591 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
594 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
595 BTRFS_LIST_LAYOUT_TABLE,
596 !is_list_all && !is_only_in_path, NULL);
598 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
599 BTRFS_LIST_LAYOUT_DEFAULT,
600 !is_list_all && !is_only_in_path, NULL);
603 close_file_or_dir(fd, dirstream);
605 btrfs_list_free_filter_set(filter_set);
607 btrfs_list_free_comparer_set(comparer_set);
609 usage(cmd_subvol_list_usage);
613 static const char * const cmd_snapshot_usage[] = {
614 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
615 "Create a snapshot of the subvolume",
616 "Create a writable/readonly snapshot of the subvolume <source> with",
617 "the name <name> in the <dest> directory. If only <dest> is given,",
618 "the subvolume will be named the basename of <source>.",
620 "-r create a readonly snapshot",
621 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
622 " option can be given multiple times.",
626 static int cmd_snapshot(int argc, char **argv)
630 int fd = -1, fddst = -1;
631 int len, readonly = 0;
632 char *dupname = NULL;
636 struct btrfs_ioctl_vol_args_v2 args;
637 struct btrfs_qgroup_inherit *inherit = NULL;
638 DIR *dirstream1 = NULL, *dirstream2 = NULL;
641 memset(&args, 0, sizeof(args));
643 int c = getopt(argc, argv, "c:i:r");
649 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
656 res = qgroup_inherit_add_group(&inherit, optarg);
666 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
673 usage(cmd_snapshot_usage);
677 if (check_argc_exact(argc - optind, 2))
678 usage(cmd_snapshot_usage);
680 subvol = argv[optind];
681 dst = argv[optind + 1];
683 retval = 1; /* failure */
684 res = test_issubvolume(subvol);
686 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
690 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
694 res = test_isdir(dst);
696 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
701 dupname = strdup(subvol);
702 newname = basename(dupname);
705 dupname = strdup(dst);
706 newname = basename(dupname);
707 dupdir = strdup(dst);
708 dstdir = dirname(dupdir);
711 if (!test_issubvolname(newname)) {
712 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
717 len = strlen(newname);
718 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
719 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
724 fddst = open_file_or_dir(dstdir, &dirstream1);
726 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
730 fd = open_file_or_dir(subvol, &dirstream2);
732 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
737 args.flags |= BTRFS_SUBVOL_RDONLY;
738 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
739 subvol, dstdir, newname);
741 printf("Create a snapshot of '%s' in '%s/%s'\n",
742 subvol, dstdir, newname);
747 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
748 args.size = qgroup_inherit_size(inherit);
749 args.qgroup_inherit = inherit;
751 strncpy_null(args.name, newname);
753 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
756 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
757 subvol, strerror(errno));
761 retval = 0; /* success */
764 close_file_or_dir(fddst, dirstream1);
765 close_file_or_dir(fd, dirstream2);
773 static const char * const cmd_subvol_get_default_usage[] = {
774 "btrfs subvolume get-default <path>",
775 "Get the default subvolume of a filesystem",
779 static int cmd_subvol_get_default(int argc, char **argv)
784 struct btrfs_list_filter_set *filter_set;
786 DIR *dirstream = NULL;
788 if (check_argc_exact(argc, 2))
789 usage(cmd_subvol_get_default_usage);
792 fd = open_file_or_dir(subvol, &dirstream);
794 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
798 ret = btrfs_list_get_default_subvolume(fd, &default_id);
800 fprintf(stderr, "ERROR: can't perform the search - %s\n",
806 if (default_id == 0) {
807 fprintf(stderr, "ERROR: 'default' dir item not found\n");
811 /* no need to resolve roots if FS_TREE is default */
812 if (default_id == BTRFS_FS_TREE_OBJECTID) {
813 printf("ID 5 (FS_TREE)\n");
818 filter_set = btrfs_list_alloc_filter_set();
819 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
822 /* by default we shall print the following columns*/
823 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
824 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
825 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
826 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
828 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
829 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
832 btrfs_list_free_filter_set(filter_set);
834 close_file_or_dir(fd, dirstream);
838 static const char * const cmd_subvol_set_default_usage[] = {
839 "btrfs subvolume set-default <subvolid> <path>",
840 "Set the default subvolume of a filesystem",
844 static int cmd_subvol_set_default(int argc, char **argv)
850 DIR *dirstream = NULL;
852 if (check_argc_exact(argc, 3))
853 usage(cmd_subvol_set_default_usage);
858 objectid = arg_strtou64(subvolid);
860 fd = open_file_or_dir(path, &dirstream);
862 fprintf(stderr, "ERROR: can't access '%s'\n", path);
866 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
868 close_file_or_dir(fd, dirstream);
870 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
877 static const char * const cmd_find_new_usage[] = {
878 "btrfs subvolume find-new <path> <lastgen>",
879 "List the recently modified files in a filesystem",
883 static int cmd_find_new(int argc, char **argv)
889 DIR *dirstream = NULL;
891 if (check_argc_exact(argc, 3))
892 usage(cmd_find_new_usage);
895 last_gen = arg_strtou64(argv[2]);
897 ret = test_issubvolume(subvol);
899 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
903 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
907 fd = open_file_or_dir(subvol, &dirstream);
909 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
913 ret = ioctl(fd, BTRFS_IOC_SYNC);
915 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
916 subvol, strerror(errno));
917 close_file_or_dir(fd, dirstream);
921 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
922 close_file_or_dir(fd, dirstream);
926 static const char * const cmd_subvol_show_usage[] = {
927 "btrfs subvolume show <subvol-path>",
928 "Show more information of the subvolume",
932 static int cmd_subvol_show(int argc, char **argv)
934 struct root_info get_ri;
935 struct btrfs_list_filter_set *filter_set;
937 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
938 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
939 char raw_prefix[] = "\t\t\t\t";
941 int fd = -1, mntfd = -1;
943 DIR *dirstream1 = NULL, *dirstream2 = NULL;
945 if (check_argc_exact(argc, 2))
946 usage(cmd_subvol_show_usage);
948 fullpath = realpath(argv[1], NULL);
950 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
951 argv[1], strerror(errno));
955 ret = test_issubvolume(fullpath);
957 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
961 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
966 ret = find_mount_root(fullpath, &mnt);
968 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
969 "%s\n", fullpath, strerror(-ret));
974 "ERROR: %s doesn't belong to btrfs mount point\n",
979 svpath = get_subvol_name(mnt, fullpath);
981 fd = open_file_or_dir(fullpath, &dirstream1);
983 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
987 ret = btrfs_list_get_path_rootid(fd, &sv_id);
989 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
994 mntfd = open_file_or_dir(mnt, &dirstream2);
996 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
1000 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
1002 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
1006 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1007 printf("%s is btrfs root\n", fullpath);
1011 memset(&get_ri, 0, sizeof(get_ri));
1012 get_ri.root_id = sv_id;
1014 ret = btrfs_get_subvol(mntfd, &get_ri);
1016 fprintf(stderr, "ERROR: can't find '%s'\n",
1021 /* print the info */
1022 printf("%s\n", fullpath);
1023 printf("\tName: \t\t\t%s\n", get_ri.name);
1025 if (uuid_is_null(get_ri.uuid))
1026 strcpy(uuidparse, "-");
1028 uuid_unparse(get_ri.uuid, uuidparse);
1029 printf("\tUUID: \t\t\t%s\n", uuidparse);
1031 if (uuid_is_null(get_ri.puuid))
1032 strcpy(uuidparse, "-");
1034 uuid_unparse(get_ri.puuid, uuidparse);
1035 printf("\tParent UUID: \t\t%s\n", uuidparse);
1037 if (uuid_is_null(get_ri.ruuid))
1038 strcpy(uuidparse, "-");
1040 uuid_unparse(get_ri.ruuid, uuidparse);
1041 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1046 localtime_r(&get_ri.otime, &tm);
1047 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1050 printf("\tCreation time: \t\t%s\n", tstr);
1052 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1053 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1054 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1055 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1056 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1058 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1059 printf("\tFlags: \t\t\treadonly\n");
1061 printf("\tFlags: \t\t\t-\n");
1063 /* print the snapshots of the given subvol if any*/
1064 printf("\tSnapshot(s):\n");
1065 filter_set = btrfs_list_alloc_filter_set();
1066 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1067 (u64)(unsigned long)get_ri.uuid);
1068 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1069 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1075 free(get_ri.full_path);
1076 btrfs_list_free_filter_set(filter_set);
1079 close_file_or_dir(fd, dirstream1);
1080 close_file_or_dir(mntfd, dirstream2);
1086 static const char * const cmd_subvol_sync_usage[] = {
1087 "btrfs subvolume sync <path> [<subvol-id>...]",
1088 "Wait until given subvolume(s) are completely removed from the filesystem.",
1089 "Wait until given subvolume(s) are completely removed from the filesystem",
1091 "If no subvolume id is given, wait until all current deletion requests",
1092 "are completed, but do not wait for subvolumes deleted meanwhile.",
1093 "The status of subvolume ids is checked periodically.",
1095 "-s <N> sleep N seconds between checks (default: 1)",
1101 * If we're looking for any dead subvolume, take a shortcut and look
1102 * for any ORPHAN_ITEMs in the tree root
1104 static int fs_has_dead_subvolumes(int fd)
1107 struct btrfs_ioctl_search_args args;
1108 struct btrfs_ioctl_search_key *sk = &args.key;
1109 struct btrfs_ioctl_search_header sh;
1110 u64 min_subvolid = 0;
1113 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1114 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1115 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1116 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1117 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1118 sk->min_offset = min_subvolid;
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);
1131 memcpy(&sh, args.buf, sizeof(sh));
1132 min_subvolid = sh.offset;
1135 * Verify that the root item is really there and we haven't hit
1138 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1139 sk->min_objectid = min_subvolid;
1140 sk->max_objectid = min_subvolid;
1141 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1142 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1144 sk->max_offset = (u64)-1;
1145 sk->min_transid = 0;
1146 sk->max_transid = (u64)-1;
1149 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1154 * Stale orphan, try the next one
1156 if (!sk->nr_items) {
1165 #define SUBVOL_ID_BATCH 1024
1168 * Enumerate all dead subvolumes that exist in the filesystem.
1169 * Fill @ids and reallocate to bigger size if needed.
1171 static int enumerate_dead_subvols(int fd, int count, u64 **ids)
1174 struct btrfs_ioctl_search_args args;
1175 struct btrfs_ioctl_search_key *sk = &args.key;
1178 memset(&args, 0, sizeof(args));
1180 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1181 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1182 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1183 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1184 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1186 sk->max_offset = (u64)-1;
1187 sk->min_transid = 0;
1188 sk->max_transid = (u64)-1;
1189 sk->nr_items = 4096;
1192 struct btrfs_ioctl_search_header *sh;
1196 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1204 for (i = 0; i < sk->nr_items; i++) {
1205 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1208 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1209 *ids[idx] = sh->offset;
1214 count += SUBVOL_ID_BATCH;
1215 newids = (u64*)realloc(*ids, count);
1223 sk->min_objectid = sh->objectid;
1224 sk->min_type = sh->type;
1225 sk->min_offset = sh->offset;
1227 if (sk->min_offset < (u64)-1)
1231 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1233 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1240 static int cmd_subvol_sync(int argc, char **argv)
1245 DIR *dirstream = NULL;
1248 int sleep_interval = 1;
1252 int c = getopt(argc, argv, "s:");
1259 sleep_interval = atoi(argv[optind]);
1260 if (sleep_interval < 1) {
1262 "ERROR: invalid sleep interval %s\n",
1269 usage(cmd_subvol_sync_usage);
1273 if (check_argc_min(argc - optind, 1))
1274 usage(cmd_subvol_sync_usage);
1276 fd = open_file_or_dir(argv[optind], &dirstream);
1278 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1284 id_count = argc - optind;
1286 id_count = SUBVOL_ID_BATCH;
1287 ids = (u64*)malloc(id_count * sizeof(u64));
1289 fprintf(stderr, "ERROR: not enough memory\n");
1293 id_count = enumerate_dead_subvols(fd, id_count, &ids);
1295 fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
1296 strerror(-id_count));
1300 if (id_count == 0) {
1305 ids = (u64*)malloc(id_count * sizeof(u64));
1307 fprintf(stderr, "ERROR: not enough memory\n");
1312 for (i = 0; i < id_count; i++) {
1316 arg = argv[optind + i];
1318 id = strtoull(arg, NULL, 10);
1321 "ERROR: unrecognized subvolume id %s\n",
1326 if (id < BTRFS_FIRST_FREE_OBJECTID
1327 || id > BTRFS_LAST_FREE_OBJECTID) {
1329 "ERROR: subvolume id %s out of range\n",
1338 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1342 close_file_or_dir(fd, dirstream);
1347 static const char subvolume_cmd_group_info[] =
1348 "manage subvolumes: create, delete, list, etc";
1350 const struct cmd_group subvolume_cmd_group = {
1351 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1352 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1353 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1354 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1355 { "snapshot", cmd_snapshot, cmd_snapshot_usage, NULL, 0 },
1356 { "get-default", cmd_subvol_get_default,
1357 cmd_subvol_get_default_usage, NULL, 0 },
1358 { "set-default", cmd_subvol_set_default,
1359 cmd_subvol_set_default_usage, NULL, 0 },
1360 { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 },
1361 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1362 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1367 int cmd_subvolume(int argc, char **argv)
1369 return handle_command_group(&subvolume_cmd_group, argc, argv);