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.
22 #include <sys/ioctl.h>
29 #include <uuid/uuid.h>
30 #include <linux/magic.h>
32 #include <btrfsutil.h>
34 #include "kerncompat.h"
41 #include "btrfs-list.h"
45 static int wait_for_subvolume_cleaning(int fd
, size_t count
, uint64_t *ids
,
49 enum btrfs_util_error err
;
54 for (i
= 0; i
< count
; i
++) {
57 err
= btrfs_util_subvolume_info_fd(fd
, ids
[i
], NULL
);
58 if (err
== BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
) {
59 printf("Subvolume id %" PRIu64
" is gone\n",
63 error_btrfs_util(err
);
71 sleep(sleep_interval
);
77 static const char * const subvolume_cmd_group_usage
[] = {
78 "btrfs subvolume <command> <args>",
82 static const char * const cmd_subvol_create_usage
[] = {
83 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
85 "Create a subvolume <name> in <dest>. If <dest> is not given",
86 "subvolume <name> will be created in the current directory.",
88 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
89 " option can be given multiple times.",
93 static int cmd_subvol_create(int argc
, char **argv
)
102 struct btrfs_qgroup_inherit
*inherit
= NULL
;
103 DIR *dirstream
= NULL
;
107 int c
= getopt(argc
, argv
, "c:i:");
113 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
120 res
= qgroup_inherit_add_group(&inherit
, optarg
);
127 usage(cmd_subvol_create_usage
);
131 if (check_argc_exact(argc
- optind
, 1))
132 usage(cmd_subvol_create_usage
);
136 retval
= 1; /* failure */
137 res
= test_isdir(dst
);
138 if (res
< 0 && res
!= -ENOENT
) {
139 error("cannot access %s: %s", dst
, strerror(-res
));
143 error("target path already exists: %s", dst
);
147 dupname
= strdup(dst
);
148 newname
= basename(dupname
);
149 dupdir
= strdup(dst
);
150 dstdir
= dirname(dupdir
);
152 if (!test_issubvolname(newname
)) {
153 error("invalid subvolume name: %s", newname
);
157 len
= strlen(newname
);
158 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
159 error("subvolume name too long: %s", newname
);
163 fddst
= btrfs_open_dir(dstdir
, &dirstream
, 1);
167 printf("Create subvolume '%s/%s'\n", dstdir
, newname
);
169 struct btrfs_ioctl_vol_args_v2 args
;
171 memset(&args
, 0, sizeof(args
));
172 strncpy_null(args
.name
, newname
);
173 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
174 args
.size
= qgroup_inherit_size(inherit
);
175 args
.qgroup_inherit
= inherit
;
177 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE_V2
, &args
);
179 struct btrfs_ioctl_vol_args args
;
181 memset(&args
, 0, sizeof(args
));
182 strncpy_null(args
.name
, newname
);
184 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE
, &args
);
188 error("cannot create subvolume: %m");
192 retval
= 0; /* success */
194 close_file_or_dir(fddst
, dirstream
);
202 static int wait_for_commit(int fd
)
204 enum btrfs_util_error err
;
207 err
= btrfs_util_start_sync_fd(fd
, &transid
);
211 err
= btrfs_util_wait_sync_fd(fd
, transid
);
218 static const char * const cmd_subvol_delete_usage
[] = {
219 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
220 "Delete subvolume(s)",
221 "Delete subvolumes from the filesystem. The corresponding directory",
222 "is removed instantly but the data blocks are removed later.",
223 "The deletion does not involve full commit by default due to",
224 "performance reasons (as a consequence, the subvolume may appear again",
225 "after a crash). Use one of the --commit options to wait until the",
226 "operation is safely stored on the media.",
228 "-c|--commit-after wait for transaction commit at the end of the operation",
229 "-C|--commit-each wait for transaction commit after deleting each subvolume",
230 "-v|--verbose verbose output of operations",
234 static int cmd_subvol_delete(int argc
, char **argv
)
239 char *dname
, *vname
, *cpath
;
240 char *dupdname
= NULL
;
241 char *dupvname
= NULL
;
243 DIR *dirstream
= NULL
;
246 u8 fsid
[BTRFS_FSID_SIZE
];
247 char uuidbuf
[BTRFS_UUID_UNPARSED_SIZE
];
248 struct seen_fsid
*seen_fsid_hash
[SEEN_FSID_HASH_SIZE
] = { NULL
, };
249 enum { COMMIT_AFTER
= 1, COMMIT_EACH
= 2 };
250 enum btrfs_util_error err
;
255 static const struct option long_options
[] = {
256 {"commit-after", no_argument
, NULL
, 'c'},
257 {"commit-each", no_argument
, NULL
, 'C'},
258 {"verbose", no_argument
, NULL
, 'v'},
262 c
= getopt_long(argc
, argv
, "cCv", long_options
, NULL
);
268 commit_mode
= COMMIT_AFTER
;
271 commit_mode
= COMMIT_EACH
;
277 usage(cmd_subvol_delete_usage
);
281 if (check_argc_min(argc
- optind
, 1))
282 usage(cmd_subvol_delete_usage
);
285 printf("Transaction commit: %s\n",
286 !commit_mode
? "none (default)" :
287 commit_mode
== COMMIT_AFTER
? "at the end" : "after each");
295 err
= btrfs_util_is_subvolume(path
);
297 error_btrfs_util(err
);
302 cpath
= realpath(path
, NULL
);
305 error("cannot find real path for '%s': %m", path
);
308 dupdname
= strdup(cpath
);
309 dname
= dirname(dupdname
);
310 dupvname
= strdup(cpath
);
311 vname
= basename(dupvname
);
314 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
320 printf("Delete subvolume (%s): '%s/%s'\n",
321 commit_mode
== COMMIT_EACH
|| (commit_mode
== COMMIT_AFTER
&& cnt
+ 1 == argc
)
322 ? "commit" : "no-commit", dname
, vname
);
324 err
= btrfs_util_delete_subvolume_fd(fd
, vname
, 0);
326 error_btrfs_util(err
);
331 if (commit_mode
== COMMIT_EACH
) {
332 res
= wait_for_commit(fd
);
334 error("unable to wait for commit after '%s': %m", path
);
337 } else if (commit_mode
== COMMIT_AFTER
) {
338 res
= get_fsid(dname
, fsid
, 0);
340 error("unable to get fsid for '%s': %s",
341 path
, strerror(-res
));
343 "delete succeeded but commit may not be done in the end");
348 if (add_seen_fsid(fsid
, seen_fsid_hash
, fd
, dirstream
) == 0) {
350 uuid_unparse(fsid
, uuidbuf
);
351 printf(" new fs is found for '%s', fsid: %s\n",
355 * This is the first time a subvolume on this
356 * filesystem is deleted, keep fd in order to issue
357 * SYNC ioctl in the end
364 close_file_or_dir(fd
, dirstream
);
376 if (commit_mode
== COMMIT_AFTER
) {
380 * Traverse seen_fsid_hash and issue SYNC ioctl on each
383 for (slot
= 0; slot
< SEEN_FSID_HASH_SIZE
; slot
++) {
384 struct seen_fsid
*seen
= seen_fsid_hash
[slot
];
387 res
= wait_for_commit(seen
->fd
);
389 uuid_unparse(seen
->fsid
, uuidbuf
);
391 "unable to do final sync after deletion: %m, fsid: %s",
394 } else if (verbose
> 0) {
395 uuid_unparse(seen
->fsid
, uuidbuf
);
396 printf("final sync is done for fsid: %s\n",
402 /* fd will also be closed in free_seen_fsid */
403 free_seen_fsid(seen_fsid_hash
);
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] <path>",
416 "List subvolumes and snapshots in the filesystem.",
419 "-o print only subvolumes below specified path",
420 "-a print all the subvolumes in the filesystem and",
421 " distinguish absolute and relative path with respect",
422 " to the given <path>",
425 "-p print parent ID",
426 "-c print the ogeneration of the subvolume",
427 "-g print the generation of the subvolume",
428 "-u print the uuid of subvolumes (and snapshots)",
429 "-q print the parent uuid of the snapshots",
430 "-R print the uuid of the received snapshots",
433 "-s list only snapshots",
434 "-r list readonly subvolumes (including snapshots)",
435 "-d list deleted subvolumes that are not yet cleaned",
438 "-t print the result as a table",
442 " filter the subvolumes by generation",
443 " (+value: >= value; -value: <= value; value: = value)",
445 " filter the subvolumes by ogeneration",
446 " (+value: >= value; -value: <= value; value: = value)",
447 "--sort=gen,ogen,rootid,path",
448 " list the subvolume in order of gen, ogen, rootid or path",
449 " you also can add '+' or '-' in front of each items.",
450 " (+:ascending, -:descending, ascending default)",
454 static int cmd_subvol_list(int argc
, char **argv
)
456 struct btrfs_list_filter_set
*filter_set
;
457 struct btrfs_list_comparer_set
*comparer_set
;
461 int ret
= -1, uerr
= 0;
464 int is_only_in_path
= 0;
465 DIR *dirstream
= NULL
;
466 enum btrfs_list_layout layout
= BTRFS_LIST_LAYOUT_DEFAULT
;
468 filter_set
= btrfs_list_alloc_filter_set();
469 comparer_set
= btrfs_list_alloc_comparer_set();
474 static const struct option long_options
[] = {
475 {"sort", required_argument
, NULL
, 'S'},
479 c
= getopt_long(argc
, argv
,
480 "acdgopqsurRG:C:t", long_options
, NULL
);
486 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
492 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
495 btrfs_list_setup_filter(&filter_set
,
496 BTRFS_LIST_FILTER_DELETED
,
500 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
506 layout
= BTRFS_LIST_LAYOUT_TABLE
;
509 btrfs_list_setup_filter(&filter_set
,
510 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
512 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
513 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
516 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
519 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
522 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
525 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
528 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
529 ret
= btrfs_list_parse_filter_string(optarg
,
531 BTRFS_LIST_FILTER_GEN
);
539 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
540 ret
= btrfs_list_parse_filter_string(optarg
,
542 BTRFS_LIST_FILTER_CGEN
);
549 ret
= btrfs_list_parse_sort_string(optarg
,
563 if (check_argc_exact(argc
- optind
, 1)) {
568 subvol
= argv
[optind
];
569 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
572 error("can't access '%s'", subvol
);
577 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
580 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
585 btrfs_list_setup_filter(&filter_set
,
586 BTRFS_LIST_FILTER_FULL_PATH
,
588 else if (is_only_in_path
)
589 btrfs_list_setup_filter(&filter_set
,
590 BTRFS_LIST_FILTER_TOPID_EQUAL
,
593 /* by default we shall print the following columns*/
594 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
595 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
596 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
597 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
599 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
600 layout
, !is_list_all
&& !is_only_in_path
, NULL
);
603 close_file_or_dir(fd
, dirstream
);
609 usage(cmd_subvol_list_usage
);
613 static const char * const cmd_subvol_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_subvol_snapshot(int argc
, char **argv
)
630 int fd
= -1, fddst
= -1;
631 int len
, readonly
= 0;
632 char *dupname
= NULL
;
636 enum btrfs_util_error err
;
637 struct btrfs_ioctl_vol_args_v2 args
;
638 struct btrfs_qgroup_inherit
*inherit
= NULL
;
639 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
641 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 err
= btrfs_util_is_subvolume(subvol
);
687 error_btrfs_util(err
);
691 res
= test_isdir(dst
);
692 if (res
< 0 && res
!= -ENOENT
) {
693 error("cannot access %s: %s", dst
, strerror(-res
));
697 error("'%s' exists and it is not a directory", 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 error("invalid snapshot name '%s'", newname
);
717 len
= strlen(newname
);
718 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
719 error("snapshot name too long '%s'", newname
);
723 fddst
= btrfs_open_dir(dstdir
, &dirstream1
, 1);
727 fd
= btrfs_open_dir(subvol
, &dirstream2
, 1);
732 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
733 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
734 subvol
, dstdir
, newname
);
736 printf("Create a snapshot of '%s' in '%s/%s'\n",
737 subvol
, dstdir
, newname
);
742 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
743 args
.size
= qgroup_inherit_size(inherit
);
744 args
.qgroup_inherit
= inherit
;
746 strncpy_null(args
.name
, newname
);
748 res
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
751 error("cannot snapshot '%s': %m", subvol
);
755 retval
= 0; /* success */
758 close_file_or_dir(fddst
, dirstream1
);
759 close_file_or_dir(fd
, dirstream2
);
767 static const char * const cmd_subvol_get_default_usage
[] = {
768 "btrfs subvolume get-default <path>",
769 "Get the default subvolume of a filesystem",
773 static int cmd_subvol_get_default(int argc
, char **argv
)
778 DIR *dirstream
= NULL
;
779 enum btrfs_util_error err
;
780 struct btrfs_util_subvolume_info subvol
;
783 clean_args_no_options(argc
, argv
, cmd_subvol_get_default_usage
);
785 if (check_argc_exact(argc
- optind
, 1))
786 usage(cmd_subvol_get_default_usage
);
788 fd
= btrfs_open_dir(argv
[1], &dirstream
, 1);
792 err
= btrfs_util_get_default_subvolume_fd(fd
, &default_id
);
794 error_btrfs_util(err
);
798 /* no need to resolve roots if FS_TREE is default */
799 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
800 printf("ID 5 (FS_TREE)\n");
805 err
= btrfs_util_subvolume_info_fd(fd
, default_id
, &subvol
);
807 error_btrfs_util(err
);
811 err
= btrfs_util_subvolume_path_fd(fd
, default_id
, &path
);
813 error_btrfs_util(err
);
817 printf("ID %" PRIu64
" gen %" PRIu64
" top level %" PRIu64
" path %s\n",
818 subvol
.id
, subvol
.generation
, subvol
.parent_id
, path
);
824 close_file_or_dir(fd
, dirstream
);
828 static const char * const cmd_subvol_set_default_usage
[] = {
829 "btrfs subvolume set-default <subvolume>\n"
830 "btrfs subvolume set-default <subvolid> <path>",
831 "Set the default subvolume of the filesystem mounted as default.",
832 "The subvolume can be specified by its path,",
833 "or the pair of subvolume id and path to the filesystem.",
837 static int cmd_subvol_set_default(int argc
, char **argv
)
841 enum btrfs_util_error err
;
843 clean_args_no_options(argc
, argv
, cmd_subvol_set_default_usage
);
845 if (check_argc_min(argc
- optind
, 1) ||
846 check_argc_max(argc
- optind
, 2))
847 usage(cmd_subvol_set_default_usage
);
849 if (argc
- optind
== 1) {
850 /* path to the subvolume is specified */
854 /* subvol id and path to the filesystem are specified */
855 objectid
= arg_strtou64(argv
[optind
]);
856 path
= argv
[optind
+ 1];
859 err
= btrfs_util_set_default_subvolume(path
, objectid
);
861 error_btrfs_util(err
);
867 static const char * const cmd_subvol_find_new_usage
[] = {
868 "btrfs subvolume find-new <path> <lastgen>",
869 "List the recently modified files in a filesystem",
873 static int cmd_subvol_find_new(int argc
, char **argv
)
879 DIR *dirstream
= NULL
;
880 enum btrfs_util_error err
;
882 clean_args_no_options(argc
, argv
, cmd_subvol_find_new_usage
);
884 if (check_argc_exact(argc
- optind
, 2))
885 usage(cmd_subvol_find_new_usage
);
887 subvol
= argv
[optind
];
888 last_gen
= arg_strtou64(argv
[optind
+ 1]);
890 err
= btrfs_util_is_subvolume(subvol
);
892 error_btrfs_util(err
);
896 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
900 err
= btrfs_util_sync_fd(fd
);
902 error_btrfs_util(err
);
903 close_file_or_dir(fd
, dirstream
);
907 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
908 close_file_or_dir(fd
, dirstream
);
912 static const char * const cmd_subvol_show_usage
[] = {
913 "btrfs subvolume show [options] <subvol-path>|<mnt>",
914 "Show more information about the subvolume",
915 "-r|--rootid rootid of the subvolume",
916 "-u|--uuid uuid of the subvolume",
918 "If no option is specified, <subvol-path> will be shown, otherwise",
919 "the rootid or uuid are resolved relative to the <mnt> path.",
923 static int cmd_subvol_show(int argc
, char **argv
)
926 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
927 char *fullpath
= NULL
;
930 DIR *dirstream1
= NULL
;
934 u8 uuid_arg
[BTRFS_UUID_SIZE
];
935 struct btrfs_util_subvolume_iterator
*iter
;
936 struct btrfs_util_subvolume_info subvol
;
937 char *subvol_path
= NULL
;
938 enum btrfs_util_error err
;
943 static const struct option long_options
[] = {
944 { "rootid", required_argument
, NULL
, 'r'},
945 { "uuid", required_argument
, NULL
, 'u'},
949 c
= getopt_long(argc
, argv
, "r:u:", long_options
, NULL
);
955 rootid_arg
= arg_strtou64(optarg
);
959 uuid_parse(optarg
, uuid_arg
);
963 usage(cmd_subvol_show_usage
);
967 if (check_argc_exact(argc
- optind
, 1))
968 usage(cmd_subvol_show_usage
);
970 if (by_rootid
&& by_uuid
) {
972 "options --rootid and --uuid cannot be used at the same time");
973 usage(cmd_subvol_show_usage
);
976 fullpath
= realpath(argv
[optind
], NULL
);
978 error("cannot find real path for '%s': %m", argv
[optind
]);
982 fd
= open_file_or_dir(fullpath
, &dirstream1
);
984 error("can't access '%s'", fullpath
);
989 err
= btrfs_util_create_subvolume_iterator_fd(fd
,
990 BTRFS_FS_TREE_OBJECTID
,
993 error_btrfs_util(err
);
998 err
= btrfs_util_subvolume_iterator_next_info(iter
,
1001 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
) {
1002 uuid_unparse(uuid_arg
, uuidparse
);
1003 error("can't find uuid '%s' on '%s'", uuidparse
,
1005 btrfs_util_destroy_subvolume_iterator(iter
);
1008 error_btrfs_util(err
);
1009 btrfs_util_destroy_subvolume_iterator(iter
);
1013 if (uuid_compare(subvol
.uuid
, uuid_arg
) == 0)
1018 btrfs_util_destroy_subvolume_iterator(iter
);
1021 * If !by_rootid, rootid_arg = 0, which means find the
1022 * subvolume ID of the fd and use that.
1024 err
= btrfs_util_subvolume_info_fd(fd
, rootid_arg
, &subvol
);
1026 error_btrfs_util(err
);
1030 err
= btrfs_util_subvolume_path_fd(fd
, subvol
.id
, &subvol_path
);
1032 error_btrfs_util(err
);
1038 /* print the info */
1039 printf("%s\n", subvol
.id
== BTRFS_FS_TREE_OBJECTID
? "/" : subvol_path
);
1040 printf("\tName: \t\t\t%s\n",
1041 (subvol
.id
== BTRFS_FS_TREE_OBJECTID
? "<FS_TREE>" :
1042 basename(subvol_path
)));
1044 if (uuid_is_null(subvol
.uuid
))
1045 strcpy(uuidparse
, "-");
1047 uuid_unparse(subvol
.uuid
, uuidparse
);
1048 printf("\tUUID: \t\t\t%s\n", uuidparse
);
1050 if (uuid_is_null(subvol
.parent_uuid
))
1051 strcpy(uuidparse
, "-");
1053 uuid_unparse(subvol
.parent_uuid
, uuidparse
);
1054 printf("\tParent UUID: \t\t%s\n", uuidparse
);
1056 if (uuid_is_null(subvol
.received_uuid
))
1057 strcpy(uuidparse
, "-");
1059 uuid_unparse(subvol
.received_uuid
, uuidparse
);
1060 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1062 if (subvol
.otime
.tv_sec
) {
1065 localtime_r(&subvol
.otime
.tv_sec
, &tm
);
1066 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1069 printf("\tCreation time: \t\t%s\n", tstr
);
1071 printf("\tSubvolume ID: \t\t%" PRIu64
"\n", subvol
.id
);
1072 printf("\tGeneration: \t\t%" PRIu64
"\n", subvol
.generation
);
1073 printf("\tGen at creation: \t%" PRIu64
"\n", subvol
.otransid
);
1074 printf("\tParent ID: \t\t%" PRIu64
"\n", subvol
.parent_id
);
1075 printf("\tTop level ID: \t\t%" PRIu64
"\n", subvol
.parent_id
);
1077 if (subvol
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1078 printf("\tFlags: \t\t\treadonly\n");
1080 printf("\tFlags: \t\t\t-\n");
1082 /* print the snapshots of the given subvol if any*/
1083 printf("\tSnapshot(s):\n");
1085 err
= btrfs_util_create_subvolume_iterator_fd(fd
,
1086 BTRFS_FS_TREE_OBJECTID
, 0,
1090 struct btrfs_util_subvolume_info subvol2
;
1093 err
= btrfs_util_subvolume_iterator_next_info(iter
, &path
, &subvol2
);
1094 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
) {
1097 error_btrfs_util(err
);
1098 btrfs_util_destroy_subvolume_iterator(iter
);
1102 if (uuid_compare(subvol2
.parent_uuid
, subvol
.uuid
) == 0)
1103 printf("\t\t\t\t%s\n", path
);
1107 btrfs_util_destroy_subvolume_iterator(iter
);
1112 close_file_or_dir(fd
, dirstream1
);
1117 static const char * const cmd_subvol_sync_usage
[] = {
1118 "btrfs subvolume sync <path> [<subvol-id>...]",
1119 "Wait until given subvolume(s) are completely removed from the filesystem.",
1120 "Wait until given subvolume(s) are completely removed from the filesystem",
1122 "If no subvolume id is given, wait until all current deletion requests",
1123 "are completed, but do not wait for subvolumes deleted meanwhile.",
1124 "The status of subvolume ids is checked periodically.",
1126 "-s <N> sleep N seconds between checks (default: 1)",
1130 static int cmd_subvol_sync(int argc
, char **argv
)
1134 DIR *dirstream
= NULL
;
1135 uint64_t *ids
= NULL
;
1137 int sleep_interval
= 1;
1138 enum btrfs_util_error err
;
1142 int c
= getopt(argc
, argv
, "s:");
1149 sleep_interval
= atoi(optarg
);
1150 if (sleep_interval
< 1) {
1151 error("invalid sleep interval %s", optarg
);
1157 usage(cmd_subvol_sync_usage
);
1161 if (check_argc_min(argc
- optind
, 1))
1162 usage(cmd_subvol_sync_usage
);
1164 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1171 id_count
= argc
- optind
;
1173 err
= btrfs_util_deleted_subvolumes_fd(fd
, &ids
, &id_count
);
1175 error_btrfs_util(err
);
1179 if (id_count
== 0) {
1184 ids
= malloc(id_count
* sizeof(uint64_t));
1186 error("not enough memory");
1191 for (i
= 0; i
< id_count
; i
++) {
1195 arg
= argv
[optind
+ i
];
1197 id
= strtoull(arg
, NULL
, 10);
1199 error("unrecognized subvolume id %s", arg
);
1203 if (id
< BTRFS_FIRST_FREE_OBJECTID
||
1204 id
> BTRFS_LAST_FREE_OBJECTID
) {
1205 error("subvolume id %s out of range", arg
);
1213 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1217 close_file_or_dir(fd
, dirstream
);
1222 static const char subvolume_cmd_group_info
[] =
1223 "manage subvolumes: create, delete, list, etc";
1225 const struct cmd_group subvolume_cmd_group
= {
1226 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1227 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1228 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1229 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1230 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1232 { "get-default", cmd_subvol_get_default
,
1233 cmd_subvol_get_default_usage
, NULL
, 0 },
1234 { "set-default", cmd_subvol_set_default
,
1235 cmd_subvol_set_default_usage
, NULL
, 0 },
1236 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1238 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1239 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1244 int cmd_subvolume(int argc
, char **argv
)
1246 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);