/*\r
- * Acess OS\r
- * Ext2 Driver Version 1\r
+ * Acess2 Ext2 Driver\r
+ * - By John Hodge (thePowersGang)\r
+ *\r
+ * ext2.c\r
+ * - Driver core\r
*/\r
-/**\r
- * \file fs/ext2.c\r
- * \brief Second Extended Filesystem Driver\r
- * \todo Implement file full write support\r
- */\r
-#define DEBUG 1\r
-#define VERBOSE 0\r
+#define DEBUG 0\r
+#define VERSION VER2(0,90)\r
#include "ext2_common.h"\r
#include <modules.h>\r
\r
-// === IMPORTS ===\r
-extern tVFS_NodeType gExt2_DirType;\r
+#define MIN_BLOCKS_PER_GROUP 2\r
+#define MAX_BLOCK_LOG_SIZE 10 // 1024 << 10 = 1MiB\r
\r
// === PROTOTYPES ===\r
int Ext2_Install(char **Arguments);\r
-// Interface Functions\r
+ int Ext2_Cleanup(void);\r
+// - Interface Functions\r
+ int Ext2_Detect(int FD);\r
tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options);\r
-void Ext2_Unmount(tVFS_Node *Node);\r
-void Ext2_CloseFile(tVFS_Node *Node);\r
-// Internal Helpers\r
- int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
-Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
-Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
-void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
+void Ext2_Unmount(tVFS_Node *Node);\r
+void Ext2_CloseFile(tVFS_Node *Node);\r
+tVFS_Node *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode);\r
+// - Internal Helpers\r
+ int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
+void Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode);\r
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
+Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
+void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode);\r
+void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
\r
// === SEMI-GLOBALS ===\r
-MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);\r
+MODULE_DEFINE(0, VERSION, FS_Ext2, Ext2_Install, Ext2_Cleanup);\r
tExt2_Disk gExt2_disks[6];\r
int giExt2_count = 0;\r
tVFS_Driver gExt2_FSInfo = {\r
- "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL\r
+ .Name = "ext2",\r
+ .Detect = Ext2_Detect,\r
+ .InitDevice = Ext2_InitDevice,\r
+ .Unmount = Ext2_Unmount,\r
+ .GetNodeFromINode = Ext2_GetNodeFromINode\r
};\r
\r
// === CODE ===\r
return MODULE_ERR_OK;\r
}\r
\r
+/**\r
+ * \brief Clean up driver state before unload\r
+ */\r
+int Ext2_Cleanup(void)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * Detect if a volume is Ext2 formatted\r
+ */\r
+int Ext2_Detect(int FD)\r
+{\r
+ tExt2_SuperBlock sb;\r
+ size_t len;\r
+ \r
+ len = VFS_ReadAt(FD, 1024, 1024, &sb);\r
+\r
+ if( len != 1024 ) {\r
+ Log_Debug("Ext2", "_Detect: Read failed? (0x%x != 1024)", len);\r
+ return 0;\r
+ }\r
+ \r
+ switch(sb.s_magic)\r
+ {\r
+ case 0xEF53:\r
+ return 2;\r
+ default:\r
+ Log_Debug("Ext2", "_Detect: s_magic = 0x%x", sb.s_magic);\r
+ return 0;\r
+ }\r
+}\r
+\r
/**\r
\brief Initializes a device to be read by by the driver\r
\param Device String - Device to read from\r
*/\r
tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
{\r
- tExt2_Disk *disk;\r
+ tExt2_Disk *disk = NULL;\r
int fd;\r
int groupCount;\r
tExt2_SuperBlock sb;\r
- tExt2_Inode inode;\r
\r
ENTER("sDevice pOptions", Device, Options);\r
\r
if(sb.s_magic != 0xEF53) {\r
Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
Device, sb.s_magic);\r
- VFS_Close(fd);\r
- LEAVE('n');\r
- return NULL;\r
+ goto _error;\r
}\r
- \r
+\r
+ if( sb.s_blocks_per_group < MIN_BLOCKS_PER_GROUP ) {\r
+ Log_Warning("Ext2", "Blocks per group is too small (%i < %i)",\r
+ sb.s_blocks_per_group, MIN_BLOCKS_PER_GROUP);\r
+ goto _error;\r
+ } \r
+\r
// Get Group count\r
groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
LOG("groupCount = %i", groupCount);\r
disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
if(!disk) {\r
Log_Warning("EXT2", "Unable to allocate disk structure");\r
- VFS_Close(fd);\r
- LEAVE('n');\r
- return NULL;\r
+ goto _error;\r
}\r
disk->FD = fd;\r
memcpy(&disk->SuperBlock, &sb, 1024);\r
disk->GroupCount = groupCount;\r
\r
// Get an inode cache handle\r
- disk->CacheID = Inode_GetHandle();\r
+ disk->CacheID = Inode_GetHandle(NULL);\r
\r
// Get Block Size\r
- LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
+ if( sb.s_log_block_size > MAX_BLOCK_LOG_SIZE ) {\r
+ Log_Warning("Ext2", "Block size (log2) too large (%i > %i)",\r
+ sb.s_log_block_size, MAX_BLOCK_LOG_SIZE);\r
+ goto _error;\r
+ }\r
disk->BlockSize = 1024 << sb.s_log_block_size;\r
+ LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size);\r
\r
// Read Group Information\r
+ LOG("sb,s_first_data_block = %x", sb.s_first_data_block);\r
VFS_ReadAt(\r
disk->FD,\r
sb.s_first_data_block * disk->BlockSize + 1024,\r
disk->Groups\r
);\r
\r
- #if VERBOSE\r
LOG("Block Group 0");\r
LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
- #endif\r
\r
// Get root Inode\r
- Ext2_int_ReadInode(disk, 2, &inode);\r
+ Ext2_int_ReadInode(disk, 2, &disk->RootInode);\r
\r
// Create Root Node\r
memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
disk->RootNode.Type = &gExt2_DirType;\r
\r
// Complete root node\r
- disk->RootNode.UID = inode.i_uid;\r
- disk->RootNode.GID = inode.i_gid;\r
+ disk->RootNode.UID = disk->RootInode.i_uid;\r
+ disk->RootNode.GID = disk->RootInode.i_gid;\r
disk->RootNode.NumACLs = 1;\r
disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
\r
#if DEBUG\r
- LOG("inode.i_size = 0x%x", inode.i_size);\r
- LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
+ LOG("inode.i_size = 0x%x", disk->RootInode.i_size);\r
+ LOG("inode.i_block[0] = 0x%x", disk->RootInode.i_block[0]);\r
#endif\r
\r
LEAVE('p', &disk->RootNode);\r
return &disk->RootNode;\r
+_error:\r
+ if( disk )\r
+ free(disk);\r
+ VFS_Close(fd);\r
+ LEAVE('n');\r
+ return NULL;\r
}\r
\r
/**\r
void Ext2_CloseFile(tVFS_Node *Node)\r
{\r
tExt2_Disk *disk = Node->ImplPtr;\r
- Inode_UncacheNode(disk->CacheID, Node->Inode);\r
+ ENTER("pNode", Node);\r
+\r
+ if( Mutex_Acquire(&Node->Lock) != 0 )\r
+ {\r
+ LEAVE('-');\r
+ return ;\r
+ }\r
+\r
+ if( Node->Flags & VFS_FFLAG_DIRTY )\r
+ {\r
+ // Commit changes\r
+ Ext2_int_WritebackNode(disk, Node);\r
+ Node->Flags &= ~VFS_FFLAG_DIRTY;\r
+ }\r
+\r
+ int was_not_referenced = (Node->ImplInt == 0);\r
+ tVFS_ACL *acls = Node->ACLs;\r
+ if( Inode_UncacheNode(disk->CacheID, Node->Inode) == 1 )\r
+ {\r
+ if( was_not_referenced )\r
+ {\r
+ LOG("Removng inode");\r
+ // Remove inode\r
+ Log_Warning("Ext2", "TODO: Remove inode when not referenced (%x)", (Uint32)Node->Inode);\r
+ }\r
+ if( acls != &gVFS_ACL_EveryoneRW ) {\r
+ free(acls);\r
+ }\r
+ LOG("Node cleaned");\r
+ }\r
+ else {\r
+ LOG("Still referenced, releasing lock");\r
+ Mutex_Release(&Node->Lock);\r
+ }\r
+ LEAVE('-');\r
return ;\r
}\r
\r
+tVFS_Node *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode)\r
+{\r
+ return Ext2_int_CreateNode(RootNode->ImplPtr, Inode);\r
+}\r
+\r
//==================================\r
//= INTERNAL FUNCTIONS =\r
//==================================\r
LEAVE('i', 0);\r
return 0;\r
}\r
- \r
+\r
+ Ext2_int_DumpInode(Disk, InodeId, Inode); \r
+\r
InodeId --; // Inodes are numbered starting at 1\r
\r
group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
return 1;\r
}\r
\r
+/**\r
+ * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)\r
+ * \brief Create a new VFS Node\r
+ */\r
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)\r
+{\r
+ struct {\r
+ tVFS_Node retNode;\r
+ tExt2_Inode inode;\r
+ } data;\r
+ tVFS_Node *node = &data.retNode;\r
+ tExt2_Inode *in = &data.inode;\r
+ \r
+ if( !Ext2_int_ReadInode(Disk, InodeID, &data.inode) )\r
+ return NULL;\r
+ \r
+ if( (node = Inode_GetCache(Disk->CacheID, InodeID)) )\r
+ return node;\r
+ node = &data.retNode;\r
+\r
+ memset(node, 0, sizeof(*node));\r
+ \r
+ // Set identifiers\r
+ node->Inode = InodeID;\r
+ node->ImplPtr = Disk;\r
+ node->ImplInt = in->i_links_count;\r
+ if( in->i_links_count == 0 ) {\r
+ Log_Notice("Ext2", "Inode %p:%x is not referenced, bug?", Disk, InodeID);\r
+ }\r
+ \r
+ // Set file length\r
+ node->Size = in->i_size;\r
+ \r
+ // Set Access Permissions\r
+ node->UID = in->i_uid;\r
+ node->GID = in->i_gid;\r
+ node->NumACLs = 3;\r
+ node->ACLs = VFS_UnixToAcessACL(in->i_mode & 0777, in->i_uid, in->i_gid);\r
+ \r
+ // Set Function Pointers\r
+ node->Type = &gExt2_FileType;\r
+ \r
+ switch(in->i_mode & EXT2_S_IFMT)\r
+ {\r
+ // Symbolic Link\r
+ case EXT2_S_IFLNK:\r
+ node->Flags = VFS_FFLAG_SYMLINK;\r
+ break;\r
+ // Regular File\r
+ case EXT2_S_IFREG:\r
+ node->Flags = 0;\r
+ node->Size |= (Uint64)in->i_dir_acl << 32;\r
+ break;\r
+ // Directory\r
+ case EXT2_S_IFDIR:\r
+ node->Type = &gExt2_DirType;\r
+ node->Flags = VFS_FFLAG_DIRECTORY;\r
+ node->Data = calloc( sizeof(Uint16), DivUp(node->Size, Disk->BlockSize) );\r
+ break;\r
+ // Unknown, Write protect it to be safe \r
+ default:\r
+ node->Flags = VFS_FFLAG_READONLY;\r
+ break;\r
+ }\r
+ \r
+ // Set Timestamps\r
+ node->ATime = in->i_atime * 1000;\r
+ node->MTime = in->i_mtime * 1000;\r
+ node->CTime = in->i_ctime * 1000;\r
+ \r
+ // Save in node cache and return saved node\r
+ return Inode_CacheNodeEx(Disk->CacheID, &data.retNode, sizeof(data));\r
+}\r
+\r
+int Ext2_int_WritebackNode(tExt2_Disk *Disk, tVFS_Node *Node)\r
+{\r
+ tExt2_Inode *inode = (void*)(Node+1);\r
+\r
+ if( Disk != Node->ImplPtr ) {\r
+ Log_Error("Ext2", "Ext2_int_WritebackNode - Disk != Node->ImplPtr");\r
+ return -1;\r
+ }\r
+\r
+ if( Node->Flags & VFS_FFLAG_SYMLINK ) {\r
+ inode->i_mode = EXT2_S_IFLNK;\r
+ }\r
+ else if( Node->Flags & VFS_FFLAG_DIRECTORY ) {\r
+ inode->i_mode = EXT2_S_IFDIR;\r
+ }\r
+ else if( Node->Flags & VFS_FFLAG_READONLY ) {\r
+ Log_Notice("Ext2", "Not writing back readonly inode %p:%x", Disk, Node->Inode);\r
+ return 1;\r
+ }\r
+ else {\r
+ inode->i_mode = EXT2_S_IFREG;\r
+ inode->i_dir_acl = Node->Size >> 32;\r
+ }\r
+\r
+ inode->i_size = Node->Size & 0xFFFFFFFF;\r
+ inode->i_links_count = Node->ImplInt;\r
+\r
+ inode->i_uid = Node->UID;\r
+ inode->i_gid = Node->GID;\r
+\r
+ inode->i_atime = Node->ATime / 1000;\r
+ inode->i_mtime = Node->MTime / 1000;\r
+ inode->i_ctime = Node->CTime / 1000;\r
+\r
+ // TODO: Compact ACLs into unix mode\r
+ Log_Warning("Ext2", "TODO: Support converting Acess ACLs into unix modes");\r
+ inode->i_mode |= 777;\r
+\r
+ Ext2_int_WriteInode(Disk, Node->Inode, inode);\r
+\r
+ return 0;\r
+}\r
+\r
+void Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode)\r
+{\r
+ LOG("%p[Inode %i] = {", Disk, InodeID);\r
+ LOG(" .i_mode = 0%04o", Inode->i_mode);\r
+ LOG(" .i_uid:i_gid = %i:%i", Inode->i_uid, Inode->i_gid);\r
+ LOG(" .i_size = 0x%x", Inode->i_size);\r
+ LOG(" .i_block[0:3] = {0x%x,0x%x,0x%x,0x%x}",\r
+ Inode->i_block[0], Inode->i_block[1], Inode->i_block[2], Inode->i_block[3]);\r
+ LOG(" .i_block[4:7] = {0x%x,0x%x,0x%x,0x%x}",\r
+ Inode->i_block[4], Inode->i_block[5], Inode->i_block[6], Inode->i_block[7]);\r
+ LOG(" .i_block[8:11] = {0x%x,0x%x,0x%x,0x%x}",\r
+ Inode->i_block[8], Inode->i_block[6], Inode->i_block[10], Inode->i_block[11]);\r
+ LOG(" .i_block[12:14] = {0x%x,0x%x,0x%x}",\r
+ Inode->i_block[12], Inode->i_block[13], Inode->i_block[14]);\r
+ LOG("}");\r
+}\r
+\r
/**\r
* \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
* \brief Get the address of a block from an inode's list\r
*/\r
Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
{\r
-// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
- Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
+ Uint start_group = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
+ Uint group = start_group;\r
+\r
+ if( Disk->SuperBlock.s_free_inodes_count == 0 ) \r
+ {\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p", Disk);\r
+ return 0;\r
+ }\r
+\r
+ while( group < Disk->GroupCount && Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ group ++;\r
+ if( group == Disk->GroupCount )\r
+ {\r
+ group = 0;\r
+ while( group < start_group && Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ group ++;\r
+ }\r
+ \r
+ if( Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ {\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p, but superblock says some free", Disk);\r
+ return 0;\r
+ }\r
+\r
+ // Load bitmap for group\r
+ // (s_inodes_per_group / 8) bytes worth\r
+ // - Allocate a buffer the size of a sector/block\r
+ // - Read in part of the bitmap\r
+ // - Search for a free inode\r
+ tExt2_Group *bg = &Disk->Groups[group];\r
+ int ofs = 0;\r
+ do {\r
+ const int sector_size = 512;\r
+ Uint8 buf[sector_size];\r
+ VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
+\r
+ int byte, bit;\r
+ for( byte = 0; byte < sector_size && buf[byte] == 0xFF; byte ++ )\r
+ ;\r
+ if( byte < sector_size )\r
+ {\r
+ for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)\r
+ ;\r
+ ASSERT(bit != 8);\r
+ buf[byte] |= 1 << bit;\r
+ VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
+\r
+ bg->bg_free_inodes_count --;\r
+ Disk->SuperBlock.s_free_inodes_count --;\r
+\r
+ Uint32 ret = group * Disk->SuperBlock.s_inodes_per_group + byte * 8 + bit + 1;\r
+ Log_Debug("Ext2", "Ext2_int_AllocateInode - Allocated 0x%x", ret);\r
+ return ret;\r
+ }\r
+\r
+ ofs += sector_size;\r
+ } while(ofs < Disk->SuperBlock.s_inodes_per_group / 8);\r
+\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes in group %p:%i but header reported free",\r
+ Disk, group);\r
+\r
return 0;\r
}\r
\r
+/**\r
+ * \brief Reduce the reference count on an inode\r
+ */\r
+void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode)\r
+{\r
+ Log_Warning("Ext2", "TODO: Impliment Ext2_int_DereferenceInode");\r
+}\r
+\r
/**\r
* \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
* \brief Updates the superblock\r
// Update Primary\r
VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
\r
+ // - Update block groups while we're at it\r
+ VFS_WriteAt(\r
+ Disk->FD,\r
+ Disk->SuperBlock.s_first_data_block * Disk->BlockSize + 1024,\r
+ sizeof(tExt2_Group)*Disk->GroupCount,\r
+ Disk->Groups\r
+ );\r
+ \r
// Secondaries\r
// at Block Group 1, 3^n, 5^n, 7^n\r
\r