From 34018a7d1fafb91778195fc6aa6970b9aac3ba19 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 3 Dec 2010 10:44:47 +0100 Subject: [PATCH] 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