Skip to content

Commit

Permalink
fs: btrfs: Add single-device read-only BTRFS implementation
Browse files Browse the repository at this point in the history
This adds the proper implementation for the BTRFS filesystem.
The implementation currently supports only read-only mode and
the filesystem can be only on a single device.

Checksums of data chunks is unimplemented.

Compression is implemented (ZLIB + LZO).

Signed-off-by: Marek Behun <[email protected]>

 create mode 100644 fs/btrfs/btrfs.h
 create mode 100644 fs/btrfs/chunk-map.c
 create mode 100644 fs/btrfs/compression.c
 create mode 100644 fs/btrfs/ctree.c
 create mode 100644 fs/btrfs/dev.c
 create mode 100644 fs/btrfs/dir-item.c
 create mode 100644 fs/btrfs/extent-io.c
 create mode 100644 fs/btrfs/hash.c
 create mode 100644 fs/btrfs/inode.c
 create mode 100644 fs/btrfs/root.c
 create mode 100644 fs/btrfs/subvolume.c
 create mode 100644 fs/btrfs/super.c
  • Loading branch information
elkablo authored and trini committed Oct 3, 2017
1 parent 597b4af commit 21a14fa
Show file tree
Hide file tree
Showing 12 changed files with 1,841 additions and 0 deletions.
89 changes: 89 additions & 0 deletions fs/btrfs/btrfs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* BTRFS filesystem implementation for U-Boot
*
* 2017 Marek Behun, CZ.NIC, [email protected]
*
* SPDX-License-Identifier: GPL-2.0+
*/

#ifndef __BTRFS_BTRFS_H__
#define __BTRFS_BTRFS_H__

#include <linux/rbtree.h>
#include "conv-funcs.h"

struct btrfs_info {
struct btrfs_super_block sb;
struct btrfs_root_backup *root_backup;

struct btrfs_root tree_root;
struct btrfs_root fs_root;
struct btrfs_root chunk_root;

struct rb_root chunks_root;
};

extern struct btrfs_info btrfs_info;

/* hash.c */
void btrfs_hash_init(void);
u32 btrfs_crc32c(u32, const void *, size_t);
u32 btrfs_csum_data(char *, u32, size_t);
void btrfs_csum_final(u32, void *);

static inline u64 btrfs_name_hash(const char *name, int len)
{
return btrfs_crc32c((u32) ~1, name, len);
}

/* dev.c */
extern struct blk_desc *btrfs_blk_desc;
extern disk_partition_t *btrfs_part_info;

int btrfs_devread(u64, int, void *);

/* chunk-map.c */
u64 btrfs_map_logical_to_physical(u64);
int btrfs_chunk_map_init(void);
void btrfs_chunk_map_exit(void);
int btrfs_read_chunk_tree(void);

/* compression.c */
u32 btrfs_decompress(u8 type, const char *, u32, char *, u32);

/* super.c */
int btrfs_read_superblock(void);

/* dir-item.c */
typedef int (*btrfs_readdir_callback_t)(const struct btrfs_root *,
struct btrfs_dir_item *);

int btrfs_lookup_dir_item(const struct btrfs_root *, u64, const char *, int,
struct btrfs_dir_item *);
int btrfs_readdir(const struct btrfs_root *, u64, btrfs_readdir_callback_t);

/* root.c */
int btrfs_find_root(u64, struct btrfs_root *, struct btrfs_root_item *);
u64 btrfs_lookup_root_ref(u64, struct btrfs_root_ref *, char *);

/* inode.c */
u64 btrfs_lookup_inode_ref(struct btrfs_root *, u64, struct btrfs_inode_ref *,
char *);
int btrfs_lookup_inode(const struct btrfs_root *, struct btrfs_key *,
struct btrfs_inode_item *, struct btrfs_root *);
int btrfs_readlink(const struct btrfs_root *, u64, char *);
u64 btrfs_lookup_path(struct btrfs_root *, u64, const char *, u8 *,
struct btrfs_inode_item *, int);
u64 btrfs_file_read(const struct btrfs_root *, u64, u64, u64, char *);

/* subvolume.c */
u64 btrfs_get_default_subvol_objectid(void);

/* extent-io.c */
u64 btrfs_read_extent_inline(struct btrfs_path *,
struct btrfs_file_extent_item *, u64, u64,
char *);
u64 btrfs_read_extent_reg(struct btrfs_path *, struct btrfs_file_extent_item *,
u64, u64, char *);

#endif /* !__BTRFS_BTRFS_H__ */
178 changes: 178 additions & 0 deletions fs/btrfs/chunk-map.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* BTRFS filesystem implementation for U-Boot
*
* 2017 Marek Behun, CZ.NIC, [email protected]
*
* SPDX-License-Identifier: GPL-2.0+
*/

#include "btrfs.h"
#include <malloc.h>

struct chunk_map_item {
struct rb_node node;
u64 logical;
u64 length;
u64 physical;
};

static int add_chunk_mapping(struct btrfs_key *key, struct btrfs_chunk *chunk)
{
struct btrfs_stripe *stripe;
u64 block_profile = chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK;
struct rb_node **new = &(btrfs_info.chunks_root.rb_node), *prnt = NULL;
struct chunk_map_item *map_item;

if (block_profile && block_profile != BTRFS_BLOCK_GROUP_DUP) {
printf("%s: unsupported chunk profile %llu\n", __func__,
block_profile);
return -1;
} else if (!chunk->length) {
printf("%s: zero length chunk\n", __func__);
return -1;
}

stripe = &chunk->stripe;
btrfs_stripe_to_cpu(stripe);

while (*new) {
struct chunk_map_item *this;

this = rb_entry(*new, struct chunk_map_item, node);

prnt = *new;
if (key->offset < this->logical) {
new = &((*new)->rb_left);
} else if (key->offset > this->logical) {
new = &((*new)->rb_right);
} else {
debug("%s: Logical address %llu already in map!\n",
__func__, key->offset);
return 0;
}
}

map_item = malloc(sizeof(struct chunk_map_item));
if (!map_item)
return -1;

map_item->logical = key->offset;
map_item->length = chunk->length;
map_item->physical = le64_to_cpu(chunk->stripe.offset);
rb_link_node(&map_item->node, prnt, new);
rb_insert_color(&map_item->node, &btrfs_info.chunks_root);

debug("%s: Mapping %llu to %llu\n", __func__, map_item->logical,
map_item->physical);

return 0;
}

u64 btrfs_map_logical_to_physical(u64 logical)
{
struct rb_node *node = btrfs_info.chunks_root.rb_node;

while (node) {
struct chunk_map_item *item;

item = rb_entry(node, struct chunk_map_item, node);

if (item->logical > logical)
node = node->rb_left;
else if (logical > item->logical + item->length)
node = node->rb_right;
else
return item->physical + logical - item->logical;
}

printf("%s: Cannot map logical address %llu to physical\n", __func__,
logical);

return -1ULL;
}

void btrfs_chunk_map_exit(void)
{
struct rb_node *now, *next;
struct chunk_map_item *item;

for (now = rb_first_postorder(&btrfs_info.chunks_root); now; now = next)
{
item = rb_entry(now, struct chunk_map_item, node);
next = rb_next_postorder(now);
free(item);
}
}

int btrfs_chunk_map_init(void)
{
u8 sys_chunk_array_copy[sizeof(btrfs_info.sb.sys_chunk_array)];
u8 * const start = sys_chunk_array_copy;
u8 * const end = start + btrfs_info.sb.sys_chunk_array_size;
u8 *cur;
struct btrfs_key *key;
struct btrfs_chunk *chunk;

btrfs_info.chunks_root = RB_ROOT;

memcpy(sys_chunk_array_copy, btrfs_info.sb.sys_chunk_array,
sizeof(sys_chunk_array_copy));

for (cur = start; cur < end;) {
key = (struct btrfs_key *) cur;
cur += sizeof(struct btrfs_key);
chunk = (struct btrfs_chunk *) cur;

btrfs_key_to_cpu(key);
btrfs_chunk_to_cpu(chunk);

if (key->type != BTRFS_CHUNK_ITEM_KEY) {
printf("%s: invalid key type %u\n", __func__,
key->type);
return -1;
}

if (add_chunk_mapping(key, chunk))
return -1;

cur += sizeof(struct btrfs_chunk);
cur += sizeof(struct btrfs_stripe) * (chunk->num_stripes - 1);
}

return 0;
}

int btrfs_read_chunk_tree(void)
{
struct btrfs_path path;
struct btrfs_key key, *found_key;
struct btrfs_chunk *chunk;
int res;

key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.type = BTRFS_CHUNK_ITEM_KEY;
key.offset = 0;

if (btrfs_search_tree(&btrfs_info.chunk_root, &key, &path))
return -1;

do {
found_key = btrfs_path_leaf_key(&path);
if (btrfs_comp_keys_type(&key, found_key))
break;

chunk = btrfs_path_item_ptr(&path, struct btrfs_chunk);
btrfs_chunk_to_cpu(chunk);
if (add_chunk_mapping(found_key, chunk)) {
res = -1;
break;
}
} while (!(res = btrfs_next_slot(&path)));

btrfs_free_path(&path);

if (res < 0)
return -1;

return 0;
}
Loading

0 comments on commit 21a14fa

Please sign in to comment.