2 * Copyright (C) 2018 Facebook
4 * This file is part of libbtrfsutil.
6 * libbtrfsutil is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * libbtrfsutil is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/ioctl.h>
27 #include <sys/types.h>
29 #include <linux/magic.h>
32 #include "btrfsutil_internal.h"
35 * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
36 * a file descriptor and calling it, because fstat() and fstatfs() don't accept
37 * file descriptors opened with O_PATH on old kernels (before v3.6 and before
38 * v3.12, respectively), but stat() and statfs() can be called on a path that
39 * the user doesn't have read or write permissions to.
41 PUBLIC
enum btrfs_util_error
btrfs_util_is_subvolume(const char *path
)
47 ret
= statfs(path
, &sfs
);
49 return BTRFS_UTIL_ERROR_STATFS_FAILED
;
51 if (sfs
.f_type
!= BTRFS_SUPER_MAGIC
) {
53 return BTRFS_UTIL_ERROR_NOT_BTRFS
;
56 ret
= stat(path
, &st
);
58 return BTRFS_UTIL_ERROR_STAT_FAILED
;
60 if (st
.st_ino
!= BTRFS_FIRST_FREE_OBJECTID
|| !S_ISDIR(st
.st_mode
)) {
62 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME
;
68 PUBLIC
enum btrfs_util_error
btrfs_util_is_subvolume_fd(int fd
)
74 ret
= fstatfs(fd
, &sfs
);
76 return BTRFS_UTIL_ERROR_STATFS_FAILED
;
78 if (sfs
.f_type
!= BTRFS_SUPER_MAGIC
) {
80 return BTRFS_UTIL_ERROR_NOT_BTRFS
;
85 return BTRFS_UTIL_ERROR_STAT_FAILED
;
87 if (st
.st_ino
!= BTRFS_FIRST_FREE_OBJECTID
|| !S_ISDIR(st
.st_mode
)) {
89 return BTRFS_UTIL_ERROR_NOT_SUBVOLUME
;
95 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_id(const char *path
,
98 enum btrfs_util_error err
;
101 fd
= open(path
, O_RDONLY
);
103 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
105 err
= btrfs_util_subvolume_id_fd(fd
, id_ret
);
106 SAVE_ERRNO_AND_CLOSE(fd
);
110 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_id_fd(int fd
,
113 struct btrfs_ioctl_ino_lookup_args args
= {
115 .objectid
= BTRFS_FIRST_FREE_OBJECTID
,
119 ret
= ioctl(fd
, BTRFS_IOC_INO_LOOKUP
, &args
);
122 return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED
;
125 *id_ret
= args
.treeid
;
127 return BTRFS_UTIL_OK
;
130 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_path(const char *path
,
134 enum btrfs_util_error err
;
137 fd
= open(path
, O_RDONLY
);
139 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
141 err
= btrfs_util_subvolume_path_fd(fd
, id
, path_ret
);
142 SAVE_ERRNO_AND_CLOSE(fd
);
146 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_path_fd(int fd
, uint64_t id
,
150 size_t capacity
= 4096;
153 enum btrfs_util_error err
;
155 err
= btrfs_util_is_subvolume_fd(fd
);
159 err
= btrfs_util_subvolume_id_fd(fd
, &id
);
164 path
= malloc(capacity
);
166 return BTRFS_UTIL_ERROR_NO_MEMORY
;
167 p
= path
+ capacity
- 1;
170 while (id
!= BTRFS_FS_TREE_OBJECTID
) {
171 struct btrfs_ioctl_search_args search
= {
173 .tree_id
= BTRFS_ROOT_TREE_OBJECTID
,
176 .min_type
= BTRFS_ROOT_BACKREF_KEY
,
177 .max_type
= BTRFS_ROOT_BACKREF_KEY
,
179 .max_offset
= UINT64_MAX
,
181 .max_transid
= UINT64_MAX
,
185 struct btrfs_ioctl_ino_lookup_args lookup
;
186 const struct btrfs_ioctl_search_header
*header
;
187 const struct btrfs_root_ref
*ref
;
194 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &search
);
197 return BTRFS_UTIL_ERROR_SEARCH_FAILED
;
200 if (search
.key
.nr_items
== 0) {
203 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
;
206 header
= (struct btrfs_ioctl_search_header
*)search
.buf
;
207 ref
= (struct btrfs_root_ref
*)(header
+ 1);
208 name
= (char *)(ref
+ 1);
209 name_len
= le16_to_cpu(ref
->name_len
);
214 lookup
.objectid
= le64_to_cpu(ref
->dirid
);
215 ret
= ioctl(fd
, BTRFS_IOC_INO_LOOKUP
, &lookup
);
218 return BTRFS_UTIL_ERROR_SEARCH_FAILED
;
220 lookup_len
= strlen(lookup
.name
);
222 total_len
= name_len
+ lookup_len
+ (id
!= BTRFS_FS_TREE_OBJECTID
);
223 if (p
- total_len
< path
) {
224 char *new_path
, *new_p
;
225 size_t new_capacity
= capacity
* 2;
227 new_path
= malloc(new_capacity
);
230 return BTRFS_UTIL_ERROR_NO_MEMORY
;
232 new_p
= new_path
+ new_capacity
- (path
+ capacity
- p
);
233 memcpy(new_p
, p
, path
+ capacity
- p
);
237 capacity
= new_capacity
;
240 memcpy(p
, name
, name_len
);
242 memcpy(p
, lookup
.name
, lookup_len
);
243 if (id
!= BTRFS_FS_TREE_OBJECTID
)
248 memmove(path
, p
, path
+ capacity
- p
);
252 return BTRFS_UTIL_OK
;
255 static void copy_timespec(struct timespec
*timespec
,
256 const struct btrfs_timespec
*btrfs_timespec
)
258 timespec
->tv_sec
= le64_to_cpu(btrfs_timespec
->sec
);
259 timespec
->tv_nsec
= le32_to_cpu(btrfs_timespec
->nsec
);
262 static void copy_root_item(struct btrfs_util_subvolume_info
*subvol
,
263 const struct btrfs_root_item
*root
)
265 subvol
->flags
= le64_to_cpu(root
->flags
);
266 memcpy(subvol
->uuid
, root
->uuid
, sizeof(subvol
->uuid
));
267 memcpy(subvol
->parent_uuid
, root
->parent_uuid
,
268 sizeof(subvol
->parent_uuid
));
269 memcpy(subvol
->received_uuid
, root
->received_uuid
,
270 sizeof(subvol
->received_uuid
));
271 subvol
->generation
= le64_to_cpu(root
->generation
);
272 subvol
->ctransid
= le64_to_cpu(root
->ctransid
);
273 subvol
->otransid
= le64_to_cpu(root
->otransid
);
274 subvol
->stransid
= le64_to_cpu(root
->stransid
);
275 subvol
->rtransid
= le64_to_cpu(root
->rtransid
);
276 copy_timespec(&subvol
->ctime
, &root
->ctime
);
277 copy_timespec(&subvol
->otime
, &root
->otime
);
278 copy_timespec(&subvol
->stime
, &root
->stime
);
279 copy_timespec(&subvol
->rtime
, &root
->rtime
);
282 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_info(const char *path
,
284 struct btrfs_util_subvolume_info
*subvol
)
286 enum btrfs_util_error err
;
289 fd
= open(path
, O_RDONLY
);
291 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
293 err
= btrfs_util_subvolume_info_fd(fd
, id
, subvol
);
294 SAVE_ERRNO_AND_CLOSE(fd
);
298 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_info_fd(int fd
, uint64_t id
,
299 struct btrfs_util_subvolume_info
*subvol
)
301 struct btrfs_ioctl_search_args search
= {
303 .tree_id
= BTRFS_ROOT_TREE_OBJECTID
,
304 .min_type
= BTRFS_ROOT_ITEM_KEY
,
305 .max_type
= BTRFS_ROOT_BACKREF_KEY
,
307 .max_offset
= UINT64_MAX
,
309 .max_transid
= UINT64_MAX
,
313 enum btrfs_util_error err
;
314 size_t items_pos
= 0, buf_off
= 0;
315 bool need_root_item
= true, need_root_backref
= true;
319 err
= btrfs_util_is_subvolume_fd(fd
);
323 err
= btrfs_util_subvolume_id_fd(fd
, &id
);
328 if ((id
< BTRFS_FIRST_FREE_OBJECTID
&& id
!= BTRFS_FS_TREE_OBJECTID
) ||
329 id
> BTRFS_LAST_FREE_OBJECTID
) {
331 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
;
334 search
.key
.min_objectid
= search
.key
.max_objectid
= id
;
338 subvol
->parent_id
= 0;
340 if (id
== BTRFS_FS_TREE_OBJECTID
)
341 need_root_backref
= false;
344 * We only need the backref for filling in the subvolume info.
346 need_root_backref
= false;
349 /* Don't bother searching for the backref if we don't need it. */
350 if (!need_root_backref
)
351 search
.key
.max_type
= BTRFS_ROOT_ITEM_KEY
;
353 while (need_root_item
|| need_root_backref
) {
354 const struct btrfs_ioctl_search_header
*header
;
356 if (items_pos
>= search
.key
.nr_items
) {
357 search
.key
.nr_items
= 4096;
358 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &search
);
360 return BTRFS_UTIL_ERROR_SEARCH_FAILED
;
364 if (search
.key
.nr_items
== 0) {
365 if (need_root_item
) {
367 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
;
374 header
= (struct btrfs_ioctl_search_header
*)(search
.buf
+ buf_off
);
375 if (header
->type
== BTRFS_ROOT_ITEM_KEY
) {
377 const struct btrfs_root_item
*root
;
379 root
= (const struct btrfs_root_item
*)(header
+ 1);
380 copy_root_item(subvol
, root
);
382 need_root_item
= false;
383 search
.key
.min_type
= BTRFS_ROOT_BACKREF_KEY
;
384 } else if (header
->type
== BTRFS_ROOT_BACKREF_KEY
) {
386 const struct btrfs_root_ref
*ref
;
388 ref
= (const struct btrfs_root_ref
*)(header
+ 1);
389 subvol
->parent_id
= header
->offset
;
390 subvol
->dir_id
= le64_to_cpu(ref
->dirid
);
392 need_root_backref
= false;
393 search
.key
.min_type
= UINT32_MAX
;
397 buf_off
+= sizeof(*header
) + header
->len
;
400 return BTRFS_UTIL_OK
;
403 PUBLIC
enum btrfs_util_error
btrfs_util_get_subvolume_read_only_fd(int fd
,
409 ret
= ioctl(fd
, BTRFS_IOC_SUBVOL_GETFLAGS
, &flags
);
411 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED
;
413 *read_only_ret
= flags
& BTRFS_SUBVOL_RDONLY
;
414 return BTRFS_UTIL_OK
;
417 PUBLIC
enum btrfs_util_error
btrfs_util_get_subvolume_read_only(const char *path
,
420 enum btrfs_util_error err
;
423 fd
= open(path
, O_RDONLY
);
425 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
427 err
= btrfs_util_get_subvolume_read_only_fd(fd
, ret
);
428 SAVE_ERRNO_AND_CLOSE(fd
);
432 PUBLIC
enum btrfs_util_error
btrfs_util_set_subvolume_read_only(const char *path
,
435 enum btrfs_util_error err
;
438 fd
= open(path
, O_RDONLY
);
440 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
442 err
= btrfs_util_set_subvolume_read_only_fd(fd
, read_only
);
443 SAVE_ERRNO_AND_CLOSE(fd
);
447 PUBLIC
enum btrfs_util_error
btrfs_util_set_subvolume_read_only_fd(int fd
,
453 ret
= ioctl(fd
, BTRFS_IOC_SUBVOL_GETFLAGS
, &flags
);
455 return BTRFS_UTIL_ERROR_SUBVOL_GETFLAGS_FAILED
;
458 flags
|= BTRFS_SUBVOL_RDONLY
;
460 flags
&= ~BTRFS_SUBVOL_RDONLY
;
462 ret
= ioctl(fd
, BTRFS_IOC_SUBVOL_SETFLAGS
, &flags
);
464 return BTRFS_UTIL_ERROR_SUBVOL_SETFLAGS_FAILED
;
466 return BTRFS_UTIL_OK
;
469 PUBLIC
enum btrfs_util_error
btrfs_util_get_default_subvolume(const char *path
,
472 enum btrfs_util_error err
;
475 fd
= open(path
, O_RDONLY
);
477 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
479 err
= btrfs_util_get_default_subvolume_fd(fd
, id_ret
);
480 SAVE_ERRNO_AND_CLOSE(fd
);
484 PUBLIC
enum btrfs_util_error
btrfs_util_get_default_subvolume_fd(int fd
,
487 struct btrfs_ioctl_search_args search
= {
489 .tree_id
= BTRFS_ROOT_TREE_OBJECTID
,
490 .min_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
,
491 .max_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
,
492 .min_type
= BTRFS_DIR_ITEM_KEY
,
493 .max_type
= BTRFS_DIR_ITEM_KEY
,
495 .max_offset
= UINT64_MAX
,
497 .max_transid
= UINT64_MAX
,
501 size_t items_pos
= 0, buf_off
= 0;
505 const struct btrfs_ioctl_search_header
*header
;
507 if (items_pos
>= search
.key
.nr_items
) {
508 search
.key
.nr_items
= 4096;
509 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &search
);
511 return BTRFS_UTIL_ERROR_SEARCH_FAILED
;
515 if (search
.key
.nr_items
== 0) {
517 return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
;
521 header
= (struct btrfs_ioctl_search_header
*)(search
.buf
+ buf_off
);
522 if (header
->type
== BTRFS_DIR_ITEM_KEY
) {
523 const struct btrfs_dir_item
*dir
;
527 dir
= (struct btrfs_dir_item
*)(header
+ 1);
528 name
= (const char *)(dir
+ 1);
529 name_len
= le16_to_cpu(dir
->name_len
);
530 if (strncmp(name
, "default", name_len
) == 0) {
531 *id_ret
= le64_to_cpu(dir
->location
.objectid
);
537 buf_off
+= sizeof(*header
) + header
->len
;
538 search
.key
.min_offset
= header
->offset
+ 1;
541 return BTRFS_UTIL_OK
;
544 PUBLIC
enum btrfs_util_error
btrfs_util_set_default_subvolume(const char *path
,
547 enum btrfs_util_error err
;
550 fd
= open(path
, O_RDONLY
);
552 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
554 err
= btrfs_util_set_default_subvolume_fd(fd
, id
);
555 SAVE_ERRNO_AND_CLOSE(fd
);
559 PUBLIC
enum btrfs_util_error
btrfs_util_set_default_subvolume_fd(int fd
,
562 enum btrfs_util_error err
;
566 err
= btrfs_util_is_subvolume_fd(fd
);
570 err
= btrfs_util_subvolume_id_fd(fd
, &id
);
575 ret
= ioctl(fd
, BTRFS_IOC_DEFAULT_SUBVOL
, &id
);
577 return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED
;
579 return BTRFS_UTIL_OK
;
582 static enum btrfs_util_error
openat_parent_and_name(int dirfd
, const char *path
,
583 char *name
, size_t name_len
,
586 char *tmp_path
, *slash
, *dirname
, *basename
;
589 /* Ignore trailing slashes. */
591 while (len
> 1 && path
[len
- 1] == '/')
594 tmp_path
= malloc(len
+ 1);
596 return BTRFS_UTIL_ERROR_NO_MEMORY
;
597 memcpy(tmp_path
, path
, len
);
598 tmp_path
[len
] = '\0';
600 slash
= memrchr(tmp_path
, '/', len
);
601 if (slash
== tmp_path
) {
603 basename
= tmp_path
+ 1;
607 basename
= slash
+ 1;
613 len
= strlen(basename
);
614 if (len
>= name_len
) {
616 errno
= ENAMETOOLONG
;
617 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
619 memcpy(name
, basename
, len
);
622 *fd
= openat(dirfd
, dirname
, O_RDONLY
| O_DIRECTORY
);
625 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
629 return BTRFS_UTIL_OK
;
632 PUBLIC
enum btrfs_util_error
btrfs_util_create_subvolume(const char *path
,
634 uint64_t *async_transid
,
635 struct btrfs_util_qgroup_inherit
*qgroup_inherit
)
637 char name
[BTRFS_SUBVOL_NAME_MAX
+ 1];
638 enum btrfs_util_error err
;
641 err
= openat_parent_and_name(AT_FDCWD
, path
, name
, sizeof(name
),
646 err
= btrfs_util_create_subvolume_fd(parent_fd
, name
, flags
,
647 async_transid
, qgroup_inherit
);
648 SAVE_ERRNO_AND_CLOSE(parent_fd
);
652 PUBLIC
enum btrfs_util_error
btrfs_util_create_subvolume_fd(int parent_fd
,
655 uint64_t *async_transid
,
656 struct btrfs_util_qgroup_inherit
*qgroup_inherit
)
658 struct btrfs_ioctl_vol_args_v2 args
= {};
664 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
668 args
.flags
|= BTRFS_SUBVOL_CREATE_ASYNC
;
669 if (qgroup_inherit
) {
670 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
671 args
.qgroup_inherit
= (struct btrfs_qgroup_inherit
*)qgroup_inherit
;
672 args
.size
= (sizeof(*args
.qgroup_inherit
) +
673 args
.qgroup_inherit
->num_qgroups
*
674 sizeof(args
.qgroup_inherit
->qgroups
[0]));
678 if (len
>= sizeof(args
.name
)) {
679 errno
= ENAMETOOLONG
;
680 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
682 memcpy(args
.name
, name
, len
);
683 args
.name
[len
] = '\0';
685 ret
= ioctl(parent_fd
, BTRFS_IOC_SUBVOL_CREATE_V2
, &args
);
687 return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED
;
690 *async_transid
= args
.transid
;
692 return BTRFS_UTIL_OK
;
695 #define BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD (1 << 30)
697 struct search_stack_entry
{
698 struct btrfs_ioctl_search_args search
;
699 size_t items_pos
, buf_off
;
703 struct btrfs_util_subvolume_iterator
{
707 struct search_stack_entry
*search_stack
;
708 size_t search_stack_len
;
709 size_t search_stack_capacity
;
712 size_t cur_path_capacity
;
715 static enum btrfs_util_error
append_to_search_stack(struct btrfs_util_subvolume_iterator
*iter
,
719 struct search_stack_entry
*entry
;
721 if (iter
->search_stack_len
>= iter
->search_stack_capacity
) {
722 size_t new_capacity
= iter
->search_stack_capacity
* 2;
723 struct search_stack_entry
*new_search_stack
;
725 new_search_stack
= reallocarray(iter
->search_stack
,
727 sizeof(*iter
->search_stack
));
728 if (!new_search_stack
)
729 return BTRFS_UTIL_ERROR_NO_MEMORY
;
731 iter
->search_stack_capacity
= new_capacity
;
732 iter
->search_stack
= new_search_stack
;
735 entry
= &iter
->search_stack
[iter
->search_stack_len
++];
737 memset(&entry
->search
, 0, sizeof(entry
->search
));
738 entry
->search
.key
.tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
739 entry
->search
.key
.min_objectid
= tree_id
;
740 entry
->search
.key
.max_objectid
= tree_id
;
741 entry
->search
.key
.min_type
= BTRFS_ROOT_REF_KEY
;
742 entry
->search
.key
.max_type
= BTRFS_ROOT_REF_KEY
;
743 entry
->search
.key
.min_offset
= 0;
744 entry
->search
.key
.max_offset
= UINT64_MAX
;
745 entry
->search
.key
.min_transid
= 0;
746 entry
->search
.key
.max_transid
= UINT64_MAX
;
747 entry
->search
.key
.nr_items
= 0;
749 entry
->items_pos
= 0;
752 entry
->path_len
= path_len
;
754 return BTRFS_UTIL_OK
;
757 PUBLIC
enum btrfs_util_error
btrfs_util_create_subvolume_iterator(const char *path
,
760 struct btrfs_util_subvolume_iterator
**ret
)
762 enum btrfs_util_error err
;
765 fd
= open(path
, O_RDONLY
);
767 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
769 err
= btrfs_util_create_subvolume_iterator_fd(fd
, top
, flags
, ret
);
771 SAVE_ERRNO_AND_CLOSE(fd
);
773 (*ret
)->flags
|= BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD
;
778 PUBLIC
enum btrfs_util_error
btrfs_util_create_subvolume_iterator_fd(int fd
,
781 struct btrfs_util_subvolume_iterator
**ret
)
783 struct btrfs_util_subvolume_iterator
*iter
;
784 enum btrfs_util_error err
;
786 if (flags
& ~BTRFS_UTIL_SUBVOLUME_ITERATOR_MASK
) {
788 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
792 err
= btrfs_util_is_subvolume_fd(fd
);
796 err
= btrfs_util_subvolume_id_fd(fd
, &top
);
801 iter
= malloc(sizeof(*iter
));
803 return BTRFS_UTIL_ERROR_NO_MEMORY
;
808 iter
->search_stack_len
= 0;
809 iter
->search_stack_capacity
= 4;
810 iter
->search_stack
= malloc(sizeof(*iter
->search_stack
) *
811 iter
->search_stack_capacity
);
812 if (!iter
->search_stack
) {
813 err
= BTRFS_UTIL_ERROR_NO_MEMORY
;
817 iter
->cur_path_capacity
= 256;
818 iter
->cur_path
= malloc(iter
->cur_path_capacity
);
819 if (!iter
->cur_path
) {
820 err
= BTRFS_UTIL_ERROR_NO_MEMORY
;
821 goto out_search_stack
;
824 err
= append_to_search_stack(iter
, top
, 0);
830 return BTRFS_UTIL_OK
;
833 free(iter
->cur_path
);
835 free(iter
->search_stack
);
841 static enum btrfs_util_error
snapshot_subvolume_children(int fd
, int parent_fd
,
843 uint64_t *async_transid
)
845 struct btrfs_util_subvolume_iterator
*iter
;
846 enum btrfs_util_error err
;
849 dstfd
= openat(parent_fd
, name
, O_RDONLY
);
851 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
853 err
= btrfs_util_create_subvolume_iterator_fd(fd
, 0, 0, &iter
);
858 char child_name
[BTRFS_SUBVOL_NAME_MAX
+ 1];
860 int child_fd
, new_parent_fd
;
861 uint64_t tmp_transid
;
863 err
= btrfs_util_subvolume_iterator_next(iter
, &child_path
,
866 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
)
871 /* Remove the placeholder directory. */
872 if (unlinkat(dstfd
, child_path
, AT_REMOVEDIR
) == -1) {
874 err
= BTRFS_UTIL_ERROR_RMDIR_FAILED
;
878 child_fd
= openat(fd
, child_path
, O_RDONLY
);
879 if (child_fd
== -1) {
881 err
= BTRFS_UTIL_ERROR_OPEN_FAILED
;
885 err
= openat_parent_and_name(dstfd
, child_path
, child_name
,
890 SAVE_ERRNO_AND_CLOSE(child_fd
);
894 err
= btrfs_util_create_snapshot_fd2(child_fd
, new_parent_fd
,
896 async_transid
? &tmp_transid
: NULL
,
898 SAVE_ERRNO_AND_CLOSE(child_fd
);
899 SAVE_ERRNO_AND_CLOSE(new_parent_fd
);
902 if (async_transid
&& tmp_transid
> *async_transid
)
903 *async_transid
= tmp_transid
;
906 btrfs_util_destroy_subvolume_iterator(iter
);
908 SAVE_ERRNO_AND_CLOSE(dstfd
);
912 PUBLIC
enum btrfs_util_error
btrfs_util_create_snapshot(const char *source
,
915 uint64_t *async_transid
,
916 struct btrfs_util_qgroup_inherit
*qgroup_inherit
)
918 enum btrfs_util_error err
;
921 fd
= open(source
, O_RDONLY
);
923 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
925 err
= btrfs_util_create_snapshot_fd(fd
, path
, flags
, async_transid
,
927 SAVE_ERRNO_AND_CLOSE(fd
);
931 PUBLIC
enum btrfs_util_error
btrfs_util_create_snapshot_fd(int fd
,
934 uint64_t *async_transid
,
935 struct btrfs_util_qgroup_inherit
*qgroup_inherit
)
937 char name
[BTRFS_SUBVOL_NAME_MAX
+ 1];
938 enum btrfs_util_error err
;
941 err
= openat_parent_and_name(AT_FDCWD
, path
, name
, sizeof(name
),
946 err
= btrfs_util_create_snapshot_fd2(fd
, parent_fd
, name
, flags
,
947 async_transid
, qgroup_inherit
);
948 SAVE_ERRNO_AND_CLOSE(parent_fd
);
952 PUBLIC
enum btrfs_util_error
btrfs_util_create_snapshot_fd2(int fd
,
956 uint64_t *async_transid
,
957 struct btrfs_util_qgroup_inherit
*qgroup_inherit
)
959 struct btrfs_ioctl_vol_args_v2 args
= {.fd
= fd
};
960 enum btrfs_util_error err
;
964 if ((flags
& ~BTRFS_UTIL_CREATE_SNAPSHOT_MASK
) ||
965 ((flags
& BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY
) &&
966 (flags
& BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE
))) {
968 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
971 if (flags
& BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY
)
972 args
.flags
|= BTRFS_SUBVOL_RDONLY
;
974 args
.flags
|= BTRFS_SUBVOL_CREATE_ASYNC
;
975 if (qgroup_inherit
) {
976 args
.flags
|= BTRFS_SUBVOL_QGROUP_INHERIT
;
977 args
.qgroup_inherit
= (struct btrfs_qgroup_inherit
*)qgroup_inherit
;
978 args
.size
= (sizeof(*args
.qgroup_inherit
) +
979 args
.qgroup_inherit
->num_qgroups
*
980 sizeof(args
.qgroup_inherit
->qgroups
[0]));
984 if (len
>= sizeof(args
.name
)) {
985 errno
= ENAMETOOLONG
;
986 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
988 memcpy(args
.name
, name
, len
);
989 args
.name
[len
] = '\0';
991 ret
= ioctl(parent_fd
, BTRFS_IOC_SNAP_CREATE_V2
, &args
);
993 return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED
;
996 *async_transid
= args
.transid
;
998 if (flags
& BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE
) {
999 err
= snapshot_subvolume_children(fd
, parent_fd
, name
,
1005 return BTRFS_UTIL_OK
;
1008 static enum btrfs_util_error
delete_subvolume_children(int parent_fd
,
1011 struct btrfs_util_subvolume_iterator
*iter
;
1012 enum btrfs_util_error err
;
1015 fd
= openat(parent_fd
, name
, O_RDONLY
);
1017 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
1019 err
= btrfs_util_create_subvolume_iterator_fd(fd
, 0,
1020 BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER
,
1026 char child_name
[BTRFS_PATH_NAME_MAX
+ 1];
1028 int child_parent_fd
;
1030 err
= btrfs_util_subvolume_iterator_next(iter
, &child_path
,
1033 if (err
== BTRFS_UTIL_ERROR_STOP_ITERATION
)
1034 err
= BTRFS_UTIL_OK
;
1038 err
= openat_parent_and_name(fd
, child_path
, child_name
,
1045 err
= btrfs_util_delete_subvolume_fd(child_parent_fd
,
1047 SAVE_ERRNO_AND_CLOSE(child_parent_fd
);
1052 btrfs_util_destroy_subvolume_iterator(iter
);
1054 SAVE_ERRNO_AND_CLOSE(fd
);
1058 PUBLIC
enum btrfs_util_error
btrfs_util_delete_subvolume(const char *path
,
1061 char name
[BTRFS_PATH_NAME_MAX
+ 1];
1062 enum btrfs_util_error err
;
1065 err
= openat_parent_and_name(AT_FDCWD
, path
, name
, sizeof(name
),
1070 err
= btrfs_util_delete_subvolume_fd(parent_fd
, name
, flags
);
1071 SAVE_ERRNO_AND_CLOSE(parent_fd
);
1075 PUBLIC
enum btrfs_util_error
btrfs_util_delete_subvolume_fd(int parent_fd
,
1079 struct btrfs_ioctl_vol_args args
= {};
1080 enum btrfs_util_error err
;
1084 if (flags
& ~BTRFS_UTIL_DELETE_SUBVOLUME_MASK
) {
1086 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
1089 if (flags
& BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE
) {
1090 err
= delete_subvolume_children(parent_fd
, name
);
1096 if (len
>= sizeof(args
.name
)) {
1097 errno
= ENAMETOOLONG
;
1098 return BTRFS_UTIL_ERROR_INVALID_ARGUMENT
;
1100 memcpy(args
.name
, name
, len
);
1101 args
.name
[len
] = '\0';
1103 ret
= ioctl(parent_fd
, BTRFS_IOC_SNAP_DESTROY
, &args
);
1105 return BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED
;
1107 return BTRFS_UTIL_OK
;
1110 PUBLIC
void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator
*iter
)
1113 free(iter
->cur_path
);
1114 free(iter
->search_stack
);
1115 if (iter
->flags
& BTRFS_UTIL_SUBVOLUME_ITERATOR_CLOSE_FD
)
1116 SAVE_ERRNO_AND_CLOSE(iter
->fd
);
1121 PUBLIC
int btrfs_util_subvolume_iterator_fd(const struct btrfs_util_subvolume_iterator
*iter
)
1126 static struct search_stack_entry
*top_search_stack_entry(struct btrfs_util_subvolume_iterator
*iter
)
1128 return &iter
->search_stack
[iter
->search_stack_len
- 1];
1131 static enum btrfs_util_error
build_subvol_path(struct btrfs_util_subvolume_iterator
*iter
,
1132 const struct btrfs_ioctl_search_header
*header
,
1133 const struct btrfs_root_ref
*ref
,
1135 size_t *path_len_ret
)
1137 struct btrfs_ioctl_ino_lookup_args lookup
= {
1138 .treeid
= header
->objectid
,
1139 .objectid
= le64_to_cpu(ref
->dirid
),
1141 struct search_stack_entry
*top
= top_search_stack_entry(iter
);
1142 size_t dir_len
, name_len
, path_len
;
1146 ret
= ioctl(iter
->fd
, BTRFS_IOC_INO_LOOKUP
, &lookup
);
1148 return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED
;
1150 dir_len
= strlen(lookup
.name
);
1151 name_len
= le16_to_cpu(ref
->name_len
);
1153 path_len
= top
->path_len
;
1155 * We need a joining slash if we have a current path and a subdirectory.
1157 if (top
->path_len
&& dir_len
)
1159 path_len
+= dir_len
;
1161 * We need another joining slash if we have a current path and a name,
1162 * but not if we have a subdirectory, because the lookup ioctl includes
1165 if (top
->path_len
&& !dir_len
&& name_len
)
1167 path_len
+= name_len
;
1169 if (path_len
> iter
->cur_path_capacity
) {
1170 char *tmp
= realloc(iter
->cur_path
, path_len
);
1173 return BTRFS_UTIL_ERROR_NO_MEMORY
;
1174 iter
->cur_path
= tmp
;
1175 iter
->cur_path_capacity
= path_len
;
1178 p
= iter
->cur_path
+ top
->path_len
;
1179 if (top
->path_len
&& dir_len
)
1181 memcpy(p
, lookup
.name
, dir_len
);
1183 if (top
->path_len
&& !dir_len
&& name_len
)
1185 memcpy(p
, name
, name_len
);
1188 *path_len_ret
= path_len
;
1190 return BTRFS_UTIL_OK
;
1193 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_iterator_next(struct btrfs_util_subvolume_iterator
*iter
,
1197 struct search_stack_entry
*top
;
1198 const struct btrfs_ioctl_search_header
*header
;
1199 const struct btrfs_root_ref
*ref
;
1201 enum btrfs_util_error err
;
1207 if (iter
->search_stack_len
== 0)
1208 return BTRFS_UTIL_ERROR_STOP_ITERATION
;
1210 top
= top_search_stack_entry(iter
);
1211 if (top
->items_pos
< top
->search
.key
.nr_items
) {
1214 top
->search
.key
.nr_items
= 4096;
1215 ret
= ioctl(iter
->fd
, BTRFS_IOC_TREE_SEARCH
, &top
->search
);
1217 return BTRFS_UTIL_ERROR_SEARCH_FAILED
;
1221 if (top
->search
.key
.nr_items
== 0) {
1222 iter
->search_stack_len
--;
1223 if ((iter
->flags
& BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER
) &&
1224 iter
->search_stack_len
)
1230 header
= (struct btrfs_ioctl_search_header
*)(top
->search
.buf
+ top
->buf_off
);
1233 top
->buf_off
+= sizeof(*header
) + header
->len
;
1234 top
->search
.key
.min_offset
= header
->offset
+ 1;
1236 /* This shouldn't happen, but handle it just in case. */
1237 if (header
->type
!= BTRFS_ROOT_REF_KEY
)
1240 ref
= (struct btrfs_root_ref
*)(header
+ 1);
1241 name
= (const char *)(ref
+ 1);
1242 err
= build_subvol_path(iter
, header
, ref
, name
, &path_len
);
1246 err
= append_to_search_stack(iter
, header
->offset
, path_len
);
1250 if (!(iter
->flags
& BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER
)) {
1251 top
= top_search_stack_entry(iter
);
1258 *path_ret
= malloc(top
->path_len
+ 1);
1260 return BTRFS_UTIL_ERROR_NO_MEMORY
;
1261 memcpy(*path_ret
, iter
->cur_path
, top
->path_len
);
1262 (*path_ret
)[top
->path_len
] = '\0';
1265 *id_ret
= top
->search
.key
.min_objectid
;
1266 return BTRFS_UTIL_OK
;
1269 PUBLIC
enum btrfs_util_error
btrfs_util_subvolume_iterator_next_info(struct btrfs_util_subvolume_iterator
*iter
,
1271 struct btrfs_util_subvolume_info
*subvol
)
1273 enum btrfs_util_error err
;
1276 err
= btrfs_util_subvolume_iterator_next(iter
, path_ret
, &id
);
1280 return btrfs_util_subvolume_info_fd(iter
->fd
, id
, subvol
);
1283 PUBLIC
enum btrfs_util_error
btrfs_util_deleted_subvolumes(const char *path
,
1287 enum btrfs_util_error err
;
1290 fd
= open(path
, O_RDONLY
);
1292 return BTRFS_UTIL_ERROR_OPEN_FAILED
;
1294 err
= btrfs_util_deleted_subvolumes_fd(fd
, ids
, n
);
1295 SAVE_ERRNO_AND_CLOSE(fd
);
1299 PUBLIC
enum btrfs_util_error
btrfs_util_deleted_subvolumes_fd(int fd
,
1303 size_t capacity
= 0;
1304 struct btrfs_ioctl_search_args search
= {
1306 .tree_id
= BTRFS_ROOT_TREE_OBJECTID
,
1307 .min_objectid
= BTRFS_ORPHAN_OBJECTID
,
1308 .max_objectid
= BTRFS_ORPHAN_OBJECTID
,
1309 .min_type
= BTRFS_ORPHAN_ITEM_KEY
,
1310 .max_type
= BTRFS_ORPHAN_ITEM_KEY
,
1312 .max_offset
= UINT64_MAX
,
1314 .max_transid
= UINT64_MAX
,
1318 enum btrfs_util_error err
;
1319 size_t items_pos
= 0, buf_off
= 0;
1325 const struct btrfs_ioctl_search_header
*header
;
1327 if (items_pos
>= search
.key
.nr_items
) {
1328 search
.key
.nr_items
= 4096;
1329 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &search
);
1331 err
= BTRFS_UTIL_ERROR_SEARCH_FAILED
;
1337 if (search
.key
.nr_items
== 0)
1341 header
= (struct btrfs_ioctl_search_header
*)(search
.buf
+ buf_off
);
1344 * The orphan item might be for a free space cache inode, so
1345 * check if there's a matching root item.
1347 err
= btrfs_util_subvolume_info_fd(fd
, header
->offset
, NULL
);
1349 if (*n
>= capacity
) {
1350 size_t new_capacity
;
1353 new_capacity
= capacity
? capacity
* 2 : 1;
1354 new_ids
= reallocarray(*ids
, new_capacity
,
1357 err
= BTRFS_UTIL_ERROR_NO_MEMORY
;
1362 capacity
= new_capacity
;
1364 (*ids
)[(*n
)++] = header
->offset
;
1365 } else if (err
!= BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
) {
1370 buf_off
+= sizeof(*header
) + header
->len
;
1371 search
.key
.min_offset
= header
->offset
+ 1;
1374 err
= BTRFS_UTIL_OK
;