symlink support

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-12-03 10:44:47 +01:00
parent a43c4bc55f
commit 34018a7d1f

View file

@ -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