// 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 }