summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2009-12-17 11:14:48 +0800
committerH. Peter Anvin <hpa@zytor.com>2009-12-22 09:46:49 -0800
commit1cd67f6eb8617be02f3a1af104100eb5869b6e20 (patch)
tree11cda66bb07adb1cf934a5770698acbcc36944bc
parent9e25d6029b8c1568a74a7d9b5bc07f22b0fb8ade (diff)
downloadsyslinux-1cd67f6eb8617be02f3a1af104100eb5869b6e20.tar.gz
btrfs: add btrfs file system support to extlinuxsyslinux-4.00-pre7
the extlinux.sys will be installed in btrfs first 64K blank area, and the extlinux.conf must be in root dir... Signed-off-by: Alek Du <alek.du@intel.com> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--core/extlinux.asm7
-rw-r--r--core/fs.c44
-rw-r--r--core/fs/btrfs/btrfs.c645
-rw-r--r--core/fs/btrfs/btrfs.h275
-rw-r--r--core/fs/btrfs/crc32c.h101
-rw-r--r--core/fs/ext2/ext2.c3
-rw-r--r--core/ldlinux.asm4
-rw-r--r--extlinux/btrfs.h22
-rw-r--r--extlinux/main.c264
9 files changed, 1290 insertions, 75 deletions
diff --git a/core/extlinux.asm b/core/extlinux.asm
index 1f5423b2..c7cea51a 100644
--- a/core/extlinux.asm
+++ b/core/extlinux.asm
@@ -25,6 +25,11 @@
my_id equ extlinux_id
extern ext2_fs_ops
-ROOT_FS_OPS equ ext2_fs_ops
+ extern btrfs_fs_ops
+
+ section .rodata
+ROOT_FS_OPS dd ext2_fs_ops
+ dd btrfs_fs_ops
+ dd 0
%include "diskfs.inc"
diff --git a/core/fs.c b/core/fs.c
index a3407cfe..4c8c1533 100644
--- a/core/fs.c
+++ b/core/fs.c
@@ -6,7 +6,7 @@
/* The currently mounted filesystem */
struct fs_info *this_fs = NULL;
-struct fs_info fs;
+static struct fs_info fs;
/* Actual file structures (we don't have malloc yet...) */
struct file files[MAX_OPEN];
@@ -166,24 +166,34 @@ void close_file(com32sys_t *regs)
*/
void fs_init(com32sys_t *regs)
{
- int blk_shift;
- const struct fs_ops *ops = (const struct fs_ops *)regs->eax.l;
+ int blk_shift = -1;
+ /* ops is a ptr list for several fs_ops */
+ const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
- /* set up the fs stucture */
- fs.fs_ops = ops;
-
- /* this is a total hack... */
- if (ops->fs_flags & FS_NODEV)
- fs.fs_dev = NULL; /* Non-device filesystem */
- else
- fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l,
- regs->esi.w[0], regs->edi.w[0]);
-
+ while ((blk_shift < 0) && *ops) {
+ /* set up the fs stucture */
+ fs.fs_ops = *ops;
+ /* this is a total hack... */
+ if (fs.fs_ops->fs_flags & FS_NODEV)
+ fs.fs_dev = NULL;
+ else {
+ static struct device *dev;
+
+ if (!dev)
+ dev = device_init(regs->edx.b[0], regs->edx.b[1],
+ regs->ecx.l, regs->esi.w[0], regs->edi.w[0]);
+ fs.fs_dev = dev;
+ }
+ /* invoke the fs-specific init code */
+ blk_shift = fs.fs_ops->fs_init(&fs);
+ ops++;
+ }
+ if (blk_shift < 0) {
+ printf("No valid file system found!\n");
+ while (1)
+ ;
+ }
this_fs = &fs;
-
- /* invoke the fs-specific init code */
- blk_shift = fs.fs_ops->fs_init(&fs);
-
/* initialize the cache */
if (fs.fs_dev && fs.fs_dev->cache_data)
cache_init(fs.fs_dev, blk_shift);
diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c
new file mode 100644
index 00000000..2092e29b
--- /dev/null
+++ b/core/fs/btrfs/btrfs.c
@@ -0,0 +1,645 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.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;
+}
+
+struct open_file_t {
+ u64 devid; /* always 1 if allocated, reserved for multi disks */
+ u64 bytenr; /* start offset in devid */
+ u64 pos; /* current offset in file */
+};
+
+static struct open_file_t Files[MAX_OPEN];
+static int cache_ready;
+static struct fs_info *fs;
+static struct btrfs_chunk_map chunk_map;
+static struct btrfs_super_block sb;
+/* used for small chunk read for btrfs_read */
+#define RAW_BUF_SIZE 4096
+static u8 raw_buf[RAW_BUF_SIZE];
+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;
+
+ 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;
+}
+
+/* raw read from disk, offset and count are bytes */
+static int raw_read(char *buf, u64 offset, u64 count)
+{
+ struct disk *disk = fs->fs_dev->disk;
+ size_t max = RAW_BUF_SIZE >> disk->sector_shift;
+ size_t off, cnt, done, total;
+ sector_t sec;
+
+ total = count;
+ while (count > 0) {
+ sec = offset >> disk->sector_shift;
+ off = offset - (sec << disk->sector_shift);
+ done = disk->rdwr_sectors(disk, raw_buf, sec, max, 0);
+ if (done == 0)/* no data */
+ break;
+ cnt = (done << disk->sector_shift) - off;
+ if (cnt > count)
+ cnt = count;
+ memcpy(buf, raw_buf + off, cnt);
+ count -= cnt;
+ buf += cnt;
+ offset += cnt;
+ if (done != max)/* no enough sectors */
+ break;
+ }
+ return total - count;
+}
+
+/* cache read from disk, offset and count are bytes */
+static int cache_read(char *buf, u64 offset, u64 count)
+{
+ size_t block_size = fs->fs_dev->cache_block_size;
+ struct cache_struct *cs;
+ size_t off, cnt, total;
+ block_t block;
+
+ total = count;
+ while (count > 0) {
+ block = offset / block_size;
+ off = offset % block_size;
+ cs = get_cache_block(fs->fs_dev, block);
+ if (cs == NULL)/* no data */
+ break;
+ cnt = block_size - off;
+ if (cnt > count)
+ cnt = count;
+ memcpy(buf, cs->data + off, cnt);
+ count -= cnt;
+ buf += cnt;
+ offset += cnt;
+ }
+ return total - count;
+}
+
+static int btrfs_read(char *buf, u64 offset, u64 count)
+{
+ if (cache_ready)
+ return cache_read(buf, offset, count);
+ return raw_read(buf, offset, 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()
+{
+ int i;
+ int ret;
+ u8 fsid[BTRFS_FSID_SIZE];
+ u64 offset;
+ u64 transid = 0;
+ struct btrfs_super_block buf;
+
+ /* find most recent super block */
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ offset = btrfs_sb_offset(i);
+ ret = btrfs_read((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;
+}
+
+/* seach tree directly on disk ... */
+static int search_tree(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((char *)header, offset, sizeof(*header));
+ if (header->level) {/*node*/
+ btrfs_read((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(node->ptrs[slot].blockptr, key, path);
+ } else {/*leaf*/
+ btrfs_read((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((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 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(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 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(path->offsets[0], key, path);
+ return 0;
+}
+
+/*
+ * read chunk_array in super block
+ */
+static void btrfs_read_sys_chunk_array()
+{
+ 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 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(sb.chunk_root, &search_key, &path);
+ do {
+ do {
+ if (path.item.key.objectid !=
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID)
+ 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(&search_key, &path));
+ if (path.item.key.objectid !=
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID)
+ break;
+ } while (!next_leaf(&search_key, &path));
+ }
+}
+
+static inline u64 btrfs_name_hash(const char *name, int len)
+{
+ return btrfs_crc32c((u32)~1, name, len);
+}
+
+/* search a file with full path in fs_tree, do not support ../ ./ style path */
+static int btrfs_search_fs_tree(const char *fullpath, u64 *offset,
+ u64 *size, u8 *type)
+{
+ char name[256];
+ char *tmp = name;
+ u64 objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+ int ret;
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ struct btrfs_dir_item dir_item;
+ struct btrfs_inode_item inode_item;
+ struct btrfs_file_extent_item extent_item;
+
+ *tmp = '\0';
+ while (1) {
+ char c = *(fullpath++);
+
+ *(tmp++) = c;
+ if (!c)
+ break;
+ if (c == '/') {
+ *(tmp-1) = '\0';
+ if (strlen(name)) {/* a "real" dir */
+ search_key.objectid = objectid;
+ search_key.type = BTRFS_DIR_ITEM_KEY;
+ search_key.offset =
+ btrfs_name_hash(name, strlen(name));
+ clear_path(&path);
+ ret = search_tree(fs_tree, &search_key, &path);
+ if (ret)
+ return ret; /* not found */
+ dir_item = *(struct btrfs_dir_item *)path.data;
+ /* found the name but it is not a dir ? */
+ if (dir_item.type != BTRFS_FT_DIR) {
+ printf("%s is not a dir\n", name);
+ return -1;
+ }
+ objectid = dir_item.location.objectid;
+ }
+ tmp = name;
+ *tmp = '\0';
+ }
+ }
+ /* get file dir_item */
+ if (!strlen(name))/* no file part */
+ return -1;
+ search_key.objectid = objectid;
+ search_key.type = BTRFS_DIR_ITEM_KEY;
+ search_key.offset = btrfs_name_hash(name, strlen(name));
+ clear_path(&path);
+ ret = search_tree(fs_tree, &search_key, &path);
+ if (ret)
+ return ret; /* not found */
+ dir_item = *(struct btrfs_dir_item *)path.data;
+ *type = dir_item.type;
+ /* found the name but it is not a file ? */
+ if (*type != BTRFS_FT_REG_FILE && *type != BTRFS_FT_SYMLINK) {
+ printf("%s is not a file\n", name);
+ return -1;
+ }
+
+ /* get inode */
+ search_key = dir_item.location;
+ clear_path(&path);
+ ret = search_tree(fs_tree, &search_key, &path);
+ if (ret)
+ return ret; /* not found */
+ inode_item = *(struct btrfs_inode_item *)path.data;
+
+ /* get file_extent_item */
+ search_key.objectid = dir_item.location.objectid;
+ search_key.type = BTRFS_EXTENT_DATA_KEY;
+ search_key.offset = 0;
+ clear_path(&path);
+ ret = search_tree(fs_tree, &search_key, &path);
+ if (ret)
+ return ret; /* not found */
+ extent_item = *(struct btrfs_file_extent_item *)path.data;
+ *size = inode_item.size;
+ 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;
+
+ return 0;
+}
+
+static struct open_file_t *alloc_file(void)
+{
+ struct open_file_t *file = Files;
+ int i;
+
+ for (i = 0; i < MAX_OPEN; i++) {
+ if (file->devid == 0) /* found it */
+ return file;
+ file++;
+ }
+
+ return NULL; /* not found */
+}
+
+static inline void close_pvt(struct open_file_t *of)
+{
+ of->devid = 0;
+}
+
+static void btrfs_close_file(struct file *file)
+{
+ close_pvt(file->open_file);
+}
+
+static void btrfs_searchdir(char *filename, struct file *file)
+{
+ struct open_file_t *open_file;
+ u64 offset, size;
+ char name[FILENAME_MAX];
+ char *fname = filename;
+ u8 type;
+ int ret;
+
+ file->open_file = NULL;
+ file->file_len = 0;
+ do {
+ ret = btrfs_search_fs_tree(fname, &offset, &size, &type);
+ if (ret)
+ break;
+ if (type == BTRFS_FT_SYMLINK) {
+ btrfs_read(name, logical_physical(offset), size);
+ name[size] = '\0';
+ fname = name;
+ continue;
+ }
+ open_file = alloc_file();
+ file->open_file = (void *)open_file;
+ if (open_file) {
+ /* we may support multi devices later on */
+ open_file->devid = 1;
+ open_file->bytenr = offset;
+ open_file->pos = 0;
+ file->file_len = size;
+ }
+ break;
+ } while (1);
+}
+
+/* Load the config file, return 1 if failed, or 0 */
+static int btrfs_load_config(void)
+{
+ char *config_name = "extlinux.conf";/* use same config name as ext2 too? */
+ com32sys_t regs;
+
+ strcpy(ConfigName, config_name);
+ *(uint16_t *)CurrentDirName = ROOT_DIR_WORD;
+
+ memset(&regs, 0, sizeof regs);
+ regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+ call16(core_open, &regs, &regs);
+
+ return !!(regs.eflags.l & EFLAGS_ZF);
+}
+
+static uint32_t btrfs_getfssec(struct file *gfile, char *buf, int sectors,
+ bool *have_more)
+{
+ struct disk *disk = fs->fs_dev->disk;
+ struct open_file_t *file = gfile->open_file;
+ u32 sec_shift = fs->fs_dev->disk->sector_shift;
+ u32 phy = logical_physical(file->bytenr + file->pos);
+ u32 sec = phy >> sec_shift;
+ u32 off = phy - (sec << sec_shift);
+ u32 remain = gfile->file_len - file->pos;
+ u32 remain_sec = (remain + (1 << sec_shift) - 1) >> sec_shift;
+ u32 size;
+
+ if (sectors > remain_sec)
+ sectors = remain_sec;
+ /* btrfs extent is continus */
+ disk->rdwr_sectors(disk, buf, sec, sectors, 0);
+ size = sectors << sec_shift;
+ if (size > remain)
+ size = remain;
+ file->pos += size;
+ *have_more = remain - size;
+
+ if (off)/* inline file is not started with sector boundary */
+ memcpy(buf, buf + off, size);
+
+ return size;
+}
+
+static void btrfs_get_fs_tree(void)
+{
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ struct btrfs_root_item *tree;
+
+ /* find fs_tree from tree_root */
+ search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+ search_key.type = BTRFS_ROOT_ITEM_KEY;
+ search_key.offset = -1;
+ clear_path(&path);
+ search_tree(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)
+{
+ fs = _fs;
+ btrfs_read_super_block();
+ if (strncmp((char *)(&sb.magic), BTRFS_MAGIC, sizeof(sb.magic)))
+ return -1;
+ btrfs_read_sys_chunk_array();
+ btrfs_read_chunk_tree();
+ btrfs_get_fs_tree();
+ cache_ready = 1;
+ return BTRFS_BLOCK_SHIFT;/* to determine cache size */
+}
+
+const struct fs_ops btrfs_fs_ops = {
+ .fs_name = "btrfs",
+ .fs_flags = 0,
+ .fs_init = btrfs_fs_init,
+ .searchdir = btrfs_searchdir,
+ .getfssec = btrfs_getfssec,
+ .close_file = btrfs_close_file,
+ .mangle_name = generic_mangle_name,
+ .unmangle_name = generic_unmangle_name,
+ .load_config = btrfs_load_config
+};
diff --git a/core/fs/btrfs/btrfs.h b/core/fs/btrfs/btrfs.h
new file mode 100644
index 00000000..9ee90926
--- /dev/null
+++ b/core/fs/btrfs/btrfs.h
@@ -0,0 +1,275 @@
+#ifndef _BTRFS_H_
+#define _BTRFS_H_
+
+#include <stdint.h>
+#include <zconf.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+/* type that store on disk, but it is same as cpu type for i386 arch */
+typedef u16 __le16;
+typedef u32 __le32;
+typedef u64 __le64;
+
+#include "crc32c.h"
+#define btrfs_crc32c crc32c_le
+
+#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_INFO_SIZE 4096
+#define BTRFS_MAX_LEAF_SIZE 4096
+#define BTRFS_BLOCK_SHIFT 12
+
+#define BTRFS_SUPER_MIRROR_MAX 3
+#define BTRFS_SUPER_MIRROR_SHIFT 12
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+#define BTRFS_UUID_SIZE 16
+
+#define BTRFS_MAGIC "_BHRfS_M"
+
+#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
+
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
+#define BTRFS_ROOT_ITEM_KEY 132
+#define BTRFS_EXTENT_DATA_KEY 108
+#define BTRFS_DIR_ITEM_KEY 84
+
+#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+#define BTRFS_FILE_EXTENT_INLINE 0
+#define BTRFS_FILE_EXTENT_REG 1
+#define BTRFS_FILE_EXTENT_PREALLOC 2
+
+#define BTRFS_MAX_LEVEL 8
+#define BTRFS_MAX_CHUNK_ENTRIES 256
+
+#define BTRFS_FT_REG_FILE 1
+#define BTRFS_FT_DIR 2
+#define BTRFS_FT_SYMLINK 7
+
+#define ROOT_DIR_WORD 0x002f
+
+struct btrfs_dev_item {
+ __le64 devid;
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le32 io_align;
+ __le32 io_width;
+ __le32 sector_size;
+ __le64 type;
+ __le64 generation;
+ __le64 start_offset;
+ __le32 dev_group;
+ u8 seek_speed;
+ u8 bandwidth;
+ u8 uuid[BTRFS_UUID_SIZE];
+ u8 fsid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_super_block {
+ u8 csum[BTRFS_CSUM_SIZE];
+ /* the first 3 fields must match struct btrfs_header */
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __le64 bytenr; /* this block number */
+ __le64 flags;
+
+ /* allowed to be different from the btrfs_header from here own down */
+ __le64 magic;
+ __le64 generation;
+ __le64 root;
+ __le64 chunk_root;
+ __le64 log_root;
+
+ /* this will help find the new super based on the log root */
+ __le64 log_root_transid;
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le64 root_dir_objectid;
+ __le64 num_devices;
+ __le32 sectorsize;
+ __le32 nodesize;
+ __le32 leafsize;
+ __le32 stripesize;
+ __le32 sys_chunk_array_size;
+ __le64 chunk_root_generation;
+ __le64 compat_flags;
+ __le64 compat_ro_flags;
+ __le64 incompat_flags;
+ __le16 csum_type;
+ u8 root_level;
+ u8 chunk_root_level;
+ u8 log_root_level;
+ struct btrfs_dev_item dev_item;
+
+ char label[BTRFS_LABEL_SIZE];
+
+ /* future expansion */
+ __le64 reserved[32];
+ u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_disk_key {
+ __le64 objectid;
+ u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_stripe {
+ __le64 devid;
+ __le64 offset;
+ u8 dev_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_chunk {
+ __le64 length;
+ __le64 owner;
+ __le64 stripe_len;
+ __le64 type;
+ __le32 io_align;
+ __le32 io_width;
+ __le32 sector_size;
+ __le16 num_stripes;
+ __le16 sub_stripes;
+ struct btrfs_stripe stripe;
+} __attribute__ ((__packed__));
+
+struct btrfs_header {
+ /* these first four must match the super block */
+ u8 csum[BTRFS_CSUM_SIZE];
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __le64 bytenr; /* which block this node is supposed to live in */
+ __le64 flags;
+
+ /* allowed to be different from the super from here on down */
+ u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ __le64 generation;
+ __le64 owner;
+ __le32 nritems;
+ u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_item {
+ struct btrfs_disk_key key;
+ __le32 offset;
+ __le32 size;
+} __attribute__ ((__packed__));
+
+struct btrfs_leaf {
+ struct btrfs_header header;
+ struct btrfs_item items[];
+} __attribute__ ((__packed__));
+
+struct btrfs_key_ptr {
+ struct btrfs_disk_key key;
+ __le64 blockptr;
+ __le64 generation;
+} __attribute__ ((__packed__));
+
+struct btrfs_node {
+ struct btrfs_header header;
+ struct btrfs_key_ptr ptrs[];
+} __attribute__ ((__packed__));
+
+/* remember how we get to a node/leaf */
+struct btrfs_path {
+ u64 offsets[BTRFS_MAX_LEVEL];
+ int itemsnr[BTRFS_MAX_LEVEL];
+ int slots[BTRFS_MAX_LEVEL];
+ /* remember last slot's item and data */
+ struct btrfs_item item;
+ u8 data[BTRFS_MAX_LEAF_SIZE];
+};
+
+/* store logical offset to physical offset mapping */
+struct btrfs_chunk_map_item {
+ u64 logical;
+ u64 length;
+ u64 devid;
+ u64 physical;
+};
+
+struct btrfs_chunk_map {
+ struct btrfs_chunk_map_item map[BTRFS_MAX_CHUNK_ENTRIES];
+ u32 map_length;
+ u32 cur_length;
+};
+
+struct btrfs_timespec {
+ __le64 sec;
+ __le32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_item {
+ /* nfs style generation number */
+ __le64 generation;
+ /* transid that last touched this inode */
+ __le64 transid;
+ __le64 size;
+ __le64 nbytes;
+ __le64 block_group;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le64 rdev;
+ __le64 flags;
+
+ /* modification sequence number for NFS */
+ __le64 sequence;
+
+ /*
+ * a little future expansion, for more than this we can
+ * just grow the inode item and version it
+ */
+ __le64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __le64 generation;
+ __le64 root_dirid;
+ __le64 bytenr;
+ __le64 byte_limit;
+ __le64 bytes_used;
+ __le64 last_snapshot;
+ __le64 flags;
+ __le32 refs;
+ struct btrfs_disk_key drop_progress;
+ u8 drop_level;
+ u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __le64 transid;
+ __le16 data_len;
+ __le16 name_len;
+ u8 type;
+} __attribute__ ((__packed__));
+
+struct btrfs_file_extent_item {
+ __le64 generation;
+ __le64 ram_bytes;
+ u8 compression;
+ u8 encryption;
+ __le16 other_encoding; /* spare for later use */
+ u8 type;
+ __le64 disk_bytenr;
+ __le64 disk_num_bytes;
+ __le64 offset;
+ __le64 num_bytes;
+} __attribute__ ((__packed__));
+
+#endif
diff --git a/core/fs/btrfs/crc32c.h b/core/fs/btrfs/crc32c.h
new file mode 100644
index 00000000..44960df2
--- /dev/null
+++ b/core/fs/btrfs/crc32c.h
@@ -0,0 +1,101 @@
+/*
+ * Copied from Linux kernel crypto/crc32c.c
+ * Copyright (c) 2004 Cisco Systems, Inc.
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+/*
+ * This is the CRC-32C table
+ * Generated with:
+ * width = 32 bits
+ * poly = 0x1EDC6F41
+ * reflect input bytes = true
+ * reflect output bytes = true
+ */
+
+static const u32 crc32c_table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+/*
+ * Steps through buffer one byte at at time, calculates reflected
+ * crc using table.
+ */
+
+inline u32 crc32c_le(u32 crc, const char *data, size_t length)
+{
+ while (length--)
+ crc =
+ crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
+
+ return crc;
+}
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
index 8c44b3ae..dde5571e 100644
--- a/core/fs/ext2/ext2.c
+++ b/core/fs/ext2/ext2.c
@@ -706,6 +706,9 @@ static int ext2_fs_init(struct fs_info *fs)
/* read the super block */
disk->rdwr_sectors(disk, &sb, 2, 2, 0);
+ /* check if it is ext2, since we also support btrfs now */
+ if (sb.s_magic != EXT2_SUPER_MAGIC)
+ return -1;
ClustByteShift = sb.s_log_block_size + 10;
ClustSize = 1 << ClustByteShift;
ClustShift = ClustByteShift - SECTOR_SHIFT;
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
index 20f0ee94..599d760a 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -31,6 +31,8 @@
my_id equ syslinux_id
extern vfat_fs_ops
-ROOT_FS_OPS equ vfat_fs_ops
+ section .rodata
+ROOT_FS_OPS dd vfat_fs_ops
+ dd 0
%include "diskfs.inc"
diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h
new file mode 100644
index 00000000..39a861a5
--- /dev/null
+++ b/extlinux/btrfs.h
@@ -0,0 +1,22 @@
+#ifndef _BTRFS_H_
+#define _BTRFS_H_
+
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_INFO_SIZE 4096
+#define BTRFS_MAGIC "_BHRfS_M"
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+
+struct btrfs_super_block {
+ unsigned char csum[BTRFS_CSUM_SIZE];
+ /* the first 3 fields must match struct btrfs_header */
+ unsigned char fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ u64 bytenr; /* this block number */
+ u64 flags;
+
+ /* allowed to be different from the btrfs_header from here own down */
+ u64 magic;
+} __attribute__ ((__packed__));
+
+#endif
diff --git a/extlinux/main.c b/extlinux/main.c
index 79ead181..7eb59dae 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -14,7 +14,7 @@
/*
* extlinux.c
*
- * Install the extlinux boot block on an ext2/3/4 filesystem
+ * Install the extlinux boot block on an ext2/3/4 and btrfs filesystem
*/
#define _GNU_SOURCE /* Enable everything */
@@ -29,6 +29,7 @@ typedef uint64_t u64;
#ifndef __KLIBC__
#include <mntent.h>
#endif
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
@@ -46,6 +47,7 @@ typedef uint64_t u64;
#undef statfs
#include "ext2_fs.h"
+#include "btrfs.h"
#include "../version.h"
#include "syslxint.h"
@@ -56,6 +58,10 @@ typedef uint64_t u64;
#endif
/* Global option handling */
+/* Global fs_type for handling ext2/3/4 vs btrfs */
+#define EXT2 1
+#define BTRFS 2
+int fs_type;
const char *program;
@@ -130,6 +136,9 @@ static const char short_options[] = "iUuzS:H:rvho:O";
#define EXT2_SUPER_OFFSET 1024
#endif
+/* the btrfs partition first 64K blank area is used to store boot sector and
+ boot image, the boot sector is from 0~512, the boot image starts at 2K */
+#define BTRFS_EXTLINUX_OFFSET (2*1024)
/*
* Boot block
*/
@@ -359,10 +368,11 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd)
uint32_t csum;
int secptroffset;
- if (fstat(dirfd, &dirst)) {
- perror("fstat dirfd");
- exit(255); /* This should never happen */
- }
+ if (fs_type == EXT2)
+ if (fstat(dirfd, &dirst)) {
+ perror("fstat dirfd");
+ exit(255); /* This should never happen */
+ }
totalbytes = get_size(devfd);
get_geometry(devfd, totalbytes, &geo);
@@ -406,14 +416,20 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd)
nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
nsect += 2; /* Two sectors for the ADV */
sectp = alloca(sizeof(uint32_t) * nsect);
- if (sectmap(fd, sectp, nsect)) {
- perror("bmap");
- exit(1);
+ if (fs_type == EXT2) {
+ if (sectmap(fd, sectp, nsect)) {
+ perror("bmap");
+ exit(1);
+ }
+ } else if (fs_type == BTRFS) {
+ int i;
+
+ for (i = 0; i < nsect; i++)
+ *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
}
/* First sector need pointer in boot sector */
set_32(&bs->NextSector, *sectp++);
-
/* Stupid mode? */
if (opt.stupid_mode)
set_16(&bs->MaxTransfer, 1);
@@ -455,13 +471,21 @@ int patch_file_and_bootblock(int fd, int dirfd, int devfd)
* Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
* ADV was found.
*/
-int read_adv(const char *path)
+int read_adv(const char *path, int devfd)
{
char *file;
int fd = -1;
struct stat st;
int err = 0;
+ if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
+ if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
+ BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
+ perror("writing adv");
+ return 1;
+ }
+ return 0;
+ }
asprintf(&file, "%s%sextlinux.sys",
path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
@@ -505,7 +529,7 @@ int read_adv(const char *path)
/*
* Update the ADV in an existing installation.
*/
-int write_adv(const char *path)
+int write_adv(const char *path, int devfd)
{
unsigned char advtmp[2 * ADV_SIZE];
char *file;
@@ -514,6 +538,14 @@ int write_adv(const char *path)
int err = 0;
int flags, nflags;
+ if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
+ if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
+ BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
+ perror("writing adv");
+ return 1;
+ }
+ return 0;
+ }
asprintf(&file, "%s%sextlinux.sys",
path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
@@ -611,17 +643,30 @@ int modify_adv(void)
int install_bootblock(int fd, const char *device)
{
struct ext2_super_block sb;
+ struct btrfs_super_block sb2;
+ bool ok = false;
- if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
- perror("reading superblock");
- return 1;
+ if (fs_type == EXT2) {
+ if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
+ perror("reading superblock");
+ return 1;
+ }
+ if (sb.s_magic == EXT2_SUPER_MAGIC)
+ ok = true;
+ } else if (fs_type == BTRFS) {
+ if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
+ != sizeof sb2) {
+ perror("reading superblock");
+ return 1;
+ }
+ if (sb2.magic == *(u64 *)BTRFS_MAGIC)
+ ok = true;
}
-
- if (sb.s_magic != EXT2_SUPER_MAGIC) {
- fprintf(stderr, "no ext2/3/4 superblock found on %s\n", device);
+ if (!ok) {
+ fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
+ device);
return 1;
}
-
if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
perror("writing bootblock");
return 1;
@@ -630,7 +675,7 @@ int install_bootblock(int fd, const char *device)
return 0;
}
-int install_file(const char *path, int devfd, struct stat *rst)
+int ext2_install_file(const char *path, int devfd, struct stat *rst)
{
char *file;
int fd = -1, dirfd = -1, flags;
@@ -721,6 +766,40 @@ bail:
return 1;
}
+/* btrfs has to install the extlinux.sys in the first 64K blank area, which
+ is not managered by btrfs tree, so actually this is not installed as files.
+ since the cow feature of btrfs will move the extlinux.sys every where */
+int btrfs_install_file(const char *path, int devfd, struct stat *rst)
+{
+ patch_file_and_bootblock(-1, -1, devfd);
+ if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
+ != boot_image_len) {
+ perror("writing bootblock");
+ return 1;
+ }
+ printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
+ if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
+ BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
+ perror("writing adv");
+ return 1;
+ }
+ printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
+ if (stat(path, rst)) {
+ perror(path);
+ return 1;
+ }
+ return 0;
+}
+
+int install_file(const char *path, int devfd, struct stat *rst)
+{
+ if (fs_type == EXT2)
+ return ext2_install_file(path, devfd, rst);
+ else if (fs_type == BTRFS)
+ return btrfs_install_file(path, devfd, rst);
+ return 1;
+}
+
/* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
sector; this is consistent with FAT filesystems. */
int already_installed(int devfd)
@@ -745,10 +824,13 @@ static void device_cleanup(void)
static int validate_device(const char *path, int devfd)
{
struct stat pst, dst;
+ struct statfs sfs;
- if (stat(path, &pst) || fstat(devfd, &dst))
+ if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
return -1;
-
+ /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
+ if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
+ return 0;
return (pst.st_dev == dst.st_rdev) ? 0 : -1;
}
@@ -759,18 +841,35 @@ static const char *find_device(const char *mtab_file, dev_t dev)
struct stat dst;
FILE *mtab;
const char *devname = NULL;
+ bool done;
mtab = setmntent(mtab_file, "r");
if (!mtab)
return NULL;
+ done = false;
while ((mnt = getmntent(mtab))) {
- if ((!strcmp(mnt->mnt_type, "ext2") ||
- !strcmp(mnt->mnt_type, "ext3") ||
- !strcmp(mnt->mnt_type, "ext4")) &&
- !stat(mnt->mnt_fsname, &dst) && dst.st_rdev == dev) {
- devname = strdup(mnt->mnt_fsname);
- break;
+ /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
+ switch (fs_type) {
+ case BTRFS:
+ if (!strcmp(mnt->mnt_type, "btrfs") &&
+ !stat(mnt->mnt_dir, &dst) &&
+ dst.st_dev == dev)
+ done = true;
+ break;
+ case EXT2:
+ if ((!strcmp(mnt->mnt_type, "ext2") ||
+ !strcmp(mnt->mnt_type, "ext3") ||
+ !strcmp(mnt->mnt_type, "ext4")) &&
+ !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
+ }
+ if (done) {
+ devname = strdup(mnt->mnt_fsname);
+ break;
}
}
endmntent(mtab);
@@ -779,30 +878,20 @@ static const char *find_device(const char *mtab_file, dev_t dev)
}
#endif
-int install_loader(const char *path, int update_only)
+static const char *get_devname(const char *path)
{
- struct stat st, fst;
- int devfd, rv;
const char *devname = NULL;
+ struct stat st;
struct statfs sfs;
if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
fprintf(stderr, "%s: Not a directory: %s\n", program, path);
- return 1;
+ return devname;
}
-
if (statfs(path, &sfs)) {
fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
- return 1;
- }
-
- if (sfs.f_type != EXT2_SUPER_MAGIC) {
- fprintf(stderr, "%s: not an ext2/3/4 filesystem: %s\n", program, path);
- return 1;
+ return devname;
}
-
- devfd = -1;
-
#ifdef __KLIBC__
/* klibc doesn't have getmntent and friends; instead, just create
@@ -812,7 +901,7 @@ int install_loader(const char *path, int update_only)
if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
fprintf(stderr, "%s: cannot create device %s\n", program, devname);
- return 1;
+ return devname;
}
atexit(device_cleanup); /* unlink the device node on exit */
@@ -827,46 +916,99 @@ int install_loader(const char *path, int update_only)
}
if (!devname) {
fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
- return 1;
+ return devname;
}
fprintf(stderr, "%s is device %s\n", path, devname);
#endif
+ return devname;
+}
+
+static int open_device(const char *path, struct stat *st, const char **_devname)
+{
+ int devfd;
+ const char *devname = NULL;
+ struct statfs sfs;
+
+ if (st)
+ if (stat(path, st) || !S_ISDIR(st->st_mode)) {
+ fprintf(stderr, "%s: Not a directory: %s\n", program, path);
+ return -1;
+ }
+
+ if (statfs(path, &sfs)) {
+ fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
+ return -1;
+ }
+ if (sfs.f_type == EXT2_SUPER_MAGIC)
+ fs_type = EXT2;
+ else if (sfs.f_type == BTRFS_SUPER_MAGIC)
+ fs_type = BTRFS;
+
+ if (!fs_type) {
+ fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
+ program, path);
+ return -1;
+ }
+
+ devfd = -1;
+ devname = get_devname(path);
+ if (_devname)
+ *_devname = devname;
if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
fprintf(stderr, "%s: cannot open device %s\n", program, devname);
- return 1;
+ return -1;
}
/* Verify that the device we opened is the device intended */
if (validate_device(path, devfd)) {
fprintf(stderr, "%s: path %s doesn't match device %s\n",
program, path, devname);
- return 1;
+ close(devfd);
+ return -1;
}
+ return devfd;
+}
+
+int install_loader(const char *path, int update_only)
+{
+ struct stat st, fst;
+ int devfd, rv;
+ const char *devname;
+
+ devfd = open_device(path, &st, &devname);
+ if (devfd < 0)
+ return 1;
if (update_only && !already_installed(devfd)) {
fprintf(stderr, "%s: no previous extlinux boot sector found\n",
program);
+ close(devfd);
return 1;
}
/* Read a pre-existing ADV, if already installed */
if (opt.reset_adv)
syslinux_reset_adv(syslinux_adv);
- else if (read_adv(path) < 0)
+ else if (read_adv(path, devfd) < 0) {
+ close(devfd);
return 1;
-
- if (modify_adv() < 0)
+ }
+ if (modify_adv() < 0) {
+ close(devfd);
return 1;
+ }
/* Install extlinux.sys */
- if (install_file(path, devfd, &fst))
+ if (install_file(path, devfd, &fst)) {
+ close(devfd);
return 1;
-
+ }
if (fst.st_dev != st.st_dev) {
fprintf(stderr, "%s: file system changed under us - aborting!\n",
program);
+ close(devfd);
return 1;
}
@@ -883,17 +1025,27 @@ int install_loader(const char *path, int update_only)
*/
int modify_existing_adv(const char *path)
{
+ int devfd;
+
+ devfd = open_device(path, NULL, NULL);
+ if (devfd < 0)
+ return 1;
+
if (opt.reset_adv)
syslinux_reset_adv(syslinux_adv);
- else if (read_adv(path) < 0)
+ else if (read_adv(path, devfd) < 0) {
+ close(devfd);
return 1;
-
- if (modify_adv() < 0)
+ }
+ if (modify_adv() < 0) {
+ close(devfd);
return 1;
-
- if (write_adv(path) < 0)
+ }
+ if (write_adv(path, devfd) < 0) {
+ close(devfd);
return 1;
-
+ }
+ close(devfd);
return 0;
}