diff --git a/fs/affs.c b/fs/affs.c index 8ebfa40..5418ffb 100644 --- a/fs/affs.c +++ b/fs/affs.c @@ -109,18 +109,19 @@ static grub_dl_t my_mod; #endif -static int -grub_affs_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { int links; grub_uint32_t pos; int block = node->block; struct grub_affs_file file; struct grub_affs_data *data = node->data; + grub_uint32_t mod; /* Find the block that points to the fileblock we are looking up by following the chain until the right table is reached. */ - for (links = fileblock / (data->htsize); links; links--) + for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--) { grub_disk_read (data->disk, block + data->blocksize - 1, data->blocksize * (GRUB_DISK_SECTOR_SIZE @@ -133,7 +134,7 @@ grub_affs_read_block (grub_fshelp_node_t node, int fileblock) } /* Translate the fileblock to the block within the right table. */ - fileblock = fileblock % (data->htsize); + fileblock = mod; grub_disk_read (data->disk, block, GRUB_AFFS_BLOCKPTR_OFFSET + (data->htsize - fileblock - 1) * sizeof (pos), @@ -567,4 +568,3 @@ GRUB_MOD_FINI(affs) { grub_fs_unregister (&grub_affs_fs); } - diff --git a/fs/afs.c b/fs/afs.c index 7c61671..5b5eb68 100644 --- a/fs/afs.c +++ b/fs/afs.c @@ -199,12 +199,12 @@ grub_afs_read_inode (struct grub_afs_data *data, } static int -grub_afs_read_block (grub_fshelp_node_t node, int fileblock) +grub_afs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_afs_sblock *sb = &node->data->sblock; struct grub_afs_datastream *ds = &node->inode.stream; - if ((grub_uint32_t) fileblock < U64 (sb, ds->max_direct_range)) + if (fileblock < U64 (sb, ds->max_direct_range)) { int i; @@ -215,7 +215,7 @@ grub_afs_read_block (grub_fshelp_node_t node, int fileblock) fileblock -= U16 (sb, ds->direct[i].len); } } - else if ((grub_uint32_t) fileblock < U64 (sb, ds->max_indirect_range)) + else if (fileblock < U64 (sb, ds->max_indirect_range)) { int ptrs_per_blk = sb->block_size / sizeof (struct grub_afs_blockrun); struct grub_afs_blockrun indir[ptrs_per_blk]; diff --git a/fs/ext2.c b/fs/ext2.c index ec66582..ce7b2fa 100644 --- a/fs/ext2.c +++ b/fs/ext2.c @@ -71,6 +71,21 @@ ? EXT2_GOOD_OLD_INODE_SIZE \ : grub_le_to_cpu16 (data->sblock.inode_size)) +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 + +#define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U + +#define EXT3_JOURNAL_DESCRIPTOR_BLOCK 1 +#define EXT3_JOURNAL_COMMIT_BLOCK 2 +#define EXT3_JOURNAL_SUPERBLOCK_V1 3 +#define EXT3_JOURNAL_SUPERBLOCK_V2 4 +#define EXT3_JOURNAL_REVOKE_BLOCK 5 + +#define EXT3_JOURNAL_FLAG_ESCAPE 1 +#define EXT3_JOURNAL_FLAG_SAME_UUID 2 +#define EXT3_JOURNAL_FLAG_DELETED 4 +#define EXT3_JOURNAL_FLAG_LAST_TAG 8 + /* The ext2 superblock. */ struct grub_ext2_sblock { @@ -109,6 +124,21 @@ struct grub_ext2_sblock char volume_name[16]; char last_mounted_on[64]; grub_uint32_t compression_info; + grub_uint8_t prealloc_blocks; + grub_uint8_t prealloc_dir_blocks; + grub_uint16_t reserved_gdt_blocks; + grub_uint8_t journal_uuid[16]; + grub_uint32_t journal_inum; + grub_uint32_t journal_dev; + grub_uint32_t last_orphan; + grub_uint32_t hash_seed[4]; + grub_uint8_t def_hash_version; + grub_uint8_t jnl_backup_type; + grub_uint16_t reserved_word_pad; + grub_uint32_t default_mount_opts; + grub_uint32_t first_meta_bg; + grub_uint32_t mkfs_time; + grub_uint32_t jnl_blocks[17]; }; /* The ext2 blockgroup. */ @@ -166,6 +196,36 @@ struct ext2_dirent grub_uint8_t filetype; }; +struct grub_ext3_journal_header +{ + grub_uint32_t magic; + grub_uint32_t block_type; + grub_uint32_t sequence; +}; + +struct grub_ext3_journal_revoke_header +{ + struct grub_ext3_journal_header header; + grub_uint32_t count; + grub_uint32_t data[0]; +}; + +struct grub_ext3_journal_block_tag +{ + grub_uint32_t block; + grub_uint32_t flags; +}; + +struct grub_ext3_journal_sblock +{ + struct grub_ext3_journal_header header; + grub_uint32_t block_size; + grub_uint32_t maxlen; + grub_uint32_t first; + grub_uint32_t sequence; + grub_uint32_t start; +}; + struct grub_fshelp_node { struct grub_ext2_data *data; @@ -181,6 +241,8 @@ struct grub_ext2_data grub_disk_t disk; struct grub_ext2_inode *inode; struct grub_fshelp_node diropen; + struct grub_fshelp_node logfile; + grub_fshelp_journal_t journal; }; #ifndef GRUB_UTIL @@ -196,20 +258,21 @@ grub_ext2_blockgroup (struct grub_ext2_data *data, int group, struct grub_ext2_block_group *blkgrp) { return grub_disk_read (data->disk, - ((grub_le_to_cpu32 (data->sblock.first_data_block) + 1) + (grub_fshelp_map_block (data->journal, + grub_le_to_cpu32 (data->sblock.first_data_block) + 1) << LOG2_EXT2_BLOCK_SIZE (data)), group * sizeof (struct grub_ext2_block_group), sizeof (struct grub_ext2_block_group), (char *) blkgrp); } -static int -grub_ext2_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_ext2_data *data = node->data; struct grub_ext2_inode *inode = &node->inode; int blknr; - int blksz = EXT2_BLOCK_SIZE (data); + unsigned int blksz = EXT2_BLOCK_SIZE (data); int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data); /* Direct blocks. */ @@ -221,7 +284,8 @@ grub_ext2_read_block (grub_fshelp_node_t node, int fileblock) grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (inode->blocks.indir_block) + grub_fshelp_map_block(data->journal, + grub_le_to_cpu32 (inode->blocks.indir_block)) << log2_blksz, 0, blksz, (char *) indir)) return grub_errno; @@ -237,13 +301,15 @@ grub_ext2_read_block (grub_fshelp_node_t node, int fileblock) grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (inode->blocks.double_indir_block) + grub_fshelp_map_block(data->journal, + grub_le_to_cpu32 (inode->blocks.double_indir_block)) << log2_blksz, 0, blksz, (char *) indir)) return grub_errno; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (indir[rblock / perblock]) + grub_fshelp_map_block(data->journal, + grub_le_to_cpu32 (indir[rblock / perblock])) << log2_blksz, 0, blksz, (char *) indir)) return grub_errno; @@ -259,10 +325,9 @@ grub_ext2_read_block (grub_fshelp_node_t node, int fileblock) blknr = -1; } - return blknr; + return grub_fshelp_map_block (data->journal, blknr); } - /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static grub_ssize_t @@ -308,8 +373,9 @@ grub_ext2_read_inode (struct grub_ext2_data *data, /* Read the inode. */ if (grub_disk_read (data->disk, - ((grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno) - << LOG2_EXT2_BLOCK_SIZE (data)), + grub_fshelp_map_block(data->journal, + grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno) + << LOG2_EXT2_BLOCK_SIZE (data), EXT2_INODE_SIZE (data) * blkoff, sizeof (struct grub_ext2_inode), (char *) inode)) return grub_errno; @@ -317,6 +383,169 @@ grub_ext2_read_inode (struct grub_ext2_data *data, return 0; } +static void +grub_ext3_get_journal (struct grub_ext2_data *data) +{ + char buf[1 << LOG2_BLOCK_SIZE (data)]; + struct grub_ext3_journal_sblock *jsb; + grub_fshelp_journal_t log; + int last_num, num, block, log2bs; + grub_uint32_t seq; + + auto void next_block (void); + void next_block (void) + { + block++; + if (block >= log->last_block) + block = log->first_block; + } + + data->journal = 0; + + if (! (data->sblock.feature_compatibility & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return; + + if (! data->sblock.journal_inum) + return; + + data->logfile.data = data; + data->logfile.ino = data->sblock.journal_inum; + data->logfile.inode_read = 1; + + if (grub_ext2_read_inode (data, data->logfile.ino, &data->logfile.inode)) + return; + + log2bs = LOG2_EXT2_BLOCK_SIZE (data); + if (grub_fshelp_read_file (data->disk, &data->logfile, 0, + 0, sizeof (struct grub_ext3_journal_sblock), + buf, grub_ext2_read_block, + sizeof (buf), log2bs) != + sizeof (struct grub_ext3_journal_sblock)) + return; + + jsb = (struct grub_ext3_journal_sblock *) &buf[0]; + if (grub_be_to_cpu32 (jsb->header.magic) != EXT3_JOURNAL_MAGIC_NUMBER) + return; + + /* Empty journal. */ + if (! jsb->start) + return; + + log = grub_malloc (sizeof (struct grub_fshelp_journal) + + grub_be_to_cpu32 (jsb->maxlen) * sizeof (grub_disk_addr_t)); + if (! log) + return; + + log->type = GRUB_FSHELP_JOURNAL_TYPE_FILE; + log->node = &data->logfile; + log->get_block = grub_ext2_read_block; + log->first_block = grub_be_to_cpu32 (jsb->first); + log->last_block = grub_be_to_cpu32 (jsb->maxlen); + log->start_block = grub_be_to_cpu32 (jsb->start); + + last_num = num = 0; + block = log->start_block; + seq = grub_be_to_cpu32 (jsb->sequence); + + while (1) + { + struct grub_ext3_journal_header *jh; + + if (grub_fshelp_read_file (data->disk, &data->logfile, 0, + block << (log2bs + 9), sizeof (buf), + buf, grub_ext2_read_block, + log->last_block << (log2bs + 9), + log2bs) != + (int) sizeof (buf)) + break; + + jh = (struct grub_ext3_journal_header *) &buf[0]; + if (grub_be_to_cpu32 (jh->magic) != EXT3_JOURNAL_MAGIC_NUMBER) + break; + + if (grub_be_to_cpu32 (jh->sequence) != seq) + break; + + log->mapping[num++] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING; + next_block(); + + switch (grub_be_to_cpu32 (jh->block_type)) + { + case EXT3_JOURNAL_DESCRIPTOR_BLOCK: + { + struct grub_ext3_journal_block_tag *tag; + int ofs, flags; + + ofs = sizeof (struct grub_ext3_journal_header); + + do + { + tag = (struct grub_ext3_journal_block_tag *) &buf[ofs]; + ofs += sizeof (struct grub_ext3_journal_block_tag); + + if (ofs > (int) sizeof (buf)) + break; + + flags = grub_be_to_cpu32 (tag->flags); + if (! (flags & EXT3_JOURNAL_FLAG_SAME_UUID)) + ofs += 16; + + log->mapping[num++] = grub_be_to_cpu32 (tag->block); + next_block(); + } + while (! (flags & EXT3_JOURNAL_FLAG_LAST_TAG)); + + continue; + } + + case EXT3_JOURNAL_COMMIT_BLOCK: + { + seq++; + last_num = num - 1; + continue; + } + + case EXT3_JOURNAL_REVOKE_BLOCK: + { + struct grub_ext3_journal_revoke_header *jrh; + grub_uint32_t i; + + jrh = (struct grub_ext3_journal_revoke_header *) jh; + + for (i = 0; i < grub_be_to_cpu32 (jrh->count); i++) + { + int j; + grub_uint32_t map; + + map = grub_be_to_cpu32 (jrh->data[i]); + for (j = 0; j < num; j++) + if (log->mapping[j] == map) + log->mapping[j] = GRUB_FSHELP_JOURNAL_UNUSED_MAPPING; + } + + continue; + } + default: + last_num = 0; + goto quit; + } + } + +quit: + if (! last_num) + grub_free (log); + else + { + int size; + + size = sizeof (struct grub_fshelp_journal) + + last_num * sizeof (grub_disk_addr_t); + + log->num_mappings = last_num; + data->journal = grub_realloc (log, size); + } +} + static struct grub_ext2_data * grub_ext2_mount (grub_disk_t disk) { @@ -336,12 +565,14 @@ grub_ext2_mount (grub_disk_t disk) if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC) goto fail; + data->disk = disk; + grub_ext3_get_journal (data); + data->diropen.data = data; data->diropen.ino = 2; data->diropen.inode_read = 1; data->inode = &data->diropen.inode; - data->disk = disk; grub_ext2_read_inode (data, 2, data->inode); if (grub_errno) @@ -540,7 +771,11 @@ grub_ext2_open (struct grub_file *file, const char *name) static grub_err_t grub_ext2_close (grub_file_t file) { - grub_free (file->data); + if (file->data) + { + grub_free (((struct grub_ext2_data *) file->data)->journal); + grub_free (file->data); + } #ifndef GRUB_UTIL grub_dl_unref (my_mod); diff --git a/fs/fshelp.c b/fs/fshelp.c index bbb58ac..13a81b6 100644 --- a/fs/fshelp.c +++ b/fs/fshelp.c @@ -72,7 +72,7 @@ grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode, void free_node (grub_fshelp_node_t node) { - if (node != rootnode && node != currroot) + if (node != rootnode && node != currroot) grub_free (node); } @@ -223,25 +223,26 @@ grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode, grub_ssize_t grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node, void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf, - int (*get_block) (grub_fshelp_node_t node, int block), + unsigned offset, + unsigned length), + grub_off_t pos, grub_size_t len, char *buf, + grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, + grub_disk_addr_t block), grub_off_t filesize, int log2blocksize) { - int i; - int blockcnt; + grub_disk_addr_t i, blockcnt; int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS); /* Adjust LEN so it we can't read past the end of the file. */ - if (len > filesize) - len = filesize; + if (pos + len > filesize) + len = filesize - pos; - blockcnt = ((len + pos) + blocksize - 1) / blocksize; + blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS); - for (i = pos / blocksize; i < blockcnt; i++) + for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++) { - int blknr; - int blockoff = pos % blocksize; + grub_disk_addr_t blknr; + int blockoff = pos & (blocksize - 1); int blockend = blocksize; int skipfirst = 0; @@ -255,7 +256,7 @@ grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node, /* Last block. */ if (i == blockcnt - 1) { - blockend = (len + pos) % blocksize; + blockend = (len + pos) & (blocksize - 1); /* The last portion is exactly blocksize. */ if (! blockend) @@ -263,7 +264,7 @@ grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node, } /* First block. */ - if (i == pos / blocksize) + if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS))) { skipfirst = blockoff; blockend -= skipfirst; @@ -310,3 +311,30 @@ grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow) return GRUB_ERR_NONE; } + +grub_disk_addr_t +grub_fshelp_map_block (grub_fshelp_journal_t log, grub_disk_addr_t block) +{ + int map_block; + + if (! log) + return block; + + for (map_block = log->num_mappings - 1; map_block >= 0; map_block--) + { + if (log->mapping[map_block] == block) + break; + } + + if (map_block < 0) + return block; + + map_block += log->start_block; + if (map_block >= log->last_block) + map_block -= log->last_block - log->first_block; + + if (log->type == GRUB_FSHELP_JOURNAL_TYPE_BLOCK) + return log->blkno + map_block; + else + return log->get_block (log->node, map_block); +} diff --git a/fs/hfsplus.c b/fs/hfsplus.c index fe72539..f4106f0 100644 --- a/fs/hfsplus.c +++ b/fs/hfsplus.c @@ -285,8 +285,8 @@ static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya, /* Search for the block FILEBLOCK inside the file NODE. Return the blocknumber of this block on disk. */ -static int -grub_hfsplus_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_hfsplus_btnode *nnode = 0; int blksleft = fileblock; diff --git a/fs/ntfs.c b/fs/ntfs.c index 2922ade..786f0e3 100644 --- a/fs/ntfs.c +++ b/fs/ntfs.c @@ -331,8 +331,8 @@ retry: return 0; } -static int -grub_ntfs_read_block (grub_fshelp_node_t node, int block) +static grub_disk_addr_t +grub_ntfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t block) { struct grub_ntfs_rlst *ctx; diff --git a/fs/reiserfs.c b/fs/reiserfs.c index b536b21..87a754c 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) * + (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) * + (block_size >> GRUB_DISK_SECTOR_BITS)); offset = grub_le_to_cpu16 (found.header.item_location); symlink_buffer = grub_malloc (len + 1); @@ -675,6 +680,124 @@ 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_disk_addr_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, mid; + + 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); + mid = grub_le_to_cpu32 (db->mount_id); + if ((id <= seq_id) && (mid <= 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; + + seq_id = id; + mount_id = mid; + }; + + if (! last_num) + grub_free (log); + else + { + int size; + + size = sizeof (struct grub_fshelp_journal) + + last_num * sizeof (grub_disk_addr_t); + + log->num_mappings = last_num; + data->journal = grub_realloc (log, size); + } +} + /* Fill the mounted filesystem structure and return it. */ static struct grub_reiserfs_data * grub_reiserfs_mount (grub_disk_t disk) @@ -688,12 +811,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: @@ -741,8 +865,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), block_size, (char *) block_header); @@ -836,7 +960,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v1_stat), (char *) &entry_v1_stat); @@ -878,7 +1003,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (entry_item->header.item_location), sizeof (entry_v2_stat), (char *) &entry_v2_stat); @@ -1026,8 +1152,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), entry_location + (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), @@ -1040,8 +1166,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), entry_location + (((grub_off_t) block_number * block_size) & (GRUB_DISK_SECTOR_SIZE - 1)), @@ -1109,8 +1235,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) * + (block_size >> GRUB_DISK_SECTOR_BITS)); grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); if (initial_position < current_position + item_size) { @@ -1142,8 +1268,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) * + (block_size >> GRUB_DISK_SECTOR_BITS), grub_le_to_cpu16 (found.header.item_location), item_size, (char *) indirect_block_ptr); if (grub_errno) @@ -1154,9 +1280,9 @@ 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])) * + (block_size >> GRUB_DISK_SECTOR_BITS)); grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block); if (current_position + block_size >= initial_position) { @@ -1255,6 +1381,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 diff --git a/fs/sfs.c b/fs/sfs.c index a453bc6..99081bd 100644 --- a/fs/sfs.c +++ b/fs/sfs.c @@ -219,8 +219,8 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block, return grub_error (GRUB_ERR_FILE_READ_ERROR, "SFS extent not found"); } -static int -grub_sfs_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { int blk = node->block; int size = 0; @@ -239,7 +239,7 @@ grub_sfs_read_block (grub_fshelp_node_t node, int fileblock) if (err) return 0; - if (fileblock < size) + if (fileblock < (unsigned int) size) return fileblock + blk; fileblock -= size; diff --git a/fs/udf.c b/fs/udf.c index 8f833eb..072e44f 100644 --- a/fs/udf.c +++ b/fs/udf.c @@ -404,8 +404,8 @@ grub_udf_read_icb (struct grub_udf_data *data, return 0; } -static int -grub_udf_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { char *ptr; int len; @@ -429,7 +429,7 @@ grub_udf_read_block (grub_fshelp_node_t node, int fileblock) len /= sizeof (struct grub_udf_short_ad); while (len > 0) { - if (fileblock < (int) U32 (ad->length)) + if (fileblock < U32 (ad->length)) return ((U32 (ad->position) & GRUB_UDF_EXT_MASK) ? 0 : (grub_udf_get_block (node->data, node->part_ref, @@ -448,7 +448,7 @@ grub_udf_read_block (grub_fshelp_node_t node, int fileblock) len /= sizeof (struct grub_udf_long_ad); while (len > 0) { - if (fileblock < (int) U32 (ad->length)) + if (fileblock < U32 (ad->length)) return ((U32 (ad->block.block_num) & GRUB_UDF_EXT_MASK) ? 0 : (grub_udf_get_block (node->data, ad->block.part_ref, diff --git a/fs/xfs.c b/fs/xfs.c index 88d22be..b8ce3e9 100644 --- a/fs/xfs.c +++ b/fs/xfs.c @@ -220,8 +220,8 @@ grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino, } -static int -grub_xfs_read_block (grub_fshelp_node_t node, int fileblock) +static grub_disk_addr_t +grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_xfs_btree_node *leaf = 0; int ex, nrec; @@ -244,7 +244,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, int fileblock) for (i = 0; i < nrec; i++) { - if ((grub_uint64_t) fileblock < grub_be_to_cpu64 (keys[i])) + if (fileblock < grub_be_to_cpu64 (keys[i])) break; } @@ -292,8 +292,8 @@ grub_xfs_read_block (grub_fshelp_node_t node, int fileblock) for (ex = 0; ex < nrec; ex++) { grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex); - int offset = GRUB_XFS_EXTENT_OFFSET (exts, ex); - int size = GRUB_XFS_EXTENT_SIZE (exts, ex); + grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex); + grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex); /* Sparse block. */ if (fileblock < offset) diff --git a/include/grub/fshelp.h b/include/grub/fshelp.h index e25dd16..32d47a3 100644 --- a/include/grub/fshelp.h +++ b/include/grub/fshelp.h @@ -34,6 +34,34 @@ enum grub_fshelp_filetype GRUB_FSHELP_SYMLINK }; +enum grub_fshelp_journal_type + { + GRUB_FSHELP_JOURNAL_TYPE_BLOCK, + GRUB_FSHELP_JOURNAL_TYPE_FILE + }; + +#define GRUB_FSHELP_JOURNAL_UNUSED_MAPPING (grub_disk_addr_t) -1 + +struct grub_fshelp_journal +{ + int type; + union + { + struct + { + grub_fshelp_node_t node; + grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, grub_disk_addr_t block); + }; + grub_disk_addr_t blkno; + }; + int first_block; + int last_block; + int start_block; + int num_mappings; + grub_disk_addr_t mapping[0]; +}; +typedef struct grub_fshelp_journal *grub_fshelp_journal_t; + /* Lookup the node PATH. The node ROOTNODE describes the root of the directory tree. The node found is returned in FOUNDNODE, which is either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to @@ -64,15 +92,18 @@ EXPORT_FUNC(grub_fshelp_find_file) (const char *path, grub_ssize_t EXPORT_FUNC(grub_fshelp_read_file) (grub_disk_t disk, grub_fshelp_node_t node, void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, - unsigned length), - int pos, grub_size_t len, char *buf, - int (*get_block) (grub_fshelp_node_t node, - int block), + unsigned offset, + unsigned length), + grub_off_t pos, grub_size_t len, char *buf, + grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, + grub_disk_addr_t block), grub_off_t filesize, int log2blocksize); unsigned int EXPORT_FUNC(grub_fshelp_log2blksize) (unsigned int blksize, unsigned int *pow); +grub_disk_addr_t +EXPORT_FUNC(grub_fshelp_map_block) (grub_fshelp_journal_t log, grub_disk_addr_t block); + #endif /* ! GRUB_FSHELP_HEADER */