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 memset(&args, 0, sizeof(args));
361 strncpy_null(args.name, vname);
362 res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
366 fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
367 dname, vname, strerror(e));
372 if (commit_mode == 1) {
373 res = wait_for_commit(fd);
376 "ERROR: unable to wait for commit after '%s': %s\n",
377 path, strerror(errno));
389 close_file_or_dir(fd, dirstream);
390 /* avoid double free */
396 if (commit_mode == 2 && fd != -1) {
397 res = wait_for_commit(fd);
400 "ERROR: unable to do final sync: %s\n",
405 close_file_or_dir(fd, dirstream);
412 * - uppercase for filters and sort options
413 * - lowercase for enabling specific items in the output
415 static const char * const cmd_subvol_list_usage[] = {
416 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
417 "[--sort=gen,ogen,rootid,path] <path>",
418 "List subvolumes (and snapshots)",
420 "-p print parent ID",
421 "-a print all the subvolumes in the filesystem and",
422 " distinguish absolute and relative path with respect",
423 " to the given <path>",
424 "-c print the ogeneration of the subvolume",
425 "-g print the generation of the subvolume",
426 "-o print only subvolumes below specified path",
427 "-u print the uuid of subvolumes (and snapshots)",
428 "-q print the parent uuid of the snapshots",
429 "-R print the uuid of the received snapshots",
430 "-t print the result as a table",
431 "-s list snapshots only in the filesystem",
432 "-r list readonly subvolumes (including snapshots)",
433 "-d list deleted subvolumes that are not yet cleaned",
435 " filter the subvolumes by generation",
436 " (+value: >= value; -value: <= value; value: = value)",
438 " filter the subvolumes by ogeneration",
439 " (+value: >= value; -value: <= value; value: = value)",
440 "--sort=gen,ogen,rootid,path",
441 " list the subvolume in order of gen, ogen, rootid or path",
442 " you also can add '+' or '-' in front of each items.",
443 " (+:ascending, -:descending, ascending default)",
447 static int cmd_subvol_list(int argc, char **argv)
449 struct btrfs_list_filter_set *filter_set;
450 struct btrfs_list_comparer_set *comparer_set;
454 int ret = -1, uerr = 0;
456 int is_tab_result = 0;
458 int is_only_in_path = 0;
459 DIR *dirstream = NULL;
461 filter_set = btrfs_list_alloc_filter_set();
462 comparer_set = btrfs_list_alloc_comparer_set();
467 static const struct option long_options[] = {
468 {"sort", required_argument, NULL, 'S'},
472 c = getopt_long(argc, argv,
473 "acdgopqsurRG:C:t", long_options, NULL);
479 btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
485 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
488 btrfs_list_setup_filter(&filter_set,
489 BTRFS_LIST_FILTER_DELETED,
493 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
502 btrfs_list_setup_filter(&filter_set,
503 BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
505 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
506 btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
509 btrfs_list_setup_print_column(BTRFS_LIST_UUID);
512 btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
515 btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
518 flags |= BTRFS_ROOT_SUBVOL_RDONLY;
521 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
522 ret = btrfs_list_parse_filter_string(optarg,
524 BTRFS_LIST_FILTER_GEN);
532 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
533 ret = btrfs_list_parse_filter_string(optarg,
535 BTRFS_LIST_FILTER_CGEN);
542 ret = btrfs_list_parse_sort_string(optarg,
557 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
560 if (check_argc_exact(argc - optind, 1)) {
565 subvol = argv[optind];
566 fd = open_file_or_dir(subvol, &dirstream);
569 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
573 ret = btrfs_list_get_path_rootid(fd, &top_id);
575 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", subvol);
580 btrfs_list_setup_filter(&filter_set,
581 BTRFS_LIST_FILTER_FULL_PATH,
583 else if (is_only_in_path)
584 btrfs_list_setup_filter(&filter_set,
585 BTRFS_LIST_FILTER_TOPID_EQUAL,
588 /* by default we shall print the following columns*/
589 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
590 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
591 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
592 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
595 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
596 BTRFS_LIST_LAYOUT_TABLE,
597 !is_list_all && !is_only_in_path, NULL);
599 ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
600 BTRFS_LIST_LAYOUT_DEFAULT,
601 !is_list_all && !is_only_in_path, NULL);
604 close_file_or_dir(fd, dirstream);
606 btrfs_list_free_filter_set(filter_set);
608 btrfs_list_free_comparer_set(comparer_set);
610 usage(cmd_subvol_list_usage);
614 static const char * const cmd_subvol_snapshot_usage[] = {
615 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
616 "Create a snapshot of the subvolume",
617 "Create a writable/readonly snapshot of the subvolume <source> with",
618 "the name <name> in the <dest> directory. If only <dest> is given,",
619 "the subvolume will be named the basename of <source>.",
621 "-r create a readonly snapshot",
622 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
623 " option can be given multiple times.",
627 static int cmd_subvol_snapshot(int argc, char **argv)
631 int fd = -1, fddst = -1;
632 int len, readonly = 0;
633 char *dupname = NULL;
637 struct btrfs_ioctl_vol_args_v2 args;
638 struct btrfs_qgroup_inherit *inherit = NULL;
639 DIR *dirstream1 = NULL, *dirstream2 = NULL;
642 memset(&args, 0, sizeof(args));
644 int c = getopt(argc, argv, "c:i:r");
650 res = qgroup_inherit_add_copy(&inherit, optarg, 0);
657 res = qgroup_inherit_add_group(&inherit, optarg);
667 res = qgroup_inherit_add_copy(&inherit, optarg, 1);
674 usage(cmd_subvol_snapshot_usage);
678 if (check_argc_exact(argc - optind, 2))
679 usage(cmd_subvol_snapshot_usage);
681 subvol = argv[optind];
682 dst = argv[optind + 1];
684 retval = 1; /* failure */
685 res = test_issubvolume(subvol);
687 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
691 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
695 res = test_isdir(dst);
697 fprintf(stderr, "ERROR: '%s' exists and it is not a directory\n", dst);
702 dupname = strdup(subvol);
703 newname = basename(dupname);
706 dupname = strdup(dst);
707 newname = basename(dupname);
708 dupdir = strdup(dst);
709 dstdir = dirname(dupdir);
712 if (!test_issubvolname(newname)) {
713 fprintf(stderr, "ERROR: incorrect snapshot name '%s'\n",
718 len = strlen(newname);
719 if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
720 fprintf(stderr, "ERROR: snapshot name too long '%s'\n",
725 fddst = open_file_or_dir(dstdir, &dirstream1);
727 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
731 fd = open_file_or_dir(subvol, &dirstream2);
733 fprintf(stderr, "ERROR: can't access '%s'\n", dstdir);
738 args.flags |= BTRFS_SUBVOL_RDONLY;
739 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
740 subvol, dstdir, newname);
742 printf("Create a snapshot of '%s' in '%s/%s'\n",
743 subvol, dstdir, newname);
748 args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
749 args.size = qgroup_inherit_size(inherit);
750 args.qgroup_inherit = inherit;
752 strncpy_null(args.name, newname);
754 res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
757 fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
758 subvol, strerror(errno));
762 retval = 0; /* success */
765 close_file_or_dir(fddst, dirstream1);
766 close_file_or_dir(fd, dirstream2);
774 static const char * const cmd_subvol_get_default_usage[] = {
775 "btrfs subvolume get-default <path>",
776 "Get the default subvolume of a filesystem",
780 static int cmd_subvol_get_default(int argc, char **argv)
785 struct btrfs_list_filter_set *filter_set;
787 DIR *dirstream = NULL;
789 if (check_argc_exact(argc, 2))
790 usage(cmd_subvol_get_default_usage);
793 fd = open_file_or_dir(subvol, &dirstream);
795 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
799 ret = btrfs_list_get_default_subvolume(fd, &default_id);
801 fprintf(stderr, "ERROR: can't perform the search - %s\n",
807 if (default_id == 0) {
808 fprintf(stderr, "ERROR: 'default' dir item not found\n");
812 /* no need to resolve roots if FS_TREE is default */
813 if (default_id == BTRFS_FS_TREE_OBJECTID) {
814 printf("ID 5 (FS_TREE)\n");
819 filter_set = btrfs_list_alloc_filter_set();
820 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
823 /* by default we shall print the following columns*/
824 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
825 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
826 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
827 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
829 ret = btrfs_list_subvols_print(fd, filter_set, NULL,
830 BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
833 btrfs_list_free_filter_set(filter_set);
835 close_file_or_dir(fd, dirstream);
839 static const char * const cmd_subvol_set_default_usage[] = {
840 "btrfs subvolume set-default <subvolid> <path>",
841 "Set the default subvolume of a filesystem",
845 static int cmd_subvol_set_default(int argc, char **argv)
851 DIR *dirstream = NULL;
853 if (check_argc_exact(argc, 3))
854 usage(cmd_subvol_set_default_usage);
859 objectid = arg_strtou64(subvolid);
861 fd = open_file_or_dir(path, &dirstream);
863 fprintf(stderr, "ERROR: can't access '%s'\n", path);
867 ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
869 close_file_or_dir(fd, dirstream);
871 fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n",
878 static const char * const cmd_subvol_find_new_usage[] = {
879 "btrfs subvolume find-new <path> <lastgen>",
880 "List the recently modified files in a filesystem",
884 static int cmd_subvol_find_new(int argc, char **argv)
890 DIR *dirstream = NULL;
892 if (check_argc_exact(argc, 3))
893 usage(cmd_subvol_find_new_usage);
896 last_gen = arg_strtou64(argv[2]);
898 ret = test_issubvolume(subvol);
900 fprintf(stderr, "ERROR: error accessing '%s'\n", subvol);
904 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol);
908 fd = open_file_or_dir(subvol, &dirstream);
910 fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
914 ret = ioctl(fd, BTRFS_IOC_SYNC);
916 fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n",
917 subvol, strerror(errno));
918 close_file_or_dir(fd, dirstream);
922 ret = btrfs_list_find_updated_files(fd, 0, last_gen);
923 close_file_or_dir(fd, dirstream);
927 static const char * const cmd_subvol_show_usage[] = {
928 "btrfs subvolume show <subvol-path>",
929 "Show more information of the subvolume",
933 static int cmd_subvol_show(int argc, char **argv)
935 struct root_info get_ri;
936 struct btrfs_list_filter_set *filter_set;
938 char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
939 char *fullpath = NULL, *svpath = NULL, *mnt = NULL;
940 char raw_prefix[] = "\t\t\t\t";
942 int fd = -1, mntfd = -1;
944 DIR *dirstream1 = NULL, *dirstream2 = NULL;
946 if (check_argc_exact(argc, 2))
947 usage(cmd_subvol_show_usage);
949 fullpath = realpath(argv[1], NULL);
951 fprintf(stderr, "ERROR: finding real path for '%s', %s\n",
952 argv[1], strerror(errno));
956 ret = test_issubvolume(fullpath);
958 fprintf(stderr, "ERROR: error accessing '%s'\n", fullpath);
962 fprintf(stderr, "ERROR: '%s' is not a subvolume\n", fullpath);
967 ret = find_mount_root(fullpath, &mnt);
969 fprintf(stderr, "ERROR: find_mount_root failed on '%s': "
970 "%s\n", fullpath, strerror(-ret));
975 "ERROR: %s doesn't belong to btrfs mount point\n",
980 svpath = get_subvol_name(mnt, fullpath);
982 fd = open_file_or_dir(fullpath, &dirstream1);
984 fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
988 ret = btrfs_list_get_path_rootid(fd, &sv_id);
990 fprintf(stderr, "ERROR: can't get rootid for '%s'\n",
995 mntfd = open_file_or_dir(mnt, &dirstream2);
997 fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
1001 ret = btrfs_list_get_path_rootid(mntfd, &mntid);
1003 fprintf(stderr, "ERROR: can't get rootid for '%s'\n", mnt);
1007 if (sv_id == BTRFS_FS_TREE_OBJECTID) {
1008 printf("%s is btrfs root\n", fullpath);
1012 memset(&get_ri, 0, sizeof(get_ri));
1013 get_ri.root_id = sv_id;
1015 ret = btrfs_get_subvol(mntfd, &get_ri);
1017 fprintf(stderr, "ERROR: can't find '%s'\n",
1022 /* print the info */
1023 printf("%s\n", fullpath);
1024 printf("\tName: \t\t\t%s\n", get_ri.name);
1026 if (uuid_is_null(get_ri.uuid))
1027 strcpy(uuidparse, "-");
1029 uuid_unparse(get_ri.uuid, uuidparse);
1030 printf("\tUUID: \t\t\t%s\n", uuidparse);
1032 if (uuid_is_null(get_ri.puuid))
1033 strcpy(uuidparse, "-");
1035 uuid_unparse(get_ri.puuid, uuidparse);
1036 printf("\tParent UUID: \t\t%s\n", uuidparse);
1038 if (uuid_is_null(get_ri.ruuid))
1039 strcpy(uuidparse, "-");
1041 uuid_unparse(get_ri.ruuid, uuidparse);
1042 printf("\tReceived UUID: \t\t%s\n", uuidparse);
1047 localtime_r(&get_ri.otime, &tm);
1048 strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
1051 printf("\tCreation time: \t\t%s\n", tstr);
1053 printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
1054 printf("\tGeneration: \t\t%llu\n", get_ri.gen);
1055 printf("\tGen at creation: \t%llu\n", get_ri.ogen);
1056 printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
1057 printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
1059 if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
1060 printf("\tFlags: \t\t\treadonly\n");
1062 printf("\tFlags: \t\t\t-\n");
1064 /* print the snapshots of the given subvol if any*/
1065 printf("\tSnapshot(s):\n");
1066 filter_set = btrfs_list_alloc_filter_set();
1067 btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
1068 (u64)(unsigned long)get_ri.uuid);
1069 btrfs_list_setup_print_column(BTRFS_LIST_PATH);
1070 btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
1076 free(get_ri.full_path);
1077 btrfs_list_free_filter_set(filter_set);
1080 close_file_or_dir(fd, dirstream1);
1081 close_file_or_dir(mntfd, dirstream2);
1087 static const char * const cmd_subvol_sync_usage[] = {
1088 "btrfs subvolume sync <path> [<subvol-id>...]",
1089 "Wait until given subvolume(s) are completely removed from the filesystem.",
1090 "Wait until given subvolume(s) are completely removed from the filesystem",
1092 "If no subvolume id is given, wait until all current deletion requests",
1093 "are completed, but do not wait for subvolumes deleted meanwhile.",
1094 "The status of subvolume ids is checked periodically.",
1096 "-s <N> sleep N seconds between checks (default: 1)",
1102 * If we're looking for any dead subvolume, take a shortcut and look
1103 * for any ORPHAN_ITEMs in the tree root
1105 static int fs_has_dead_subvolumes(int fd)
1108 struct btrfs_ioctl_search_args args;
1109 struct btrfs_ioctl_search_key *sk = &args.key;
1110 struct btrfs_ioctl_search_header sh;
1111 u64 min_subvolid = 0;
1114 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1115 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1116 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1117 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1118 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1119 sk->min_offset = min_subvolid;
1120 sk->max_offset = (u64)-1;
1121 sk->min_transid = 0;
1122 sk->max_transid = (u64)-1;
1125 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1132 memcpy(&sh, args.buf, sizeof(sh));
1133 min_subvolid = sh.offset;
1136 * Verify that the root item is really there and we haven't hit
1139 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1140 sk->min_objectid = min_subvolid;
1141 sk->max_objectid = min_subvolid;
1142 sk->min_type = BTRFS_ROOT_ITEM_KEY;
1143 sk->max_type = BTRFS_ROOT_ITEM_KEY;
1145 sk->max_offset = (u64)-1;
1146 sk->min_transid = 0;
1147 sk->max_transid = (u64)-1;
1150 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1155 * Stale orphan, try the next one
1157 if (!sk->nr_items) {
1166 #define SUBVOL_ID_BATCH 1024
1169 * Enumerate all dead subvolumes that exist in the filesystem.
1170 * Fill @ids and reallocate to bigger size if needed.
1172 static int enumerate_dead_subvols(int fd, int count, u64 **ids)
1175 struct btrfs_ioctl_search_args args;
1176 struct btrfs_ioctl_search_key *sk = &args.key;
1179 memset(&args, 0, sizeof(args));
1181 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
1182 sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
1183 sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
1184 sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
1185 sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
1187 sk->max_offset = (u64)-1;
1188 sk->min_transid = 0;
1189 sk->max_transid = (u64)-1;
1190 sk->nr_items = 4096;
1193 struct btrfs_ioctl_search_header *sh;
1197 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
1205 for (i = 0; i < sk->nr_items; i++) {
1206 sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
1209 if (sh->type == BTRFS_ORPHAN_ITEM_KEY) {
1210 *ids[idx] = sh->offset;
1215 count += SUBVOL_ID_BATCH;
1216 newids = (u64*)realloc(*ids, count);
1224 sk->min_objectid = sh->objectid;
1225 sk->min_type = sh->type;
1226 sk->min_offset = sh->offset;
1228 if (sk->min_offset < (u64)-1)
1232 if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
1234 if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
1241 static int cmd_subvol_sync(int argc, char **argv)
1246 DIR *dirstream = NULL;
1249 int sleep_interval = 1;
1253 int c = getopt(argc, argv, "s:");
1260 sleep_interval = atoi(argv[optind]);
1261 if (sleep_interval < 1) {
1263 "ERROR: invalid sleep interval %s\n",
1270 usage(cmd_subvol_sync_usage);
1274 if (check_argc_min(argc - optind, 1))
1275 usage(cmd_subvol_sync_usage);
1277 fd = open_file_or_dir(argv[optind], &dirstream);
1279 fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
1285 id_count = argc - optind;
1287 id_count = SUBVOL_ID_BATCH;
1288 ids = (u64*)malloc(id_count * sizeof(u64));
1290 fprintf(stderr, "ERROR: not enough memory\n");
1294 id_count = enumerate_dead_subvols(fd, id_count, &ids);
1296 fprintf(stderr, "ERROR: can't enumerate dead subvolumes: %s\n",
1297 strerror(-id_count));
1301 if (id_count == 0) {
1306 ids = (u64*)malloc(id_count * sizeof(u64));
1308 fprintf(stderr, "ERROR: not enough memory\n");
1313 for (i = 0; i < id_count; i++) {
1317 arg = argv[optind + i];
1319 id = strtoull(arg, NULL, 10);
1322 "ERROR: unrecognized subvolume id %s\n",
1327 if (id < BTRFS_FIRST_FREE_OBJECTID
1328 || id > BTRFS_LAST_FREE_OBJECTID) {
1330 "ERROR: subvolume id %s out of range\n",
1339 ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
1343 close_file_or_dir(fd, dirstream);
1348 static const char subvolume_cmd_group_info[] =
1349 "manage subvolumes: create, delete, list, etc";
1351 const struct cmd_group subvolume_cmd_group = {
1352 subvolume_cmd_group_usage, subvolume_cmd_group_info, {
1353 { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
1354 { "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
1355 { "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
1356 { "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
1358 { "get-default", cmd_subvol_get_default,
1359 cmd_subvol_get_default_usage, NULL, 0 },
1360 { "set-default", cmd_subvol_set_default,
1361 cmd_subvol_set_default_usage, NULL, 0 },
1362 { "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
1364 { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
1365 { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
1370 int cmd_subvolume(int argc, char **argv)
1372 return handle_command_group(&subvolume_cmd_group, argc, argv);