symlink loop detection. btrfs-raid0 and raid1 support

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-12-03 16:56:49 +01:00
parent 0e761d3dbd
commit db51e201fc

View file

@ -64,13 +64,22 @@ struct btrfs_header
grub_uint8_t level; grub_uint8_t level;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct grub_btrfs_device_desc
{
grub_device_t dev;
grub_uint64_t id;
};
struct grub_btrfs_data struct grub_btrfs_data
{ {
struct grub_btrfs_superblock sblock; struct grub_btrfs_superblock sblock;
unsigned int sblock_number;
grub_uint64_t tree; grub_uint64_t tree;
grub_uint64_t inode; grub_uint64_t inode;
struct grub_btrfs_device_desc *devices_attached;
unsigned n_devices_attached;
unsigned n_devices_allocated;
/* Cached extent data. */ /* Cached extent data. */
grub_uint64_t extstart; grub_uint64_t extstart;
grub_uint64_t extino; grub_uint64_t extino;
@ -96,9 +105,15 @@ struct grub_btrfs_chunk_item
grub_uint64_t size; grub_uint64_t size;
grub_uint64_t dummy; grub_uint64_t dummy;
grub_uint64_t stripe_length; 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 nstripes;
grub_uint16_t dummy3; grub_uint16_t nsubstripes;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct grub_btrfs_chunk_stripe 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 static grub_err_t
grub_btrfs_read_logical (struct grub_btrfs_data *data, 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);
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 static int
key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b) 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 static int
next (struct grub_btrfs_data *data, grub_disk_t disk, next (struct grub_btrfs_data *data,
struct grub_btrfs_leaf_descriptor *desc, struct grub_btrfs_leaf_descriptor *desc,
grub_disk_addr_t *outaddr, grub_size_t *outsize, grub_disk_addr_t *outaddr, grub_size_t *outsize,
struct grub_btrfs_key *key_out) 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 grub_btrfs_internal_node node;
struct btrfs_header head; struct btrfs_header head;
err = grub_btrfs_read_logical (data, disk, err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
desc->data[desc->depth - 1].iter
* sizeof (node) * sizeof (node)
+ sizeof (struct btrfs_header) + sizeof (struct btrfs_header)
+ desc->data[desc->depth - 1].addr, &node, + desc->data[desc->depth - 1].addr, &node,
@ -270,8 +313,7 @@ next (struct grub_btrfs_data *data, grub_disk_t disk,
if (err) if (err)
return -err; return -err;
err = grub_btrfs_read_logical (data, disk, err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
grub_le_to_cpu64 (node.addr), &head,
sizeof (head)); sizeof (head));
if (err) if (err)
return -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, save_ref (desc, grub_le_to_cpu64 (node.addr), 0,
grub_le_to_cpu32 (head.nitems), !head.level); grub_le_to_cpu32 (head.nitems), !head.level);
} }
err = grub_btrfs_read_logical (data, disk, err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
desc->data[desc->depth - 1].iter
* sizeof (leaf) * sizeof (leaf)
+ sizeof (struct btrfs_header) + sizeof (struct btrfs_header)
+ desc->data[desc->depth - 1].addr, &leaf, + 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 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, const struct grub_btrfs_key *key_in,
struct grub_btrfs_key *key_out, struct grub_btrfs_key *key_out,
grub_disk_addr_t root, grub_disk_addr_t root,
@ -327,7 +368,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk,
reiter: reiter:
depth++; depth++;
/* FIXME: preread few nodes into buffer. */ /* 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) if (err)
return err; return err;
addr += sizeof (head); 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)); grub_memset (&node_last, 0, sizeof (node_last));
for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
{ {
err = grub_btrfs_read_logical (data, disk, addr err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
+ i * sizeof (node),
&node, sizeof (node)); &node, sizeof (node));
if (err) if (err)
return err; return err;
@ -391,7 +431,7 @@ lower_bound (struct grub_btrfs_data *data, grub_disk_t disk,
int have_last = 0; int have_last = 0;
for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) 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)); &leaf, sizeof (leaf));
if (err) if (err)
return 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 static grub_err_t
grub_btrfs_read_logical (struct grub_btrfs_data *data, 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) void *buf, grub_size_t size)
{ {
while (size > 0) while (size > 0)
@ -451,14 +565,11 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data,
struct grub_btrfs_key *key; struct grub_btrfs_key *key;
struct grub_btrfs_chunk_item *chunk; struct grub_btrfs_chunk_item *chunk;
struct grub_btrfs_chunk_stripe *stripe; struct grub_btrfs_chunk_stripe *stripe;
grub_size_t csize; grub_ssize_t csize;
grub_err_t err; 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; struct grub_btrfs_key key_out;
int challoc = 0; int challoc = 0;
grub_device_t dev;
for (ptr = data->sblock.bootstrap_mapping; for (ptr = data->sblock.bootstrap_mapping;
ptr < data->sblock.bootstrap_mapping ptr < data->sblock.bootstrap_mapping
+ sizeof (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.object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK; key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK;
key_in.offset = addr; key_in.offset = addr;
err = lower_bound (data, disk, err = lower_bound (data, &key_in, &key_out,
&key_in, &key_out,
grub_le_to_cpu64 (data->sblock.chunk_tree), grub_le_to_cpu64 (data->sblock.chunk_tree),
&chaddr, &chsize, NULL); &chaddr, &chsize, NULL);
if (err) if (err)
@ -502,55 +612,99 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data,
return grub_errno; return grub_errno;
challoc = 1; challoc = 1;
err = grub_btrfs_read_logical (data, disk, chaddr, err = grub_btrfs_read_logical (data, chaddr, chunk, chsize);
chunk, chsize);
if (err) if (err)
{ {
grub_free (chunk); grub_free (chunk);
return err; 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: chunk_found:
stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), {
grub_le_to_cpu16 (chunk->nstripes), grub_uint32_t stripen;
NULL); grub_uint32_t stripe_offset;
stripen = grub_divmod64 (addr - grub_le_to_cpu64 (key->offset), grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset);
stripe_length, &stripe_offset); grub_disk_addr_t paddr;
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 stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
" for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr, switch (grub_le_to_cpu64 (chunk->type)
addr); & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE)
err = grub_disk_read (disk, paddr >> GRUB_DISK_SECTOR_BITS, {
paddr & (GRUB_DISK_SECTOR_SIZE - 1), csize, buf); 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; size -= csize;
buf = (grub_uint8_t *) buf + csize; buf = (grub_uint8_t *) buf + csize;
addr += csize; addr += csize;
@ -561,54 +715,57 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data,
} }
static struct grub_btrfs_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)); struct grub_btrfs_data *data;
unsigned i; grub_err_t err;
grub_err_t err = GRUB_ERR_NONE;
if (!dev->disk)
{
grub_error (GRUB_ERR_BAD_FS, "not BtrFS");
return NULL;
}
data = grub_zalloc (sizeof (*data));
if (! data) if (! data)
return NULL; 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; grub_free (data);
err = grub_disk_read (disk, superblock_sectors[i], 0, return NULL;
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;
}
} }
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"); grub_free (data);
goto fail; return NULL;
} }
data->n_devices_attached = 1;
if (err == GRUB_ERR_OUT_OF_RANGE) data->devices_attached[0].dev = dev;
grub_errno = err = GRUB_ERR_NONE; data->devices_attached[0].id = data->sblock.this_device.device_id;
grub_dprintf ("btrfs", "using superblock %d\n", data->sblock_number);
return data; 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); grub_free (data);
return NULL;
} }
static grub_err_t 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, struct grub_btrfs_inode *inode, grub_uint64_t num,
grub_uint64_t tree) 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.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
key_in.offset = 0; 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); &elemaddr, &elemsize, NULL);
if (err) if (err)
return 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) || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM)
return grub_error (GRUB_ERR_BAD_FS, "inode not found"); 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 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_uint64_t ino, grub_uint64_t tree,
grub_off_t pos0, char *buf, grub_size_t len) 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.object_id = ino;
key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
key_in.offset = grub_cpu_to_le64 (pos); 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); &elemaddr, &elemsize, NULL);
if (err) if (err)
return -1; return -1;
@ -671,7 +828,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk,
if (!data->extent) if (!data->extent)
return grub_errno; return grub_errno;
err = grub_btrfs_read_logical (data, disk, elemaddr, err = grub_btrfs_read_logical (data, elemaddr,
data->extent, elemsize); data->extent, elemsize);
if (err) if (err)
return 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); grub_memset (buf, 0, csize);
break; break;
} }
err = grub_btrfs_read_logical (data, disk, err = grub_btrfs_read_logical (data,
grub_le_to_cpu64 (data->extent->laddr) grub_le_to_cpu64 (data->extent->laddr)
+ extoff, + extoff,
buf, csize); buf, csize);
@ -742,7 +899,6 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk,
static grub_err_t static grub_err_t
find_path (struct grub_btrfs_data *data, find_path (struct grub_btrfs_data *data,
grub_disk_t disk,
const char *path, struct grub_btrfs_key *key, const char *path, struct grub_btrfs_key *key,
grub_uint64_t *tree, grub_uint8_t *type) grub_uint64_t *tree, grub_uint8_t *type)
{ {
@ -757,6 +913,7 @@ find_path (struct grub_btrfs_data *data,
const char *ctoken; const char *ctoken;
grub_size_t ctokenlen; grub_size_t ctokenlen;
char *path_alloc = NULL; char *path_alloc = NULL;
unsigned symlinks_max = 32;
*type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
*tree = data->sblock.root_tree; *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->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); 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); &elemaddr, &elemsize, NULL);
if (err) if (err)
{ {
@ -822,8 +979,7 @@ find_path (struct grub_btrfs_data *data,
} }
} }
err = grub_btrfs_read_logical (data, disk, elemaddr, err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
direl, elemsize);
if (err) if (err)
{ {
grub_free (direl); grub_free (direl);
@ -860,7 +1016,15 @@ find_path (struct grub_btrfs_data *data,
{ {
struct grub_btrfs_inode inode; struct grub_btrfs_inode inode;
char *tmp; 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); cdirel->key.object_id, *tree);
if (err) if (err)
{ {
@ -877,7 +1041,7 @@ find_path (struct grub_btrfs_data *data,
return grub_errno; 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, *tree, 0, tmp,
grub_le_to_cpu64 (inode.size)) grub_le_to_cpu64 (inode.size))
!= (grub_ssize_t) 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: case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
{ {
struct grub_btrfs_root_item ri; 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, data->sblock.root_tree,
&elemaddr, &elemsize, NULL); &elemaddr, &elemsize, NULL);
if (err) if (err)
@ -925,7 +1089,7 @@ find_path (struct grub_btrfs_data *data,
grub_free (path_alloc); grub_free (path_alloc);
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); 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)); &ri, sizeof (ri));
if (err) if (err)
{ {
@ -967,7 +1131,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename, int (*hook) (const char *filename,
const struct grub_dirhook_info *info)) 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; struct grub_btrfs_key key_in, key_out;
grub_err_t err; grub_err_t err;
grub_disk_addr_t elemaddr; grub_disk_addr_t elemaddr;
@ -982,21 +1146,20 @@ grub_btrfs_dir (grub_device_t device, const char *path,
if (!data) if (!data)
return grub_errno; 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) if (err)
return err; return err;
if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
err = lower_bound (data, device->disk, &key_in, &key_out, err = lower_bound (data, &key_in, &key_out, tree,
tree,
&elemaddr, &elemsize, &desc); &elemaddr, &elemsize, &desc);
if (err) if (err)
return err; return err;
if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
|| key_out.object_id != key_in.object_id) || 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) if (r <= 0)
{ {
free_iterator (&desc); 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, err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
direl, elemsize);
if (err) if (err)
return err; return err;
@ -1046,7 +1208,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
goto out; goto out;
cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; 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); while (r > 0);
@ -1054,7 +1216,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
grub_free (direl); grub_free (direl);
free_iterator (&desc); free_iterator (&desc);
grub_free (data); grub_btrfs_unmount (data);
return -r; return -r;
} }
@ -1062,7 +1224,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
static grub_err_t static grub_err_t
grub_btrfs_open (struct grub_file *file, const char *name) 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; grub_err_t err;
struct grub_btrfs_inode inode; struct grub_btrfs_inode inode;
grub_uint8_t type; grub_uint8_t type;
@ -1071,24 +1233,23 @@ grub_btrfs_open (struct grub_file *file, const char *name)
if (!data) if (!data)
return grub_errno; 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) if (err)
{ {
grub_free (data); grub_btrfs_unmount (data);
return err; return err;
} }
if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) 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"); return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
} }
data->inode = key_in.object_id; data->inode = key_in.object_id;
err = grub_btrfs_read_inode (data, file->device->disk, &inode, data->inode, err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
data->tree);
if (err) if (err)
{ {
grub_free (data); grub_btrfs_unmount (data);
return err; return err;
} }
@ -1101,10 +1262,7 @@ grub_btrfs_open (struct grub_file *file, const char *name)
static grub_err_t static grub_err_t
grub_btrfs_close (grub_file_t file) grub_btrfs_close (grub_file_t file)
{ {
struct grub_btrfs_data *data = file->data; grub_btrfs_unmount (file->data);
grub_free (data->extent);
grub_free (data);
return GRUB_ERR_NONE; 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; 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); data->tree, file->offset, buf, len);
} }
@ -1125,7 +1283,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid)
*uuid = NULL; *uuid = NULL;
data = grub_btrfs_mount (device->disk); data = grub_btrfs_mount (device);
if (! data) if (! data)
return grub_errno; 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[6]),
grub_be_to_cpu16 (data->sblock.uuid[7])); grub_be_to_cpu16 (data->sblock.uuid[7]));
grub_free (data); grub_btrfs_unmount (data);
return grub_errno; return grub_errno;
} }
@ -1151,13 +1309,13 @@ grub_btrfs_label (grub_device_t device, char **label)
*label = NULL; *label = NULL;
data = grub_btrfs_mount (device->disk); data = grub_btrfs_mount (device);
if (! data) if (! data)
return grub_errno; return grub_errno;
*label = grub_strndup (data->sblock.label, sizeof (data->sblock.label)); *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
grub_free (data); grub_btrfs_unmount (data);
return grub_errno; return grub_errno;
} }