/* reiserfs.c - ReiserFS versions up to 3.6 */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2003,2004,2005,2008 Free Software Foundation, Inc. * * VAS_EBOOT 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. * * VAS_EBOOT 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 VAS_EBOOT. If not, see . */ /* TODO: implement journal handling (ram replay) test tail packing & direct files validate partition label position */ #if 0 # define VAS_EBOOT_REISERFS_DEBUG # define VAS_EBOOT_REISERFS_JOURNALING # define VAS_EBOOT_HEXDUMP #endif #include #include #include #include #include #include #include #include #include #include VAS_EBOOT_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 VAS_EBOOT_REISERFS_VISIBLE_MASK ((VasEBoot_uint16_t) 0x04) #define S_IFLNK 0xA000 static VasEBoot_dl_t my_mod; #define assert(boolean) real_assert (boolean, VAS_EBOOT_FILE, __LINE__) static inline void real_assert (int boolean, const char *file, const int line) { if (! boolean) VasEBoot_printf ("Assertion failed at %s:%d\n", file, line); } enum VasEBoot_reiserfs_item_type { VAS_EBOOT_REISERFS_STAT, VAS_EBOOT_REISERFS_DIRECTORY, VAS_EBOOT_REISERFS_DIRECT, VAS_EBOOT_REISERFS_INDIRECT, /* Matches both _DIRECT and _INDIRECT when searching. */ VAS_EBOOT_REISERFS_ANY, VAS_EBOOT_REISERFS_UNKNOWN }; struct VasEBoot_reiserfs_superblock { VasEBoot_uint32_t block_count; VasEBoot_uint32_t block_free_count; VasEBoot_uint32_t root_block; VasEBoot_uint32_t journal_block; VasEBoot_uint32_t journal_device; VasEBoot_uint32_t journal_original_size; VasEBoot_uint32_t journal_max_transaction_size; VasEBoot_uint32_t journal_block_count; VasEBoot_uint32_t journal_max_batch; VasEBoot_uint32_t journal_max_commit_age; VasEBoot_uint32_t journal_max_transaction_age; VasEBoot_uint16_t block_size; VasEBoot_uint16_t oid_max_size; VasEBoot_uint16_t oid_current_size; VasEBoot_uint16_t state; VasEBoot_uint8_t magic_string[REISERFS_MAGIC_LEN]; VasEBoot_uint32_t function_hash_code; VasEBoot_uint16_t tree_height; VasEBoot_uint16_t bitmap_number; VasEBoot_uint16_t version; VasEBoot_uint16_t reserved; VasEBoot_uint32_t inode_generation; VasEBoot_uint8_t unused[4]; VasEBoot_uint16_t uuid[8]; char label[16]; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_journal_header { VasEBoot_uint32_t last_flush_uid; VasEBoot_uint32_t unflushed_offset; VasEBoot_uint32_t mount_id; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_description_block { VasEBoot_uint32_t id; VasEBoot_uint32_t len; VasEBoot_uint32_t mount_id; VasEBoot_uint32_t real_blocks[0]; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_commit_block { VasEBoot_uint32_t id; VasEBoot_uint32_t len; VasEBoot_uint32_t real_blocks[0]; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_stat_item_v1 { VasEBoot_uint16_t mode; VasEBoot_uint16_t hardlink_count; VasEBoot_uint16_t uid; VasEBoot_uint16_t gid; VasEBoot_uint32_t size; VasEBoot_uint32_t atime; VasEBoot_uint32_t mtime; VasEBoot_uint32_t ctime; VasEBoot_uint32_t rdev; VasEBoot_uint32_t first_direct_byte; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_stat_item_v2 { VasEBoot_uint16_t mode; VasEBoot_uint16_t reserved; VasEBoot_uint32_t hardlink_count; VasEBoot_uint64_t size; VasEBoot_uint32_t uid; VasEBoot_uint32_t gid; VasEBoot_uint32_t atime; VasEBoot_uint32_t mtime; VasEBoot_uint32_t ctime; VasEBoot_uint32_t blocks; VasEBoot_uint32_t first_direct_byte; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_key { VasEBoot_uint32_t directory_id; VasEBoot_uint32_t object_id; union { struct { VasEBoot_uint32_t offset; VasEBoot_uint32_t type; } VAS_EBOOT_PACKED v1; struct { VasEBoot_uint64_t offset_type; } VAS_EBOOT_PACKED v2; } u; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_item_header { struct VasEBoot_reiserfs_key key; union { VasEBoot_uint16_t free_space; VasEBoot_uint16_t entry_count; } VAS_EBOOT_PACKED u; VasEBoot_uint16_t item_size; VasEBoot_uint16_t item_location; VasEBoot_uint16_t version; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_block_header { VasEBoot_uint16_t level; VasEBoot_uint16_t item_count; VasEBoot_uint16_t free_space; VasEBoot_uint16_t reserved; struct VasEBoot_reiserfs_key block_right_delimiting_key; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_disk_child { VasEBoot_uint32_t block_number; VasEBoot_uint16_t size; VasEBoot_uint16_t reserved; } VAS_EBOOT_PACKED; struct VasEBoot_reiserfs_directory_header { VasEBoot_uint32_t offset; VasEBoot_uint32_t directory_id; VasEBoot_uint32_t object_id; VasEBoot_uint16_t location; VasEBoot_uint16_t state; } VAS_EBOOT_PACKED; struct VasEBoot_fshelp_node { struct VasEBoot_reiserfs_data *data; VasEBoot_uint32_t block_number; /* 0 if node is not found. */ VasEBoot_uint16_t block_position; VasEBoot_uint64_t next_offset; VasEBoot_int32_t mtime; VasEBoot_off_t size; enum VasEBoot_reiserfs_item_type type; /* To know how to read the header. */ struct VasEBoot_reiserfs_item_header header; }; /* Returned when opening a file. */ struct VasEBoot_reiserfs_data { struct VasEBoot_reiserfs_superblock superblock; VasEBoot_disk_t disk; }; static VasEBoot_ssize_t VasEBoot_reiserfs_read_real (struct VasEBoot_fshelp_node *node, VasEBoot_off_t off, char *buf, VasEBoot_size_t len, VasEBoot_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 VasEBoot_reiserfs_item_type VasEBoot_reiserfs_get_key_v2_type (const struct VasEBoot_reiserfs_key *key) { switch (VasEBoot_le_to_cpu64 (key->u.v2.offset_type) >> 60) { case 0: return VAS_EBOOT_REISERFS_STAT; case 15: return VAS_EBOOT_REISERFS_ANY; case 3: return VAS_EBOOT_REISERFS_DIRECTORY; case 2: return VAS_EBOOT_REISERFS_DIRECT; case 1: return VAS_EBOOT_REISERFS_INDIRECT; } return VAS_EBOOT_REISERFS_UNKNOWN; } /* Return the type of given v1 key. */ static enum VasEBoot_reiserfs_item_type VasEBoot_reiserfs_get_key_v1_type (const struct VasEBoot_reiserfs_key *key) { switch (VasEBoot_le_to_cpu32 (key->u.v1.type)) { case 0: return VAS_EBOOT_REISERFS_STAT; case 555: return VAS_EBOOT_REISERFS_ANY; case 500: return VAS_EBOOT_REISERFS_DIRECTORY; case 0x20000000: case 0xFFFFFFFF: return VAS_EBOOT_REISERFS_DIRECT; case 0x10000000: case 0xFFFFFFFE: return VAS_EBOOT_REISERFS_INDIRECT; } return VAS_EBOOT_REISERFS_UNKNOWN; } /* Return 1 if the given key is version 1 key, 2 otherwise. */ static int VasEBoot_reiserfs_get_key_version (const struct VasEBoot_reiserfs_key *key) { return VasEBoot_reiserfs_get_key_v1_type (key) == VAS_EBOOT_REISERFS_UNKNOWN ? 2 : 1; } #ifdef VAS_EBOOT_HEXDUMP static void VasEBoot_hexdump (char *buffer, VasEBoot_size_t len) { VasEBoot_size_t a; for (a = 0; a < len; a++) { if (! (a & 0x0F)) VasEBoot_printf ("\n%08x ", a); VasEBoot_printf ("%02x ", ((unsigned int) ((unsigned char *) buffer)[a]) & 0xFF); } VasEBoot_printf ("\n"); } #endif #ifdef VAS_EBOOT_REISERFS_DEBUG static VasEBoot_uint64_t VasEBoot_reiserfs_get_key_offset (const struct VasEBoot_reiserfs_key *key); static enum VasEBoot_reiserfs_item_type VasEBoot_reiserfs_get_key_type (const struct VasEBoot_reiserfs_key *key); static void VasEBoot_reiserfs_print_key (const struct VasEBoot_reiserfs_key *key) { unsigned int a; char *reiserfs_type_strings[] = { "stat ", "directory", "direct ", "indirect ", "any ", "unknown " }; for (a = 0; a < sizeof (struct VasEBoot_reiserfs_key); a++) VasEBoot_printf ("%02x ", ((unsigned int) ((unsigned char *) key)[a]) & 0xFF); VasEBoot_printf ("parent id = 0x%08x, self id = 0x%08x, type = %s, offset = ", VasEBoot_le_to_cpu32 (key->directory_id), VasEBoot_le_to_cpu32 (key->object_id), reiserfs_type_strings [VasEBoot_reiserfs_get_key_type (key)]); if (VasEBoot_reiserfs_get_key_version (key) == 1) VasEBoot_printf("%08x", (unsigned int) VasEBoot_reiserfs_get_key_offset (key)); else VasEBoot_printf("0x%07x%08x", (unsigned) (VasEBoot_reiserfs_get_key_offset (key) >> 32), (unsigned) (VasEBoot_reiserfs_get_key_offset (key) & 0xFFFFFFFF)); VasEBoot_printf ("\n"); } #endif /* Return the offset of given key. */ static VasEBoot_uint64_t VasEBoot_reiserfs_get_key_offset (const struct VasEBoot_reiserfs_key *key) { if (VasEBoot_reiserfs_get_key_version (key) == 1) return VasEBoot_le_to_cpu32 (key->u.v1.offset); else return VasEBoot_le_to_cpu64 (key->u.v2.offset_type) & (~0ULL >> 4); } /* Set the offset of given key. */ static void VasEBoot_reiserfs_set_key_offset (struct VasEBoot_reiserfs_key *key, VasEBoot_uint64_t value) { if (VasEBoot_reiserfs_get_key_version (key) == 1) key->u.v1.offset = VasEBoot_cpu_to_le32 (value); else key->u.v2.offset_type \ = ((key->u.v2.offset_type & VasEBoot_cpu_to_le64_compile_time (15ULL << 60)) | VasEBoot_cpu_to_le64 (value & (~0ULL >> 4))); } /* Return the type of given key. */ static enum VasEBoot_reiserfs_item_type VasEBoot_reiserfs_get_key_type (const struct VasEBoot_reiserfs_key *key) { if (VasEBoot_reiserfs_get_key_version (key) == 1) return VasEBoot_reiserfs_get_key_v1_type (key); else return VasEBoot_reiserfs_get_key_v2_type (key); } /* Set the type of given key, with given version number. */ static void VasEBoot_reiserfs_set_key_type (struct VasEBoot_reiserfs_key *key, enum VasEBoot_reiserfs_item_type VasEBoot_type, int version) { VasEBoot_uint32_t type; switch (VasEBoot_type) { case VAS_EBOOT_REISERFS_STAT: type = 0; break; case VAS_EBOOT_REISERFS_ANY: type = (version == 1) ? 555 : 15; break; case VAS_EBOOT_REISERFS_DIRECTORY: type = (version == 1) ? 500 : 3; break; case VAS_EBOOT_REISERFS_DIRECT: type = (version == 1) ? 0xFFFFFFFF : 2; break; case VAS_EBOOT_REISERFS_INDIRECT: type = (version == 1) ? 0xFFFFFFFE : 1; break; default: return; } if (version == 1) key->u.v1.type = VasEBoot_cpu_to_le32 (type); else key->u.v2.offset_type = ((key->u.v2.offset_type & VasEBoot_cpu_to_le64_compile_time (~0ULL >> 4)) | VasEBoot_cpu_to_le64 ((VasEBoot_uint64_t) type << 60)); assert (VasEBoot_reiserfs_get_key_type (key) == VasEBoot_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 VasEBoot_reiserfs_compare_keys (const struct VasEBoot_reiserfs_key *key1, const struct VasEBoot_reiserfs_key *key2) { VasEBoot_uint64_t offset1, offset2; enum VasEBoot_reiserfs_item_type type1, type2; VasEBoot_uint32_t id1, id2; if (! key1 || ! key2) return -2; id1 = VasEBoot_le_to_cpu32 (key1->directory_id); id2 = VasEBoot_le_to_cpu32 (key2->directory_id); if (id1 < id2) return -1; if (id1 > id2) return 1; id1 = VasEBoot_le_to_cpu32 (key1->object_id); id2 = VasEBoot_le_to_cpu32 (key2->object_id); if (id1 < id2) return -1; if (id1 > id2) return 1; offset1 = VasEBoot_reiserfs_get_key_offset (key1); offset2 = VasEBoot_reiserfs_get_key_offset (key2); if (offset1 < offset2) return -1; if (offset1 > offset2) return 1; type1 = VasEBoot_reiserfs_get_key_type (key1); type2 = VasEBoot_reiserfs_get_key_type (key2); if ((type1 == VAS_EBOOT_REISERFS_ANY && (type2 == VAS_EBOOT_REISERFS_DIRECT || type2 == VAS_EBOOT_REISERFS_INDIRECT)) || (type2 == VAS_EBOOT_REISERFS_ANY && (type1 == VAS_EBOOT_REISERFS_DIRECT || type1 == VAS_EBOOT_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 VasEBoot_err_t VasEBoot_reiserfs_get_item (struct VasEBoot_reiserfs_data *data, const struct VasEBoot_reiserfs_key *key, struct VasEBoot_fshelp_node *item, int exact) { VasEBoot_uint32_t block_number; struct VasEBoot_reiserfs_block_header *block_header = 0; struct VasEBoot_reiserfs_key *block_key = 0; VasEBoot_uint16_t block_size, item_count, current_level; VasEBoot_uint16_t i; VasEBoot_uint16_t previous_level = ~0; struct VasEBoot_reiserfs_item_header *item_headers = 0; #if 0 if (! data) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "data is NULL"); goto fail; } if (! key) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "key is NULL"); goto fail; } if (! item) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "item is NULL"); goto fail; } #endif block_size = VasEBoot_le_to_cpu16 (data->superblock.block_size); block_number = VasEBoot_le_to_cpu32 (data->superblock.root_block); #ifdef VAS_EBOOT_REISERFS_DEBUG VasEBoot_printf("Searching for "); VasEBoot_reiserfs_print_key (key); #endif block_header = VasEBoot_malloc (block_size); if (! block_header) goto fail; item->next_offset = 0; do { VasEBoot_disk_read (data->disk, block_number * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS), (((VasEBoot_off_t) block_number * block_size) & (VAS_EBOOT_DISK_SECTOR_SIZE - 1)), block_size, block_header); if (VasEBoot_errno) goto fail; current_level = VasEBoot_le_to_cpu16 (block_header->level); VasEBoot_dprintf ("reiserfs_tree", " at level %d\n", current_level); if (current_level >= previous_level) { VasEBoot_dprintf ("reiserfs_tree", "level loop detected, aborting\n"); VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "level loop"); goto fail; } previous_level = current_level; item_count = VasEBoot_le_to_cpu16 (block_header->item_count); VasEBoot_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 VasEBoot_reiserfs_key *keys = (struct VasEBoot_reiserfs_key *) (block_header + 1); struct VasEBoot_reiserfs_disk_child *children = ((struct VasEBoot_reiserfs_disk_child *) (keys + item_count)); for (i = 0; i < item_count && VasEBoot_reiserfs_compare_keys (key, &(keys[i])) >= 0; i++) { #ifdef VAS_EBOOT_REISERFS_DEBUG VasEBoot_printf("i %03d/%03d ", i + 1, item_count + 1); VasEBoot_reiserfs_print_key (&(keys[i])); #endif } block_number = VasEBoot_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 = VasEBoot_reiserfs_get_key_offset(&(keys[i])); #ifdef VAS_EBOOT_REISERFS_DEBUG if (i == item_count || VasEBoot_reiserfs_compare_keys (key, &(keys[i])) == 0) VasEBoot_printf(">"); else VasEBoot_printf("<"); if (i < item_count) { VasEBoot_printf (" %03d/%03d ", i + 1, item_count + 1); VasEBoot_reiserfs_print_key (&(keys[i])); if (i + 1 < item_count) { VasEBoot_printf ("+ %03d/%03d ", i + 2, item_count); VasEBoot_reiserfs_print_key (&(keys[i + 1])); } } else VasEBoot_printf ("Accessing rightmost child at block %d.\n", block_number); #endif } else { /* Leaf node. Check that the key is actually present. */ item_headers = (struct VasEBoot_reiserfs_item_header *) (block_header + 1); for (i = 0; i < item_count; i++) { int val; val = VasEBoot_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) { VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, "unexpected btree node"); goto fail; } i--; block_key = &(item_headers[i].key); break; } } if (!exact && i == item_count) { if (i == 0) { VasEBoot_error (VAS_EBOOT_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 = VAS_EBOOT_REISERFS_UNKNOWN; #ifdef VAS_EBOOT_REISERFS_DEBUG VasEBoot_printf("Not found.\n"); #endif } else { item->block_number = block_number; item->block_position = i; item->type = VasEBoot_reiserfs_get_key_type (block_key); VasEBoot_memcpy (&(item->header), &(item_headers[i]), sizeof (struct VasEBoot_reiserfs_item_header)); #ifdef VAS_EBOOT_REISERFS_DEBUG VasEBoot_printf ("F %03d/%03d ", i + 1, item_count); VasEBoot_reiserfs_print_key (block_key); #endif } assert (VasEBoot_errno == VAS_EBOOT_ERR_NONE); VasEBoot_free (block_header); return VAS_EBOOT_ERR_NONE; fail: assert (VasEBoot_errno != VAS_EBOOT_ERR_NONE); VasEBoot_free (block_header); assert (VasEBoot_errno != VAS_EBOOT_ERR_NONE); return VasEBoot_errno; } /* Return the path of the file which is pointed at by symlink NODE. */ static char * VasEBoot_reiserfs_read_symlink (VasEBoot_fshelp_node_t node) { char *symlink_buffer = 0; VasEBoot_size_t len = node->size; VasEBoot_ssize_t ret; symlink_buffer = VasEBoot_malloc (len + 1); if (! symlink_buffer) return 0; ret = VasEBoot_reiserfs_read_real (node, 0, symlink_buffer, len, 0, 0); if (ret < 0) { VasEBoot_free (symlink_buffer); return 0; } symlink_buffer[ret] = 0; return symlink_buffer; } /* Fill the mounted filesystem structure and return it. */ static struct VasEBoot_reiserfs_data * VasEBoot_reiserfs_mount (VasEBoot_disk_t disk) { struct VasEBoot_reiserfs_data *data = 0; data = VasEBoot_malloc (sizeof (*data)); if (! data) goto fail; VasEBoot_disk_read (disk, REISERFS_SUPER_BLOCK_OFFSET / VAS_EBOOT_DISK_SECTOR_SIZE, 0, sizeof (data->superblock), &(data->superblock)); if (VasEBoot_errno) goto fail; if (VasEBoot_memcmp (data->superblock.magic_string, REISERFS_MAGIC_STRING, sizeof (REISERFS_MAGIC_STRING) - 1)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a ReiserFS filesystem"); goto fail; } data->disk = disk; return data; fail: /* Disk is too small to contain a ReiserFS. */ if (VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE) VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a ReiserFS filesystem"); VasEBoot_free (data); return 0; } /* Call HOOK for each file in directory ITEM. */ static int VasEBoot_reiserfs_iterate_dir (VasEBoot_fshelp_node_t item, VasEBoot_fshelp_iterate_dir_hook_t hook, void *hook_data) { struct VasEBoot_reiserfs_data *data = item->data; struct VasEBoot_reiserfs_block_header *block_header = 0; VasEBoot_uint16_t block_size, block_position; VasEBoot_uint32_t block_number; VasEBoot_uint64_t next_offset = item->next_offset; int ret = 0; if (item->type != VAS_EBOOT_REISERFS_DIRECTORY) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILE_TYPE, N_("not a directory")); goto fail; } block_size = VasEBoot_le_to_cpu16 (data->superblock.block_size); block_header = VasEBoot_malloc (block_size + 1); if (! block_header) goto fail; block_number = item->block_number; block_position = item->block_position; VasEBoot_dprintf ("reiserfs", "Iterating directory...\n"); do { struct VasEBoot_reiserfs_directory_header *directory_headers; struct VasEBoot_fshelp_node directory_item; VasEBoot_uint16_t entry_count, entry_number; struct VasEBoot_reiserfs_item_header *item_headers; VasEBoot_disk_read (data->disk, block_number * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS), (((VasEBoot_off_t) block_number * block_size) & (VAS_EBOOT_DISK_SECTOR_SIZE - 1)), block_size, (char *) block_header); if (VasEBoot_errno) goto fail; ((char *) block_header)[block_size] = 0; #if 0 if (VasEBoot_le_to_cpu16 (block_header->level) != 1) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "reiserfs: block %d is not a leaf block", block_number); goto fail; } #endif item_headers = (struct VasEBoot_reiserfs_item_header *) (block_header + 1); directory_headers = ((struct VasEBoot_reiserfs_directory_header *) ((char *) block_header + VasEBoot_le_to_cpu16 (item_headers[block_position].item_location))); entry_count = VasEBoot_le_to_cpu16 (item_headers[block_position].u.entry_count); for (entry_number = 0; entry_number < entry_count; entry_number++) { struct VasEBoot_reiserfs_directory_header *directory_header = &directory_headers[entry_number]; VasEBoot_uint16_t entry_state = VasEBoot_le_to_cpu16 (directory_header->state); VasEBoot_fshelp_node_t entry_item; struct VasEBoot_reiserfs_key entry_key; enum VasEBoot_fshelp_filetype entry_type; char *entry_name; char *entry_name_end = 0; char c; if (!(entry_state & VAS_EBOOT_REISERFS_VISIBLE_MASK)) continue; entry_name = (((char *) directory_headers) + VasEBoot_le_to_cpu16 (directory_header->location)); if (entry_number == 0) { entry_name_end = (char *) block_header + VasEBoot_le_to_cpu16 (item_headers[block_position].item_location) + VasEBoot_le_to_cpu16 (item_headers[block_position].item_size); } else { entry_name_end = (((char *) directory_headers) + VasEBoot_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; VasEBoot_reiserfs_set_key_type (&entry_key, VAS_EBOOT_REISERFS_DIRECTORY, 2); VasEBoot_reiserfs_set_key_offset (&entry_key, 1); entry_item = VasEBoot_malloc (sizeof (*entry_item)); if (! entry_item) goto fail; if (VasEBoot_reiserfs_get_item (data, &entry_key, entry_item, 1) != VAS_EBOOT_ERR_NONE) { VasEBoot_free (entry_item); goto fail; } if (entry_item->type == VAS_EBOOT_REISERFS_DIRECTORY) entry_type = VAS_EBOOT_FSHELP_DIR; else { VasEBoot_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. */ VasEBoot_reiserfs_set_key_offset (&entry_key, 0); VasEBoot_reiserfs_set_key_type (&entry_key, VAS_EBOOT_REISERFS_STAT, 2); if (VasEBoot_reiserfs_get_item (data, &entry_key, entry_item, 1) != VAS_EBOOT_ERR_NONE) { VasEBoot_free (entry_item); goto fail; } if (entry_item->block_number != 0) { VasEBoot_uint16_t entry_version; entry_version = VasEBoot_le_to_cpu16 (entry_item->header.version); entry_block_number = entry_item->block_number; #if 0 VasEBoot_dprintf ("reiserfs", "version %04x block %08x (%08x) position %08x\n", entry_version, entry_block_number, ((VasEBoot_disk_addr_t) entry_block_number * block_size) / VAS_EBOOT_DISK_SECTOR_SIZE, VasEBoot_le_to_cpu16 (entry_item->header.item_location)); #endif if (entry_version == 0) /* Version 1 stat item. */ { struct VasEBoot_reiserfs_stat_item_v1 entry_v1_stat; VasEBoot_disk_read (data->disk, entry_block_number * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS), VasEBoot_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v1_stat), (char *) &entry_v1_stat); if (VasEBoot_errno) goto fail; #if 0 VasEBoot_dprintf ("reiserfs", "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", VasEBoot_le_to_cpu16 (entry_v1_stat.mode), VasEBoot_le_to_cpu16 (entry_v1_stat.hardlink_count), VasEBoot_le_to_cpu16 (entry_v1_stat.uid), VasEBoot_le_to_cpu16 (entry_v1_stat.gid), VasEBoot_le_to_cpu32 (entry_v1_stat.size), VasEBoot_le_to_cpu32 (entry_v1_stat.atime), VasEBoot_le_to_cpu32 (entry_v1_stat.mtime), VasEBoot_le_to_cpu32 (entry_v1_stat.ctime), VasEBoot_le_to_cpu32 (entry_v1_stat.rdev), VasEBoot_le_to_cpu32 (entry_v1_stat.first_direct_byte)); VasEBoot_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 = VasEBoot_le_to_cpu32 (entry_v1_stat.mtime); if ((VasEBoot_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK) == S_IFLNK) entry_type = VAS_EBOOT_FSHELP_SYMLINK; else entry_type = VAS_EBOOT_FSHELP_REG; entry_item->size = (VasEBoot_off_t) VasEBoot_le_to_cpu32 (entry_v1_stat.size); } else { struct VasEBoot_reiserfs_stat_item_v2 entry_v2_stat; VasEBoot_disk_read (data->disk, entry_block_number * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS), VasEBoot_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v2_stat), (char *) &entry_v2_stat); if (VasEBoot_errno) goto fail; #if 0 VasEBoot_dprintf ("reiserfs", "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", VasEBoot_le_to_cpu16 (entry_v2_stat.mode), VasEBoot_le_to_cpu16 (entry_v2_stat.reserved), VasEBoot_le_to_cpu32 (entry_v2_stat.hardlink_count), (unsigned int) (VasEBoot_le_to_cpu64 (entry_v2_stat.size) >> 32), (unsigned int) (VasEBoot_le_to_cpu64 (entry_v2_stat.size) && 0xFFFFFFFF), VasEBoot_le_to_cpu32 (entry_v2_stat.uid), VasEBoot_le_to_cpu32 (entry_v2_stat.gid), VasEBoot_le_to_cpu32 (entry_v2_stat.atime), VasEBoot_le_to_cpu32 (entry_v2_stat.mtime), VasEBoot_le_to_cpu32 (entry_v2_stat.ctime), VasEBoot_le_to_cpu32 (entry_v2_stat.blocks), VasEBoot_le_to_cpu32 (entry_v2_stat.first_direct_byte)); VasEBoot_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 = VasEBoot_le_to_cpu32 (entry_v2_stat.mtime); entry_item->size = (VasEBoot_off_t) VasEBoot_le_to_cpu64 (entry_v2_stat.size); if ((VasEBoot_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK) == S_IFLNK) entry_type = VAS_EBOOT_FSHELP_SYMLINK; else entry_type = VAS_EBOOT_FSHELP_REG; } } else { /* Pseudo file ".." never has stat block. */ if (entry_name_end == entry_name + 2 && VasEBoot_memcmp (entry_name, "..", 2) != 0) VasEBoot_dprintf ("reiserfs", "Warning : %s has no stat block !\n", entry_name); VasEBoot_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; VasEBoot_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; VasEBoot_reiserfs_set_key_offset (&(item_headers[block_position].key), next_offset); if (VasEBoot_reiserfs_get_item (data, &(item_headers[block_position].key), &directory_item, 1) != VAS_EBOOT_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 (VasEBoot_errno == VAS_EBOOT_ERR_NONE); VasEBoot_free (block_header); return ret; fail: assert (VasEBoot_errno != VAS_EBOOT_ERR_NONE); VasEBoot_free (block_header); return 0; } /****************************************************************************/ /* VasEBoot api functions */ /****************************************************************************/ /* Open a file named NAME and initialize FILE. */ static VasEBoot_err_t VasEBoot_reiserfs_open (struct VasEBoot_file *file, const char *name) { struct VasEBoot_reiserfs_data *data = 0; struct VasEBoot_fshelp_node root, *found = 0; struct VasEBoot_reiserfs_key key; VasEBoot_dl_ref (my_mod); data = VasEBoot_reiserfs_mount (file->device->disk); if (! data) goto fail; key.directory_id = VasEBoot_cpu_to_le32_compile_time (1); key.object_id = VasEBoot_cpu_to_le32_compile_time (2); key.u.v2.offset_type = 0; VasEBoot_reiserfs_set_key_type (&key, VAS_EBOOT_REISERFS_DIRECTORY, 2); VasEBoot_reiserfs_set_key_offset (&key, 1); if (VasEBoot_reiserfs_get_item (data, &key, &root, 1) != VAS_EBOOT_ERR_NONE) goto fail; if (root.block_number == 0) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "unable to find root item"); goto fail; /* Should never happen since checked at mount. */ } VasEBoot_fshelp_find_file (name, &root, &found, VasEBoot_reiserfs_iterate_dir, VasEBoot_reiserfs_read_symlink, VAS_EBOOT_FSHELP_REG); if (VasEBoot_errno) goto fail; file->size = found->size; VasEBoot_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 VAS_EBOOT_ERR_NONE; fail: assert (VasEBoot_errno != VAS_EBOOT_ERR_NONE); if (found != &root) VasEBoot_free (found); VasEBoot_free (data); VasEBoot_dl_unref (my_mod); return VasEBoot_errno; } static VasEBoot_ssize_t VasEBoot_reiserfs_read_real (struct VasEBoot_fshelp_node *node, VasEBoot_off_t off, char *buf, VasEBoot_size_t len, VasEBoot_disk_read_hook_t read_hook, void *read_hook_data) { unsigned int indirect_block, indirect_block_count; struct VasEBoot_reiserfs_key key; struct VasEBoot_reiserfs_data *data = node->data; struct VasEBoot_fshelp_node found; VasEBoot_uint16_t block_size = VasEBoot_le_to_cpu16 (data->superblock.block_size); VasEBoot_uint16_t item_size; VasEBoot_uint32_t *indirect_block_ptr = 0; VasEBoot_uint64_t current_key_offset = 1; VasEBoot_off_t initial_position, current_position, final_position, length; VasEBoot_disk_addr_t block; VasEBoot_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; VasEBoot_reiserfs_set_key_type (&key, VAS_EBOOT_REISERFS_ANY, 2); initial_position = off; current_position = 0; final_position = MIN (len + initial_position, node->size); VasEBoot_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); VasEBoot_reiserfs_set_key_offset (&key, initial_position + 1); if (VasEBoot_reiserfs_get_item (data, &key, &found, 0) != VAS_EBOOT_ERR_NONE) goto fail; if (found.block_number == 0) { VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, "offset %lld not found", (unsigned long long) initial_position); goto fail; } current_key_offset = VasEBoot_reiserfs_get_key_offset (&found.header.key); current_position = current_key_offset - 1; while (current_position < final_position) { VasEBoot_reiserfs_set_key_offset (&key, current_key_offset); if (VasEBoot_reiserfs_get_item (data, &key, &found, 1) != VAS_EBOOT_ERR_NONE) goto fail; if (found.block_number == 0) goto fail; item_size = VasEBoot_le_to_cpu16 (found.header.item_size); switch (found.type) { case VAS_EBOOT_REISERFS_DIRECT: block = ((VasEBoot_disk_addr_t) found.block_number) * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS); VasEBoot_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); VasEBoot_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; VasEBoot_disk_read (found.data->disk, block, offset + VasEBoot_le_to_cpu16 (found.header.item_location), length, buf); found.data->disk->read_hook = 0; if (VasEBoot_errno) goto fail; buf += length; current_position += offset + length; } else current_position += item_size; break; case VAS_EBOOT_REISERFS_INDIRECT: indirect_block_count = item_size / sizeof (*indirect_block_ptr); indirect_block_ptr = VasEBoot_malloc (item_size); if (! indirect_block_ptr) goto fail; VasEBoot_disk_read (found.data->disk, found.block_number * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS), VasEBoot_le_to_cpu16 (found.header.item_location), item_size, indirect_block_ptr); if (VasEBoot_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 = VasEBoot_le_to_cpu32 (indirect_block_ptr[indirect_block]) * (block_size >> VAS_EBOOT_DISK_SECTOR_BITS); VasEBoot_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); VasEBoot_dprintf ("reiserfs", "Reading indirect block %u from %u to %u...\n", (unsigned) block, (unsigned) offset, (unsigned) (offset + length)); #if 0 VasEBoot_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 VasEBoot_disk_read (found.data->disk, block, offset, length, buf); if (VasEBoot_errno) goto fail; buf += length; current_position += offset + length; } else current_position += block_size; } found.data->disk->read_hook = 0; VasEBoot_free (indirect_block_ptr); indirect_block_ptr = 0; break; default: goto fail; } current_key_offset = current_position + 1; } VasEBoot_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 VAS_EBOOT_REISERFS_DIRECT: read_length = MIN (len, item_size - file->offset); VasEBoot_disk_read (found.data->disk, (found.block_number * block_size) / VAS_EBOOT_DISK_SECTOR_SIZE, VasEBoot_le_to_cpu16 (found.header.item_location) + file->offset, read_length, buf); if (VasEBoot_errno) goto fail; break; case VAS_EBOOT_REISERFS_INDIRECT: indirect_block_count = item_size / sizeof (*indirect_block_ptr); indirect_block_ptr = VasEBoot_malloc (item_size); if (!indirect_block_ptr) goto fail; VasEBoot_disk_read (found.data->disk, (found.block_number * block_size) / VAS_EBOOT_DISK_SECTOR_SIZE, VasEBoot_le_to_cpu16 (found.header.item_location), item_size, (char *) indirect_block_ptr); if (VasEBoot_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); VasEBoot_disk_read (found.data->disk, (VasEBoot_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / VAS_EBOOT_DISK_SECTOR_SIZE, file->offset % block_size, read, ((void *) buf) + read_length); if (VasEBoot_errno) goto fail; read_length += read; } VasEBoot_free (indirect_block_ptr); break; default: goto fail; } return read_length; #endif fail: VasEBoot_free (indirect_block_ptr); return -1; } static VasEBoot_ssize_t VasEBoot_reiserfs_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len) { return VasEBoot_reiserfs_read_real (file->data, file->offset, buf, len, file->read_hook, file->read_hook_data); } /* Close the file FILE. */ static VasEBoot_err_t VasEBoot_reiserfs_close (VasEBoot_file_t file) { struct VasEBoot_fshelp_node *node = file->data; struct VasEBoot_reiserfs_data *data = node->data; VasEBoot_free (data); VasEBoot_free (node); VasEBoot_dl_unref (my_mod); return VAS_EBOOT_ERR_NONE; } /* Context for VasEBoot_reiserfs_dir. */ struct VasEBoot_reiserfs_dir_ctx { VasEBoot_fs_dir_hook_t hook; void *hook_data; }; /* Helper for VasEBoot_reiserfs_dir. */ static int VasEBoot_reiserfs_dir_iter (const char *filename, enum VasEBoot_fshelp_filetype filetype, VasEBoot_fshelp_node_t node, void *data) { struct VasEBoot_reiserfs_dir_ctx *ctx = data; struct VasEBoot_dirhook_info info; VasEBoot_memset (&info, 0, sizeof (info)); info.dir = ((filetype & VAS_EBOOT_FSHELP_TYPE_MASK) == VAS_EBOOT_FSHELP_DIR); info.mtimeset = 1; info.mtime = node->mtime; VasEBoot_free (node); return ctx->hook (filename, &info, ctx->hook_data); } /* Call HOOK with each file under DIR. */ static VasEBoot_err_t VasEBoot_reiserfs_dir (VasEBoot_device_t device, const char *path, VasEBoot_fs_dir_hook_t hook, void *hook_data) { struct VasEBoot_reiserfs_dir_ctx ctx = { hook, hook_data }; struct VasEBoot_reiserfs_data *data = 0; struct VasEBoot_fshelp_node root, *found; struct VasEBoot_reiserfs_key root_key; VasEBoot_dl_ref (my_mod); data = VasEBoot_reiserfs_mount (device->disk); if (! data) goto fail; root_key.directory_id = VasEBoot_cpu_to_le32_compile_time (1); root_key.object_id = VasEBoot_cpu_to_le32_compile_time (2); root_key.u.v2.offset_type = 0; VasEBoot_reiserfs_set_key_type (&root_key, VAS_EBOOT_REISERFS_DIRECTORY, 2); VasEBoot_reiserfs_set_key_offset (&root_key, 1); if (VasEBoot_reiserfs_get_item (data, &root_key, &root, 1) != VAS_EBOOT_ERR_NONE) goto fail; if (root.block_number == 0) { VasEBoot_error(VAS_EBOOT_ERR_BAD_FS, "root not found"); goto fail; } VasEBoot_fshelp_find_file (path, &root, &found, VasEBoot_reiserfs_iterate_dir, VasEBoot_reiserfs_read_symlink, VAS_EBOOT_FSHELP_DIR); if (VasEBoot_errno) goto fail; VasEBoot_reiserfs_iterate_dir (found, VasEBoot_reiserfs_dir_iter, &ctx); VasEBoot_free (data); VasEBoot_dl_unref (my_mod); return VAS_EBOOT_ERR_NONE; fail: VasEBoot_free (data); VasEBoot_dl_unref (my_mod); return VasEBoot_errno; } /* Return the label of the device DEVICE in LABEL. The label is returned in a VasEBoot_malloc'ed buffer and should be freed by the caller. */ static VasEBoot_err_t VasEBoot_reiserfs_label (VasEBoot_device_t device, char **label) { struct VasEBoot_reiserfs_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_reiserfs_mount (disk); if (data) { *label = VasEBoot_strndup (data->superblock.label, sizeof (data->superblock.label)); } else *label = NULL; VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_reiserfs_uuid (VasEBoot_device_t device, char **uuid) { struct VasEBoot_reiserfs_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); *uuid = NULL; data = VasEBoot_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 = VasEBoot_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", VasEBoot_be_to_cpu16 (data->superblock.uuid[0]), VasEBoot_be_to_cpu16 (data->superblock.uuid[1]), VasEBoot_be_to_cpu16 (data->superblock.uuid[2]), VasEBoot_be_to_cpu16 (data->superblock.uuid[3]), VasEBoot_be_to_cpu16 (data->superblock.uuid[4]), VasEBoot_be_to_cpu16 (data->superblock.uuid[5]), VasEBoot_be_to_cpu16 (data->superblock.uuid[6]), VasEBoot_be_to_cpu16 (data->superblock.uuid[7])); } VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static struct VasEBoot_fs VasEBoot_reiserfs_fs = { .name = "reiserfs", .fs_dir = VasEBoot_reiserfs_dir, .fs_open = VasEBoot_reiserfs_open, .fs_read = VasEBoot_reiserfs_read, .fs_close = VasEBoot_reiserfs_close, .fs_label = VasEBoot_reiserfs_label, .fs_uuid = VasEBoot_reiserfs_uuid, #ifdef VAS_EBOOT_UTIL .reserved_first_sector = 1, .blocklist_install = 1, #endif .next = 0 }; VAS_EBOOT_MOD_INIT(reiserfs) { if (!VasEBoot_is_lockdown ()) { VasEBoot_reiserfs_fs.mod = mod; VasEBoot_fs_register (&VasEBoot_reiserfs_fs); } my_mod = mod; } VAS_EBOOT_MOD_FINI(reiserfs) { if (!VasEBoot_is_lockdown ()) VasEBoot_fs_unregister (&VasEBoot_reiserfs_fs); }