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
;
106 int c
= getopt(argc
, argv
, "c:i:");
112 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
119 res
= qgroup_inherit_add_group(&inherit
, optarg
);
126 usage(cmd_subvol_create_usage
);
130 if (check_argc_exact(argc
- optind
, 1))
131 usage(cmd_subvol_create_usage
);
135 retval
= 1; /* failure */
136 res
= test_isdir(dst
);
137 if (res
< 0 && res
!= -ENOENT
) {
138 error("cannot access %s: %s", dst
, strerror(-res
));
142 error("target path already exists: %s", dst
);
146 dupname
= strdup(dst
);
147 newname
= basename(dupname
);
148 dupdir
= strdup(dst
);
149 dstdir
= dirname(dupdir
);
151 if (!test_issubvolname(newname
)) {
152 error("invalid subvolume name: %s", newname
);
156 len
= strlen(newname
);
157 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
158 error("subvolume name too long: %s", newname
);
162 fddst
= btrfs_open_dir(dstdir
, &dirstream
, 1);
166 printf("Create subvolume '%s/%s'\n", dstdir
, newname
);
168 struct btrfs_ioctl_vol_args_v2 args
;
170 memset(&args
, 0, sizeof(args
));
171 strncpy_null(args
.name
, newname
);
172 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
173 args
.size
= qgroup_inherit_size(inherit
);
174 args
.qgroup_inherit
= inherit
;
176 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE_V2
, &args
);
178 struct btrfs_ioctl_vol_args args
;
180 memset(&args
, 0, sizeof(args
));
181 strncpy_null(args
.name
, newname
);
183 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE
, &args
);
187 error("cannot create subvolume: %m");
191 retval
= 0; /* success */
193 close_file_or_dir(fddst
, dirstream
);
201 static int wait_for_commit(int fd
)
203 enum btrfs_util_error err
;
206 err
= btrfs_util_start_sync_fd(fd
, &transid
);
210 err
= btrfs_util_wait_sync_fd(fd
, transid
);
217 static const char * const cmd_subvol_delete_usage
[] = {
218 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
219 "Delete subvolume(s)",
220 "Delete subvolumes from the filesystem. The corresponding directory",
221 "is removed instantly but the data blocks are removed later.",
222 "The deletion does not involve full commit by default due to",
223 "performance reasons (as a consequence, the subvolume may appear again",
224 "after a crash). Use one of the --commit options to wait until the",
225 "operation is safely stored on the media.",
227 "-c|--commit-after wait for transaction commit at the end of the operation",
228 "-C|--commit-each wait for transaction commit after deleting each subvolume",
229 "-v|--verbose verbose output of operations",
233 static int cmd_subvol_delete(int argc
, char **argv
)
238 char *dname
, *vname
, *cpath
;
239 char *dupdname
= NULL
;
240 char *dupvname
= NULL
;
242 DIR *dirstream
= NULL
;
245 u8 fsid
[BTRFS_FSID_SIZE
];
246 char uuidbuf
[BTRFS_UUID_UNPARSED_SIZE
];
247 struct seen_fsid
*seen_fsid_hash
[SEEN_FSID_HASH_SIZE
] = { NULL
, };
248 enum { COMMIT_AFTER
= 1, COMMIT_EACH
= 2 };
249 enum btrfs_util_error err
;
253 static const struct option long_options
[] = {
254 {"commit-after", no_argument
, NULL
, 'c'},
255 {"commit-each", no_argument
, NULL
, 'C'},
256 {"verbose", no_argument
, NULL
, 'v'},
260 c
= getopt_long(argc
, argv
, "cCv", long_options
, NULL
);
266 commit_mode
= COMMIT_AFTER
;
269 commit_mode
= COMMIT_EACH
;
275 usage(cmd_subvol_delete_usage
);
279 if (check_argc_min(argc
- optind
, 1))
280 usage(cmd_subvol_delete_usage
);
283 printf("Transaction commit: %s\n",
284 !commit_mode
? "none (default)" :
285 commit_mode
== COMMIT_AFTER
? "at the end" : "after each");
293 err
= btrfs_util_is_subvolume(path
);
295 error_btrfs_util(err
);
300 cpath
= realpath(path
, NULL
);
303 error("cannot find real path for '%s': %m", path
);
306 dupdname
= strdup(cpath
);
307 dname
= dirname(dupdname
);
308 dupvname
= strdup(cpath
);
309 vname
= basename(dupvname
);
312 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
318 printf("Delete subvolume (%s): '%s/%s'\n",
319 commit_mode
== COMMIT_EACH
|| (commit_mode
== COMMIT_AFTER
&& cnt
+ 1 == argc
)
320 ? "commit" : "no-commit", dname
, vname
);
322 err
= btrfs_util_delete_subvolume_fd(fd
, vname
, 0);
324 error_btrfs_util(err
);
329 if (commit_mode
== COMMIT_EACH
) {
330 res
= wait_for_commit(fd
);
332 error("unable to wait for commit after '%s': %m", path
);
335 } else if (commit_mode
== COMMIT_AFTER
) {
336 res
= get_fsid(dname
, fsid
, 0);
338 error("unable to get fsid for '%s': %s",
339 path
, strerror(-res
));
341 "delete succeeded but commit may not be done in the end");
346 if (add_seen_fsid(fsid
, seen_fsid_hash
, fd
, dirstream
) == 0) {
348 uuid_unparse(fsid
, uuidbuf
);
349 printf(" new fs is found for '%s', fsid: %s\n",
353 * This is the first time a subvolume on this
354 * filesystem is deleted, keep fd in order to issue
355 * SYNC ioctl in the end
362 close_file_or_dir(fd
, dirstream
);
374 if (commit_mode
== COMMIT_AFTER
) {
378 * Traverse seen_fsid_hash and issue SYNC ioctl on each
381 for (slot
= 0; slot
< SEEN_FSID_HASH_SIZE
; slot
++) {
382 struct seen_fsid
*seen
= seen_fsid_hash
[slot
];
385 res
= wait_for_commit(seen
->fd
);
387 uuid_unparse(seen
->fsid
, uuidbuf
);
389 "unable to do final sync after deletion: %m, fsid: %s",
392 } else if (verbose
> 0) {
393 uuid_unparse(seen
->fsid
, uuidbuf
);
394 printf("final sync is done for fsid: %s\n",
400 /* fd will also be closed in free_seen_fsid */
401 free_seen_fsid(seen_fsid_hash
);
409 * - uppercase for filters and sort options
410 * - lowercase for enabling specific items in the output
412 static const char * const cmd_subvol_list_usage
[] = {
413 "btrfs subvolume list [options] <path>",
414 "List subvolumes and snapshots in the filesystem.",
417 "-o print only subvolumes below specified path",
418 "-a print all the subvolumes in the filesystem and",
419 " distinguish absolute and relative path with respect",
420 " to the given <path>",
423 "-p print parent ID",
424 "-c print the ogeneration of the subvolume",
425 "-g print the generation of the subvolume",
426 "-u print the uuid of subvolumes (and snapshots)",
427 "-q print the parent uuid of the snapshots",
428 "-R print the uuid of the received snapshots",
431 "-s list only snapshots",
432 "-r list readonly subvolumes (including snapshots)",
433 "-d list deleted subvolumes that are not yet cleaned",
436 "-t print the result as a table",
440 " filter the subvolumes by generation",
441 " (+value: >= value; -value: <= value; value: = value)",
443 " filter the subvolumes by ogeneration",
444 " (+value: >= value; -value: <= value; value: = value)",
445 "--sort=gen,ogen,rootid,path",
446 " list the subvolume in order of gen, ogen, rootid or path",
447 " you also can add '+' or '-' in front of each items.",
448 " (+:ascending, -:descending, ascending default)",
452 static int cmd_subvol_list(int argc
, char **argv
)
454 struct btrfs_list_filter_set
*filter_set
;
455 struct btrfs_list_comparer_set
*comparer_set
;
459 int ret
= -1, uerr
= 0;
462 int is_only_in_path
= 0;
463 DIR *dirstream
= NULL
;
464 enum btrfs_list_layout layout
= BTRFS_LIST_LAYOUT_DEFAULT
;
466 filter_set
= btrfs_list_alloc_filter_set();
467 comparer_set
= btrfs_list_alloc_comparer_set();
471 static const struct option long_options
[] = {
472 {"sort", required_argument
, NULL
, 'S'},
476 c
= getopt_long(argc
, argv
,
477 "acdgopqsurRG:C:t", long_options
, NULL
);
483 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
489 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
492 btrfs_list_setup_filter(&filter_set
,
493 BTRFS_LIST_FILTER_DELETED
,
497 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
503 layout
= BTRFS_LIST_LAYOUT_TABLE
;
506 btrfs_list_setup_filter(&filter_set
,
507 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
509 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
510 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
513 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
516 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
519 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
522 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
525 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
526 ret
= btrfs_list_parse_filter_string(optarg
,
528 BTRFS_LIST_FILTER_GEN
);
536 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
537 ret
= btrfs_list_parse_filter_string(optarg
,
539 BTRFS_LIST_FILTER_CGEN
);
546 ret
= btrfs_list_parse_sort_string(optarg
,
560 if (check_argc_exact(argc
- optind
, 1)) {
565 subvol
= argv
[optind
];
566 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
569 error("can't access '%s'", subvol
);
574 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
577 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
582 btrfs_list_setup_filter(&filter_set
,
583 BTRFS_LIST_FILTER_FULL_PATH
,
585 else if (is_only_in_path
)
586 btrfs_list_setup_filter(&filter_set
,
587 BTRFS_LIST_FILTER_TOPID_EQUAL
,
590 /* by default we shall print the following columns*/
591 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
592 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
593 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
594 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
596 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
597 layout
, !is_list_all
&& !is_only_in_path
, NULL
);
600 close_file_or_dir(fd
, dirstream
);
606 usage(cmd_subvol_list_usage
);
610 static const char * const cmd_subvol_snapshot_usage
[] = {
611 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
612 "Create a snapshot of the subvolume",
613 "Create a writable/readonly snapshot of the subvolume <source> with",
614 "the name <name> in the <dest> directory. If only <dest> is given,",
615 "the subvolume will be named the basename of <source>.",
617 "-r create a readonly snapshot",
618 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
619 " option can be given multiple times.",
623 static int cmd_subvol_snapshot(int argc
, char **argv
)
627 int fd
= -1, fddst
= -1;
628 int len
, readonly
= 0;
629 char *dupname
= NULL
;
633 enum btrfs_util_error err
;
634 struct btrfs_ioctl_vol_args_v2 args
;
635 struct btrfs_qgroup_inherit
*inherit
= NULL
;
636 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
638 memset(&args
, 0, sizeof(args
));
640 int c
= getopt(argc
, argv
, "c:i:r");
646 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
653 res
= qgroup_inherit_add_group(&inherit
, optarg
);
663 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 1);
670 usage(cmd_subvol_snapshot_usage
);
674 if (check_argc_exact(argc
- optind
, 2))
675 usage(cmd_subvol_snapshot_usage
);
677 subvol
= argv
[optind
];
678 dst
= argv
[optind
+ 1];
680 retval
= 1; /* failure */
681 err
= btrfs_util_is_subvolume(subvol
);
683 error_btrfs_util(err
);
687 res
= test_isdir(dst
);
688 if (res
< 0 && res
!= -ENOENT
) {
689 error("cannot access %s: %s", dst
, strerror(-res
));
693 error("'%s' exists and it is not a directory", dst
);
698 dupname
= strdup(subvol
);
699 newname
= basename(dupname
);
702 dupname
= strdup(dst
);
703 newname
= basename(dupname
);
704 dupdir
= strdup(dst
);
705 dstdir
= dirname(dupdir
);
708 if (!test_issubvolname(newname
)) {
709 error("invalid snapshot name '%s'", newname
);
713 len
= strlen(newname
);
714 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
715 error("snapshot name too long '%s'", newname
);
719 fddst
= btrfs_open_dir(dstdir
, &dirstream1
, 1);
723 fd
= btrfs_open_dir(subvol
, &dirstream2
, 1);
728 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
729 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
730 subvol
, dstdir
, newname
);
732 printf("Create a snapshot of '%s' in '%s/%s'\n",
733 subvol
, dstdir
, newname
);
738 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
739 args
.size
= qgroup_inherit_size(inherit
);
740 args
.qgroup_inherit
= inherit
;
742 strncpy_null(args
.name
, newname
);
744 res
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
747 error("cannot snapshot '%s': %m", subvol
);
751 retval
= 0; /* success */
754 close_file_or_dir(fddst
, dirstream1
);
755 close_file_or_dir(fd
, dirstream2
);
763 static const char * const cmd_subvol_get_default_usage
[] = {
764 "btrfs subvolume get-default <path>",
765 "Get the default subvolume of a filesystem",
769 static int cmd_subvol_get_default(int argc
, char **argv
)
774 DIR *dirstream
= NULL
;
775 enum btrfs_util_error err
;
776 struct btrfs_util_subvolume_info subvol
;
779 clean_args_no_options(argc
, argv
, cmd_subvol_get_default_usage
);
781 if (check_argc_exact(argc
- optind
, 1))
782 usage(cmd_subvol_get_default_usage
);
784 fd
= btrfs_open_dir(argv
[1], &dirstream
, 1);
788 err
= btrfs_util_get_default_subvolume_fd(fd
, &default_id
);
790 error_btrfs_util(err
);
794 /* no need to resolve roots if FS_TREE is default */
795 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
796 printf("ID 5 (FS_TREE)\n");
801 err
= btrfs_util_subvolume_info_fd(fd
, default_id
, &subvol
);
803 error_btrfs_util(err
);
807 err
= btrfs_util_subvolume_path_fd(fd
, default_id
, &path
);
809 error_btrfs_util(err
);
813 printf("ID %" PRIu64
" gen %" PRIu64
" top level %" PRIu64
" path %s\n",
814 subvol
.id
, subvol
.generation
, subvol
.parent_id
, path
);
820 close_file_or_dir(fd
, dirstream
);
824 static const char * const cmd_subvol_set_default_usage
[] = {
825 "btrfs subvolume set-default <subvolume>\n"
826 "btrfs subvolume set-default <subvolid> <path>",
827 "Set the default subvolume of the filesystem mounted as default.",
828 "The subvolume can be specified by its path,",
829 "or the pair of subvolume id and path to the filesystem.",
833 static int cmd_subvol_set_default(int argc
, char **argv
)
837 enum btrfs_util_error err
;
839 clean_args_no_options(argc
, argv
, cmd_subvol_set_default_usage
);
841 if (check_argc_min(argc
- optind
, 1) ||
842 check_argc_max(argc
- optind
, 2))
843 usage(cmd_subvol_set_default_usage
);
845 if (argc
- optind
== 1) {
846 /* path to the subvolume is specified */
850 /* subvol id and path to the filesystem are specified */
851 objectid
= arg_strtou64(argv
[optind
]);
852 path
= argv
[optind
+ 1];
855 err
= btrfs_util_set_default_subvolume(path
, objectid
);
857 error_btrfs_util(err
);
863 static const char * const cmd_subvol_find_new_usage
[] = {
864 "btrfs subvolume find-new <path> <lastgen>",
865 "List the recently modified files in a filesystem",
869 static int cmd_subvol_find_new(int argc
, char **argv
)
875 DIR *dirstream
= NULL
;
876 enum btrfs_util_error err
;
878 clean_args_no_options(argc
, argv
, cmd_subvol_find_new_usage
);
880 if (check_argc_exact(argc
- optind
, 2))
881 usage(cmd_subvol_find_new_usage
);
883 subvol
= argv
[optind
];
884 last_gen
= arg_strtou64(argv
[optind
+ 1]);
886 err
= btrfs_util_is_subvolume(subvol
);
888 error_btrfs_util(err
);
892 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
896 err
= btrfs_util_sync_fd(fd
);
898 error_btrfs_util(err
);
899 close_file_or_dir(fd
, dirstream
);
903 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
904 close_file_or_dir(fd
, dirstream
);
908 static const char * const cmd_subvol_show_usage
[] = {
909 "btrfs subvolume show [options] <subvol-path>|<mnt>",
910 "Show more information about the subvolume",
911 "-r|--rootid rootid of the subvolume",
912 "-u|--uuid uuid of the subvolume",
914 "If no option is specified, <subvol-path> will be shown, otherwise",
915 "the rootid or uuid are resolved relative to the <mnt> path.",
919 static int cmd_subvol_show(int argc
, char **argv
)
922 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
923 char *fullpath
= NULL
;
926 DIR *dirstream1
= NULL
;
930 u8 uuid_arg
[BTRFS_UUID_SIZE
];
931 struct btrfs_util_subvolume_iterator
*iter
;
932 struct btrfs_util_subvolume_info subvol
;
933 char *subvol_path
= NULL
;
934 enum btrfs_util_error err
;
938 static const struct option long_options
[] = {
939 { "rootid", required_argument
, NULL
, 'r'},
940 { "uuid", required_argument
, NULL
, 'u'},
944 c
= getopt_long(argc
, argv
, "r:u:", long_options
, NULL
);
950 rootid_arg
= arg_strtou64(optarg
);
954 uuid_parse(optarg
, uuid_arg
);
958 usage(cmd_subvol_show_usage
);
962 if (check_argc_exact(argc
- optind
, 1))
963 usage(cmd_subvol_show_usage
);
965 if (by_rootid
&& by_uuid
) {
967 "options --rootid and --uuid cannot be used at the same time");
968 usage(cmd_subvol_show_usage
);
971 fullpath
= realpath(argv
[optind
], NULL
);
973 error("cannot find real path for '%s': %m", argv
[optind
]);
977 fd
= open_file_or_dir(fullpath
, &dirstream1
);
979 error("can't access '%s'", fullpath
);
984 err
= btrfs_util_create_subvolume_iterator_fd(fd
,
985 BTRFS_FS_TREE_OBJECTID
,
988 error_btrfs_util(err
);
993 err
= btrfs_util_subvolume_iterator_next_info(iter
,
996 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
) {
997 uuid_unparse(uuid_arg
, uuidparse
);
998 error("can't find uuid '%s' on '%s'", uuidparse
,
1000 btrfs_util_destroy_subvolume_iterator(iter
);
1003 error_btrfs_util(err
);
1004 btrfs_util_destroy_subvolume_iterator(iter
);
1008 if (uuid_compare(subvol
.uuid
, uuid_arg
) == 0)
1013 btrfs_util_destroy_subvolume_iterator(iter
);
1016 * If !by_rootid, rootid_arg = 0, which means find the
1017 * subvolume ID of the fd and use that.
1019 err
= btrfs_util_subvolume_info_fd(fd
, rootid_arg
, &subvol
);
1021 error_btrfs_util(err
);
1025 err
= btrfs_util_subvolume_path_fd(fd
, subvol
.id
, &subvol_path
);
1027 error_btrfs_util(err
);
1033 /* print the info */
1034 printf("%s\n", subvol
.id
== BTRFS_FS_TREE_OBJECTID
? "/" : subvol_path
);
1035 printf("\tName: \t\t\t%s\n",
1036 (subvol
.id
== BTRFS_FS_TREE_OBJECTID
? "<FS_TREE>" :
1037 basename(subvol_path
)));
1039 if (uuid_is_null(subvol
.uuid
))
1040 strcpy(uuidparse
, "-");
1042 uuid_unparse(subvol
.uuid
, uuidparse
);
1043 printf("\tUUID: \t\t\t%s\n", uuidparse
);
1045 if (uuid_is_null(subvol
.parent_uuid
))
1046 strcpy(uuidparse
, "-");
1048 uuid_unparse(subvol
.parent_uuid
, uuidparse
);
1049 printf("\tParent UUID: \t\t%s\n", uuidparse
);
1051 if (uuid_is_null(subvol
.received_uuid
))
1052 strcpy(uuidparse
, "-");
1054 uuid_unparse(subvol
.received_uuid
, uuidparse
);
1055 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1057 if (subvol
.otime
.tv_sec
) {
1060 localtime_r(&subvol
.otime
.tv_sec
, &tm
);
1061 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1064 printf("\tCreation time: \t\t%s\n", tstr
);
1066 printf("\tSubvolume ID: \t\t%" PRIu64
"\n", subvol
.id
);
1067 printf("\tGeneration: \t\t%" PRIu64
"\n", subvol
.generation
);
1068 printf("\tGen at creation: \t%" PRIu64
"\n", subvol
.otransid
);
1069 printf("\tParent ID: \t\t%" PRIu64
"\n", subvol
.parent_id
);
1070 printf("\tTop level ID: \t\t%" PRIu64
"\n", subvol
.parent_id
);
1072 if (subvol
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1073 printf("\tFlags: \t\t\treadonly\n");
1075 printf("\tFlags: \t\t\t-\n");
1077 /* print the snapshots of the given subvol if any*/
1078 printf("\tSnapshot(s):\n");
1080 err
= btrfs_util_create_subvolume_iterator_fd(fd
,
1081 BTRFS_FS_TREE_OBJECTID
, 0,
1085 struct btrfs_util_subvolume_info subvol2
;
1088 err
= btrfs_util_subvolume_iterator_next_info(iter
, &path
, &subvol2
);
1089 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
) {
1092 error_btrfs_util(err
);
1093 btrfs_util_destroy_subvolume_iterator(iter
);
1097 if (uuid_compare(subvol2
.parent_uuid
, subvol
.uuid
) == 0)
1098 printf("\t\t\t\t%s\n", path
);
1102 btrfs_util_destroy_subvolume_iterator(iter
);
1107 close_file_or_dir(fd
, dirstream1
);
1112 static const char * const cmd_subvol_sync_usage
[] = {
1113 "btrfs subvolume sync <path> [<subvol-id>...]",
1114 "Wait until given subvolume(s) are completely removed from the filesystem.",
1115 "Wait until given subvolume(s) are completely removed from the filesystem",
1117 "If no subvolume id is given, wait until all current deletion requests",
1118 "are completed, but do not wait for subvolumes deleted meanwhile.",
1119 "The status of subvolume ids is checked periodically.",
1121 "-s <N> sleep N seconds between checks (default: 1)",
1125 static int cmd_subvol_sync(int argc
, char **argv
)
1129 DIR *dirstream
= NULL
;
1130 uint64_t *ids
= NULL
;
1132 int sleep_interval
= 1;
1133 enum btrfs_util_error err
;
1136 int c
= getopt(argc
, argv
, "s:");
1143 sleep_interval
= atoi(optarg
);
1144 if (sleep_interval
< 1) {
1145 error("invalid sleep interval %s", optarg
);
1151 usage(cmd_subvol_sync_usage
);
1155 if (check_argc_min(argc
- optind
, 1))
1156 usage(cmd_subvol_sync_usage
);
1158 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1165 id_count
= argc
- optind
;
1167 err
= btrfs_util_deleted_subvolumes_fd(fd
, &ids
, &id_count
);
1169 error_btrfs_util(err
);
1173 if (id_count
== 0) {
1178 ids
= malloc(id_count
* sizeof(uint64_t));
1180 error("not enough memory");
1185 for (i
= 0; i
< id_count
; i
++) {
1189 arg
= argv
[optind
+ i
];
1191 id
= strtoull(arg
, NULL
, 10);
1193 error("unrecognized subvolume id %s", arg
);
1197 if (id
< BTRFS_FIRST_FREE_OBJECTID
||
1198 id
> BTRFS_LAST_FREE_OBJECTID
) {
1199 error("subvolume id %s out of range", arg
);
1207 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1211 close_file_or_dir(fd
, dirstream
);
1216 static const char subvolume_cmd_group_info
[] =
1217 "manage subvolumes: create, delete, list, etc";
1219 const struct cmd_group subvolume_cmd_group
= {
1220 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1221 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1222 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1223 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1224 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1226 { "get-default", cmd_subvol_get_default
,
1227 cmd_subvol_get_default_usage
, NULL
, 0 },
1228 { "set-default", cmd_subvol_set_default
,
1229 cmd_subvol_set_default_usage
, NULL
, 0 },
1230 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1232 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1233 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1238 int cmd_subvolume(int argc
, char **argv
)
1240 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);