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>
26 #include "send-utils.h"
28 #include "btrfs-list.h"
30 static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
33 static int btrfs_get_root_id_by_sub_path(int mnt_fd, const char *sub_path,
39 subvol_fd = openat(mnt_fd, sub_path, O_RDONLY);
42 fprintf(stderr, "ERROR: open %s failed. %s\n", sub_path,
47 ret = btrfs_list_get_path_rootid(subvol_fd, root_id);
52 static int btrfs_read_root_item_raw(int mnt_fd, u64 root_id, size_t buf_len,
53 u32 *read_len, void *buf)
56 struct btrfs_ioctl_search_args args;
57 struct btrfs_ioctl_search_key *sk = &args.key;
58 struct btrfs_ioctl_search_header *sh;
59 unsigned long off = 0;
64 memset(&args, 0, sizeof(args));
66 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
69 * there may be more than one ROOT_ITEM key if there are
70 * snapshots pending deletion, we have to loop through
73 sk->min_objectid = root_id;
74 sk->max_objectid = root_id;
75 sk->max_type = BTRFS_ROOT_ITEM_KEY;
76 sk->min_type = BTRFS_ROOT_ITEM_KEY;
77 sk->max_offset = (u64)-1;
78 sk->max_transid = (u64)-1;
82 ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
85 "ERROR: can't perform the search - %s\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);
103 sk->min_objectid = sh->objectid;
104 sk->min_type = sh->type;
105 sk->min_offset = sh->offset;
107 if (sh->objectid > root_id)
110 if (sh->objectid == root_id &&
111 sh->type == BTRFS_ROOT_ITEM_KEY) {
112 if (sh->len > 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, sh->len);
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: %s\n",
270 (unsigned long long)subvol_id, ret, strerror(errno));
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 (search_header->offset != BTRFS_FS_TREE_OBJECTID) {
285 sub_ret = btrfs_subvolid_resolve_sub(fd, path, path_len,
286 search_header->offset);
295 if (btrfs_stack_root_ref_dirid(backref_item) !=
296 BTRFS_FIRST_FREE_OBJECTID) {
299 memset(&ino_lookup_arg, 0, sizeof(ino_lookup_arg));
300 ino_lookup_arg.treeid = search_header->offset;
301 ino_lookup_arg.objectid =
302 btrfs_stack_root_ref_dirid(backref_item);
303 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_lookup_arg);
306 "ioctl(BTRFS_IOC_INO_LOOKUP) ret=%d, error: %s\n",
307 ret, strerror(errno));
311 len = strlen(ino_lookup_arg.name);
314 strcat(path, ino_lookup_arg.name);
318 if (*path_len < btrfs_stack_root_ref_name_len(backref_item))
320 strncat(path, (char *)(backref_item + 1),
321 btrfs_stack_root_ref_name_len(backref_item));
322 (*path_len) -= btrfs_stack_root_ref_name_len(backref_item);
326 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
327 static int count_bytes(void *buf, int len, char b)
332 for (i = 0; i < len; i++) {
333 if (((char *)buf)[i] == b)
339 void subvol_uuid_search_add(struct subvol_uuid_search *s,
340 struct subvol_info *si)
344 tree_insert(&s->root_id_subvols, si, subvol_search_by_root_id);
345 tree_insert(&s->path_subvols, si, subvol_search_by_path);
347 cnt = count_bytes(si->uuid, BTRFS_UUID_SIZE, 0);
348 if (cnt != BTRFS_UUID_SIZE)
349 tree_insert(&s->local_subvols, si, subvol_search_by_uuid);
350 cnt = count_bytes(si->received_uuid, BTRFS_UUID_SIZE, 0);
351 if (cnt != BTRFS_UUID_SIZE)
352 tree_insert(&s->received_subvols, si,
353 subvol_search_by_received_uuid);
356 static struct subvol_info *tree_search(struct rb_root *root,
357 u64 root_id, const u8 *uuid,
358 u64 stransid, const char *path,
359 enum subvol_search_type type)
361 struct rb_node *n = root->rb_node;
362 struct subvol_info *entry;
366 if (type == subvol_search_by_received_uuid) {
367 entry = rb_entry(n, struct subvol_info,
369 comp = memcmp(entry->received_uuid, uuid,
372 if (entry->stransid < stransid)
374 else if (entry->stransid > stransid)
379 } else if (type == subvol_search_by_uuid) {
380 entry = rb_entry(n, struct subvol_info, rb_local_node);
381 comp = memcmp(entry->uuid, uuid, BTRFS_UUID_SIZE);
382 } else if (type == subvol_search_by_root_id) {
383 entry = rb_entry(n, struct subvol_info,
385 comp = entry->root_id - root_id;
386 } else if (type == subvol_search_by_path) {
387 entry = rb_entry(n, struct subvol_info, rb_path_node);
388 comp = strcmp(entry->path, path);
403 * this function will be only called if kernel dosen't support uuid tree.
405 static struct subvol_info *subvol_uuid_search_old(struct subvol_uuid_search *s,
406 u64 root_id, const u8 *uuid, u64 transid,
408 enum subvol_search_type type)
410 struct rb_root *root;
411 if (type == subvol_search_by_received_uuid)
412 root = &s->received_subvols;
413 else if (type == subvol_search_by_uuid)
414 root = &s->local_subvols;
415 else if (type == subvol_search_by_root_id)
416 root = &s->root_id_subvols;
417 else if (type == subvol_search_by_path)
418 root = &s->path_subvols;
421 return tree_search(root, root_id, uuid, transid, path, type);
424 void subvol_uuid_search_add(struct subvol_uuid_search *s,
425 struct subvol_info *si)
434 struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
435 u64 root_id, const u8 *uuid, u64 transid,
437 enum subvol_search_type type)
440 struct btrfs_root_item root_item;
441 struct subvol_info *info = NULL;
443 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
444 if (!s->uuid_tree_existed)
445 return subvol_uuid_search_old(s, root_id, uuid, transid,
449 case subvol_search_by_received_uuid:
450 ret = btrfs_lookup_uuid_received_subvol_item(s->mnt_fd, uuid,
453 case subvol_search_by_uuid:
454 ret = btrfs_lookup_uuid_subvol_item(s->mnt_fd, uuid, &root_id);
456 case subvol_search_by_root_id:
458 case subvol_search_by_path:
459 ret = btrfs_get_root_id_by_sub_path(s->mnt_fd, path, &root_id);
469 ret = btrfs_read_root_item(s->mnt_fd, root_id, &root_item);
473 info = calloc(1, sizeof(*info));
474 info->root_id = root_id;
475 memcpy(info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
476 memcpy(info->received_uuid, root_item.received_uuid, BTRFS_UUID_SIZE);
477 memcpy(info->parent_uuid, root_item.parent_uuid, BTRFS_UUID_SIZE);
478 info->ctransid = btrfs_root_ctransid(&root_item);
479 info->otransid = btrfs_root_otransid(&root_item);
480 info->stransid = btrfs_root_stransid(&root_item);
481 info->rtransid = btrfs_root_rtransid(&root_item);
482 if (type == subvol_search_by_path) {
483 info->path = strdup(path);
485 info->path = malloc(PATH_MAX);
486 ret = btrfs_subvolid_resolve(s->mnt_fd, info->path,
500 #ifdef BTRFS_COMPAT_SEND_NO_UUID_TREE
501 static int is_uuid_tree_supported(int fd)
504 struct btrfs_ioctl_search_args args;
505 struct btrfs_ioctl_search_key *sk = &args.key;
507 memset(&args, 0, sizeof(args));
509 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
511 sk->min_objectid = BTRFS_UUID_TREE_OBJECTID;
512 sk->max_objectid = BTRFS_UUID_TREE_OBJECTID;
513 sk->max_type = BTRFS_ROOT_ITEM_KEY;
514 sk->min_type = BTRFS_ROOT_ITEM_KEY;
515 sk->max_offset = (u64)-1;
516 sk->max_transid = (u64)-1;
519 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
523 /* the ioctl returns the number of item it found in nr_items */
524 if (sk->nr_items == 0)
531 * this function is mainly used to read all root items
532 * it will be only used when we use older kernel which uuid
533 * tree is not supported yet
535 int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
538 struct btrfs_ioctl_search_args args;
539 struct btrfs_ioctl_search_key *sk = &args.key;
540 struct btrfs_ioctl_search_header *sh;
541 struct btrfs_root_item *root_item_ptr;
542 struct btrfs_root_item root_item = {};
543 struct subvol_info *si = NULL;
544 int root_item_valid = 0;
545 unsigned long off = 0;
552 s->root_id_subvols = RB_ROOT;
553 s->local_subvols = RB_ROOT;
554 s->received_subvols = RB_ROOT;
555 s->path_subvols = RB_ROOT;
557 ret = is_uuid_tree_supported(mnt_fd);
560 "ERROR: check if we support uuid tree fails - %s\n",
564 /* uuid tree is supported */
565 s->uuid_tree_existed = 1;
568 memset(&args, 0, sizeof(args));
570 sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
572 sk->max_objectid = (u64)-1;
573 sk->max_offset = (u64)-1;
574 sk->max_transid = (u64)-1;
575 sk->min_type = BTRFS_ROOT_ITEM_KEY;
576 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
580 ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
583 fprintf(stderr, "ERROR: can't perform the search - %s\n",
587 if (sk->nr_items == 0)
592 for (i = 0; i < sk->nr_items; i++) {
593 sh = (struct btrfs_ioctl_search_header *)(args.buf +
597 if ((sh->objectid != 5 &&
598 sh->objectid < BTRFS_FIRST_FREE_OBJECTID) ||
599 sh->objectid > BTRFS_LAST_FREE_OBJECTID)
602 if (sh->type == BTRFS_ROOT_ITEM_KEY) {
603 /* older kernels don't have uuids+times */
604 if (sh->len < sizeof(root_item)) {
608 root_item_ptr = (struct btrfs_root_item *)
610 memcpy(&root_item, root_item_ptr,
613 } else if (sh->type == BTRFS_ROOT_BACKREF_KEY ||
615 if (!root_item_valid)
618 path = btrfs_list_path_for_root(mnt_fd,
624 fprintf(stderr, "ERROR: unable to "
631 si = calloc(1, sizeof(*si));
632 si->root_id = sh->objectid;
633 memcpy(si->uuid, root_item.uuid,
635 memcpy(si->parent_uuid, root_item.parent_uuid,
637 memcpy(si->received_uuid,
638 root_item.received_uuid,
640 si->ctransid = btrfs_root_ctransid(&root_item);
641 si->otransid = btrfs_root_otransid(&root_item);
642 si->stransid = btrfs_root_stransid(&root_item);
643 si->rtransid = btrfs_root_rtransid(&root_item);
645 subvol_uuid_search_add(s, si);
655 * record the mins in sk so we can make sure the
656 * next search doesn't repeat this root
658 sk->min_objectid = sh->objectid;
659 sk->min_offset = sh->offset;
660 sk->min_type = sh->type;
663 if (sk->min_offset < (u64)-1)
665 else if (sk->min_objectid < (u64)-1) {
677 void subvol_uuid_search_finit(struct subvol_uuid_search *s)
679 struct rb_root *root = &s->root_id_subvols;
680 struct rb_node *node;
682 if (!s->uuid_tree_existed)
685 while ((node = rb_first(root))) {
686 struct subvol_info *entry =
687 rb_entry(node, struct subvol_info, rb_root_id_node);
690 rb_erase(node, root);
694 s->root_id_subvols = RB_ROOT;
695 s->local_subvols = RB_ROOT;
696 s->received_subvols = RB_ROOT;
697 s->path_subvols = RB_ROOT;
700 int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
707 void subvol_uuid_search_finit(struct subvol_uuid_search *s)
712 char *path_cat(const char *p1, const char *p2)
714 int p1_len = strlen(p1);
715 int p2_len = strlen(p2);
716 char *new = malloc(p1_len + p2_len + 2);
718 if (p1_len && p1[p1_len - 1] == '/')
720 if (p2_len && p2[p2_len - 1] == '/')
722 sprintf(new, "%.*s/%.*s", p1_len, p1, p2_len, p2);
726 char *path_cat3(const char *p1, const char *p2, const char *p3)
728 int p1_len = strlen(p1);
729 int p2_len = strlen(p2);
730 int p3_len = strlen(p3);
731 char *new = malloc(p1_len + p2_len + p3_len + 3);
733 if (p1_len && p1[p1_len - 1] == '/')
735 if (p2_len && p2[p2_len - 1] == '/')
737 if (p3_len && p3[p3_len - 1] == '/')
739 sprintf(new, "%.*s/%.*s/%.*s", p1_len, p1, p2_len, p2, p3_len, p3);