From 3836e89df153ae4063ab55e9ea43e5820949a031 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 01:22:55 +0100 Subject: [PATCH 01/34] Add crc32c for btrfs --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 1 + grub-core/lib/crc.c | 75 +++++++++++++++++++++++++++++++++++++ include/grub/lib/crc.h | 25 +++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 grub-core/lib/crc.c create mode 100644 include/grub/lib/crc.h diff --git a/Makefile.util.def b/Makefile.util.def index 3e8ae16f5..4d642a2b6 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -79,6 +79,7 @@ library = { common = grub-core/lib/LzFind.c; common = grub-core/lib/LzmaEnc.c; common = grub-core/lib/pbkdf2.c; + common = grub-core/lib/crc.c; common = grub-core/normal/datetime.c; common = grub-core/normal/misc.c; common = grub-core/partmap/acorn.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ce10ba372..c62d7d12f 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -905,6 +905,7 @@ module = { module = { name = btrfs; common = fs/btrfs.c; + common = lib/crc.c; }; module = { diff --git a/grub-core/lib/crc.c b/grub-core/lib/crc.c new file mode 100644 index 000000000..ffc3ef3b5 --- /dev/null +++ b/grub-core/lib/crc.c @@ -0,0 +1,75 @@ +/* crc.c - crc function */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +static grub_uint32_t crc32c_table [256]; + +static void +init_crc32c_table (void) +{ + auto grub_uint32_t reflect (grub_uint32_t ref, int len); + grub_uint32_t reflect (grub_uint32_t ref, int len) + { + grub_uint32_t result = 0; + int i; + + for (i = 1; i <= len; i++) + { + if (ref & 1) + result |= 1 << (len - i); + ref >>= 1; + } + + return result; + } + + grub_uint32_t polynomial = 0x1edc6f41; + int i, j; + + for(i = 0; i < 256; i++) + { + crc32c_table[i] = reflect(i, 8) << 24; + for (j = 0; j < 8; j++) + crc32c_table[i] = (crc32c_table[i] << 1) ^ + (crc32c_table[i] & (1 << 31) ? polynomial : 0); + crc32c_table[i] = reflect(crc32c_table[i], 32); + } +} + +grub_uint32_t +grub_getcrc32c (grub_uint32_t crc, const void *buf, int size) +{ + int i; + const grub_uint8_t *data = buf; + + if (! crc32c_table[1]) + init_crc32c_table (); + + crc^= 0xffffffff; + + for (i = 0; i < size; i++) + { + crc = (crc >> 8) ^ crc32c_table[(crc & 0xFF) ^ *data]; + data++; + } + + return crc ^ 0xffffffff; +} diff --git a/include/grub/lib/crc.h b/include/grub/lib/crc.h new file mode 100644 index 000000000..c5098a8c3 --- /dev/null +++ b/include/grub/lib/crc.h @@ -0,0 +1,25 @@ +/* crc.h - prototypes for crc */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_CRC_H +#define GRUB_CRC_H 1 + +grub_uint32_t grub_getcrc32c (grub_uint32_t crc, const void *buf, int size); + +#endif /* ! GRUB_CRC_H */ From b18610feb5711ec84c10438d7712e63c41d09b50 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 01:23:47 +0100 Subject: [PATCH 02/34] partial btrfs support. Now able to list and access files as long as all trees are flat --- grub-core/fs/btrfs.c | 852 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 839 insertions(+), 13 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index a2ee485b4..780327702 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -24,39 +24,491 @@ #include #include #include +#include #define BTRFS_SIGNATURE "_BHRfS_M" +typedef grub_uint8_t btrfs_checksum_t[0x20]; +typedef grub_uint16_t btrfs_uuid_t[8]; + +struct grub_btrfs_device +{ + grub_uint64_t device_id; + grub_uint8_t dummy[0x62 - 8]; +} __attribute__ ((packed)); + struct btrfs_superblock { - grub_uint8_t dummy1[32]; - grub_uint16_t uuid[8]; - grub_uint8_t dummy2[16]; + btrfs_checksum_t checksum; + btrfs_uuid_t uuid; + grub_uint8_t dummy[0x10]; grub_uint8_t signature[sizeof (BTRFS_SIGNATURE) - 1]; + grub_uint64_t generation; + grub_uint64_t root_tree; + grub_uint64_t chunk_tree; + grub_uint8_t dummy2[0x20]; + grub_uint64_t root_dir_objectid; + grub_uint8_t dummy3[0x41]; + struct grub_btrfs_device this_device; + char label[0x100]; + grub_uint8_t dummy4[0x100]; + grub_uint8_t bootstrap_mapping[0x800]; +} __attribute__ ((packed)); + +struct btrfs_header +{ + btrfs_checksum_t checksum; + btrfs_uuid_t uuid; + grub_uint8_t dummy[0x30]; + grub_uint32_t nitems; + grub_uint8_t level; } __attribute__ ((packed)); struct grub_btrfs_data { struct btrfs_superblock sblock; + unsigned int sblock_number; + grub_uint64_t tree; + grub_uint64_t inode; }; +struct grub_btrfs_key +{ + grub_uint64_t object_id; +#define GRUB_BTRFS_ITEM_TYPE_INODE_ITEM 0x01 +#define GRUB_BTRFS_ITEM_TYPE_DIR_ITEM 0x54 +#define GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM 0x6c +#define GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM 0x84 +#define GRUB_BTRFS_ITEM_TYPE_DEVICE 0xd8 +#define GRUB_BTRFS_ITEM_TYPE_CHUNK 0xe4 + grub_uint8_t type; + grub_uint64_t offset; +} __attribute__ ((packed)); + +struct grub_btrfs_chunk_item +{ + grub_uint64_t size; + grub_uint64_t dummy; + grub_uint64_t stripe_length; + grub_uint8_t dummy2[0x14]; + grub_uint16_t nstripes; + grub_uint16_t dummy3; +} __attribute__ ((packed)); + +struct grub_btrfs_chunk_stripe +{ + grub_uint64_t device_id; + grub_uint64_t offset; + btrfs_uuid_t device_uuid; +} __attribute__ ((packed)); + +struct grub_btrfs_leaf_node +{ + struct grub_btrfs_key key; + grub_uint32_t offset; + grub_uint32_t size; +} __attribute__ ((packed)); + +struct grub_btrfs_dir_item +{ + struct grub_btrfs_key key; + grub_uint8_t dummy[8]; + grub_uint16_t m; + grub_uint16_t n; + grub_uint8_t type; + char name[0]; +} __attribute__ ((packed)); + +struct grub_btrfs_leaf_descriptor +{ + unsigned depth; + unsigned allocated; + struct { + grub_disk_addr_t addr; + unsigned iter; + unsigned maxiter; + int leaf; + } *data; +}; + +struct grub_btrfs_root_item +{ + grub_uint8_t dummy[0xb0]; + grub_uint64_t tree; + grub_uint64_t inode; +}; + +struct grub_btrfs_inode +{ + grub_uint8_t dummy[0x10]; + grub_uint64_t size; +} __attribute__ ((packed)); + +struct grub_btrfs_extent_data +{ + grub_uint64_t dummy; + grub_uint64_t size; + grub_uint8_t compression; + grub_uint8_t encryption; + grub_uint16_t encoding; + grub_uint8_t type; + union + { + char inl[0]; + grub_uint64_t laddr; + }; +} __attribute__ ((packed)); + +#define GRUB_BTRFS_EXTENT_INLINE 0 +#define GRUB_BTRFS_EXTENT_REGULAR 1 + + +#define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 + +static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, + 256 * 1048576 * 2, + 1048576ULL * 1048576ULL * 2 }; + +static grub_err_t +grub_btrfs_read_logical (struct grub_btrfs_data *data, + grub_disk_t disk, grub_disk_addr_t addr, + void *buf, grub_size_t size); + +static int +key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b) +{ + if (grub_cpu_to_le64 (a->object_id) < grub_cpu_to_le64 (b->object_id)) + return -1; + if (grub_cpu_to_le64 (a->object_id) > grub_cpu_to_le64 (b->object_id)) + return +1; + + if (a->type < b->type) + return -1; + if (a->type > b->type) + return +1; + + if (grub_cpu_to_le64 (a->offset) < grub_cpu_to_le64 (b->offset)) + return -1; + if (grub_cpu_to_le64 (a->offset) > grub_cpu_to_le64 (b->offset)) + return +1; + return 0; +} + +static void +free_iterator (struct grub_btrfs_leaf_descriptor *desc) +{ + grub_free (desc->data); +} + +static int +next (struct grub_btrfs_data *data, grub_disk_t disk, + struct grub_btrfs_leaf_descriptor *desc, + grub_disk_addr_t *outaddr, grub_size_t *outsize, + struct grub_btrfs_key *key_out) +{ + int i; + grub_err_t err; + struct grub_btrfs_leaf_node leaf; + + if (desc->depth == 0) + return 0; + for (i = desc->depth - 1; i >= 0; i--) + { + desc->data[i].iter++; + if (desc->data[i].iter + < desc->data[desc->depth - 1].maxiter) + break; + desc->depth--; + } + if (i == -1) + return 0; + while (!desc->data[desc->depth - 1].leaf) + { + grub_printf ("No trees\n"); + return -grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "no trees yet"); + } + err = grub_btrfs_read_logical (data, disk, + desc->data[desc->depth - 1].iter + * sizeof (leaf) + + sizeof (struct btrfs_header) + + desc->data[desc->depth - 1].addr, &leaf, + sizeof (leaf)); + if (err) + return -err; + *outsize = grub_le_to_cpu32 (leaf.size); + *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header) + + grub_le_to_cpu32 (leaf.offset); + *key_out = leaf.key; + return 1; +} + +static grub_err_t +save_ref (struct grub_btrfs_leaf_descriptor *desc, + grub_disk_addr_t addr, unsigned i, unsigned m, int l) +{ + desc->depth++; + if (desc->allocated > desc->depth) + { + void *newdata; + desc->allocated *= 2; + newdata = grub_realloc (desc->data, sizeof (desc->data[0]) + * desc->allocated); + if (!newdata) + return grub_errno; + } + desc->data[desc->depth - 1].addr = addr; + desc->data[desc->depth - 1].iter = i; + desc->data[desc->depth - 1].maxiter = m; + desc->data[desc->depth - 1].leaf = l; + return GRUB_ERR_NONE; +} + +static grub_err_t +lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, + const struct grub_btrfs_key *key_in, + struct grub_btrfs_key *key_out, + grub_disk_addr_t root, + grub_disk_addr_t *outaddr, grub_size_t *outsize, + struct grub_btrfs_leaf_descriptor *desc) +{ + grub_disk_addr_t addr = root; + struct btrfs_header head; + grub_err_t err; + unsigned i; + struct grub_btrfs_leaf_node leaf, leaf_last; + int have_last = 0; + + if (desc) + { + desc->allocated = 16; + desc->depth = 0; + desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated); + if (!desc->data) + return grub_errno; + } + + while (1) + { + /* FIXME: preread few leafs into buffer. */ + err = grub_btrfs_read_logical (data, disk, addr, &head, sizeof (head)); + if (err) + return err; + if (head.level) + { + grub_printf ("No trees\n"); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "trees aren't implemented yet"); + } + addr += sizeof (head); + for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) + { + err = grub_btrfs_read_logical (data, disk, addr + i * sizeof (leaf), + &leaf, sizeof (leaf)); + if (err) + return err; + + grub_dprintf ("btrfs", + "%" PRIxGRUB_UINT64_T " %x %" PRIxGRUB_UINT64_T "\n", + leaf.key.object_id, leaf.key.type, leaf.key.offset); + + if (key_cmp (&leaf.key, key_in) == 0) + { + grub_memcpy (key_out, &leaf.key, sizeof(*key_out)); + *outsize = grub_le_to_cpu32 (leaf.size); + *outaddr = addr + grub_le_to_cpu32 (leaf.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + + if (key_cmp (&leaf.key, key_in) > 0) + break; + + have_last = 1; + leaf_last = leaf; + } + + if (have_last) + { + grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out)); + *outsize = grub_le_to_cpu32 (leaf_last.size); + *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i - 1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + *outsize = 0; + *outaddr = 0; + grub_memset (key_out, 0, sizeof (*key_out)); + if (desc) + return save_ref (desc, addr - sizeof (head), -1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } +} + +static grub_err_t +grub_btrfs_read_logical (struct grub_btrfs_data *data, + grub_disk_t disk, grub_disk_addr_t addr, + void *buf, grub_size_t size) +{ + while (size > 0) + { + grub_uint8_t *ptr; + struct grub_btrfs_key *key; + struct grub_btrfs_chunk_item *chunk; + struct grub_btrfs_chunk_stripe *stripe; + grub_size_t csize; + grub_err_t err; + grub_disk_addr_t paddr; + grub_uint64_t stripen; + grub_uint32_t stripe_length; + grub_uint32_t stripe_offset; + struct grub_btrfs_key key_out; + int challoc = 0; + for (ptr = data->sblock.bootstrap_mapping; + ptr < data->sblock.bootstrap_mapping + + sizeof (data->sblock.bootstrap_mapping) + - sizeof (struct grub_btrfs_key); + ) + { + key = (struct grub_btrfs_key *) ptr; + if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK) + break; + chunk = (struct grub_btrfs_chunk_item *) (key + 1); + grub_dprintf ("btrfs", "%" PRIxGRUB_UINT64_T " %" PRIxGRUB_UINT64_T " \n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size)); + if (grub_le_to_cpu64 (key->offset) <= addr + && addr < grub_le_to_cpu64 (key->offset) + + grub_le_to_cpu64 (chunk->size)) + goto chunk_found; + ptr += sizeof (*key) + sizeof (*chunk) + + sizeof (*stripe) * grub_le_to_cpu16 (chunk->nstripes); + } + struct grub_btrfs_key key_in; + grub_size_t chsize; + grub_disk_addr_t chaddr; + key_in.object_id = GRUB_BTRFS_OBJECT_ID_CHUNK; + key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK; + key_in.offset = addr; + err = lower_bound (data, disk, + &key_in, &key_out, + grub_le_to_cpu64 (data->sblock.chunk_tree), + &chaddr, &chsize, NULL); + if (err) + return err; + key = &key_out; + if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK + || !(grub_le_to_cpu64 (key->offset) <= addr)) + return grub_error (GRUB_ERR_BAD_FS, + "couldn't find the chunk descriptor"); + + chunk = grub_malloc (chsize); + if (!chunk) + return grub_errno; + + challoc = 1; + err = grub_btrfs_read_logical (data, disk, chaddr, + chunk, chsize); + if (err) + { + grub_free (chunk); + return err; + } + + if (!(addr < grub_le_to_cpu64 (key->offset) + + grub_le_to_cpu64 (chunk->size))) + return grub_error (GRUB_ERR_BAD_FS, + "couldn't find the chunk descriptor"); + + chunk_found: + stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + NULL); + stripen = grub_divmod64 (addr - grub_le_to_cpu64 (key->offset), + stripe_length, &stripe_offset); + stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); + stripe += stripen; + csize = grub_le_to_cpu64 (key->offset) + grub_le_to_cpu64 (chunk->size) + - addr; + if (csize > size) + csize = size; + if (grub_le_to_cpu64 (stripe->device_id) != grub_le_to_cpu64 (data->sblock.this_device.device_id)) + { + if (challoc) + grub_free (chunk); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "multidevice isn't implemented yet"); + } + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T " (%d stripes of %" + PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T + " maps to 0x%" PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + grub_le_to_cpu64 (chunk->stripe_length), + stripen, + stripe->offset); + paddr = stripe->offset + stripe_offset; + + grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T + " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, + addr); + err = grub_disk_read (disk, paddr >> GRUB_DISK_SECTOR_BITS, + paddr & (GRUB_DISK_SECTOR_SIZE - 1), csize, buf); + size -= csize; + buf = (grub_uint8_t *) buf + csize; + addr += csize; + if (challoc) + grub_free (chunk); + } + return GRUB_ERR_NONE; +} + static struct grub_btrfs_data * grub_btrfs_mount (grub_disk_t disk) { struct grub_btrfs_data *data = grub_malloc (sizeof (*data)); + unsigned i; + grub_err_t err = GRUB_ERR_NONE; + if (! data) return NULL; - if (grub_disk_read (disk, 128, 0, sizeof (data->sblock), - &data->sblock) != GRUB_ERR_NONE) - goto fail; + for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) + { + struct btrfs_superblock sblock; + err = grub_disk_read (disk, superblock_sectors[i], 0, + sizeof (sblock), &sblock); + if (err == GRUB_ERR_OUT_OF_RANGE) + break; - if (grub_memcmp ((char *) data->sblock.signature, BTRFS_SIGNATURE, sizeof (BTRFS_SIGNATURE) - 1)) + if (grub_memcmp ((char *) sblock.signature, BTRFS_SIGNATURE, + sizeof (BTRFS_SIGNATURE) - 1)) + break; + if (i == 0 || grub_le_to_cpu64 (sblock.generation) + > grub_le_to_cpu64 (data->sblock.generation)) + { + grub_memcpy (&data->sblock, &sblock, sizeof (sblock)); + data->sblock_number = i; + } + } + + if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0) { grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem"); goto fail; } + if (err == GRUB_ERR_OUT_OF_RANGE) + grub_errno = err = GRUB_ERR_NONE; + + grub_dprintf ("btrfs", "using superblock %d\n", data->sblock_number); + return data; fail: @@ -65,28 +517,381 @@ grub_btrfs_mount (grub_disk_t disk) } static grub_err_t -grub_btrfs_open (struct grub_file *file __attribute__ ((unused)), - const char *name __attribute__ ((unused))) +find_path (struct grub_btrfs_data *data, + grub_disk_t disk, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree) { - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "only detection is supported for Btrfs"); + const char *slash; + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_key key_out; + + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + + while (1) + { + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, path, slash - path)); + + err = lower_bound (data, disk, key, &key_out, *tree, + &elemaddr, &elemsize, NULL); + if (err) + { + grub_free (direl); + return err; + } + if (key_cmp (key, &key_out) != 0) + { + grub_free (direl); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + } + + struct grub_btrfs_dir_item *cdirel; + if (elemsize > allocated) + { + allocated = 2 * elemsize; + grub_free (direl); + direl = grub_malloc (allocated + 1); + if (!direl) + return grub_errno; + } + + err = grub_btrfs_read_logical (data, disk, elemaddr, + direl, elemsize); + if (err) + { + grub_free (direl); + return err; + } + + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + char c; + c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; + if (grub_strncmp (cdirel->name, path, slash - path) == 0) + break; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + } + + path = slash; + + switch (cdirel->key.type) + { + case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM: + { + struct grub_btrfs_root_item ri; + err = lower_bound (data, disk, &cdirel->key, &key_out, *tree, + &elemaddr, &elemsize, NULL); + if (err) + return err; + if (cdirel->key.object_id != key_out.object_id + || cdirel->key.type != key_out.type) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + err = grub_btrfs_read_logical (data, disk, elemaddr, + &ri, sizeof (ri)); + if (err) + return err; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK; + *tree = grub_le_to_cpu64 (ri.tree); + break; + } + case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM: + if (*slash) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + *key = cdirel->key; + break; + default: + return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x", + cdirel->key.type); + } + } + + grub_free (direl); + + return GRUB_ERR_NONE; } static grub_err_t grub_btrfs_dir (grub_device_t device, const char *path __attribute__ ((unused)), int (*hook) (const char *filename, - const struct grub_dirhook_info *info) - __attribute__ ((unused))) + const struct grub_dirhook_info *info)) { struct grub_btrfs_data *data = grub_btrfs_mount (device->disk); - if (grub_errno) + struct grub_btrfs_key key_in, key_out; + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_leaf_descriptor desc; + int r; + grub_uint64_t tree; + + if (!data) return grub_errno; + err = find_path (data, device->disk, path, &key_in, &tree); + if (err) + return err; + + err = lower_bound (data, device->disk, &key_in, &key_out, + tree, + &elemaddr, &elemsize, &desc); + if (err) + return err; + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM + || key_out.object_id != key_in.object_id) + { + r = next (data, device->disk, &desc, &elemaddr, &elemsize, &key_out); + if (r <= 0) + { + free_iterator (&desc); + return -r; + } + } + do + { + struct grub_dirhook_info info; + struct grub_btrfs_dir_item *cdirel; + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM + || key_out.object_id != key_in.object_id) + { + r = 0; + break; + } + if (elemsize > allocated) + { + allocated = 2 * elemsize; + grub_free (direl); + direl = grub_malloc (allocated + 1); + if (!direl) + { + free_iterator (&desc); + return grub_errno; + } + } + + err = grub_btrfs_read_logical (data, device->disk, elemaddr, + direl, elemsize); + if (err) + return err; + + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + char c; + c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; + grub_memset (&info, 0, sizeof (info)); + info.dir = (cdirel->type == 2); + if (hook (cdirel->name, &info)) + goto out; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; + } + r = next (data, device->disk, &desc, &elemaddr, &elemsize, &key_out); + } + while (r > 0); + + out: + grub_free (direl); + + free_iterator (&desc); grub_free (data); + return -r; +} + +static grub_err_t +grub_btrfs_open (struct grub_file *file, const char *name) +{ + struct grub_btrfs_data *data = grub_btrfs_mount (file->device->disk); + struct grub_btrfs_key key_in, key_out; + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_inode inode; + + if (!data) + return grub_errno; + + err = find_path (data, file->device->disk, name, &key_in, &data->tree); + if (err) + { + grub_free (data); + return err; + } + data->inode = key_in.object_id; + key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; + + err = lower_bound (data, file->device->disk, &key_in, &key_out, + data->tree, + &elemaddr, &elemsize, NULL); + if (err) + return err; + if (data->inode != key_out.object_id + || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); + + err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, + &inode, sizeof (inode)); + if (err) + return err; + + file->data = data; + file->size = grub_le_to_cpu64 (inode.size); + return GRUB_ERR_NONE; } +static grub_err_t +grub_btrfs_close (grub_file_t file) +{ + grub_free (file->data); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_btrfs_data *data = file->data; + grub_off_t pos = file->offset; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key_in, key_out; + + while (len) + { + grub_size_t csize; + struct grub_btrfs_extent_data *extent; + grub_err_t err; + key_in.object_id = data->inode; + key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; + key_in.offset = grub_cpu_to_le64 (pos); + err = lower_bound (data, file->device->disk, &key_in, &key_out, + data->tree, + &elemaddr, &elemsize, NULL); + if (err) + return -1; + if (key_out.object_id != data->inode + || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + { + grub_error (GRUB_ERR_BAD_FS, "extent not found"); + grub_printf ("no extent\n"); + return -1; + } + extent = grub_malloc (elemsize); + if (!extent) + return grub_errno; + + err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, + extent, elemsize); + if (err) + { + grub_free (extent); + return err; + } + if (grub_le_to_cpu64 (extent->size) + grub_le_to_cpu64 (key_out.offset) + <= pos) + { + grub_free (extent); + return grub_error (GRUB_ERR_BAD_FS, "extent not found"); + } + grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" + PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key_out.offset), + grub_le_to_cpu64 (extent->size)); + csize = grub_le_to_cpu64 (extent->size) + + grub_le_to_cpu64 (key_out.offset) - pos; + if (csize > len) + csize = len; + + if (extent->encryption) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "encryption not supported"); + return -1; + } + + if (extent->compression) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression not supported"); + return -1; + } + + + if (extent->encoding) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "encoding not supported"); + return -1; + } + + switch (extent->type) + { + case GRUB_BTRFS_EXTENT_INLINE: + grub_memcpy (buf, extent->inl, csize); + grub_free (extent); + break; + case GRUB_BTRFS_EXTENT_REGULAR: + if (!extent->laddr) + { + grub_memset (buf, 0, csize); + break; + } + err = grub_btrfs_read_logical (data, file->device->disk, + grub_le_to_cpu64 (extent->laddr), + buf, csize); + grub_free (extent); + if (err) + return -1; + break; + default: + grub_free (extent); + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported extent type 0x%x\n", extent->type); + return -1; + } + buf += csize; + pos += csize; + len -= csize; + } + return pos - file->offset; +} + static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid) { @@ -113,12 +918,33 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) return grub_errno; } +static grub_err_t +grub_btrfs_label (grub_device_t device, char **label) +{ + struct grub_btrfs_data *data; + + *label = NULL; + + data = grub_btrfs_mount (device->disk); + if (! data) + return grub_errno; + + *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label)); + + grub_free (data); + + return grub_errno; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .dir = grub_btrfs_dir, .open = grub_btrfs_open, + .read = grub_btrfs_read, + .close = grub_btrfs_close, .uuid = grub_btrfs_uuid, + .label = grub_btrfs_label, }; GRUB_MOD_INIT(btrfs) From 355b3eed0ff8a8e90fb5f52c4e77809f6c0da922 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 16:22:51 +0100 Subject: [PATCH 03/34] support trees --- grub-core/fs/btrfs.c | 238 +++++++++++++++++++++++++++++-------------- 1 file changed, 161 insertions(+), 77 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 780327702..ff169e8c4 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -109,6 +109,13 @@ struct grub_btrfs_leaf_node grub_uint32_t size; } __attribute__ ((packed)); +struct grub_btrfs_internal_node +{ + struct grub_btrfs_key key; + grub_uint64_t blockn; + grub_uint64_t dummy; +} __attribute__ ((packed)); + struct grub_btrfs_dir_item { struct grub_btrfs_key key; @@ -200,6 +207,28 @@ free_iterator (struct grub_btrfs_leaf_descriptor *desc) grub_free (desc->data); } +static grub_err_t +save_ref (struct grub_btrfs_leaf_descriptor *desc, + grub_disk_addr_t addr, unsigned i, unsigned m, int l) +{ + desc->depth++; + if (desc->allocated > desc->depth) + { + void *newdata; + desc->allocated *= 2; + newdata = grub_realloc (desc->data, sizeof (desc->data[0]) + * desc->allocated); + if (!newdata) + return grub_errno; + desc->data = newdata; + } + desc->data[desc->depth - 1].addr = addr; + desc->data[desc->depth - 1].iter = i; + desc->data[desc->depth - 1].maxiter = m; + desc->data[desc->depth - 1].leaf = l; + return GRUB_ERR_NONE; +} + static int next (struct grub_btrfs_data *data, grub_disk_t disk, struct grub_btrfs_leaf_descriptor *desc, @@ -224,8 +253,26 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, return 0; while (!desc->data[desc->depth - 1].leaf) { - grub_printf ("No trees\n"); - return -grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "no trees yet"); + struct grub_btrfs_internal_node node; + struct btrfs_header head; + + err = grub_btrfs_read_logical (data, disk, + desc->data[desc->depth - 1].iter + * sizeof (node) + + sizeof (struct btrfs_header) + + desc->data[desc->depth - 1].addr, &node, + sizeof (node)); + if (err) + return -err; + + err = grub_btrfs_read_logical (data, disk, + grub_le_to_cpu64 (node.blockn), &head, + sizeof (head)); + if (err) + return -err; + + save_ref (desc, grub_le_to_cpu64 (node.blockn), 0, + grub_le_to_cpu32 (head.nitems), !head.level); } err = grub_btrfs_read_logical (data, disk, desc->data[desc->depth - 1].iter @@ -242,27 +289,6 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, return 1; } -static grub_err_t -save_ref (struct grub_btrfs_leaf_descriptor *desc, - grub_disk_addr_t addr, unsigned i, unsigned m, int l) -{ - desc->depth++; - if (desc->allocated > desc->depth) - { - void *newdata; - desc->allocated *= 2; - newdata = grub_realloc (desc->data, sizeof (desc->data[0]) - * desc->allocated); - if (!newdata) - return grub_errno; - } - desc->data[desc->depth - 1].addr = addr; - desc->data[desc->depth - 1].iter = i; - desc->data[desc->depth - 1].maxiter = m; - desc->data[desc->depth - 1].leaf = l; - return GRUB_ERR_NONE; -} - static grub_err_t lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, const struct grub_btrfs_key *key_in, @@ -272,11 +298,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, struct grub_btrfs_leaf_descriptor *desc) { grub_disk_addr_t addr = root; - struct btrfs_header head; - grub_err_t err; - unsigned i; - struct grub_btrfs_leaf_node leaf, leaf_last; - int have_last = 0; + int depth = -1; if (desc) { @@ -287,65 +309,128 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, return grub_errno; } + grub_dprintf ("btrfs", + "retrieving %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", + key_in->object_id, key_in->type, key_in->offset); + while (1) { - /* FIXME: preread few leafs into buffer. */ + grub_err_t err; + struct btrfs_header head; + + reiter: + depth++; + /* FIXME: preread few nodes into buffer. */ err = grub_btrfs_read_logical (data, disk, addr, &head, sizeof (head)); if (err) return err; + addr += sizeof (head); if (head.level) { - grub_printf ("No trees\n"); - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "trees aren't implemented yet"); - } - addr += sizeof (head); - for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) - { - err = grub_btrfs_read_logical (data, disk, addr + i * sizeof (leaf), - &leaf, sizeof (leaf)); - if (err) - return err; - - grub_dprintf ("btrfs", - "%" PRIxGRUB_UINT64_T " %x %" PRIxGRUB_UINT64_T "\n", - leaf.key.object_id, leaf.key.type, leaf.key.offset); - - if (key_cmp (&leaf.key, key_in) == 0) + unsigned i; + struct grub_btrfs_internal_node node, node_last; + int have_last = 0; + for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { - grub_memcpy (key_out, &leaf.key, sizeof(*key_out)); - *outsize = grub_le_to_cpu32 (leaf.size); - *outaddr = addr + grub_le_to_cpu32 (leaf.offset); - if (desc) - return save_ref (desc, addr - sizeof (head), i, - grub_le_to_cpu32 (head.nitems), 1); - return GRUB_ERR_NONE; + err = grub_btrfs_read_logical (data, disk, addr + + i * sizeof (node), + &node, sizeof (node)); + if (err) + return err; + + grub_dprintf ("btrfs", + "internal node (depth %d) %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", depth, + node.key.object_id, node.key.type, node.key.offset); + + if (key_cmp (&node.key, key_in) == 0) + { + err = GRUB_ERR_NONE; + if (desc) + err = save_ref (desc, addr - sizeof (head), i, + grub_le_to_cpu32 (head.nitems), 0); + if (err) + return err; + addr = grub_le_to_cpu64 (node.blockn); + goto reiter; + } + if (key_cmp (&node.key, key_in) > 0) + break; + node_last = node; + have_last = 1; } - - if (key_cmp (&leaf.key, key_in) > 0) - break; - - have_last = 1; - leaf_last = leaf; - } - - if (have_last) - { - grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out)); - *outsize = grub_le_to_cpu32 (leaf_last.size); - *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset); + if (have_last) + { + addr = grub_le_to_cpu64 (node_last.blockn); + err = GRUB_ERR_NONE; + if (desc) + err = save_ref (desc, addr - sizeof (head), i - 1, + grub_le_to_cpu32 (head.nitems), 0); + if (err) + return err; + goto reiter; + } + *outsize = 0; + *outaddr = 0; + grub_memset (key_out, 0, sizeof (*key_out)); if (desc) - return save_ref (desc, addr - sizeof (head), i - 1, - grub_le_to_cpu32 (head.nitems), 1); - return GRUB_ERR_NONE; + return save_ref (desc, addr - sizeof (head), -1, + grub_le_to_cpu32 (head.nitems), 0); + return GRUB_ERR_NONE; } - *outsize = 0; - *outaddr = 0; - grub_memset (key_out, 0, sizeof (*key_out)); - if (desc) - return save_ref (desc, addr - sizeof (head), -1, - grub_le_to_cpu32 (head.nitems), 1); - return GRUB_ERR_NONE; + { + unsigned i; + struct grub_btrfs_leaf_node leaf, leaf_last; + int have_last = 0; + for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) + { + err = grub_btrfs_read_logical (data, disk, addr + i * sizeof (leaf), + &leaf, sizeof (leaf)); + if (err) + return err; + + grub_dprintf ("btrfs", + "leaf (depth %d) %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", depth, + leaf.key.object_id, leaf.key.type, leaf.key.offset); + + if (key_cmp (&leaf.key, key_in) == 0) + { + grub_memcpy (key_out, &leaf.key, sizeof(*key_out)); + *outsize = grub_le_to_cpu32 (leaf.size); + *outaddr = addr + grub_le_to_cpu32 (leaf.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + + if (key_cmp (&leaf.key, key_in) > 0) + break; + + have_last = 1; + leaf_last = leaf; + } + + if (have_last) + { + grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out)); + *outsize = grub_le_to_cpu32 (leaf_last.size); + *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i - 1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + *outsize = 0; + *outaddr = 0; + grub_memset (key_out, 0, sizeof (*key_out)); + if (desc) + return save_ref (desc, addr - sizeof (head), -1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } } } @@ -809,7 +894,6 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) { grub_error (GRUB_ERR_BAD_FS, "extent not found"); - grub_printf ("no extent\n"); return -1; } extent = grub_malloc (elemsize); From df80cd06fb4308717d92186d9dbe7cd59bc6cbf0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 16:36:05 +0100 Subject: [PATCH 04/34] Check file type --- grub-core/fs/btrfs.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ff169e8c4..58639ff5d 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -122,6 +122,9 @@ struct grub_btrfs_dir_item grub_uint8_t dummy[8]; grub_uint16_t m; grub_uint16_t n; +#define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1 +#define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2 +#define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7 grub_uint8_t type; char name[0]; } __attribute__ ((packed)); @@ -605,7 +608,7 @@ static grub_err_t find_path (struct grub_btrfs_data *data, grub_disk_t disk, const char *path, struct grub_btrfs_key *key, - grub_uint64_t *tree) + grub_uint64_t *tree, grub_uint8_t *type) { const char *slash; grub_err_t err; @@ -615,17 +618,22 @@ find_path (struct grub_btrfs_data *data, struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *tree = data->sblock.root_tree; key->object_id = data->sblock.root_dir_objectid; while (1) { - key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; - key->offset = 0; while (path[0] == '/') path++; if (!path[0]) break; + + if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; slash = grub_strchr (path, '/'); if (!slash) slash = path + grub_strlen (path); @@ -684,6 +692,13 @@ find_path (struct grub_btrfs_data *data, } path = slash; + *type = cdirel->type; + if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) + { + grub_free (direl); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "symlinks not supported"); + } switch (cdirel->key.type) { @@ -739,13 +754,16 @@ grub_btrfs_dir (grub_device_t device, struct grub_btrfs_leaf_descriptor desc; int r; grub_uint64_t tree; + grub_uint8_t type; if (!data) return grub_errno; - err = find_path (data, device->disk, path, &key_in, &tree); + err = find_path (data, device->disk, path, &key_in, &tree, &type); if (err) return err; + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); err = lower_bound (data, device->disk, &key_in, &key_out, tree, @@ -800,7 +818,7 @@ grub_btrfs_dir (grub_device_t device, c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; grub_memset (&info, 0, sizeof (info)); - info.dir = (cdirel->type == 2); + info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY); if (hook (cdirel->name, &info)) goto out; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; @@ -827,16 +845,19 @@ grub_btrfs_open (struct grub_file *file, const char *name) grub_disk_addr_t elemaddr; grub_size_t elemsize; struct grub_btrfs_inode inode; + grub_uint8_t type; if (!data) return grub_errno; - err = find_path (data, file->device->disk, name, &key_in, &data->tree); + err = find_path (data, file->device->disk, name, &key_in, &data->tree, &type); if (err) { grub_free (data); return err; } + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); data->inode = key_in.object_id; key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; From d980826df2d5214eb3ee9092739b0766d7d822b7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 16:45:12 +0100 Subject: [PATCH 05/34] Remove \n from error message --- grub-core/fs/btrfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 58639ff5d..65d298e74 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -987,7 +987,7 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) default: grub_free (extent); grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported extent type 0x%x\n", extent->type); + "unsupported extent type 0x%x", extent->type); return -1; } buf += csize; From a3d1fcfb1047d9ccc9ca60027766ed886af52cad Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 22:59:51 +0100 Subject: [PATCH 06/34] Fix 2 warnings --- grub-core/fs/btrfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 65d298e74..2410b4550 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -334,6 +334,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, unsigned i; struct grub_btrfs_internal_node node, node_last; int have_last = 0; + grub_memset (node_last, 0, sizeof (node_last)); for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { err = grub_btrfs_read_logical (data, disk, addr @@ -752,7 +753,7 @@ grub_btrfs_dir (grub_device_t device, grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_leaf_descriptor desc; - int r; + int r = 0; grub_uint64_t tree; grub_uint8_t type; From ac5dcabe67536ffdb945fbf99426878e458a6ca6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Dec 2010 23:16:19 +0100 Subject: [PATCH 07/34] Fix incorrect statement from previous commit --- grub-core/fs/btrfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 2410b4550..67aa768ce 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -334,7 +334,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, unsigned i; struct grub_btrfs_internal_node node, node_last; int have_last = 0; - grub_memset (node_last, 0, sizeof (node_last)); + grub_memset (&node_last, 0, sizeof (node_last)); for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { err = grub_btrfs_read_logical (data, disk, addr From 1f60e3533163404e2b9e2dc0b98f42ab352b73ac Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 00:03:19 +0100 Subject: [PATCH 08/34] initialise the type of search for root --- grub-core/fs/btrfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 67aa768ce..6c6a6f9f8 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -622,6 +622,8 @@ find_path (struct grub_btrfs_data *data, *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *tree = data->sblock.root_tree; key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; while (1) { From 9b4cb862f889a922c23f68f89403b0565d66bbdc Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 00:11:14 +0100 Subject: [PATCH 09/34] handle directories correctly --- grub-core/fs/btrfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 6c6a6f9f8..1afd8fbb4 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -726,9 +726,11 @@ find_path (struct grub_btrfs_data *data, break; } case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM: - if (*slash) + if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); *key = cdirel->key; + if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; break; default: return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x", From 93e0c7a7c20dc469e3f30631fdf444d96f07d85b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 13:23:20 +0100 Subject: [PATCH 10/34] Fix subvolume handling --- grub-core/fs/btrfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 1afd8fbb4..7ad2d40ff 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -702,13 +702,14 @@ find_path (struct grub_btrfs_data *data, return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "symlinks not supported"); } - + switch (cdirel->key.type) { case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM: { struct grub_btrfs_root_item ri; - err = lower_bound (data, disk, &cdirel->key, &key_out, *tree, + err = lower_bound (data, disk, &cdirel->key, &key_out, + data->sblock.root_tree, &elemaddr, &elemsize, NULL); if (err) return err; From d9865a25f7416678d78b1629444f2d674ff0466f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 13:31:50 +0100 Subject: [PATCH 11/34] Implicitly skip /default prefix --- grub-core/fs/btrfs.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 7ad2d40ff..dd9e41149 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -611,13 +611,16 @@ find_path (struct grub_btrfs_data *data, const char *path, struct grub_btrfs_key *key, grub_uint64_t *tree, grub_uint8_t *type) { - const char *slash; + const char *slash = path; grub_err_t err; grub_disk_addr_t elemaddr; grub_size_t elemsize; grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; + int skip_default = 1; + const char *ctoken; + grub_size_t ctokenlen; *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *tree = data->sblock.root_tree; @@ -627,20 +630,29 @@ find_path (struct grub_btrfs_data *data, while (1) { - while (path[0] == '/') - path++; - if (!path[0]) - break; + if (!skip_default) + { + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + ctoken = path; + ctokenlen = slash - path; + } + else + { + ctoken = "default"; + ctokenlen = sizeof ("default") - 1; + } if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; - key->offset = 0; - slash = grub_strchr (path, '/'); - if (!slash) - slash = path + grub_strlen (path); - key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, path, slash - path)); + key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); err = lower_bound (data, disk, key, &key_out, *tree, &elemaddr, &elemsize, NULL); @@ -683,7 +695,7 @@ find_path (struct grub_btrfs_data *data, char c; c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; - if (grub_strncmp (cdirel->name, path, slash - path) == 0) + if (grub_strncmp (cdirel->name, ctoken, ctokenlen) == 0) break; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; } @@ -694,7 +706,9 @@ find_path (struct grub_btrfs_data *data, return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); } - path = slash; + if (!skip_default) + path = slash; + skip_default = 0; *type = cdirel->type; if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) { From eb82b8569ad2e9ffff50669c253c6bda1724b40e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 13:32:24 +0100 Subject: [PATCH 12/34] Remove leftover unused attribute --- grub-core/fs/btrfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index dd9e41149..052ef1a93 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -759,8 +759,7 @@ find_path (struct grub_btrfs_data *data, } static grub_err_t -grub_btrfs_dir (grub_device_t device, - const char *path __attribute__ ((unused)), +grub_btrfs_dir (grub_device_t device, const char *path, int (*hook) (const char *filename, const struct grub_dirhook_info *info)) { From 98042add0cded2addc7658afb86beef7535d4910 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 13:57:07 +0100 Subject: [PATCH 13/34] Fix handling of non-leaf next --- grub-core/fs/btrfs.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 052ef1a93..d508d75b9 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -215,7 +215,7 @@ save_ref (struct grub_btrfs_leaf_descriptor *desc, grub_disk_addr_t addr, unsigned i, unsigned m, int l) { desc->depth++; - if (desc->allocated > desc->depth) + if (desc->allocated < desc->depth) { void *newdata; desc->allocated *= 2; @@ -238,21 +238,17 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, grub_disk_addr_t *outaddr, grub_size_t *outsize, struct grub_btrfs_key *key_out) { - int i; grub_err_t err; struct grub_btrfs_leaf_node leaf; - if (desc->depth == 0) - return 0; - for (i = desc->depth - 1; i >= 0; i--) + for (; desc->depth > 0; desc->depth--) { - desc->data[i].iter++; - if (desc->data[i].iter + desc->data[desc->depth - 1].iter++; + if (desc->data[desc->depth - 1].iter < desc->data[desc->depth - 1].maxiter) break; - desc->depth--; } - if (i == -1) + if (desc->depth == 0) return 0; while (!desc->data[desc->depth - 1].leaf) { @@ -366,13 +362,13 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, } if (have_last) { - addr = grub_le_to_cpu64 (node_last.blockn); err = GRUB_ERR_NONE; if (desc) err = save_ref (desc, addr - sizeof (head), i - 1, grub_le_to_cpu32 (head.nitems), 0); if (err) return err; + addr = grub_le_to_cpu64 (node_last.blockn); goto reiter; } *outsize = 0; From 228cfb40bf9f6fa70cdb6c8aa7304efe9f558870 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 14:26:46 +0100 Subject: [PATCH 14/34] support bind and subvolume mount --- grub-core/kern/emu/getroot.c | 23 ++++++++--------------- grub-core/kern/emu/misc.c | 28 ++++++++++++++++++++++++++++ include/grub/emu/misc.h | 2 ++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index f51dcd770..373834127 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -103,8 +103,8 @@ xgetcwd (void) can't deal with the multiple-device case yet, but in the meantime, we can at least cope with the single-device case by scanning /proc/self/mountinfo. */ -static char * -find_root_device_from_mountinfo (const char *dir) +char * +grub_find_root_device_from_mountinfo (const char *dir, char **relroot) { FILE *fp; char *buf = NULL; @@ -115,6 +115,9 @@ find_root_device_from_mountinfo (const char *dir) if (! fp) return NULL; /* fall through to other methods */ + if (relroot) + *relroot = NULL; + while (getline (&buf, &len, fp) > 0) { int mnt_id, parent_mnt_id; @@ -131,9 +134,6 @@ find_root_device_from_mountinfo (const char *dir) &count) < 6) continue; - if (strcmp (enc_root, "/") != 0) - continue; /* only a subtree is mounted */ - enc_path_len = strlen (enc_path); if (strncmp (dir, enc_path, enc_path_len) != 0 || (dir[enc_path_len] && dir[enc_path_len] != '/')) @@ -147,9 +147,6 @@ find_root_device_from_mountinfo (const char *dir) free (ret); ret = NULL; - if (major != 0) - continue; /* not a virtual device */ - sep = strstr (buf + count, " - "); if (!sep) continue; @@ -158,13 +155,9 @@ find_root_device_from_mountinfo (const char *dir) if (sscanf (sep, "%s %s", fstype, device) != 2) continue; - if (stat (device, &st) < 0) - continue; - - if (!S_ISBLK (st.st_mode)) - continue; /* not a block device */ - ret = strdup (device); + if (relroot) + *relroot = strdup (enc_root); } free (buf); @@ -531,7 +524,7 @@ grub_guess_root_device (const char *dir) struct stat st; #ifdef __linux__ - os_dev = find_root_device_from_mountinfo (dir); + os_dev = grub_find_root_device_from_mountinfo (dir, NULL); if (os_dev) return os_dev; #endif /* __linux__ */ diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index c8b95443b..44c40b010 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -415,6 +415,18 @@ grub_make_system_path_relative_to_its_root (const char *path) if (offset == 0) { free (buf); +#ifdef __linux__ + { + char *bind; + grub_free (grub_find_root_device_from_mountinfo (buf2, &bind)); + if (bind && bind[0] && bind[1]) + { + buf3 = bind; + goto parsedir; + } + grub_free (bind); + } +#endif free (buf2); #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) if (poolfs) @@ -437,6 +449,21 @@ grub_make_system_path_relative_to_its_root (const char *path) } free (buf); buf3 = xstrdup (buf2 + offset); + buf2[offset] = 0; +#ifdef __linux__ + { + char *bind; + grub_free (grub_find_root_device_from_mountinfo (buf2, &bind)); + if (bind && bind[0] && bind[1]) + { + char *temp = buf3; + buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3); + grub_free (temp); + } + grub_free (bind); + } +#endif + free (buf2); #ifdef __CYGWIN__ @@ -452,6 +479,7 @@ grub_make_system_path_relative_to_its_root (const char *path) } #endif + parsedir: /* Remove trailing slashes, return empty string if root directory. */ len = strlen (buf3); while (len > 0 && buf3[len - 1] == '/') diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index ef0d18300..d4ebcb0d1 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -78,4 +78,6 @@ extern char * canonicalize_file_name (const char *path); int grub_device_mapper_supported (void); #endif +char *grub_find_root_device_from_mountinfo (const char *dir, char **relroot); + #endif /* GRUB_EMU_MISC_H */ From 8006f6779eed5e840690d86d997bd5dc211713f3 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 15:08:46 +0100 Subject: [PATCH 15/34] Fix in-extent reading --- grub-core/fs/btrfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index d508d75b9..ac61452db 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -912,6 +912,7 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) grub_disk_addr_t elemaddr; grub_size_t elemsize; struct grub_btrfs_key key_in, key_out; + grub_off_t extoff; while (len) { @@ -955,6 +956,7 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) grub_le_to_cpu64 (extent->size)); csize = grub_le_to_cpu64 (extent->size) + grub_le_to_cpu64 (key_out.offset) - pos; + extoff = pos - grub_le_to_cpu64 (key_out.offset); if (csize > len) csize = len; @@ -983,7 +985,7 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) switch (extent->type) { case GRUB_BTRFS_EXTENT_INLINE: - grub_memcpy (buf, extent->inl, csize); + grub_memcpy (buf, extent->inl + extoff, csize); grub_free (extent); break; case GRUB_BTRFS_EXTENT_REGULAR: @@ -993,7 +995,8 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) break; } err = grub_btrfs_read_logical (data, file->device->disk, - grub_le_to_cpu64 (extent->laddr), + grub_le_to_cpu64 (extent->laddr) + + extoff, buf, csize); grub_free (extent); if (err) From a43c4bc55f1df3fb5792548bd3045c184f57ab1e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 2 Dec 2010 15:28:29 +0100 Subject: [PATCH 16/34] buffer extent for performance --- grub-core/fs/btrfs.c | 106 +++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ac61452db..2e32248b4 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -70,6 +70,8 @@ struct grub_btrfs_data unsigned int sblock_number; grub_uint64_t tree; grub_uint64_t inode; + grub_uint64_t extstart; + struct grub_btrfs_extent_data *extent; }; struct grub_btrfs_key @@ -557,7 +559,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, static struct grub_btrfs_data * grub_btrfs_mount (grub_disk_t disk) { - struct grub_btrfs_data *data = grub_malloc (sizeof (*data)); + struct grub_btrfs_data *data = grub_zalloc (sizeof (*data)); unsigned i; grub_err_t err = GRUB_ERR_NONE; @@ -899,7 +901,10 @@ grub_btrfs_open (struct grub_file *file, const char *name) static grub_err_t grub_btrfs_close (grub_file_t file) { - grub_free (file->data); + struct grub_btrfs_data *data = file->data; + + grub_free (data->extent); + grub_free (data); return GRUB_ERR_NONE; } @@ -909,65 +914,63 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_btrfs_data *data = file->data; grub_off_t pos = file->offset; - grub_disk_addr_t elemaddr; - grub_size_t elemsize; - struct grub_btrfs_key key_in, key_out; - grub_off_t extoff; while (len) { grub_size_t csize; - struct grub_btrfs_extent_data *extent; grub_err_t err; - key_in.object_id = data->inode; - key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; - key_in.offset = grub_cpu_to_le64 (pos); - err = lower_bound (data, file->device->disk, &key_in, &key_out, - data->tree, - &elemaddr, &elemsize, NULL); - if (err) - return -1; - if (key_out.object_id != data->inode - || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + grub_off_t extoff; + if (!data->extent || data->extstart > pos || + grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) { - grub_error (GRUB_ERR_BAD_FS, "extent not found"); - return -1; - } - extent = grub_malloc (elemsize); - if (!extent) - return grub_errno; + struct grub_btrfs_key key_in, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_free (data->extent); + key_in.object_id = data->inode; + key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; + key_in.offset = grub_cpu_to_le64 (pos); + err = lower_bound (data, file->device->disk, &key_in, &key_out, + data->tree, + &elemaddr, &elemsize, NULL); + if (err) + return -1; + if (key_out.object_id != data->inode + || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + { + grub_error (GRUB_ERR_BAD_FS, "extent not found"); + return -1; + } + data->extstart = grub_le_to_cpu64 (key_out.offset); + data->extent = grub_malloc (elemsize); + if (!data->extent) + return grub_errno; - err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, - extent, elemsize); - if (err) - { - grub_free (extent); - return err; + err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, + data->extent, elemsize); + if (err) + return err; + if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) + return grub_error (GRUB_ERR_BAD_FS, "extent not found"); + grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" + PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key_out.offset), + grub_le_to_cpu64 (data->extent->size)); } - if (grub_le_to_cpu64 (extent->size) + grub_le_to_cpu64 (key_out.offset) - <= pos) - { - grub_free (extent); - return grub_error (GRUB_ERR_BAD_FS, "extent not found"); - } - grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" - PRIxGRUB_UINT64_T "\n", - grub_le_to_cpu64 (key_out.offset), - grub_le_to_cpu64 (extent->size)); - csize = grub_le_to_cpu64 (extent->size) - + grub_le_to_cpu64 (key_out.offset) - pos; - extoff = pos - grub_le_to_cpu64 (key_out.offset); + csize = grub_le_to_cpu64 (data->extent->size) + + grub_le_to_cpu64 (data->extstart) - pos; + extoff = pos - data->extstart; if (csize > len) csize = len; - if (extent->encryption) + if (data->extent->encryption) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "encryption not supported"); return -1; } - if (extent->compression) + if (data->extent->compression) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "compression not supported"); @@ -975,37 +978,34 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) } - if (extent->encoding) + if (data->extent->encoding) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "encoding not supported"); return -1; } - switch (extent->type) + switch (data->extent->type) { case GRUB_BTRFS_EXTENT_INLINE: - grub_memcpy (buf, extent->inl + extoff, csize); - grub_free (extent); + grub_memcpy (buf, data->extent->inl + extoff, csize); break; case GRUB_BTRFS_EXTENT_REGULAR: - if (!extent->laddr) + if (!data->extent->laddr) { grub_memset (buf, 0, csize); break; } err = grub_btrfs_read_logical (data, file->device->disk, - grub_le_to_cpu64 (extent->laddr) + grub_le_to_cpu64 (data->extent->laddr) + extoff, buf, csize); - grub_free (extent); if (err) return -1; break; default: - grub_free (extent); grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported extent type 0x%x", extent->type); + "unsupported extent type 0x%x", data->extent->type); return -1; } buf += csize; From 34018a7d1fafb91778195fc6aa6970b9aac3ba19 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 10:44:47 +0100 Subject: [PATCH 17/34] symlink support --- grub-core/fs/btrfs.c | 367 +++++++++++++++++++++++++++---------------- 1 file changed, 235 insertions(+), 132 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 2e32248b4..5046b8a28 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -70,7 +70,11 @@ struct grub_btrfs_data unsigned int sblock_number; grub_uint64_t tree; grub_uint64_t inode; + + /* Cached extent data. */ grub_uint64_t extstart; + grub_uint64_t extino; + grub_uint64_t exttree; struct grub_btrfs_extent_data *extent; }; @@ -603,6 +607,139 @@ grub_btrfs_mount (grub_disk_t disk) return NULL; } +static grub_err_t +grub_btrfs_read_inode (struct grub_btrfs_data *data, grub_disk_t disk, + struct grub_btrfs_inode *inode, grub_uint64_t num, + grub_uint64_t tree) +{ + struct grub_btrfs_key key_in, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_err_t err; + + key_in.object_id = num; + key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; + key_in.offset = 0; + + err = lower_bound (data,disk, &key_in, &key_out, tree, + &elemaddr, &elemsize, NULL); + if (err) + return err; + if (num != key_out.object_id + || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM) + return grub_error (GRUB_ERR_BAD_FS, "inode not found"); + + return grub_btrfs_read_logical (data, disk, elemaddr, inode, sizeof (*inode)); +} + +static grub_ssize_t +grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, + grub_uint64_t ino, grub_uint64_t tree, + grub_off_t pos0, char *buf, grub_size_t len) +{ + grub_off_t pos = pos0; + while (len) + { + grub_size_t csize; + grub_err_t err; + grub_off_t extoff; + if (!data->extent || data->extstart > pos || data->extino != ino + || data->exttree != tree + || grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) + { + struct grub_btrfs_key key_in, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_free (data->extent); + key_in.object_id = ino; + key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; + key_in.offset = grub_cpu_to_le64 (pos); + err = lower_bound (data, disk, &key_in, &key_out, tree, + &elemaddr, &elemsize, NULL); + if (err) + return -1; + if (key_out.object_id != ino + || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + { + grub_error (GRUB_ERR_BAD_FS, "extent not found"); + return -1; + } + data->extstart = grub_le_to_cpu64 (key_out.offset); + data->extent = grub_malloc (elemsize); + data->extino = ino; + data->exttree = tree; + if (!data->extent) + return grub_errno; + + err = grub_btrfs_read_logical (data, disk, elemaddr, + data->extent, elemsize); + if (err) + return err; + if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) + return grub_error (GRUB_ERR_BAD_FS, "extent not found"); + grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" + PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key_out.offset), + grub_le_to_cpu64 (data->extent->size)); + } + csize = grub_le_to_cpu64 (data->extent->size) + + grub_le_to_cpu64 (data->extstart) - pos; + extoff = pos - data->extstart; + if (csize > len) + csize = len; + + if (data->extent->encryption) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "encryption not supported"); + return -1; + } + + if (data->extent->compression) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression not supported"); + return -1; + } + + + if (data->extent->encoding) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "encoding not supported"); + return -1; + } + + switch (data->extent->type) + { + case GRUB_BTRFS_EXTENT_INLINE: + grub_memcpy (buf, data->extent->inl + extoff, csize); + break; + case GRUB_BTRFS_EXTENT_REGULAR: + if (!data->extent->laddr) + { + grub_memset (buf, 0, csize); + break; + } + err = grub_btrfs_read_logical (data, disk, + grub_le_to_cpu64 (data->extent->laddr) + + extoff, + buf, csize); + if (err) + return -1; + break; + default: + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported extent type 0x%x", data->extent->type); + return -1; + } + buf += csize; + pos += csize; + len -= csize; + } + return pos - pos0; +} + static grub_err_t find_path (struct grub_btrfs_data *data, grub_disk_t disk, @@ -616,15 +753,17 @@ find_path (struct grub_btrfs_data *data, grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; - int skip_default = 1; + int skip_default; const char *ctoken; grub_size_t ctokenlen; + char *path_alloc = NULL; *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *tree = data->sblock.root_tree; key->object_id = data->sblock.root_dir_objectid; key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; key->offset = 0; + skip_default = 1; while (1) { @@ -647,7 +786,10 @@ find_path (struct grub_btrfs_data *data, } if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + { + grub_free (path_alloc); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + } key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); @@ -657,11 +799,13 @@ find_path (struct grub_btrfs_data *data, if (err) { grub_free (direl); + grub_free (path_alloc); return err; } if (key_cmp (key, &key_out) != 0) { grub_free (direl); + grub_free (path_alloc); return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); } @@ -672,7 +816,10 @@ find_path (struct grub_btrfs_data *data, grub_free (direl); direl = grub_malloc (allocated + 1); if (!direl) - return grub_errno; + { + grub_free (path_alloc); + return grub_errno; + } } err = grub_btrfs_read_logical (data, disk, elemaddr, @@ -680,6 +827,7 @@ find_path (struct grub_btrfs_data *data, if (err) { grub_free (direl); + grub_free (path_alloc); return err; } @@ -701,19 +849,60 @@ find_path (struct grub_btrfs_data *data, >= (grub_ssize_t) elemsize) { grub_free (direl); + grub_free (path_alloc); return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); } if (!skip_default) path = slash; skip_default = 0; - *type = cdirel->type; - if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) + if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) { - grub_free (direl); - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "symlinks not supported"); + struct grub_btrfs_inode inode; + char *tmp; + err = grub_btrfs_read_inode (data, disk, &inode, + cdirel->key.object_id, *tree); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + return err; + } + tmp = grub_malloc (grub_le_to_cpu64 (inode.size) + + grub_strlen (path) + 1); + if (!tmp) + { + grub_free (direl); + grub_free (path_alloc); + return grub_errno; + } + + if (grub_btrfs_extent_read (data, disk, cdirel->key.object_id, + *tree, 0, tmp, + grub_le_to_cpu64 (inode.size)) + != (grub_ssize_t) grub_le_to_cpu64 (inode.size)) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (tmp); + return grub_errno; + } + grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path, + grub_strlen (path) + 1); + grub_free (path_alloc); + path = path_alloc = tmp; + if (path[0] == '/') + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + skip_default = 1; + } + continue; } + *type = cdirel->type; switch (cdirel->key.type) { @@ -724,14 +913,26 @@ find_path (struct grub_btrfs_data *data, data->sblock.root_tree, &elemaddr, &elemsize, NULL); if (err) - return err; + { + grub_free (direl); + grub_free (path_alloc); + return err; + } if (cdirel->key.object_id != key_out.object_id || cdirel->key.type != key_out.type) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + { + grub_free (direl); + grub_free (path_alloc); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + } err = grub_btrfs_read_logical (data, disk, elemaddr, &ri, sizeof (ri)); if (err) - return err; + { + grub_free (direl); + grub_free (path_alloc); + return err; + } key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; key->offset = 0; key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK; @@ -740,19 +941,24 @@ find_path (struct grub_btrfs_data *data, } case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM: if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + { + grub_free (direl); + grub_free (path_alloc); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + } *key = cdirel->key; if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) - key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; break; default: + grub_free (path_alloc); + grub_free (direl); return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x", cdirel->key.type); } } grub_free (direl); - return GRUB_ERR_NONE; } @@ -857,12 +1063,10 @@ static grub_err_t grub_btrfs_open (struct grub_file *file, const char *name) { struct grub_btrfs_data *data = grub_btrfs_mount (file->device->disk); - struct grub_btrfs_key key_in, key_out; grub_err_t err; - grub_disk_addr_t elemaddr; - grub_size_t elemsize; struct grub_btrfs_inode inode; grub_uint8_t type; + struct grub_btrfs_key key_in; if (!data) return grub_errno; @@ -874,28 +1078,24 @@ grub_btrfs_open (struct grub_file *file, const char *name) return err; } if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); + { + grub_free (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); + } + data->inode = key_in.object_id; - key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; - - err = lower_bound (data, file->device->disk, &key_in, &key_out, - data->tree, - &elemaddr, &elemsize, NULL); + err = grub_btrfs_read_inode (data, file->device->disk, &inode, data->inode, + data->tree); if (err) - return err; - if (data->inode != key_out.object_id - || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); - - err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, - &inode, sizeof (inode)); - if (err) - return err; + { + grub_free (data); + return err; + } file->data = data; file->size = grub_le_to_cpu64 (inode.size); - return GRUB_ERR_NONE; + return err; } static grub_err_t @@ -913,106 +1113,9 @@ static grub_ssize_t grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_btrfs_data *data = file->data; - grub_off_t pos = file->offset; - while (len) - { - grub_size_t csize; - grub_err_t err; - grub_off_t extoff; - if (!data->extent || data->extstart > pos || - grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) - { - struct grub_btrfs_key key_in, key_out; - grub_disk_addr_t elemaddr; - grub_size_t elemsize; - grub_free (data->extent); - key_in.object_id = data->inode; - key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; - key_in.offset = grub_cpu_to_le64 (pos); - err = lower_bound (data, file->device->disk, &key_in, &key_out, - data->tree, - &elemaddr, &elemsize, NULL); - if (err) - return -1; - if (key_out.object_id != data->inode - || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) - { - grub_error (GRUB_ERR_BAD_FS, "extent not found"); - return -1; - } - data->extstart = grub_le_to_cpu64 (key_out.offset); - data->extent = grub_malloc (elemsize); - if (!data->extent) - return grub_errno; - - err = grub_btrfs_read_logical (data, file->device->disk, elemaddr, - data->extent, elemsize); - if (err) - return err; - if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) - return grub_error (GRUB_ERR_BAD_FS, "extent not found"); - grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" - PRIxGRUB_UINT64_T "\n", - grub_le_to_cpu64 (key_out.offset), - grub_le_to_cpu64 (data->extent->size)); - } - csize = grub_le_to_cpu64 (data->extent->size) - + grub_le_to_cpu64 (data->extstart) - pos; - extoff = pos - data->extstart; - if (csize > len) - csize = len; - - if (data->extent->encryption) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "encryption not supported"); - return -1; - } - - if (data->extent->compression) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "compression not supported"); - return -1; - } - - - if (data->extent->encoding) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "encoding not supported"); - return -1; - } - - switch (data->extent->type) - { - case GRUB_BTRFS_EXTENT_INLINE: - grub_memcpy (buf, data->extent->inl + extoff, csize); - break; - case GRUB_BTRFS_EXTENT_REGULAR: - if (!data->extent->laddr) - { - grub_memset (buf, 0, csize); - break; - } - err = grub_btrfs_read_logical (data, file->device->disk, - grub_le_to_cpu64 (data->extent->laddr) - + extoff, - buf, csize); - if (err) - return -1; - break; - default: - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported extent type 0x%x", data->extent->type); - return -1; - } - buf += csize; - pos += csize; - len -= csize; - } - return pos - file->offset; + return grub_btrfs_extent_read (data, file->device->disk, data->inode, + data->tree, file->offset, buf, len); } static grub_err_t From 0e761d3dbd9b20032dcde44b3caf0ce3dcbab191 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 11:30:24 +0100 Subject: [PATCH 18/34] Rename some btrfs variables for more uniformity --- grub-core/fs/btrfs.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 5046b8a28..ad7b10826 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -26,10 +26,10 @@ #include #include -#define BTRFS_SIGNATURE "_BHRfS_M" +#define GRUB_BTRFS_SIGNATURE "_BHRfS_M" -typedef grub_uint8_t btrfs_checksum_t[0x20]; -typedef grub_uint16_t btrfs_uuid_t[8]; +typedef grub_uint8_t grub_btrfs_checksum_t[0x20]; +typedef grub_uint16_t grub_btrfs_uuid_t[8]; struct grub_btrfs_device { @@ -37,12 +37,12 @@ struct grub_btrfs_device grub_uint8_t dummy[0x62 - 8]; } __attribute__ ((packed)); -struct btrfs_superblock +struct grub_btrfs_superblock { - btrfs_checksum_t checksum; - btrfs_uuid_t uuid; + grub_btrfs_checksum_t checksum; + grub_btrfs_uuid_t uuid; grub_uint8_t dummy[0x10]; - grub_uint8_t signature[sizeof (BTRFS_SIGNATURE) - 1]; + grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1]; grub_uint64_t generation; grub_uint64_t root_tree; grub_uint64_t chunk_tree; @@ -57,8 +57,8 @@ struct btrfs_superblock struct btrfs_header { - btrfs_checksum_t checksum; - btrfs_uuid_t uuid; + grub_btrfs_checksum_t checksum; + grub_btrfs_uuid_t uuid; grub_uint8_t dummy[0x30]; grub_uint32_t nitems; grub_uint8_t level; @@ -66,7 +66,7 @@ struct btrfs_header struct grub_btrfs_data { - struct btrfs_superblock sblock; + struct grub_btrfs_superblock sblock; unsigned int sblock_number; grub_uint64_t tree; grub_uint64_t inode; @@ -105,7 +105,7 @@ struct grub_btrfs_chunk_stripe { grub_uint64_t device_id; grub_uint64_t offset; - btrfs_uuid_t device_uuid; + grub_btrfs_uuid_t device_uuid; } __attribute__ ((packed)); struct grub_btrfs_leaf_node @@ -118,7 +118,7 @@ struct grub_btrfs_leaf_node struct grub_btrfs_internal_node { struct grub_btrfs_key key; - grub_uint64_t blockn; + grub_uint64_t addr; grub_uint64_t dummy; } __attribute__ ((packed)); @@ -271,12 +271,12 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, return -err; err = grub_btrfs_read_logical (data, disk, - grub_le_to_cpu64 (node.blockn), &head, + grub_le_to_cpu64 (node.addr), &head, sizeof (head)); if (err) return -err; - save_ref (desc, grub_le_to_cpu64 (node.blockn), 0, + save_ref (desc, grub_le_to_cpu64 (node.addr), 0, grub_le_to_cpu32 (head.nitems), !head.level); } err = grub_btrfs_read_logical (data, disk, @@ -358,7 +358,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, grub_le_to_cpu32 (head.nitems), 0); if (err) return err; - addr = grub_le_to_cpu64 (node.blockn); + addr = grub_le_to_cpu64 (node.addr); goto reiter; } if (key_cmp (&node.key, key_in) > 0) @@ -374,7 +374,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, grub_le_to_cpu32 (head.nitems), 0); if (err) return err; - addr = grub_le_to_cpu64 (node_last.blockn); + addr = grub_le_to_cpu64 (node_last.addr); goto reiter; } *outsize = 0; @@ -572,14 +572,14 @@ grub_btrfs_mount (grub_disk_t disk) for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) { - struct btrfs_superblock sblock; + struct grub_btrfs_superblock sblock; err = grub_disk_read (disk, superblock_sectors[i], 0, sizeof (sblock), &sblock); if (err == GRUB_ERR_OUT_OF_RANGE) break; - if (grub_memcmp ((char *) sblock.signature, BTRFS_SIGNATURE, - sizeof (BTRFS_SIGNATURE) - 1)) + if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE, + sizeof (GRUB_BTRFS_SIGNATURE) - 1)) break; if (i == 0 || grub_le_to_cpu64 (sblock.generation) > grub_le_to_cpu64 (data->sblock.generation)) From db51e201fc37f0d2b16d7b4748a9c3c9950e7389 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 16:56:49 +0100 Subject: [PATCH 19/34] symlink loop detection. btrfs-raid0 and raid1 support --- grub-core/fs/btrfs.c | 432 +++++++++++++++++++++++++++++-------------- 1 file changed, 295 insertions(+), 137 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ad7b10826..f8fbf95e9 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -49,7 +49,7 @@ struct grub_btrfs_superblock grub_uint8_t dummy2[0x20]; grub_uint64_t root_dir_objectid; grub_uint8_t dummy3[0x41]; - struct grub_btrfs_device this_device; + struct grub_btrfs_device this_device; char label[0x100]; grub_uint8_t dummy4[0x100]; grub_uint8_t bootstrap_mapping[0x800]; @@ -64,13 +64,22 @@ struct btrfs_header grub_uint8_t level; } __attribute__ ((packed)); +struct grub_btrfs_device_desc +{ + grub_device_t dev; + grub_uint64_t id; +}; + struct grub_btrfs_data { struct grub_btrfs_superblock sblock; - unsigned int sblock_number; grub_uint64_t tree; grub_uint64_t inode; + struct grub_btrfs_device_desc *devices_attached; + unsigned n_devices_attached; + unsigned n_devices_allocated; + /* Cached extent data. */ grub_uint64_t extstart; grub_uint64_t extino; @@ -96,9 +105,15 @@ struct grub_btrfs_chunk_item grub_uint64_t size; grub_uint64_t dummy; grub_uint64_t stripe_length; - grub_uint8_t dummy2[0x14]; + grub_uint64_t type; +#define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07 +#define GRUB_BTRFS_CHUNK_TYPE_SINGLE 0x00 +#define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08 +#define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10 +#define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20 + grub_uint8_t dummy2[0xc]; grub_uint16_t nstripes; - grub_uint16_t dummy3; + grub_uint16_t nsubstripes; } __attribute__ ((packed)); struct grub_btrfs_chunk_stripe @@ -187,8 +202,37 @@ static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, static grub_err_t grub_btrfs_read_logical (struct grub_btrfs_data *data, - grub_disk_t disk, grub_disk_addr_t addr, - void *buf, grub_size_t size); + grub_disk_addr_t addr, void *buf, grub_size_t size); + +static grub_err_t +read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) +{ + unsigned i; + grub_err_t err = GRUB_ERR_NONE; + for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) + { + struct grub_btrfs_superblock sblock; + err = grub_disk_read (disk, superblock_sectors[i], 0, + sizeof (sblock), &sblock); + if (err == GRUB_ERR_OUT_OF_RANGE) + break; + + if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE, + sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0) + break; + if (i == 0 || grub_le_to_cpu64 (sblock.generation) + > grub_le_to_cpu64 (sb->generation)) + grub_memcpy (sb, &sblock, sizeof (sblock)); + } + + if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0) + return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem"); + + if (err == GRUB_ERR_OUT_OF_RANGE) + grub_errno = err = GRUB_ERR_NONE; + + return err; +} static int key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b) @@ -239,7 +283,7 @@ save_ref (struct grub_btrfs_leaf_descriptor *desc, } static int -next (struct grub_btrfs_data *data, grub_disk_t disk, +next (struct grub_btrfs_data *data, struct grub_btrfs_leaf_descriptor *desc, grub_disk_addr_t *outaddr, grub_size_t *outsize, struct grub_btrfs_key *key_out) @@ -261,8 +305,7 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, struct grub_btrfs_internal_node node; struct btrfs_header head; - err = grub_btrfs_read_logical (data, disk, - desc->data[desc->depth - 1].iter + err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter * sizeof (node) + sizeof (struct btrfs_header) + desc->data[desc->depth - 1].addr, &node, @@ -270,8 +313,7 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, if (err) return -err; - err = grub_btrfs_read_logical (data, disk, - grub_le_to_cpu64 (node.addr), &head, + err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head, sizeof (head)); if (err) return -err; @@ -279,8 +321,7 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, save_ref (desc, grub_le_to_cpu64 (node.addr), 0, grub_le_to_cpu32 (head.nitems), !head.level); } - err = grub_btrfs_read_logical (data, disk, - desc->data[desc->depth - 1].iter + err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter * sizeof (leaf) + sizeof (struct btrfs_header) + desc->data[desc->depth - 1].addr, &leaf, @@ -295,7 +336,7 @@ next (struct grub_btrfs_data *data, grub_disk_t disk, } static grub_err_t -lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, +lower_bound (struct grub_btrfs_data *data, const struct grub_btrfs_key *key_in, struct grub_btrfs_key *key_out, grub_disk_addr_t root, @@ -327,7 +368,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, reiter: depth++; /* FIXME: preread few nodes into buffer. */ - err = grub_btrfs_read_logical (data, disk, addr, &head, sizeof (head)); + err = grub_btrfs_read_logical (data, addr, &head, sizeof (head)); if (err) return err; addr += sizeof (head); @@ -339,8 +380,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, grub_memset (&node_last, 0, sizeof (node_last)); for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { - err = grub_btrfs_read_logical (data, disk, addr - + i * sizeof (node), + err = grub_btrfs_read_logical (data, addr + i * sizeof (node), &node, sizeof (node)); if (err) return err; @@ -391,7 +431,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, int have_last = 0; for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) { - err = grub_btrfs_read_logical (data, disk, addr + i * sizeof (leaf), + err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf), &leaf, sizeof (leaf)); if (err) return err; @@ -440,9 +480,83 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk, } } +static grub_device_t +find_device (struct grub_btrfs_data *data, grub_uint64_t id) +{ + grub_device_t dev_found = NULL; + auto int hook (const char *name); + int hook (const char *name) + { + grub_device_t dev; + grub_err_t err; + struct grub_btrfs_superblock sb; + dev = grub_device_open (name); + if (!dev) + return 0; + if (!dev->disk) + { + grub_device_close (dev); + return 0; + } + err = read_sblock (dev->disk, &sb); + if (err == GRUB_ERR_BAD_FS) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (err) + { + grub_device_close (dev); + grub_print_error (); + return 0; + } + if (grub_memcmp (data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0 + || sb.this_device.device_id != id) + { + grub_device_close (dev); + return 0; + } + + dev_found = dev; + return 1; + } + + unsigned i; + + for (i = 0; i < data->n_devices_attached; i++) + if (id == data->devices_attached[i].id) + return data->devices_attached[i].dev; + grub_device_iterate (hook); + if (!dev_found) + { + grub_error (GRUB_ERR_BAD_FS, "couldn't find a member device"); + return NULL; + } + data->n_devices_attached++; + if (data->n_devices_attached > data->n_devices_allocated) + { + void *tmp; + data->n_devices_allocated = 2 * data->n_devices_attached + 1; + data->devices_attached + = grub_realloc (tmp = data->devices_attached, + data->n_devices_allocated + * sizeof (data->devices_attached[0])); + if (!data->devices_attached) + { + grub_device_close (dev_found); + data->devices_attached = tmp; + return NULL; + } + } + data->devices_attached[data->n_devices_attached - 1].id = id; + data->devices_attached[data->n_devices_attached - 1].dev = dev_found; + return dev_found; +} + static grub_err_t grub_btrfs_read_logical (struct grub_btrfs_data *data, - grub_disk_t disk, grub_disk_addr_t addr, + grub_disk_addr_t addr, void *buf, grub_size_t size) { while (size > 0) @@ -451,14 +565,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, struct grub_btrfs_key *key; struct grub_btrfs_chunk_item *chunk; struct grub_btrfs_chunk_stripe *stripe; - grub_size_t csize; + grub_ssize_t csize; grub_err_t err; - grub_disk_addr_t paddr; - grub_uint64_t stripen; - grub_uint32_t stripe_length; - grub_uint32_t stripe_offset; struct grub_btrfs_key key_out; int challoc = 0; + grub_device_t dev; for (ptr = data->sblock.bootstrap_mapping; ptr < data->sblock.bootstrap_mapping + sizeof (data->sblock.bootstrap_mapping) @@ -485,8 +596,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, key_in.object_id = GRUB_BTRFS_OBJECT_ID_CHUNK; key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK; key_in.offset = addr; - err = lower_bound (data, disk, - &key_in, &key_out, + err = lower_bound (data, &key_in, &key_out, grub_le_to_cpu64 (data->sblock.chunk_tree), &chaddr, &chsize, NULL); if (err) @@ -502,55 +612,99 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, return grub_errno; challoc = 1; - err = grub_btrfs_read_logical (data, disk, chaddr, - chunk, chsize); + err = grub_btrfs_read_logical (data, chaddr, chunk, chsize); if (err) { grub_free (chunk); return err; } - - if (!(addr < grub_le_to_cpu64 (key->offset) - + grub_le_to_cpu64 (chunk->size))) - return grub_error (GRUB_ERR_BAD_FS, - "couldn't find the chunk descriptor"); chunk_found: - stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), - grub_le_to_cpu16 (chunk->nstripes), - NULL); - stripen = grub_divmod64 (addr - grub_le_to_cpu64 (key->offset), - stripe_length, &stripe_offset); - stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); - stripe += stripen; - csize = grub_le_to_cpu64 (key->offset) + grub_le_to_cpu64 (chunk->size) - - addr; - if (csize > size) - csize = size; - if (grub_le_to_cpu64 (stripe->device_id) != grub_le_to_cpu64 (data->sblock.this_device.device_id)) - { - if (challoc) - grub_free (chunk); - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "multidevice isn't implemented yet"); - } - grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T - "+0x%" PRIxGRUB_UINT64_T " (%d stripes of %" - PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T - " maps to 0x%" PRIxGRUB_UINT64_T "\n", - grub_le_to_cpu64 (key->offset), - grub_le_to_cpu64 (chunk->size), - grub_le_to_cpu16 (chunk->nstripes), - grub_le_to_cpu64 (chunk->stripe_length), - stripen, - stripe->offset); - paddr = stripe->offset + stripe_offset; + { + grub_uint32_t stripen; + grub_uint32_t stripe_offset; + grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset); + grub_disk_addr_t paddr; - grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T - " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, - addr); - err = grub_disk_read (disk, paddr >> GRUB_DISK_SECTOR_BITS, - paddr & (GRUB_DISK_SECTOR_SIZE - 1), csize, buf); + stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); + switch (grub_le_to_cpu64 (chunk->type) + & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE) + { + case GRUB_BTRFS_CHUNK_TYPE_SINGLE: + { + grub_uint32_t stripe_length; + stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + NULL); + stripen = grub_divmod64 (off, stripe_length, &stripe_offset); + csize = (stripen + 1) * stripe_length - off; + break; + } + case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED: + case GRUB_BTRFS_CHUNK_TYPE_RAID1: + /* FIXME: Use redundancy. */ + { + grub_uint32_t stripe_length; + stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + NULL); + stripen = 0; + stripe_offset = off; + csize = stripe_length - off; + break; + } + case GRUB_BTRFS_CHUNK_TYPE_RAID0: + { + grub_uint64_t middle, high; + grub_uint32_t low; + middle = grub_divmod64 (off, + grub_le_to_cpu64 (chunk->stripe_length), + &low); + + high = grub_divmod64 (middle, grub_le_to_cpu16 (chunk->nstripes), + &stripen); + stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length) + * high; + csize = grub_le_to_cpu64 (chunk->stripe_length) - low; + break; + } + default: + grub_printf ("unsupported RAID flags %" PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (chunk->type)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID flags %" PRIxGRUB_UINT64_T, + grub_le_to_cpu64 (chunk->type)); + } + stripe += stripen; + if (csize <= 0) + return grub_error (GRUB_ERR_BAD_FS, + "couldn't find the chunk descriptor"); + if ((grub_size_t) csize > size) + csize = size; + dev = find_device (data, stripe->device_id); + if (!dev) + return grub_errno; + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %" + PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT32_T + " maps to 0x%" PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + grub_le_to_cpu16 (chunk->nsubstripes), + grub_le_to_cpu64 (chunk->stripe_length), + stripen, + stripe->offset); + paddr = stripe->offset + stripe_offset; + + grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T + " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, + addr); + err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS, + paddr & (GRUB_DISK_SECTOR_SIZE - 1), csize, buf); + if (err) + return err; + } size -= csize; buf = (grub_uint8_t *) buf + csize; addr += csize; @@ -561,54 +715,57 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, } static struct grub_btrfs_data * -grub_btrfs_mount (grub_disk_t disk) +grub_btrfs_mount (grub_device_t dev) { - struct grub_btrfs_data *data = grub_zalloc (sizeof (*data)); - unsigned i; - grub_err_t err = GRUB_ERR_NONE; + struct grub_btrfs_data *data; + grub_err_t err; + if (!dev->disk) + { + grub_error (GRUB_ERR_BAD_FS, "not BtrFS"); + return NULL; + } + + data = grub_zalloc (sizeof (*data)); if (! data) return NULL; - for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) + err = read_sblock (dev->disk, &data->sblock); + if (err) { - struct grub_btrfs_superblock sblock; - err = grub_disk_read (disk, superblock_sectors[i], 0, - sizeof (sblock), &sblock); - if (err == GRUB_ERR_OUT_OF_RANGE) - break; - - if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE, - sizeof (GRUB_BTRFS_SIGNATURE) - 1)) - break; - if (i == 0 || grub_le_to_cpu64 (sblock.generation) - > grub_le_to_cpu64 (data->sblock.generation)) - { - grub_memcpy (&data->sblock, &sblock, sizeof (sblock)); - data->sblock_number = i; - } + grub_free (data); + return NULL; } - if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0) + data->n_devices_allocated = 16; + data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) + * data->n_devices_allocated); + if (!data->devices_attached) { - grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem"); - goto fail; + grub_free (data); + return NULL; } - - if (err == GRUB_ERR_OUT_OF_RANGE) - grub_errno = err = GRUB_ERR_NONE; - - grub_dprintf ("btrfs", "using superblock %d\n", data->sblock_number); + data->n_devices_attached = 1; + data->devices_attached[0].dev = dev; + data->devices_attached[0].id = data->sblock.this_device.device_id; return data; +} - fail: +static void +grub_btrfs_unmount (struct grub_btrfs_data *data) +{ + unsigned i; + /* The device 0 is closed one layer upper. */ + for (i = 1; i < data->n_devices_attached; i++) + grub_device_close (data->devices_attached[i].dev); + grub_free (data->devices_attached); + grub_free (data->extent); grub_free (data); - return NULL; } static grub_err_t -grub_btrfs_read_inode (struct grub_btrfs_data *data, grub_disk_t disk, +grub_btrfs_read_inode (struct grub_btrfs_data *data, struct grub_btrfs_inode *inode, grub_uint64_t num, grub_uint64_t tree) { @@ -621,7 +778,7 @@ grub_btrfs_read_inode (struct grub_btrfs_data *data, grub_disk_t disk, key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; key_in.offset = 0; - err = lower_bound (data,disk, &key_in, &key_out, tree, + err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, NULL); if (err) return err; @@ -629,11 +786,11 @@ grub_btrfs_read_inode (struct grub_btrfs_data *data, grub_disk_t disk, || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM) return grub_error (GRUB_ERR_BAD_FS, "inode not found"); - return grub_btrfs_read_logical (data, disk, elemaddr, inode, sizeof (*inode)); + return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode)); } static grub_ssize_t -grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, +grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_uint64_t ino, grub_uint64_t tree, grub_off_t pos0, char *buf, grub_size_t len) { @@ -654,7 +811,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, key_in.object_id = ino; key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; key_in.offset = grub_cpu_to_le64 (pos); - err = lower_bound (data, disk, &key_in, &key_out, tree, + err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, NULL); if (err) return -1; @@ -671,7 +828,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, if (!data->extent) return grub_errno; - err = grub_btrfs_read_logical (data, disk, elemaddr, + err = grub_btrfs_read_logical (data, elemaddr, data->extent, elemsize); if (err) return err; @@ -721,7 +878,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, grub_memset (buf, 0, csize); break; } - err = grub_btrfs_read_logical (data, disk, + err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (data->extent->laddr) + extoff, buf, csize); @@ -742,7 +899,6 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk, static grub_err_t find_path (struct grub_btrfs_data *data, - grub_disk_t disk, const char *path, struct grub_btrfs_key *key, grub_uint64_t *tree, grub_uint8_t *type) { @@ -757,6 +913,7 @@ find_path (struct grub_btrfs_data *data, const char *ctoken; grub_size_t ctokenlen; char *path_alloc = NULL; + unsigned symlinks_max = 32; *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *tree = data->sblock.root_tree; @@ -794,7 +951,7 @@ find_path (struct grub_btrfs_data *data, key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); - err = lower_bound (data, disk, key, &key_out, *tree, + err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize, NULL); if (err) { @@ -822,8 +979,7 @@ find_path (struct grub_btrfs_data *data, } } - err = grub_btrfs_read_logical (data, disk, elemaddr, - direl, elemsize); + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize); if (err) { grub_free (direl); @@ -860,7 +1016,15 @@ find_path (struct grub_btrfs_data *data, { struct grub_btrfs_inode inode; char *tmp; - err = grub_btrfs_read_inode (data, disk, &inode, + if (--symlinks_max == 0) + { + grub_free (direl); + grub_free (path_alloc); + return grub_error (GRUB_ERR_SYMLINK_LOOP, + "too deep nesting of symlinks"); + } + + err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id, *tree); if (err) { @@ -877,7 +1041,7 @@ find_path (struct grub_btrfs_data *data, return grub_errno; } - if (grub_btrfs_extent_read (data, disk, cdirel->key.object_id, + if (grub_btrfs_extent_read (data, cdirel->key.object_id, *tree, 0, tmp, grub_le_to_cpu64 (inode.size)) != (grub_ssize_t) grub_le_to_cpu64 (inode.size)) @@ -909,7 +1073,7 @@ find_path (struct grub_btrfs_data *data, case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM: { struct grub_btrfs_root_item ri; - err = lower_bound (data, disk, &cdirel->key, &key_out, + err = lower_bound (data, &cdirel->key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, NULL); if (err) @@ -925,7 +1089,7 @@ find_path (struct grub_btrfs_data *data, grub_free (path_alloc); return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); } - err = grub_btrfs_read_logical (data, disk, elemaddr, + err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri)); if (err) { @@ -967,7 +1131,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, int (*hook) (const char *filename, const struct grub_dirhook_info *info)) { - struct grub_btrfs_data *data = grub_btrfs_mount (device->disk); + struct grub_btrfs_data *data = grub_btrfs_mount (device); struct grub_btrfs_key key_in, key_out; grub_err_t err; grub_disk_addr_t elemaddr; @@ -982,21 +1146,20 @@ grub_btrfs_dir (grub_device_t device, const char *path, if (!data) return grub_errno; - err = find_path (data, device->disk, path, &key_in, &tree, &type); + err = find_path (data, path, &key_in, &tree, &type); if (err) return err; if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - err = lower_bound (data, device->disk, &key_in, &key_out, - tree, + err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, &desc); if (err) return err; if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM || key_out.object_id != key_in.object_id) { - r = next (data, device->disk, &desc, &elemaddr, &elemsize, &key_out); + r = next (data, &desc, &elemaddr, &elemsize, &key_out); if (r <= 0) { free_iterator (&desc); @@ -1025,8 +1188,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, } } - err = grub_btrfs_read_logical (data, device->disk, elemaddr, - direl, elemsize); + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize); if (err) return err; @@ -1046,7 +1208,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, goto out; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; } - r = next (data, device->disk, &desc, &elemaddr, &elemsize, &key_out); + r = next (data, &desc, &elemaddr, &elemsize, &key_out); } while (r > 0); @@ -1054,7 +1216,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_free (direl); free_iterator (&desc); - grub_free (data); + grub_btrfs_unmount (data); return -r; } @@ -1062,7 +1224,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, static grub_err_t grub_btrfs_open (struct grub_file *file, const char *name) { - struct grub_btrfs_data *data = grub_btrfs_mount (file->device->disk); + struct grub_btrfs_data *data = grub_btrfs_mount (file->device); grub_err_t err; struct grub_btrfs_inode inode; grub_uint8_t type; @@ -1071,24 +1233,23 @@ grub_btrfs_open (struct grub_file *file, const char *name) if (!data) return grub_errno; - err = find_path (data, file->device->disk, name, &key_in, &data->tree, &type); + err = find_path (data, name, &key_in, &data->tree, &type); if (err) { - grub_free (data); + grub_btrfs_unmount (data); return err; } if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) { - grub_free (data); + grub_btrfs_unmount (data); return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); } data->inode = key_in.object_id; - err = grub_btrfs_read_inode (data, file->device->disk, &inode, data->inode, - data->tree); + err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree); if (err) { - grub_free (data); + grub_btrfs_unmount (data); return err; } @@ -1101,10 +1262,7 @@ grub_btrfs_open (struct grub_file *file, const char *name) static grub_err_t grub_btrfs_close (grub_file_t file) { - struct grub_btrfs_data *data = file->data; - - grub_free (data->extent); - grub_free (data); + grub_btrfs_unmount (file->data); return GRUB_ERR_NONE; } @@ -1114,7 +1272,7 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_btrfs_data *data = file->data; - return grub_btrfs_extent_read (data, file->device->disk, data->inode, + return grub_btrfs_extent_read (data, data->inode, data->tree, file->offset, buf, len); } @@ -1125,7 +1283,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) *uuid = NULL; - data = grub_btrfs_mount (device->disk); + data = grub_btrfs_mount (device); if (! data) return grub_errno; @@ -1139,7 +1297,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) grub_be_to_cpu16 (data->sblock.uuid[6]), grub_be_to_cpu16 (data->sblock.uuid[7])); - grub_free (data); + grub_btrfs_unmount (data); return grub_errno; } @@ -1151,13 +1309,13 @@ grub_btrfs_label (grub_device_t device, char **label) *label = NULL; - data = grub_btrfs_mount (device->disk); + data = grub_btrfs_mount (device); if (! data) return grub_errno; *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label)); - grub_free (data); + grub_btrfs_unmount (data); return grub_errno; } From 6333f1e9b680a8eefca82bfcdfd3afe66547ab29 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 18:11:10 +0100 Subject: [PATCH 20/34] Add RAID10 support --- grub-core/fs/btrfs.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index f8fbf95e9..e6bab83aa 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -111,6 +111,7 @@ struct grub_btrfs_chunk_item #define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20 +#define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40 grub_uint8_t dummy2[0xc]; grub_uint16_t nstripes; grub_uint16_t nsubstripes; @@ -668,9 +669,26 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, csize = grub_le_to_cpu64 (chunk->stripe_length) - low; break; } + case GRUB_BTRFS_CHUNK_TYPE_RAID10: + /* FIXME: Use redundancy. */ + { + grub_uint64_t middle, high; + grub_uint32_t low; + middle = grub_divmod64 (off, + grub_le_to_cpu64 (chunk->stripe_length), + &low); + + high = grub_divmod64 (middle, + grub_le_to_cpu16 (chunk->nsubstripes), + &stripen); + stripen *= grub_le_to_cpu16 (chunk->nstripes) + / grub_le_to_cpu16 (chunk->nsubstripes); + stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length) + * high; + csize = grub_le_to_cpu64 (chunk->stripe_length) - low; + break; + } default: - grub_printf ("unsupported RAID flags %" PRIxGRUB_UINT64_T "\n", - grub_le_to_cpu64 (chunk->type)); return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "unsupported RAID flags %" PRIxGRUB_UINT64_T, grub_le_to_cpu64 (chunk->type)); From 3be8e5ea96264c2d0df38b63ff13596c634522dd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 21:42:13 +0100 Subject: [PATCH 21/34] BtrFS zlib compression support --- Makefile.util.def | 1 + grub-core/fs/btrfs.c | 57 +++++++++++++-- grub-core/io/gzio.c | 157 +++++++++++++++++++++++++++++++---------- include/grub/deflate.h | 26 +++++++ 4 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 include/grub/deflate.h diff --git a/Makefile.util.def b/Makefile.util.def index 4d642a2b6..f3cd1d234 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -95,6 +95,7 @@ library = { common = grub-core/script/main.c; common = grub-core/script/script.c; common = grub-core/script/argv.c; + common = grub-core/io/gzio.c; }; program = { diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index e6bab83aa..7e31fa3f1 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -25,6 +25,7 @@ #include #include #include +#include #define GRUB_BTRFS_SIGNATURE "_BHRfS_M" @@ -84,6 +85,7 @@ struct grub_btrfs_data grub_uint64_t extstart; grub_uint64_t extino; grub_uint64_t exttree; + grub_size_t extsize; struct grub_btrfs_extent_data *extent; }; @@ -187,13 +189,20 @@ struct grub_btrfs_extent_data union { char inl[0]; - grub_uint64_t laddr; + struct + { + grub_uint64_t laddr; + grub_uint64_t compressed_size; + grub_uint64_t offset; + }; }; } __attribute__ ((packed)); #define GRUB_BTRFS_EXTENT_INLINE 0 #define GRUB_BTRFS_EXTENT_REGULAR 1 +#define GRUB_BTRFS_COMPRESSION_NONE 0 +#define GRUB_BTRFS_COMPRESSION_ZLIB 1 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 @@ -840,6 +849,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, return -1; } data->extstart = grub_le_to_cpu64 (key_out.offset); + data->extsize = elemsize; data->extent = grub_malloc (elemsize); data->extino = ino; data->exttree = tree; @@ -870,14 +880,15 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, return -1; } - if (data->extent->compression) + if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE + && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "compression not supported"); + "compression type 0x%x not supported", + data->extent->compression); return -1; } - if (data->extent->encoding) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, @@ -888,7 +899,17 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, switch (data->extent->type) { case GRUB_BTRFS_EXTENT_INLINE: - grub_memcpy (buf, data->extent->inl + extoff, csize); + if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) + { + if (grub_zlib_decompress (data->extent->inl, data->extsize - + ((grub_uint8_t *) data->extent->inl + - (grub_uint8_t *) data->extent), + extoff, buf, csize) + != (grub_ssize_t) csize) + return -1; + } + else + grub_memcpy (buf, data->extent->inl + extoff, csize); break; case GRUB_BTRFS_EXTENT_REGULAR: if (!data->extent->laddr) @@ -896,6 +917,32 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_memset (buf, 0, csize); break; } + if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) + { + char *tmp; + grub_uint64_t zsize; + zsize = grub_le_to_cpu64 (data->extent->compressed_size); + tmp = grub_malloc (zsize); + if (!tmp) + return -1; + err = grub_btrfs_read_logical (data, + grub_le_to_cpu64 (data->extent->laddr), + tmp, zsize); + if (err) + { + grub_free (tmp); + return -1; + } + if (grub_zlib_decompress (tmp, zsize, extoff + + grub_le_to_cpu64 (data->extent->offset), + buf, csize) != (grub_ssize_t) csize) + { + grub_free (tmp); + return -1; + } + grub_free (tmp); + break; + } err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (data->extent->laddr) + extoff, diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c index 43b67c373..248a1750e 100644 --- a/grub-core/io/gzio.c +++ b/grub-core/io/gzio.c @@ -41,6 +41,7 @@ #include #include #include +#include /* * Window Size @@ -58,6 +59,9 @@ struct grub_gzio { /* The underlying file object. */ grub_file_t file; + /* If input is in memory following fields are used instead of file. */ + grub_size_t mem_input_size, mem_input_off; + grub_uint8_t *mem_input; /* The offset at which the data starts in the underlying file. */ grub_off_t data_offset; /* The type of current block. */ @@ -100,7 +104,7 @@ typedef struct grub_gzio *grub_gzio_t; static struct grub_fs grub_gzio_fs; /* Function prototypes */ -static void initialize_tables (grub_file_t file); +static void initialize_tables (grub_gzio_t); /* Eat variable-length header fields. */ static int @@ -162,7 +166,7 @@ typedef unsigned short ush; typedef unsigned long ulg; static int -test_header (grub_file_t file) +test_gzip_header (grub_file_t file) { struct { grub_uint16_t magic; @@ -226,7 +230,7 @@ test_header (grub_file_t file) But how can we know the real original size? */ file->size = grub_le_to_cpu32 (orig_len); - initialize_tables (file); + initialize_tables (gzio); return 1; } @@ -366,13 +370,18 @@ static ush mask_bits[] = 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; -#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(file))<>=(n);k-=(n);} while (0) static int -get_byte (grub_file_t file) +get_byte (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; + if (gzio->mem_input) + { + if (gzio->mem_input_off < gzio->mem_input_size) + return gzio->mem_input[gzio->mem_input_off++]; + return 0; + } if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset || gzio->inbuf_d == INBUFSIZ) @@ -384,11 +393,26 @@ get_byte (grub_file_t file) return gzio->inbuf[gzio->inbuf_d++]; } +static void +gzio_seek (grub_gzio_t gzio, grub_off_t off) +{ + if (gzio->mem_input) + { + if (off > gzio->mem_input_size) + grub_error (GRUB_ERR_OUT_OF_RANGE, + "attempt to seek outside of the file"); + else + gzio->mem_input_off = gzio->data_offset; + } + else + grub_file_seek (gzio->file, off); +} + /* more function prototypes */ static int huft_build (unsigned *, unsigned, unsigned, ush *, ush *, struct huft **, int *); static int huft_free (struct huft *); -static int inflate_codes_in_window (grub_file_t); +static int inflate_codes_in_window (grub_gzio_t); /* Given a list of code lengths and a maximum table size, make a set of @@ -615,7 +639,7 @@ huft_free (struct huft *t) */ static int -inflate_codes_in_window (grub_file_t file) +inflate_codes_in_window (grub_gzio_t gzio) { register unsigned e; /* table entry flag/number of extra bits */ unsigned n, d; /* length and index for copy */ @@ -624,7 +648,6 @@ inflate_codes_in_window (grub_file_t file) unsigned ml, md; /* masks for bl and bd bits */ register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local copies of globals */ d = gzio->inflate_d; @@ -752,11 +775,10 @@ inflate_codes_in_window (grub_file_t file) /* get header for an inflated type 0 (stored) block. */ static void -init_stored_block (grub_file_t file) +init_stored_block (grub_gzio_t gzio) { register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local copies of globals */ b = gzio->bb; /* initialize bit buffer */ @@ -786,11 +808,10 @@ init_stored_block (grub_file_t file) Huffman tables. */ static void -init_fixed_block (grub_file_t file) +init_fixed_block (grub_gzio_t gzio) { int i; /* temporary variable */ unsigned l[288]; /* length list for huft_build */ - grub_gzio_t gzio = file->data; /* set up literal table */ for (i = 0; i < 144; i++) @@ -833,7 +854,7 @@ init_fixed_block (grub_file_t file) /* get header for an inflated type 2 (dynamic Huffman codes) block. */ static void -init_dynamic_block (grub_file_t file) +init_dynamic_block (grub_gzio_t gzio) { int i; /* temporary variables */ unsigned j; @@ -846,7 +867,6 @@ init_dynamic_block (grub_file_t file) unsigned ll[286 + 30]; /* literal/length and distance code lengths */ register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local bit buffer */ b = gzio->bb; @@ -977,11 +997,10 @@ init_dynamic_block (grub_file_t file) static void -get_new_block (grub_file_t file) +get_new_block (grub_gzio_t gzio) { register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local bit buffer */ b = gzio->bb; @@ -1004,13 +1023,13 @@ get_new_block (grub_file_t file) switch (gzio->block_type) { case INFLATE_STORED: - init_stored_block (file); + init_stored_block (gzio); break; case INFLATE_FIXED: - init_fixed_block (file); + init_fixed_block (gzio); break; case INFLATE_DYNAMIC: - init_dynamic_block (file); + init_dynamic_block (gzio); break; default: break; @@ -1019,10 +1038,8 @@ get_new_block (grub_file_t file) static void -inflate_window (grub_file_t file) +inflate_window (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; - /* initialize window */ gzio->wp = 0; @@ -1037,7 +1054,7 @@ inflate_window (grub_file_t file) if (gzio->last_block) break; - get_new_block (file); + get_new_block (gzio); } if (gzio->block_type > INFLATE_DYNAMIC) @@ -1060,7 +1077,7 @@ inflate_window (grub_file_t file) while (gzio->block_len && w < WSIZE && grub_errno == GRUB_ERR_NONE) { - gzio->slide[w++] = get_byte (file); + gzio->slide[w++] = get_byte (gzio); gzio->block_len--; } @@ -1073,7 +1090,7 @@ inflate_window (grub_file_t file) * Expand other kind of block. */ - if (inflate_codes_in_window (file)) + if (inflate_codes_in_window (gzio)) { huft_free (gzio->tl); huft_free (gzio->td); @@ -1089,12 +1106,10 @@ inflate_window (grub_file_t file) static void -initialize_tables (grub_file_t file) +initialize_tables (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; - gzio->saved_offset = 0; - grub_file_seek (gzio->file, gzio->data_offset); + gzio_seek (gzio, gzio->data_offset); /* Initialize the bit buffer. */ gzio->bk = 0; @@ -1139,7 +1154,7 @@ grub_gzio_open (grub_file_t io) file->fs = &grub_gzio_fs; file->not_easly_seekable = 1; - if (! test_header (file)) + if (! test_gzip_header (file)) { grub_free (gzio); grub_free (file); @@ -1155,16 +1170,49 @@ grub_gzio_open (grub_file_t io) return file; } +static int +test_zlib_header (grub_gzio_t gzio) +{ + grub_uint8_t cmf, flg; + + cmf = get_byte (gzio); + flg = get_byte (gzio); + + /* Check that compression method is DEFLATE. */ + if ((cmf & 0xf) != DEFLATED) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format"); + return 0; + } + + if ((cmf * 256 + flg) % 31) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format"); + return 0; + } + + /* Dictionary isn't supported. */ + if (flg & 0x20) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format"); + return 0; + } + + gzio->data_offset = 2; + initialize_tables (gzio); + + return 1; +} + static grub_ssize_t -grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) +grub_gzio_read_real (grub_gzio_t gzio, grub_off_t offset, + char *buf, grub_size_t len) { grub_ssize_t ret = 0; - grub_gzio_t gzio = file->data; - grub_off_t offset; /* Do we reset decompression to the beginning of the file? */ - if (gzio->saved_offset > file->offset + WSIZE) - initialize_tables (file); + if (gzio->saved_offset > offset + WSIZE) + initialize_tables (gzio); /* * This loop operates upon uncompressed data only. The only @@ -1172,15 +1220,13 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) * window is within the range of data it needs. */ - offset = file->offset; - while (len > 0 && grub_errno == GRUB_ERR_NONE) { register grub_size_t size; register char *srcaddr; while (offset >= gzio->saved_offset) - inflate_window (file); + inflate_window (gzio); srcaddr = (char *) ((offset & (WSIZE - 1)) + gzio->slide); size = gzio->saved_offset - offset; @@ -1201,6 +1247,12 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) return ret; } +static grub_ssize_t +grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) +{ + return grub_gzio_read_real (file->data, file->offset, buf, len); +} + /* Release everything, including the underlying file object. */ static grub_err_t grub_gzio_close (grub_file_t file) @@ -1218,6 +1270,33 @@ grub_gzio_close (grub_file_t file) return grub_errno; } +grub_ssize_t +grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize) +{ + grub_gzio_t gzio = 0; + grub_ssize_t ret; + + gzio = grub_zalloc (sizeof (*gzio)); + if (! gzio) + return -1; + gzio->mem_input = (grub_uint8_t *) inbuf; + gzio->mem_input_size = insize; + gzio->mem_input_off = 0; + + if (!test_zlib_header (gzio)) + { + grub_free (gzio); + return -1; + } + + ret = grub_gzio_read_real (gzio, off, outbuf, outsize); + grub_free (gzio); + + /* FIXME: Check Adler. */ + return ret; +} + static struct grub_fs grub_gzio_fs = diff --git a/include/grub/deflate.h b/include/grub/deflate.h new file mode 100644 index 000000000..6ec4eaa99 --- /dev/null +++ b/include/grub/deflate.h @@ -0,0 +1,26 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_DEFLATE_HEADER +#define GRUB_DEFLATE_HEADER 1 + +grub_ssize_t +grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize); + +#endif From 79282228ec14e6509d1c6df5eb0ec9a7ee67a3f7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 6 Dec 2010 20:26:49 +0100 Subject: [PATCH 22/34] use anopther RAID1(0) copy if main one fails --- grub-core/fs/btrfs.c | 86 +++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 7e31fa3f1..b7edb7e39 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -491,7 +491,8 @@ lower_bound (struct grub_btrfs_data *data, } static grub_device_t -find_device (struct grub_btrfs_data *data, grub_uint64_t id) +find_device (struct grub_btrfs_data *data, grub_uint64_t id, + int do_rescan) { grub_device_t dev_found = NULL; auto int hook (const char *name); @@ -537,7 +538,8 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t id) for (i = 0; i < data->n_devices_attached; i++) if (id == data->devices_attached[i].id) return data->devices_attached[i].dev; - grub_device_iterate (hook); + if (do_rescan) + grub_device_iterate (hook); if (!dev_found) { grub_error (GRUB_ERR_BAD_FS, "couldn't find a member device"); @@ -574,7 +576,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_uint8_t *ptr; struct grub_btrfs_key *key; struct grub_btrfs_chunk_item *chunk; - struct grub_btrfs_chunk_stripe *stripe; grub_ssize_t csize; grub_err_t err; struct grub_btrfs_key key_out; @@ -598,7 +599,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, + grub_le_to_cpu64 (chunk->size)) goto chunk_found; ptr += sizeof (*key) + sizeof (*chunk) - + sizeof (*stripe) * grub_le_to_cpu16 (chunk->nstripes); + + sizeof (struct grub_btrfs_chunk_stripe) + * grub_le_to_cpu16 (chunk->nstripes); } struct grub_btrfs_key key_in; grub_size_t chsize; @@ -634,9 +636,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_uint32_t stripen; grub_uint32_t stripe_offset; grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset); - grub_disk_addr_t paddr; + unsigned redundancy = 1; + unsigned i, j; - stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); switch (grub_le_to_cpu64 (chunk->type) & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE) { @@ -661,6 +663,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, stripen = 0; stripe_offset = off; csize = stripe_length - off; + redundancy = 2; break; } case GRUB_BTRFS_CHUNK_TYPE_RAID0: @@ -692,6 +695,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, &stripen); stripen *= grub_le_to_cpu16 (chunk->nstripes) / grub_le_to_cpu16 (chunk->nsubstripes); + redundancy = grub_le_to_cpu16 (chunk->nstripes) + / grub_le_to_cpu16 (chunk->nsubstripes); stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length) * high; csize = grub_le_to_cpu64 (chunk->stripe_length) - low; @@ -702,35 +707,60 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, "unsupported RAID flags %" PRIxGRUB_UINT64_T, grub_le_to_cpu64 (chunk->type)); } - stripe += stripen; if (csize <= 0) return grub_error (GRUB_ERR_BAD_FS, "couldn't find the chunk descriptor"); if ((grub_size_t) csize > size) csize = size; - dev = find_device (data, stripe->device_id); - if (!dev) - return grub_errno; - grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T - "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %" - PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT32_T - " maps to 0x%" PRIxGRUB_UINT64_T "\n", - grub_le_to_cpu64 (key->offset), - grub_le_to_cpu64 (chunk->size), - grub_le_to_cpu16 (chunk->nstripes), - grub_le_to_cpu16 (chunk->nsubstripes), - grub_le_to_cpu64 (chunk->stripe_length), - stripen, - stripe->offset); - paddr = stripe->offset + stripe_offset; - grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T - " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, - addr); - err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS, - paddr & (GRUB_DISK_SECTOR_SIZE - 1), csize, buf); + for (j = 0; j < 2; j++) + { + for (i = 0; i < redundancy; i++) + { + struct grub_btrfs_chunk_stripe *stripe; + grub_disk_addr_t paddr; + + stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); + /* Right now the redundancy handlind is easy. + With RAID5-like it will be more difficult. */ + stripe += stripen + i; + + paddr = stripe->offset + stripe_offset; + + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %" + PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT32_T + " maps to 0x%" PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + grub_le_to_cpu16 (chunk->nsubstripes), + grub_le_to_cpu64 (chunk->stripe_length), + stripen, stripe->offset); + grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T + " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, + addr); + + dev = find_device (data, stripe->device_id, j); + if (!dev) + { + err = grub_errno; + grub_errno = GRUB_ERR_NONE; + continue; + } + + err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS, + paddr & (GRUB_DISK_SECTOR_SIZE - 1), + csize, buf); + if (!err) + break; + grub_errno = GRUB_ERR_NONE; + } + if (i != redundancy) + break; + } if (err) - return err; + return grub_errno = err; } size -= csize; buf = (grub_uint8_t *) buf + csize; From d6f07b29fca3f555b22000fde18577a45bd42b39 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 10 Dec 2010 14:38:16 +0100 Subject: [PATCH 23/34] mtime btrfs support --- grub-core/fs/btrfs.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index b7edb7e39..b7c59065c 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -172,10 +172,18 @@ struct grub_btrfs_root_item grub_uint64_t inode; }; +struct grub_btrfs_time +{ + grub_int64_t sec; + grub_uint32_t nanosec; +} __attribute__ ((aligned(4))); + struct grub_btrfs_inode { - grub_uint8_t dummy[0x10]; + grub_uint8_t dummy1[0x10]; grub_uint64_t size; + grub_uint8_t dummy2[0x70]; + struct grub_btrfs_time mtime; } __attribute__ ((packed)); struct grub_btrfs_extent_data @@ -1263,7 +1271,6 @@ grub_btrfs_dir (grub_device_t device, const char *path, } do { - struct grub_dirhook_info info; struct grub_btrfs_dir_item *cdirel; if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM || key_out.object_id != key_in.object_id) @@ -1295,9 +1302,20 @@ grub_btrfs_dir (grub_device_t device, const char *path, + grub_le_to_cpu16 (cdirel->m))) { char c; + struct grub_btrfs_inode inode; + struct grub_dirhook_info info; + err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id, + tree); + grub_memset (&info, 0, sizeof (info)); + if (err) + grub_errno = GRUB_ERR_NONE; + else + { + info.mtime = inode.mtime.sec; + info.mtimeset = 1; + } c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; - grub_memset (&info, 0, sizeof (info)); info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY); if (hook (cdirel->name, &info)) goto out; From 11b970d7c9e820e4d7574d0cb63ddc55cd27176e Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 7 Jan 2011 17:24:25 +0000 Subject: [PATCH 24/34] Always initialise *relroot in grub_find_root_device_from_mountinfo, otherwise we free an uninitialised pointer if /proc is unmounted. Reported by: Scott Moser. --- grub-core/kern/emu/getroot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 373834127..a4cfaffc9 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -111,13 +111,13 @@ grub_find_root_device_from_mountinfo (const char *dir, char **relroot) size_t len = 0; char *ret = NULL; + if (relroot) + *relroot = NULL; + fp = fopen ("/proc/self/mountinfo", "r"); if (! fp) return NULL; /* fall through to other methods */ - if (relroot) - *relroot = NULL; - while (getline (&buf, &len, fp) > 0) { int mnt_id, parent_mnt_id; From bd1a4147141708625214564959684257a6614935 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Wed, 12 Jan 2011 17:27:52 -0600 Subject: [PATCH 25/34] Resolve the device returned by grub_find_root_device_from_mountinfo or find_root_device_from_libzfs using grub_find_device. Reported by: Roderich Schupp. --- grub-core/kern/emu/getroot.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index df3a4d765..92ff971e5 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -467,7 +467,7 @@ grub_find_device (const char *path, dev_t dev) char * grub_guess_root_device (const char *dir) { - char *os_dev; + char *os_dev = NULL; #ifdef __GNU__ file_t file; mach_port_t *ports; @@ -526,30 +526,42 @@ grub_guess_root_device (const char *dir) mach_port_deallocate (mach_task_self (), file); #else /* !__GNU__ */ struct stat st; + dev_t dev; #ifdef __linux__ - os_dev = grub_find_root_device_from_mountinfo (dir, NULL); - if (os_dev) - return os_dev; + if (!os_dev) + os_dev = grub_find_root_device_from_mountinfo (dir, NULL); #endif /* __linux__ */ #if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) - os_dev = find_root_device_from_libzfs (dir); - if (os_dev) - return os_dev; + if (!os_dev) + os_dev = find_root_device_from_libzfs (dir); #endif - if (stat (dir, &st) < 0) - grub_util_error ("cannot stat `%s'", dir); + if (os_dev) + { + if (stat (os_dev, &st) >= 0) + dev = st.st_rdev; + else + grub_util_error ("cannot stat `%s'", os_dev); + free (os_dev); + } + else + { + if (stat (dir, &st) >= 0) + dev = st.st_dev; + else + grub_util_error ("cannot stat `%s'", dir); + } #ifdef __CYGWIN__ /* Cygwin specific function. */ - os_dev = grub_find_device (dir, st.st_dev); + os_dev = grub_find_device (dir, dev); #else /* This might be truly slow, but is there any better way? */ - os_dev = grub_find_device ("/dev", st.st_dev); + os_dev = grub_find_device ("/dev", dev); #endif #endif /* !__GNU__ */ From 8c2c4ff2f533cf37c2025655497f3dfc8cfd5eef Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 4 Feb 2011 13:33:16 +0000 Subject: [PATCH 26/34] Handle empty dir passed to grub_find_root_device_from_mountinfo; fixes grub-mkrelpath on btrfs subvolumes. --- grub-core/kern/emu/getroot.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 92ff971e5..c1ed35b2d 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -111,6 +111,8 @@ grub_find_root_device_from_mountinfo (const char *dir, char **relroot) size_t len = 0; char *ret = NULL; + if (! *dir) + dir = "/"; if (relroot) *relroot = NULL; From 5870a4a06f11059defad9ff546b7b2811a838873 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 4 Feb 2011 16:35:07 +0000 Subject: [PATCH 27/34] typo --- grub-core/fs/btrfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index b7c59065c..ac90c055a 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -729,7 +729,7 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t paddr; stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); - /* Right now the redundancy handlind is easy. + /* Right now the redundancy handling is easy. With RAID5-like it will be more difficult. */ stripe += stripen + i; From ebad0b81be1e6cd148688bcb0c4192b1923a51d0 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Wed, 23 Mar 2011 14:45:04 +0000 Subject: [PATCH 28/34] remove unused variable --- grub-core/kern/emu/getroot.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 601ac8b04..71b9a3c50 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -131,7 +131,6 @@ grub_find_root_device_from_mountinfo (const char *dir, char **relroot) size_t enc_path_len; const char *sep; char fstype[PATH_MAX], device[PATH_MAX]; - struct stat st; if (sscanf (buf, "%d %d %u:%u %s %s%n", &mnt_id, &parent_mnt_id, &major, &minor, enc_root, enc_path, From 099821e9e4d55abe11054380068decda0cec4a17 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 9 Apr 2011 21:55:50 +0200 Subject: [PATCH 29/34] Fix RAID1/duplicated chunk size calculation --- grub-core/fs/btrfs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ac90c055a..7632d535c 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -664,13 +664,9 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, case GRUB_BTRFS_CHUNK_TYPE_RAID1: /* FIXME: Use redundancy. */ { - grub_uint32_t stripe_length; - stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), - grub_le_to_cpu16 (chunk->nstripes), - NULL); stripen = 0; stripe_offset = off; - csize = stripe_length - off; + csize = grub_le_to_cpu64 (chunk->size) - off; redundancy = 2; break; } From e8980227e8e3df225dfecd9bb98d5ea515d7b041 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 11 Apr 2011 07:49:26 +0200 Subject: [PATCH 30/34] Remove stale comment about redundancy --- grub-core/fs/btrfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 7632d535c..16aa0742a 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -662,7 +662,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, } case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED: case GRUB_BTRFS_CHUNK_TYPE_RAID1: - /* FIXME: Use redundancy. */ { stripen = 0; stripe_offset = off; @@ -686,7 +685,6 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, break; } case GRUB_BTRFS_CHUNK_TYPE_RAID10: - /* FIXME: Use redundancy. */ { grub_uint64_t middle, high; grub_uint32_t low; From ec25b87d2904ef8ea1417bb0e61397c008e1b7f1 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 11 Apr 2011 07:50:22 +0200 Subject: [PATCH 31/34] Add dprintfs to report chunk lookups --- grub-core/fs/btrfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 16aa0742a..fb7469b67 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -589,6 +589,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, struct grub_btrfs_key key_out; int challoc = 0; grub_device_t dev; + grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n", + addr); for (ptr = data->sblock.bootstrap_mapping; ptr < data->sblock.bootstrap_mapping + sizeof (data->sblock.bootstrap_mapping) @@ -647,6 +649,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, unsigned redundancy = 1; unsigned i, j; + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T + " (%d stripes (%d substripes) of %" + PRIxGRUB_UINT64_T ")\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + grub_le_to_cpu16 (chunk->nsubstripes), + grub_le_to_cpu64 (chunk->stripe_length)); + switch (grub_le_to_cpu64 (chunk->type) & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE) { From 228f95a2502a35a5a2e9a8a8eab0e66fa2c515ad Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 11 Apr 2011 07:51:15 +0200 Subject: [PATCH 32/34] Fix filename comparison --- grub-core/fs/btrfs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index fb7469b67..add590dfb 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1103,12 +1103,9 @@ find_path (struct grub_btrfs_data *data, + grub_le_to_cpu16 (cdirel->n) + grub_le_to_cpu16 (cdirel->m))) { - char c; - c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; - cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; - if (grub_strncmp (cdirel->name, ctoken, ctokenlen) == 0) + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) break; - cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; } if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl >= (grub_ssize_t) elemsize) From 565f0763118ac5d7789f4a85075e8f013f13ac69 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 11 Apr 2011 07:52:39 +0200 Subject: [PATCH 33/34] Take extent offset in account on uncompressed extents --- grub-core/fs/btrfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index add590dfb..7b1888502 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -989,6 +989,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, } err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (data->extent->laddr) + + grub_le_to_cpu64 (data->extent->offset) + extoff, buf, csize); if (err) From 6a01f54affc70b25a0a0507775ba159423402387 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 11 Apr 2011 07:53:21 +0200 Subject: [PATCH 34/34] use actually filled extent size if available --- grub-core/fs/btrfs.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 7b1888502..0b0e5259c 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -83,6 +83,7 @@ struct grub_btrfs_data /* Cached extent data. */ grub_uint64_t extstart; + grub_uint64_t extend; grub_uint64_t extino; grub_uint64_t exttree; grub_size_t extsize; @@ -202,6 +203,7 @@ struct grub_btrfs_extent_data grub_uint64_t laddr; grub_uint64_t compressed_size; grub_uint64_t offset; + grub_uint64_t filled; }; }; } __attribute__ ((packed)); @@ -872,12 +874,12 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_err_t err; grub_off_t extoff; if (!data->extent || data->extstart > pos || data->extino != ino - || data->exttree != tree - || grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) + || data->exttree != tree || data->extend <= pos) { struct grub_btrfs_key key_in, key_out; grub_disk_addr_t elemaddr; grub_size_t elemsize; + grub_free (data->extent); key_in.object_id = ino; key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; @@ -904,15 +906,29 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, data->extent, elemsize); if (err) return err; - if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos) - return grub_error (GRUB_ERR_BAD_FS, "extent not found"); + + data->extend = data->extstart + + grub_le_to_cpu64 (data->extent->size); + if (data->extent->type == GRUB_BTRFS_EXTENT_REGULAR + && (char *) &data->extent + elemsize + >= (char *) &data->extent->filled + + sizeof (data->extent->filled)) + data->extend = data->extstart + + grub_le_to_cpu64 (data->extent->filled); + grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%" - PRIxGRUB_UINT64_T "\n", + PRIxGRUB_UINT64_T " (0x%" + PRIxGRUB_UINT64_T ")\n", grub_le_to_cpu64 (key_out.offset), - grub_le_to_cpu64 (data->extent->size)); + grub_le_to_cpu64 (data->extent->size), + grub_le_to_cpu64 (data->extent->filled)); + if (data->extend <= pos) + { + grub_error (GRUB_ERR_BAD_FS, "extent not found"); + return -1; + } } - csize = grub_le_to_cpu64 (data->extent->size) - + grub_le_to_cpu64 (data->extstart) - pos; + csize = data->extend - pos; extoff = pos - data->extstart; if (csize > len) csize = len;