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: %m");
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 u8 fsid
[BTRFS_FSID_SIZE
];
267 char uuidbuf
[BTRFS_UUID_UNPARSED_SIZE
];
268 struct seen_fsid
*seen_fsid_hash
[SEEN_FSID_HASH_SIZE
] = { NULL
, };
269 enum { COMMIT_AFTER
= 1, COMMIT_EACH
= 2 };
273 static const struct option long_options
[] = {
274 {"commit-after", no_argument
, NULL
, 'c'},
275 {"commit-each", no_argument
, NULL
, 'C'},
276 {"verbose", no_argument
, NULL
, 'v'},
280 c
= getopt_long(argc
, argv
, "cCv", long_options
, NULL
);
286 commit_mode
= COMMIT_AFTER
;
289 commit_mode
= COMMIT_EACH
;
295 usage(cmd_subvol_delete_usage
);
299 if (check_argc_min(argc
- optind
, 1))
300 usage(cmd_subvol_delete_usage
);
303 printf("Transaction commit: %s\n",
304 !commit_mode
? "none (default)" :
305 commit_mode
== COMMIT_AFTER
? "at the end" : "after each");
313 res
= test_issubvolume(path
);
315 error("cannot access subvolume %s: %s", path
, strerror(-res
));
320 error("not a subvolume: %s", path
);
325 cpath
= realpath(path
, NULL
);
328 error("cannot find real path for '%s': %m", path
);
331 dupdname
= strdup(cpath
);
332 dname
= dirname(dupdname
);
333 dupvname
= strdup(cpath
);
334 vname
= basename(dupvname
);
337 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
343 printf("Delete subvolume (%s): '%s/%s'\n",
344 commit_mode
== COMMIT_EACH
|| (commit_mode
== COMMIT_AFTER
&& cnt
+ 1 == argc
)
345 ? "commit" : "no-commit", dname
, vname
);
346 memset(&args
, 0, sizeof(args
));
347 strncpy_null(args
.name
, vname
);
348 res
= ioctl(fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
350 error("cannot delete '%s/%s': %m", dname
, vname
);
355 if (commit_mode
== COMMIT_EACH
) {
356 res
= wait_for_commit(fd
);
358 error("unable to wait for commit after '%s': %m", path
);
361 } else if (commit_mode
== COMMIT_AFTER
) {
362 res
= get_fsid(dname
, fsid
, 0);
364 error("unable to get fsid for '%s': %s",
365 path
, strerror(-res
));
367 "delete suceeded but commit may not be done in the end");
372 if (add_seen_fsid(fsid
, seen_fsid_hash
, fd
, dirstream
) == 0) {
374 uuid_unparse(fsid
, uuidbuf
);
375 printf(" new fs is found for '%s', fsid: %s\n",
379 * This is the first time a subvolume on this
380 * filesystem is deleted, keep fd in order to issue
381 * SYNC ioctl in the end
388 close_file_or_dir(fd
, dirstream
);
400 if (commit_mode
== COMMIT_AFTER
) {
404 * Traverse seen_fsid_hash and issue SYNC ioctl on each
407 for (slot
= 0; slot
< SEEN_FSID_HASH_SIZE
; slot
++) {
408 struct seen_fsid
*seen
= seen_fsid_hash
[slot
];
411 res
= wait_for_commit(seen
->fd
);
413 uuid_unparse(seen
->fsid
, uuidbuf
);
415 "unable to do final sync after deletion: %m, fsid: %s",
418 } else if (verbose
> 0) {
419 uuid_unparse(seen
->fsid
, uuidbuf
);
420 printf("final sync is done for fsid: %s\n",
426 /* fd will also be closed in free_seen_fsid */
427 free_seen_fsid(seen_fsid_hash
);
435 * - uppercase for filters and sort options
436 * - lowercase for enabling specific items in the output
438 static const char * const cmd_subvol_list_usage
[] = {
439 "btrfs subvolume list [options] <path>",
440 "List subvolumes and snapshots in the filesystem.",
443 "-o print only subvolumes below specified path",
444 "-a print all the subvolumes in the filesystem and",
445 " distinguish absolute and relative path with respect",
446 " to the given <path>",
449 "-p print parent ID",
450 "-c print the ogeneration of the subvolume",
451 "-g print the generation of the subvolume",
452 "-u print the uuid of subvolumes (and snapshots)",
453 "-q print the parent uuid of the snapshots",
454 "-R print the uuid of the received snapshots",
457 "-s list only snapshots",
458 "-r list readonly subvolumes (including snapshots)",
459 "-d list deleted subvolumes that are not yet cleaned",
462 "-t print the result as a table",
466 " filter the subvolumes by generation",
467 " (+value: >= value; -value: <= value; value: = value)",
469 " filter the subvolumes by ogeneration",
470 " (+value: >= value; -value: <= value; value: = value)",
471 "--sort=gen,ogen,rootid,path",
472 " list the subvolume in order of gen, ogen, rootid or path",
473 " you also can add '+' or '-' in front of each items.",
474 " (+:ascending, -:descending, ascending default)",
478 static int cmd_subvol_list(int argc
, char **argv
)
480 struct btrfs_list_filter_set
*filter_set
;
481 struct btrfs_list_comparer_set
*comparer_set
;
485 int ret
= -1, uerr
= 0;
488 int is_only_in_path
= 0;
489 DIR *dirstream
= NULL
;
490 enum btrfs_list_layout layout
= BTRFS_LIST_LAYOUT_DEFAULT
;
492 filter_set
= btrfs_list_alloc_filter_set();
493 comparer_set
= btrfs_list_alloc_comparer_set();
497 static const struct option long_options
[] = {
498 {"sort", required_argument
, NULL
, 'S'},
502 c
= getopt_long(argc
, argv
,
503 "acdgopqsurRG:C:t", long_options
, NULL
);
509 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
515 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
518 btrfs_list_setup_filter(&filter_set
,
519 BTRFS_LIST_FILTER_DELETED
,
523 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
529 layout
= BTRFS_LIST_LAYOUT_TABLE
;
532 btrfs_list_setup_filter(&filter_set
,
533 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
535 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
536 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
539 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
542 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
545 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
548 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
551 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
552 ret
= btrfs_list_parse_filter_string(optarg
,
554 BTRFS_LIST_FILTER_GEN
);
562 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
563 ret
= btrfs_list_parse_filter_string(optarg
,
565 BTRFS_LIST_FILTER_CGEN
);
572 ret
= btrfs_list_parse_sort_string(optarg
,
586 if (check_argc_exact(argc
- optind
, 1)) {
591 subvol
= argv
[optind
];
592 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
595 error("can't access '%s'", subvol
);
600 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
603 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
608 btrfs_list_setup_filter(&filter_set
,
609 BTRFS_LIST_FILTER_FULL_PATH
,
611 else if (is_only_in_path
)
612 btrfs_list_setup_filter(&filter_set
,
613 BTRFS_LIST_FILTER_TOPID_EQUAL
,
616 /* by default we shall print the following columns*/
617 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
618 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
619 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
620 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
622 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
623 layout
, !is_list_all
&& !is_only_in_path
, NULL
);
626 close_file_or_dir(fd
, dirstream
);
632 usage(cmd_subvol_list_usage
);
636 static const char * const cmd_subvol_snapshot_usage
[] = {
637 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
638 "Create a snapshot of the subvolume",
639 "Create a writable/readonly snapshot of the subvolume <source> with",
640 "the name <name> in the <dest> directory. If only <dest> is given,",
641 "the subvolume will be named the basename of <source>.",
643 "-r create a readonly snapshot",
644 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
645 " option can be given multiple times.",
649 static int cmd_subvol_snapshot(int argc
, char **argv
)
653 int fd
= -1, fddst
= -1;
654 int len
, readonly
= 0;
655 char *dupname
= NULL
;
659 struct btrfs_ioctl_vol_args_v2 args
;
660 struct btrfs_qgroup_inherit
*inherit
= NULL
;
661 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
663 memset(&args
, 0, sizeof(args
));
665 int c
= getopt(argc
, argv
, "c:i:r");
671 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
678 res
= qgroup_inherit_add_group(&inherit
, optarg
);
688 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 1);
695 usage(cmd_subvol_snapshot_usage
);
699 if (check_argc_exact(argc
- optind
, 2))
700 usage(cmd_subvol_snapshot_usage
);
702 subvol
= argv
[optind
];
703 dst
= argv
[optind
+ 1];
705 retval
= 1; /* failure */
706 res
= test_issubvolume(subvol
);
708 error("cannot access subvolume %s: %s", subvol
, strerror(-res
));
712 error("not a subvolume: %s", subvol
);
716 res
= test_isdir(dst
);
717 if (res
< 0 && res
!= -ENOENT
) {
718 error("cannot access %s: %s", dst
, strerror(-res
));
722 error("'%s' exists and it is not a directory", dst
);
727 dupname
= strdup(subvol
);
728 newname
= basename(dupname
);
731 dupname
= strdup(dst
);
732 newname
= basename(dupname
);
733 dupdir
= strdup(dst
);
734 dstdir
= dirname(dupdir
);
737 if (!test_issubvolname(newname
)) {
738 error("invalid snapshot name '%s'", newname
);
742 len
= strlen(newname
);
743 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
744 error("snapshot name too long '%s'", newname
);
748 fddst
= btrfs_open_dir(dstdir
, &dirstream1
, 1);
752 fd
= btrfs_open_dir(subvol
, &dirstream2
, 1);
757 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
758 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
759 subvol
, dstdir
, newname
);
761 printf("Create a snapshot of '%s' in '%s/%s'\n",
762 subvol
, dstdir
, newname
);
767 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
768 args
.size
= qgroup_inherit_size(inherit
);
769 args
.qgroup_inherit
= inherit
;
771 strncpy_null(args
.name
, newname
);
773 res
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
776 error("cannot snapshot '%s': %m", subvol
);
780 retval
= 0; /* success */
783 close_file_or_dir(fddst
, dirstream1
);
784 close_file_or_dir(fd
, dirstream2
);
792 static const char * const cmd_subvol_get_default_usage
[] = {
793 "btrfs subvolume get-default <path>",
794 "Get the default subvolume of a filesystem",
798 static int cmd_subvol_get_default(int argc
, char **argv
)
803 struct btrfs_list_filter_set
*filter_set
;
805 DIR *dirstream
= NULL
;
807 clean_args_no_options(argc
, argv
, cmd_subvol_get_default_usage
);
809 if (check_argc_exact(argc
- optind
, 1))
810 usage(cmd_subvol_get_default_usage
);
813 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
817 ret
= btrfs_list_get_default_subvolume(fd
, &default_id
);
819 error("failed to look up default subvolume: %m");
824 if (default_id
== 0) {
825 error("'default' dir item not found");
829 /* no need to resolve roots if FS_TREE is default */
830 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
831 printf("ID 5 (FS_TREE)\n");
836 filter_set
= btrfs_list_alloc_filter_set();
837 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_ROOTID
,
840 /* by default we shall print the following columns*/
841 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
842 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
843 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
844 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
846 ret
= btrfs_list_subvols_print(fd
, filter_set
, NULL
,
847 BTRFS_LIST_LAYOUT_DEFAULT
, 1, NULL
);
852 close_file_or_dir(fd
, dirstream
);
856 static const char * const cmd_subvol_set_default_usage
[] = {
857 "btrfs subvolume set-default <subvolume>\n"
858 "btrfs subvolume set-default <subvolid> <path>",
859 "Set the default subvolume of the filesystem mounted as default.",
860 "The subvolume can be specified by its path,",
861 "or the pair of subvolume id and path to the filesystem.",
865 static int cmd_subvol_set_default(int argc
, char **argv
)
871 DIR *dirstream
= NULL
;
873 clean_args_no_options(argc
, argv
, cmd_subvol_set_default_usage
);
875 if (check_argc_min(argc
- optind
, 1) ||
876 check_argc_max(argc
- optind
, 2))
877 usage(cmd_subvol_set_default_usage
);
879 if (argc
- optind
== 1) {
880 /* path to the subvolume is specified */
883 ret
= test_issubvolume(path
);
885 error("stat error: %s", strerror(-ret
));
888 error("'%s' is not a subvolume", path
);
892 fd
= btrfs_open_dir(path
, &dirstream
, 1);
896 ret
= lookup_path_rootid(fd
, &objectid
);
898 error("unable to get subvol id: %s", strerror(-ret
));
899 close_file_or_dir(fd
, dirstream
);
903 /* subvol id and path to the filesystem are specified */
904 subvolid
= argv
[optind
];
905 path
= argv
[optind
+ 1];
906 objectid
= arg_strtou64(subvolid
);
908 fd
= btrfs_open_dir(path
, &dirstream
, 1);
913 ret
= ioctl(fd
, BTRFS_IOC_DEFAULT_SUBVOL
, &objectid
);
914 close_file_or_dir(fd
, dirstream
);
916 error("unable to set a new default subvolume: %m");
922 static const char * const cmd_subvol_find_new_usage
[] = {
923 "btrfs subvolume find-new <path> <lastgen>",
924 "List the recently modified files in a filesystem",
928 static int cmd_subvol_find_new(int argc
, char **argv
)
934 DIR *dirstream
= NULL
;
936 clean_args_no_options(argc
, argv
, cmd_subvol_find_new_usage
);
938 if (check_argc_exact(argc
- optind
, 2))
939 usage(cmd_subvol_find_new_usage
);
941 subvol
= argv
[optind
];
942 last_gen
= arg_strtou64(argv
[optind
+ 1]);
944 ret
= test_issubvolume(subvol
);
946 error("cannot access subvolume %s: %s", subvol
, strerror(-ret
));
950 error("not a subvolume: %s", subvol
);
954 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
958 ret
= ioctl(fd
, BTRFS_IOC_SYNC
);
960 error("sync ioctl failed on '%s': %m", subvol
);
961 close_file_or_dir(fd
, dirstream
);
965 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
966 close_file_or_dir(fd
, dirstream
);
970 static const char * const cmd_subvol_show_usage
[] = {
971 "btrfs subvolume show [options] <subvol-path>|<mnt>",
972 "Show more information about the subvolume",
973 "-r|--rootid rootid of the subvolume",
974 "-u|--uuid uuid of the subvolume",
976 "If no option is specified, <subvol-path> will be shown, otherwise",
977 "the rootid or uuid are resolved relative to the <mnt> path.",
981 static int cmd_subvol_show(int argc
, char **argv
)
983 struct root_info get_ri
;
984 struct btrfs_list_filter_set
*filter_set
= NULL
;
986 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
987 char *fullpath
= NULL
;
988 char raw_prefix
[] = "\t\t\t\t";
991 DIR *dirstream1
= NULL
;
995 u8 uuid_arg
[BTRFS_UUID_SIZE
];
999 static const struct option long_options
[] = {
1000 { "rootid", required_argument
, NULL
, 'r'},
1001 { "uuid", required_argument
, NULL
, 'u'},
1002 { NULL
, 0, NULL
, 0 }
1005 c
= getopt_long(argc
, argv
, "r:u:", long_options
, NULL
);
1011 rootid_arg
= arg_strtou64(optarg
);
1015 uuid_parse(optarg
, uuid_arg
);
1019 usage(cmd_subvol_show_usage
);
1023 if (check_argc_exact(argc
- optind
, 1))
1024 usage(cmd_subvol_show_usage
);
1026 if (by_rootid
&& by_uuid
) {
1028 "options --rootid and --uuid cannot be used at the same time");
1029 usage(cmd_subvol_show_usage
);
1032 memset(&get_ri
, 0, sizeof(get_ri
));
1033 fullpath
= realpath(argv
[optind
], NULL
);
1035 error("cannot find real path for '%s': %m", argv
[optind
]);
1040 ret
= get_subvol_info_by_rootid(fullpath
, &get_ri
, rootid_arg
);
1041 } else if (by_uuid
) {
1042 ret
= get_subvol_info_by_uuid(fullpath
, &get_ri
, uuid_arg
);
1044 ret
= get_subvol_info(fullpath
, &get_ri
);
1049 error("Failed to get subvol info %s: %s",
1050 fullpath
, strerror(-ret
));
1052 error("Failed to get subvol info %s: %d",
1058 /* print the info */
1059 printf("%s\n", get_ri
.full_path
);
1060 printf("\tName: \t\t\t%s\n", get_ri
.name
);
1062 if (uuid_is_null(get_ri
.uuid
))
1063 strcpy(uuidparse
, "-");
1065 uuid_unparse(get_ri
.uuid
, uuidparse
);
1066 printf("\tUUID: \t\t\t%s\n", uuidparse
);
1068 if (uuid_is_null(get_ri
.puuid
))
1069 strcpy(uuidparse
, "-");
1071 uuid_unparse(get_ri
.puuid
, uuidparse
);
1072 printf("\tParent UUID: \t\t%s\n", uuidparse
);
1074 if (uuid_is_null(get_ri
.ruuid
))
1075 strcpy(uuidparse
, "-");
1077 uuid_unparse(get_ri
.ruuid
, uuidparse
);
1078 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1083 localtime_r(&get_ri
.otime
, &tm
);
1084 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1087 printf("\tCreation time: \t\t%s\n", tstr
);
1089 printf("\tSubvolume ID: \t\t%llu\n", get_ri
.root_id
);
1090 printf("\tGeneration: \t\t%llu\n", get_ri
.gen
);
1091 printf("\tGen at creation: \t%llu\n", get_ri
.ogen
);
1092 printf("\tParent ID: \t\t%llu\n", get_ri
.ref_tree
);
1093 printf("\tTop level ID: \t\t%llu\n", get_ri
.top_id
);
1095 if (get_ri
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1096 printf("\tFlags: \t\t\treadonly\n");
1098 printf("\tFlags: \t\t\t-\n");
1100 /* print the snapshots of the given subvol if any*/
1101 printf("\tSnapshot(s):\n");
1102 filter_set
= btrfs_list_alloc_filter_set();
1103 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_BY_PARENT
,
1104 (u64
)(unsigned long)get_ri
.uuid
);
1105 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
1107 fd
= open_file_or_dir(fullpath
, &dirstream1
);
1109 fprintf(stderr
, "ERROR: can't access '%s'\n", fullpath
);
1112 btrfs_list_subvols_print(fd
, filter_set
, NULL
, BTRFS_LIST_LAYOUT_RAW
,
1119 free(get_ri
.full_path
);
1122 close_file_or_dir(fd
, dirstream1
);
1127 static const char * const cmd_subvol_sync_usage
[] = {
1128 "btrfs subvolume sync <path> [<subvol-id>...]",
1129 "Wait until given subvolume(s) are completely removed from the filesystem.",
1130 "Wait until given subvolume(s) are completely removed from the filesystem",
1132 "If no subvolume id is given, wait until all current deletion requests",
1133 "are completed, but do not wait for subvolumes deleted meanwhile.",
1134 "The status of subvolume ids is checked periodically.",
1136 "-s <N> sleep N seconds between checks (default: 1)",
1142 * If we're looking for any dead subvolume, take a shortcut and look
1143 * for any ORPHAN_ITEMs in the tree root
1145 static int fs_has_dead_subvolumes(int fd
)
1148 struct btrfs_ioctl_search_args args
;
1149 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1150 struct btrfs_ioctl_search_header sh
;
1151 u64 min_subvolid
= 0;
1154 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1155 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1156 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1157 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1158 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1159 sk
->min_offset
= min_subvolid
;
1160 sk
->max_offset
= (u64
)-1;
1161 sk
->min_transid
= 0;
1162 sk
->max_transid
= (u64
)-1;
1165 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1172 memcpy(&sh
, args
.buf
, sizeof(sh
));
1173 min_subvolid
= sh
.offset
;
1176 * Verify that the root item is really there and we haven't hit
1179 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1180 sk
->min_objectid
= min_subvolid
;
1181 sk
->max_objectid
= min_subvolid
;
1182 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
1183 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
1185 sk
->max_offset
= (u64
)-1;
1186 sk
->min_transid
= 0;
1187 sk
->max_transid
= (u64
)-1;
1190 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1195 * Stale orphan, try the next one
1197 if (!sk
->nr_items
) {
1206 #define SUBVOL_ID_BATCH 1024
1209 * Enumerate all dead subvolumes that exist in the filesystem.
1210 * Fill @ids and reallocate to bigger size if needed.
1212 static int enumerate_dead_subvols(int fd
, u64
**ids
)
1215 struct btrfs_ioctl_search_args args
;
1216 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1220 memset(&args
, 0, sizeof(args
));
1222 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1223 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1224 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1225 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1226 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1228 sk
->max_offset
= (u64
)-1;
1229 sk
->min_transid
= 0;
1230 sk
->max_transid
= (u64
)-1;
1231 sk
->nr_items
= 4096;
1235 struct btrfs_ioctl_search_header
*sh
;
1239 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1247 for (i
= 0; i
< sk
->nr_items
; i
++) {
1248 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
1251 if (btrfs_search_header_type(sh
)
1252 == BTRFS_ORPHAN_ITEM_KEY
) {
1256 count
+= SUBVOL_ID_BATCH
;
1257 newids
= (u64
*)realloc(*ids
,
1258 count
* sizeof(u64
));
1263 (*ids
)[idx
] = btrfs_search_header_offset(sh
);
1266 off
+= btrfs_search_header_len(sh
);
1268 sk
->min_objectid
= btrfs_search_header_objectid(sh
);
1269 sk
->min_type
= btrfs_search_header_type(sh
);
1270 sk
->min_offset
= btrfs_search_header_offset(sh
);
1272 if (sk
->min_offset
< (u64
)-1)
1276 if (sk
->min_type
!= BTRFS_ORPHAN_ITEM_KEY
)
1278 if (sk
->min_objectid
!= BTRFS_ORPHAN_OBJECTID
)
1285 static int cmd_subvol_sync(int argc
, char **argv
)
1290 DIR *dirstream
= NULL
;
1293 int sleep_interval
= 1;
1296 int c
= getopt(argc
, argv
, "s:");
1303 sleep_interval
= atoi(optarg
);
1304 if (sleep_interval
< 1) {
1305 error("invalid sleep interval %s", optarg
);
1311 usage(cmd_subvol_sync_usage
);
1315 if (check_argc_min(argc
- optind
, 1))
1316 usage(cmd_subvol_sync_usage
);
1318 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1325 id_count
= argc
- optind
;
1327 id_count
= enumerate_dead_subvols(fd
, &ids
);
1329 error("can't enumerate dead subvolumes: %s",
1330 strerror(-id_count
));
1334 if (id_count
== 0) {
1339 ids
= (u64
*)malloc(id_count
* sizeof(u64
));
1341 error("not enough memory");
1346 for (i
= 0; i
< id_count
; i
++) {
1350 arg
= argv
[optind
+ i
];
1352 id
= strtoull(arg
, NULL
, 10);
1354 error("unrecognized subvolume id %s", arg
);
1358 if (id
< BTRFS_FIRST_FREE_OBJECTID
1359 || id
> BTRFS_LAST_FREE_OBJECTID
) {
1360 error("subvolume id %s out of range", arg
);
1368 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1372 close_file_or_dir(fd
, dirstream
);
1377 static const char subvolume_cmd_group_info
[] =
1378 "manage subvolumes: create, delete, list, etc";
1380 const struct cmd_group subvolume_cmd_group
= {
1381 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1382 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1383 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1384 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1385 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1387 { "get-default", cmd_subvol_get_default
,
1388 cmd_subvol_get_default_usage
, NULL
, 0 },
1389 { "set-default", cmd_subvol_set_default
,
1390 cmd_subvol_set_default_usage
, NULL
, 0 },
1391 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1393 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1394 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1399 int cmd_subvolume(int argc
, char **argv
)
1401 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);