diff options
Diffstat (limited to 'contrib/syslinux/syslinux-4.03/core/fs/btrfs/btrfs.c')
-rw-r--r-- | contrib/syslinux/syslinux-4.03/core/fs/btrfs/btrfs.c | 674 |
1 files changed, 0 insertions, 674 deletions
diff --git a/contrib/syslinux/syslinux-4.03/core/fs/btrfs/btrfs.c b/contrib/syslinux/syslinux-4.03/core/fs/btrfs/btrfs.c deleted file mode 100644 index b6a14e3..0000000 --- a/contrib/syslinux/syslinux-4.03/core/fs/btrfs/btrfs.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * btrfs.c -- readonly btrfs support for syslinux - * Some data structures are derivated from btrfs-tools-0.19 ctree.h - * Copyright 2009 Intel Corporation; author: alek.du@intel.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, Inc., 53 Temple Place Ste 330, - * Boston MA 02111-1307, USA; either version 2 of the License, or - * (at your option) any later version; incorporated herein by reference. - * - */ - -#include <dprintf.h> -#include <stdio.h> -#include <string.h> -#include <cache.h> -#include <core.h> -#include <disk.h> -#include <fs.h> -#include <dirent.h> -#include "btrfs.h" - -/* compare function used for bin_search */ -typedef int (*cmp_func)(void *ptr1, void *ptr2); - -/* simple but useful bin search, used for chunk search and btree search */ -static int bin_search(void *ptr, int item_size, void *cmp_item, cmp_func func, - int min, int max, int *slot) -{ - int low = min; - int high = max; - int mid; - int ret; - unsigned long offset; - void *item; - - while (low < high) { - mid = (low + high) / 2; - offset = mid * item_size; - - item = ptr + offset; - ret = func(item, cmp_item); - - if (ret < 0) - low = mid + 1; - else if (ret > 0) - high = mid; - else { - *slot = mid; - return 0; - } - } - *slot = low; - return 1; -} - -/* XXX: these should go into the filesystem instance structure */ -static struct btrfs_chunk_map chunk_map; -static struct btrfs_super_block sb; -static u64 fs_tree; - -static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1, - struct btrfs_chunk_map_item *m2) -{ - if (m1->logical > m2->logical) - return 1; - if (m1->logical < m2->logical) - return -1; - return 0; -} - -/* insert a new chunk mapping item */ -static void insert_map(struct btrfs_chunk_map_item *item) -{ - int ret; - int slot; - int i; - - if (chunk_map.map == NULL) { /* first item */ - chunk_map.map_length = BTRFS_MAX_CHUNK_ENTRIES; - chunk_map.map = (struct btrfs_chunk_map_item *) - malloc(chunk_map.map_length * sizeof(*chunk_map.map)); - chunk_map.map[0] = *item; - chunk_map.cur_length = 1; - return; - } - ret = bin_search(chunk_map.map, sizeof(*item), item, - (cmp_func)btrfs_comp_chunk_map, 0, - chunk_map.cur_length, &slot); - if (ret == 0)/* already in map */ - return; - if (chunk_map.cur_length == BTRFS_MAX_CHUNK_ENTRIES) { - /* should be impossible */ - printf("too many chunk items\n"); - return; - } - for (i = chunk_map.cur_length; i > slot; i--) - chunk_map.map[i] = chunk_map.map[i-1]; - chunk_map.map[slot] = *item; - chunk_map.cur_length++; -} - -/* - * from sys_chunk_array or chunk_tree, we can convert a logical address to - * a physical address we can not support multi device case yet - */ -static u64 logical_physical(u64 logical) -{ - struct btrfs_chunk_map_item item; - int slot, ret; - - item.logical = logical; - ret = bin_search(chunk_map.map, sizeof(*chunk_map.map), &item, - (cmp_func)btrfs_comp_chunk_map, 0, - chunk_map.cur_length, &slot); - if (ret == 0) - slot++; - else if (slot == 0) - return -1; - if (logical >= - chunk_map.map[slot-1].logical + chunk_map.map[slot-1].length) - return -1; - return chunk_map.map[slot-1].physical + logical - - chunk_map.map[slot-1].logical; -} - -/* cache read from disk, offset and count are bytes */ -static int btrfs_read(struct fs_info *fs, char *buf, u64 offset, u64 count) -{ - const char *cd; - size_t block_size = fs->fs_dev->cache_block_size; - size_t off, cnt, total; - block_t block; - - total = count; - while (count > 0) { - block = offset / block_size; - off = offset % block_size; - cd = get_cache(fs->fs_dev, block); - if (!cd) - break; - cnt = block_size - off; - if (cnt > count) - cnt = count; - memcpy(buf, cd + off, cnt); - count -= cnt; - buf += cnt; - offset += cnt; - } - return total - count; -} - -/* btrfs has several super block mirrors, need to calculate their location */ -static inline u64 btrfs_sb_offset(int mirror) -{ - u64 start = 16 * 1024; - if (mirror) - return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror); - return BTRFS_SUPER_INFO_OFFSET; -} - -/* find the most recent super block */ -static void btrfs_read_super_block(struct fs_info *fs) -{ - int i; - int ret; - u8 fsid[BTRFS_FSID_SIZE]; - u64 offset; - u64 transid = 0; - struct btrfs_super_block buf; - - sb.total_bytes = ~0; /* Unknown as of yet */ - - /* find most recent super block */ - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - offset = btrfs_sb_offset(i); - dprintf("btrfs super: %llu max %llu\n", - offset, sb.total_bytes); - if (offset >= sb.total_bytes) - break; - - ret = btrfs_read(fs, (char *)&buf, offset, sizeof(buf)); - if (ret < sizeof(buf)) - break; - - if (buf.bytenr != offset || - strncmp((char *)(&buf.magic), BTRFS_MAGIC, - sizeof(buf.magic))) - continue; - - if (i == 0) - memcpy(fsid, buf.fsid, sizeof(fsid)); - else if (memcmp(fsid, buf.fsid, sizeof(fsid))) - continue; - - if (buf.generation > transid) { - memcpy(&sb, &buf, sizeof(sb)); - transid = buf.generation; - } - } -} - -static inline unsigned long btrfs_chunk_item_size(int num_stripes) -{ - return sizeof(struct btrfs_chunk) + - sizeof(struct btrfs_stripe) * (num_stripes - 1); -} - -static void clear_path(struct btrfs_path *path) -{ - memset(path, 0, sizeof(*path)); -} - -static int btrfs_comp_keys(struct btrfs_disk_key *k1, struct btrfs_disk_key *k2) -{ - if (k1->objectid > k2->objectid) - return 1; - if (k1->objectid < k2->objectid) - return -1; - if (k1->type > k2->type) - return 1; - if (k1->type < k2->type) - return -1; - if (k1->offset > k2->offset) - return 1; - if (k1->offset < k2->offset) - return -1; - return 0; -} - -/* compare keys but ignore offset, is useful to enumerate all same kind keys */ -static int btrfs_comp_keys_type(struct btrfs_disk_key *k1, - struct btrfs_disk_key *k2) -{ - if (k1->objectid > k2->objectid) - return 1; - if (k1->objectid < k2->objectid) - return -1; - if (k1->type > k2->type) - return 1; - if (k1->type < k2->type) - return -1; - return 0; -} - -/* seach tree directly on disk ... */ -static int search_tree(struct fs_info *fs, u64 loffset, - struct btrfs_disk_key *key, struct btrfs_path *path) -{ - u8 buf[BTRFS_MAX_LEAF_SIZE]; - struct btrfs_header *header = (struct btrfs_header *)buf; - struct btrfs_node *node = (struct btrfs_node *)buf; - struct btrfs_leaf *leaf = (struct btrfs_leaf *)buf; - int slot, ret; - u64 offset; - - offset = logical_physical(loffset); - btrfs_read(fs, (char *)header, offset, sizeof(*header)); - if (header->level) {/*node*/ - btrfs_read(fs, (char *)&node->ptrs[0], offset + sizeof(*header), - sb.nodesize - sizeof(*header)); - path->itemsnr[header->level] = header->nritems; - path->offsets[header->level] = loffset; - ret = bin_search(&node->ptrs[0], sizeof(struct btrfs_key_ptr), - key, (cmp_func)btrfs_comp_keys, - path->slots[header->level], header->nritems, &slot); - if (ret && slot > path->slots[header->level]) - slot--; - path->slots[header->level] = slot; - ret = search_tree(fs, node->ptrs[slot].blockptr, key, path); - } else {/*leaf*/ - btrfs_read(fs, (char *)&leaf->items, offset + sizeof(*header), - sb.leafsize - sizeof(*header)); - path->itemsnr[header->level] = header->nritems; - path->offsets[0] = loffset; - ret = bin_search(&leaf->items[0], sizeof(struct btrfs_item), - key, (cmp_func)btrfs_comp_keys, path->slots[0], - header->nritems, &slot); - if (ret && slot > path->slots[header->level]) - slot--; - path->slots[0] = slot; - path->item = leaf->items[slot]; - btrfs_read(fs, (char *)&path->data, - offset + sizeof(*header) + leaf->items[slot].offset, - leaf->items[slot].size); - } - return ret; -} - -/* return 0 if leaf found */ -static int next_leaf(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path) -{ - int slot; - int level = 1; - - while (level < BTRFS_MAX_LEVEL) { - if (!path->itemsnr[level]) /* no more nodes */ - return 1; - slot = path->slots[level] + 1; - if (slot >= path->itemsnr[level]) { - level++; - continue;; - } - path->slots[level] = slot; - path->slots[level-1] = 0; /* reset low level slots info */ - search_tree(fs, path->offsets[level], key, path); - break; - } - if (level == BTRFS_MAX_LEVEL) - return 1; - return 0; -} - -/* return 0 if slot found */ -static int next_slot(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path) -{ - int slot; - - if (!path->itemsnr[0]) - return 1; - slot = path->slots[0] + 1; - if (slot >= path->itemsnr[0]) - return 1; - path->slots[0] = slot; - search_tree(fs, path->offsets[0], key, path); - return 0; -} - -/* - * read chunk_array in super block - */ -static void btrfs_read_sys_chunk_array(void) -{ - struct btrfs_chunk_map_item item; - struct btrfs_disk_key *key; - struct btrfs_chunk *chunk; - int cur; - - /* read chunk array in superblock */ - cur = 0; - while (cur < sb.sys_chunk_array_size) { - key = (struct btrfs_disk_key *)(sb.sys_chunk_array + cur); - cur += sizeof(*key); - chunk = (struct btrfs_chunk *)(sb.sys_chunk_array + cur); - cur += btrfs_chunk_item_size(chunk->num_stripes); - /* insert to mapping table, ignore multi stripes */ - item.logical = key->offset; - item.length = chunk->length; - item.devid = chunk->stripe.devid; - item.physical = chunk->stripe.offset;/*ignore other stripes */ - insert_map(&item); - } -} - -/* read chunk items from chunk_tree and insert them to chunk map */ -static void btrfs_read_chunk_tree(struct fs_info *fs) -{ - struct btrfs_disk_key search_key; - struct btrfs_chunk *chunk; - struct btrfs_chunk_map_item item; - struct btrfs_path path; - - if (!(sb.flags & BTRFS_SUPER_FLAG_METADUMP)) { - if (sb.num_devices > 1) - printf("warning: only support single device btrfs\n"); - /* read chunk from chunk_tree */ - search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; - search_key.type = BTRFS_CHUNK_ITEM_KEY; - search_key.offset = 0; - clear_path(&path); - search_tree(fs, sb.chunk_root, &search_key, &path); - do { - do { - if (btrfs_comp_keys_type(&search_key, - &path.item.key)) - break; - chunk = (struct btrfs_chunk *)(path.data); - /* insert to mapping table, ignore stripes */ - item.logical = path.item.key.offset; - item.length = chunk->length; - item.devid = chunk->stripe.devid; - item.physical = chunk->stripe.offset; - insert_map(&item); - } while (!next_slot(fs, &search_key, &path)); - if (btrfs_comp_keys_type(&search_key, &path.item.key)) - break; - } while (!next_leaf(fs, &search_key, &path)); - } -} - -static inline u64 btrfs_name_hash(const char *name, int len) -{ - return btrfs_crc32c((u32)~1, name, len); -} - -static struct inode *btrfs_iget_by_inr(struct fs_info *fs, u64 inr) -{ - struct inode *inode; - struct btrfs_inode_item inode_item; - struct btrfs_disk_key search_key; - struct btrfs_path path; - int ret; - - /* FIXME: some BTRFS inode member are u64, while our logical inode - is u32, we may need change them to u64 later */ - search_key.objectid = inr; - search_key.type = BTRFS_INODE_ITEM_KEY; - search_key.offset = 0; - clear_path(&path); - ret = search_tree(fs, fs_tree, &search_key, &path); - if (ret) - return NULL; - inode_item = *(struct btrfs_inode_item *)path.data; - if (!(inode = alloc_inode(fs, inr, sizeof(struct btrfs_pvt_inode)))) - return NULL; - inode->ino = inr; - inode->size = inode_item.size; - inode->mode = IFTODT(inode_item.mode); - - if (inode->mode == DT_REG || inode->mode == DT_LNK) { - struct btrfs_file_extent_item extent_item; - u64 offset; - - /* get file_extent_item */ - search_key.type = BTRFS_EXTENT_DATA_KEY; - search_key.offset = 0; - clear_path(&path); - ret = search_tree(fs, fs_tree, &search_key, &path); - if (ret) - return NULL; /* impossible */ - extent_item = *(struct btrfs_file_extent_item *)path.data; - if (extent_item.type == BTRFS_FILE_EXTENT_INLINE)/* inline file */ - offset = path.offsets[0] + sizeof(struct btrfs_header) - + path.item.offset - + offsetof(struct btrfs_file_extent_item, disk_bytenr); - else - offset = extent_item.disk_bytenr; - PVT(inode)->offset = offset; - } - return inode; -} - -static struct inode *btrfs_iget_root(struct fs_info *fs) -{ - /* BTRFS_FIRST_CHUNK_TREE_OBJECTID(256) actually is first OBJECTID for FS_TREE */ - return btrfs_iget_by_inr(fs, BTRFS_FIRST_CHUNK_TREE_OBJECTID); -} - -static struct inode *btrfs_iget(const char *name, struct inode *parent) -{ - struct fs_info *fs = parent->fs; - struct btrfs_disk_key search_key; - struct btrfs_path path; - struct btrfs_dir_item dir_item; - int ret; - - search_key.objectid = parent->ino; - search_key.type = BTRFS_DIR_ITEM_KEY; - search_key.offset = btrfs_name_hash(name, strlen(name)); - clear_path(&path); - ret = search_tree(fs, fs_tree, &search_key, &path); - if (ret) - return NULL; - dir_item = *(struct btrfs_dir_item *)path.data; - - return btrfs_iget_by_inr(fs, dir_item.location.objectid); -} - -static int btrfs_readlink(struct inode *inode, char *buf) -{ - btrfs_read(inode->fs, buf, logical_physical(PVT(inode)->offset), inode->size); - buf[inode->size] = '\0'; - return inode->size; -} - -static int btrfs_readdir(struct file *file, struct dirent *dirent) -{ - struct fs_info *fs = file->fs; - struct inode *inode = file->inode; - struct btrfs_disk_key search_key; - struct btrfs_path path; - struct btrfs_dir_item *dir_item; - int ret; - - /* - * we use file->offset to store last search key.offset, will will search - * key that lower that offset, 0 means first search and we will search - * -1UL, which is the biggest possible key - */ - search_key.objectid = inode->ino; - search_key.type = BTRFS_DIR_ITEM_KEY; - search_key.offset = file->offset - 1; - clear_path(&path); - ret = search_tree(fs, fs_tree, &search_key, &path); - - if (ret) { - if (btrfs_comp_keys_type(&search_key, &path.item.key)) - return -1; - } - - dir_item = (struct btrfs_dir_item *)path.data; - file->offset = path.item.key.offset; - dirent->d_ino = dir_item->location.objectid; - dirent->d_off = file->offset; - dirent->d_reclen = offsetof(struct dirent, d_name) - + dir_item->name_len + 1; - dirent->d_type = IFTODT(dir_item->type); - memcpy(dirent->d_name, dir_item + 1, dir_item->name_len); - dirent->d_name[dir_item->name_len] = '\0'; - - return 0; -} - -static int btrfs_next_extent(struct inode *inode, uint32_t lstart) -{ - struct btrfs_disk_key search_key; - struct btrfs_file_extent_item extent_item; - struct btrfs_path path; - int ret; - u64 offset; - struct fs_info *fs = inode->fs; - u32 sec_shift = SECTOR_SHIFT(fs); - u32 sec_size = SECTOR_SIZE(fs); - - search_key.objectid = inode->ino; - search_key.type = BTRFS_EXTENT_DATA_KEY; - search_key.offset = lstart << sec_shift; - clear_path(&path); - ret = search_tree(fs, fs_tree, &search_key, &path); - if (ret) { /* impossible */ - printf("btrfs: search extent data error!\n"); - return -1; - } - extent_item = *(struct btrfs_file_extent_item *)path.data; - - if (extent_item.encryption) { - printf("btrfs: found encrypted data, cannot continue!\n"); - return -1; - } - if (extent_item.compression) { - printf("btrfs: found compressed data, cannot continue!\n"); - return -1; - } - - if (extent_item.type == BTRFS_FILE_EXTENT_INLINE) {/* inline file */ - /* we fake a extent here, and PVT of inode will tell us */ - offset = path.offsets[0] + sizeof(struct btrfs_header) - + path.item.offset - + offsetof(struct btrfs_file_extent_item, disk_bytenr); - inode->next_extent.len = - (inode->size + sec_size -1) >> sec_shift; - } else { - offset = extent_item.disk_bytenr + extent_item.offset; - inode->next_extent.len = - (extent_item.num_bytes + sec_size - 1) >> sec_shift; - } - inode->next_extent.pstart = - logical_physical(offset) >> sec_shift; - PVT(inode)->offset = offset; - return 0; -} - -static uint32_t btrfs_getfssec(struct file *file, char *buf, int sectors, - bool *have_more) -{ - u32 ret; - struct fs_info *fs = file->fs; - u32 off = PVT(file->inode)->offset % SECTOR_SIZE(fs); - bool handle_inline = false; - - if (off && !file->offset) {/* inline file first read patch */ - file->inode->size += off; - handle_inline = true; - } - ret = generic_getfssec(file, buf, sectors, have_more); - if (!ret) - return ret; - off = PVT(file->inode)->offset % SECTOR_SIZE(fs); - if (handle_inline) {/* inline file patch */ - ret -= off; - memcpy(buf, buf + off, ret); - } - return ret; -} - -static void btrfs_get_fs_tree(struct fs_info *fs) -{ - struct btrfs_disk_key search_key; - struct btrfs_path path; - struct btrfs_root_item *tree; - bool subvol_ok = false; - - /* check if subvol is filled by installer */ - if (*SubvolName) { - search_key.objectid = BTRFS_FS_TREE_OBJECTID; - search_key.type = BTRFS_ROOT_REF_KEY; - search_key.offset = 0; - clear_path(&path); - if (search_tree(fs, sb.root, &search_key, &path)) - next_slot(fs, &search_key, &path); - do { - do { - struct btrfs_root_ref *ref; - - if (btrfs_comp_keys_type(&search_key, - &path.item.key)) - break; - ref = (struct btrfs_root_ref *)path.data; - if (!strcmp((char*)(ref + 1), SubvolName)) { - subvol_ok = true; - break; - } - } while (!next_slot(fs, &search_key, &path)); - if (subvol_ok) - break; - if (btrfs_comp_keys_type(&search_key, &path.item.key)) - break; - } while (!next_leaf(fs, &search_key, &path)); - if (!subvol_ok) /* should be impossible */ - printf("no subvol found!\n"); - } - /* find fs_tree from tree_root */ - if (subvol_ok) - search_key.objectid = path.item.key.offset; - else /* "default" volume */ - search_key.objectid = BTRFS_FS_TREE_OBJECTID; - search_key.type = BTRFS_ROOT_ITEM_KEY; - search_key.offset = -1; - clear_path(&path); - search_tree(fs, sb.root, &search_key, &path); - tree = (struct btrfs_root_item *)path.data; - fs_tree = tree->bytenr; -} - -/* init. the fs meta data, return the block size shift bits. */ -static int btrfs_fs_init(struct fs_info *fs) -{ - struct disk *disk = fs->fs_dev->disk; - - btrfs_init_crc32c(); - - fs->sector_shift = disk->sector_shift; - fs->sector_size = 1 << fs->sector_shift; - fs->block_shift = BTRFS_BLOCK_SHIFT; - fs->block_size = 1 << fs->block_shift; - - /* Initialize the block cache */ - cache_init(fs->fs_dev, fs->block_shift); - - btrfs_read_super_block(fs); - if (strncmp((char *)(&sb.magic), BTRFS_MAGIC, sizeof(sb.magic))) - return -1; - btrfs_read_sys_chunk_array(); - btrfs_read_chunk_tree(fs); - btrfs_get_fs_tree(fs); - - return fs->block_shift; -} - -const struct fs_ops btrfs_fs_ops = { - .fs_name = "btrfs", - .fs_flags = 0, - .fs_init = btrfs_fs_init, - .iget_root = btrfs_iget_root, - .iget = btrfs_iget, - .readlink = btrfs_readlink, - .getfssec = btrfs_getfssec, - .close_file = generic_close_file, - .mangle_name = generic_mangle_name, - .next_extent = btrfs_next_extent, - .readdir = btrfs_readdir, - .load_config = generic_load_config -}; |