Skip to content

Commit

Permalink
fix block group descriptor handling for ext4
Browse files Browse the repository at this point in the history
  • Loading branch information
cleverca22 committed Sep 10, 2021
1 parent c126090 commit 0d9ce11
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 29 deletions.
6 changes: 3 additions & 3 deletions lib/fs/ext2/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,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)
Expand Down Expand Up @@ -173,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;
}
}
Expand Down
70 changes: 55 additions & 15 deletions lib/fs/ext2/ext2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -121,14 +125,31 @@ 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));
LTRACEF("block count %d\n", ext2->sb.s_blocks_count);
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) {
Expand All @@ -145,26 +166,41 @@ status_t ext2_mount(bdev_t *dev, fscookie **cookie) {
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 */
Expand Down Expand Up @@ -206,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);
Expand Down Expand Up @@ -241,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;
}

Expand Down
67 changes: 63 additions & 4 deletions lib/fs/ext2/ext2_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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
};

/*
Expand Down Expand Up @@ -281,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;
};

/*
Expand Down
2 changes: 2 additions & 0 deletions lib/fs/ext2/ext2_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,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 {
Expand Down
4 changes: 4 additions & 0 deletions lib/fs/ext2/ext4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
#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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/fs/ext2/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <lk/trace.h>
#include "ext2_priv.h"

#define LOCAL_TRACE 1
#define LOCAL_TRACE 0

int ext2_open_file(fscookie *cookie, const char *path, filecookie **fcookie) {
ext2_t *ext2 = (ext2_t *)cookie;
Expand Down
14 changes: 8 additions & 6 deletions lib/fs/ext2/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ static blocknum_t file_block_to_fs_block(ext2_t *ext2, struct ext2_inode *inode,
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;
//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 (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;
Expand Down

0 comments on commit 0d9ce11

Please sign in to comment.