Various changes, most of them involving the FAT and Ext2 Drivers, adding write support
[tpg/acess2.git] / Modules / Filesystems / Ext2 / ext2.c
diff --git a/Modules/Filesystems/Ext2/ext2.c b/Modules/Filesystems/Ext2/ext2.c
new file mode 100644 (file)
index 0000000..c2c2df9
--- /dev/null
@@ -0,0 +1,330 @@
+/*\r
+ * Acess OS\r
+ * Ext2 Driver Version 1\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
+#include "ext2_common.h"\r
+#include <modules.h>\r
+\r
+// === PROTOTYPES ===\r
+ int   Ext2_Install(char **Arguments);\r
+// Interface Functions\r
+tVFS_Node      *Ext2_InitDevice(char *Device, 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
+\r
+// === SEMI-GLOBALS ===\r
+MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);\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
+       };\r
+\r
+// === CODE ===\r
+\r
+/**\r
+ * \fn int Ext2_Install(char **Arguments)\r
+ * \brief Install the Ext2 Filesystem Driver\r
+ */\r
+int Ext2_Install(char **Arguments)\r
+{\r
+       VFS_AddDriver( &gExt2_FSInfo );\r
+       return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ \fn tVFS_Node *Ext2_InitDevice(char *Device, char **Options)\r
+ \brief Initializes a device to be read by by the driver\r
+ \param Device String - Device to read from\r
+ \param Options        NULL Terminated array of option strings\r
+ \return Root Node\r
+*/\r
+tVFS_Node *Ext2_InitDevice(char *Device, char **Options)\r
+{\r
+       tExt2_Disk      *disk;\r
+        int    fd;\r
+        int    groupCount;\r
+       tExt2_SuperBlock        sb;\r
+       tExt2_Inode     inode;\r
+       \r
+       ENTER("sDevice pOptions", Device, Options);\r
+       \r
+       // Open Disk\r
+       fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
+       if(fd == -1) {\r
+               Warning("[EXT2 ] Unable to open '%s'", Device);\r
+               LEAVE('n');\r
+               return NULL;\r
+       }\r
+       \r
+       // Read Superblock at offset 1024\r
+       VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
+       \r
+       // Sanity Check Magic value\r
+       if(sb.s_magic != 0xEF53) {\r
+               Warning("[EXT2 ] Volume '%s' is not an EXT2 volume", Device);\r
+               VFS_Close(fd);\r
+               LEAVE('n');\r
+               return NULL;\r
+       }\r
+       \r
+       // Get Group count\r
+       groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
+       LOG("groupCount = %i", groupCount);\r
+       \r
+       // Allocate Disk Information\r
+       disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
+       if(!disk) {\r
+               Warning("[EXT2 ] Unable to allocate disk structure");\r
+               VFS_Close(fd);\r
+               LEAVE('n');\r
+               return NULL;\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
+       \r
+       // Get Block Size\r
+       LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
+       disk->BlockSize = 1024 << sb.s_log_block_size;\r
+       \r
+       // Read Group Information\r
+       VFS_ReadAt(\r
+               disk->FD,\r
+               sb.s_first_data_block * disk->BlockSize + 1024,\r
+               sizeof(tExt2_Group)*groupCount,\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_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
+       LOG("Block Group 1");\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
+       \r
+       // Create Root Node\r
+       memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
+       disk->RootNode.Inode = 2;       // Root inode ID\r
+       disk->RootNode.ImplPtr = disk;  // Save disk pointer\r
+       disk->RootNode.Size = -1;       // Fill in later (on readdir)\r
+       disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
+       \r
+       disk->RootNode.ReadDir = Ext2_ReadDir;\r
+       disk->RootNode.FindDir = Ext2_FindDir;\r
+       //disk->RootNode.Relink = Ext2_Relink;\r
+       \r
+       // Complete root node\r
+       disk->RootNode.UID = inode.i_uid;\r
+       disk->RootNode.GID = inode.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
+       #endif\r
+       \r
+       LEAVE('p', &disk->RootNode);\r
+       return &disk->RootNode;\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_Unmount(tVFS_Node *Node)\r
+ * \brief Close a mounted device\r
+ */\r
+void Ext2_Unmount(tVFS_Node *Node)\r
+{\r
+       tExt2_Disk      *disk = Node->ImplPtr;\r
+       \r
+       VFS_Close( disk->FD );\r
+       Inode_ClearCache( disk->CacheID );\r
+       memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
+       free(disk);\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
+ * \brief Close a file (Remove it from the cache)\r
+ */\r
+void Ext2_CloseFile(tVFS_Node *Node)\r
+{\r
+       tExt2_Disk      *disk = Node->ImplPtr;\r
+       Inode_UncacheNode(disk->CacheID, Node->Inode);\r
+       return ;\r
+}\r
+\r
+//==================================\r
+//=       INTERNAL FUNCTIONS       =\r
+//==================================\r
+/**\r
+ * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
+ * \brief Read an inode into memory\r
+ */\r
+int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
+{\r
+        int    group, subId;\r
+       \r
+       //ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
+       \r
+       if(InodeId == 0)        return 0;\r
+       \r
+       InodeId --;     // Inodes are numbered starting at 1\r
+       \r
+       group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
+       subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
+       \r
+       //LOG("group=%i, subId = %i", group, subId);\r
+       \r
+       // Read Inode\r
+       VFS_ReadAt(Disk->FD,\r
+               Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
+               sizeof(tExt2_Inode),\r
+               Inode);\r
+       \r
+       //LEAVE('i', 1);\r
+       return 1;\r
+}\r
+\r
+/**\r
+ * \brief Write a modified inode out to disk\r
+ */\r
+int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
+{\r
+        int    group, subId;\r
+       ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
+       \r
+       if(InodeId == 0)        return 0;\r
+       \r
+       InodeId --;     // Inodes are numbered starting at 1\r
+       \r
+       group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
+       subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
+       \r
+       LOG("group=%i, subId = %i", group, subId);\r
+       \r
+       // Write Inode\r
+       VFS_WriteAt(Disk->FD,\r
+               Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
+               sizeof(tExt2_Inode),\r
+               Inode);\r
+       \r
+       LEAVE('i', 1);\r
+       return 1;\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
+ * \param Disk Disk information structure\r
+ * \param Blocks       Pointer to an inode's block list\r
+ * \param BlockNum     Block index in list\r
+ */\r
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
+{\r
+       Uint32  *iBlocks;\r
+        int    dwPerBlock = Disk->BlockSize / 4;\r
+       \r
+       // Direct Blocks\r
+       if(BlockNum < 12)\r
+               return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
+       \r
+       // Single Indirect Blocks\r
+       iBlocks = malloc( Disk->BlockSize );\r
+       VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+       \r
+       BlockNum -= 12;\r
+       if(BlockNum < dwPerBlock)\r
+       {\r
+               BlockNum = iBlocks[BlockNum];\r
+               free(iBlocks);\r
+               return (Uint64)BlockNum * Disk->BlockSize;\r
+       }\r
+       \r
+       BlockNum -= dwPerBlock;\r
+       // Double Indirect Blocks\r
+       if(BlockNum < dwPerBlock*dwPerBlock)\r
+       {\r
+               VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+               VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+               BlockNum = iBlocks[BlockNum%dwPerBlock];\r
+               free(iBlocks);\r
+               return (Uint64)BlockNum * Disk->BlockSize;\r
+       }\r
+       \r
+       BlockNum -= dwPerBlock*dwPerBlock;\r
+       // Triple Indirect Blocks\r
+       VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+       VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+       VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+       BlockNum = iBlocks[BlockNum%dwPerBlock];\r
+       free(iBlocks);\r
+       return (Uint64)BlockNum * Disk->BlockSize;\r
+}\r
+\r
+/**\r
+ * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
+ * \brief Allocate an inode (from the current group preferably)\r
+ * \param Disk EXT2 Disk Information Structure\r
+ * \param Parent       Inode ID of the parent (used to locate the child nearby)\r
+ */\r
+Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
+{\r
+//     Uint    block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
+       return 0;\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
+ * \brief Updates the superblock\r
+ */\r
+void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
+{\r
+        int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
+        int    ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
+        int    i;\r
+        \r
+       // Update Primary\r
+       VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
+       \r
+       // Secondaries\r
+       // at Block Group 1, 3^n, 5^n, 7^n\r
+       \r
+       // 1\r
+       if(ngrp <= 1)   return;\r
+       VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+       \r
+       // Powers of 3\r
+       for( i = 3; i < ngrp; i *= 3 )\r
+               VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+       \r
+       // Powers of 5\r
+       for( i = 5; i < ngrp; i *= 5 )\r
+               VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+       \r
+       // Powers of 7\r
+       for( i = 7; i < ngrp; i *= 7 )\r
+               VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+}\r

UCC git Repository :: git.ucc.asn.au