diff --git a/arch/arm/include/arch/arm.h b/arch/arm/include/arch/arm.h index 2124bcf869..ed5667baf3 100644 --- a/arch/arm/include/arch/arm.h +++ b/arch/arm/include/arch/arm.h @@ -156,6 +156,9 @@ GEN_CP15_REG_FUNCS(midr, 0, c0, c0, 0); GEN_CP15_REG_FUNCS(mpidr, 0, c0, c0, 5); GEN_CP15_REG_FUNCS(vbar, 0, c12, c0, 0); GEN_CP15_REG_FUNCS(cbar, 4, c15, c0, 0); +GEN_CP15_REG_FUNCS(nsacr, 0, c1, c1, 2); // non-secure access control register +GEN_CP15_REG_FUNCS(cntfrq, 0, c14, c0, 0); // counter frequency +GEN_CP14_REG_FUNCS(scr, 0, c1, c1, 0); // secure configuration register GEN_CP15_REG_FUNCS(ats1cpr, 0, c7, c8, 0); GEN_CP15_REG_FUNCS(ats1cpw, 0, c7, c8, 1); diff --git a/lib/fs/ext2/dir.c b/lib/fs/ext2/dir.c index b617ae50db..52f9812f95 100644 --- a/lib/fs/ext2/dir.c +++ b/lib/fs/ext2/dir.c @@ -14,12 +14,34 @@ #define LOCAL_TRACE 0 +struct dircookie { + ext2_t *fs; + struct ext2_inode dir_inode; + uint fileblock; + uint cursor; +}; + +// TODO, move to ext2.c +static void ext2_dump_inode(struct ext2_inode *inode) { +#if LOCAL_TRACE + printf("mode: %d\n", inode->i_mode); + printf("uid: %d\n", inode->i_uid); + printf("size: %d\n", inode->i_size); + printf("blocks: %d\n", inode->i_blocks); + printf("flags: 0x%x\n", inode->i_flags); +#endif +} + /* read in the dir, look for the entry */ static int ext2_dir_lookup(ext2_t *ext2, struct ext2_inode *dir_inode, const char *name, inodenum_t *inum) { uint file_blocknum; int err; uint8_t *buf; size_t namelen = strlen(name); +#if LOCAL_TRACE + printf("looking for %s in inode %p\n", name, dir_inode); + ext2_dump_inode(dir_inode); +#endif if (!S_ISDIR(dir_inode->i_mode)) return ERR_NOT_DIR; @@ -41,8 +63,8 @@ static int ext2_dir_lookup(ext2_t *ext2, struct ext2_inode *dir_inode, const cha while (pos < EXT2_BLOCK_SIZE(ext2->sb)) { ent = (struct ext2_dir_entry_2 *)&buf[pos]; - LTRACEF("ent %d:%d: inode 0x%x, reclen %d, namelen %d\n", - file_blocknum, pos, LE32(ent->inode), LE16(ent->rec_len), ent->name_len/* , ent->name*/); + LTRACEF("ent %d:%d: inode 0x%x, reclen %d, namelen %d, type: %d, name '%s'\n", + file_blocknum, pos, LE32(ent->inode), LE16(ent->rec_len), ent->name_len, ent->file_type , ent->name); /* sanity check the record length */ if (LE16(ent->rec_len) == 0) @@ -78,6 +100,7 @@ static int ext2_walk(ext2_t *ext2, char *path, struct ext2_inode *start_inode, i bool done; LTRACEF("path '%s', start_inode %p, inum %p, recurse %d\n", path, start_inode, inum, recurse); + ext2_dump_inode(start_inode); if (recurse > 4) return ERR_RECURSE_TOO_DEEP; @@ -150,7 +173,7 @@ static int ext2_walk(ext2_t *ext2, char *path, struct ext2_inode *start_inode, i } else { if (!done) { /* we aren't done and this walked over a nondir, abort */ - LTRACEF("not finished and component is nondir\n"); + LTRACEF("not finished and component is nondir: i_mode: 0x%x\n", inode.i_mode); return ERR_NOT_FOUND; } } @@ -178,3 +201,99 @@ int ext2_lookup(ext2_t *ext2, const char *_path, inodenum_t *inum) { return ext2_walk(ext2, path, &ext2->root_inode, inum, 1); } +status_t ext2_opendir(fscookie *cookie, const char *name, dircookie **dcookie) { + LTRACEF("cookie %p name '%s' dircookie %p\n", cookie, name, dcookie); + ext2_t *ext2 = (ext2_t *)cookie; + int err; + inodenum_t inum; + //uint file_blocknum; + if (name[0] == 0) { + inum = EXT2_ROOT_INO; + } else { + err = ext2_lookup(ext2, name, &inum); + if (err < 0) { + LTRACEF("err = %d\n", err); + return err; + } + } + LTRACEF("inum = %d\n", inum); + + // allocate a dir cookie, point it at the first file, and stuff it in the dircookie jar + dircookie *dir = calloc(1, sizeof(*dir)); + if (!dir) return ERR_NO_MEMORY; + dir->fs = ext2; + /* load the next inode */ + err = ext2_load_inode(ext2, inum, &dir->dir_inode); + if (err < 0) { + free(dir); + return err; + } + + if (!S_ISDIR(dir->dir_inode.i_mode)) { + free(dir); + return ERR_NOT_DIR; + } + + *dcookie = dir; + + return 0; +} +status_t ext2_readdir(dircookie *cookie, struct dirent *ent_out) { + int err; + uint8_t *buf; + struct ext2_dir_entry_2 *ent; + uint pos; + + if (!ent_out) + return ERR_INVALID_ARGS; + + LTRACEF("dircookie: %p, direnv: %p, %d:%d\n", cookie, ent_out, cookie->fileblock, cookie->cursor); + + if (cookie->fileblock >= cookie->dir_inode.i_blocks) { + return ERR_NOT_FOUND; + } + + // TODO, write a function that will get a reference to the block cache + // then copy and byte-swap the ext2_dir_entry_2 in one action + buf = malloc(EXT2_BLOCK_SIZE(cookie->fs->sb)); + + err = ext2_read_inode(cookie->fs, &cookie->dir_inode, buf, cookie->fileblock * EXT2_BLOCK_SIZE(cookie->fs->sb), EXT2_BLOCK_SIZE(cookie->fs->sb)); + if (err <= 0) { + free(buf); + return -1; + } + + pos = cookie->cursor; + ent = (struct ext2_dir_entry_2 *)&buf[pos]; + + LTRACEF("ent %d:%d: inode 0x%x, reclen %d, namelen %d\n", cookie->fileblock, pos, LE32(ent->inode), LE16(ent->rec_len), ent->name_len/* , ent->name*/); + + /* sanity check the record length */ + if (LE16(ent->rec_len) == 0) { + free(buf); + return -1; + } + + pos += ROUNDUP(LE16(ent->rec_len), 4); + if (ent->inode == 0) { // deleted file + // TODO, assumes end of dir listing + // it should continue to the next record, potentially in the next block + free(buf); + return ERR_NOT_FOUND; + } + if (pos >= EXT2_BLOCK_SIZE(cookie->fs->sb)) { + cookie->fileblock++; + cookie->cursor = 0; + } else { + cookie->cursor = pos; + } + int size = MIN(ent->name_len, FS_MAX_FILE_LEN-1); + memcpy(ent_out->name, ent->name, size); + ent_out->name[size] = 0; + free(buf); + return NO_ERROR; +} +status_t ext2_closedir(dircookie *cookie) { + free(cookie); + return NO_ERROR; +} diff --git a/lib/fs/ext2/ext2.c b/lib/fs/ext2/ext2.c index 08daaa61c9..0edca2a235 100644 --- a/lib/fs/ext2/ext2.c +++ b/lib/fs/ext2/ext2.c @@ -57,6 +57,9 @@ static void endian_swap_superblock(struct ext2_super_block *sb) { LE32SWAP(sb->s_last_orphan); LE32SWAP(sb->s_default_mount_opts); LE32SWAP(sb->s_first_meta_bg); + LE32SWAP(sb->s_desc_size); + // TODO not all fields past s_first_meta_bg are byte-swapped + LE32SWAP(sb->s_log_groups_per_flex); } static void endian_swap_inode(struct ext2_inode *inode) { @@ -94,6 +97,7 @@ static void endian_swap_group_desc(struct ext2_group_desc *gd) { status_t ext2_mount(bdev_t *dev, fscookie **cookie) { int err; + int i; LTRACEF("dev %p\n", dev); @@ -121,7 +125,21 @@ status_t ext2_mount(bdev_t *dev, fscookie **cookie) { /* print some info */ LTRACEF("rev level %d\n", ext2->sb.s_rev_level); LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat); + if (LOCAL_TRACE) { + if (ext2->sb.s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) puts(" 0x001 sparse super"); + if (ext2->sb.s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) puts(" 0x002 large file"); + if (ext2->sb.s_feature_ro_compat & RO_COMPAT_HUGE_FILE) puts(" 0x008 huge file"); + if (ext2->sb.s_feature_ro_compat & RO_COMPAT_DIR_NLINK) puts(" 0x020 directory nlink"); + if (ext2->sb.s_feature_ro_compat & RO_COMPAT_EXTRA_ISIZE) puts(" 0x040 extra isize"); + if (ext2->sb.s_feature_ro_compat & RO_COMPAT_METADATA_CSUM) puts(" 0x400 metadata checksum"); + } LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat); + if (LOCAL_TRACE) { + if (ext2->sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) puts(" 0x002 ext4_dir_entry_2 in use"); + if (ext2->sb.s_feature_incompat & INCOMPAT_EXTENTS) puts(" 0x040 inodes may be using extents "); + if (ext2->sb.s_feature_incompat & INCOMPAT_64BIT) puts(" 0x080 allow an FS of up to 2^64 blocks "); + if (ext2->sb.s_feature_incompat & INCOMPAT_FLEX_BG) puts(" 0x200 flexible block groups "); + } LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat); LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb)); LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb)); @@ -129,6 +147,9 @@ status_t ext2_mount(bdev_t *dev, fscookie **cookie) { LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group); LTRACEF("group count %d\n", ext2->s_group_count); LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group); + if (ext2->sb.s_feature_incompat & INCOMPAT_64BIT) { + LTRACEF("block group size: %d\n", ext2->sb.s_desc_size); + } /* we only support dynamic revs */ if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) { @@ -137,31 +158,49 @@ status_t ext2_mount(bdev_t *dev, fscookie **cookie) { } /* make sure it doesn't have any ro features we don't support */ - if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { + if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER + |EXT2_FEATURE_RO_COMPAT_LARGE_FILE|RO_COMPAT_HUGE_FILE + |RO_COMPAT_DIR_NLINK|RO_COMPAT_EXTRA_ISIZE|RO_COMPAT_METADATA_CSUM)) { + LTRACEF("s_feature_ro_compat not compatible, 0x%x\n", ext2->sb.s_feature_ro_compat); err = -3; return err; } + ext2->has_new_directory_entries = (ext2->sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) != 0; + + if (ext2->sb.s_feature_incompat & INCOMPAT_FLEX_BG) { + printf("s_log_groups_per_flex: %d\n", ext2->sb.s_log_groups_per_flex); + } + + // ext2/3 always have 32 byte block group descriptors + // ext4 WITHOUT INCOMPAT_64BIT also has 32 byte descriptors + // ext4 with INCOMPAT_64BIT has ${s_desc_size} byte descriptors + uint32_t block_group_size; + if (ext2->sb.s_feature_incompat & INCOMPAT_64BIT) { + block_group_size = ext2->sb.s_desc_size; + } else { + block_group_size = 32; + } + /* read in all the group descriptors */ ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count); - err = bio_read(ext2->dev, (void *)ext2->gd, - (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048, - sizeof(struct ext2_group_desc) * ext2->s_group_count); - if (err < 0) { - err = -4; - return err; + for (i=0; i < ext2->s_group_count; i++) { + uint32_t block_groups_start = (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048; + uint32_t offset = block_groups_start + (i * block_group_size); + err = bio_read(ext2->dev, (void *)&ext2->gd[i], offset, + sizeof(struct ext2_group_desc)); + if (err < 0) { + err = -4; + return err; + } + endian_swap_group_desc(&ext2->gd[i]); } - int i; for (i=0; i < ext2->s_group_count; i++) { - endian_swap_group_desc(&ext2->gd[i]); - LTRACEF("group %d:\n", i); - LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap); - LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap); - LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table); - LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count); - LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count); - LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count); + LTRACEF(" Group %2d: block bitmap at %d, inode bitmap at %d, inode table at %d\n", i, ext2->gd[i].bg_block_bitmap, + ext2->gd[i].bg_inode_bitmap, ext2->gd[i].bg_inode_table); + LTRACEF(" %d free blocks, %d free inodes, %d used directories\n", + ext2->gd[i].bg_free_blocks_count, ext2->gd[i].bg_free_inodes_count, ext2->gd[i].bg_used_dirs_count); } /* initialize the block cache */ @@ -203,6 +242,7 @@ static void get_inode_addr(ext2_t *ext2, inodenum_t num, blocknum_t *block, size // calculate the start of the inode table for the group it's in *block = ext2->gd[group].bg_inode_table; + LTRACEF("inode %d is in block group %d, the inode table for that group begins at %d\n", num, group, *block); // add the offset of the inode within the group size_t offset = (num % EXT2_INODES_PER_GROUP(ext2->sb)) * EXT2_INODE_SIZE(ext2->sb); @@ -238,6 +278,9 @@ int ext2_load_inode(ext2_t *ext2, inodenum_t num, struct ext2_inode *inode) { LTRACEF("read inode: mode 0x%x, size %d\n", inode->i_mode, inode->i_size); + LTRACEF("loaded inode %d from disk block %d offset %d, i_uid:%d i_gid:%d i_flags:0x%x i_blocks:%d i_size:%d\n", + num, bnum, block_offset, inode->i_uid, inode->i_gid, inode->i_flags, inode->i_blocks, inode->i_size); + return 0; } @@ -248,6 +291,10 @@ static const struct fs_api ext2_api = { .stat = ext2_stat_file, .read = ext2_read_file, .close = ext2_close_file, + + .opendir = ext2_opendir, + .readdir = ext2_readdir, + .closedir = ext2_closedir, }; STATIC_FS_IMPL(ext2, &ext2_api); diff --git a/lib/fs/ext2/ext2_fs.h b/lib/fs/ext2/ext2_fs.h index c06f456557..a1ac5fec69 100644 --- a/lib/fs/ext2/ext2_fs.h +++ b/lib/fs/ext2/ext2_fs.h @@ -69,6 +69,8 @@ /* * Structure of a blocks group descriptor + * for ext4 compat, each descriptor is now 64 bytes in size + * when mounting ext2, bio_read will only populate the lower 32 bytes of each descriptor */ struct ext2_group_desc { uint32_t bg_block_bitmap; /* Blocks bitmap block */ @@ -79,6 +81,7 @@ struct ext2_group_desc { uint16_t bg_used_dirs_count; /* Directories count */ uint16_t bg_pad; uint32_t bg_reserved[3]; + uint8_t bg_reserved2[32]; // TODO, fill in the other fields }; /* @@ -99,6 +102,7 @@ struct ext2_group_desc { /* * Structure of an inode on the disk + * endian_swap_inode and ext2_load_inode deal with byte-order */ struct ext2_inode { uint16_t i_mode; /* File mode */ @@ -280,12 +284,68 @@ struct ext2_super_block { uint32_t s_journal_dev; /* device number of journal file */ uint32_t s_last_orphan; /* start of list of inodes to delete */ uint32_t s_hash_seed[4]; /* HTREE hash seed */ - uint8_t s_def_hash_version; /* Default hash version to use */ - uint8_t s_reserved_char_pad; - uint16_t s_reserved_word_pad; + uint8_t s_def_hash_version; /* Default hash version to use */ + uint8_t s_jnl_backup_type; + uint16_t s_desc_size; // size of group descriptors, if INCOMPAT_64BIT is set uint32_t s_default_mount_opts; uint32_t s_first_meta_bg; /* First metablock block group */ - uint32_t s_reserved[190]; /* Padding to the end of the block */ + // the following are added in at least ext4 + uint32_t s_mkfs_time; + uint32_t s_jnl_blocks[17]; + // these upper 32bits are only used if EXT4_FEATURE_COMPAT_64BIT is set + uint32_t s_blocks_count_hi; + uint32_t s_r_blocks_count_hi; + uint32_t s_free_blocks_count_hi; + uint16_t s_min_extra_isize; + uint16_t s_want_extra_isize; + uint32_t s_flags; + uint16_t s_raid_stride; + uint16_t s_mmp_interval; + uint64_t s_mmp_block; + uint32_t s_raid_stripe_width; + uint8_t s_log_groups_per_flex; + uint8_t s_checksum_type; + uint16_t s_reserved_pad; + uint64_t s_kbytes_written; + uint32_t s_snapshot_inum; + uint32_t s_snapshot_id; + uint64_t s_snapshot_r_blocks_count; + uint32_t s_snapshot_list; + uint32_t s_error_count; + + uint32_t s_first_error_time; + uint32_t s_first_error_ino; + uint64_t s_first_error_block; + uint8_t s_first_error_func[32]; + uint32_t s_first_error_line; + + uint32_t s_last_error_time; + uint32_t s_last_error_ino; + uint32_t s_last_error_line; + uint64_t s_last_error_block; + uint8_t s_last_error_func[32]; + + uint8_t s_mount_opts[64]; + uint32_t s_usr_quota_inum; + uint32_t s_grp_quota_inum; + uint32_t s_overhead_blocks; + uint32_t s_backup_bgs[2]; + uint8_t s_encrypt_algos[4]; + uint8_t s_encrypt_pw_salt[16]; + uint32_t s_lpf_ino; + uint32_t s_prj_quota_inum; + uint32_t s_checksum_seed; + uint8_t s_wtime_hi; + uint8_t s_mtime_hi; + uint8_t s_mkfs_time_hi; + uint8_t s_lastcheck_hi; + uint8_t s_first_error_time_hi; + uint8_t s_last_error_time_hi; + uint8_t s_pad[2]; + uint16_t s_encoding; + uint16_t s_encoding_flags; + uint32_t s_reserved[95]; /* Padding to the end of the block */ + uint32_t s_checksum; }; /* diff --git a/lib/fs/ext2/ext2_priv.h b/lib/fs/ext2/ext2_priv.h index 0f8ffe781f..174ea6dc03 100644 --- a/lib/fs/ext2/ext2_priv.h +++ b/lib/fs/ext2/ext2_priv.h @@ -11,6 +11,7 @@ #include #include #include "ext2_fs.h" +#include "ext4_fs.h" typedef uint32_t blocknum_t; typedef uint32_t inodenum_t; @@ -24,6 +25,8 @@ typedef struct { int s_group_count; struct ext2_group_desc *gd; struct ext2_inode root_inode; + + bool has_new_directory_entries; } ext2_t; struct cache_block { @@ -60,6 +63,10 @@ ssize_t ext2_read_file(filecookie *fcookie, void *buf, off_t offset, size_t len) status_t ext2_close_file(filecookie *fcookie); status_t ext2_stat_file(filecookie *fcookie, struct file_stat *); +status_t ext2_opendir(fscookie *, const char *, dircookie **); +status_t ext2_readdir(dircookie *, struct dirent *); +status_t ext2_closedir(dircookie *); + /* mode stuff */ #define S_IFMT 0170000 #define S_IFIFO 0010000 diff --git a/lib/fs/ext2/ext4_fs.h b/lib/fs/ext2/ext4_fs.h new file mode 100644 index 0000000000..3d616170e4 --- /dev/null +++ b/lib/fs/ext2/ext4_fs.h @@ -0,0 +1,43 @@ +/* + * linux/include/linux/ext4_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ +#pragma once + +#define RO_COMPAT_HUGE_FILE 0x008 +#define RO_COMPAT_DIR_NLINK 0x020 +#define RO_COMPAT_EXTRA_ISIZE 0x040 +#define RO_COMPAT_METADATA_CSUM 0x400 + +#define INCOMPAT_64BIT 0x080 +#define INCOMPAT_EXTENTS 0x040 +#define INCOMPAT_FLEX_BG 0x200 + +// all fields in little-endian, LE16 and LE32 must be used +// TODO? add a variant of endian_swap_inode +typedef struct { + uint16_t eh_magic; + uint16_t eh_entries; + uint16_t eh_max; + uint16_t eh_depth; + uint32_t eh_generation; +} ext4_extent_header; + +// all fields in little-endian, LE16 and LE32 must be used +// TODO? add a variant of endian_swap_inode +typedef struct { + uint32_t ee_block; + uint16_t ee_len; + uint16_t ee_start_hi; + uint32_t ee_start_lo; +} ext4_extent; diff --git a/lib/fs/ext2/io.c b/lib/fs/ext2/io.c index 198ace1249..5e87b102a1 100644 --- a/lib/fs/ext2/io.c +++ b/lib/fs/ext2/io.c @@ -128,30 +128,65 @@ static blocknum_t file_block_to_fs_block(ext2_t *ext2, struct ext2_inode *inode, blocknum_t block; LTRACEF("inode %p, fileblock %u\n", inode, fileblock); + if (inode->i_flags & 0x80000) { // inode is stored using extents + ext4_extent_header *eh = (ext4_extent_header*)&inode->i_block; + if (LOCAL_TRACE) { + printf("its an extent based object\n"); + printf("eh_magic: 0x%x\n", LE16(eh->eh_magic)); + printf("eh_entries: %d\n", LE16(eh->eh_entries)); + printf("eh_max: %d\n", LE16(eh->eh_max)); + printf("eh_depth: %d\n", LE16(eh->eh_depth)); + printf("eh_generation: %d\n", LE32(eh->eh_generation)); + } + if (LE16(eh->eh_magic) != 0xf30a) { + puts("extent header magic invalid"); + return 0; + } + block = 0; // TODO + if (LE16(eh->eh_depth) == 0) { + ext4_extent *extents = (ext4_extent*)( ((ext4_extent*)&inode->i_block) + 1); + for (int i=0; i < LE16(eh->eh_entries); i++) { +#if 0 + printf("extent %d\n", i); + printf(" ee_block: %d\n", LE32(extents[i].ee_block)); + printf(" ee_len: %d\n", LE16(extents[i].ee_len)); + printf(" ee_start_hi: %d\n", LE16(extents[i].ee_start_hi)); + printf(" ee_start_lo: %d\n", LE32(extents[i].ee_start_lo)); +#endif + if ((fileblock >= LE32(extents[i].ee_block)) && (fileblock < (LE32(extents[i].ee_block) + LE16(extents[i].ee_len)))) { + if (LE16(extents[i].ee_start_hi) != 0) { + puts("unsupported >32bit blocknr"); + return 0; + } + block = LE32(extents[i].ee_start_lo) + (fileblock - LE32(extents[i].ee_block)); + } + } + } + } else { + uint32_t pos[4]; + uint32_t level = 0; + ext2_calculate_block_pointer_pos(ext2, fileblock, &level, pos); - uint32_t pos[4]; - uint32_t level = 0; - ext2_calculate_block_pointer_pos(ext2, fileblock, &level, pos); - - LTRACEF("level %d, pos 0x%x 0x%x 0x%x 0x%x\n", level, pos[0], pos[1], pos[2], pos[3]); + LTRACEF("level %d, pos 0x%x 0x%x 0x%x 0x%x\n", level, pos[0], pos[1], pos[2], pos[3]); - if (level == 0) { - /* direct block, just return it directly */ - block = LE32(inode->i_block[fileblock]); - } else { - /* at least one level of indirection, get a pointer to the final indirect block table and dereference it */ - blocknum_t *ind_table; - blocknum_t phys_block; - err = ext2_get_indirect_block_pointer_cache_block(ext2, inode, &ind_table, level, pos, &phys_block); - if (err < 0) - return 0; - - /* dereference the final entry in the final table */ - block = LE32(ind_table[pos[level]]); - LTRACEF("block %u, indirect_block %u\n", block, phys_block); - - /* release the ref on the cache block */ - ext2_put_block(ext2, phys_block); + if (level == 0) { + /* direct block, just return it directly */ + block = LE32(inode->i_block[fileblock]); + } else { + /* at least one level of indirection, get a pointer to the final indirect block table and dereference it */ + blocknum_t *ind_table; + blocknum_t phys_block; + err = ext2_get_indirect_block_pointer_cache_block(ext2, inode, &ind_table, level, pos, &phys_block); + if (err < 0) + return 0; + + /* dereference the final entry in the final table */ + block = LE32(ind_table[pos[level]]); + LTRACEF("block %u, indirect_block %u\n", block, phys_block); + + /* release the ref on the cache block */ + ext2_put_block(ext2, phys_block); + } } LTRACEF("returning %u\n", block); @@ -166,6 +201,10 @@ ssize_t ext2_read_inode(ext2_t *ext2, struct ext2_inode *inode, void *_buf, off_ /* calculate the file size */ off_t file_size = ext2_file_len(ext2, inode); + if (inode->i_flags & ~(0x80000)) { // the extent flag + printf("unsupported flags on inode: 0x%x\n", inode->i_flags); + return -1; + } LTRACEF("inode %p, offset %lld, len %zd, file_size %lld\n", inode, offset, len, file_size);