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>
28 #include <uuid/uuid.h>
29 #include <linux/magic.h>
31 #include "kerncompat.h"
38 #include "btrfs-list.h"
42 static int is_subvolume_cleaned(int fd
, u64 subvolid
)
45 struct btrfs_ioctl_search_args args
;
46 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
48 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
49 sk
->min_objectid
= subvolid
;
50 sk
->max_objectid
= subvolid
;
51 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
52 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
54 sk
->max_offset
= (u64
)-1;
56 sk
->max_transid
= (u64
)-1;
59 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
63 if (sk
->nr_items
== 0)
69 static int wait_for_subvolume_cleaning(int fd
, int count
, u64
*ids
,
78 for (i
= 0; i
< count
; i
++) {
81 ret
= is_subvolume_cleaned(fd
, ids
[i
]);
84 "cannot read status of dead subvolume %llu: %s",
85 (unsigned long long)ids
[i
], strerror(-ret
));
89 printf("Subvolume id %llu is gone\n", ids
[i
]);
97 sleep(sleep_interval
);
103 static const char * const subvolume_cmd_group_usage
[] = {
104 "btrfs subvolume <command> <args>",
108 static const char * const cmd_subvol_create_usage
[] = {
109 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
110 "Create a subvolume",
111 "Create a subvolume <name> in <dest>. If <dest> is not given",
112 "subvolume <name> will be created in the current directory.",
114 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
115 " option can be given multiple times.",
119 static int cmd_subvol_create(int argc
, char **argv
)
121 int retval
, res
, len
;
123 char *dupname
= NULL
;
128 struct btrfs_qgroup_inherit
*inherit
= NULL
;
129 DIR *dirstream
= NULL
;
132 int c
= getopt(argc
, argv
, "c:i:");
138 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
145 res
= qgroup_inherit_add_group(&inherit
, optarg
);
152 usage(cmd_subvol_create_usage
);
156 if (check_argc_exact(argc
- optind
, 1))
157 usage(cmd_subvol_create_usage
);
161 retval
= 1; /* failure */
162 res
= test_isdir(dst
);
163 if (res
< 0 && res
!= -ENOENT
) {
164 error("cannot access %s: %s", dst
, strerror(-res
));
168 error("target path already exists: %s", dst
);
172 dupname
= strdup(dst
);
173 newname
= basename(dupname
);
174 dupdir
= strdup(dst
);
175 dstdir
= dirname(dupdir
);
177 if (!test_issubvolname(newname
)) {
178 error("invalid subvolume name: %s", newname
);
182 len
= strlen(newname
);
183 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
184 error("subvolume name too long: %s", newname
);
188 fddst
= btrfs_open_dir(dstdir
, &dirstream
, 1);
192 printf("Create subvolume '%s/%s'\n", dstdir
, newname
);
194 struct btrfs_ioctl_vol_args_v2 args
;
196 memset(&args
, 0, sizeof(args
));
197 strncpy_null(args
.name
, newname
);
198 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
199 args
.size
= qgroup_inherit_size(inherit
);
200 args
.qgroup_inherit
= inherit
;
202 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE_V2
, &args
);
204 struct btrfs_ioctl_vol_args args
;
206 memset(&args
, 0, sizeof(args
));
207 strncpy_null(args
.name
, newname
);
209 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE
, &args
);
213 error("cannot create subvolume: %s", strerror(errno
));
217 retval
= 0; /* success */
219 close_file_or_dir(fddst
, dirstream
);
227 static int wait_for_commit(int fd
)
231 ret
= ioctl(fd
, BTRFS_IOC_START_SYNC
, NULL
);
234 return ioctl(fd
, BTRFS_IOC_WAIT_SYNC
, NULL
);
237 static const char * const cmd_subvol_delete_usage
[] = {
238 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
239 "Delete subvolume(s)",
240 "Delete subvolumes from the filesystem. The corresponding directory",
241 "is removed instantly but the data blocks are removed later.",
242 "The deletion does not involve full commit by default due to",
243 "performance reasons (as a consequence, the subvolume may appear again",
244 "after a crash). Use one of the --commit options to wait until the",
245 "operation is safely stored on the media.",
247 "-c|--commit-after wait for transaction commit at the end of the operation",
248 "-C|--commit-each wait for transaction commit after deleting each subvolume",
249 "-v|--verbose verbose output of operations",
253 static int cmd_subvol_delete(int argc
, char **argv
)
258 struct btrfs_ioctl_vol_args args
;
259 char *dname
, *vname
, *cpath
;
260 char *dupdname
= NULL
;
261 char *dupvname
= NULL
;
263 DIR *dirstream
= NULL
;
266 enum { COMMIT_AFTER
= 1, COMMIT_EACH
= 2 };
270 static const struct option long_options
[] = {
271 {"commit-after", no_argument
, NULL
, 'c'},
272 {"commit-each", no_argument
, NULL
, 'C'},
273 {"verbose", no_argument
, NULL
, 'v'},
277 c
= getopt_long(argc
, argv
, "cCv", long_options
, NULL
);
283 commit_mode
= COMMIT_AFTER
;
286 commit_mode
= COMMIT_EACH
;
292 usage(cmd_subvol_delete_usage
);
296 if (check_argc_min(argc
- optind
, 1))
297 usage(cmd_subvol_delete_usage
);
300 printf("Transaction commit: %s\n",
301 !commit_mode
? "none (default)" :
302 commit_mode
== COMMIT_AFTER
? "at the end" : "after each");
310 res
= test_issubvolume(path
);
312 error("cannot access subvolume %s: %s", path
, strerror(-res
));
317 error("not a subvolume: %s", path
);
322 cpath
= realpath(path
, NULL
);
325 error("cannot find real path for '%s': %s",
326 path
, strerror(errno
));
329 dupdname
= strdup(cpath
);
330 dname
= dirname(dupdname
);
331 dupvname
= strdup(cpath
);
332 vname
= basename(dupvname
);
335 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
341 printf("Delete subvolume (%s): '%s/%s'\n",
342 commit_mode
== COMMIT_EACH
|| (commit_mode
== COMMIT_AFTER
&& cnt
+ 1 == argc
)
343 ? "commit" : "no-commit", dname
, vname
);
344 memset(&args
, 0, sizeof(args
));
345 strncpy_null(args
.name
, vname
);
346 res
= ioctl(fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
348 error("cannot delete '%s/%s': %s", dname
, vname
,
354 if (commit_mode
== COMMIT_EACH
) {
355 res
= wait_for_commit(fd
);
357 error("unable to wait for commit after '%s': %s",
358 path
, strerror(errno
));
370 close_file_or_dir(fd
, dirstream
);
371 /* avoid double free */
377 if (commit_mode
== COMMIT_AFTER
&& fd
!= -1) {
378 res
= wait_for_commit(fd
);
380 error("unable to do final sync after deletion: %s",
385 close_file_or_dir(fd
, dirstream
);
392 * - uppercase for filters and sort options
393 * - lowercase for enabling specific items in the output
395 static const char * const cmd_subvol_list_usage
[] = {
396 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
397 "[--sort=gen,ogen,rootid,path] <path>",
398 "List subvolumes (and snapshots)",
400 "-p print parent ID",
401 "-a print all the subvolumes in the filesystem and",
402 " distinguish absolute and relative path with respect",
403 " to the given <path>",
404 "-c print the ogeneration of the subvolume",
405 "-g print the generation of the subvolume",
406 "-o print only subvolumes below specified path",
407 "-u print the uuid of subvolumes (and snapshots)",
408 "-q print the parent uuid of the snapshots",
409 "-R print the uuid of the received snapshots",
410 "-t print the result as a table",
411 "-s list snapshots only in the filesystem",
412 "-r list readonly subvolumes (including snapshots)",
413 "-d list deleted subvolumes that are not yet cleaned",
415 " filter the subvolumes by generation",
416 " (+value: >= value; -value: <= value; value: = value)",
418 " filter the subvolumes by ogeneration",
419 " (+value: >= value; -value: <= value; value: = value)",
420 "--sort=gen,ogen,rootid,path",
421 " list the subvolume in order of gen, ogen, rootid or path",
422 " you also can add '+' or '-' in front of each items.",
423 " (+:ascending, -:descending, ascending default)",
427 static int cmd_subvol_list(int argc
, char **argv
)
429 struct btrfs_list_filter_set
*filter_set
;
430 struct btrfs_list_comparer_set
*comparer_set
;
434 int ret
= -1, uerr
= 0;
437 int is_only_in_path
= 0;
438 DIR *dirstream
= NULL
;
439 enum btrfs_list_layout layout
= BTRFS_LIST_LAYOUT_DEFAULT
;
441 filter_set
= btrfs_list_alloc_filter_set();
442 comparer_set
= btrfs_list_alloc_comparer_set();
446 static const struct option long_options
[] = {
447 {"sort", required_argument
, NULL
, 'S'},
451 c
= getopt_long(argc
, argv
,
452 "acdgopqsurRG:C:t", long_options
, NULL
);
458 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
464 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
467 btrfs_list_setup_filter(&filter_set
,
468 BTRFS_LIST_FILTER_DELETED
,
472 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
478 layout
= BTRFS_LIST_LAYOUT_TABLE
;
481 btrfs_list_setup_filter(&filter_set
,
482 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
484 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
485 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
488 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
491 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
494 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
497 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
500 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
501 ret
= btrfs_list_parse_filter_string(optarg
,
503 BTRFS_LIST_FILTER_GEN
);
511 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
512 ret
= btrfs_list_parse_filter_string(optarg
,
514 BTRFS_LIST_FILTER_CGEN
);
521 ret
= btrfs_list_parse_sort_string(optarg
,
535 if (check_argc_exact(argc
- optind
, 1)) {
540 subvol
= argv
[optind
];
541 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
544 error("can't access '%s'", subvol
);
549 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
552 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
557 btrfs_list_setup_filter(&filter_set
,
558 BTRFS_LIST_FILTER_FULL_PATH
,
560 else if (is_only_in_path
)
561 btrfs_list_setup_filter(&filter_set
,
562 BTRFS_LIST_FILTER_TOPID_EQUAL
,
565 /* by default we shall print the following columns*/
566 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
567 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
568 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
569 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
571 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
572 layout
, !is_list_all
&& !is_only_in_path
, NULL
);
575 close_file_or_dir(fd
, dirstream
);
581 usage(cmd_subvol_list_usage
);
585 static const char * const cmd_subvol_snapshot_usage
[] = {
586 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
587 "Create a snapshot of the subvolume",
588 "Create a writable/readonly snapshot of the subvolume <source> with",
589 "the name <name> in the <dest> directory. If only <dest> is given,",
590 "the subvolume will be named the basename of <source>.",
592 "-r create a readonly snapshot",
593 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
594 " option can be given multiple times.",
598 static int cmd_subvol_snapshot(int argc
, char **argv
)
602 int fd
= -1, fddst
= -1;
603 int len
, readonly
= 0;
604 char *dupname
= NULL
;
608 struct btrfs_ioctl_vol_args_v2 args
;
609 struct btrfs_qgroup_inherit
*inherit
= NULL
;
610 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
612 memset(&args
, 0, sizeof(args
));
614 int c
= getopt(argc
, argv
, "c:i:r");
620 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
627 res
= qgroup_inherit_add_group(&inherit
, optarg
);
637 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 1);
644 usage(cmd_subvol_snapshot_usage
);
648 if (check_argc_exact(argc
- optind
, 2))
649 usage(cmd_subvol_snapshot_usage
);
651 subvol
= argv
[optind
];
652 dst
= argv
[optind
+ 1];
654 retval
= 1; /* failure */
655 res
= test_issubvolume(subvol
);
657 error("cannot access subvolume %s: %s", subvol
, strerror(-res
));
661 error("not a subvolume: %s", subvol
);
665 res
= test_isdir(dst
);
666 if (res
< 0 && res
!= -ENOENT
) {
667 error("cannot access %s: %s", dst
, strerror(-res
));
671 error("'%s' exists and it is not a directory", dst
);
676 dupname
= strdup(subvol
);
677 newname
= basename(dupname
);
680 dupname
= strdup(dst
);
681 newname
= basename(dupname
);
682 dupdir
= strdup(dst
);
683 dstdir
= dirname(dupdir
);
686 if (!test_issubvolname(newname
)) {
687 error("invalid snapshot name '%s'", newname
);
691 len
= strlen(newname
);
692 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
693 error("snapshot name too long '%s'", newname
);
697 fddst
= btrfs_open_dir(dstdir
, &dirstream1
, 1);
701 fd
= btrfs_open_dir(subvol
, &dirstream2
, 1);
706 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
707 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
708 subvol
, dstdir
, newname
);
710 printf("Create a snapshot of '%s' in '%s/%s'\n",
711 subvol
, dstdir
, newname
);
716 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
717 args
.size
= qgroup_inherit_size(inherit
);
718 args
.qgroup_inherit
= inherit
;
720 strncpy_null(args
.name
, newname
);
722 res
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
725 error("cannot snapshot '%s': %s", subvol
, strerror(errno
));
729 retval
= 0; /* success */
732 close_file_or_dir(fddst
, dirstream1
);
733 close_file_or_dir(fd
, dirstream2
);
741 static const char * const cmd_subvol_get_default_usage
[] = {
742 "btrfs subvolume get-default <path>",
743 "Get the default subvolume of a filesystem",
747 static int cmd_subvol_get_default(int argc
, char **argv
)
752 struct btrfs_list_filter_set
*filter_set
;
754 DIR *dirstream
= NULL
;
756 clean_args_no_options(argc
, argv
, cmd_subvol_get_default_usage
);
758 if (check_argc_exact(argc
- optind
, 1))
759 usage(cmd_subvol_get_default_usage
);
762 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
766 ret
= btrfs_list_get_default_subvolume(fd
, &default_id
);
768 error("failed to look up default subvolume: %s",
774 if (default_id
== 0) {
775 error("'default' dir item not found");
779 /* no need to resolve roots if FS_TREE is default */
780 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
781 printf("ID 5 (FS_TREE)\n");
786 filter_set
= btrfs_list_alloc_filter_set();
787 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_ROOTID
,
790 /* by default we shall print the following columns*/
791 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
792 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
793 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
794 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
796 ret
= btrfs_list_subvols_print(fd
, filter_set
, NULL
,
797 BTRFS_LIST_LAYOUT_DEFAULT
, 1, NULL
);
802 close_file_or_dir(fd
, dirstream
);
806 static const char * const cmd_subvol_set_default_usage
[] = {
807 "btrfs subvolume set-default <subvolid> <path>",
808 "Set the default subvolume of a filesystem",
812 static int cmd_subvol_set_default(int argc
, char **argv
)
818 DIR *dirstream
= NULL
;
820 clean_args_no_options(argc
, argv
, cmd_subvol_set_default_usage
);
822 if (check_argc_exact(argc
- optind
, 2))
823 usage(cmd_subvol_set_default_usage
);
825 subvolid
= argv
[optind
];
826 path
= argv
[optind
+ 1];
828 objectid
= arg_strtou64(subvolid
);
830 fd
= btrfs_open_dir(path
, &dirstream
, 1);
834 ret
= ioctl(fd
, BTRFS_IOC_DEFAULT_SUBVOL
, &objectid
);
836 close_file_or_dir(fd
, dirstream
);
838 error("unable to set a new default subvolume: %s",
845 static const char * const cmd_subvol_find_new_usage
[] = {
846 "btrfs subvolume find-new <path> <lastgen>",
847 "List the recently modified files in a filesystem",
851 static int cmd_subvol_find_new(int argc
, char **argv
)
857 DIR *dirstream
= NULL
;
859 clean_args_no_options(argc
, argv
, cmd_subvol_find_new_usage
);
861 if (check_argc_exact(argc
- optind
, 2))
862 usage(cmd_subvol_find_new_usage
);
864 subvol
= argv
[optind
];
865 last_gen
= arg_strtou64(argv
[optind
+ 1]);
867 ret
= test_issubvolume(subvol
);
869 error("cannot access subvolume %s: %s", subvol
, strerror(-ret
));
873 error("not a subvolume: %s", subvol
);
877 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
881 ret
= ioctl(fd
, BTRFS_IOC_SYNC
);
883 error("sync ioctl failed on '%s': %s",
884 subvol
, strerror(errno
));
885 close_file_or_dir(fd
, dirstream
);
889 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
890 close_file_or_dir(fd
, dirstream
);
894 static const char * const cmd_subvol_show_usage
[] = {
895 "btrfs subvolume show [options] <subvol-path>|<mnt>",
896 "Show more information about the subvolume",
897 "-r|--rootid rootid of the subvolume",
898 "-u|--uuid uuid of the subvolume",
900 "If no option is specified, <subvol-path> will be shown, otherwise",
901 "the rootid or uuid are resolved relative to the <mnt> path.",
905 static int cmd_subvol_show(int argc
, char **argv
)
907 struct root_info get_ri
;
908 struct btrfs_list_filter_set
*filter_set
= NULL
;
910 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
911 char *fullpath
= NULL
;
912 char raw_prefix
[] = "\t\t\t\t";
915 DIR *dirstream1
= NULL
;
919 u8 uuid_arg
[BTRFS_UUID_SIZE
];
923 static const struct option long_options
[] = {
924 { "rootid", required_argument
, NULL
, 'r'},
925 { "uuid", required_argument
, NULL
, 'u'},
929 c
= getopt_long(argc
, argv
, "r:u:", long_options
, NULL
);
935 rootid_arg
= arg_strtou64(optarg
);
939 uuid_parse(optarg
, uuid_arg
);
943 usage(cmd_subvol_show_usage
);
947 if (check_argc_exact(argc
- optind
, 1))
948 usage(cmd_subvol_show_usage
);
950 if (by_rootid
&& by_uuid
) {
952 "options --rootid and --uuid cannot be used at the same time");
953 usage(cmd_subvol_show_usage
);
956 memset(&get_ri
, 0, sizeof(get_ri
));
957 fullpath
= realpath(argv
[optind
], NULL
);
959 error("cannot find real path for '%s': %s",
960 argv
[optind
], strerror(errno
));
965 ret
= get_subvol_info_by_rootid(fullpath
, &get_ri
, rootid_arg
);
966 } else if (by_uuid
) {
967 ret
= get_subvol_info_by_uuid(fullpath
, &get_ri
, uuid_arg
);
969 ret
= get_subvol_info(fullpath
, &get_ri
);
974 error("Failed to get subvol info %s: %s",
975 fullpath
, strerror(-ret
));
977 error("Failed to get subvol info %s: %d",
984 printf("%s\n", get_ri
.full_path
);
985 printf("\tName: \t\t\t%s\n", get_ri
.name
);
987 if (uuid_is_null(get_ri
.uuid
))
988 strcpy(uuidparse
, "-");
990 uuid_unparse(get_ri
.uuid
, uuidparse
);
991 printf("\tUUID: \t\t\t%s\n", uuidparse
);
993 if (uuid_is_null(get_ri
.puuid
))
994 strcpy(uuidparse
, "-");
996 uuid_unparse(get_ri
.puuid
, uuidparse
);
997 printf("\tParent UUID: \t\t%s\n", uuidparse
);
999 if (uuid_is_null(get_ri
.ruuid
))
1000 strcpy(uuidparse
, "-");
1002 uuid_unparse(get_ri
.ruuid
, uuidparse
);
1003 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1008 localtime_r(&get_ri
.otime
, &tm
);
1009 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1012 printf("\tCreation time: \t\t%s\n", tstr
);
1014 printf("\tSubvolume ID: \t\t%llu\n", get_ri
.root_id
);
1015 printf("\tGeneration: \t\t%llu\n", get_ri
.gen
);
1016 printf("\tGen at creation: \t%llu\n", get_ri
.ogen
);
1017 printf("\tParent ID: \t\t%llu\n", get_ri
.ref_tree
);
1018 printf("\tTop level ID: \t\t%llu\n", get_ri
.top_id
);
1020 if (get_ri
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1021 printf("\tFlags: \t\t\treadonly\n");
1023 printf("\tFlags: \t\t\t-\n");
1025 /* print the snapshots of the given subvol if any*/
1026 printf("\tSnapshot(s):\n");
1027 filter_set
= btrfs_list_alloc_filter_set();
1028 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_BY_PARENT
,
1029 (u64
)(unsigned long)get_ri
.uuid
);
1030 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
1032 fd
= open_file_or_dir(fullpath
, &dirstream1
);
1034 fprintf(stderr
, "ERROR: can't access '%s'\n", fullpath
);
1037 btrfs_list_subvols_print(fd
, filter_set
, NULL
, BTRFS_LIST_LAYOUT_RAW
,
1044 free(get_ri
.full_path
);
1047 close_file_or_dir(fd
, dirstream1
);
1052 static const char * const cmd_subvol_sync_usage
[] = {
1053 "btrfs subvolume sync <path> [<subvol-id>...]",
1054 "Wait until given subvolume(s) are completely removed from the filesystem.",
1055 "Wait until given subvolume(s) are completely removed from the filesystem",
1057 "If no subvolume id is given, wait until all current deletion requests",
1058 "are completed, but do not wait for subvolumes deleted meanwhile.",
1059 "The status of subvolume ids is checked periodically.",
1061 "-s <N> sleep N seconds between checks (default: 1)",
1067 * If we're looking for any dead subvolume, take a shortcut and look
1068 * for any ORPHAN_ITEMs in the tree root
1070 static int fs_has_dead_subvolumes(int fd
)
1073 struct btrfs_ioctl_search_args args
;
1074 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1075 struct btrfs_ioctl_search_header sh
;
1076 u64 min_subvolid
= 0;
1079 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1080 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1081 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1082 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1083 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1084 sk
->min_offset
= min_subvolid
;
1085 sk
->max_offset
= (u64
)-1;
1086 sk
->min_transid
= 0;
1087 sk
->max_transid
= (u64
)-1;
1090 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1097 memcpy(&sh
, args
.buf
, sizeof(sh
));
1098 min_subvolid
= sh
.offset
;
1101 * Verify that the root item is really there and we haven't hit
1104 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1105 sk
->min_objectid
= min_subvolid
;
1106 sk
->max_objectid
= min_subvolid
;
1107 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
1108 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
1110 sk
->max_offset
= (u64
)-1;
1111 sk
->min_transid
= 0;
1112 sk
->max_transid
= (u64
)-1;
1115 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1120 * Stale orphan, try the next one
1122 if (!sk
->nr_items
) {
1131 #define SUBVOL_ID_BATCH 1024
1134 * Enumerate all dead subvolumes that exist in the filesystem.
1135 * Fill @ids and reallocate to bigger size if needed.
1137 static int enumerate_dead_subvols(int fd
, u64
**ids
)
1140 struct btrfs_ioctl_search_args args
;
1141 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1145 memset(&args
, 0, sizeof(args
));
1147 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1148 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1149 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1150 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1151 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1153 sk
->max_offset
= (u64
)-1;
1154 sk
->min_transid
= 0;
1155 sk
->max_transid
= (u64
)-1;
1156 sk
->nr_items
= 4096;
1160 struct btrfs_ioctl_search_header
*sh
;
1164 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1172 for (i
= 0; i
< sk
->nr_items
; i
++) {
1173 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
1176 if (btrfs_search_header_type(sh
)
1177 == BTRFS_ORPHAN_ITEM_KEY
) {
1181 count
+= SUBVOL_ID_BATCH
;
1182 newids
= (u64
*)realloc(*ids
,
1183 count
* sizeof(u64
));
1188 (*ids
)[idx
] = btrfs_search_header_offset(sh
);
1191 off
+= btrfs_search_header_len(sh
);
1193 sk
->min_objectid
= btrfs_search_header_objectid(sh
);
1194 sk
->min_type
= btrfs_search_header_type(sh
);
1195 sk
->min_offset
= btrfs_search_header_offset(sh
);
1197 if (sk
->min_offset
< (u64
)-1)
1201 if (sk
->min_type
!= BTRFS_ORPHAN_ITEM_KEY
)
1203 if (sk
->min_objectid
!= BTRFS_ORPHAN_OBJECTID
)
1210 static int cmd_subvol_sync(int argc
, char **argv
)
1215 DIR *dirstream
= NULL
;
1218 int sleep_interval
= 1;
1221 int c
= getopt(argc
, argv
, "s:");
1228 sleep_interval
= atoi(optarg
);
1229 if (sleep_interval
< 1) {
1230 error("invalid sleep interval %s", optarg
);
1236 usage(cmd_subvol_sync_usage
);
1240 if (check_argc_min(argc
- optind
, 1))
1241 usage(cmd_subvol_sync_usage
);
1243 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1250 id_count
= argc
- optind
;
1252 id_count
= enumerate_dead_subvols(fd
, &ids
);
1254 error("can't enumerate dead subvolumes: %s",
1255 strerror(-id_count
));
1259 if (id_count
== 0) {
1264 ids
= (u64
*)malloc(id_count
* sizeof(u64
));
1266 error("not enough memory");
1271 for (i
= 0; i
< id_count
; i
++) {
1275 arg
= argv
[optind
+ i
];
1277 id
= strtoull(arg
, NULL
, 10);
1279 error("unrecognized subvolume id %s", arg
);
1283 if (id
< BTRFS_FIRST_FREE_OBJECTID
1284 || id
> BTRFS_LAST_FREE_OBJECTID
) {
1285 error("subvolume id %s out of range", arg
);
1293 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1297 close_file_or_dir(fd
, dirstream
);
1302 static const char subvolume_cmd_group_info
[] =
1303 "manage subvolumes: create, delete, list, etc";
1305 const struct cmd_group subvolume_cmd_group
= {
1306 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1307 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1308 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1309 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1310 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1312 { "get-default", cmd_subvol_get_default
,
1313 cmd_subvol_get_default_usage
, NULL
, 0 },
1314 { "set-default", cmd_subvol_set_default
,
1315 cmd_subvol_set_default_usage
, NULL
, 0 },
1316 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1318 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1319 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1324 int cmd_subvolume(int argc
, char **argv
)
1326 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);