+/*\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 read support\r
+ */\r
+#include <common.h>\r
+#include <vfs.h>\r
+#include "fs_ext2.h"\r
+\r
+// === STRUCTURES ===\r
+typedef struct {\r
+ int FD;\r
+ int CacheID;\r
+ vfs_node RootNode;\r
+ \r
+ tExt2_SuperBlock SuperBlock;\r
+ int BlockSize;\r
+ \r
+ int GroupCount;\r
+ tExt2_Group Groups[];\r
+} tExt2_Disk;\r
+\r
+// === PROTOTYPES ===\r
+//Interface Functions\r
+tVFS_Node *Ext2_InitDevice(char *Device, char **Options);\r
+void Ext2_UnMount(tVFS_Node *Node);\r
+Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
+Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos);\r
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *FileName);\r
+tVFS_Node *Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);\r
+ int Ext2_int_GetInode(vfs_node *Node, tExt2_Inode *Inode);\r
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name, Uint64 VfsInode);\r
+ int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);\r
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
+\r
+// === SEMI-GLOBALS ===\r
+tExt2_Disk gExt2_disks[6];\r
+ int giExt2_count = 0;\r
+tVFS_Driver gExt2_FSInfo = {NULL,\r
+ "ext2", 0, Ext2_InitDevice, Ext2_UnMount, NULL\r
+ };\r
+\r
+// === CODE ===\r
+\r
+/**\r
+ * \fn void Ext2_Install()\r
+ * \brief Install the Ext2 Filesystem Driver\r
+ */\r
+void Ext2_Install()\r
+{\r
+ VFS_AddDriver( &gExt2_FSInfo );\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
+ // 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'\n", Device);\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
+ WarningEx("EXT2", "Volume '%s' is not an EXT2 volume\n", Device);\r
+ VFS_Close(fd);\r
+ return NULL;\r
+ }\r
+ \r
+ // Get Group count\r
+ groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
+ //LogF(" Ext2_initDevice: groupCount = %i\n", groupCount);\r
+ \r
+ // Allocate Disk Information\r
+ disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\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
+ //LogF(" Ext2_initDevice: s_log_block_size = 0x%x\n", sb.s_log_block_size);\r
+ disk->BlockSize = 1024 << sb.s_log_block_size;\r
+ \r
+ // Read Group Information\r
+ VFS_ReadAt(disk->fd,\r
+ sb.s_first_data_block * disk->BlockSize + 1024,\r
+ sizeof(tExt2_Group)*groupCount,\r
+ disk->Groups);\r
+ \r
+ #if 0\r
+ Log(" Ext2_initDevice: Block Group 0\n");\r
+ Log(" Ext2_initDevice: .bg_block_bitmap = 0x%x\n", disk->Groups[0].bg_block_bitmap);\r
+ Log(" Ext2_initDevice: .bg_inode_bitmap = 0x%x\n", disk->Groups[0].bg_inode_bitmap);\r
+ Log(" Ext2_initDevice: .bg_inode_table = 0x%x\n", disk->Groups[0].bg_inode_table);\r
+ Log(" Ext2_initDevice: Block Group 1\n");\r
+ Log(" Ext2_initDevice: .bg_block_bitmap = 0x%x\n", disk->Groups[1].bg_block_bitmap);\r
+ Log(" Ext2_initDevice: .bg_inode_bitmap = 0x%x\n", disk->Groups[1].bg_inode_bitmap);\r
+ Log(" Ext2_initDevice: .bg_inode_table = 0x%x\n", 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(vfs_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 0\r
+ Log(" Ext2_InitDevice: inode.i_size = 0x%x\n", inode.i_size);\r
+ Log(" Ext2_InitDevice: inode.i_block[0] = 0x%x\n", inode.i_block[0]);\r
+ #endif\r
+ \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 Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+ * \brief Read from a file\r
+ */\r
+Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ tExt2_Inode inode;\r
+ Uint64 base;\r
+ Uint block;\r
+ Uint64 remLen;\r
+ \r
+ // Get Inode\r
+ Ext2_int_GetInode(Node, &inode);\r
+ \r
+ block = Offset / disk->BlockSize;\r
+ Offset = Offset / disk->BlockSize;\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ \r
+ // Read only block\r
+ if(Length <= disk->BlockSize - Offset)\r
+ {\r
+ VFS_ReadAt( disk->fd, base+Offset, Length, Buffer);\r
+ return Length;\r
+ }\r
+ \r
+ // Read first block\r
+ remLen = Length;\r
+ VFS_ReadAt( disk->fd, base + Offset, disk->BlockSize - Offset, Buffer);\r
+ remLen -= disk->BlockSize - Offset;\r
+ Buffer += disk->BlockSize - Offset;\r
+ block ++;\r
+ \r
+ // Read middle blocks\r
+ while(remLen > disk->BlockSize)\r
+ {\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ VFS_ReadAt( disk->fd, base, disk->BlockSize, Buffer);\r
+ Buffer += disk->BlockSize;\r
+ remLen -= disk->BlockSize;\r
+ block ++;\r
+ }\r
+ \r
+ // Read last block\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ VFS_ReadAt( disk->fd, base, remLen, Buffer);\r
+ \r
+ return Length;\r
+}\r
+\r
+/**\r
+ * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+ * \brief Write to a file\r
+ */\r
+Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ tExt2_Inode inode;\r
+ Uint64 base;\r
+ Uint64 retLen;\r
+ Uint block;\r
+ Uint64 allocSize;\r
+ int bNewBlocks = 0;\r
+ \r
+ Ext2_int_GetInode(Node, &inode);\r
+ \r
+ // Round size up to block size\r
+ // block size is a power of two, so this will work\r
+ allocSize = (inode.i_size + disk->BlockSize) & ~(disk->BlockSize-1);\r
+ if( Offset < allocSize )\r
+ {\r
+ if(Offset + Length > allocSize) {\r
+ bNewBlocks = 1;\r
+ retLen = allocSize - Offset;\r
+ } else\r
+ retLen = Length;\r
+ // Within the allocated space\r
+ block = Offset / disk->BlockSize;\r
+ Offset %= disk->BlockSize;\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ \r
+ // Write only block (if only one)\r
+ if(Offset + retLen <= disk->BlockSize) {\r
+ VFS_WriteAt(disk->fd, base+Offset, retLen, Buffer);\r
+ if(bNewBlocks) return Length;\r
+ goto addBlocks; // Ugh! A goto, but it seems unavoidable\r
+ }\r
+ \r
+ // Write First Block\r
+ VFS_WriteAt(disk->fd, base+Offset, disk->BlockSize-Offset, Buffer);\r
+ Buffer += disk->BlockSize-Offset;\r
+ retLen -= disk->BlockSize-Offset;\r
+ block ++;\r
+ \r
+ // Write middle blocks\r
+ while(retLen > disk->BlockSize)\r
+ {\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ VFS_WriteAt(disk->fd, base, disk->BlockSize, Buffer);\r
+ Buffer += disk->BlockSize;\r
+ retLen -= disk->BlockSize;\r
+ block ++;\r
+ }\r
+ \r
+ // Write last block\r
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
+ VFS_WriteAt(disk->fd, base, retLen, Buffer);\r
+ if(bNewBlocks) return Length; // Writing in only allocated space\r
+ }\r
+ \r
+addBlocks:\r
+ ///\todo Implement block allocation\r
+ WarningEx("EXT2", "File extending is not yet supported");\r
+ \r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int Ext2_CloseFile(vfs_node *Node)\r
+ * \brief Close a file (Remove it from the cache)\r
+ */\r
+int Ext2_CloseFile(tVFS_Node *Node)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ inode_uncacheNode(disk->CacheID, Node->impl);\r
+ return 1;\r
+}\r
+\r
+/**\r
+ \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)\r
+ \brief Reads a directory entry\r
+*/\r
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos)\r
+{\r
+ tExt2_Inode inode;\r
+ char namebuf[EXT2_NAME_LEN+1];\r
+ tExt2_DirEnt dirent;\r
+ Uint64 Base; // Block's Base Address\r
+ int block = 0, ofs = 0;\r
+ int entNum = 0;\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ Uint64 vfsInode = 0;\r
+ tVFS_Node *retNode;\r
+ Uint size;\r
+ \r
+ ENTER("pNode iPos", Node, Pos);\r
+ \r
+ // Read directory's inode\r
+ Ext2_int_GetInode(Node, &inode);\r
+ size = inode.i_size;\r
+ \r
+ LOG("inode.i_block[0] = 0x%x\n", inode.i_block[0]);\r
+ \r
+ // Find Entry\r
+ // Get First Block\r
+ // - Do this ourselves as it is a simple operation\r
+ Base = inode.i_block[0] * disk->BlockSize;\r
+ while(Pos -- && size > 0)\r
+ {\r
+ VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent);\r
+ ofs += dirent.rec_len;\r
+ size -= dirent.rec_len;\r
+ entNum ++;\r
+ \r
+ if(ofs >= disk->BlockSize) {\r
+ block ++;\r
+ if( ofs > disk->BlockSize ) {\r
+ Warning("[EXT2] Directory Entry %i of inode %i ('%s') extends over a block boundary, ignoring\n",\r
+ entNum-1, Node->impl, Node->name);\r
+ }\r
+ ofs = 0;\r
+ Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );\r
+ }\r
+ }\r
+ \r
+ if(size <= 0) return NULL;\r
+ \r
+ // Read Entry\r
+ VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent );\r
+ //LOG(" Ext2_ReadDir: dirent.inode = %i\n", dirent.inode);\r
+ //LOG(" Ext2_ReadDir: dirent.rec_len = %i\n", dirent.rec_len);\r
+ //LOG(" Ext2_ReadDir: dirent.name_len = %i\n", dirent.name_len);\r
+ VFS_ReadAt( disk->fd, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );\r
+ namebuf[ dirent.name_len ] = '\0'; // Cap off string\r
+ \r
+ \r
+ // Ignore . and .. (these are done in the VFS)\r
+ if( (namebuf[0] == '.' && namebuf[1] == '\0')\r
+ || (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0'))\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP; // Skip\r
+ }\r
+ \r
+ LEAVE('s', namebuf);\r
+ // Create new node\r
+ return strdup(namebuf);\r
+}\r
+\r
+/**\r
+ \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)\r
+ \brief Gets information about a file\r
+ \param node vfs node - Parent Node\r
+ \param filename String - Name of file\r
+ \return VFS Node of file\r
+*/\r
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ tExt2_Inode inode;\r
+ char namebuf[EXT2_NAME_LEN+1];\r
+ tExt2_DirEnt dirent;\r
+ Uint64 Base; // Block's Base Address\r
+ int block = 0, ofs = 0;\r
+ int entNum = 0;\r
+ Uint size;\r
+ \r
+ // Read directory's inode\r
+ Ext2_int_GetInode(Node, &inode);\r
+ size = inode.i_size;\r
+ \r
+ // Get First Block\r
+ // - Do this ourselves as it is a simple operation\r
+ Base = inode.i_block[0] * disk->BlockSize;\r
+ // Find File\r
+ while(size > 0)\r
+ {\r
+ VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent);\r
+ VFS_ReadAt( disk->fd, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );\r
+ namebuf[ dirent.name_len ] = '\0'; // Cap off string\r
+ // If it matches, create a node and return it\r
+ if(strcmp(namebuf, Filename) == 0)\r
+ return Ext2_int_CreateNode( disk, dirent.inode, namebuf );\r
+ // Increment pointers\r
+ ofs += dirent.rec_len;\r
+ size -= dirent.rec_len;\r
+ entNum ++;\r
+ \r
+ // Check for end of block\r
+ if(ofs >= disk->BlockSize) {\r
+ block ++;\r
+ if( ofs > disk->BlockSize ) {\r
+ Warnin("[EXT2 ] Directory Entry %i of inode %i ('%s') extends over a block boundary, ignoring\n",\r
+ entNum-1, Node->impl, Node->name);\r
+ }\r
+ ofs = 0;\r
+ Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );\r
+ }\r
+ }\r
+ \r
+ return NULL;\r
+}\r
+\r
+/**\r
+ * \fn tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, char *Name, int Flags)\r
+ * \brief Create a new node\r
+ */\r
+tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, char *Name, int Flags)\r
+{\r
+ return 0;\r
+}\r
+\r
+//==================================\r
+//= INTERNAL FUNCTIONS =\r
+//==================================\r
+\r
+\r
+/**\r
+ \internal\r
+ \fn int Ext2_int_GetInode(vfs_node *Node, tExt2_Inode *Inode)\r
+ \brief Gets the inode descriptor for a node\r
+ \param node node to get the Inode of\r
+ \param inode Destination\r
+*/\r
+int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)\r
+{\r
+ return Ext2_int_ReadInode(Node->ImplPtr, Node->Inode, Inode);\r
+}\r
+\r
+/**\r
+ * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)\r
+ * \brief Create a new VFS Node\r
+ */\r
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)\r
+{\r
+ tExt2_Inode inode;\r
+ tVFS_Node retNode;\r
+ tVFS_Node *tmpNode;\r
+ \r
+ if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )\r
+ return NULL;\r
+ \r
+ if( (tmpNode = inode_getCache(Disk->CacheID, InodeID)) )\r
+ return tmpNode;\r
+ \r
+ \r
+ // Set identifiers\r
+ retNode.Inode = InodeID;\r
+ retNode.ImplPtr = Disk;\r
+ \r
+ // Set file length\r
+ retNode.Size = inode.i_size;\r
+ \r
+ // Set Access Permissions\r
+ retNode.UID = inode.i_uid;\r
+ retNode.GID = inode.i_gid;\r
+ retNode.NumACLs = 3;\r
+ retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);\r
+ \r
+ // Set Function Pointers\r
+ retNode.Read = Ext2_Read;\r
+ retNode.Write = Ext2_Write;\r
+ retNode.Close = Ext2_CloseFile;\r
+ \r
+ switch(inode.i_mode & EXT2_S_IFMT)\r
+ {\r
+ // Symbolic Link\r
+ case EXT2_S_IFLNK:\r
+ retNode.Flags = VFS_FFLAG_SYMLINK;\r
+ break;\r
+ // Regular File\r
+ case EXT2_S_IFREG:\r
+ retNode.flags = 0;\r
+ break;\r
+ // Directory\r
+ case EXT2_S_IFDIR:\r
+ retNode.ReadRir = Ext2_ReadDir;\r
+ retNode.FindDir = Ext2_FindDir;\r
+ retNode.MkNod = Ext2_MkNod;\r
+ //retNode.Relink = Ext2_Relink;\r
+ retNode.Flags = VFS_FFLAG_DIRECTORY;\r
+ break;\r
+ // Unknown, Write protect and hide it to be safe \r
+ default:\r
+ retNode.flags = VFS_FFLAG_READONLY|VFS_FFLAG_HIDDEN;\r
+ break;\r
+ }\r
+ \r
+ // Check if the file should be hidden\r
+ if(Name[0] == '.') retNode.Flags |= VFS_FFLAG_HIDDEN;\r
+ \r
+ // Set Timestamps\r
+ retNode.ATime = now();\r
+ retNode.MTime = inode.i_mtime * 1000;\r
+ retNode.CTime = inode.i_ctime * 1000;\r
+ \r
+ // Save in node cache and return saved node\r
+ return Inode_CacheNode(Disk->CacheID, &retNode);\r
+}\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, Uint InodeId, tExt2_Inode *Inode)\r
+{\r
+ int group, subId;\r
+ \r
+ //LogF("Ext2_int_ReadInode: (Disk=%p, InodeId=%i, Inode=%p)\n", 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
+ //LogF(" Ext2_int_ReadInode: group=%i, subId = %i\n", group, subId);\r
+ \r
+ //Seek to Block - Absolute\r
+ vfs_seek(Disk->fd, Disk->Groups[group].bg_inode_table * Disk->BlockSize, SEEK_SET);\r
+ //Seeek to inode - Relative\r
+ vfs_seek(Disk->fd, sizeof(tExt2_Inode)*subId, SEEK_CUR);\r
+ vfs_read(Disk->fd, sizeof(tExt2_Inode), Inode);\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
+ // 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 < 256) {\r
+ BlockNum = iBlocks[BlockNum];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+ }\r
+ \r
+ // Double Indirect Blocks\r
+ if(BlockNum < 256*256)\r
+ {\r
+ VFS_ReadAt(Disk->fd, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ VFS_ReadAt(Disk->fd, (Uint64)iBlocks[BlockNum/256]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ BlockNum = iBlocks[BlockNum%256];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+ }\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/(256*256)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ VFS_ReadAt(Disk->fd, (Uint64)iBlocks[(BlockNum/256)%256]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ BlockNum = iBlocks[BlockNum%256];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+}\r