Page 1 of 1

EXT2 Problems

Posted: Sun Nov 20, 2016 8:04 am
by Agola
Hello osdev forum :)

It's my os, Agola. I've done the simple ata support recently and I started to implementing ext2 filesystem.

Everything looks good until inode table, after it everything gets too complicated for me. I can't understand fully the relation of inodes - files - directories. I can read the entries of root directory inodes, but don't know how to go far.

Is there a detailed but easier to read (feeling like a dumb :() documentation for ext2?

Also I think there is some problems in my code, for example there is strange characters at end of the /boot directory:

Image

I can't understand some things on these datas, too. Why the block of inode table is really too far from starting block? And why the inode of "." , ".." and "boot" is same?

Is inodes per block group = 2016 normal? (Because of 8192/128 = 64)

And what is the characters after the "boot"?

How can I read the text.txt for example?

This is my ext2 code: (Currently on kernel main, will transfer to ext2.c after fully learning ext2...) I ported newlib to Agola, and using a i686-agola target gcc.

Code: Select all

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#include <kernel/tty.h>
#include <kernel/gdt.h>
#include <kernel/idt.h>
#include <kernel/isr.h>
#include <kernel/irq.h>
#include <kernel/atapio.h>

unsigned char superblock[1024];
unsigned char block_group_descriptor[512];

typedef struct superblock
{
	uint32_t number_of_inodes;
	uint32_t number_of_blocks;
	uint32_t blocks_reserved_superuser;
	uint32_t unallocated_blocks;
	uint32_t unallocated_inodes;
	uint32_t block_number_of_superblock;
	uint32_t block_size;
	uint32_t fragment_size;
	uint32_t number_of_blocks_in_block_group;
	uint32_t number_of_fragments_in_block_group;
	uint32_t number_of_inodes_in_block_group;
	uint32_t last_mount_time;
	uint32_t last_write_time;
	uint16_t volume_mount_count_after_last_check;
	uint16_t max_mount_count_before_last_check;
	uint16_t ext2_signature;
	uint16_t filesystem_state;
	uint16_t error_handling_methods;
	uint16_t minor_version;
	uint32_t last_check_time;
	uint32_t interval_forced_check;
	uint32_t os_id;
	uint32_t major_version;
	uint16_t user_id;
	uint16_t group_id;
} superblock_t;

typedef struct block_group_descriptor
{
	uint32_t block_of_block_usage_bitmap;
	uint32_t block_of_inode_usage_bitmap;
	uint32_t block_of_inode_table;
	uint16_t number_unallocated_blocks;
	uint16_t number_unallocated_inodes;
	uint16_t number_directories;
} block_group_descriptor_t;

typedef struct inode
{
	uint16_t type_and_permissions;
	uint16_t user_id;
	uint32_t size;
	uint32_t last_access_time;
	uint32_t creation_time;
	uint32_t last_modification_time;
	uint32_t deletion_time;
	uint16_t group_id;
	uint16_t count_hard_links;
	uint32_t count_disk_sectors;
	uint32_t flags;
	uint32_t os_specific_value_1;
	uint32_t dbp[12];
	uint32_t singly_indirect_block_pointer;
	uint32_t doubly_indirect_block_pointer;
	uint32_t triply_indirect_block_pointer;
	uint32_t generation_number;
	uint32_t reserved_1;
	uint32_t reserved_2;
	uint32_t block_address_of_fragments;
	uint8_t os_specific_value_2[12];
} inode_t;

typedef struct directory
{
	uint32_t inode;
	uint16_t size;
	uint8_t name_length;
	uint8_t type_indicator;
	char name[];
} __attribute__ ((packed)) directory_t;

#define FS_CLEAN 1
#define FS_HAS_ERRORS 2

#define FS_IGNORE_ERROR
#define FS_REMOUNT_READONLY
#define FS_KERNEL_PANIC

#define FS_FIFO 0x1000
#define FS_CHARACTER_DEVICE 0x2000
#define FS_DIRECTORY 0x4000
#define FS_BLOCK_DEVICE 0x6000
#define FS_REGULAR_FILE 0x8000
#define FS_SYMBOLIC_LINK 0xA000
#define FS_UNIX_SOCKET 0xC000

#define LINUX 0
#define GNU_HURD 1
#define MASIX 2
#define FREEBSD 3
#define OTHER 4

const unsigned int EXT2_START = 1048576;

void kernel_main(void) {
	gdt_install();
	idt_install();
	isr_install();
	irq_install();

	asm volatile ("sti");

	terminal_initialize(TERMINAL_COLOR_LIGHT_GREY, TERMINAL_COLOR_BLACK);

	setbuf(stdout, NULL);

	printf("Reading superblock and block group descriptor...\n\n");

	superblock_t superblock;
	block_group_descriptor_t block_group_descriptor;

	atapio_read(EXT2_START + 1024, sizeof(superblock_t), &superblock);
	atapio_read(EXT2_START + 2048, sizeof(block_group_descriptor_t), &block_group_descriptor);

	printf("Superblock:\nEXT2 signature: %X, Filesystem state: %s, Inodes per block group: %d\n\n", superblock.ext2_signature, (superblock.filesystem_state == 1) ? "Clean" : "Has errors", superblock.number_of_inodes_in_block_group);

	printf("Block group descriptor:\nBlock of inode table: %d\n\n", block_group_descriptor.block_of_inode_table);

	printf("Reading root from inode table:\n\n");
	inode_t *inode_table = (inode_t*) malloc(128 * superblock.number_of_inodes_in_block_group);
	atapio_read(EXT2_START + (block_group_descriptor.block_of_inode_table) * 1024, sizeof(inode_t) * superblock.number_of_inodes_in_block_group, inode_table);

	printf("Getting directories of root\n\n");

	uint8_t *block = (uint8_t*) malloc(1024);

	atapio_read(EXT2_START + (inode_table[1].dbp[0] * 1024), 1024, block);

	unsigned int offset = 0;
	unsigned int times = 0;

	directory_t* directory;

	while(offset < 1024)
	{
		directory = (directory_t*)((uintptr_t)block + offset);

		offset += directory->size;

		printf("%s %s Inode:%d\n", directory->name, directory->type_indicator == 2 ? "<DIR>" : "<FILE>", directory->size);
	}
} 
And this is my ata read / write code:

Code: Select all

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <kernel/io.h>

#define ATAPIO_REG_DATA 0x1F0
#define ATAPIO_REG_ERROR 0x1F1
#define ATAPIO_SECT_COUNT 0x1F2
#define ATAPIO_LBA_LOW 0x1F3
#define ATAPIO_LBA_MID 0x1F4
#define ATAPIO_LBA_HIGH 0x1F5
#define ATAPIO_DRIVE_HEAD 0x1F6
#define ATAPIO_COMMAND_STATUS 0x1F7

typedef struct 
{
    unsigned int err : 1;
    unsigned int idx : 1;
    unsigned int corr : 1;
    unsigned int drq : 1;
    unsigned int srv : 1;
    unsigned int df : 1;
    unsigned int rdy : 1;
    unsigned int bsy : 1;
} atapio_status_t;

unsigned long round_up_to_multiple_of_n(unsigned long value, unsigned long n)
{
    return ((value + (n - 1)) / n) * n;
}

unsigned long round_down_to_multiple_of_n(unsigned long value, unsigned long n)
{
    return value - (value % n);
}

atapio_status_t atapio_read_status()
{
    atapio_status_t atapio_status;
    memcpy(&atapio_status, inb(ATAPIO_COMMAND_STATUS), sizeof(atapio_status_t));

    return atapio_status;
}

void atapio_wait_ready()
{
    while(atapio_read_status().bsy);
}

void atapio_wait_drq()
{
    while(!(atapio_read_status().drq));
}

void atapio_400ns_delay()
{
    for (int x = 0; x < 4; x++) inb(ATAPIO_COMMAND_STATUS);
}

void atapio_read_lba48(uint64_t lba, uint16_t count, uint8_t *buffer)
{
    unsigned int i = 0;

    outb(ATAPIO_DRIVE_HEAD, 0x40);
    outb(ATAPIO_SECT_COUNT, (unsigned char)((count >> 8) & 0xFF));
    outb(ATAPIO_LBA_LOW, (unsigned char)((lba >> 24) & 0xFF));
    outb(ATAPIO_LBA_MID, (unsigned char)((lba >> 32) & 0xFF));
    outb(ATAPIO_LBA_HIGH, (unsigned char)((lba >> 40) & 0xFF));
    outb(ATAPIO_SECT_COUNT, (unsigned char)(count & 0xFF));
    outb(ATAPIO_LBA_LOW, (unsigned char)(lba & 0xFF));
    outb(ATAPIO_LBA_MID, (unsigned char)((lba >> 8) & 0xFF));
    outb(ATAPIO_LBA_HIGH, (unsigned char)((lba >> 16) & 0xFF));

    atapio_wait_ready();

    outb(ATAPIO_COMMAND_STATUS, 0x24);
   
    while (count--)
    {
        atapio_wait_drq();

        insw (ATAPIO_REG_DATA, buffer, 256);

        atapio_wait_ready();

        atapio_400ns_delay();

        buffer += 512;
    }
}

void atapio_write_lba48(uint64_t lba, uint16_t count, uint8_t *buffer)
{
    unsigned int i = 0;

    outb(ATAPIO_DRIVE_HEAD, 0x40);
    outb(ATAPIO_SECT_COUNT, (unsigned char)((count >> 8) & 0xFF));
    outb(ATAPIO_LBA_LOW, (unsigned char)((lba >> 24) & 0xFF));
    outb(ATAPIO_LBA_MID, (unsigned char)((lba >> 32) & 0xFF));
    outb(ATAPIO_LBA_HIGH, (unsigned char)((lba >> 40) & 0xFF));
    outb(ATAPIO_SECT_COUNT, (unsigned char)(count & 0xFF));
    outb(ATAPIO_LBA_LOW, (unsigned char)(lba & 0xFF));
    outb(ATAPIO_LBA_MID, (unsigned char)((lba >> 8) & 0xFF));
    outb(ATAPIO_LBA_HIGH, (unsigned char)((lba >> 16) & 0xFF));

    atapio_wait_ready();

    outb(ATAPIO_COMMAND_STATUS, 0x34);
   
    while (count--)
    {   
        atapio_wait_drq();

        while (i < 256)
        {
            uint16_t to_write = (buffer[i * 2 + 1] << 8) | buffer[i * 2];
            outw(ATAPIO_REG_DATA, to_write);
            i++;
        };

        atapio_wait_ready();

        atapio_400ns_delay();

        outb(ATAPIO_COMMAND_STATUS, 0xE7);
    }
}

void atapio_read(uint64_t from, uint64_t bytes, uint8_t *buffer)
{
    unsigned long needed_blocks = 0;
    unsigned long needed_bytes = 0;
    unsigned long lba = 0;

    if (bytes == 0) return;

    if (from > 512) lba = round_down_to_multiple_of_n(from, 512) / 512;
    else lba = 0;

    bytes += (from - (512 * lba));

    if (bytes > 512) needed_bytes = round_up_to_multiple_of_n(bytes, 512);
    else needed_bytes = 512;

    needed_blocks = needed_bytes / 512;

    uint8_t *read_buffer = (uint8_t*) malloc(needed_bytes);

    atapio_read_lba48(lba, needed_blocks, read_buffer);

    memcpy(buffer, read_buffer + (from - (512 * lba)), bytes - (from - (512 * lba)));

    free(read_buffer);
}

void atapio_write(uint64_t to, uint64_t bytes, uint8_t *buffer)
{
    unsigned long needed_blocks = 0;
    unsigned long needed_bytes = 0;
    unsigned long lba = 0;

    if (bytes == 0) return;

    if (to > 512) lba = round_down_to_multiple_of_n(to, 512) / 512;
    else lba = 0;

    bytes += (to - (512 * lba));

    if (bytes > 512) needed_bytes = round_up_to_multiple_of_n(bytes, 512);
    else needed_bytes = 512;

    needed_blocks = needed_bytes / 512;

    uint8_t *write_buffer = (uint8_t*) malloc(needed_bytes);

    atapio_read_lba48(lba, needed_blocks, write_buffer);

    memcpy(write_buffer + (to - (512 * lba)), buffer, bytes - (to - (512 * lba)));

    atapio_write_lba48(lba, needed_blocks, write_buffer);

    free(write_buffer);
}
Thanks for helping :mrgreen:

Also note: I tested ata driver many times, I think there is no problem on it. :?

Re: EXT2 Problems

Posted: Mon Nov 21, 2016 4:02 pm
by simeonz
I cannot contribute much, but... I suspect that the directory name is not null-terminated. Try to use the name_length field explicitly with the "%*s" format specifier to print it.

Re: EXT2 Problems

Posted: Tue Nov 22, 2016 12:59 am
by iansjack
All you need to know about ext2 is in this document: http://www.nongnu.org/ext2-doc/ext2.html

Re: EXT2 Problems

Posted: Tue Nov 22, 2016 12:26 pm
by osdever
You at least can read root inode, my code is completely broken :(

Re: EXT2 Problems

Posted: Sat Dec 17, 2016 11:53 am
by Agola
Finally fixed, now my ext2 works with full read / write support, yey!