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>
27 #include <uuid/uuid.h>
29 #include "kerncompat.h"
36 #include "btrfs-list.h"
39 static int is_subvolume_cleaned(int fd
, u64 subvolid
)
42 struct btrfs_ioctl_search_args args
;
43 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
45 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
46 sk
->min_objectid
= subvolid
;
47 sk
->max_objectid
= subvolid
;
48 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
49 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
51 sk
->max_offset
= (u64
)-1;
53 sk
->max_transid
= (u64
)-1;
56 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
60 if (sk
->nr_items
== 0)
66 static int wait_for_subvolume_cleaning(int fd
, int count
, u64
*ids
,
75 for (i
= 0; i
< count
; i
++) {
78 ret
= is_subvolume_cleaned(fd
, ids
[i
]);
81 "ERROR: can't perform the search - %s\n",
86 printf("Subvolume id %llu is gone\n", ids
[i
]);
94 sleep(sleep_interval
);
100 static const char * const subvolume_cmd_group_usage
[] = {
101 "btrfs subvolume <command> <args>",
105 static const char * const cmd_subvol_create_usage
[] = {
106 "btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
107 "Create a subvolume",
108 "Create a subvolume <name> in <dest>. If <dest> is not given",
109 "subvolume <name> will be created in the current directory.",
111 "-i <qgroupid> add the newly created subvolume to a qgroup. This",
112 " option can be given multiple times.",
116 static int cmd_subvol_create(int argc
, char **argv
)
118 int retval
, res
, len
;
120 char *dupname
= NULL
;
125 struct btrfs_qgroup_inherit
*inherit
= NULL
;
126 DIR *dirstream
= NULL
;
130 int c
= getopt(argc
, argv
, "c:i:v");
136 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
143 res
= qgroup_inherit_add_group(&inherit
, optarg
);
150 usage(cmd_subvol_create_usage
);
154 if (check_argc_exact(argc
- optind
, 1))
155 usage(cmd_subvol_create_usage
);
159 retval
= 1; /* failure */
160 res
= test_isdir(dst
);
162 fprintf(stderr
, "ERROR: '%s' exists\n", dst
);
166 dupname
= strdup(dst
);
167 newname
= basename(dupname
);
168 dupdir
= strdup(dst
);
169 dstdir
= dirname(dupdir
);
171 if (!test_issubvolname(newname
)) {
172 fprintf(stderr
, "ERROR: incorrect subvolume name '%s'\n",
177 len
= strlen(newname
);
178 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
179 fprintf(stderr
, "ERROR: subvolume name too long '%s'\n",
184 fddst
= btrfs_open_dir(dstdir
, &dirstream
, 1);
188 printf("Create subvolume '%s/%s'\n", dstdir
, newname
);
190 struct btrfs_ioctl_vol_args_v2 args
;
192 memset(&args
, 0, sizeof(args
));
193 strncpy_null(args
.name
, newname
);
194 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
195 args
.size
= qgroup_inherit_size(inherit
);
196 args
.qgroup_inherit
= inherit
;
198 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE_V2
, &args
);
200 struct btrfs_ioctl_vol_args args
;
202 memset(&args
, 0, sizeof(args
));
203 strncpy_null(args
.name
, newname
);
205 res
= ioctl(fddst
, BTRFS_IOC_SUBVOL_CREATE
, &args
);
209 fprintf(stderr
, "ERROR: cannot create subvolume - %s\n",
214 retval
= 0; /* success */
216 close_file_or_dir(fddst
, dirstream
);
225 * test if path is a subvolume:
226 * this function return
227 * 0-> path exists but it is not a subvolume
228 * 1-> path exists and it is a subvolume
229 * -1 -> path is unaccessible
231 int test_issubvolume(char *path
)
236 res
= stat(path
, &st
);
240 return (st
.st_ino
== 256) && S_ISDIR(st
.st_mode
);
243 static int wait_for_commit(int fd
)
247 ret
= ioctl(fd
, BTRFS_IOC_START_SYNC
, NULL
);
250 return ioctl(fd
, BTRFS_IOC_WAIT_SYNC
, NULL
);
253 static const char * const cmd_subvol_delete_usage
[] = {
254 "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
255 "Delete subvolume(s)",
256 "Delete subvolumes from the filesystem. The corresponding directory",
257 "is removed instantly but the data blocks are removed later.",
258 "The deletion does not involve full commit by default due to",
259 "performance reasons (as a consequence, the subvolume may appear again",
260 "after a crash). Use one of the --commit options to wait until the",
261 "operation is safely stored on the media.",
263 "-c|--commit-after wait for transaction commit at the end of the operation",
264 "-C|--commit-each wait for transaction commit after deleting each subvolume",
268 static int cmd_subvol_delete(int argc
, char **argv
)
273 struct btrfs_ioctl_vol_args args
;
274 char *dname
, *vname
, *cpath
;
275 char *dupdname
= NULL
;
276 char *dupvname
= NULL
;
278 DIR *dirstream
= NULL
;
285 static const struct option long_options
[] = {
286 {"commit-after", no_argument
, NULL
, 'c'}, /* commit mode 1 */
287 {"commit-each", no_argument
, NULL
, 'C'}, /* commit mode 2 */
291 c
= getopt_long(argc
, argv
, "cC", long_options
, NULL
);
306 usage(cmd_subvol_delete_usage
);
310 if (check_argc_min(argc
- optind
, 1))
311 usage(cmd_subvol_delete_usage
);
314 printf("Transaction commit: %s\n",
315 !commit_mode
? "none (default)" :
316 commit_mode
== 1 ? "at the end" : "after each");
324 res
= test_issubvolume(path
);
326 fprintf(stderr
, "ERROR: error accessing '%s'\n", path
);
331 fprintf(stderr
, "ERROR: '%s' is not a subvolume\n", path
);
336 cpath
= realpath(path
, NULL
);
339 fprintf(stderr
, "ERROR: finding real path for '%s': %s\n",
340 path
, strerror(errno
));
343 dupdname
= strdup(cpath
);
344 dname
= dirname(dupdname
);
345 dupvname
= strdup(cpath
);
346 vname
= basename(dupvname
);
349 fd
= btrfs_open_dir(dname
, &dirstream
, 1);
355 printf("Delete subvolume (%s): '%s/%s'\n",
356 commit_mode
== 2 || (commit_mode
== 1 && cnt
+ 1 == argc
)
357 ? "commit" : "no-commit", dname
, vname
);
358 memset(&args
, 0, sizeof(args
));
359 strncpy_null(args
.name
, vname
);
360 res
= ioctl(fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
364 fprintf( stderr
, "ERROR: cannot delete '%s/%s' - %s\n",
365 dname
, vname
, strerror(e
));
370 if (commit_mode
== 1) {
371 res
= wait_for_commit(fd
);
374 "ERROR: unable to wait for commit after '%s': %s\n",
375 path
, strerror(errno
));
387 close_file_or_dir(fd
, dirstream
);
388 /* avoid double free */
394 if (commit_mode
== 2 && fd
!= -1) {
395 res
= wait_for_commit(fd
);
398 "ERROR: unable to do final sync: %s\n",
403 close_file_or_dir(fd
, dirstream
);
410 * - uppercase for filters and sort options
411 * - lowercase for enabling specific items in the output
413 static const char * const cmd_subvol_list_usage
[] = {
414 "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
415 "[--sort=gen,ogen,rootid,path] <path>",
416 "List subvolumes (and snapshots)",
418 "-p print parent ID",
419 "-a print all the subvolumes in the filesystem and",
420 " distinguish absolute and relative path with respect",
421 " to the given <path>",
422 "-c print the ogeneration of the subvolume",
423 "-g print the generation of the subvolume",
424 "-o print only subvolumes below specified path",
425 "-u print the uuid of subvolumes (and snapshots)",
426 "-q print the parent uuid of the snapshots",
427 "-R print the uuid of the received snapshots",
428 "-t print the result as a table",
429 "-s list snapshots only in the filesystem",
430 "-r list readonly subvolumes (including snapshots)",
431 "-d list deleted subvolumes that are not yet cleaned",
433 " filter the subvolumes by generation",
434 " (+value: >= value; -value: <= value; value: = value)",
436 " filter the subvolumes by ogeneration",
437 " (+value: >= value; -value: <= value; value: = value)",
438 "--sort=gen,ogen,rootid,path",
439 " list the subvolume in order of gen, ogen, rootid or path",
440 " you also can add '+' or '-' in front of each items.",
441 " (+:ascending, -:descending, ascending default)",
445 static int cmd_subvol_list(int argc
, char **argv
)
447 struct btrfs_list_filter_set
*filter_set
;
448 struct btrfs_list_comparer_set
*comparer_set
;
452 int ret
= -1, uerr
= 0;
454 int is_tab_result
= 0;
456 int is_only_in_path
= 0;
457 DIR *dirstream
= NULL
;
459 filter_set
= btrfs_list_alloc_filter_set();
460 comparer_set
= btrfs_list_alloc_comparer_set();
465 static const struct option long_options
[] = {
466 {"sort", required_argument
, NULL
, 'S'},
470 c
= getopt_long(argc
, argv
,
471 "acdgopqsurRG:C:t", long_options
, NULL
);
477 btrfs_list_setup_print_column(BTRFS_LIST_PARENT
);
483 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
486 btrfs_list_setup_filter(&filter_set
,
487 BTRFS_LIST_FILTER_DELETED
,
491 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
500 btrfs_list_setup_filter(&filter_set
,
501 BTRFS_LIST_FILTER_SNAPSHOT_ONLY
,
503 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
504 btrfs_list_setup_print_column(BTRFS_LIST_OTIME
);
507 btrfs_list_setup_print_column(BTRFS_LIST_UUID
);
510 btrfs_list_setup_print_column(BTRFS_LIST_PUUID
);
513 btrfs_list_setup_print_column(BTRFS_LIST_RUUID
);
516 flags
|= BTRFS_ROOT_SUBVOL_RDONLY
;
519 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
520 ret
= btrfs_list_parse_filter_string(optarg
,
522 BTRFS_LIST_FILTER_GEN
);
530 btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION
);
531 ret
= btrfs_list_parse_filter_string(optarg
,
533 BTRFS_LIST_FILTER_CGEN
);
540 ret
= btrfs_list_parse_sort_string(optarg
,
555 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_FLAGS
,
558 if (check_argc_exact(argc
- optind
, 1)) {
563 subvol
= argv
[optind
];
564 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
567 fprintf(stderr
, "ERROR: can't access '%s'\n", subvol
);
571 ret
= btrfs_list_get_path_rootid(fd
, &top_id
);
573 fprintf(stderr
, "ERROR: can't get rootid for '%s'\n", subvol
);
578 btrfs_list_setup_filter(&filter_set
,
579 BTRFS_LIST_FILTER_FULL_PATH
,
581 else if (is_only_in_path
)
582 btrfs_list_setup_filter(&filter_set
,
583 BTRFS_LIST_FILTER_TOPID_EQUAL
,
586 /* by default we shall print the following columns*/
587 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
588 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
589 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
590 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
593 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
594 BTRFS_LIST_LAYOUT_TABLE
,
595 !is_list_all
&& !is_only_in_path
, NULL
);
597 ret
= btrfs_list_subvols_print(fd
, filter_set
, comparer_set
,
598 BTRFS_LIST_LAYOUT_DEFAULT
,
599 !is_list_all
&& !is_only_in_path
, NULL
);
602 close_file_or_dir(fd
, dirstream
);
604 btrfs_list_free_filter_set(filter_set
);
606 btrfs_list_free_comparer_set(comparer_set
);
608 usage(cmd_subvol_list_usage
);
612 static const char * const cmd_subvol_snapshot_usage
[] = {
613 "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
614 "Create a snapshot of the subvolume",
615 "Create a writable/readonly snapshot of the subvolume <source> with",
616 "the name <name> in the <dest> directory. If only <dest> is given,",
617 "the subvolume will be named the basename of <source>.",
619 "-r create a readonly snapshot",
620 "-i <qgroupid> add the newly created snapshot to a qgroup. This",
621 " option can be given multiple times.",
625 static int cmd_subvol_snapshot(int argc
, char **argv
)
629 int fd
= -1, fddst
= -1;
630 int len
, readonly
= 0;
631 char *dupname
= NULL
;
635 struct btrfs_ioctl_vol_args_v2 args
;
636 struct btrfs_qgroup_inherit
*inherit
= NULL
;
637 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
640 memset(&args
, 0, sizeof(args
));
642 int c
= getopt(argc
, argv
, "c:i:r");
648 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 0);
655 res
= qgroup_inherit_add_group(&inherit
, optarg
);
665 res
= qgroup_inherit_add_copy(&inherit
, optarg
, 1);
672 usage(cmd_subvol_snapshot_usage
);
676 if (check_argc_exact(argc
- optind
, 2))
677 usage(cmd_subvol_snapshot_usage
);
679 subvol
= argv
[optind
];
680 dst
= argv
[optind
+ 1];
682 retval
= 1; /* failure */
683 res
= test_issubvolume(subvol
);
685 fprintf(stderr
, "ERROR: error accessing '%s'\n", subvol
);
689 fprintf(stderr
, "ERROR: '%s' is not a subvolume\n", subvol
);
693 res
= test_isdir(dst
);
695 fprintf(stderr
, "ERROR: '%s' exists and it is not a directory\n", dst
);
700 dupname
= strdup(subvol
);
701 newname
= basename(dupname
);
704 dupname
= strdup(dst
);
705 newname
= basename(dupname
);
706 dupdir
= strdup(dst
);
707 dstdir
= dirname(dupdir
);
710 if (!test_issubvolname(newname
)) {
711 fprintf(stderr
, "ERROR: incorrect snapshot name '%s'\n",
716 len
= strlen(newname
);
717 if (len
== 0 || len
>= BTRFS_VOL_NAME_MAX
) {
718 fprintf(stderr
, "ERROR: snapshot name too long '%s'\n",
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 fprintf( stderr
, "ERROR: cannot snapshot '%s' - %s\n",
752 subvol
, strerror(errno
));
756 retval
= 0; /* success */
759 close_file_or_dir(fddst
, dirstream1
);
760 close_file_or_dir(fd
, dirstream2
);
768 static const char * const cmd_subvol_get_default_usage
[] = {
769 "btrfs subvolume get-default <path>",
770 "Get the default subvolume of a filesystem",
774 static int cmd_subvol_get_default(int argc
, char **argv
)
779 struct btrfs_list_filter_set
*filter_set
;
781 DIR *dirstream
= NULL
;
783 if (check_argc_exact(argc
, 2))
784 usage(cmd_subvol_get_default_usage
);
787 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
791 ret
= btrfs_list_get_default_subvolume(fd
, &default_id
);
793 fprintf(stderr
, "ERROR: can't perform the search - %s\n",
799 if (default_id
== 0) {
800 fprintf(stderr
, "ERROR: 'default' dir item not found\n");
804 /* no need to resolve roots if FS_TREE is default */
805 if (default_id
== BTRFS_FS_TREE_OBJECTID
) {
806 printf("ID 5 (FS_TREE)\n");
811 filter_set
= btrfs_list_alloc_filter_set();
812 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_ROOTID
,
815 /* by default we shall print the following columns*/
816 btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID
);
817 btrfs_list_setup_print_column(BTRFS_LIST_GENERATION
);
818 btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL
);
819 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
821 ret
= btrfs_list_subvols_print(fd
, filter_set
, NULL
,
822 BTRFS_LIST_LAYOUT_DEFAULT
, 1, NULL
);
825 btrfs_list_free_filter_set(filter_set
);
827 close_file_or_dir(fd
, dirstream
);
831 static const char * const cmd_subvol_set_default_usage
[] = {
832 "btrfs subvolume set-default <subvolid> <path>",
833 "Set the default subvolume of a filesystem",
837 static int cmd_subvol_set_default(int argc
, char **argv
)
843 DIR *dirstream
= NULL
;
845 if (check_argc_exact(argc
, 3))
846 usage(cmd_subvol_set_default_usage
);
851 objectid
= arg_strtou64(subvolid
);
853 fd
= btrfs_open_dir(path
, &dirstream
, 1);
857 ret
= ioctl(fd
, BTRFS_IOC_DEFAULT_SUBVOL
, &objectid
);
859 close_file_or_dir(fd
, dirstream
);
861 fprintf(stderr
, "ERROR: unable to set a new default subvolume - %s\n",
868 static const char * const cmd_subvol_find_new_usage
[] = {
869 "btrfs subvolume find-new <path> <lastgen>",
870 "List the recently modified files in a filesystem",
874 static int cmd_subvol_find_new(int argc
, char **argv
)
880 DIR *dirstream
= NULL
;
882 if (check_argc_exact(argc
, 3))
883 usage(cmd_subvol_find_new_usage
);
886 last_gen
= arg_strtou64(argv
[2]);
888 ret
= test_issubvolume(subvol
);
890 fprintf(stderr
, "ERROR: error accessing '%s'\n", subvol
);
894 fprintf(stderr
, "ERROR: '%s' is not a subvolume\n", subvol
);
898 fd
= btrfs_open_dir(subvol
, &dirstream
, 1);
902 ret
= ioctl(fd
, BTRFS_IOC_SYNC
);
904 fprintf(stderr
, "ERROR: unable to fs-syncing '%s' - %s\n",
905 subvol
, strerror(errno
));
906 close_file_or_dir(fd
, dirstream
);
910 ret
= btrfs_list_find_updated_files(fd
, 0, last_gen
);
911 close_file_or_dir(fd
, dirstream
);
915 static const char * const cmd_subvol_show_usage
[] = {
916 "btrfs subvolume show <subvol-path>",
917 "Show more information of the subvolume",
921 static int cmd_subvol_show(int argc
, char **argv
)
923 struct root_info get_ri
;
924 struct btrfs_list_filter_set
*filter_set
;
926 char uuidparse
[BTRFS_UUID_UNPARSED_SIZE
];
927 char *fullpath
= NULL
, *svpath
= NULL
, *mnt
= NULL
;
928 char raw_prefix
[] = "\t\t\t\t";
930 int fd
= -1, mntfd
= -1;
932 DIR *dirstream1
= NULL
, *dirstream2
= NULL
;
934 if (check_argc_exact(argc
, 2))
935 usage(cmd_subvol_show_usage
);
937 fullpath
= realpath(argv
[1], NULL
);
939 fprintf(stderr
, "ERROR: finding real path for '%s', %s\n",
940 argv
[1], strerror(errno
));
944 ret
= test_issubvolume(fullpath
);
946 fprintf(stderr
, "ERROR: error accessing '%s'\n", fullpath
);
950 fprintf(stderr
, "ERROR: '%s' is not a subvolume\n", fullpath
);
955 ret
= find_mount_root(fullpath
, &mnt
);
957 fprintf(stderr
, "ERROR: find_mount_root failed on '%s': "
958 "%s\n", fullpath
, strerror(-ret
));
963 "ERROR: %s doesn't belong to btrfs mount point\n",
968 svpath
= get_subvol_name(mnt
, fullpath
);
970 fd
= btrfs_open_dir(fullpath
, &dirstream1
, 1);
974 ret
= btrfs_list_get_path_rootid(fd
, &sv_id
);
976 fprintf(stderr
, "ERROR: can't get rootid for '%s'\n",
981 mntfd
= btrfs_open_dir(mnt
, &dirstream2
, 1);
985 if (sv_id
== BTRFS_FS_TREE_OBJECTID
) {
986 printf("%s is btrfs root\n", fullpath
);
990 memset(&get_ri
, 0, sizeof(get_ri
));
991 get_ri
.root_id
= sv_id
;
993 ret
= btrfs_get_subvol(mntfd
, &get_ri
);
995 fprintf(stderr
, "ERROR: can't find '%s'\n",
1000 /* print the info */
1001 printf("%s\n", fullpath
);
1002 printf("\tName: \t\t\t%s\n", get_ri
.name
);
1004 if (uuid_is_null(get_ri
.uuid
))
1005 strcpy(uuidparse
, "-");
1007 uuid_unparse(get_ri
.uuid
, uuidparse
);
1008 printf("\tUUID: \t\t\t%s\n", uuidparse
);
1010 if (uuid_is_null(get_ri
.puuid
))
1011 strcpy(uuidparse
, "-");
1013 uuid_unparse(get_ri
.puuid
, uuidparse
);
1014 printf("\tParent UUID: \t\t%s\n", uuidparse
);
1016 if (uuid_is_null(get_ri
.ruuid
))
1017 strcpy(uuidparse
, "-");
1019 uuid_unparse(get_ri
.ruuid
, uuidparse
);
1020 printf("\tReceived UUID: \t\t%s\n", uuidparse
);
1025 localtime_r(&get_ri
.otime
, &tm
);
1026 strftime(tstr
, 256, "%Y-%m-%d %X %z", &tm
);
1029 printf("\tCreation time: \t\t%s\n", tstr
);
1031 printf("\tSubvolume ID: \t\t%llu\n", get_ri
.root_id
);
1032 printf("\tGeneration: \t\t%llu\n", get_ri
.gen
);
1033 printf("\tGen at creation: \t%llu\n", get_ri
.ogen
);
1034 printf("\tParent ID: \t\t%llu\n", get_ri
.ref_tree
);
1035 printf("\tTop level ID: \t\t%llu\n", get_ri
.top_id
);
1037 if (get_ri
.flags
& BTRFS_ROOT_SUBVOL_RDONLY
)
1038 printf("\tFlags: \t\t\treadonly\n");
1040 printf("\tFlags: \t\t\t-\n");
1042 /* print the snapshots of the given subvol if any*/
1043 printf("\tSnapshot(s):\n");
1044 filter_set
= btrfs_list_alloc_filter_set();
1045 btrfs_list_setup_filter(&filter_set
, BTRFS_LIST_FILTER_BY_PARENT
,
1046 (u64
)(unsigned long)get_ri
.uuid
);
1047 btrfs_list_setup_print_column(BTRFS_LIST_PATH
);
1048 btrfs_list_subvols_print(fd
, filter_set
, NULL
, BTRFS_LIST_LAYOUT_RAW
,
1054 free(get_ri
.full_path
);
1055 btrfs_list_free_filter_set(filter_set
);
1058 close_file_or_dir(fd
, dirstream1
);
1059 close_file_or_dir(mntfd
, dirstream2
);
1065 static const char * const cmd_subvol_sync_usage
[] = {
1066 "btrfs subvolume sync <path> [<subvol-id>...]",
1067 "Wait until given subvolume(s) are completely removed from the filesystem.",
1068 "Wait until given subvolume(s) are completely removed from the filesystem",
1070 "If no subvolume id is given, wait until all current deletion requests",
1071 "are completed, but do not wait for subvolumes deleted meanwhile.",
1072 "The status of subvolume ids is checked periodically.",
1074 "-s <N> sleep N seconds between checks (default: 1)",
1080 * If we're looking for any dead subvolume, take a shortcut and look
1081 * for any ORPHAN_ITEMs in the tree root
1083 static int fs_has_dead_subvolumes(int fd
)
1086 struct btrfs_ioctl_search_args args
;
1087 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1088 struct btrfs_ioctl_search_header sh
;
1089 u64 min_subvolid
= 0;
1092 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1093 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1094 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1095 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1096 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1097 sk
->min_offset
= min_subvolid
;
1098 sk
->max_offset
= (u64
)-1;
1099 sk
->min_transid
= 0;
1100 sk
->max_transid
= (u64
)-1;
1103 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1110 memcpy(&sh
, args
.buf
, sizeof(sh
));
1111 min_subvolid
= sh
.offset
;
1114 * Verify that the root item is really there and we haven't hit
1117 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1118 sk
->min_objectid
= min_subvolid
;
1119 sk
->max_objectid
= min_subvolid
;
1120 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
1121 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
1123 sk
->max_offset
= (u64
)-1;
1124 sk
->min_transid
= 0;
1125 sk
->max_transid
= (u64
)-1;
1128 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1133 * Stale orphan, try the next one
1135 if (!sk
->nr_items
) {
1144 #define SUBVOL_ID_BATCH 1024
1147 * Enumerate all dead subvolumes that exist in the filesystem.
1148 * Fill @ids and reallocate to bigger size if needed.
1150 static int enumerate_dead_subvols(int fd
, u64
**ids
)
1153 struct btrfs_ioctl_search_args args
;
1154 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
1158 memset(&args
, 0, sizeof(args
));
1160 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
1161 sk
->min_objectid
= BTRFS_ORPHAN_OBJECTID
;
1162 sk
->max_objectid
= BTRFS_ORPHAN_OBJECTID
;
1163 sk
->min_type
= BTRFS_ORPHAN_ITEM_KEY
;
1164 sk
->max_type
= BTRFS_ORPHAN_ITEM_KEY
;
1166 sk
->max_offset
= (u64
)-1;
1167 sk
->min_transid
= 0;
1168 sk
->max_transid
= (u64
)-1;
1169 sk
->nr_items
= 4096;
1173 struct btrfs_ioctl_search_header
*sh
;
1177 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
1185 for (i
= 0; i
< sk
->nr_items
; i
++) {
1186 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
1189 if (sh
->type
== BTRFS_ORPHAN_ITEM_KEY
) {
1193 count
+= SUBVOL_ID_BATCH
;
1194 newids
= (u64
*)realloc(*ids
, count
);
1199 (*ids
)[idx
] = sh
->offset
;
1204 sk
->min_objectid
= sh
->objectid
;
1205 sk
->min_type
= sh
->type
;
1206 sk
->min_offset
= sh
->offset
;
1208 if (sk
->min_offset
< (u64
)-1)
1212 if (sk
->min_type
!= BTRFS_ORPHAN_ITEM_KEY
)
1214 if (sk
->min_objectid
!= BTRFS_ORPHAN_OBJECTID
)
1221 static int cmd_subvol_sync(int argc
, char **argv
)
1226 DIR *dirstream
= NULL
;
1229 int sleep_interval
= 1;
1233 int c
= getopt(argc
, argv
, "s:");
1240 sleep_interval
= atoi(argv
[optind
]);
1241 if (sleep_interval
< 1) {
1243 "ERROR: invalid sleep interval %s\n",
1250 usage(cmd_subvol_sync_usage
);
1254 if (check_argc_min(argc
- optind
, 1))
1255 usage(cmd_subvol_sync_usage
);
1257 fd
= btrfs_open_dir(argv
[optind
], &dirstream
, 1);
1264 id_count
= argc
- optind
;
1266 id_count
= enumerate_dead_subvols(fd
, &ids
);
1268 fprintf(stderr
, "ERROR: can't enumerate dead subvolumes: %s\n",
1269 strerror(-id_count
));
1273 if (id_count
== 0) {
1278 ids
= (u64
*)malloc(id_count
* sizeof(u64
));
1280 fprintf(stderr
, "ERROR: not enough memory\n");
1285 for (i
= 0; i
< id_count
; i
++) {
1289 arg
= argv
[optind
+ i
];
1291 id
= strtoull(arg
, NULL
, 10);
1294 "ERROR: unrecognized subvolume id %s\n",
1299 if (id
< BTRFS_FIRST_FREE_OBJECTID
1300 || id
> BTRFS_LAST_FREE_OBJECTID
) {
1302 "ERROR: subvolume id %s out of range\n",
1311 ret
= wait_for_subvolume_cleaning(fd
, id_count
, ids
, sleep_interval
);
1315 close_file_or_dir(fd
, dirstream
);
1320 static const char subvolume_cmd_group_info
[] =
1321 "manage subvolumes: create, delete, list, etc";
1323 const struct cmd_group subvolume_cmd_group
= {
1324 subvolume_cmd_group_usage
, subvolume_cmd_group_info
, {
1325 { "create", cmd_subvol_create
, cmd_subvol_create_usage
, NULL
, 0 },
1326 { "delete", cmd_subvol_delete
, cmd_subvol_delete_usage
, NULL
, 0 },
1327 { "list", cmd_subvol_list
, cmd_subvol_list_usage
, NULL
, 0 },
1328 { "snapshot", cmd_subvol_snapshot
, cmd_subvol_snapshot_usage
,
1330 { "get-default", cmd_subvol_get_default
,
1331 cmd_subvol_get_default_usage
, NULL
, 0 },
1332 { "set-default", cmd_subvol_set_default
,
1333 cmd_subvol_set_default_usage
, NULL
, 0 },
1334 { "find-new", cmd_subvol_find_new
, cmd_subvol_find_new_usage
,
1336 { "show", cmd_subvol_show
, cmd_subvol_show_usage
, NULL
, 0 },
1337 { "sync", cmd_subvol_sync
, cmd_subvol_sync_usage
, NULL
, 0 },
1342 int cmd_subvolume(int argc
, char **argv
)
1344 return handle_command_group(&subvolume_cmd_group
, argc
, argv
);