mirror of
https://github.com/vivier/EMILE.git
synced 2024-12-22 10:29:31 +00:00
318 lines
9.8 KiB
C
318 lines
9.8 KiB
C
/*
|
|
*
|
|
* (c) 2008 Laurent Vivier <Laurent@lvivier.info>
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
#include "libext2.h"
|
|
#include "ext2_utils.h"
|
|
|
|
void ext2_get_super(device_io_t *device, struct ext2_super_block *super)
|
|
{
|
|
device->read_sector(device->data, 2, 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)
|
|
{
|
|
off_t block;
|
|
int blocksize;
|
|
|
|
if (fsblock == volume->current)
|
|
return;
|
|
|
|
volume->current = fsblock;
|
|
blocksize = volume->device->get_blocksize(volume->device->data);
|
|
block = fsblock * (EXT2_BLOCK_SIZE(volume->super) / blocksize);
|
|
volume->device->read_sector(volume->device->data, block,
|
|
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, 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) {
|
|
if (*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;
|
|
}
|