2 * Copyright (C) 2012 Alexander Block. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
21 #include <sys/ioctl.h>
22 #include <uuid/uuid.h>
27 #include "send-utils.h"
29 #include "btrfs-list.h"
31 static int btrfs_subvolid_resolve_sub(int fd
, char *path
, size_t *path_len
,
34 static int btrfs_get_root_id_by_sub_path(int mnt_fd
, const char *sub_path
,
40 subvol_fd
= openat(mnt_fd
, sub_path
, O_RDONLY
);
43 fprintf(stderr
, "ERROR: open %s failed. %s\n", sub_path
,
48 ret
= btrfs_list_get_path_rootid(subvol_fd
, root_id
);
53 static int btrfs_read_root_item_raw(int mnt_fd
, u64 root_id
, size_t buf_len
,
54 u32
*read_len
, void *buf
)
57 struct btrfs_ioctl_search_args args
;
58 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
59 struct btrfs_ioctl_search_header
*sh
;
60 unsigned long off
= 0;
65 memset(&args
, 0, sizeof(args
));
67 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
70 * there may be more than one ROOT_ITEM key if there are
71 * snapshots pending deletion, we have to loop through
74 sk
->min_objectid
= root_id
;
75 sk
->max_objectid
= root_id
;
76 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
77 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
78 sk
->max_offset
= (u64
)-1;
79 sk
->max_transid
= (u64
)-1;
83 ret
= ioctl(mnt_fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
86 "ERROR: can't perform the search - %m\n");
89 /* the ioctl returns the number of item it found in nr_items */
90 if (sk
->nr_items
== 0)
94 for (i
= 0; i
< sk
->nr_items
; i
++) {
95 struct btrfs_root_item
*item
;
96 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+
100 item
= (struct btrfs_root_item
*)(args
.buf
+ off
);
101 off
+= btrfs_search_header_len(sh
);
103 sk
->min_objectid
= btrfs_search_header_objectid(sh
);
104 sk
->min_type
= btrfs_search_header_type(sh
);
105 sk
->min_offset
= btrfs_search_header_offset(sh
);
107 if (btrfs_search_header_objectid(sh
) > root_id
)
110 if (btrfs_search_header_objectid(sh
) == root_id
&&
111 btrfs_search_header_type(sh
) == BTRFS_ROOT_ITEM_KEY
) {
112 if (btrfs_search_header_len(sh
) > buf_len
) {
113 /* btrfs-progs is too old for kernel */
115 "ERROR: buf for read_root_item_raw() is too small, get newer btrfs tools!\n");
118 memcpy(buf
, item
, btrfs_search_header_len(sh
));
119 *read_len
= btrfs_search_header_len(sh
);
123 if (sk
->min_offset
< (u64
)-1)
128 if (sk
->min_type
!= BTRFS_ROOT_ITEM_KEY
||
129 sk
->min_objectid
!= root_id
)
133 return found
? 0 : -ENOENT
;
137 * Read a root item from the tree. In case we detect a root item smaller then
138 * sizeof(root_item), we know it's an old version of the root structure and
139 * initialize all new fields to zero. The same happens if we detect mismatching
140 * generation numbers as then we know the root was once mounted with an older
141 * kernel that was not aware of the root item structure change.
143 static int btrfs_read_root_item(int mnt_fd
, u64 root_id
,
144 struct btrfs_root_item
*item
)
149 ret
= btrfs_read_root_item_raw(mnt_fd
, root_id
, sizeof(*item
),
154 if (read_len
< sizeof(*item
) ||
155 btrfs_root_generation(item
) != btrfs_root_generation_v2(item
))
156 memset(&item
->generation_v2
, 0,
157 sizeof(*item
) - offsetof(struct btrfs_root_item
,
163 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
164 static struct rb_node
*tree_insert(struct rb_root
*root
,
165 struct subvol_info
*si
,
166 enum subvol_search_type type
)
168 struct rb_node
**p
= &root
->rb_node
;
169 struct rb_node
*parent
= NULL
;
170 struct subvol_info
*entry
;
175 if (type
== subvol_search_by_received_uuid
) {
176 entry
= rb_entry(parent
, struct subvol_info
,
179 comp
= memcmp(entry
->received_uuid
, si
->received_uuid
,
182 if (entry
->stransid
< si
->stransid
)
184 else if (entry
->stransid
> si
->stransid
)
189 } else if (type
== subvol_search_by_uuid
) {
190 entry
= rb_entry(parent
, struct subvol_info
,
192 comp
= memcmp(entry
->uuid
, si
->uuid
, BTRFS_UUID_SIZE
);
193 } else if (type
== subvol_search_by_root_id
) {
194 entry
= rb_entry(parent
, struct subvol_info
,
196 comp
= entry
->root_id
- si
->root_id
;
197 } else if (type
== subvol_search_by_path
) {
198 entry
= rb_entry(parent
, struct subvol_info
,
200 comp
= strcmp(entry
->path
, si
->path
);
213 if (type
== subvol_search_by_received_uuid
) {
214 rb_link_node(&si
->rb_received_node
, parent
, p
);
215 rb_insert_color(&si
->rb_received_node
, root
);
216 } else if (type
== subvol_search_by_uuid
) {
217 rb_link_node(&si
->rb_local_node
, parent
, p
);
218 rb_insert_color(&si
->rb_local_node
, root
);
219 } else if (type
== subvol_search_by_root_id
) {
220 rb_link_node(&si
->rb_root_id_node
, parent
, p
);
221 rb_insert_color(&si
->rb_root_id_node
, root
);
222 } else if (type
== subvol_search_by_path
) {
223 rb_link_node(&si
->rb_path_node
, parent
, p
);
224 rb_insert_color(&si
->rb_path_node
, root
);
230 int btrfs_subvolid_resolve(int fd
, char *path
, size_t path_len
, u64 subvol_id
)
236 path
[path_len
] = '\0';
237 return btrfs_subvolid_resolve_sub(fd
, path
, &path_len
, subvol_id
);
240 static int btrfs_subvolid_resolve_sub(int fd
, char *path
, size_t *path_len
,
244 struct btrfs_ioctl_search_args search_arg
;
245 struct btrfs_ioctl_ino_lookup_args ino_lookup_arg
;
246 struct btrfs_ioctl_search_header
*search_header
;
247 struct btrfs_root_ref
*backref_item
;
249 if (subvol_id
== BTRFS_FS_TREE_OBJECTID
) {
257 memset(&search_arg
, 0, sizeof(search_arg
));
258 search_arg
.key
.tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
259 search_arg
.key
.min_objectid
= subvol_id
;
260 search_arg
.key
.max_objectid
= subvol_id
;
261 search_arg
.key
.min_type
= BTRFS_ROOT_BACKREF_KEY
;
262 search_arg
.key
.max_type
= BTRFS_ROOT_BACKREF_KEY
;
263 search_arg
.key
.max_offset
= (u64
)-1;
264 search_arg
.key
.max_transid
= (u64
)-1;
265 search_arg
.key
.nr_items
= 1;
266 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &search_arg
);
269 "ioctl(BTRFS_IOC_TREE_SEARCH, subvol_id %llu) ret=%d, error: %m\n",
270 (unsigned long long)subvol_id
, ret
);
274 if (search_arg
.key
.nr_items
< 1) {
276 "failed to lookup subvol_id %llu!\n",
277 (unsigned long long)subvol_id
);
280 search_header
= (struct btrfs_ioctl_search_header
*)search_arg
.buf
;
281 backref_item
= (struct btrfs_root_ref
*)(search_header
+ 1);
282 if (btrfs_search_header_offset(search_header
)
283 != BTRFS_FS_TREE_OBJECTID
) {
286 sub_ret
= btrfs_subvolid_resolve_sub(fd
, path
, path_len
,
287 btrfs_search_header_offset(search_header
));
296 if (btrfs_stack_root_ref_dirid(backref_item
) !=
297 BTRFS_FIRST_FREE_OBJECTID
) {
300 memset(&ino_lookup_arg
, 0, sizeof(ino_lookup_arg
));
301 ino_lookup_arg
.treeid
=
302 btrfs_search_header_offset(search_header
);
303 ino_lookup_arg
.objectid
=
304 btrfs_stack_root_ref_dirid(backref_item
);
305 ret
= ioctl(fd
, BTRFS_IOC_INO_LOOKUP
, &ino_lookup_arg
);
308 "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %m\n",
313 len
= strlen(ino_lookup_arg
.name
);
316 strcat(path
, ino_lookup_arg
.name
);
320 if (*path_len
< btrfs_stack_root_ref_name_len(backref_item
))
322 strncat(path
, (char *)(backref_item
+ 1),
323 btrfs_stack_root_ref_name_len(backref_item
));
324 (*path_len
) -= btrfs_stack_root_ref_name_len(backref_item
);
328 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
329 static int count_bytes(void *buf
, int len
, char b
)
334 for (i
= 0; i
< len
; i
++) {
335 if (((char *)buf
)[i
] == b
)
341 void subvol_uuid_search_add(struct subvol_uuid_search
*s
,
342 struct subvol_info
*si
)
346 tree_insert(&s
->root_id_subvols
, si
, subvol_search_by_root_id
);
347 tree_insert(&s
->path_subvols
, si
, subvol_search_by_path
);
349 cnt
= count_bytes(si
->uuid
, BTRFS_UUID_SIZE
, 0);
350 if (cnt
!= BTRFS_UUID_SIZE
)
351 tree_insert(&s
->local_subvols
, si
, subvol_search_by_uuid
);
352 cnt
= count_bytes(si
->received_uuid
, BTRFS_UUID_SIZE
, 0);
353 if (cnt
!= BTRFS_UUID_SIZE
)
354 tree_insert(&s
->received_subvols
, si
,
355 subvol_search_by_received_uuid
);
358 static struct subvol_info
*tree_search(struct rb_root
*root
,
359 u64 root_id
, const u8
*uuid
,
360 u64 stransid
, const char *path
,
361 enum subvol_search_type type
)
363 struct rb_node
*n
= root
->rb_node
;
364 struct subvol_info
*entry
;
368 if (type
== subvol_search_by_received_uuid
) {
369 entry
= rb_entry(n
, struct subvol_info
,
371 comp
= memcmp(entry
->received_uuid
, uuid
,
374 if (entry
->stransid
< stransid
)
376 else if (entry
->stransid
> stransid
)
381 } else if (type
== subvol_search_by_uuid
) {
382 entry
= rb_entry(n
, struct subvol_info
, rb_local_node
);
383 comp
= memcmp(entry
->uuid
, uuid
, BTRFS_UUID_SIZE
);
384 } else if (type
== subvol_search_by_root_id
) {
385 entry
= rb_entry(n
, struct subvol_info
,
387 comp
= entry
->root_id
- root_id
;
388 } else if (type
== subvol_search_by_path
) {
389 entry
= rb_entry(n
, struct subvol_info
, rb_path_node
);
390 comp
= strcmp(entry
->path
, path
);
405 * this function will be only called if kernel doesn't support uuid tree.
407 static struct subvol_info
*subvol_uuid_search_old(struct subvol_uuid_search
*s
,
408 u64 root_id
, const u8
*uuid
, u64 transid
,
410 enum subvol_search_type type
)
412 struct rb_root
*root
;
413 if (type
== subvol_search_by_received_uuid
)
414 root
= &s
->received_subvols
;
415 else if (type
== subvol_search_by_uuid
)
416 root
= &s
->local_subvols
;
417 else if (type
== subvol_search_by_root_id
)
418 root
= &s
->root_id_subvols
;
419 else if (type
== subvol_search_by_path
)
420 root
= &s
->path_subvols
;
423 return tree_search(root
, root_id
, uuid
, transid
, path
, type
);
426 void subvol_uuid_search_add(struct subvol_uuid_search
*s
,
427 struct subvol_info
*si
)
436 struct subvol_info
*subvol_uuid_search(struct subvol_uuid_search
*s
,
437 u64 root_id
, const u8
*uuid
, u64 transid
,
439 enum subvol_search_type type
)
441 struct subvol_info
*si
;
443 si
= subvol_uuid_search2(s
, root_id
, uuid
, transid
, path
, type
);
449 struct subvol_info
*subvol_uuid_search2(struct subvol_uuid_search
*s
,
450 u64 root_id
, const u8
*uuid
, u64 transid
,
452 enum subvol_search_type type
)
455 struct btrfs_root_item root_item
;
456 struct subvol_info
*info
= NULL
;
458 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
459 if (!s
->uuid_tree_existed
)
460 return subvol_uuid_search_old(s
, root_id
, uuid
, transid
,
464 case subvol_search_by_received_uuid
:
465 ret
= btrfs_lookup_uuid_received_subvol_item(s
->mnt_fd
, uuid
,
468 case subvol_search_by_uuid
:
469 ret
= btrfs_lookup_uuid_subvol_item(s
->mnt_fd
, uuid
, &root_id
);
471 case subvol_search_by_root_id
:
473 case subvol_search_by_path
:
474 ret
= btrfs_get_root_id_by_sub_path(s
->mnt_fd
, path
, &root_id
);
484 ret
= btrfs_read_root_item(s
->mnt_fd
, root_id
, &root_item
);
488 info
= calloc(1, sizeof(*info
));
493 info
->root_id
= root_id
;
494 memcpy(info
->uuid
, root_item
.uuid
, BTRFS_UUID_SIZE
);
495 memcpy(info
->received_uuid
, root_item
.received_uuid
, BTRFS_UUID_SIZE
);
496 memcpy(info
->parent_uuid
, root_item
.parent_uuid
, BTRFS_UUID_SIZE
);
497 info
->ctransid
= btrfs_root_ctransid(&root_item
);
498 info
->otransid
= btrfs_root_otransid(&root_item
);
499 info
->stransid
= btrfs_root_stransid(&root_item
);
500 info
->rtransid
= btrfs_root_rtransid(&root_item
);
501 if (type
== subvol_search_by_path
) {
502 info
->path
= strdup(path
);
508 info
->path
= malloc(PATH_MAX
);
513 ret
= btrfs_subvolid_resolve(s
->mnt_fd
, info
->path
,
529 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
530 static int is_uuid_tree_supported(int fd
)
533 struct btrfs_ioctl_search_args args
;
534 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
536 memset(&args
, 0, sizeof(args
));
538 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
540 sk
->min_objectid
= BTRFS_UUID_TREE_OBJECTID
;
541 sk
->max_objectid
= BTRFS_UUID_TREE_OBJECTID
;
542 sk
->max_type
= BTRFS_ROOT_ITEM_KEY
;
543 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
544 sk
->max_offset
= (u64
)-1;
545 sk
->max_transid
= (u64
)-1;
548 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
552 /* the ioctl returns the number of item it found in nr_items */
553 if (sk
->nr_items
== 0)
560 * this function is mainly used to read all root items
561 * it will be only used when we use older kernel which uuid
562 * tree is not supported yet
564 int subvol_uuid_search_init(int mnt_fd
, struct subvol_uuid_search
*s
)
567 struct btrfs_ioctl_search_args args
;
568 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
569 struct btrfs_ioctl_search_header
*sh
;
570 struct btrfs_root_item
*root_item_ptr
;
571 struct btrfs_root_item root_item
= {};
572 struct subvol_info
*si
= NULL
;
573 int root_item_valid
= 0;
574 unsigned long off
= 0;
580 s
->root_id_subvols
= RB_ROOT
;
581 s
->local_subvols
= RB_ROOT
;
582 s
->received_subvols
= RB_ROOT
;
583 s
->path_subvols
= RB_ROOT
;
585 ret
= is_uuid_tree_supported(mnt_fd
);
588 "ERROR: check if we support uuid tree fails - %m\n");
591 /* uuid tree is supported */
592 s
->uuid_tree_existed
= 1;
595 memset(&args
, 0, sizeof(args
));
597 sk
->tree_id
= BTRFS_ROOT_TREE_OBJECTID
;
599 sk
->max_objectid
= (u64
)-1;
600 sk
->max_offset
= (u64
)-1;
601 sk
->max_transid
= (u64
)-1;
602 sk
->min_type
= BTRFS_ROOT_ITEM_KEY
;
603 sk
->max_type
= BTRFS_ROOT_BACKREF_KEY
;
607 ret
= ioctl(mnt_fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
609 fprintf(stderr
, "ERROR: can't perform the search - %m\n");
612 if (sk
->nr_items
== 0)
617 for (i
= 0; i
< sk
->nr_items
; i
++) {
618 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+
622 if ((btrfs_search_header_objectid(sh
) != 5 &&
623 btrfs_search_header_objectid(sh
)
624 < BTRFS_FIRST_FREE_OBJECTID
) ||
625 btrfs_search_header_objectid(sh
)
626 > BTRFS_LAST_FREE_OBJECTID
) {
630 if (btrfs_search_header_type(sh
)
631 == BTRFS_ROOT_ITEM_KEY
) {
632 /* older kernels don't have uuids+times */
633 if (btrfs_search_header_len(sh
)
634 < sizeof(root_item
)) {
638 root_item_ptr
= (struct btrfs_root_item
*)
640 memcpy(&root_item
, root_item_ptr
,
643 } else if (btrfs_search_header_type(sh
)
644 == BTRFS_ROOT_BACKREF_KEY
||
646 if (!root_item_valid
)
649 path
= btrfs_list_path_for_root(mnt_fd
,
650 btrfs_search_header_objectid(sh
));
655 fprintf(stderr
, "ERROR: unable to "
658 btrfs_search_header_objectid(sh
));
662 si
= calloc(1, sizeof(*si
));
663 si
->root_id
= btrfs_search_header_objectid(sh
);
664 memcpy(si
->uuid
, root_item
.uuid
,
666 memcpy(si
->parent_uuid
, root_item
.parent_uuid
,
668 memcpy(si
->received_uuid
,
669 root_item
.received_uuid
,
671 si
->ctransid
= btrfs_root_ctransid(&root_item
);
672 si
->otransid
= btrfs_root_otransid(&root_item
);
673 si
->stransid
= btrfs_root_stransid(&root_item
);
674 si
->rtransid
= btrfs_root_rtransid(&root_item
);
676 subvol_uuid_search_add(s
, si
);
683 off
+= btrfs_search_header_len(sh
);
686 * record the mins in sk so we can make sure the
687 * next search doesn't repeat this root
689 sk
->min_objectid
= btrfs_search_header_objectid(sh
);
690 sk
->min_offset
= btrfs_search_header_offset(sh
);
691 sk
->min_type
= btrfs_search_header_type(sh
);
694 if (sk
->min_offset
< (u64
)-1)
696 else if (sk
->min_objectid
< (u64
)-1) {
708 void subvol_uuid_search_finit(struct subvol_uuid_search
*s
)
710 struct rb_root
*root
= &s
->root_id_subvols
;
711 struct rb_node
*node
;
713 if (!s
->uuid_tree_existed
)
716 while ((node
= rb_first(root
))) {
717 struct subvol_info
*entry
=
718 rb_entry(node
, struct subvol_info
, rb_root_id_node
);
721 rb_erase(node
, root
);
725 s
->root_id_subvols
= RB_ROOT
;
726 s
->local_subvols
= RB_ROOT
;
727 s
->received_subvols
= RB_ROOT
;
728 s
->path_subvols
= RB_ROOT
;
731 int subvol_uuid_search_init(int mnt_fd
, struct subvol_uuid_search
*s
)
738 void subvol_uuid_search_finit(struct subvol_uuid_search
*s
)
743 int path_cat_out(char *out
, const char *p1
, const char *p2
)
745 int p1_len
= strlen(p1
);
746 int p2_len
= strlen(p2
);
748 if (p1_len
+ p2_len
+ 2 >= PATH_MAX
)
749 return -ENAMETOOLONG
;
751 if (p1_len
&& p1
[p1_len
- 1] == '/')
753 if (p2_len
&& p2
[p2_len
- 1] == '/')
755 sprintf(out
, "%.*s/%.*s", p1_len
, p1
, p2_len
, p2
);
760 int path_cat3_out(char *out
, const char *p1
, const char *p2
, const char *p3
)
762 int p1_len
= strlen(p1
);
763 int p2_len
= strlen(p2
);
764 int p3_len
= strlen(p3
);
766 if (p1_len
+ p2_len
+ p3_len
+ 3 >= PATH_MAX
)
767 return -ENAMETOOLONG
;
769 if (p1_len
&& p1
[p1_len
- 1] == '/')
771 if (p2_len
&& p2
[p2_len
- 1] == '/')
773 if (p3_len
&& p3
[p3_len
- 1] == '/')
775 sprintf(out
, "%.*s/%.*s/%.*s", p1_len
, p1
, p2_len
, p2
, p3_len
, p3
);