Modules/Ext2 - Fixed typo and forgetting to set inode link count in VFS node
[tpg/acess2.git] / KernelLand / Modules / Filesystems / Ext2 / ext2.c
1 /*\r
2  * Acess2 Ext2 Driver\r
3  * - By John Hodge (thePowersGang)\r
4  *\r
5  * ext2.c\r
6  * - Driver core\r
7  */\r
8 #define DEBUG   0\r
9 #define VERSION VER2(0,90)\r
10 #include "ext2_common.h"\r
11 #include <modules.h>\r
12 \r
13 // === IMPORTS ===\r
14 extern tVFS_NodeType    gExt2_DirType;\r
15 \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
30 \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
36         .Name = "ext2",\r
37         .Detect = Ext2_Detect,\r
38         .InitDevice = Ext2_InitDevice,\r
39         .Unmount = Ext2_Unmount,\r
40         .GetNodeFromINode = NULL\r
41         };\r
42 \r
43 // === CODE ===\r
44 /**\r
45  * \fn int Ext2_Install(char **Arguments)\r
46  * \brief Install the Ext2 Filesystem Driver\r
47  */\r
48 int Ext2_Install(char **Arguments)\r
49 {\r
50         VFS_AddDriver( &gExt2_FSInfo );\r
51         return MODULE_ERR_OK;\r
52 }\r
53 \r
54 /**\r
55  * \brief Clean up driver state before unload\r
56  */\r
57 int Ext2_Cleanup(void)\r
58 {\r
59         return 0;\r
60 }\r
61 \r
62 /**\r
63  * Detect if a volume is Ext2 formatted\r
64  */\r
65 int Ext2_Detect(int FD)\r
66 {\r
67         tExt2_SuperBlock        sb;\r
68         size_t  len;\r
69         \r
70         len = VFS_ReadAt(FD, 1024, 1024, &sb);\r
71 \r
72         if( len != 1024 ) {\r
73                 Log_Debug("Ext2", "_Detect: Read failed? (0x%x != 1024)", len);\r
74                 return 0;\r
75         }\r
76         \r
77         switch(sb.s_magic)\r
78         {\r
79         case 0xEF53:\r
80                 return 2;\r
81         default:\r
82                 Log_Debug("Ext2", "_Detect: s_magic = 0x%x", sb.s_magic);\r
83                 return 0;\r
84         }\r
85 }\r
86 \r
87 /**\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
91  \return Root Node\r
92 */\r
93 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
94 {\r
95         tExt2_Disk      *disk;\r
96          int    fd;\r
97          int    groupCount;\r
98         tExt2_SuperBlock        sb;\r
99         tExt2_Inode     inode;\r
100         \r
101         ENTER("sDevice pOptions", Device, Options);\r
102         \r
103         // Open Disk\r
104         fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
105         if(fd == -1) {\r
106                 Log_Warning("EXT2", "Unable to open '%s'", Device);\r
107                 LEAVE('n');\r
108                 return NULL;\r
109         }\r
110         \r
111         // Read Superblock at offset 1024\r
112         VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
113         \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
118                 VFS_Close(fd);\r
119                 LEAVE('n');\r
120                 return NULL;\r
121         }\r
122         \r
123         // Get Group count\r
124         groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
125         LOG("groupCount = %i", groupCount);\r
126         \r
127         // Allocate Disk Information\r
128         disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
129         if(!disk) {\r
130                 Log_Warning("EXT2", "Unable to allocate disk structure");\r
131                 VFS_Close(fd);\r
132                 LEAVE('n');\r
133                 return NULL;\r
134         }\r
135         disk->FD = fd;\r
136         memcpy(&disk->SuperBlock, &sb, 1024);\r
137         disk->GroupCount = groupCount;\r
138         \r
139         // Get an inode cache handle\r
140         disk->CacheID = Inode_GetHandle();\r
141         \r
142         // Get Block Size\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
145         \r
146         // Read Group Information\r
147         LOG("sb,s_first_data_block = %x", sb.s_first_data_block);\r
148         VFS_ReadAt(\r
149                 disk->FD,\r
150                 sb.s_first_data_block * disk->BlockSize + 1024,\r
151                 sizeof(tExt2_Group)*groupCount,\r
152                 disk->Groups\r
153                 );\r
154         \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
163         \r
164         // Get root Inode\r
165         Ext2_int_ReadInode(disk, 2, &inode);\r
166         \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
173 \r
174         disk->RootNode.Type = &gExt2_DirType;\r
175         \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
181         \r
182         #if DEBUG\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
185         #endif\r
186         \r
187         LEAVE('p', &disk->RootNode);\r
188         return &disk->RootNode;\r
189 }\r
190 \r
191 /**\r
192  * \fn void Ext2_Unmount(tVFS_Node *Node)\r
193  * \brief Close a mounted device\r
194  */\r
195 void Ext2_Unmount(tVFS_Node *Node)\r
196 {\r
197         tExt2_Disk      *disk = Node->ImplPtr;\r
198         \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
202         free(disk);\r
203 }\r
204 \r
205 /**\r
206  * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
207  * \brief Close a file (Remove it from the cache)\r
208  */\r
209 void Ext2_CloseFile(tVFS_Node *Node)\r
210 {\r
211         tExt2_Disk      *disk = Node->ImplPtr;\r
212         ENTER("pNode", Node);\r
213 \r
214         if( Mutex_Acquire(&Node->Lock) != 0 )\r
215         {\r
216                 LEAVE('-');\r
217                 return ;\r
218         }\r
219 \r
220         if( Node->Flags & VFS_FFLAG_DIRTY )\r
221         {\r
222                 // Commit changes\r
223                 Log_Warning("Ext2", "TODO: Commit node changes");\r
224         }\r
225 \r
226         int was_not_referenced = (Node->ImplInt == 0);\r
227         tVFS_ACL        *acls = Node->ACLs;\r
228         if( Inode_UncacheNode(disk->CacheID, Node->Inode) == 1 )\r
229         {\r
230                 if( was_not_referenced )\r
231                 {\r
232                         LOG("Removng inode");\r
233                         // Remove inode\r
234                         Log_Warning("Ext2", "TODO: Remove inode when not referenced");\r
235                 }\r
236                 if( acls != &gVFS_ACL_EveryoneRW ) {\r
237                         free(acls);\r
238                 }\r
239                 LOG("Node cleaned");\r
240         }\r
241         else {\r
242                 LOG("Still referenced, releasing lock");\r
243                 Mutex_Release(&Node->Lock);\r
244         }\r
245         LEAVE('-');\r
246         return ;\r
247 }\r
248 \r
249 //==================================\r
250 //=       INTERNAL FUNCTIONS       =\r
251 //==================================\r
252 /**\r
253  * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
254  * \brief Read an inode into memory\r
255  */\r
256 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
257 {\r
258          int    group, subId;\r
259         \r
260         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
261         \r
262         if(InodeId == 0)        return 0;\r
263         \r
264         InodeId --;     // Inodes are numbered starting at 1\r
265         \r
266         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
267         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
268         \r
269         LOG("group=%i, subId = %i", group, subId);\r
270         \r
271         // Read Inode\r
272         VFS_ReadAt(Disk->FD,\r
273                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
274                 sizeof(tExt2_Inode),\r
275                 Inode);\r
276         \r
277         LEAVE('i', 1);\r
278         return 1;\r
279 }\r
280 \r
281 /**\r
282  * \brief Write a modified inode out to disk\r
283  */\r
284 int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
285 {\r
286          int    group, subId;\r
287         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
288         \r
289         if(InodeId == 0) {\r
290                 LEAVE('i', 0);\r
291                 return 0;\r
292         }\r
293         \r
294         InodeId --;     // Inodes are numbered starting at 1\r
295         \r
296         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
297         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
298         \r
299         LOG("group=%i, subId = %i", group, subId);\r
300         \r
301         // Write Inode\r
302         VFS_WriteAt(Disk->FD,\r
303                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
304                 sizeof(tExt2_Inode),\r
305                 Inode\r
306                 );\r
307         \r
308         LEAVE('i', 1);\r
309         return 1;\r
310 }\r
311 \r
312 /**\r
313  * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
314  * \brief Get the address of a block from an inode's list\r
315  * \param Disk  Disk information structure\r
316  * \param Blocks        Pointer to an inode's block list\r
317  * \param BlockNum      Block index in list\r
318  */\r
319 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
320 {\r
321         Uint32  *iBlocks;\r
322          int    dwPerBlock = Disk->BlockSize / 4;\r
323         \r
324         // Direct Blocks\r
325         if(BlockNum < 12)\r
326                 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
327         \r
328         // Single Indirect Blocks\r
329         iBlocks = malloc( Disk->BlockSize );\r
330         VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
331         \r
332         BlockNum -= 12;\r
333         if(BlockNum < dwPerBlock)\r
334         {\r
335                 BlockNum = iBlocks[BlockNum];\r
336                 free(iBlocks);\r
337                 return (Uint64)BlockNum * Disk->BlockSize;\r
338         }\r
339         \r
340         BlockNum -= dwPerBlock;\r
341         // Double Indirect Blocks\r
342         if(BlockNum < dwPerBlock*dwPerBlock)\r
343         {\r
344                 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
345                 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
346                 BlockNum = iBlocks[BlockNum%dwPerBlock];\r
347                 free(iBlocks);\r
348                 return (Uint64)BlockNum * Disk->BlockSize;\r
349         }\r
350         \r
351         BlockNum -= dwPerBlock*dwPerBlock;\r
352         // Triple Indirect Blocks\r
353         VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
354         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
355         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
356         BlockNum = iBlocks[BlockNum%dwPerBlock];\r
357         free(iBlocks);\r
358         return (Uint64)BlockNum * Disk->BlockSize;\r
359 }\r
360 \r
361 /**\r
362  * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
363  * \brief Allocate an inode (from the current group preferably)\r
364  * \param Disk  EXT2 Disk Information Structure\r
365  * \param Parent        Inode ID of the parent (used to locate the child nearby)\r
366  */\r
367 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
368 {\r
369 //      Uint    block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
370         Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
371         return 0;\r
372 }\r
373 \r
374 /**\r
375  * \brief Reduce the reference count on an inode\r
376  */\r
377 void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode)\r
378 {\r
379         \r
380 }\r
381 \r
382 /**\r
383  * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
384  * \brief Updates the superblock\r
385  */\r
386 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
387 {\r
388          int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
389          int    ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
390          int    i;\r
391          \r
392         // Update Primary\r
393         VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
394         \r
395         // Secondaries\r
396         // at Block Group 1, 3^n, 5^n, 7^n\r
397         \r
398         // 1\r
399         if(ngrp <= 1)   return;\r
400         VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
401         \r
402         #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
403         \r
404         // Powers of 3\r
405         for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
406                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
407         \r
408         // Powers of 5\r
409         for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
410                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
411         \r
412         // Powers of 7\r
413         for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
414                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
415 }\r

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