symlink loop detection. btrfs-raid0 and raid1 support
This commit is contained in:
parent
0e761d3dbd
commit
db51e201fc
1 changed files with 295 additions and 137 deletions
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue