Skip to content

Commit 83ccf08

Browse files
Wang Shilongmasoncl
Wang Shilong
authored andcommitted
Btrfs-progs: make send/receive compatible with older kernels
Some users complaint that with latest btrfs-progs, they will fail to use send/receive. The problem is new tool will try to use uuid tree while it dosen't work on older kernel. Now we first check if we support uuid tree, if not we fall into normal search as previous way.i copy most of codes from Alexander Block's previous codes and did some adjustments to make it work. Signed-off-by: Alexander Block <ablock84@googlemail.com> Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Chris Mason <clm@fb.com>
1 parent cf822f5 commit 83ccf08

File tree

2 files changed

+359
-4
lines changed

2 files changed

+359
-4
lines changed

send-utils.c

+348-4
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,71 @@ static int btrfs_read_root_item(int mnt_fd, u64 root_id,
159159
return 0;
160160
}
161161

162+
static struct rb_node *tree_insert(struct rb_root *root,
163+
struct subvol_info *si,
164+
enum subvol_search_type type)
165+
{
166+
struct rb_node **p = &root->rb_node;
167+
struct rb_node *parent = NULL;
168+
struct subvol_info *entry;
169+
__s64 comp;
170+
171+
while (*p) {
172+
parent = *p;
173+
if (type == subvol_search_by_received_uuid) {
174+
entry = rb_entry(parent, struct subvol_info,
175+
rb_received_node);
176+
177+
comp = memcmp(entry->received_uuid, si->received_uuid,
178+
BTRFS_UUID_SIZE);
179+
if (!comp) {
180+
if (entry->stransid < si->stransid)
181+
comp = -1;
182+
else if (entry->stransid > si->stransid)
183+
comp = 1;
184+
else
185+
comp = 0;
186+
}
187+
} else if (type == subvol_search_by_uuid) {
188+
entry = rb_entry(parent, struct subvol_info,
189+
rb_local_node);
190+
comp = memcmp(entry->uuid, si->uuid, BTRFS_UUID_SIZE);
191+
} else if (type == subvol_search_by_root_id) {
192+
entry = rb_entry(parent, struct subvol_info,
193+
rb_root_id_node);
194+
comp = entry->root_id - si->root_id;
195+
} else if (type == subvol_search_by_path) {
196+
entry = rb_entry(parent, struct subvol_info,
197+
rb_path_node);
198+
comp = strcmp(entry->path, si->path);
199+
} else {
200+
BUG();
201+
}
202+
203+
if (comp < 0)
204+
p = &(*p)->rb_left;
205+
else if (comp > 0)
206+
p = &(*p)->rb_right;
207+
else
208+
return parent;
209+
}
210+
211+
if (type == subvol_search_by_received_uuid) {
212+
rb_link_node(&si->rb_received_node, parent, p);
213+
rb_insert_color(&si->rb_received_node, root);
214+
} else if (type == subvol_search_by_uuid) {
215+
rb_link_node(&si->rb_local_node, parent, p);
216+
rb_insert_color(&si->rb_local_node, root);
217+
} else if (type == subvol_search_by_root_id) {
218+
rb_link_node(&si->rb_root_id_node, parent, p);
219+
rb_insert_color(&si->rb_root_id_node, root);
220+
} else if (type == subvol_search_by_path) {
221+
rb_link_node(&si->rb_path_node, parent, p);
222+
rb_insert_color(&si->rb_path_node, root);
223+
}
224+
return NULL;
225+
}
226+
162227
int btrfs_subvolid_resolve(int fd, char *path, size_t path_len, u64 subvol_id)
163228
{
164229
if (path_len < 1)
@@ -255,13 +320,101 @@ static int btrfs_subvolid_resolve_sub(int fd, char *path, size_t *path_len,
255320
return 0;
256321
}
257322

323+
static int count_bytes(void *buf, int len, char b)
324+
{
325+
int cnt = 0;
326+
int i;
327+
328+
for (i = 0; i < len; i++) {
329+
if (((char *)buf)[i] == b)
330+
cnt++;
331+
}
332+
return cnt;
333+
}
334+
258335
void subvol_uuid_search_add(struct subvol_uuid_search *s,
259336
struct subvol_info *si)
260337
{
261-
if (si) {
262-
free(si->path);
263-
free(si);
338+
int cnt;
339+
340+
tree_insert(&s->root_id_subvols, si, subvol_search_by_root_id);
341+
tree_insert(&s->path_subvols, si, subvol_search_by_path);
342+
343+
cnt = count_bytes(si->uuid, BTRFS_UUID_SIZE, 0);
344+
if (cnt != BTRFS_UUID_SIZE)
345+
tree_insert(&s->local_subvols, si, subvol_search_by_uuid);
346+
cnt = count_bytes(si->received_uuid, BTRFS_UUID_SIZE, 0);
347+
if (cnt != BTRFS_UUID_SIZE)
348+
tree_insert(&s->received_subvols, si,
349+
subvol_search_by_received_uuid);
350+
}
351+
352+
static struct subvol_info *tree_search(struct rb_root *root,
353+
u64 root_id, const u8 *uuid,
354+
u64 stransid, const char *path,
355+
enum subvol_search_type type)
356+
{
357+
struct rb_node *n = root->rb_node;
358+
struct subvol_info *entry;
359+
__s64 comp;
360+
361+
while (n) {
362+
if (type == subvol_search_by_received_uuid) {
363+
entry = rb_entry(n, struct subvol_info,
364+
rb_received_node);
365+
comp = memcmp(entry->received_uuid, uuid,
366+
BTRFS_UUID_SIZE);
367+
if (!comp) {
368+
if (entry->stransid < stransid)
369+
comp = -1;
370+
else if (entry->stransid > stransid)
371+
comp = 1;
372+
else
373+
comp = 0;
374+
}
375+
} else if (type == subvol_search_by_uuid) {
376+
entry = rb_entry(n, struct subvol_info, rb_local_node);
377+
comp = memcmp(entry->uuid, uuid, BTRFS_UUID_SIZE);
378+
} else if (type == subvol_search_by_root_id) {
379+
entry = rb_entry(n, struct subvol_info,
380+
rb_root_id_node);
381+
comp = entry->root_id - root_id;
382+
} else if (type == subvol_search_by_path) {
383+
entry = rb_entry(n, struct subvol_info, rb_path_node);
384+
comp = strcmp(entry->path, path);
385+
} else {
386+
BUG();
387+
}
388+
if (comp < 0)
389+
n = n->rb_left;
390+
else if (comp > 0)
391+
n = n->rb_right;
392+
else
393+
return entry;
264394
}
395+
return NULL;
396+
}
397+
398+
/*
399+
* this function will be only called if kernel dosen't support uuid tree.
400+
*/
401+
static struct subvol_info *subvol_uuid_search_old(struct subvol_uuid_search *s,
402+
u64 root_id, const u8 *uuid, u64 transid,
403+
const char *path,
404+
enum subvol_search_type type)
405+
{
406+
struct rb_root *root;
407+
if (type == subvol_search_by_received_uuid)
408+
root = &s->received_subvols;
409+
else if (type == subvol_search_by_uuid)
410+
root = &s->local_subvols;
411+
else if (type == subvol_search_by_root_id)
412+
root = &s->root_id_subvols;
413+
else if (type == subvol_search_by_path)
414+
root = &s->path_subvols;
415+
else
416+
return NULL;
417+
return tree_search(root, root_id, uuid, transid, path, type);
265418
}
266419

267420
struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
@@ -273,6 +426,9 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
273426
struct btrfs_root_item root_item;
274427
struct subvol_info *info = NULL;
275428

429+
if (!s->uuid_tree_existed)
430+
return subvol_uuid_search_old(s, root_id, uuid, transid,
431+
path, type);
276432
switch (type) {
277433
case subvol_search_by_received_uuid:
278434
ret = btrfs_lookup_uuid_received_subvol_item(s->mnt_fd, uuid,
@@ -325,15 +481,203 @@ struct subvol_info *subvol_uuid_search(struct subvol_uuid_search *s,
325481
return info;
326482
}
327483

484+
static int is_uuid_tree_supported(int fd)
485+
{
486+
int ret;
487+
struct btrfs_ioctl_search_args args;
488+
struct btrfs_ioctl_search_key *sk = &args.key;
489+
490+
memset(&args, 0, sizeof(args));
491+
492+
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
493+
494+
sk->min_objectid = BTRFS_UUID_TREE_OBJECTID;
495+
sk->max_objectid = BTRFS_UUID_TREE_OBJECTID;
496+
sk->max_type = BTRFS_ROOT_ITEM_KEY;
497+
sk->min_type = BTRFS_ROOT_ITEM_KEY;
498+
sk->max_offset = (u64)-1;
499+
sk->max_transid = (u64)-1;
500+
sk->nr_items = 1;
501+
502+
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
503+
if (ret < 0)
504+
return ret;
505+
506+
/* the ioctl returns the number of item it found in nr_items */
507+
if (sk->nr_items == 0)
508+
return 0;
509+
510+
return 1;
511+
}
512+
513+
/*
514+
* this function is mainly used to read all root items
515+
* it will be only used when we use older kernel which uuid
516+
* tree is not supported yet
517+
*/
328518
int subvol_uuid_search_init(int mnt_fd, struct subvol_uuid_search *s)
329519
{
520+
int ret;
521+
struct btrfs_ioctl_search_args args;
522+
struct btrfs_ioctl_search_key *sk = &args.key;
523+
struct btrfs_ioctl_search_header *sh;
524+
struct btrfs_root_item *root_item_ptr;
525+
struct btrfs_root_item root_item;
526+
struct subvol_info *si = NULL;
527+
int root_item_valid = 0;
528+
unsigned long off = 0;
529+
int i;
530+
int e;
531+
char *path;
532+
330533
s->mnt_fd = mnt_fd;
331534

332-
return 0;
535+
s->root_id_subvols = RB_ROOT;
536+
s->local_subvols = RB_ROOT;
537+
s->received_subvols = RB_ROOT;
538+
s->path_subvols = RB_ROOT;
539+
540+
ret = is_uuid_tree_supported(mnt_fd);
541+
if (ret < 0) {
542+
fprintf(stderr,
543+
"ERROR: check if we support uuid tree fails- %s\n",
544+
strerror(errno));
545+
return ret;
546+
} else if (ret) {
547+
/* uuid tree is supported */
548+
s->uuid_tree_existed = 1;
549+
return 0;
550+
}
551+
memset(&args, 0, sizeof(args));
552+
553+
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
554+
555+
sk->max_objectid = (u64)-1;
556+
sk->max_offset = (u64)-1;
557+
sk->max_transid = (u64)-1;
558+
sk->min_type = BTRFS_ROOT_ITEM_KEY;
559+
sk->max_type = BTRFS_ROOT_BACKREF_KEY;
560+
sk->nr_items = 4096;
561+
562+
while (1) {
563+
ret = ioctl(mnt_fd, BTRFS_IOC_TREE_SEARCH, &args);
564+
e = errno;
565+
if (ret < 0) {
566+
fprintf(stderr, "ERROR: can't perform the search- %s\n",
567+
strerror(e));
568+
return ret;
569+
}
570+
if (sk->nr_items == 0)
571+
break;
572+
573+
off = 0;
574+
575+
for (i = 0; i < sk->nr_items; i++) {
576+
sh = (struct btrfs_ioctl_search_header *)(args.buf +
577+
off);
578+
off += sizeof(*sh);
579+
580+
if ((sh->objectid != 5 &&
581+
sh->objectid < BTRFS_FIRST_FREE_OBJECTID) ||
582+
sh->objectid > BTRFS_LAST_FREE_OBJECTID)
583+
goto skip;
584+
585+
if (sh->type == BTRFS_ROOT_ITEM_KEY) {
586+
/* older kernels don't have uuids+times */
587+
if (sh->len < sizeof(root_item)) {
588+
root_item_valid = 0;
589+
goto skip;
590+
}
591+
root_item_ptr = (struct btrfs_root_item *)
592+
(args.buf + off);
593+
memcpy(&root_item, root_item_ptr,
594+
sizeof(root_item));
595+
root_item_valid = 1;
596+
} else if (sh->type == BTRFS_ROOT_BACKREF_KEY ||
597+
root_item_valid) {
598+
if (!root_item_valid)
599+
goto skip;
600+
601+
path = btrfs_list_path_for_root(mnt_fd,
602+
sh->objectid);
603+
if (!path)
604+
path = strdup("");
605+
if (IS_ERR(path)) {
606+
ret = PTR_ERR(path);
607+
fprintf(stderr, "ERROR: unable to "
608+
"resolve path "
609+
"for root %llu\n",
610+
sh->objectid);
611+
goto out;
612+
}
613+
614+
si = calloc(1, sizeof(*si));
615+
si->root_id = sh->objectid;
616+
memcpy(si->uuid, root_item.uuid,
617+
BTRFS_UUID_SIZE);
618+
memcpy(si->parent_uuid, root_item.parent_uuid,
619+
BTRFS_UUID_SIZE);
620+
memcpy(si->received_uuid,
621+
root_item.received_uuid,
622+
BTRFS_UUID_SIZE);
623+
si->ctransid = btrfs_root_ctransid(&root_item);
624+
si->otransid = btrfs_root_otransid(&root_item);
625+
si->stransid = btrfs_root_stransid(&root_item);
626+
si->rtransid = btrfs_root_rtransid(&root_item);
627+
si->path = path;
628+
subvol_uuid_search_add(s, si);
629+
root_item_valid = 0;
630+
} else {
631+
goto skip;
632+
}
633+
634+
skip:
635+
off += sh->len;
636+
637+
/*
638+
* record the mins in sk so we can make sure the
639+
* next search doesn't repeat this root
640+
*/
641+
sk->min_objectid = sh->objectid;
642+
sk->min_offset = sh->offset;
643+
sk->min_type = sh->type;
644+
}
645+
sk->nr_items = 4096;
646+
if (sk->min_offset < (u64)-1)
647+
sk->min_offset++;
648+
else if (sk->min_objectid < (u64)-1) {
649+
sk->min_objectid++;
650+
sk->min_offset = 0;
651+
sk->min_type = 0;
652+
} else
653+
break;
654+
}
655+
656+
out:
657+
return ret;
333658
}
334659

335660
void subvol_uuid_search_finit(struct subvol_uuid_search *s)
336661
{
662+
struct rb_root *root = &s->root_id_subvols;
663+
struct rb_node *node;
664+
665+
if (!s->uuid_tree_existed)
666+
return;
667+
668+
while ((node = rb_first(root))) {
669+
struct subvol_info *entry =
670+
rb_entry(node, struct subvol_info, rb_root_id_node);
671+
672+
free(entry->path);
673+
rb_erase(node, root);
674+
free(entry);
675+
}
676+
677+
s->root_id_subvols = RB_ROOT;
678+
s->local_subvols = RB_ROOT;
679+
s->received_subvols = RB_ROOT;
680+
s->path_subvols = RB_ROOT;
337681
}
338682

339683
char *path_cat(const char *p1, const char *p2)

0 commit comments

Comments
 (0)