/*\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
+#define MIN_BLOCKS_PER_GROUP 2\r
+#define MAX_BLOCK_LOG_SIZE 10 // 1024 << 10 = 1MiB\r
+\r
// === IMPORTS ===\r
extern tVFS_NodeType gExt2_DirType;\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
+// - 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_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 = NULL\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
LEAVE('n');\r
return NULL;\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->CacheID = Inode_GetHandle();\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
\r
LEAVE('p', &disk->RootNode);\r
return &disk->RootNode;\r
+_error:\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
+ Log_Warning("Ext2", "TODO: Commit node changes");\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");\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
*/\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