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
;
269 static const struct option long_options
[] = {
270 {"commit-after", no_argument
, NULL
, 'c'}, /* commit mode 1 */
271 {"commit-each", no_argument
, NULL
, 'C'}, /* commit mode 2 */
272 {"verbose", no_argument
, NULL
, 'v'},
276 c
= getopt_long(argc
, argv
, "cCv", long_options
, NULL
);
291 usage(cmd_subvol_delete_usage
);
295 if (check_argc_min(argc
- optind
, 1))
296 usage(cmd_subvol_delete_usage
);
299 printf("Transaction commit: %s\n",
300 !commit_mode
? "none (default)" :
301 commit_mode
== 1 ? "at the end" : "after each");
309 res
= test_issubvolume(path
);
311 error("cannot access subvolume %s: %s", path
, strerror(-res
));
316 error("not a subvolume: %s", path
);
321 cpath
= realpath(path
, NULL
);
324 error("cannot find real path for '%s': %s",
325 path
, strerror(errno
));
328 dupdname
= strdup(cpath
);
329 dname
= dirname(dupdname
);
330 dupvname
= strdup(cpath
);
331 vname
= basename(dupvname
);
334 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
340 printf("Delete subvolume (%s): '%s/%s'\n",
341 commit_mode
== 2 || (commit_mode
== 1 && cnt
+ 1 == argc
)
342 ? "commit" : "no-commit", dname
, vname
);
343 memset(&args
, 0, sizeof(args
));
344 strncpy_null(args
.name
, vname
);
345 res
= ioctl(fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
347 error("cannot delete '%s/%s': %s", dname
, vname
,
353 if (commit_mode
== 1) {
354 res
= wait_for_commit(fd
);
356 error("unable to wait for commit after '%s': %s",
357 path
, strerror(errno
));
369 close_file_or_dir(fd
, dirstream
);
370 /* avoid double free */
376 if (commit_mode
== 2 && fd
!= -1) {
377 res
= wait_for_commit(fd
);
379 error("unable to do final sync after deletion: %s",
384 close_file_or_dir(fd
, dirstream
);
391 * - uppercase for filters and sort options
392 * - lowercase for enabling specific items in the output
394 static const char * const cmd_subvol_list_usage
[] = {
395 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
396 "[--sort=gen,ogen,rootid,path] <path>",
397 "List subvolumes (and snapshots)",
399 "-p print parent ID",
400 "-a print all the subvolumes in the filesystem and",
401 " distinguish absolute and relative path with respect",
402 " to the given <path>",
403 "-c print the ogeneration of the subvolume",
404 "-g print the generation of the subvolume",
405 "-o print only subvolumes below specified path",
406 "-u print the uuid of subvolumes (and snapshots)",
407 "-q print the parent uuid of the snapshots",
408 "-R print the uuid of the received snapshots",
409 "-t print the result as a table",
410 "-s list snapshots only in the filesystem",
411 "-r list readonly subvolumes (including snapshots)",
412 "-d list deleted subvolumes that are not yet cleaned",
414 " filter the subvolumes by generation",
415 " (+value: >= value; -value: <= value; value: = value)",
417 " filter the subvolumes by ogeneration",
418 " (+value: >= value; -value: <= value; value: = value)",
419 "--sort=gen,ogen,rootid,path",
420 " list the subvolume in order of gen, ogen, rootid or path",
421 " you also can add '+' or '-' in front of each items.",
422 " (+:ascending, -:descending, ascending default)",
426 static int cmd_subvol_list(int argc
, char **argv
)
428 struct btrfs_list_filter_set
*filter_set
;
429 struct btrfs_list_comparer_set
*comparer_set
;
433 int ret
= -1, uerr
= 0;
436 int is_only_in_path
= 0;
437 DIR *dirstream
= NULL
;
438 enum btrfs_list_layout layout
= BTRFS_LIST_LAYOUT_DEFAULT
;
440 filter_set
= btrfs_list_alloc_filter_set();
441 comparer_set
= btrfs_list_alloc_comparer_set();
445 static const struct option long_options
[] = {
446 {"sort", required_argument
, NULL
, 'S'},
450 c
= getopt_long(argc
, argv
,
451 "acdgopqsurRG:C:t", long_options
, NULL
);
457 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
463 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
466 btrfs_list_setup_filter(&filter_set
,
467 BTRFS_LIST_FILTER_DELETED
,
471 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
477 layout
= BTRFS_LIST_LAYOUT_TABLE
;
480 btrfs_list_setup_filter(&filter_set
,
481 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
483 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
484 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
487 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
490 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
493 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
496 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
499 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
500 ret
= btrfs_list_parse_filter_string(optarg
,
502 BTRFS_LIST_FILTER_GEN
);
510 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
511 ret
= btrfs_list_parse_filter_string(optarg
,
513 BTRFS_LIST_FILTER_CGEN
);
520 ret
= btrfs_list_parse_sort_string(optarg
,
534 if (check_argc_exact(argc
- optind
, 1)) {
539 subvol
= argv
[optind
];
540 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
543 error("can't access '%s'", subvol
);
548 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
551 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
556 btrfs_list_setup_filter(&filter_set
,
557 BTRFS_LIST_FILTER_FULL_PATH
,
559 else if (is_only_in_path
)
560 btrfs_list_setup_filter(&filter_set
,
561 BTRFS_LIST_FILTER_TOPID_EQUAL
,
564 /* by default we shall print the following columns*/
565 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
566 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
567 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
568 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
570 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
571 layout
, !is_list_all
&& !is_only_in_path
, NULL
);
574 close_file_or_dir(fd
, dirstream
);
580 usage(cmd_subvol_list_usage
);
584 static const char * const cmd_subvol_snapshot_usage
[] = {
585 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
586 "Create a snapshot of the subvolume",
587 "Create a writable/readonly snapshot of the subvolume <source> with",
588 "the name <name> in the <dest> directory. If only <dest> is given,",
589 "the subvolume will be named the basename of <source>.",
591 "-r create a readonly snapshot",
592 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
593 " option can be given multiple times.",
597 static int cmd_subvol_snapshot(int argc
, char **argv
)
601 int fd
= -1, fddst
= -1;
602 int len
, readonly
= 0;
603 char *dupname
= NULL
;
607 struct btrfs_ioctl_vol_args_v2 args
;
608 struct btrfs_qgroup_inherit
*inherit
= NULL
;
609 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
611 memset(&args
, 0, sizeof(args
));
613 int c
= getopt(argc
, argv
, "c:i:r");
619 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
626 res
= qgroup_inherit_add_group(&inherit
, optarg
);
636 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 1);
643 usage(cmd_subvol_snapshot_usage
);
647 if (check_argc_exact(argc
- optind
, 2))
648 usage(cmd_subvol_snapshot_usage
);
650 subvol
= argv
[optind
];
651 dst
= argv
[optind
+ 1];
653 retval
= 1; /* failure */
654 res
= test_issubvolume(subvol
);
656 error("cannot access subvolume %s: %s", subvol
, strerror(-res
));
660 error("not a subvolume: %s", subvol
);
664 res
= test_isdir(dst
);
665 if (res
< 0 && res
!= -ENOENT
) {
666 error("cannot access %s: %s", dst
, strerror(-res
));
670 error("'%s' exists and it is not a directory", dst
);
675 dupname
= strdup(subvol
);
676 newname
= basename(dupname
);
679 dupname
= strdup(dst
);
680 newname
= basename(dupname
);
681 dupdir
= strdup(dst
);
682 dstdir
= dirname(dupdir
);
685 if (!test_issubvolname(newname
)) {
686 error("invalid snapshot name '%s'", newname
);
690 len
= strlen(newname
);
691 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
692 error("snapshot name too long '%s'", newname
);
696 fddst
= btrfs_open_dir(dstdir
, &dirstream1
, 1);
700 fd
= btrfs_open_dir(subvol
, &dirstream2
, 1);
705 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
706 printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
707 subvol
, dstdir
, newname
);
709 printf("Create a snapshot of '%s' in '%s/%s'\n",
710 subvol
, dstdir
, newname
);
715 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
716 args
.size
= qgroup_inherit_size(inherit
);
717 args
.qgroup_inherit
= inherit
;
719 strncpy_null(args
.name
, newname
);
721 res
= ioctl(fddst
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
724 error("cannot snapshot '%s': %s", subvol
, strerror(errno
));
728 retval
= 0; /* success */
731 close_file_or_dir(fddst
, dirstream1
);
732 close_file_or_dir(fd
, dirstream2
);
740 static const char * const cmd_subvol_get_default_usage
[] = {
741 "btrfs subvolume get-default <path>",
742 "Get the default subvolume of a filesystem",
746 static int cmd_subvol_get_default(int argc
, char **argv
)
751 struct btrfs_list_filter_set
*filter_set
;
753 DIR *dirstream
= NULL
;
755 clean_args_no_options(argc
, argv
, cmd_subvol_get_default_usage
);
757 if (check_argc_exact(argc
- optind
, 1))
758 usage(cmd_subvol_get_default_usage
);
761 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
765 ret
= btrfs_list_get_default_subvolume(fd
, &default_id
);
767 error("failed to look up default subvolume: %s",
773 if (default_id
== 0) {
774 error("'default' dir item not found");
778 /* no need to resolve roots if FS_TREE is default */
779 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
780 printf("ID 5 (FS_TREE)\n");
785 filter_set
= btrfs_list_alloc_filter_set();
786 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_ROOTID
,
789 /* by default we shall print the following columns*/
790 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
791 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
792 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
793 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
795 ret
= btrfs_list_subvols_print(fd
, filter_set
, NULL
,
796 BTRFS_LIST_LAYOUT_DEFAULT
, 1, NULL
);
801 close_file_or_dir(fd
, dirstream
);
805 static const char * const cmd_subvol_set_default_usage
[] = {
806 "btrfs subvolume set-default <subvolid> <path>",
807 "Set the default subvolume of a filesystem",
811 static int cmd_subvol_set_default(int argc
, char **argv
)
817 DIR *dirstream
= NULL
;
819 clean_args_no_options(argc
, argv
, cmd_subvol_set_default_usage
);
821 if (check_argc_exact(argc
- optind
, 2))
822 usage(cmd_subvol_set_default_usage
);
824 subvolid
= argv
[optind
];
825 path
= argv
[optind
+ 1];
827 objectid
= arg_strtou64(subvolid
);
829 fd
= btrfs_open_dir(path
, &dirstream
, 1);
833 ret
= ioctl(fd
, BTRFS_IOC_DEFAULT_SUBVOL
, &objectid
);
835 close_file_or_dir(fd
, dirstream
);
837 error("unable to set a new default subvolume: %s",
844 static const char * const cmd_subvol_find_new_usage
[] = {
845 "btrfs subvolume find-new <path> <lastgen>",
846 "List the recently modified files in a filesystem",
850 static int cmd_subvol_find_new(int argc
, char **argv
)
856 DIR *dirstream
= NULL
;
858 clean_args_no_options(argc
, argv
, cmd_subvol_find_new_usage
);
860 if (check_argc_exact(argc
- optind
, 2))
861 usage(cmd_subvol_find_new_usage
);
863 subvol
= argv
[optind
];
864 last_gen
= arg_strtou64(argv
[optind
+ 1]);
866 ret
= test_issubvolume(subvol
);
868 error("cannot access subvolume %s: %s", subvol
, strerror(-ret
));
872 error("not a subvolume: %s", subvol
);
876 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
880 ret
= ioctl(fd
, BTRFS_IOC_SYNC
);
882 error("sync ioctl failed on '%s': %s",
883 subvol
, strerror(errno
));
884 close_file_or_dir(fd
, dirstream
);
888 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
889 close_file_or_dir(fd
, dirstream
);
893 static const char * const cmd_subvol_show_usage
[] = {
894 "btrfs subvolume show [options] <subvol-path>|<mnt>",
895 "Show more information about the subvolume",
896 "-r|--rootid rootid of the subvolume",
897 "-u|--uuid uuid of the subvolume",
899 "If no option is specified, <subvol-path> will be shown, otherwise",
900 "the rootid or uuid are resolved relative to the <mnt> path.",
904 static int cmd_subvol_show(int argc
, char **argv
)
906 struct root_info get_ri
;
907 struct btrfs_list_filter_set
*filter_set
= NULL
;
909 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
910 char *fullpath
= NULL
;
911 char raw_prefix
[] = "\t\t\t\t";
914 DIR *dirstream1
= NULL
;
918 u8 uuid_arg
[BTRFS_UUID_SIZE
];
922 static const struct option long_options
[] = {
923 { "rootid", required_argument
, NULL
, 'r'},
924 { "uuid", required_argument
, NULL
, 'u'},
928 c
= getopt_long(argc
, argv
, "r:u:", long_options
, NULL
);
934 rootid_arg
= arg_strtou64(optarg
);
938 uuid_parse(optarg
, uuid_arg
);
942 usage(cmd_subvol_show_usage
);
946 if (check_argc_exact(argc
- optind
, 1))
947 usage(cmd_subvol_show_usage
);
949 if (by_rootid
&& by_uuid
) {
951 "options --rootid and --uuid cannot be used at the same time");
952 usage(cmd_subvol_show_usage
);
955 memset(&get_ri
, 0, sizeof(get_ri
));
956 fullpath
= realpath(argv
[optind
], NULL
);
958 error("cannot find real path for '%s': %s",
959 argv
[optind
], strerror(errno
));
964 ret
= get_subvol_info_by_rootid(fullpath
, &get_ri
, rootid_arg
);
965 } else if (by_uuid
) {
966 ret
= get_subvol_info_by_uuid(fullpath
, &get_ri
, uuid_arg
);
968 ret
= get_subvol_info(fullpath
, &get_ri
);
973 error("Failed to get subvol info %s: %s",
974 fullpath
, strerror(-ret
));
976 error("Failed to get subvol info %s: %d",
983 printf("%s\n", get_ri
.full_path
);
984 printf("\tName: \t\t\t%s\n", get_ri
.name
);
986 if (uuid_is_null(get_ri
.uuid
))
987 strcpy(uuidparse
, "-");
989 uuid_unparse(get_ri
.uuid
, uuidparse
);
990 printf("\tUUID: \t\t\t%s\n", uuidparse
);
992 if (uuid_is_null(get_ri
.puuid
))
993 strcpy(uuidparse
, "-");
995 uuid_unparse(get_ri
.puuid
, uuidparse
);
996 printf("\tParent UUID: \t\t%s\n", uuidparse
);
998 if (uuid_is_null(get_ri
.ruuid
))
999 strcpy(uuidparse
, "-");
1001 uuid_unparse(get_ri
.ruuid
, uuidparse
);
1002 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1007 localtime_r(&get_ri
.otime
, &tm
);
1008 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1011 printf("\tCreation time: \t\t%s\n", tstr
);
1013 printf("\tSubvolume ID: \t\t%llu\n", get_ri
.root_id
);
1014 printf("\tGeneration: \t\t%llu\n", get_ri
.gen
);
1015 printf("\tGen at creation: \t%llu\n", get_ri
.ogen
);
1016 printf("\tParent ID: \t\t%llu\n", get_ri
.ref_tree
);
1017 printf("\tTop level ID: \t\t%llu\n", get_ri
.top_id
);
1019 if (get_ri
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1020 printf("\tFlags: \t\t\treadonly\n");
1022 printf("\tFlags: \t\t\t-\n");
1024 /* print the snapshots of the given subvol if any*/
1025 printf("\tSnapshot(s):\n");
1026 filter_set
= btrfs_list_alloc_filter_set();
1027 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_BY_PARENT
,
1028 (u64
)(unsigned long)get_ri
.uuid
);
1029 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
1031 fd
= open_file_or_dir(fullpath
, &dirstream1
);
1033 fprintf(stderr
, "ERROR: can't access '%s'\n", fullpath
);
1036 btrfs_list_subvols_print(fd
, filter_set
, NULL
, BTRFS_LIST_LAYOUT_RAW
,
1043 free(get_ri
.full_path
);
1046 close_file_or_dir(fd
, dirstream1
);
1051 static const char * const cmd_subvol_sync_usage
[] = {
1052 "btrfs subvolume sync <path> [<subvol-id>...]",
1053 "Wait until given subvolume(s) are completely removed from the filesystem.",
1054 "Wait until given subvolume(s) are completely removed from the filesystem",
1056 "If no subvolume id is given, wait until all current deletion requests",
1057 "are completed, but do not wait for subvolumes deleted meanwhile.",
1058 "The status of subvolume ids is checked periodically.",
1060 "-s <N> sleep N seconds between checks (default: 1)",
1066 * If we're looking for any dead subvolume, take a shortcut and look
1067 * for any ORPHAN_ITEMs in the tree root
1069 static int fs_has_dead_subvolumes(int fd
)
1072 struct btrfs_ioctl_search_args args
;
1073 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1074 struct btrfs_ioctl_search_header sh
;
1075 u64 min_subvolid
= 0;
1078 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1079 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1080 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1081 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1082 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1083 sk
->min_offset
= min_subvolid
;
1084 sk
->max_offset
= (u64
)-1;
1085 sk
->min_transid
= 0;
1086 sk
->max_transid
= (u64
)-1;
1089 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1096 memcpy(&sh
, args
.buf
, sizeof(sh
));
1097 min_subvolid
= sh
.offset
;
1100 * Verify that the root item is really there and we haven't hit
1103 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1104 sk
->min_objectid
= min_subvolid
;
1105 sk
->max_objectid
= min_subvolid
;
1106 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
1107 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
1109 sk
->max_offset
= (u64
)-1;
1110 sk
->min_transid
= 0;
1111 sk
->max_transid
= (u64
)-1;
1114 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1119 * Stale orphan, try the next one
1121 if (!sk
->nr_items
) {
1130 #define SUBVOL_ID_BATCH 1024
1133 * Enumerate all dead subvolumes that exist in the filesystem.
1134 * Fill @ids and reallocate to bigger size if needed.
1136 static int enumerate_dead_subvols(int fd
, u64
**ids
)
1139 struct btrfs_ioctl_search_args args
;
1140 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1144 memset(&args
, 0, sizeof(args
));
1146 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1147 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1148 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1149 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1150 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1152 sk
->max_offset
= (u64
)-1;
1153 sk
->min_transid
= 0;
1154 sk
->max_transid
= (u64
)-1;
1155 sk
->nr_items
= 4096;
1159 struct btrfs_ioctl_search_header
*sh
;
1163 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1171 for (i
= 0; i
< sk
->nr_items
; i
++) {
1172 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
1175 if (btrfs_search_header_type(sh
)
1176 == BTRFS_ORPHAN_ITEM_KEY
) {
1180 count
+= SUBVOL_ID_BATCH
;
1181 newids
= (u64
*)realloc(*ids
,
1182 count
* sizeof(u64
));
1187 (*ids
)[idx
] = btrfs_search_header_offset(sh
);
1190 off
+= btrfs_search_header_len(sh
);
1192 sk
->min_objectid
= btrfs_search_header_objectid(sh
);
1193 sk
->min_type
= btrfs_search_header_type(sh
);
1194 sk
->min_offset
= btrfs_search_header_offset(sh
);
1196 if (sk
->min_offset
< (u64
)-1)
1200 if (sk
->min_type
!= BTRFS_ORPHAN_ITEM_KEY
)
1202 if (sk
->min_objectid
!= BTRFS_ORPHAN_OBJECTID
)
1209 static int cmd_subvol_sync(int argc
, char **argv
)
1214 DIR *dirstream
= NULL
;
1217 int sleep_interval
= 1;
1220 int c
= getopt(argc
, argv
, "s:");
1227 sleep_interval
= atoi(optarg
);
1228 if (sleep_interval
< 1) {
1229 error("invalid sleep interval %s", optarg
);
1235 usage(cmd_subvol_sync_usage
);
1239 if (check_argc_min(argc
- optind
, 1))
1240 usage(cmd_subvol_sync_usage
);
1242 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1249 id_count
= argc
- optind
;
1251 id_count
= enumerate_dead_subvols(fd
, &ids
);
1253 error("can't enumerate dead subvolumes: %s",
1254 strerror(-id_count
));
1258 if (id_count
== 0) {
1263 ids
= (u64
*)malloc(id_count
* sizeof(u64
));
1265 error("not enough memory");
1270 for (i
= 0; i
< id_count
; i
++) {
1274 arg
= argv
[optind
+ i
];
1276 id
= strtoull(arg
, NULL
, 10);
1278 error("unrecognized subvolume id %s", arg
);
1282 if (id
< BTRFS_FIRST_FREE_OBJECTID
1283 || id
> BTRFS_LAST_FREE_OBJECTID
) {
1284 error("subvolume id %s out of range", arg
);
1292 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1296 close_file_or_dir(fd
, dirstream
);
1301 static const char subvolume_cmd_group_info
[] =
1302 "manage subvolumes: create, delete, list, etc";
1304 const struct cmd_group subvolume_cmd_group
= {
1305 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1306 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1307 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1308 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1309 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1311 { "get-default", cmd_subvol_get_default
,
1312 cmd_subvol_get_default_usage
, NULL
, 0 },
1313 { "set-default", cmd_subvol_set_default
,
1314 cmd_subvol_set_default_usage
, NULL
, 0 },
1315 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1317 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1318 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1323 int cmd_subvolume(int argc
, char **argv
)
1325 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);