| /* reiserfs.c - ReiserFS versions up to 3.6 */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2003,2004,2005,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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| TODO: |
| implement journal handling (ram replay) |
| test tail packing & direct files |
| validate partition label position |
| */ |
| |
| #if 0 |
| # define GRUB_REISERFS_DEBUG |
| # define GRUB_REISERFS_JOURNALING |
| # define GRUB_HEXDUMP |
| #endif |
| |
| #include <grub/err.h> |
| #include <grub/file.h> |
| #include <grub/mm.h> |
| #include <grub/misc.h> |
| #include <grub/disk.h> |
| #include <grub/dl.h> |
| #include <grub/types.h> |
| #include <grub/fshelp.h> |
| #include <grub/i18n.h> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| #define MIN(a, b) \ |
| ({ typeof (a) _a = (a); \ |
| typeof (b) _b = (b); \ |
| _a < _b ? _a : _b; }) |
| |
| #define MAX(a, b) \ |
| ({ typeof (a) _a = (a); \ |
| typeof (b) _b = (b); \ |
| _a > _b ? _a : _b; }) |
| |
| #define REISERFS_SUPER_BLOCK_OFFSET 0x10000 |
| #define REISERFS_MAGIC_LEN 12 |
| #define REISERFS_MAGIC_STRING "ReIsEr" |
| #define REISERFS_MAGIC_DESC_BLOCK "ReIsErLB" |
| /* If the 3rd bit of an item state is set, then it's visible. */ |
| #define GRUB_REISERFS_VISIBLE_MASK ((grub_uint16_t) 0x04) |
| |
| #define S_IFLNK 0xA000 |
| |
| static grub_dl_t my_mod; |
| |
| #define assert(boolean) real_assert (boolean, GRUB_FILE, __LINE__) |
| static inline void |
| real_assert (int boolean, const char *file, const int line) |
| { |
| if (! boolean) |
| grub_printf ("Assertion failed at %s:%d\n", file, line); |
| } |
| |
| enum grub_reiserfs_item_type |
| { |
| GRUB_REISERFS_STAT, |
| GRUB_REISERFS_DIRECTORY, |
| GRUB_REISERFS_DIRECT, |
| GRUB_REISERFS_INDIRECT, |
| /* Matches both _DIRECT and _INDIRECT when searching. */ |
| GRUB_REISERFS_ANY, |
| GRUB_REISERFS_UNKNOWN |
| }; |
| |
| struct grub_reiserfs_superblock |
| { |
| grub_uint32_t block_count; |
| grub_uint32_t block_free_count; |
| grub_uint32_t root_block; |
| grub_uint32_t journal_block; |
| grub_uint32_t journal_device; |
| grub_uint32_t journal_original_size; |
| grub_uint32_t journal_max_transaction_size; |
| grub_uint32_t journal_block_count; |
| grub_uint32_t journal_max_batch; |
| grub_uint32_t journal_max_commit_age; |
| grub_uint32_t journal_max_transaction_age; |
| grub_uint16_t block_size; |
| grub_uint16_t oid_max_size; |
| grub_uint16_t oid_current_size; |
| grub_uint16_t state; |
| grub_uint8_t magic_string[REISERFS_MAGIC_LEN]; |
| grub_uint32_t function_hash_code; |
| grub_uint16_t tree_height; |
| grub_uint16_t bitmap_number; |
| grub_uint16_t version; |
| grub_uint16_t reserved; |
| grub_uint32_t inode_generation; |
| grub_uint8_t unused[4]; |
| grub_uint16_t uuid[8]; |
| char label[16]; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_journal_header |
| { |
| grub_uint32_t last_flush_uid; |
| grub_uint32_t unflushed_offset; |
| grub_uint32_t mount_id; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_description_block |
| { |
| grub_uint32_t id; |
| grub_uint32_t len; |
| grub_uint32_t mount_id; |
| grub_uint32_t real_blocks[0]; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_commit_block |
| { |
| grub_uint32_t id; |
| grub_uint32_t len; |
| grub_uint32_t real_blocks[0]; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_stat_item_v1 |
| { |
| grub_uint16_t mode; |
| grub_uint16_t hardlink_count; |
| grub_uint16_t uid; |
| grub_uint16_t gid; |
| grub_uint32_t size; |
| grub_uint32_t atime; |
| grub_uint32_t mtime; |
| grub_uint32_t ctime; |
| grub_uint32_t rdev; |
| grub_uint32_t first_direct_byte; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_stat_item_v2 |
| { |
| grub_uint16_t mode; |
| grub_uint16_t reserved; |
| grub_uint32_t hardlink_count; |
| grub_uint64_t size; |
| grub_uint32_t uid; |
| grub_uint32_t gid; |
| grub_uint32_t atime; |
| grub_uint32_t mtime; |
| grub_uint32_t ctime; |
| grub_uint32_t blocks; |
| grub_uint32_t first_direct_byte; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_key |
| { |
| grub_uint32_t directory_id; |
| grub_uint32_t object_id; |
| union |
| { |
| struct |
| { |
| grub_uint32_t offset; |
| grub_uint32_t type; |
| } GRUB_PACKED v1; |
| struct |
| { |
| grub_uint64_t offset_type; |
| } GRUB_PACKED v2; |
| } u; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_item_header |
| { |
| struct grub_reiserfs_key key; |
| union |
| { |
| grub_uint16_t free_space; |
| grub_uint16_t entry_count; |
| } GRUB_PACKED u; |
| grub_uint16_t item_size; |
| grub_uint16_t item_location; |
| grub_uint16_t version; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_block_header |
| { |
| grub_uint16_t level; |
| grub_uint16_t item_count; |
| grub_uint16_t free_space; |
| grub_uint16_t reserved; |
| struct grub_reiserfs_key block_right_delimiting_key; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_disk_child |
| { |
| grub_uint32_t block_number; |
| grub_uint16_t size; |
| grub_uint16_t reserved; |
| } GRUB_PACKED; |
| |
| struct grub_reiserfs_directory_header |
| { |
| grub_uint32_t offset; |
| grub_uint32_t directory_id; |
| grub_uint32_t object_id; |
| grub_uint16_t location; |
| grub_uint16_t state; |
| } GRUB_PACKED; |
| |
| struct grub_fshelp_node |
| { |
| struct grub_reiserfs_data *data; |
| grub_uint32_t block_number; /* 0 if node is not found. */ |
| grub_uint16_t block_position; |
| grub_uint64_t next_offset; |
| grub_int32_t mtime; |
| grub_off_t size; |
| enum grub_reiserfs_item_type type; /* To know how to read the header. */ |
| struct grub_reiserfs_item_header header; |
| }; |
| |
| /* Returned when opening a file. */ |
| struct grub_reiserfs_data |
| { |
| struct grub_reiserfs_superblock superblock; |
| grub_disk_t disk; |
| }; |
| |
| static grub_ssize_t |
| grub_reiserfs_read_real (struct grub_fshelp_node *node, |
| grub_off_t off, char *buf, grub_size_t len, |
| grub_disk_read_hook_t read_hook, |
| void *read_hook_data); |
| |
| /* Internal-only functions. Not to be used outside of this file. */ |
| |
| /* Return the type of given v2 key. */ |
| static enum grub_reiserfs_item_type |
| grub_reiserfs_get_key_v2_type (const struct grub_reiserfs_key *key) |
| { |
| switch (grub_le_to_cpu64 (key->u.v2.offset_type) >> 60) |
| { |
| case 0: |
| return GRUB_REISERFS_STAT; |
| case 15: |
| return GRUB_REISERFS_ANY; |
| case 3: |
| return GRUB_REISERFS_DIRECTORY; |
| case 2: |
| return GRUB_REISERFS_DIRECT; |
| case 1: |
| return GRUB_REISERFS_INDIRECT; |
| } |
| return GRUB_REISERFS_UNKNOWN; |
| } |
| |
| /* Return the type of given v1 key. */ |
| static enum grub_reiserfs_item_type |
| grub_reiserfs_get_key_v1_type (const struct grub_reiserfs_key *key) |
| { |
| switch (grub_le_to_cpu32 (key->u.v1.type)) |
| { |
| case 0: |
| return GRUB_REISERFS_STAT; |
| case 555: |
| return GRUB_REISERFS_ANY; |
| case 500: |
| return GRUB_REISERFS_DIRECTORY; |
| case 0x20000000: |
| case 0xFFFFFFFF: |
| return GRUB_REISERFS_DIRECT; |
| case 0x10000000: |
| case 0xFFFFFFFE: |
| return GRUB_REISERFS_INDIRECT; |
| } |
| return GRUB_REISERFS_UNKNOWN; |
| } |
| |
| /* Return 1 if the given key is version 1 key, 2 otherwise. */ |
| static int |
| grub_reiserfs_get_key_version (const struct grub_reiserfs_key *key) |
| { |
| return grub_reiserfs_get_key_v1_type (key) == GRUB_REISERFS_UNKNOWN ? 2 : 1; |
| } |
| |
| #ifdef GRUB_HEXDUMP |
| static void |
| grub_hexdump (char *buffer, grub_size_t len) |
| { |
| grub_size_t a; |
| for (a = 0; a < len; a++) |
| { |
| if (! (a & 0x0F)) |
| grub_printf ("\n%08x ", a); |
| grub_printf ("%02x ", |
| ((unsigned int) ((unsigned char *) buffer)[a]) & 0xFF); |
| } |
| grub_printf ("\n"); |
| } |
| #endif |
| |
| #ifdef GRUB_REISERFS_DEBUG |
| static grub_uint64_t |
| grub_reiserfs_get_key_offset (const struct grub_reiserfs_key *key); |
| |
| static enum grub_reiserfs_item_type |
| grub_reiserfs_get_key_type (const struct grub_reiserfs_key *key); |
| |
| static void |
| grub_reiserfs_print_key (const struct grub_reiserfs_key *key) |
| { |
| unsigned int a; |
| char *reiserfs_type_strings[] = { |
| "stat ", |
| "directory", |
| "direct ", |
| "indirect ", |
| "any ", |
| "unknown " |
| }; |
| |
| for (a = 0; a < sizeof (struct grub_reiserfs_key); a++) |
| grub_printf ("%02x ", ((unsigned int) ((unsigned char *) key)[a]) & 0xFF); |
| grub_printf ("parent id = 0x%08x, self id = 0x%08x, type = %s, offset = ", |
| grub_le_to_cpu32 (key->directory_id), |
| grub_le_to_cpu32 (key->object_id), |
| reiserfs_type_strings [grub_reiserfs_get_key_type (key)]); |
| if (grub_reiserfs_get_key_version (key) == 1) |
| grub_printf("%08x", (unsigned int) grub_reiserfs_get_key_offset (key)); |
| else |
| grub_printf("0x%07x%08x", |
| (unsigned) (grub_reiserfs_get_key_offset (key) >> 32), |
| (unsigned) (grub_reiserfs_get_key_offset (key) & 0xFFFFFFFF)); |
| grub_printf ("\n"); |
| } |
| #endif |
| |
| /* Return the offset of given key. */ |
| static grub_uint64_t |
| grub_reiserfs_get_key_offset (const struct grub_reiserfs_key *key) |
| { |
| if (grub_reiserfs_get_key_version (key) == 1) |
| return grub_le_to_cpu32 (key->u.v1.offset); |
| else |
| return grub_le_to_cpu64 (key->u.v2.offset_type) & (~0ULL >> 4); |
| } |
| |
| /* Set the offset of given key. */ |
| static void |
| grub_reiserfs_set_key_offset (struct grub_reiserfs_key *key, |
| grub_uint64_t value) |
| { |
| if (grub_reiserfs_get_key_version (key) == 1) |
| key->u.v1.offset = grub_cpu_to_le32 (value); |
| else |
| key->u.v2.offset_type \ |
| = ((key->u.v2.offset_type & grub_cpu_to_le64_compile_time (15ULL << 60)) |
| | grub_cpu_to_le64 (value & (~0ULL >> 4))); |
| } |
| |
| /* Return the type of given key. */ |
| static enum grub_reiserfs_item_type |
| grub_reiserfs_get_key_type (const struct grub_reiserfs_key *key) |
| { |
| if (grub_reiserfs_get_key_version (key) == 1) |
| return grub_reiserfs_get_key_v1_type (key); |
| else |
| return grub_reiserfs_get_key_v2_type (key); |
| } |
| |
| /* Set the type of given key, with given version number. */ |
| static void |
| grub_reiserfs_set_key_type (struct grub_reiserfs_key *key, |
| enum grub_reiserfs_item_type grub_type, |
| int version) |
| { |
| grub_uint32_t type; |
| |
| switch (grub_type) |
| { |
| case GRUB_REISERFS_STAT: |
| type = 0; |
| break; |
| case GRUB_REISERFS_ANY: |
| type = (version == 1) ? 555 : 15; |
| break; |
| case GRUB_REISERFS_DIRECTORY: |
| type = (version == 1) ? 500 : 3; |
| break; |
| case GRUB_REISERFS_DIRECT: |
| type = (version == 1) ? 0xFFFFFFFF : 2; |
| break; |
| case GRUB_REISERFS_INDIRECT: |
| type = (version == 1) ? 0xFFFFFFFE : 1; |
| break; |
| default: |
| return; |
| } |
| |
| if (version == 1) |
| key->u.v1.type = grub_cpu_to_le32 (type); |
| else |
| key->u.v2.offset_type |
| = ((key->u.v2.offset_type & grub_cpu_to_le64_compile_time (~0ULL >> 4)) |
| | grub_cpu_to_le64 ((grub_uint64_t) type << 60)); |
| |
| assert (grub_reiserfs_get_key_type (key) == grub_type); |
| } |
| |
| /* -1 if key 1 if lower than key 2. |
| 0 if key 1 is equal to key 2. |
| 1 if key 1 is higher than key 2. */ |
| static int |
| grub_reiserfs_compare_keys (const struct grub_reiserfs_key *key1, |
| const struct grub_reiserfs_key *key2) |
| { |
| grub_uint64_t offset1, offset2; |
| enum grub_reiserfs_item_type type1, type2; |
| grub_uint32_t id1, id2; |
| |
| if (! key1 || ! key2) |
| return -2; |
| |
| id1 = grub_le_to_cpu32 (key1->directory_id); |
| id2 = grub_le_to_cpu32 (key2->directory_id); |
| if (id1 < id2) |
| return -1; |
| if (id1 > id2) |
| return 1; |
| |
| id1 = grub_le_to_cpu32 (key1->object_id); |
| id2 = grub_le_to_cpu32 (key2->object_id); |
| if (id1 < id2) |
| return -1; |
| if (id1 > id2) |
| return 1; |
| |
| offset1 = grub_reiserfs_get_key_offset (key1); |
| offset2 = grub_reiserfs_get_key_offset (key2); |
| if (offset1 < offset2) |
| return -1; |
| if (offset1 > offset2) |
| return 1; |
| |
| type1 = grub_reiserfs_get_key_type (key1); |
| type2 = grub_reiserfs_get_key_type (key2); |
| if ((type1 == GRUB_REISERFS_ANY |
| && (type2 == GRUB_REISERFS_DIRECT |
| || type2 == GRUB_REISERFS_INDIRECT)) |
| || (type2 == GRUB_REISERFS_ANY |
| && (type1 == GRUB_REISERFS_DIRECT |
| || type1 == GRUB_REISERFS_INDIRECT))) |
| return 0; |
| if (type1 < type2) |
| return -1; |
| if (type1 > type2) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Find the item identified by KEY in mounted filesystem DATA, and fill ITEM |
| accordingly to what was found. */ |
| static grub_err_t |
| grub_reiserfs_get_item (struct grub_reiserfs_data *data, |
| const struct grub_reiserfs_key *key, |
| struct grub_fshelp_node *item, int exact) |
| { |
| grub_uint32_t block_number; |
| struct grub_reiserfs_block_header *block_header = 0; |
| struct grub_reiserfs_key *block_key = 0; |
| grub_uint16_t block_size, item_count, current_level; |
| grub_uint16_t i; |
| grub_uint16_t previous_level = ~0; |
| struct grub_reiserfs_item_header *item_headers = 0; |
| |
| #if 0 |
| if (! data) |
| { |
| grub_error (GRUB_ERR_BAD_FS, "data is NULL"); |
| goto fail; |
| } |
| |
| if (! key) |
| { |
| grub_error (GRUB_ERR_BAD_FS, "key is NULL"); |
| goto fail; |
| } |
| |
| if (! item) |
| { |
| grub_error (GRUB_ERR_BAD_FS, "item is NULL"); |
| goto fail; |
| } |
| #endif |
| |
| block_size = grub_le_to_cpu16 (data->superblock.block_size); |
| block_number = grub_le_to_cpu32 (data->superblock.root_block); |
| #ifdef GRUB_REISERFS_DEBUG |
| grub_printf("Searching for "); |
| grub_reiserfs_print_key (key); |
| #endif |
| block_header = grub_malloc (block_size); |
| if (! block_header) |
| goto fail; |
| |
| item->next_offset = 0; |
| do |
| { |
| grub_disk_read (data->disk, |
| block_number * (block_size >> GRUB_DISK_SECTOR_BITS), |
| (((grub_off_t) block_number * block_size) |
| & (GRUB_DISK_SECTOR_SIZE - 1)), |
| block_size, block_header); |
| if (grub_errno) |
| goto fail; |
| current_level = grub_le_to_cpu16 (block_header->level); |
| grub_dprintf ("reiserfs_tree", " at level %d\n", current_level); |
| if (current_level >= previous_level) |
| { |
| grub_dprintf ("reiserfs_tree", "level loop detected, aborting\n"); |
| grub_error (GRUB_ERR_BAD_FS, "level loop"); |
| goto fail; |
| } |
| previous_level = current_level; |
| item_count = grub_le_to_cpu16 (block_header->item_count); |
| grub_dprintf ("reiserfs_tree", " number of contained items : %d\n", |
| item_count); |
| if (current_level > 1) |
| { |
| /* Internal node. Navigate to the child that should contain |
| the searched key. */ |
| struct grub_reiserfs_key *keys |
| = (struct grub_reiserfs_key *) (block_header + 1); |
| struct grub_reiserfs_disk_child *children |
| = ((struct grub_reiserfs_disk_child *) |
| (keys + item_count)); |
| |
| for (i = 0; |
| i < item_count |
| && grub_reiserfs_compare_keys (key, &(keys[i])) >= 0; |
| i++) |
| { |
| #ifdef GRUB_REISERFS_DEBUG |
| grub_printf("i %03d/%03d ", i + 1, item_count + 1); |
| grub_reiserfs_print_key (&(keys[i])); |
| #endif |
| } |
| block_number = grub_le_to_cpu32 (children[i].block_number); |
| if ((i < item_count) && (key->directory_id == keys[i].directory_id) |
| && (key->object_id == keys[i].object_id)) |
| item->next_offset = grub_reiserfs_get_key_offset(&(keys[i])); |
| #ifdef GRUB_REISERFS_DEBUG |
| if (i == item_count |
| || grub_reiserfs_compare_keys (key, &(keys[i])) == 0) |
| grub_printf(">"); |
| else |
| grub_printf("<"); |
| if (i < item_count) |
| { |
| grub_printf (" %03d/%03d ", i + 1, item_count + 1); |
| grub_reiserfs_print_key (&(keys[i])); |
| if (i + 1 < item_count) |
| { |
| grub_printf ("+ %03d/%03d ", i + 2, item_count); |
| grub_reiserfs_print_key (&(keys[i + 1])); |
| } |
| } |
| else |
| grub_printf ("Accessing rightmost child at block %d.\n", |
| block_number); |
| #endif |
| } |
| else |
| { |
| /* Leaf node. Check that the key is actually present. */ |
| item_headers |
| = (struct grub_reiserfs_item_header *) (block_header + 1); |
| for (i = 0; |
| i < item_count; |
| i++) |
| { |
| int val; |
| val = grub_reiserfs_compare_keys (key, &(item_headers[i].key)); |
| if (val == 0) |
| { |
| block_key = &(item_headers[i].key); |
| break; |
| } |
| if (val < 0 && exact) |
| break; |
| if (val < 0) |
| { |
| if (i == 0) |
| { |
| grub_error (GRUB_ERR_READ_ERROR, "unexpected btree node"); |
| goto fail; |
| } |
| i--; |
| block_key = &(item_headers[i].key); |
| break; |
| } |
| } |
| if (!exact && i == item_count) |
| { |
| if (i == 0) |
| { |
| grub_error (GRUB_ERR_READ_ERROR, "unexpected btree node"); |
| goto fail; |
| } |
| i--; |
| block_key = &(item_headers[i].key); |
| } |
| } |
| } |
| while (current_level > 1); |
| |
| item->data = data; |
| |
| if (!block_key) |
| { |
| item->block_number = 0; |
| item->block_position = 0; |
| item->type = GRUB_REISERFS_UNKNOWN; |
| #ifdef GRUB_REISERFS_DEBUG |
| grub_printf("Not found.\n"); |
| #endif |
| } |
| else |
| { |
| item->block_number = block_number; |
| item->block_position = i; |
| item->type = grub_reiserfs_get_key_type (block_key); |
| grub_memcpy (&(item->header), &(item_headers[i]), |
| sizeof (struct grub_reiserfs_item_header)); |
| #ifdef GRUB_REISERFS_DEBUG |
| grub_printf ("F %03d/%03d ", i + 1, item_count); |
| grub_reiserfs_print_key (block_key); |
| #endif |
| } |
| |
| assert (grub_errno == GRUB_ERR_NONE); |
| grub_free (block_header); |
| return GRUB_ERR_NONE; |
| |
| fail: |
| assert (grub_errno != GRUB_ERR_NONE); |
| grub_free (block_header); |
| assert (grub_errno != GRUB_ERR_NONE); |
| return grub_errno; |
| } |
| |
| /* Return the path of the file which is pointed at by symlink NODE. */ |
| static char * |
| grub_reiserfs_read_symlink (grub_fshelp_node_t node) |
| { |
| char *symlink_buffer = 0; |
| grub_size_t len = node->size; |
| grub_ssize_t ret; |
| |
| symlink_buffer = grub_malloc (len + 1); |
| if (! symlink_buffer) |
| return 0; |
| |
| ret = grub_reiserfs_read_real (node, 0, symlink_buffer, len, 0, 0); |
| if (ret < 0) |
| { |
| grub_free (symlink_buffer); |
| return 0; |
| } |
| |
| symlink_buffer[ret] = 0; |
| return symlink_buffer; |
| } |
| |
| /* Fill the mounted filesystem structure and return it. */ |
| static struct grub_reiserfs_data * |
| grub_reiserfs_mount (grub_disk_t disk) |
| { |
| struct grub_reiserfs_data *data = 0; |
| data = grub_malloc (sizeof (*data)); |
| if (! data) |
| goto fail; |
| grub_disk_read (disk, REISERFS_SUPER_BLOCK_OFFSET / GRUB_DISK_SECTOR_SIZE, |
| 0, sizeof (data->superblock), &(data->superblock)); |
| if (grub_errno) |
| goto fail; |
| if (grub_memcmp (data->superblock.magic_string, |
| REISERFS_MAGIC_STRING, sizeof (REISERFS_MAGIC_STRING) - 1)) |
| { |
| grub_error (GRUB_ERR_BAD_FS, "not a ReiserFS filesystem"); |
| goto fail; |
| } |
| data->disk = disk; |
| return data; |
| |
| fail: |
| /* Disk is too small to contain a ReiserFS. */ |
| if (grub_errno == GRUB_ERR_OUT_OF_RANGE) |
| grub_error (GRUB_ERR_BAD_FS, "not a ReiserFS filesystem"); |
| |
| grub_free (data); |
| return 0; |
| } |
| |
| /* Call HOOK for each file in directory ITEM. */ |
| static int |
| grub_reiserfs_iterate_dir (grub_fshelp_node_t item, |
| grub_fshelp_iterate_dir_hook_t hook, |
| void *hook_data) |
| { |
| struct grub_reiserfs_data *data = item->data; |
| struct grub_reiserfs_block_header *block_header = 0; |
| grub_uint16_t block_size, block_position; |
| grub_uint32_t block_number; |
| grub_uint64_t next_offset = item->next_offset; |
| int ret = 0; |
| |
| if (item->type != GRUB_REISERFS_DIRECTORY) |
| { |
| grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); |
| goto fail; |
| } |
| block_size = grub_le_to_cpu16 (data->superblock.block_size); |
| block_header = grub_malloc (block_size + 1); |
| if (! block_header) |
| goto fail; |
| block_number = item->block_number; |
| block_position = item->block_position; |
| grub_dprintf ("reiserfs", "Iterating directory...\n"); |
| do |
| { |
| struct grub_reiserfs_directory_header *directory_headers; |
| struct grub_fshelp_node directory_item; |
| grub_uint16_t entry_count, entry_number; |
| struct grub_reiserfs_item_header *item_headers; |
| |
| grub_disk_read (data->disk, |
| block_number * (block_size >> GRUB_DISK_SECTOR_BITS), |
| (((grub_off_t) block_number * block_size) |
| & (GRUB_DISK_SECTOR_SIZE - 1)), |
| block_size, (char *) block_header); |
| if (grub_errno) |
| goto fail; |
| |
| ((char *) block_header)[block_size] = 0; |
| |
| #if 0 |
| if (grub_le_to_cpu16 (block_header->level) != 1) |
| { |
| grub_error (GRUB_ERR_BAD_FS, |
| "reiserfs: block %d is not a leaf block", |
| block_number); |
| goto fail; |
| } |
| #endif |
| |
| item_headers = (struct grub_reiserfs_item_header *) (block_header + 1); |
| directory_headers |
| = ((struct grub_reiserfs_directory_header *) |
| ((char *) block_header |
| + grub_le_to_cpu16 (item_headers[block_position].item_location))); |
| entry_count |
| = grub_le_to_cpu16 (item_headers[block_position].u.entry_count); |
| for (entry_number = 0; entry_number < entry_count; entry_number++) |
| { |
| struct grub_reiserfs_directory_header *directory_header |
| = &directory_headers[entry_number]; |
| grub_uint16_t entry_state |
| = grub_le_to_cpu16 (directory_header->state); |
| grub_fshelp_node_t entry_item; |
| struct grub_reiserfs_key entry_key; |
| enum grub_fshelp_filetype entry_type; |
| char *entry_name; |
| char *entry_name_end = 0; |
| char c; |
| |
| if (!(entry_state & GRUB_REISERFS_VISIBLE_MASK)) |
| continue; |
| |
| entry_name = (((char *) directory_headers) |
| + grub_le_to_cpu16 (directory_header->location)); |
| if (entry_number == 0) |
| { |
| entry_name_end = (char *) block_header |
| + grub_le_to_cpu16 (item_headers[block_position].item_location) |
| + grub_le_to_cpu16 (item_headers[block_position].item_size); |
| } |
| else |
| { |
| entry_name_end = (((char *) directory_headers) |
| + grub_le_to_cpu16 (directory_headers[entry_number - 1].location)); |
| } |
| if (entry_name_end < entry_name || entry_name_end > (char *) block_header + block_size) |
| { |
| entry_name_end = (char *) block_header + block_size; |
| } |
| |
| entry_key.directory_id = directory_header->directory_id; |
| entry_key.object_id = directory_header->object_id; |
| entry_key.u.v2.offset_type = 0; |
| grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_DIRECTORY, |
| 2); |
| grub_reiserfs_set_key_offset (&entry_key, 1); |
| |
| entry_item = grub_malloc (sizeof (*entry_item)); |
| if (! entry_item) |
| goto fail; |
| |
| if (grub_reiserfs_get_item (data, &entry_key, entry_item, 1) |
| != GRUB_ERR_NONE) |
| { |
| grub_free (entry_item); |
| goto fail; |
| } |
| |
| if (entry_item->type == GRUB_REISERFS_DIRECTORY) |
| entry_type = GRUB_FSHELP_DIR; |
| else |
| { |
| grub_uint32_t entry_block_number; |
| /* Order is very important here. |
| First set the offset to 0 using current key version. |
| Then change the key type, which affects key version |
| detection. */ |
| grub_reiserfs_set_key_offset (&entry_key, 0); |
| grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_STAT, |
| 2); |
| if (grub_reiserfs_get_item (data, &entry_key, entry_item, 1) |
| != GRUB_ERR_NONE) |
| { |
| grub_free (entry_item); |
| goto fail; |
| } |
| |
| if (entry_item->block_number != 0) |
| { |
| grub_uint16_t entry_version; |
| entry_version |
| = grub_le_to_cpu16 (entry_item->header.version); |
| entry_block_number = entry_item->block_number; |
| #if 0 |
| grub_dprintf ("reiserfs", |
| "version %04x block %08x (%08x) position %08x\n", |
| entry_version, entry_block_number, |
| ((grub_disk_addr_t) entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE, |
| grub_le_to_cpu16 (entry_item->header.item_location)); |
| #endif |
| if (entry_version == 0) /* Version 1 stat item. */ |
| { |
| struct grub_reiserfs_stat_item_v1 entry_v1_stat; |
| grub_disk_read (data->disk, |
| entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), |
| grub_le_to_cpu16 (entry_item->header.item_location), |
| sizeof (entry_v1_stat), |
| (char *) &entry_v1_stat); |
| if (grub_errno) |
| goto fail; |
| #if 0 |
| grub_dprintf ("reiserfs", |
| "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", |
| grub_le_to_cpu16 (entry_v1_stat.mode), |
| grub_le_to_cpu16 (entry_v1_stat.hardlink_count), |
| grub_le_to_cpu16 (entry_v1_stat.uid), |
| grub_le_to_cpu16 (entry_v1_stat.gid), |
| grub_le_to_cpu32 (entry_v1_stat.size), |
| grub_le_to_cpu32 (entry_v1_stat.atime), |
| grub_le_to_cpu32 (entry_v1_stat.mtime), |
| grub_le_to_cpu32 (entry_v1_stat.ctime), |
| grub_le_to_cpu32 (entry_v1_stat.rdev), |
| grub_le_to_cpu32 (entry_v1_stat.first_direct_byte)); |
| grub_dprintf ("reiserfs", |
| "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", |
| entry_v1_stat.mode, |
| entry_v1_stat.hardlink_count, |
| entry_v1_stat.uid, |
| entry_v1_stat.gid, |
| entry_v1_stat.size, |
| entry_v1_stat.atime, |
| entry_v1_stat.mtime, |
| entry_v1_stat.ctime, |
| entry_v1_stat.rdev, |
| entry_v1_stat.first_direct_byte); |
| #endif |
| entry_item->mtime = grub_le_to_cpu32 (entry_v1_stat.mtime); |
| if ((grub_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK) |
| == S_IFLNK) |
| entry_type = GRUB_FSHELP_SYMLINK; |
| else |
| entry_type = GRUB_FSHELP_REG; |
| entry_item->size = (grub_off_t) grub_le_to_cpu32 (entry_v1_stat.size); |
| } |
| else |
| { |
| struct grub_reiserfs_stat_item_v2 entry_v2_stat; |
| grub_disk_read (data->disk, |
| entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), |
| grub_le_to_cpu16 (entry_item->header.item_location), |
| sizeof (entry_v2_stat), |
| (char *) &entry_v2_stat); |
| if (grub_errno) |
| goto fail; |
| #if 0 |
| grub_dprintf ("reiserfs", |
| "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", |
| grub_le_to_cpu16 (entry_v2_stat.mode), |
| grub_le_to_cpu16 (entry_v2_stat.reserved), |
| grub_le_to_cpu32 (entry_v2_stat.hardlink_count), |
| (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) >> 32), |
| (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) && 0xFFFFFFFF), |
| grub_le_to_cpu32 (entry_v2_stat.uid), |
| grub_le_to_cpu32 (entry_v2_stat.gid), |
| grub_le_to_cpu32 (entry_v2_stat.atime), |
| grub_le_to_cpu32 (entry_v2_stat.mtime), |
| grub_le_to_cpu32 (entry_v2_stat.ctime), |
| grub_le_to_cpu32 (entry_v2_stat.blocks), |
| grub_le_to_cpu32 (entry_v2_stat.first_direct_byte)); |
| grub_dprintf ("reiserfs", |
| "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", |
| entry_v2_stat.mode, |
| entry_v2_stat.reserved, |
| entry_v2_stat.hardlink_count, |
| (unsigned int) (entry_v2_stat.size >> 32), |
| (unsigned int) (entry_v2_stat.size && 0xFFFFFFFF), |
| entry_v2_stat.uid, |
| entry_v2_stat.gid, |
| entry_v2_stat.atime, |
| entry_v2_stat.mtime, |
| entry_v2_stat.ctime, |
| entry_v2_stat.blocks, |
| entry_v2_stat.first_direct_byte); |
| #endif |
| entry_item->mtime = grub_le_to_cpu32 (entry_v2_stat.mtime); |
| entry_item->size = (grub_off_t) grub_le_to_cpu64 (entry_v2_stat.size); |
| if ((grub_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK) |
| == S_IFLNK) |
| entry_type = GRUB_FSHELP_SYMLINK; |
| else |
| entry_type = GRUB_FSHELP_REG; |
| } |
| } |
| else |
| { |
| /* Pseudo file ".." never has stat block. */ |
| if (entry_name_end == entry_name + 2 && grub_memcmp (entry_name, "..", 2) != 0) |
| grub_dprintf ("reiserfs", |
| "Warning : %s has no stat block !\n", |
| entry_name); |
| grub_free (entry_item); |
| goto next; |
| } |
| } |
| |
| c = *entry_name_end; |
| *entry_name_end = 0; |
| if (hook (entry_name, entry_type, entry_item, hook_data)) |
| { |
| *entry_name_end = c; |
| grub_dprintf ("reiserfs", "Found : %s, type=%d\n", |
| entry_name, entry_type); |
| ret = 1; |
| goto found; |
| } |
| *entry_name_end = c; |
| |
| next: |
| ; |
| } |
| |
| if (next_offset == 0) |
| break; |
| |
| grub_reiserfs_set_key_offset (&(item_headers[block_position].key), |
| next_offset); |
| if (grub_reiserfs_get_item (data, &(item_headers[block_position].key), |
| &directory_item, 1) != GRUB_ERR_NONE) |
| goto fail; |
| block_number = directory_item.block_number; |
| block_position = directory_item.block_position; |
| next_offset = directory_item.next_offset; |
| } |
| while (block_number); |
| |
| found: |
| assert (grub_errno == GRUB_ERR_NONE); |
| grub_free (block_header); |
| return ret; |
| fail: |
| assert (grub_errno != GRUB_ERR_NONE); |
| grub_free (block_header); |
| return 0; |
| } |
| |
| /****************************************************************************/ |
| /* grub api functions */ |
| /****************************************************************************/ |
| |
| /* Open a file named NAME and initialize FILE. */ |
| static grub_err_t |
| grub_reiserfs_open (struct grub_file *file, const char *name) |
| { |
| struct grub_reiserfs_data *data = 0; |
| struct grub_fshelp_node root, *found = 0; |
| struct grub_reiserfs_key key; |
| |
| grub_dl_ref (my_mod); |
| data = grub_reiserfs_mount (file->device->disk); |
| if (! data) |
| goto fail; |
| key.directory_id = grub_cpu_to_le32_compile_time (1); |
| key.object_id = grub_cpu_to_le32_compile_time (2); |
| key.u.v2.offset_type = 0; |
| grub_reiserfs_set_key_type (&key, GRUB_REISERFS_DIRECTORY, 2); |
| grub_reiserfs_set_key_offset (&key, 1); |
| if (grub_reiserfs_get_item (data, &key, &root, 1) != GRUB_ERR_NONE) |
| goto fail; |
| if (root.block_number == 0) |
| { |
| grub_error (GRUB_ERR_BAD_FS, "unable to find root item"); |
| goto fail; /* Should never happen since checked at mount. */ |
| } |
| grub_fshelp_find_file (name, &root, &found, |
| grub_reiserfs_iterate_dir, |
| grub_reiserfs_read_symlink, GRUB_FSHELP_REG); |
| if (grub_errno) |
| goto fail; |
| file->size = found->size; |
| |
| grub_dprintf ("reiserfs", "file size : %d (%08x%08x)\n", |
| (unsigned int) file->size, |
| (unsigned int) (file->size >> 32), (unsigned int) file->size); |
| file->offset = 0; |
| file->data = found; |
| return GRUB_ERR_NONE; |
| |
| fail: |
| assert (grub_errno != GRUB_ERR_NONE); |
| if (found != &root) |
| grub_free (found); |
| grub_free (data); |
| grub_dl_unref (my_mod); |
| return grub_errno; |
| } |
| |
| static grub_ssize_t |
| grub_reiserfs_read_real (struct grub_fshelp_node *node, |
| grub_off_t off, char *buf, grub_size_t len, |
| grub_disk_read_hook_t read_hook, void *read_hook_data) |
| { |
| unsigned int indirect_block, indirect_block_count; |
| struct grub_reiserfs_key key; |
| struct grub_reiserfs_data *data = node->data; |
| struct grub_fshelp_node found; |
| grub_uint16_t block_size = grub_le_to_cpu16 (data->superblock.block_size); |
| grub_uint16_t item_size; |
| grub_uint32_t *indirect_block_ptr = 0; |
| grub_uint64_t current_key_offset = 1; |
| grub_off_t initial_position, current_position, final_position, length; |
| grub_disk_addr_t block; |
| grub_off_t offset; |
| |
| key.directory_id = node->header.key.directory_id; |
| key.object_id = node->header.key.object_id; |
| key.u.v2.offset_type = 0; |
| grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2); |
| initial_position = off; |
| current_position = 0; |
| final_position = MIN (len + initial_position, node->size); |
| grub_dprintf ("reiserfs", |
| "Reading from %lld to %lld (%lld instead of requested %ld)\n", |
| (unsigned long long) initial_position, |
| (unsigned long long) final_position, |
| (unsigned long long) (final_position - initial_position), |
| (unsigned long) len); |
| |
| grub_reiserfs_set_key_offset (&key, initial_position + 1); |
| |
| if (grub_reiserfs_get_item (data, &key, &found, 0) != GRUB_ERR_NONE) |
| goto fail; |
| |
| if (found.block_number == 0) |
| { |
| grub_error (GRUB_ERR_READ_ERROR, "offset %lld not found", |
| (unsigned long long) initial_position); |
| goto fail; |
| } |
| |
| current_key_offset = grub_reiserfs_get_key_offset (&found.header.key); |
| current_position = current_key_offset - 1; |
| |
| while (current_position < final_position) |
| { |
| grub_reiserfs_set_key_offset (&key, current_key_offset); |
| |
| if (grub_reiserfs_get_item (data, &key, &found, 1) != GRUB_ERR_NONE) |
| goto fail; |
| if (found.block_number == 0) |
| goto fail; |
| item_size = grub_le_to_cpu16 (found.header.item_size); |
| switch (found.type) |
| { |
| case GRUB_REISERFS_DIRECT: |
| block = ((grub_disk_addr_t) found.block_number) * (block_size >> GRUB_DISK_SECTOR_BITS); |
| grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); |
| if (initial_position < current_position + item_size) |
| { |
| offset = MAX ((signed) (initial_position - current_position), 0); |
| length = (MIN (item_size, final_position - current_position) |
| - offset); |
| grub_dprintf ("reiserfs", |
| "Reading direct block %u from %u to %u...\n", |
| (unsigned) block, (unsigned) offset, |
| (unsigned) (offset + length)); |
| found.data->disk->read_hook = read_hook; |
| found.data->disk->read_hook_data = read_hook_data; |
| grub_disk_read (found.data->disk, |
| block, |
| offset |
| + grub_le_to_cpu16 (found.header.item_location), |
| length, buf); |
| found.data->disk->read_hook = 0; |
| if (grub_errno) |
| goto fail; |
| buf += length; |
| current_position += offset + length; |
| } |
| else |
| current_position += item_size; |
| break; |
| case GRUB_REISERFS_INDIRECT: |
| indirect_block_count = item_size / sizeof (*indirect_block_ptr); |
| indirect_block_ptr = grub_malloc (item_size); |
| if (! indirect_block_ptr) |
| goto fail; |
| grub_disk_read (found.data->disk, |
| found.block_number * (block_size >> GRUB_DISK_SECTOR_BITS), |
| grub_le_to_cpu16 (found.header.item_location), |
| item_size, indirect_block_ptr); |
| if (grub_errno) |
| goto fail; |
| found.data->disk->read_hook = read_hook; |
| found.data->disk->read_hook_data = read_hook_data; |
| for (indirect_block = 0; |
| indirect_block < indirect_block_count |
| && current_position < final_position; |
| indirect_block++) |
| { |
| block = grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * |
| (block_size >> GRUB_DISK_SECTOR_BITS); |
| grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block); |
| if (current_position + block_size >= initial_position) |
| { |
| offset = MAX ((signed) (initial_position - current_position), |
| 0); |
| length = (MIN (block_size, final_position - current_position) |
| - offset); |
| grub_dprintf ("reiserfs", |
| "Reading indirect block %u from %u to %u...\n", |
| (unsigned) block, (unsigned) offset, |
| (unsigned) (offset + length)); |
| #if 0 |
| grub_dprintf ("reiserfs", |
| "\nib=%04d/%04d, ip=%d, cp=%d, fp=%d, off=%d, l=%d, tl=%d\n", |
| indirect_block + 1, indirect_block_count, |
| initial_position, current_position, |
| final_position, offset, length, len); |
| #endif |
| grub_disk_read (found.data->disk, block, offset, length, buf); |
| if (grub_errno) |
| goto fail; |
| buf += length; |
| current_position += offset + length; |
| } |
| else |
| current_position += block_size; |
| } |
| found.data->disk->read_hook = 0; |
| grub_free (indirect_block_ptr); |
| indirect_block_ptr = 0; |
| break; |
| default: |
| goto fail; |
| } |
| current_key_offset = current_position + 1; |
| } |
| |
| grub_dprintf ("reiserfs", |
| "Have successfully read %lld bytes (%ld requested)\n", |
| (unsigned long long) (current_position - initial_position), |
| (unsigned long) len); |
| return current_position - initial_position; |
| |
| #if 0 |
| switch (found.type) |
| { |
| case GRUB_REISERFS_DIRECT: |
| read_length = MIN (len, item_size - file->offset); |
| grub_disk_read (found.data->disk, |
| (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE, |
| grub_le_to_cpu16 (found.header.item_location) + file->offset, |
| read_length, buf); |
| if (grub_errno) |
| goto fail; |
| break; |
| case GRUB_REISERFS_INDIRECT: |
| indirect_block_count = item_size / sizeof (*indirect_block_ptr); |
| indirect_block_ptr = grub_malloc (item_size); |
| if (!indirect_block_ptr) |
| goto fail; |
| grub_disk_read (found.data->disk, |
| (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE, |
| grub_le_to_cpu16 (found.header.item_location), |
| item_size, (char *) indirect_block_ptr); |
| if (grub_errno) |
| goto fail; |
| len = MIN (len, file->size - file->offset); |
| for (indirect_block = file->offset / block_size; |
| indirect_block < indirect_block_count && read_length < len; |
| indirect_block++) |
| { |
| read = MIN (block_size, len - read_length); |
| grub_disk_read (found.data->disk, |
| (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE, |
| file->offset % block_size, read, |
| ((void *) buf) + read_length); |
| if (grub_errno) |
| goto fail; |
| read_length += read; |
| } |
| grub_free (indirect_block_ptr); |
| break; |
| default: |
| goto fail; |
| } |
| |
| return read_length; |
| #endif |
| |
| fail: |
| grub_free (indirect_block_ptr); |
| return -1; |
| } |
| |
| static grub_ssize_t |
| grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) |
| { |
| return grub_reiserfs_read_real (file->data, file->offset, buf, len, |
| file->read_hook, file->read_hook_data); |
| } |
| |
| /* Close the file FILE. */ |
| static grub_err_t |
| grub_reiserfs_close (grub_file_t file) |
| { |
| struct grub_fshelp_node *node = file->data; |
| struct grub_reiserfs_data *data = node->data; |
| |
| grub_free (data); |
| grub_free (node); |
| grub_dl_unref (my_mod); |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Context for grub_reiserfs_dir. */ |
| struct grub_reiserfs_dir_ctx |
| { |
| grub_fs_dir_hook_t hook; |
| void *hook_data; |
| }; |
| |
| /* Helper for grub_reiserfs_dir. */ |
| static int |
| grub_reiserfs_dir_iter (const char *filename, |
| enum grub_fshelp_filetype filetype, |
| grub_fshelp_node_t node, void *data) |
| { |
| struct grub_reiserfs_dir_ctx *ctx = data; |
| struct grub_dirhook_info info; |
| |
| grub_memset (&info, 0, sizeof (info)); |
| info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); |
| info.mtimeset = 1; |
| info.mtime = node->mtime; |
| grub_free (node); |
| return ctx->hook (filename, &info, ctx->hook_data); |
| } |
| |
| /* Call HOOK with each file under DIR. */ |
| static grub_err_t |
| grub_reiserfs_dir (grub_device_t device, const char *path, |
| grub_fs_dir_hook_t hook, void *hook_data) |
| { |
| struct grub_reiserfs_dir_ctx ctx = { hook, hook_data }; |
| struct grub_reiserfs_data *data = 0; |
| struct grub_fshelp_node root, *found; |
| struct grub_reiserfs_key root_key; |
| |
| grub_dl_ref (my_mod); |
| data = grub_reiserfs_mount (device->disk); |
| if (! data) |
| goto fail; |
| root_key.directory_id = grub_cpu_to_le32_compile_time (1); |
| root_key.object_id = grub_cpu_to_le32_compile_time (2); |
| root_key.u.v2.offset_type = 0; |
| grub_reiserfs_set_key_type (&root_key, GRUB_REISERFS_DIRECTORY, 2); |
| grub_reiserfs_set_key_offset (&root_key, 1); |
| if (grub_reiserfs_get_item (data, &root_key, &root, 1) != GRUB_ERR_NONE) |
| goto fail; |
| if (root.block_number == 0) |
| { |
| grub_error(GRUB_ERR_BAD_FS, "root not found"); |
| goto fail; |
| } |
| grub_fshelp_find_file (path, &root, &found, grub_reiserfs_iterate_dir, |
| grub_reiserfs_read_symlink, GRUB_FSHELP_DIR); |
| if (grub_errno) |
| goto fail; |
| grub_reiserfs_iterate_dir (found, grub_reiserfs_dir_iter, &ctx); |
| grub_free (data); |
| grub_dl_unref (my_mod); |
| return GRUB_ERR_NONE; |
| |
| fail: |
| grub_free (data); |
| grub_dl_unref (my_mod); |
| return grub_errno; |
| } |
| |
| /* Return the label of the device DEVICE in LABEL. The label is |
| returned in a grub_malloc'ed buffer and should be freed by the |
| caller. */ |
| static grub_err_t |
| grub_reiserfs_label (grub_device_t device, char **label) |
| { |
| struct grub_reiserfs_data *data; |
| grub_disk_t disk = device->disk; |
| |
| grub_dl_ref (my_mod); |
| |
| data = grub_reiserfs_mount (disk); |
| if (data) |
| { |
| *label = grub_strndup (data->superblock.label, |
| sizeof (data->superblock.label)); |
| } |
| else |
| *label = NULL; |
| |
| grub_dl_unref (my_mod); |
| |
| grub_free (data); |
| |
| return grub_errno; |
| } |
| |
| static grub_err_t |
| grub_reiserfs_uuid (grub_device_t device, char **uuid) |
| { |
| struct grub_reiserfs_data *data; |
| grub_disk_t disk = device->disk; |
| |
| grub_dl_ref (my_mod); |
| |
| *uuid = NULL; |
| data = grub_reiserfs_mount (disk); |
| if (data) |
| { |
| unsigned i; |
| for (i = 0; i < ARRAY_SIZE (data->superblock.uuid); i++) |
| if (data->superblock.uuid[i]) |
| break; |
| if (i < ARRAY_SIZE (data->superblock.uuid)) |
| *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", |
| grub_be_to_cpu16 (data->superblock.uuid[0]), |
| grub_be_to_cpu16 (data->superblock.uuid[1]), |
| grub_be_to_cpu16 (data->superblock.uuid[2]), |
| grub_be_to_cpu16 (data->superblock.uuid[3]), |
| grub_be_to_cpu16 (data->superblock.uuid[4]), |
| grub_be_to_cpu16 (data->superblock.uuid[5]), |
| grub_be_to_cpu16 (data->superblock.uuid[6]), |
| grub_be_to_cpu16 (data->superblock.uuid[7])); |
| } |
| |
| grub_dl_unref (my_mod); |
| |
| grub_free (data); |
| |
| return grub_errno; |
| } |
| |
| static struct grub_fs grub_reiserfs_fs = |
| { |
| .name = "reiserfs", |
| .dir = grub_reiserfs_dir, |
| .open = grub_reiserfs_open, |
| .read = grub_reiserfs_read, |
| .close = grub_reiserfs_close, |
| .label = grub_reiserfs_label, |
| .uuid = grub_reiserfs_uuid, |
| #ifdef GRUB_UTIL |
| .reserved_first_sector = 1, |
| .blocklist_install = 1, |
| #endif |
| .next = 0 |
| }; |
| |
| GRUB_MOD_INIT(reiserfs) |
| { |
| grub_fs_register (&grub_reiserfs_fs); |
| my_mod = mod; |
| } |
| |
| GRUB_MOD_FINI(reiserfs) |
| { |
| grub_fs_unregister (&grub_reiserfs_fs); |
| } |