mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 14:00:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			292 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * BTRFS filesystem implementation for U-Boot
 | 
						|
 *
 | 
						|
 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
 | 
						|
 */
 | 
						|
 | 
						|
#include "btrfs.h"
 | 
						|
#include <log.h>
 | 
						|
#include <malloc.h>
 | 
						|
#include <memalign.h>
 | 
						|
 | 
						|
int btrfs_comp_keys(struct btrfs_key *a, struct btrfs_key *b)
 | 
						|
{
 | 
						|
	if (a->objectid > b->objectid)
 | 
						|
		return 1;
 | 
						|
	if (a->objectid < b->objectid)
 | 
						|
		return -1;
 | 
						|
	if (a->type > b->type)
 | 
						|
		return 1;
 | 
						|
	if (a->type < b->type)
 | 
						|
		return -1;
 | 
						|
	if (a->offset > b->offset)
 | 
						|
		return 1;
 | 
						|
	if (a->offset < b->offset)
 | 
						|
		return -1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int btrfs_comp_keys_type(struct btrfs_key *a, struct btrfs_key *b)
 | 
						|
{
 | 
						|
	if (a->objectid > b->objectid)
 | 
						|
		return 1;
 | 
						|
	if (a->objectid < b->objectid)
 | 
						|
		return -1;
 | 
						|
	if (a->type > b->type)
 | 
						|
		return 1;
 | 
						|
	if (a->type < b->type)
 | 
						|
		return -1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int generic_bin_search(void *addr, int item_size, struct btrfs_key *key,
 | 
						|
			      int max, int *slot)
 | 
						|
{
 | 
						|
	int low = 0, high = max, mid, ret;
 | 
						|
	struct btrfs_key *tmp;
 | 
						|
 | 
						|
	while (low < high) {
 | 
						|
		mid = (low + high) / 2;
 | 
						|
 | 
						|
		tmp = (struct btrfs_key *) ((u8 *) addr + mid*item_size);
 | 
						|
		ret = btrfs_comp_keys(tmp, key);
 | 
						|
 | 
						|
		if (ret < 0) {
 | 
						|
			low = mid + 1;
 | 
						|
		} else if (ret > 0) {
 | 
						|
			high = mid;
 | 
						|
		} else {
 | 
						|
			*slot = mid;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	*slot = low;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
int btrfs_bin_search(union btrfs_tree_node *p, struct btrfs_key *key,
 | 
						|
		     int *slot)
 | 
						|
{
 | 
						|
	void *addr;
 | 
						|
	unsigned long size;
 | 
						|
 | 
						|
	if (p->header.level) {
 | 
						|
		addr = p->node.ptrs;
 | 
						|
		size = sizeof(struct btrfs_key_ptr);
 | 
						|
	} else {
 | 
						|
		addr = p->leaf.items;
 | 
						|
		size = sizeof(struct btrfs_item);
 | 
						|
	}
 | 
						|
 | 
						|
	return generic_bin_search(addr, size, key, p->header.nritems, slot);
 | 
						|
}
 | 
						|
 | 
						|
static void clear_path(struct btrfs_path *p)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
 | 
						|
		p->nodes[i] = NULL;
 | 
						|
		p->slots[i] = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void btrfs_free_path(struct btrfs_path *p)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
 | 
						|
		if (p->nodes[i])
 | 
						|
			free(p->nodes[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	clear_path(p);
 | 
						|
}
 | 
						|
 | 
						|
static int read_tree_node(u64 physical, union btrfs_tree_node **buf)
 | 
						|
{
 | 
						|
	ALLOC_CACHE_ALIGN_BUFFER(struct btrfs_header, hdr,
 | 
						|
				 sizeof(struct btrfs_header));
 | 
						|
	unsigned long size, offset = sizeof(*hdr);
 | 
						|
	union btrfs_tree_node *res;
 | 
						|
	u32 i;
 | 
						|
 | 
						|
	if (!btrfs_devread(physical, sizeof(*hdr), hdr))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	btrfs_header_to_cpu(hdr);
 | 
						|
 | 
						|
	if (hdr->level)
 | 
						|
		size = sizeof(struct btrfs_node)
 | 
						|
		       + hdr->nritems * sizeof(struct btrfs_key_ptr);
 | 
						|
	else
 | 
						|
		size = btrfs_info.sb.nodesize;
 | 
						|
 | 
						|
	res = malloc_cache_aligned(size);
 | 
						|
	if (!res) {
 | 
						|
		debug("%s: malloc failed\n", __func__);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!btrfs_devread(physical + offset, size - offset,
 | 
						|
			   ((u8 *) res) + offset)) {
 | 
						|
		free(res);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	memcpy(&res->header, hdr, sizeof(*hdr));
 | 
						|
	if (hdr->level)
 | 
						|
		for (i = 0; i < hdr->nritems; ++i)
 | 
						|
			btrfs_key_ptr_to_cpu(&res->node.ptrs[i]);
 | 
						|
	else
 | 
						|
		for (i = 0; i < hdr->nritems; ++i)
 | 
						|
			btrfs_item_to_cpu(&res->leaf.items[i]);
 | 
						|
 | 
						|
	*buf = res;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int btrfs_search_tree(const struct btrfs_root *root, struct btrfs_key *key,
 | 
						|
		      struct btrfs_path *p)
 | 
						|
{
 | 
						|
	u8 lvl, prev_lvl;
 | 
						|
	int i, slot, ret;
 | 
						|
	u64 logical, physical;
 | 
						|
	union btrfs_tree_node *buf;
 | 
						|
 | 
						|
	clear_path(p);
 | 
						|
 | 
						|
	logical = root->bytenr;
 | 
						|
 | 
						|
	for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
 | 
						|
		physical = btrfs_map_logical_to_physical(logical);
 | 
						|
		if (physical == -1ULL)
 | 
						|
			goto err;
 | 
						|
 | 
						|
		if (read_tree_node(physical, &buf))
 | 
						|
			goto err;
 | 
						|
 | 
						|
		lvl = buf->header.level;
 | 
						|
		if (i && prev_lvl != lvl + 1) {
 | 
						|
			printf("%s: invalid level in header at %llu\n",
 | 
						|
			       __func__, logical);
 | 
						|
			goto err;
 | 
						|
		}
 | 
						|
		prev_lvl = lvl;
 | 
						|
 | 
						|
		ret = btrfs_bin_search(buf, key, &slot);
 | 
						|
		if (ret < 0)
 | 
						|
			goto err;
 | 
						|
		if (ret && slot > 0 && lvl)
 | 
						|
			slot -= 1;
 | 
						|
 | 
						|
		p->slots[lvl] = slot;
 | 
						|
		p->nodes[lvl] = buf;
 | 
						|
 | 
						|
		if (lvl) {
 | 
						|
			logical = buf->node.ptrs[slot].blockptr;
 | 
						|
		} else {
 | 
						|
			/*
 | 
						|
			 * The path might be invalid if:
 | 
						|
			 *   cur leaf max < searched value < next leaf min
 | 
						|
			 *
 | 
						|
			 * Jump to the next valid element if it exists.
 | 
						|
			 */
 | 
						|
			if (slot >= buf->header.nritems)
 | 
						|
				if (btrfs_next_slot(p) < 0)
 | 
						|
					goto err;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
err:
 | 
						|
	btrfs_free_path(p);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int jump_leaf(struct btrfs_path *path, int dir)
 | 
						|
{
 | 
						|
	struct btrfs_path p;
 | 
						|
	u32 slot;
 | 
						|
	int level = 1, from_level, i;
 | 
						|
 | 
						|
	dir = dir >= 0 ? 1 : -1;
 | 
						|
 | 
						|
	p = *path;
 | 
						|
 | 
						|
	while (level < BTRFS_MAX_LEVEL) {
 | 
						|
		if (!p.nodes[level])
 | 
						|
			return 1;
 | 
						|
 | 
						|
		slot = p.slots[level];
 | 
						|
		if ((dir > 0 && slot + dir >= p.nodes[level]->header.nritems)
 | 
						|
		    || (dir < 0 && !slot))
 | 
						|
			level++;
 | 
						|
		else
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (level == BTRFS_MAX_LEVEL)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	p.slots[level] = slot + dir;
 | 
						|
	level--;
 | 
						|
	from_level = level;
 | 
						|
 | 
						|
	while (level >= 0) {
 | 
						|
		u64 logical, physical;
 | 
						|
 | 
						|
		slot = p.slots[level + 1];
 | 
						|
		logical = p.nodes[level + 1]->node.ptrs[slot].blockptr;
 | 
						|
		physical = btrfs_map_logical_to_physical(logical);
 | 
						|
		if (physical == -1ULL)
 | 
						|
			goto err;
 | 
						|
 | 
						|
		if (read_tree_node(physical, &p.nodes[level]))
 | 
						|
			goto err;
 | 
						|
 | 
						|
		if (dir > 0)
 | 
						|
			p.slots[level] = 0;
 | 
						|
		else
 | 
						|
			p.slots[level] = p.nodes[level]->header.nritems - 1;
 | 
						|
		level--;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Free rewritten nodes in path */
 | 
						|
	for (i = 0; i <= from_level; ++i)
 | 
						|
		free(path->nodes[i]);
 | 
						|
 | 
						|
	*path = p;
 | 
						|
	return 0;
 | 
						|
 | 
						|
err:
 | 
						|
	/* Free rewritten nodes in p */
 | 
						|
	for (i = level + 1; i <= from_level; ++i)
 | 
						|
		free(p.nodes[i]);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
int btrfs_prev_slot(struct btrfs_path *p)
 | 
						|
{
 | 
						|
	if (!p->slots[0])
 | 
						|
		return jump_leaf(p, -1);
 | 
						|
 | 
						|
	p->slots[0]--;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int btrfs_next_slot(struct btrfs_path *p)
 | 
						|
{
 | 
						|
	struct btrfs_leaf *leaf = &p->nodes[0]->leaf;
 | 
						|
 | 
						|
	if (p->slots[0] + 1 >= leaf->header.nritems)
 | 
						|
		return jump_leaf(p, 1);
 | 
						|
 | 
						|
	p->slots[0]++;
 | 
						|
	return 0;
 | 
						|
}
 |