3 * - By John Hodge (thePowersGang)
\r
9 #define VERSION VER2(0,90)
\r
10 #include "ext2_common.h"
\r
11 #include <modules.h>
\r
14 extern tVFS_NodeType gExt2_DirType;
\r
16 // === PROTOTYPES ===
\r
17 int Ext2_Install(char **Arguments);
\r
18 int Ext2_Cleanup(void);
\r
19 // - Interface Functions
\r
20 int Ext2_Detect(int FD);
\r
21 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options);
\r
22 void Ext2_Unmount(tVFS_Node *Node);
\r
23 void Ext2_CloseFile(tVFS_Node *Node);
\r
24 // - Internal Helpers
\r
25 int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);
\r
26 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
\r
27 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);
\r
28 void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode);
\r
29 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
\r
31 // === SEMI-GLOBALS ===
\r
32 MODULE_DEFINE(0, VERSION, FS_Ext2, Ext2_Install, Ext2_Cleanup);
\r
33 tExt2_Disk gExt2_disks[6];
\r
34 int giExt2_count = 0;
\r
35 tVFS_Driver gExt2_FSInfo = {
\r
37 .Detect = Ext2_Detect,
\r
38 .InitDevice = Ext2_InitDevice,
\r
39 .Unmount = Ext2_Unmount,
\r
40 .GetNodeFromINode = NULL
\r
45 * \fn int Ext2_Install(char **Arguments)
\r
46 * \brief Install the Ext2 Filesystem Driver
\r
48 int Ext2_Install(char **Arguments)
\r
50 VFS_AddDriver( &gExt2_FSInfo );
\r
51 return MODULE_ERR_OK;
\r
55 * \brief Clean up driver state before unload
\r
57 int Ext2_Cleanup(void)
\r
63 * Detect if a volume is Ext2 formatted
\r
65 int Ext2_Detect(int FD)
\r
67 tExt2_SuperBlock sb;
\r
70 len = VFS_ReadAt(FD, 1024, 1024, &sb);
\r
73 Log_Debug("Ext2", "_Detect: Read failed? (0x%x != 1024)", len);
\r
82 Log_Debug("Ext2", "_Detect: s_magic = 0x%x", sb.s_magic);
\r
88 \brief Initializes a device to be read by by the driver
\r
89 \param Device String - Device to read from
\r
90 \param Options NULL Terminated array of option strings
\r
93 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)
\r
98 tExt2_SuperBlock sb;
\r
101 ENTER("sDevice pOptions", Device, Options);
\r
104 fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device
\r
106 Log_Warning("EXT2", "Unable to open '%s'", Device);
\r
111 // Read Superblock at offset 1024
\r
112 VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock
\r
114 // Sanity Check Magic value
\r
115 if(sb.s_magic != 0xEF53) {
\r
116 Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",
\r
117 Device, sb.s_magic);
\r
124 groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);
\r
125 LOG("groupCount = %i", groupCount);
\r
127 // Allocate Disk Information
\r
128 disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);
\r
130 Log_Warning("EXT2", "Unable to allocate disk structure");
\r
136 memcpy(&disk->SuperBlock, &sb, 1024);
\r
137 disk->GroupCount = groupCount;
\r
139 // Get an inode cache handle
\r
140 disk->CacheID = Inode_GetHandle();
\r
143 disk->BlockSize = 1024 << sb.s_log_block_size;
\r
144 LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size);
\r
146 // Read Group Information
\r
147 LOG("sb,s_first_data_block = %x", sb.s_first_data_block);
\r
150 sb.s_first_data_block * disk->BlockSize + 1024,
\r
151 sizeof(tExt2_Group)*groupCount,
\r
155 LOG("Block Group 0");
\r
156 LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);
\r
157 LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);
\r
158 LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);
\r
159 LOG("Block Group 1");
\r
160 LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);
\r
161 LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);
\r
162 LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);
\r
165 Ext2_int_ReadInode(disk, 2, &inode);
\r
167 // Create Root Node
\r
168 memset(&disk->RootNode, 0, sizeof(tVFS_Node));
\r
169 disk->RootNode.Inode = 2; // Root inode ID
\r
170 disk->RootNode.ImplPtr = disk; // Save disk pointer
\r
171 disk->RootNode.Size = -1; // Fill in later (on readdir)
\r
172 disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;
\r
174 disk->RootNode.Type = &gExt2_DirType;
\r
176 // Complete root node
\r
177 disk->RootNode.UID = inode.i_uid;
\r
178 disk->RootNode.GID = inode.i_gid;
\r
179 disk->RootNode.NumACLs = 1;
\r
180 disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;
\r
183 LOG("inode.i_size = 0x%x", inode.i_size);
\r
184 LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
\r
187 LEAVE('p', &disk->RootNode);
\r
188 return &disk->RootNode;
\r
192 * \fn void Ext2_Unmount(tVFS_Node *Node)
\r
193 * \brief Close a mounted device
\r
195 void Ext2_Unmount(tVFS_Node *Node)
\r
197 tExt2_Disk *disk = Node->ImplPtr;
\r
199 VFS_Close( disk->FD );
\r
200 Inode_ClearCache( disk->CacheID );
\r
201 memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));
\r
206 * \fn void Ext2_CloseFile(tVFS_Node *Node)
\r
207 * \brief Close a file (Remove it from the cache)
\r
209 void Ext2_CloseFile(tVFS_Node *Node)
\r
211 tExt2_Disk *disk = Node->ImplPtr;
\r
213 if( Mutex_Acquire(&Node->Lock) != 0 )
\r
218 if( Node->Flags & VFS_FFLAG_DIRTY )
\r
223 int was_not_referenced = (Node->ImplInt == 0);
\r
224 tVFS_ACL *acls = Node->ACLs;
\r
225 if( Inode_UncacheNode(disk->CacheID, Node->Inode) )
\r
227 if( was_not_referenced )
\r
231 if( acls != &gVFS_ACL_EveryoneRW ) {
\r
238 //==================================
\r
239 //= INTERNAL FUNCTIONS =
\r
240 //==================================
\r
242 * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
\r
243 * \brief Read an inode into memory
\r
245 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)
\r
249 ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);
\r
251 if(InodeId == 0) return 0;
\r
253 InodeId --; // Inodes are numbered starting at 1
\r
255 group = InodeId / Disk->SuperBlock.s_inodes_per_group;
\r
256 subId = InodeId % Disk->SuperBlock.s_inodes_per_group;
\r
258 LOG("group=%i, subId = %i", group, subId);
\r
261 VFS_ReadAt(Disk->FD,
\r
262 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,
\r
263 sizeof(tExt2_Inode),
\r
271 * \brief Write a modified inode out to disk
\r
273 int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)
\r
276 ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);
\r
283 InodeId --; // Inodes are numbered starting at 1
\r
285 group = InodeId / Disk->SuperBlock.s_inodes_per_group;
\r
286 subId = InodeId % Disk->SuperBlock.s_inodes_per_group;
\r
288 LOG("group=%i, subId = %i", group, subId);
\r
291 VFS_WriteAt(Disk->FD,
\r
292 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,
\r
293 sizeof(tExt2_Inode),
\r
302 * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
\r
303 * \brief Get the address of a block from an inode's list
\r
304 * \param Disk Disk information structure
\r
305 * \param Blocks Pointer to an inode's block list
\r
306 * \param BlockNum Block index in list
\r
308 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
\r
311 int dwPerBlock = Disk->BlockSize / 4;
\r
315 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;
\r
317 // Single Indirect Blocks
\r
318 iBlocks = malloc( Disk->BlockSize );
\r
319 VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
322 if(BlockNum < dwPerBlock)
\r
324 BlockNum = iBlocks[BlockNum];
\r
326 return (Uint64)BlockNum * Disk->BlockSize;
\r
329 BlockNum -= dwPerBlock;
\r
330 // Double Indirect Blocks
\r
331 if(BlockNum < dwPerBlock*dwPerBlock)
\r
333 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
334 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
335 BlockNum = iBlocks[BlockNum%dwPerBlock];
\r
337 return (Uint64)BlockNum * Disk->BlockSize;
\r
340 BlockNum -= dwPerBlock*dwPerBlock;
\r
341 // Triple Indirect Blocks
\r
342 VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
343 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
344 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
345 BlockNum = iBlocks[BlockNum%dwPerBlock];
\r
347 return (Uint64)BlockNum * Disk->BlockSize;
\r
351 * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
\r
352 * \brief Allocate an inode (from the current group preferably)
\r
353 * \param Disk EXT2 Disk Information Structure
\r
354 * \param Parent Inode ID of the parent (used to locate the child nearby)
\r
356 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
\r
358 // Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;
\r
359 Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");
\r
364 * \brief Reduce the reference count on an inode
\r
366 void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode)
\r
372 * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
\r
373 * \brief Updates the superblock
\r
375 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
\r
377 int bpg = Disk->SuperBlock.s_blocks_per_group;
\r
378 int ngrp = Disk->SuperBlock.s_blocks_count / bpg;
\r
382 VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);
\r
385 // at Block Group 1, 3^n, 5^n, 7^n
\r
388 if(ngrp <= 1) return;
\r
389 VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
\r
391 #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)
\r
394 for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )
\r
395 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
\r
398 for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )
\r
399 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
\r
402 for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )
\r
403 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
\r