diff options
Diffstat (limited to 'qemu/roms/openbios/fs/ext2/ext2_utils.c')
-rw-r--r-- | qemu/roms/openbios/fs/ext2/ext2_utils.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/qemu/roms/openbios/fs/ext2/ext2_utils.c b/qemu/roms/openbios/fs/ext2/ext2_utils.c new file mode 100644 index 000000000..64563c826 --- /dev/null +++ b/qemu/roms/openbios/fs/ext2/ext2_utils.c @@ -0,0 +1,332 @@ +/* + * + * (c) 2008-2009 Laurent Vivier <Laurent@lvivier.info> + * + * This file has been copied from EMILE, http://emile.sf.net + * + */ + +#include "libext2.h" +#include "ext2_utils.h" +#include "libopenbios/bindings.h" +#include "libc/diskio.h" +#include "libc/byteorder.h" + +int ext2_probe(int fd, long long offset) +{ + struct ext2_super_block *super; + + super = (struct ext2_super_block*)malloc(sizeof(struct ext2_super_block)); + seek_io(fd, 2 * 512 + offset); + read_io(fd, super, sizeof (*super)); + + if (__le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) { + free(super); + return 0; + } + + free(super); + return -1; +} + +void ext2_get_super(int fd, struct ext2_super_block *super) +{ + seek_io(fd, 2 * 512); + read_io(fd, super, sizeof (*super)); + + super->s_inodes_count = __le32_to_cpu(super->s_inodes_count); + super->s_blocks_count = __le32_to_cpu(super->s_blocks_count); + super->s_r_blocks_count = __le32_to_cpu(super->s_r_blocks_count); + super->s_free_blocks_count = __le32_to_cpu(super->s_free_blocks_count); + super->s_free_inodes_count = __le32_to_cpu(super->s_free_inodes_count); + super->s_first_data_block = __le32_to_cpu(super->s_first_data_block); + super->s_log_block_size = __le32_to_cpu(super->s_log_block_size); + super->s_log_frag_size = __le32_to_cpu(super->s_log_frag_size); + super->s_blocks_per_group = __le32_to_cpu(super->s_blocks_per_group); + super->s_frags_per_group = __le32_to_cpu(super->s_frags_per_group); + super->s_inodes_per_group = __le32_to_cpu(super->s_inodes_per_group); + super->s_mtime = __le32_to_cpu(super->s_mtime); + super->s_wtime = __le32_to_cpu(super->s_wtime); + super->s_mnt_count = __le16_to_cpu(super->s_mnt_count); + super->s_max_mnt_count = __le16_to_cpu(super->s_max_mnt_count); + super->s_magic = __le16_to_cpu(super->s_magic); + super->s_state = __le16_to_cpu(super->s_state); + super->s_errors = __le16_to_cpu(super->s_errors); + super->s_minor_rev_level = __le16_to_cpu(super->s_minor_rev_level); + super->s_lastcheck = __le32_to_cpu(super->s_lastcheck); + super->s_checkinterval = __le32_to_cpu(super->s_checkinterval); + super->s_creator_os = __le32_to_cpu(super->s_creator_os); + super->s_rev_level = __le32_to_cpu(super->s_rev_level); + super->s_def_resuid = __le16_to_cpu(super->s_def_resuid); + super->s_def_resgid = __le16_to_cpu(super->s_def_resgid); + super->s_first_ino = __le32_to_cpu(super->s_first_ino); + super->s_inode_size = __le16_to_cpu(super->s_inode_size); + super->s_block_group_nr = __le16_to_cpu(super->s_block_group_nr); + super->s_feature_compat = __le32_to_cpu(super->s_feature_compat); + super->s_feature_incompat = __le32_to_cpu(super->s_feature_incompat); + super->s_feature_ro_compat = __le32_to_cpu(super->s_feature_ro_compat); + super->s_algorithm_usage_bitmap = + __le32_to_cpu(super->s_algorithm_usage_bitmap); + super->s_journal_inum = __le32_to_cpu(super->s_journal_inum); + super->s_journal_dev = __le32_to_cpu(super->s_journal_dev); + super->s_last_orphan = __le32_to_cpu(super->s_last_orphan); + super->s_hash_seed[0] = __le32_to_cpu(super->s_hash_seed[0]); + super->s_hash_seed[1] = __le32_to_cpu(super->s_hash_seed[1]); + super->s_hash_seed[2] = __le32_to_cpu(super->s_hash_seed[2]); + super->s_hash_seed[3] = __le32_to_cpu(super->s_hash_seed[3]); + super->s_default_mount_opts = + __le32_to_cpu(super->s_default_mount_opts); + super->s_first_meta_bg = __le32_to_cpu(super->s_first_meta_bg); +} + +void ext2_read_block(ext2_VOLUME* volume, unsigned int fsblock) +{ + long long offset; + + if (fsblock == volume->current) + return; + + volume->current = fsblock; + offset = fsblock * EXT2_BLOCK_SIZE(volume->super); + + seek_io(volume->fd, offset); + read_io(volume->fd, volume->buffer, EXT2_BLOCK_SIZE(volume->super)); +} + +void ext2_get_group_desc(ext2_VOLUME* volume, + int group_id, struct ext2_group_desc *gdp) +{ + unsigned int block, offset; + struct ext2_group_desc *le_gdp; + + block = 1 + volume->super->s_first_data_block; + block += group_id / EXT2_DESC_PER_BLOCK(volume->super); + ext2_read_block(volume, block); + + offset = group_id % EXT2_DESC_PER_BLOCK(volume->super); + offset *= sizeof(*gdp); + + le_gdp = (struct ext2_group_desc *)(volume->buffer + offset); + + gdp->bg_block_bitmap = __le32_to_cpu(le_gdp->bg_block_bitmap); + gdp->bg_inode_bitmap = __le32_to_cpu(le_gdp->bg_inode_bitmap); + gdp->bg_inode_table = __le32_to_cpu(le_gdp->bg_inode_table); + gdp->bg_free_blocks_count = __le16_to_cpu(le_gdp->bg_free_blocks_count); + gdp->bg_free_inodes_count = __le16_to_cpu(le_gdp->bg_free_inodes_count); + gdp->bg_used_dirs_count = __le16_to_cpu(le_gdp->bg_used_dirs_count); +} + +int ext2_get_inode(ext2_VOLUME* volume, + unsigned int ino, struct ext2_inode *inode) +{ + struct ext2_group_desc desc; + unsigned int block; + unsigned int group_id; + unsigned int offset; + struct ext2_inode *le_inode; + int i; + + ino--; + + group_id = ino / EXT2_INODES_PER_GROUP(volume->super); + ext2_get_group_desc(volume, group_id, &desc); + + ino %= EXT2_INODES_PER_GROUP(volume->super); + + block = desc.bg_inode_table; + block += ino / (EXT2_BLOCK_SIZE(volume->super) / + EXT2_INODE_SIZE(volume->super)); + ext2_read_block(volume, block); + + offset = ino % (EXT2_BLOCK_SIZE(volume->super) / + EXT2_INODE_SIZE(volume->super)); + offset *= EXT2_INODE_SIZE(volume->super); + + le_inode = (struct ext2_inode *)(volume->buffer + offset); + + inode->i_mode = __le16_to_cpu(le_inode->i_mode); + inode->i_uid = __le16_to_cpu(le_inode->i_uid); + inode->i_size = __le32_to_cpu(le_inode->i_size); + inode->i_atime = __le32_to_cpu(le_inode->i_atime); + inode->i_ctime = __le32_to_cpu(le_inode->i_ctime); + inode->i_mtime = __le32_to_cpu(le_inode->i_mtime); + inode->i_dtime = __le32_to_cpu(le_inode->i_dtime); + inode->i_gid = __le16_to_cpu(le_inode->i_gid); + inode->i_links_count = __le16_to_cpu(le_inode->i_links_count); + inode->i_blocks = __le32_to_cpu(le_inode->i_blocks); + inode->i_flags = __le32_to_cpu(le_inode->i_flags); + if (S_ISLNK(inode->i_mode)) { + memcpy(inode->i_block, le_inode->i_block, EXT2_N_BLOCKS * 4); + } else { + for (i = 0; i < EXT2_N_BLOCKS; i++) + inode->i_block[i] = __le32_to_cpu(le_inode->i_block[i]); + } + inode->i_generation = __le32_to_cpu(le_inode->i_generation); + inode->i_file_acl = __le32_to_cpu(le_inode->i_file_acl); + inode->i_dir_acl = __le32_to_cpu(le_inode->i_dir_acl); + inode->i_faddr = __le32_to_cpu(le_inode->i_faddr); + inode->osd2.linux2.l_i_frag = le_inode->osd2.linux2.l_i_frag; + inode->osd2.linux2.l_i_fsize = le_inode->osd2.linux2.l_i_fsize; + inode->osd2.linux2.l_i_uid_high = + __le16_to_cpu(le_inode->osd2.linux2.l_i_uid_high); + inode->osd2.linux2.l_i_gid_high = + __le16_to_cpu(le_inode->osd2.linux2.l_i_gid_high); + return 0; +} + +unsigned int ext2_get_block_addr(ext2_VOLUME* volume, struct ext2_inode *inode, + unsigned int logical) +{ + unsigned int physical; + unsigned int addr_per_block; + + /* direct */ + + if (logical < EXT2_NDIR_BLOCKS) { + physical = inode->i_block[logical]; + return physical; + } + + /* indirect */ + + logical -= EXT2_NDIR_BLOCKS; + + addr_per_block = EXT2_ADDR_PER_BLOCK (volume->super); + if (logical < addr_per_block) { + ext2_read_block(volume, inode->i_block[EXT2_IND_BLOCK]); + physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical]); + return physical; + } + + /* double indirect */ + + logical -= addr_per_block; + + if (logical < addr_per_block * addr_per_block) { + ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]); + physical = __le32_to_cpu(((unsigned int *)volume->buffer) + [logical / addr_per_block]); + ext2_read_block(volume, physical); + physical = __le32_to_cpu(((unsigned int *)volume->buffer) + [logical % addr_per_block]); + return physical; + } + + /* triple indirect */ + + logical -= addr_per_block * addr_per_block; + ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]); + physical = __le32_to_cpu(((unsigned int *)volume->buffer) + [logical / (addr_per_block * addr_per_block)]); + ext2_read_block(volume, physical); + logical = logical % (addr_per_block * addr_per_block); + physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical / addr_per_block]); + ext2_read_block(volume, physical); + physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical % addr_per_block]); + return physical; +} + +int ext2_read_data(ext2_VOLUME* volume, struct ext2_inode *inode, + off_t offset, char *buffer, size_t length) +{ + unsigned int logical, physical; + int blocksize = EXT2_BLOCK_SIZE(volume->super); + int shift; + size_t read; + + if (offset >= inode->i_size) + return -1; + + if (offset + length >= inode->i_size) + length = inode->i_size - offset; + + read = 0; + logical = offset / blocksize; + shift = offset % blocksize; + + if (shift) { + physical = ext2_get_block_addr(volume, inode, logical); + ext2_read_block(volume, physical); + + if (length < blocksize - shift) { + memcpy(buffer, volume->buffer + shift, length); + return length; + } + read += blocksize - shift; + memcpy(buffer, volume->buffer + shift, read); + + buffer += read; + length -= read; + logical++; + } + + while (length) { + physical = ext2_get_block_addr(volume, inode, logical); + ext2_read_block(volume, physical); + + if (length < blocksize) { + memcpy(buffer, volume->buffer, length); + read += length; + return read; + } + memcpy(buffer, volume->buffer, blocksize); + + buffer += blocksize; + length -= blocksize; + read += blocksize; + logical++; + } + + return read; +} + +off_t ext2_dir_entry(ext2_VOLUME *volume, struct ext2_inode *inode, + off_t index, struct ext2_dir_entry_2 *entry) +{ + int ret; + + ret = ext2_read_data(volume, inode, index, + (char*)entry, sizeof(*entry)); + if (ret == -1) + return -1; + + entry->inode = __le32_to_cpu(entry->inode); + entry->rec_len = __le16_to_cpu(entry->rec_len); + return index + entry->rec_len; +} + +unsigned int ext2_seek_name(ext2_VOLUME *volume, const char *name) +{ + struct ext2_inode inode; + int ret; + unsigned int ino; + off_t index; + struct ext2_dir_entry_2 entry; + + ino = EXT2_ROOT_INO; + while(1) { + while (*name == '\\') + name++; + if (!*name) + break; + ret = ext2_get_inode(volume, ino, &inode); + if (ret == -1) + return 0; + index = 0; + while (1) { + index = ext2_dir_entry(volume, &inode, index, &entry); + if (index == -1) + return 0; + ret = strncmp(name, entry.name, entry.name_len); + if (ret == 0 && + (name[entry.name_len] == 0 || + name[entry.name_len] == '\\')) { + ino = entry.inode; + break; + } + } + name += entry.name_len; + } + + return ino; +} |