X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FFilesystems%2FExt2%2Fext2.c;h=eacf87bed249226ca6d168146cf661d31b88b5e8;hb=13078002b01ee4f63eb2001d2ef479a2a006ea32;hp=5c9f97a5c5413930a225e203aacc8f30f6921820;hpb=e02f66c7125bf18f77c6c53587238cbd49da2c89;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/Filesystems/Ext2/ext2.c b/KernelLand/Modules/Filesystems/Ext2/ext2.c index 5c9f97a5..eacf87be 100644 --- a/KernelLand/Modules/Filesystems/Ext2/ext2.c +++ b/KernelLand/Modules/Filesystems/Ext2/ext2.c @@ -10,8 +10,8 @@ #include "ext2_common.h" #include -// === IMPORTS === -extern tVFS_NodeType gExt2_DirType; +#define MIN_BLOCKS_PER_GROUP 2 +#define MAX_BLOCK_LOG_SIZE 10 // 1024 << 10 = 1MiB // === PROTOTYPES === int Ext2_Install(char **Arguments); @@ -21,8 +21,10 @@ extern tVFS_NodeType gExt2_DirType; tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options); void Ext2_Unmount(tVFS_Node *Node); void Ext2_CloseFile(tVFS_Node *Node); +tVFS_Node *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode); // - Internal Helpers int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode); +void Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode); Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum); Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent); void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode); @@ -37,7 +39,7 @@ tVFS_Driver gExt2_FSInfo = { .Detect = Ext2_Detect, .InitDevice = Ext2_InitDevice, .Unmount = Ext2_Unmount, - .GetNodeFromINode = NULL + .GetNodeFromINode = Ext2_GetNodeFromINode }; // === CODE === @@ -92,11 +94,10 @@ int Ext2_Detect(int FD) */ tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) { - tExt2_Disk *disk; + tExt2_Disk *disk = NULL; int fd; int groupCount; tExt2_SuperBlock sb; - tExt2_Inode inode; ENTER("sDevice pOptions", Device, Options); @@ -115,11 +116,15 @@ tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) if(sb.s_magic != 0xEF53) { Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)", Device, sb.s_magic); - VFS_Close(fd); - LEAVE('n'); - return NULL; + goto _error; } - + + if( sb.s_blocks_per_group < MIN_BLOCKS_PER_GROUP ) { + Log_Warning("Ext2", "Blocks per group is too small (%i < %i)", + sb.s_blocks_per_group, MIN_BLOCKS_PER_GROUP); + goto _error; + } + // Get Group count groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group); LOG("groupCount = %i", groupCount); @@ -128,18 +133,21 @@ tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount); if(!disk) { Log_Warning("EXT2", "Unable to allocate disk structure"); - VFS_Close(fd); - LEAVE('n'); - return NULL; + goto _error; } disk->FD = fd; memcpy(&disk->SuperBlock, &sb, 1024); disk->GroupCount = groupCount; // Get an inode cache handle - disk->CacheID = Inode_GetHandle(); + disk->CacheID = Inode_GetHandle(NULL); // Get Block Size + if( sb.s_log_block_size > MAX_BLOCK_LOG_SIZE ) { + Log_Warning("Ext2", "Block size (log2) too large (%i > %i)", + sb.s_log_block_size, MAX_BLOCK_LOG_SIZE); + goto _error; + } disk->BlockSize = 1024 << sb.s_log_block_size; LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size); @@ -162,7 +170,7 @@ tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table); // Get root Inode - Ext2_int_ReadInode(disk, 2, &inode); + Ext2_int_ReadInode(disk, 2, &disk->RootInode); // Create Root Node memset(&disk->RootNode, 0, sizeof(tVFS_Node)); @@ -174,18 +182,24 @@ tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) disk->RootNode.Type = &gExt2_DirType; // Complete root node - disk->RootNode.UID = inode.i_uid; - disk->RootNode.GID = inode.i_gid; + disk->RootNode.UID = disk->RootInode.i_uid; + disk->RootNode.GID = disk->RootInode.i_gid; disk->RootNode.NumACLs = 1; disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW; #if DEBUG - LOG("inode.i_size = 0x%x", inode.i_size); - LOG("inode.i_block[0] = 0x%x", inode.i_block[0]); + LOG("inode.i_size = 0x%x", disk->RootInode.i_size); + LOG("inode.i_block[0] = 0x%x", disk->RootInode.i_block[0]); #endif LEAVE('p', &disk->RootNode); return &disk->RootNode; +_error: + if( disk ) + free(disk); + VFS_Close(fd); + LEAVE('n'); + return NULL; } /** @@ -209,32 +223,49 @@ void Ext2_Unmount(tVFS_Node *Node) void Ext2_CloseFile(tVFS_Node *Node) { tExt2_Disk *disk = Node->ImplPtr; + ENTER("pNode", Node); if( Mutex_Acquire(&Node->Lock) != 0 ) { + LEAVE('-'); return ; } if( Node->Flags & VFS_FFLAG_DIRTY ) { // Commit changes + Ext2_int_WritebackNode(disk, Node); + Node->Flags &= ~VFS_FFLAG_DIRTY; } int was_not_referenced = (Node->ImplInt == 0); tVFS_ACL *acls = Node->ACLs; - if( Inode_UncacheNode(disk->CacheID, Node->Inode) ) + if( Inode_UncacheNode(disk->CacheID, Node->Inode) == 1 ) { if( was_not_referenced ) { + LOG("Removng inode"); // Remove inode + Log_Warning("Ext2", "TODO: Remove inode when not referenced (%x)", (Uint32)Node->Inode); } if( acls != &gVFS_ACL_EveryoneRW ) { free(acls); } + LOG("Node cleaned"); } + else { + LOG("Still referenced, releasing lock"); + Mutex_Release(&Node->Lock); + } + LEAVE('-'); return ; } +tVFS_Node *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode) +{ + return Ext2_int_CreateNode(RootNode->ImplPtr, Inode); +} + //================================== //= INTERNAL FUNCTIONS = //================================== @@ -279,7 +310,9 @@ int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) LEAVE('i', 0); return 0; } - + + Ext2_int_DumpInode(Disk, InodeId, Inode); + InodeId --; // Inodes are numbered starting at 1 group = InodeId / Disk->SuperBlock.s_inodes_per_group; @@ -298,6 +331,140 @@ int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) return 1; } +/** + * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) + * \brief Create a new VFS Node + */ +tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) +{ + struct { + tVFS_Node retNode; + tExt2_Inode inode; + } data; + tVFS_Node *node = &data.retNode; + tExt2_Inode *in = &data.inode; + + if( !Ext2_int_ReadInode(Disk, InodeID, &data.inode) ) + return NULL; + + if( (node = Inode_GetCache(Disk->CacheID, InodeID)) ) + return node; + node = &data.retNode; + + memset(node, 0, sizeof(*node)); + + // Set identifiers + node->Inode = InodeID; + node->ImplPtr = Disk; + node->ImplInt = in->i_links_count; + if( in->i_links_count == 0 ) { + Log_Notice("Ext2", "Inode %p:%x is not referenced, bug?", Disk, InodeID); + } + + // Set file length + node->Size = in->i_size; + + // Set Access Permissions + node->UID = in->i_uid; + node->GID = in->i_gid; + node->NumACLs = 3; + node->ACLs = VFS_UnixToAcessACL(in->i_mode & 0777, in->i_uid, in->i_gid); + + // Set Function Pointers + node->Type = &gExt2_FileType; + + switch(in->i_mode & EXT2_S_IFMT) + { + // Symbolic Link + case EXT2_S_IFLNK: + node->Flags = VFS_FFLAG_SYMLINK; + break; + // Regular File + case EXT2_S_IFREG: + node->Flags = 0; + node->Size |= (Uint64)in->i_dir_acl << 32; + break; + // Directory + case EXT2_S_IFDIR: + node->Type = &gExt2_DirType; + node->Flags = VFS_FFLAG_DIRECTORY; + node->Data = calloc( sizeof(Uint16), DivUp(node->Size, Disk->BlockSize) ); + break; + // Unknown, Write protect it to be safe + default: + node->Flags = VFS_FFLAG_READONLY; + break; + } + + // Set Timestamps + node->ATime = in->i_atime * 1000; + node->MTime = in->i_mtime * 1000; + node->CTime = in->i_ctime * 1000; + + // Save in node cache and return saved node + return Inode_CacheNodeEx(Disk->CacheID, &data.retNode, sizeof(data)); +} + +int Ext2_int_WritebackNode(tExt2_Disk *Disk, tVFS_Node *Node) +{ + tExt2_Inode *inode = (void*)(Node+1); + + if( Disk != Node->ImplPtr ) { + Log_Error("Ext2", "Ext2_int_WritebackNode - Disk != Node->ImplPtr"); + return -1; + } + + if( Node->Flags & VFS_FFLAG_SYMLINK ) { + inode->i_mode = EXT2_S_IFLNK; + } + else if( Node->Flags & VFS_FFLAG_DIRECTORY ) { + inode->i_mode = EXT2_S_IFDIR; + } + else if( Node->Flags & VFS_FFLAG_READONLY ) { + Log_Notice("Ext2", "Not writing back readonly inode %p:%x", Disk, Node->Inode); + return 1; + } + else { + inode->i_mode = EXT2_S_IFREG; + inode->i_dir_acl = Node->Size >> 32; + } + + inode->i_size = Node->Size & 0xFFFFFFFF; + inode->i_links_count = Node->ImplInt; + + inode->i_uid = Node->UID; + inode->i_gid = Node->GID; + + inode->i_atime = Node->ATime / 1000; + inode->i_mtime = Node->MTime / 1000; + inode->i_ctime = Node->CTime / 1000; + + // TODO: Compact ACLs into unix mode + Log_Warning("Ext2", "TODO: Support converting Acess ACLs into unix modes"); + inode->i_mode |= 777; + + Ext2_int_WriteInode(Disk, Node->Inode, inode); + + return 0; +} + +void Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode) +{ + LOG("%p[Inode %i] = {", Disk, InodeID); + LOG(" .i_mode = 0%04o", Inode->i_mode); + LOG(" .i_uid:i_gid = %i:%i", Inode->i_uid, Inode->i_gid); + LOG(" .i_size = 0x%x", Inode->i_size); + LOG(" .i_block[0:3] = {0x%x,0x%x,0x%x,0x%x}", + Inode->i_block[0], Inode->i_block[1], Inode->i_block[2], Inode->i_block[3]); + LOG(" .i_block[4:7] = {0x%x,0x%x,0x%x,0x%x}", + Inode->i_block[4], Inode->i_block[5], Inode->i_block[6], Inode->i_block[7]); + LOG(" .i_block[8:11] = {0x%x,0x%x,0x%x,0x%x}", + Inode->i_block[8], Inode->i_block[6], Inode->i_block[10], Inode->i_block[11]); + LOG(" .i_block[12:14] = {0x%x,0x%x,0x%x}", + Inode->i_block[12], Inode->i_block[13], Inode->i_block[14]); + LOG("}"); +} + /** * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) * \brief Get the address of a block from an inode's list @@ -355,8 +522,67 @@ Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) */ Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) { -// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group; - Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented"); + Uint start_group = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group; + Uint group = start_group; + + if( Disk->SuperBlock.s_free_inodes_count == 0 ) + { + Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p", Disk); + return 0; + } + + while( group < Disk->GroupCount && Disk->Groups[group].bg_free_inodes_count == 0 ) + group ++; + if( group == Disk->GroupCount ) + { + group = 0; + while( group < start_group && Disk->Groups[group].bg_free_inodes_count == 0 ) + group ++; + } + + if( Disk->Groups[group].bg_free_inodes_count == 0 ) + { + Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p, but superblock says some free", Disk); + return 0; + } + + // Load bitmap for group + // (s_inodes_per_group / 8) bytes worth + // - Allocate a buffer the size of a sector/block + // - Read in part of the bitmap + // - Search for a free inode + tExt2_Group *bg = &Disk->Groups[group]; + int ofs = 0; + do { + const int sector_size = 512; + Uint8 buf[sector_size]; + VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf); + + int byte, bit; + for( byte = 0; byte < sector_size && buf[byte] == 0xFF; byte ++ ) + ; + if( byte < sector_size ) + { + for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++) + ; + ASSERT(bit != 8); + buf[byte] |= 1 << bit; + VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf); + + bg->bg_free_inodes_count --; + Disk->SuperBlock.s_free_inodes_count --; + + Uint32 ret = group * Disk->SuperBlock.s_inodes_per_group + byte * 8 + bit + 1; + Log_Debug("Ext2", "Ext2_int_AllocateInode - Allocated 0x%x", ret); + return ret; + } + + ofs += sector_size; + } while(ofs < Disk->SuperBlock.s_inodes_per_group / 8); + + Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes in group %p:%i but header reported free", + Disk, group); + return 0; } @@ -365,7 +591,7 @@ Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) */ void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode) { - + Log_Warning("Ext2", "TODO: Impliment Ext2_int_DereferenceInode"); } /** @@ -381,6 +607,14 @@ void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk) // Update Primary VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock); + // - Update block groups while we're at it + VFS_WriteAt( + Disk->FD, + Disk->SuperBlock.s_first_data_block * Disk->BlockSize + 1024, + sizeof(tExt2_Group)*Disk->GroupCount, + Disk->Groups + ); + // Secondaries // at Block Group 1, 3^n, 5^n, 7^n