/* * * (c) 2008-2009 Laurent Vivier * * 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; }