242 lines
8.3 KiB
C
242 lines
8.3 KiB
C
// ext2.c
|
|
// Author: Josh Holtrop
|
|
// Date: 08/22/04
|
|
// Modified: 12/24/04
|
|
|
|
#include "hos_defines.h"
|
|
#include "fs/devices.h"
|
|
#include "kout.h"
|
|
#include "ext2.h"
|
|
#include "mm/vmm.h"
|
|
#include "fs/vfs.h"
|
|
#include "lang/asmfuncs.h"
|
|
#include "functions.h"
|
|
|
|
/* Turning an inode number into a (group, inode_index) pair:
|
|
* group = (inode - 1) / s_inodes_per_group
|
|
* index = (inode - 1) % s_inodes_per_group
|
|
*/
|
|
|
|
|
|
// initialize the filesystem driver
|
|
int ext2_init(int fsID)
|
|
{
|
|
vfs_fs_t *fs;
|
|
if (( fs = New(vfs_fs_t) )) // give the VFS system our FS structure
|
|
{
|
|
fs->mount_super = ext2_mount_super;
|
|
fs->umount_super = ext2_umount_super;
|
|
fs->mkfs = ext2_mkfs;
|
|
fs->stat = ext2_stat;
|
|
vfs_register_fs(fsID, fs);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
// create an ext2 filesystem on the given device
|
|
int ext2_mkfs(major_t major, minor_t minor)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
// mount the superblock of the filesystem and return a pointer to it, if valid
|
|
void *ext2_mount_super(major_t major, minor_t minor)
|
|
{
|
|
ext2_super_block_t *super = kmalloc(1024);
|
|
block_read(major, minor, 2, 2, super);
|
|
if (super->s_magic != EXT2_MAGIC) // not an ext2 filesystem
|
|
{
|
|
kfree(super);
|
|
return NULL;
|
|
}
|
|
return super;
|
|
}
|
|
|
|
// stat a file, return a structure of info about it
|
|
int ext2_stat(vfs_mount_t *mount, char *file, vfs_stat_t *stat)
|
|
{
|
|
u32_t inode_number = ext2_get_inode_number(mount, file);
|
|
if (!inode_number)
|
|
return -1;
|
|
ext2_inode_t inode = ext2_get_inode(mount, inode_number);
|
|
|
|
switch(inode.i_mode & EXT2_I_MODE_TYPE_MASK)
|
|
{
|
|
case EXT2_I_MODE_FIFO: stat->type = VFS_FT_FIFO;
|
|
case EXT2_I_MODE_CHAR: stat->type = VFS_FT_CHAR;
|
|
case EXT2_I_MODE_DIR: stat->type = VFS_FT_DIR;
|
|
case EXT2_I_MODE_BLOCK: stat->type = VFS_FT_BLOCK;
|
|
case EXT2_I_MODE_FILE: stat->type = VFS_FT_FILE;
|
|
case EXT2_I_MODE_SYM: stat->type = VFS_FT_SYMLINK;
|
|
case EXT2_I_MODE_SOCK: stat->type = VFS_FT_SOCK;
|
|
default: stat->type = VFS_FT_UNKNOWN;
|
|
}
|
|
stat->size = inode.i_size;
|
|
stat->inode = inode_number;
|
|
stat->permissions = inode.i_mode & EXT2_I_MODE_ATTR_MASK;
|
|
stat->uid = inode.i_uid;
|
|
stat->gid = inode.i_gid;
|
|
stat->atime = inode.i_atime;
|
|
stat->mtime = inode.i_mtime;
|
|
stat->ctime = inode.i_ctime;
|
|
stat->links = inode.i_links_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// returns the inode number that the given file name points to
|
|
u32_t ext2_get_inode_number(vfs_mount_t *mount, char *file)
|
|
{
|
|
if (file[0] != '/')
|
|
return 0;
|
|
int length = strlen(file);
|
|
if (length == 1)
|
|
return 2;
|
|
char *fil = kmalloc(length + 1);
|
|
strcpy(fil, file);
|
|
if (fil[length-1] == '/')
|
|
{
|
|
length--;
|
|
fil[length] = 0;
|
|
}
|
|
u32_t inode_number = 0;
|
|
// TODO: get inode number
|
|
|
|
kfree(fil);
|
|
return inode_number;
|
|
}
|
|
|
|
// open an inode for reading
|
|
ext2_open_inode_t *ext2_open_inode(vfs_mount_t *mount, u32_t inode_number)
|
|
{
|
|
ext2_inode_t inode = ext2_get_inode(mount, inode_number);
|
|
ext2_open_inode_t *open_inode = New(ext2_open_inode_t);
|
|
open_inode->inode = inode;
|
|
open_inode->block = 0;
|
|
open_inode->block_pointers = NULL;
|
|
open_inode->block_pointers_start = 0;
|
|
mount->refs++;
|
|
return open_inode;
|
|
}
|
|
|
|
// seek to a certain block of an open inode
|
|
int ext2_inode_seek(ext2_open_inode_t *open_inode, u32_t block_number)
|
|
{
|
|
if (open_inode->inode.i_size <= (block_number << (10 + super->s_log_block_size)))
|
|
return -1; // at or past EOF
|
|
open_inode->block = block_number;
|
|
return 0;
|
|
}
|
|
|
|
// returns number of bytes read
|
|
int ext2_read_inode_block(vfs_mount_t *mount, ext2_open_inode_t *open_inode, void *block)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
if (open_inode->inode.i_size <= (open_inode->block << (10 + super->s_log_block_size)))
|
|
return 0; // at or past EOF
|
|
u32_t leftover_bytes = open_inode->inode.i_size - (open_inode->block << (10 + super->s_log_block_size));
|
|
u32_t block_number = ext2_block_number(mount, open_inode);
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_number, super),
|
|
2 << super->s_log_block_size, block);
|
|
open_inode->block++;
|
|
return min(leftover_bytes, 1024 << super->s_log_block_size);
|
|
}
|
|
|
|
// transform open_inode->block (a relative block number) to an absolute block number for the filesystem
|
|
u32_t ext2_block_number(vfs_mount_t *mount, ext2_open_inode_t *open_inode)
|
|
{
|
|
if (open_inode->block < 12)
|
|
return open_inode->inode.i_block[open_inode->block];
|
|
ext2_super_block_t *super = mount->super;
|
|
int pointersPerBlock = 256 << super->s_log_block_size;
|
|
if (open_inode->block_pointers && // there is a block pointers cache block allocated
|
|
(open_inode->block >= open_inode->block_pointers_start) && // and the block number is in it
|
|
(open_inode->block < (open_inode->block_pointers_start + pointersPerBlock)))
|
|
return open_inode->block_pointers[open_inode->block - open_inode->block_pointers_start];
|
|
u32_t rel_block = open_inode->block - 12;
|
|
if (!open_inode->block_pointers)
|
|
open_inode->block_pointers = kmalloc(pointersPerBlock << 2);
|
|
if (rel_block < pointersPerBlock) // indirect block in i_block[12]
|
|
{
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[12], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12;
|
|
return open_inode->block_pointers[rel_block];
|
|
}
|
|
rel_block -= pointersPerBlock;
|
|
if (rel_block < (pointersPerBlock * pointersPerBlock)) // double-indirect block in i_block[13]
|
|
{
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[13], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t real_block = open_inode->block_pointers[rel_block / pointersPerBlock];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(real_block, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12 + pointersPerBlock + rel_block - (rel_block % pointersPerBlock);
|
|
return open_inode->block_pointers[rel_block % pointersPerBlock];
|
|
}
|
|
// this code shouldn't run unless we are dealing with a 65+mb file ...
|
|
rel_block -= pointersPerBlock * pointersPerBlock;
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(open_inode->inode.i_block[14], super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t index_1 = rel_block / (pointersPerBlock * pointersPerBlock);
|
|
u32_t leftover_1 = rel_block % (pointersPerBlock * pointersPerBlock);
|
|
u32_t block_1 = open_inode->block_pointers[index_1];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_1, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
u32_t index_2 = leftover_1 / pointersPerBlock;
|
|
u32_t leftover_2 = leftover_1 % pointersPerBlock;
|
|
u32_t block_2 = open_inode->block_pointers[index_2];
|
|
block_read(mount->major, mount->minor, ext2_FSToDiskBlock(block_2, super),
|
|
2 << super->s_log_block_size, open_inode->block_pointers);
|
|
open_inode->block_pointers_start = 12 + (pointersPerBlock + 1) * pointersPerBlock + rel_block - (rel_block % pointersPerBlock);
|
|
return open_inode->block_pointers[leftover_2];
|
|
}
|
|
|
|
// close an open inode
|
|
int ext2_close_inode(vfs_mount_t *mount, ext2_open_inode_t *open_inode)
|
|
{
|
|
mount->refs--;
|
|
if (open_inode->block_pointers)
|
|
kfree(open_inode->block_pointers); // free the block pointers cache
|
|
kfree(open_inode);
|
|
return 0;
|
|
}
|
|
|
|
// read the inode structure from the device and return it
|
|
ext2_inode_t ext2_get_inode(vfs_mount_t *mount, u32_t inode)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
inode--; // turn inode into a 0-based index
|
|
u32_t group = inode / super->s_inodes_per_group;
|
|
u32_t index = inode % super->s_inodes_per_group;
|
|
u32_t inodeAddr = (ext2_get_group_desc(mount, group).bg_inode_table <<
|
|
(10 + super->s_log_block_size)) + (index << 7);
|
|
void *block = kmalloc(512);
|
|
block_read(mount->major, mount->minor, inodeAddr >> 9, 1, block);
|
|
ext2_inode_t in = *(ext2_inode_t *)(block + (inodeAddr & 0x1FF));
|
|
kfree(block);
|
|
return in;
|
|
}
|
|
|
|
// read the group descriptor structure from the device and return it
|
|
ext2_group_desc_t ext2_get_group_desc(vfs_mount_t *mount, u32_t group)
|
|
{
|
|
ext2_super_block_t *super = mount->super;
|
|
u32_t groupDescAddr = ((1 + super->s_first_data_block) << (10 + super->s_log_block_size)) + (group << 5);
|
|
void *block = kmalloc(512);
|
|
block_read(mount->major, mount->minor, groupDescAddr >> 9, 1, block);
|
|
ext2_group_desc_t gd = *(ext2_group_desc_t *)(block + (groupDescAddr & 0x1FF));
|
|
kfree(block);
|
|
return gd;
|
|
}
|
|
|
|
// called when we are unmounting this filesystem mount
|
|
int ext2_umount_super(vfs_mount_t *mount)
|
|
{
|
|
return kfree(mount->super); // free memory that the superblock was taking
|
|
}
|
|
|