ext2/3: reading a file

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
bpaterni
Posts: 12
Joined: Sat May 30, 2009 4:38 pm

ext2/3: reading a file

Post by bpaterni »

Hi, I'm trying to develop my own second-stage bootloader that will load my kernel from an ext3 partition (currently on a disk image) and in the process I've made a test program to see how to actually go about doing this. Right now, it just reads superblocks/group descriptors/inodes/directory entries and displays their content. My problem is when I try and loop through the directory information. I can never seem to find any reference to an actual file/data.

For example, the file I'm scanning for has the path, "/test0/child0/file0". I'm able to successfully find inodes/direntries containing information referring to the directories "test0" and "child0", but I do not find any dir_entry in the data blocks referred by child0's inode, in fact, child0's inode is marked valid in the group bitmap, but contains nothing useful as a hexdump of the area on the disk image where that inode should be contains all zeroes.

output of test program:

Code: Select all

Inode:
	mode: 0x41ED
	Block[0]:	835
Dirent: test0
	inode: 155649
	rec_len: 40
	name_len: 5
	file_type: 2

Inode:
	mode: 0x41ED
	Block[0]:	632832
Dirent: child0
	inode: 155650
	rec_len: 4072
	name_len: 6
	file_type: 2

Error: NULL Inode.
ext2_get_inode function:

Code: Select all

ext2_inode *
ext2_get_inode(u32 inode_num)
{
        ext2_inode *r_ino;
        struct ext2_group_desc *gd;
        size_t r;
        size_t ino_sz;
        long inode_disk_addr;
        u32 *i_bitmap;
        u32 inode_in_group;
        u32 group;
        int bm_index, bm_offset;

        ino_sz = sizeof(ext2_inode);
        group = (inode_num - 1) / EXT2_INODES_PER_GROUP(sb);
        gd = ext2_get_group_desc(group);

        r_ino = (ext2_inode *)calloc(1, ino_sz);
        i_bitmap = ext2_get_data_block(gd->bg_inode_bitmap);
        if (i_bitmap == NULL)
        {
                printf("Error: finding bitmap.\n");
                goto out;
        }
        inode_in_group = (inode_num-1) % EXT2_INODES_PER_GROUP(sb);
        bm_index = EXT2_GRP_BM_INDEX(inode_in_group);
        bm_offset = EXT2_GRP_BM_OFFSET(inode_in_group);

        if (!(i_bitmap[bm_index] & (1 << bm_offset)))
        {
                printf("Error: Inode not valid, %u\n", inode_num);
                goto out;
        }

        inode_disk_addr = PART_START;
        inode_disk_addr += (long)gd->bg_inode_table * EXT2_BLOCK_SIZE(sb);
        inode_disk_addr += (long)(ino_sz * inode_in_group);

        fseek(f, inode_disk_addr, SEEK_SET);
        r = fread(r_ino, ino_sz, 1, f);
        if (!r)
        {
                printf("Error: Reading inode, %u\n", inode_num);
                goto out;
        }
        return r_ino;
out:
        return NULL;
}
I'm not sure what I'm misunderstanding about the ext2/3 filesystem. Hopefully someone here will know what's going on. :)
KarimAllah
Posts: 3
Joined: Mon Nov 30, 2009 4:19 am

Re: ext2/3: reading a file

Post by KarimAllah »

Code: Select all

        i_bitmap = ext2_get_data_block(gd->bg_inode_bitmap);
        if (i_bitmap == NULL)
        {
                printf("Error: finding bitmap.\n");
                goto out;
        }
I think the problem is here.

as i remember , "gd->bg_inode_bitmap" should represent - as the reference says - a 32bit block id of the first block of the "inode bitmap" for the group represented ( In another words , this member of the structure should refer to the first block that represent your inode bitmap ) and obviously "ext2_get_data_block" is supposed to take this value and return the data block , which is supposed to be a block of "block_size" bytes not a 32-bit value.

So you really need to check your "ext2_get_data_block" and find out what it returns and then change the assignment above to make more sense to something like that

Code: Select all

foo *db; # foo is the data type that "ext2_get_data_block" returns.
db = ext2_get_data_block(bg_inode_bitmap);
and of course refactor the rest of the code to reflect the changes here ..

and you should consider rewriting the whole function because there are some redundancy in it.
best wishes :)
bpaterni
Posts: 12
Joined: Sat May 30, 2009 4:38 pm

Re: ext2/3: reading a file

Post by bpaterni »

Actually, my ext2_get_data_block returns a void pointer to the data retrieved from the block position on disk. Perhaps, I should have added an explicit cast, but for the most part I think my usage is correct.

As for the redundancy, I wasn't sure exactly what you were referring to, but I've made an effort to clean it up anyway:
  • move an error check back to its respective function
  • nuke size_t r; variable
  • fixup memory leaks as some functions do return malloced memory

Code: Select all

ext2_inode *
ext2_get_inode(u32 inode_num)
{
        ext2_inode *r_ino;
        struct ext2_group_desc *gd;
        size_t ino_sz           = sizeof(ext2_inode);
        long inode_disk_addr;
        u32 *i_bitmap;
        u32 block_group         = (inode_num-1) / EXT2_INODES_PER_GROUP(sb);
        u32 local_inode_index;
        u32 bm_index, bm_offset;

        gd = ext2_get_group_desc(block_group);

        r_ino = (ext2_inode *)calloc(1, ino_sz);
        i_bitmap = (u32 *)ext2_get_data_block(gd->bg_inode_bitmap);
        local_inode_index = (inode_num-1) % EXT2_INODES_PER_GROUP(sb);
        bm_index = EXT2_GRP_BM_INDEX(local_inode_index);
        bm_offset = EXT2_GRP_BM_OFFSET(local_inode_index);

        if (!(i_bitmap[bm_index] & (1 << bm_offset)))
        {
                printf("Error: Inode not valid, %u\n", inode_num);
                goto GIerr_out;
        }

        inode_disk_addr = PART_START;
        inode_disk_addr += (long)gd->bg_inode_table * EXT2_BLOCK_SIZE(sb);
        inode_disk_addr += (long)(ino_sz * local_inode_index);

        fseek(f, inode_disk_addr, SEEK_SET);
        if (fread(r_ino, ino_sz, 1, f) == 0 || r_ino->i_mode == 0)
        {
                printf("Error: Reading inode, %u\n", inode_num);
                goto GIerr_out;
        }
GI_deinit:
        free(gd);
        free(i_bitmap);
        return r_ino;
GIerr_out:
        free(r_ino);
        r_ino = NULL;
        goto GI_deinit;
}
bpaterni
Posts: 12
Joined: Sat May 30, 2009 4:38 pm

Re: ext2/3: reading a file

Post by bpaterni »

Okay, I've found my problem. It turns out that the one of the variables I was using to calculate inode_disk_addr was wrong:

Code: Select all

        inode_disk_addr = PART_START;
        inode_disk_addr += (long)gd->bg_inode_table * EXT2_BLOCK_SIZE(sb);
        inode_disk_addr += (long)(ino_sz * local_inode_index);
Here, 'ino_sz' is the size of an inode structure in memory which is 128 bytes in my case, however the size of an inode on disk is actually super_block->s_inode_size bytes long, which is 256 bytes on the filesystem. I changed ino_sz to 256 and my test program ran without error. It certainly took a while, but the problem is now fixed! :)

Thank anyway for the help KarimAllah.
Post Reply