diff --git a/fs/reiserfs.c b/fs/reiserfs.c index a4c60ca..610fadd 100644 --- a/fs/reiserfs.c +++ b/fs/reiserfs.c @@ -52,7 +52,8 @@ #define REISERFS_SUPER_BLOCK_OFFSET 0x10000 #define REISERFS_MAGIC_LEN 12 -#define REISERFS_MAGIC_STRING "ReIsEr2Fs\0\0\0" +#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 REISERFS_MAX_LABEL_LENGTH 16 @@ -109,8 +110,6 @@ struct grub_reiserfs_superblock grub_uint32_t inode_generation; } __attribute__ ((packed)); -#ifdef GRUB_REISERFS_JOURNALING -# error "Journaling not yet supported." struct grub_reiserfs_journal_header { grub_uint32_t last_flush_uid; @@ -118,15 +117,20 @@ struct grub_reiserfs_journal_header grub_uint32_t mount_id; } __attribute__ ((packed)); -struct grub_reiserfs_transaction_header +struct grub_reiserfs_description_block { grub_uint32_t id; grub_uint32_t len; grub_uint32_t mount_id; - char *data; - char checksum[12]; + grub_uint32_t real_blocks[0]; +} __attribute__ ((packed)); + +struct grub_reiserfs_commit_block +{ + grub_uint32_t id; + grub_uint32_t len; + grub_uint32_t real_blocks[0]; } __attribute__ ((packed)); -#endif struct grub_reiserfs_stat_item_v1 { @@ -228,6 +232,7 @@ struct grub_reiserfs_data { struct grub_reiserfs_superblock superblock; grub_disk_t disk; + grub_fshelp_journal_t journal; }; /* Internal-only functions. Not to be used outside of this file. */ @@ -504,8 +509,8 @@ grub_reiserfs_get_item (struct grub_reiserfs_data *data, do { grub_disk_read (data->disk, - (((grub_disk_addr_t) block_number * block_size) - >> GRUB_DISK_SECTOR_BITS), + grub_fshelp_map_block (data->journal, block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), block_size, (char *) block_header); @@ -655,8 +660,8 @@ grub_reiserfs_read_symlink (grub_fshelp_node_t node) block_size = grub_le_to_cpu16 (node->data->superblock.block_size); len = grub_le_to_cpu16 (found.header.item_size); - block = (((grub_disk_addr_t) found.block_number * block_size) - >> GRUB_DISK_SECTOR_BITS); + block = (grub_fshelp_map_block (node->data->journal, found.block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS)); offset = grub_le_to_cpu16 (found.header.item_location); symlink_buffer = grub_malloc (len); @@ -674,6 +679,115 @@ grub_reiserfs_read_symlink (grub_fshelp_node_t node) return 0; } +static void +grub_reiserfs_get_journal (struct grub_reiserfs_data *data) +{ + int block_size = grub_le_to_cpu16 (data->superblock.block_size); + char buf[block_size]; + struct grub_reiserfs_journal_header *jh; + grub_fshelp_journal_t log; + grub_uint32_t seq_id, mount_id; + int num_blocks = grub_le_to_cpu32 (data->superblock.journal_original_size); + int base_block = grub_le_to_cpu32 (data->superblock.journal_block); + int last_num, num, block; + + data->journal = 0; + + if (! data->superblock.journal_block) + return; + + if (grub_disk_read (data->disk, + (base_block + num_blocks) + * (block_size >> GRUB_DISK_SECTOR_BITS), + 0, sizeof (struct grub_reiserfs_journal_header), + buf)) + return; + + log = grub_malloc (sizeof (struct grub_fshelp_journal) + + num_blocks * sizeof (grub_uint32_t)); + if (! log) + return; + + jh = (struct grub_reiserfs_journal_header *) &buf[0]; + + log->type = GRUB_FSHELP_JOURNAL_TYPE_BLOCK; + log->blkno = base_block; + log->first_block = 0; + log->last_block = num_blocks; + log->start_block = grub_le_to_cpu32 (jh->unflushed_offset); + + seq_id = grub_le_to_cpu32 (jh->last_flush_uid); + mount_id = grub_le_to_cpu32 (jh->mount_id); + + last_num = num = 0; + block = log->start_block; + + while (1) + { + struct grub_reiserfs_description_block *db; + struct grub_reiserfs_commit_block *cb; + grub_uint32_t i, len, half_len, id; + + if (grub_disk_read (data->disk, + (base_block + block) + * (block_size >> GRUB_DISK_SECTOR_BITS), + 0, sizeof (buf), buf)) + break; + + if (grub_memcmp (&buf[block_size - REISERFS_MAGIC_LEN], + REISERFS_MAGIC_DESC_BLOCK, + sizeof (REISERFS_MAGIC_DESC_BLOCK) - 1)) + break; + + db = (struct grub_reiserfs_description_block *) &buf[0]; + id = grub_le_to_cpu32 (db->id); + len = grub_le_to_cpu32 (db->len); + if ((grub_le_to_cpu32 (db->id) <= seq_id) && (id <= mount_id)) + break; + + log->mapping[num++] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING; + half_len = ((block_size - 24) >> 2); + if (half_len > len) + half_len = len; + + for (i = 0; i < half_len; i++) + log->mapping[num++] = db->real_blocks[i]; + + block += grub_le_to_cpu32 (db->len) + 1; + if (block >= log->last_block) + block -= log->last_block; + + if (grub_disk_read (data->disk, + (base_block + block) + * (block_size >> GRUB_DISK_SECTOR_BITS), + 0, sizeof (buf), buf)) + break; + + cb = (struct grub_reiserfs_commit_block *) &buf[0]; + if ((grub_le_to_cpu32 (cb->id) != id) || + (grub_le_to_cpu32 (cb->len) != len)) + break; + + for (i = 0; i < len - half_len; i++) + log->mapping[num++] = cb->real_blocks[i]; + + last_num = num; + log->mapping[num++] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING; + + block++; + if (block >= log->last_block) + block -= log->last_block; + }; + + if (! last_num) + grub_free (log); + else + { + log->num_mappings = last_num; + data->journal = log; + } +} + /* Fill the mounted filesystem structure and return it. */ static struct grub_reiserfs_data * grub_reiserfs_mount (grub_disk_t disk) @@ -687,12 +801,13 @@ grub_reiserfs_mount (grub_disk_t disk) if (grub_errno) goto fail; if (grub_memcmp (data->superblock.magic_string, - REISERFS_MAGIC_STRING, REISERFS_MAGIC_LEN)) + REISERFS_MAGIC_STRING, sizeof (REISERFS_MAGIC_STRING) - 1)) { grub_error (GRUB_ERR_BAD_FS, "not a reiserfs filesystem"); goto fail; } data->disk = disk; + grub_reiserfs_get_journal (data); return data; fail: @@ -740,8 +855,8 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, struct grub_reiserfs_item_header *item_headers; grub_disk_read (data->disk, - (((grub_disk_addr_t) block_number * block_size) - >> GRUB_DISK_SECTOR_BITS), + grub_fshelp_map_block (data->journal, block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), block_size, (char *) block_header); @@ -835,7 +950,8 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, { struct grub_reiserfs_stat_item_v1 entry_v1_stat; grub_disk_read (data->disk, - ((grub_disk_addr_t) entry_block_number * block_size) >> GRUB_DISK_SECTOR_BITS, + grub_fshelp_map_block (data->journal, entry_block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v1_stat), (char *) &entry_v1_stat); @@ -877,7 +993,8 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, { struct grub_reiserfs_stat_item_v2 entry_v2_stat; grub_disk_read (data->disk, - ((grub_disk_addr_t) entry_block_number * block_size) >> GRUB_DISK_SECTOR_BITS, + grub_fshelp_map_block (data->journal, entry_block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v2_stat), (char *) &entry_v2_stat); @@ -1025,8 +1142,8 @@ grub_reiserfs_open (struct grub_file *file, const char *name) { struct grub_reiserfs_stat_item_v1 entry_v1_stat; grub_disk_read (data->disk, - (((grub_disk_addr_t) block_number * block_size) - >> GRUB_DISK_SECTOR_BITS), + grub_fshelp_map_block (data->journal, block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), entry_location + (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), @@ -1039,8 +1156,8 @@ grub_reiserfs_open (struct grub_file *file, const char *name) { struct grub_reiserfs_stat_item_v2 entry_v2_stat; grub_disk_read (data->disk, - (((grub_disk_addr_t) block_number * block_size) - >> GRUB_DISK_SECTOR_BITS), + grub_fshelp_map_block (data->journal, block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), entry_location + (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), @@ -1108,8 +1225,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) switch (found.type) { case GRUB_REISERFS_DIRECT: - block = (((grub_disk_addr_t) found.block_number * block_size) - >> GRUB_DISK_SECTOR_BITS); + block = (grub_fshelp_map_block (data->journal, found.block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS)); grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); if (initial_position < current_position + item_size) { @@ -1141,8 +1258,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) if (! indirect_block_ptr) goto fail; grub_disk_read (found.data->disk, - (((grub_disk_addr_t) found.block_number * block_size) - >> GRUB_DISK_SECTOR_BITS), + grub_fshelp_map_block (data->journal, found.block_number, 0) + * (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (found.header.item_location), item_size, (char *) indirect_block_ptr); if (grub_errno) @@ -1153,9 +1270,10 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) && current_position < final_position; indirect_block++) { - block = ((grub_disk_addr_t) - grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) - * block_size) >> GRUB_DISK_SECTOR_BITS; + block = grub_fshelp_map_block (data->journal, + grub_le_to_cpu32 (indirect_block_ptr[indirect_block]), + 0) + * (block_size >> GRUB_DISK_SECTOR_BITS); grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block); if (current_position + block_size >= initial_position) { @@ -1254,6 +1372,7 @@ grub_reiserfs_close (grub_file_t file) struct grub_fshelp_node *node = file->data; struct grub_reiserfs_data *data = node->data; + grub_free (data->journal); grub_free (data); grub_free (node); #ifndef GRUB_UTIL