Merge branch 'master' of git://cadel.mutabah.net/acess2
[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 #define MIN_BLOCKS_PER_GROUP    2\r
14 #define MAX_BLOCK_LOG_SIZE      10      // 1024 << 10 = 1MiB\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 tVFS_Node       *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode);\r
25 // - Internal Helpers\r
26  int    Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
27 void    Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode);\r
28 Uint64  Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
29 Uint32  Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
30 void    Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode);\r
31 void    Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
32 \r
33 // === SEMI-GLOBALS ===\r
34 MODULE_DEFINE(0, VERSION, FS_Ext2, Ext2_Install, Ext2_Cleanup);\r
35 tExt2_Disk      gExt2_disks[6];\r
36  int    giExt2_count = 0;\r
37 tVFS_Driver     gExt2_FSInfo = {\r
38         .Name = "ext2",\r
39         .Detect = Ext2_Detect,\r
40         .InitDevice = Ext2_InitDevice,\r
41         .Unmount = Ext2_Unmount,\r
42         .GetNodeFromINode = Ext2_GetNodeFromINode\r
43         };\r
44 \r
45 // === CODE ===\r
46 /**\r
47  * \fn int Ext2_Install(char **Arguments)\r
48  * \brief Install the Ext2 Filesystem Driver\r
49  */\r
50 int Ext2_Install(char **Arguments)\r
51 {\r
52         VFS_AddDriver( &gExt2_FSInfo );\r
53         return MODULE_ERR_OK;\r
54 }\r
55 \r
56 /**\r
57  * \brief Clean up driver state before unload\r
58  */\r
59 int Ext2_Cleanup(void)\r
60 {\r
61         return 0;\r
62 }\r
63 \r
64 /**\r
65  * Detect if a volume is Ext2 formatted\r
66  */\r
67 int Ext2_Detect(int FD)\r
68 {\r
69         tExt2_SuperBlock        sb;\r
70         size_t  len;\r
71         \r
72         len = VFS_ReadAt(FD, 1024, 1024, &sb);\r
73 \r
74         if( len != 1024 ) {\r
75                 Log_Debug("Ext2", "_Detect: Read failed? (0x%x != 1024)", len);\r
76                 return 0;\r
77         }\r
78         \r
79         switch(sb.s_magic)\r
80         {\r
81         case 0xEF53:\r
82                 return 2;\r
83         default:\r
84                 Log_Debug("Ext2", "_Detect: s_magic = 0x%x", sb.s_magic);\r
85                 return 0;\r
86         }\r
87 }\r
88 \r
89 /**\r
90  \brief Initializes a device to be read by by the driver\r
91  \param Device  String - Device to read from\r
92  \param Options NULL Terminated array of option strings\r
93  \return Root Node\r
94 */\r
95 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
96 {\r
97         tExt2_Disk      *disk = NULL;\r
98          int    fd;\r
99          int    groupCount;\r
100         tExt2_SuperBlock        sb;\r
101         \r
102         ENTER("sDevice pOptions", Device, Options);\r
103         \r
104         // Open Disk\r
105         fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
106         if(fd == -1) {\r
107                 Log_Warning("EXT2", "Unable to open '%s'", Device);\r
108                 LEAVE('n');\r
109                 return NULL;\r
110         }\r
111         \r
112         // Read Superblock at offset 1024\r
113         VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
114         \r
115         // Sanity Check Magic value\r
116         if(sb.s_magic != 0xEF53) {\r
117                 Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
118                         Device, sb.s_magic);\r
119                 goto _error;\r
120         }\r
121 \r
122         if( sb.s_blocks_per_group < MIN_BLOCKS_PER_GROUP ) {\r
123                 Log_Warning("Ext2", "Blocks per group is too small (%i < %i)",\r
124                         sb.s_blocks_per_group, MIN_BLOCKS_PER_GROUP);\r
125                 goto _error;\r
126         }       \r
127 \r
128         // Get Group count\r
129         groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
130         LOG("groupCount = %i", groupCount);\r
131         \r
132         // Allocate Disk Information\r
133         disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
134         if(!disk) {\r
135                 Log_Warning("EXT2", "Unable to allocate disk structure");\r
136                 goto _error;\r
137         }\r
138         disk->FD = fd;\r
139         memcpy(&disk->SuperBlock, &sb, 1024);\r
140         disk->GroupCount = groupCount;\r
141         \r
142         // Get an inode cache handle\r
143         disk->CacheID = Inode_GetHandle(NULL);\r
144         \r
145         // Get Block Size\r
146         if( sb.s_log_block_size > MAX_BLOCK_LOG_SIZE ) {\r
147                 Log_Warning("Ext2", "Block size (log2) too large (%i > %i)",\r
148                         sb.s_log_block_size, MAX_BLOCK_LOG_SIZE);\r
149                 goto _error;\r
150         }\r
151         disk->BlockSize = 1024 << sb.s_log_block_size;\r
152         LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size);\r
153         \r
154         // Read Group Information\r
155         LOG("sb,s_first_data_block = %x", sb.s_first_data_block);\r
156         VFS_ReadAt(\r
157                 disk->FD,\r
158                 sb.s_first_data_block * disk->BlockSize + 1024,\r
159                 sizeof(tExt2_Group)*groupCount,\r
160                 disk->Groups\r
161                 );\r
162         \r
163         LOG("Block Group 0");\r
164         LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
165         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
166         LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
167         LOG("Block Group 1");\r
168         LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
169         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
170         LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
171         \r
172         // Get root Inode\r
173         Ext2_int_ReadInode(disk, 2, &disk->RootInode);\r
174         \r
175         // Create Root Node\r
176         memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
177         disk->RootNode.Inode = 2;       // Root inode ID\r
178         disk->RootNode.ImplPtr = disk;  // Save disk pointer\r
179         disk->RootNode.Size = -1;       // Fill in later (on readdir)\r
180         disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
181 \r
182         disk->RootNode.Type = &gExt2_DirType;\r
183         \r
184         // Complete root node\r
185         disk->RootNode.UID = disk->RootInode.i_uid;\r
186         disk->RootNode.GID = disk->RootInode.i_gid;\r
187         disk->RootNode.NumACLs = 1;\r
188         disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
189         \r
190         #if DEBUG\r
191         LOG("inode.i_size = 0x%x", disk->RootInode.i_size);\r
192         LOG("inode.i_block[0] = 0x%x", disk->RootInode.i_block[0]);\r
193         #endif\r
194         \r
195         LEAVE('p', &disk->RootNode);\r
196         return &disk->RootNode;\r
197 _error:\r
198         if( disk )\r
199                 free(disk);\r
200         VFS_Close(fd);\r
201         LEAVE('n');\r
202         return NULL;\r
203 }\r
204 \r
205 /**\r
206  * \fn void Ext2_Unmount(tVFS_Node *Node)\r
207  * \brief Close a mounted device\r
208  */\r
209 void Ext2_Unmount(tVFS_Node *Node)\r
210 {\r
211         tExt2_Disk      *disk = Node->ImplPtr;\r
212         \r
213         VFS_Close( disk->FD );\r
214         Inode_ClearCache( disk->CacheID );\r
215         memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
216         free(disk);\r
217 }\r
218 \r
219 /**\r
220  * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
221  * \brief Close a file (Remove it from the cache)\r
222  */\r
223 void Ext2_CloseFile(tVFS_Node *Node)\r
224 {\r
225         tExt2_Disk      *disk = Node->ImplPtr;\r
226         ENTER("pNode", Node);\r
227 \r
228         if( Mutex_Acquire(&Node->Lock) != 0 )\r
229         {\r
230                 LEAVE('-');\r
231                 return ;\r
232         }\r
233 \r
234         if( Node->Flags & VFS_FFLAG_DIRTY )\r
235         {\r
236                 // Commit changes\r
237                 Ext2_int_WritebackNode(disk, Node);\r
238                 Node->Flags &= ~VFS_FFLAG_DIRTY;\r
239         }\r
240 \r
241         int was_not_referenced = (Node->ImplInt == 0);\r
242         tVFS_ACL        *acls = Node->ACLs;\r
243         if( Inode_UncacheNode(disk->CacheID, Node->Inode) == 1 )\r
244         {\r
245                 if( was_not_referenced )\r
246                 {\r
247                         LOG("Removng inode");\r
248                         // Remove inode\r
249                         Log_Warning("Ext2", "TODO: Remove inode when not referenced (%x)", (Uint32)Node->Inode);\r
250                 }\r
251                 if( acls != &gVFS_ACL_EveryoneRW ) {\r
252                         free(acls);\r
253                 }\r
254                 LOG("Node cleaned");\r
255         }\r
256         else {\r
257                 LOG("Still referenced, releasing lock");\r
258                 Mutex_Release(&Node->Lock);\r
259         }\r
260         LEAVE('-');\r
261         return ;\r
262 }\r
263 \r
264 tVFS_Node *Ext2_GetNodeFromINode(tVFS_Node *RootNode, Uint64 Inode)\r
265 {\r
266         return Ext2_int_CreateNode(RootNode->ImplPtr, Inode);\r
267 }\r
268 \r
269 //==================================\r
270 //=       INTERNAL FUNCTIONS       =\r
271 //==================================\r
272 /**\r
273  * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
274  * \brief Read an inode into memory\r
275  */\r
276 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
277 {\r
278          int    group, subId;\r
279         \r
280         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
281         \r
282         if(InodeId == 0)        return 0;\r
283         \r
284         InodeId --;     // Inodes are numbered starting at 1\r
285         \r
286         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
287         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
288         \r
289         LOG("group=%i, subId = %i", group, subId);\r
290         \r
291         // Read Inode\r
292         VFS_ReadAt(Disk->FD,\r
293                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
294                 sizeof(tExt2_Inode),\r
295                 Inode);\r
296         \r
297         LEAVE('i', 1);\r
298         return 1;\r
299 }\r
300 \r
301 /**\r
302  * \brief Write a modified inode out to disk\r
303  */\r
304 int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
305 {\r
306          int    group, subId;\r
307         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
308         \r
309         if(InodeId == 0) {\r
310                 LEAVE('i', 0);\r
311                 return 0;\r
312         }\r
313 \r
314         Ext2_int_DumpInode(Disk, InodeId, Inode);       \r
315 \r
316         InodeId --;     // Inodes are numbered starting at 1\r
317         \r
318         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
319         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
320         \r
321         LOG("group=%i, subId = %i", group, subId);\r
322         \r
323         // Write Inode\r
324         VFS_WriteAt(Disk->FD,\r
325                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
326                 sizeof(tExt2_Inode),\r
327                 Inode\r
328                 );\r
329         \r
330         LEAVE('i', 1);\r
331         return 1;\r
332 }\r
333 \r
334 /**\r
335  * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)\r
336  * \brief Create a new VFS Node\r
337  */\r
338 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)\r
339 {\r
340         struct {\r
341                 tVFS_Node       retNode;\r
342                 tExt2_Inode     inode;\r
343         } data;\r
344         tVFS_Node       *node = &data.retNode;\r
345         tExt2_Inode     *in = &data.inode;\r
346         \r
347         if( !Ext2_int_ReadInode(Disk, InodeID, &data.inode) )\r
348                 return NULL;\r
349         \r
350         if( (node = Inode_GetCache(Disk->CacheID, InodeID)) )\r
351                 return node;\r
352         node = &data.retNode;\r
353 \r
354         memset(node, 0, sizeof(*node));\r
355         \r
356         // Set identifiers\r
357         node->Inode = InodeID;\r
358         node->ImplPtr = Disk;\r
359         node->ImplInt = in->i_links_count;\r
360         if( in->i_links_count == 0 ) {\r
361                 Log_Notice("Ext2", "Inode %p:%x is not referenced, bug?", Disk, InodeID);\r
362         }\r
363         \r
364         // Set file length\r
365         node->Size = in->i_size;\r
366         \r
367         // Set Access Permissions\r
368         node->UID = in->i_uid;\r
369         node->GID = in->i_gid;\r
370         node->NumACLs = 3;\r
371         node->ACLs = VFS_UnixToAcessACL(in->i_mode & 0777, in->i_uid, in->i_gid);\r
372         \r
373         //  Set Function Pointers\r
374         node->Type = &gExt2_FileType;\r
375         \r
376         switch(in->i_mode & EXT2_S_IFMT)\r
377         {\r
378         // Symbolic Link\r
379         case EXT2_S_IFLNK:\r
380                 node->Flags = VFS_FFLAG_SYMLINK;\r
381                 break;\r
382         // Regular File\r
383         case EXT2_S_IFREG:\r
384                 node->Flags = 0;\r
385                 node->Size |= (Uint64)in->i_dir_acl << 32;\r
386                 break;\r
387         // Directory\r
388         case EXT2_S_IFDIR:\r
389                 node->Type = &gExt2_DirType;\r
390                 node->Flags = VFS_FFLAG_DIRECTORY;\r
391                 node->Data = calloc( sizeof(Uint16), DivUp(node->Size, Disk->BlockSize) );\r
392                 break;\r
393         // Unknown, Write protect it to be safe \r
394         default:\r
395                 node->Flags = VFS_FFLAG_READONLY;\r
396                 break;\r
397         }\r
398         \r
399         // Set Timestamps\r
400         node->ATime = in->i_atime * 1000;\r
401         node->MTime = in->i_mtime * 1000;\r
402         node->CTime = in->i_ctime * 1000;\r
403         \r
404         // Save in node cache and return saved node\r
405         return Inode_CacheNodeEx(Disk->CacheID, &data.retNode, sizeof(data));\r
406 }\r
407 \r
408 int Ext2_int_WritebackNode(tExt2_Disk *Disk, tVFS_Node *Node)\r
409 {\r
410         tExt2_Inode     *inode = (void*)(Node+1);\r
411 \r
412         if( Disk != Node->ImplPtr ) {\r
413                 Log_Error("Ext2", "Ext2_int_WritebackNode - Disk != Node->ImplPtr");\r
414                 return -1;\r
415         }\r
416 \r
417         if( Node->Flags & VFS_FFLAG_SYMLINK ) {\r
418                 inode->i_mode = EXT2_S_IFLNK;\r
419         }\r
420         else if( Node->Flags & VFS_FFLAG_DIRECTORY ) {\r
421                 inode->i_mode = EXT2_S_IFDIR;\r
422         }\r
423         else if( Node->Flags & VFS_FFLAG_READONLY ) {\r
424                 Log_Notice("Ext2", "Not writing back readonly inode %p:%x", Disk, Node->Inode);\r
425                 return 1;\r
426         }\r
427         else {\r
428                 inode->i_mode = EXT2_S_IFREG;\r
429                 inode->i_dir_acl = Node->Size >> 32;\r
430         }\r
431 \r
432         inode->i_size = Node->Size & 0xFFFFFFFF;\r
433         inode->i_links_count = Node->ImplInt;\r
434 \r
435         inode->i_uid = Node->UID;\r
436         inode->i_gid = Node->GID;\r
437 \r
438         inode->i_atime = Node->ATime / 1000;\r
439         inode->i_mtime = Node->MTime / 1000;\r
440         inode->i_ctime = Node->CTime / 1000;\r
441 \r
442         // TODO: Compact ACLs into unix mode\r
443         Log_Warning("Ext2", "TODO: Support converting Acess ACLs into unix modes");\r
444         inode->i_mode |= 777;\r
445 \r
446         Ext2_int_WriteInode(Disk, Node->Inode, inode);\r
447 \r
448         return 0;\r
449 }\r
450 \r
451 void Ext2_int_DumpInode(tExt2_Disk *Disk, Uint32 InodeID, tExt2_Inode *Inode)\r
452 {\r
453         LOG("%p[Inode %i] = {", Disk, InodeID);\r
454         LOG(" .i_mode = 0%04o", Inode->i_mode);\r
455         LOG(" .i_uid:i_gid = %i:%i", Inode->i_uid, Inode->i_gid);\r
456         LOG(" .i_size = 0x%x", Inode->i_size);\r
457         LOG(" .i_block[0:3] = {0x%x,0x%x,0x%x,0x%x}",\r
458                 Inode->i_block[0], Inode->i_block[1], Inode->i_block[2], Inode->i_block[3]);\r
459         LOG(" .i_block[4:7] = {0x%x,0x%x,0x%x,0x%x}",\r
460                 Inode->i_block[4], Inode->i_block[5], Inode->i_block[6], Inode->i_block[7]);\r
461         LOG(" .i_block[8:11] = {0x%x,0x%x,0x%x,0x%x}",\r
462                 Inode->i_block[8], Inode->i_block[6], Inode->i_block[10], Inode->i_block[11]);\r
463         LOG(" .i_block[12:14] = {0x%x,0x%x,0x%x}",\r
464                 Inode->i_block[12], Inode->i_block[13], Inode->i_block[14]);\r
465         LOG("}");\r
466 }\r
467 \r
468 /**\r
469  * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
470  * \brief Get the address of a block from an inode's list\r
471  * \param Disk  Disk information structure\r
472  * \param Blocks        Pointer to an inode's block list\r
473  * \param BlockNum      Block index in list\r
474  */\r
475 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
476 {\r
477         Uint32  *iBlocks;\r
478          int    dwPerBlock = Disk->BlockSize / 4;\r
479         \r
480         // Direct Blocks\r
481         if(BlockNum < 12)\r
482                 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
483         \r
484         // Single Indirect Blocks\r
485         iBlocks = malloc( Disk->BlockSize );\r
486         VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
487         \r
488         BlockNum -= 12;\r
489         if(BlockNum < dwPerBlock)\r
490         {\r
491                 BlockNum = iBlocks[BlockNum];\r
492                 free(iBlocks);\r
493                 return (Uint64)BlockNum * Disk->BlockSize;\r
494         }\r
495         \r
496         BlockNum -= dwPerBlock;\r
497         // Double Indirect Blocks\r
498         if(BlockNum < dwPerBlock*dwPerBlock)\r
499         {\r
500                 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
501                 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
502                 BlockNum = iBlocks[BlockNum%dwPerBlock];\r
503                 free(iBlocks);\r
504                 return (Uint64)BlockNum * Disk->BlockSize;\r
505         }\r
506         \r
507         BlockNum -= dwPerBlock*dwPerBlock;\r
508         // Triple Indirect Blocks\r
509         VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
510         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
511         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
512         BlockNum = iBlocks[BlockNum%dwPerBlock];\r
513         free(iBlocks);\r
514         return (Uint64)BlockNum * Disk->BlockSize;\r
515 }\r
516 \r
517 /**\r
518  * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
519  * \brief Allocate an inode (from the current group preferably)\r
520  * \param Disk  EXT2 Disk Information Structure\r
521  * \param Parent        Inode ID of the parent (used to locate the child nearby)\r
522  */\r
523 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
524 {\r
525         Uint    start_group = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
526         Uint    group = start_group;\r
527 \r
528         if( Disk->SuperBlock.s_free_inodes_count == 0 ) \r
529         {\r
530                 Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p", Disk);\r
531                 return 0;\r
532         }\r
533 \r
534         while( group < Disk->GroupCount && Disk->Groups[group].bg_free_inodes_count == 0 )\r
535                 group ++;\r
536         if( group == Disk->GroupCount )\r
537         {\r
538                 group = 0;\r
539                 while( group < start_group && Disk->Groups[group].bg_free_inodes_count == 0 )\r
540                         group ++;\r
541         }\r
542         \r
543         if( Disk->Groups[group].bg_free_inodes_count == 0 )\r
544         {\r
545                 Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p, but superblock says some free", Disk);\r
546                 return 0;\r
547         }\r
548 \r
549         // Load bitmap for group\r
550         //  (s_inodes_per_group / 8) bytes worth\r
551         // - Allocate a buffer the size of a sector/block\r
552         // - Read in part of the bitmap\r
553         // - Search for a free inode\r
554         tExt2_Group     *bg = &Disk->Groups[group];\r
555          int    ofs = 0;\r
556         do {\r
557                 const int sector_size = 512;\r
558                 Uint8 buf[sector_size];\r
559                 VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
560 \r
561                 int byte, bit;\r
562                 for( byte = 0; byte < sector_size && buf[byte] == 0xFF; byte ++ )\r
563                         ;\r
564                 if( byte < sector_size )\r
565                 {\r
566                         for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)\r
567                                 ;\r
568                         ASSERT(bit != 8);\r
569                         buf[byte] |= 1 << bit;\r
570                         VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
571 \r
572                         bg->bg_free_inodes_count --;\r
573                         Disk->SuperBlock.s_free_inodes_count --;\r
574 \r
575                         Uint32  ret = group * Disk->SuperBlock.s_inodes_per_group + byte * 8 + bit + 1;\r
576                         Log_Debug("Ext2", "Ext2_int_AllocateInode - Allocated 0x%x", ret);\r
577                         return ret;\r
578                 }\r
579 \r
580                 ofs += sector_size;\r
581         } while(ofs < Disk->SuperBlock.s_inodes_per_group / 8);\r
582 \r
583         Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes in group %p:%i but header reported free",\r
584                 Disk, group);\r
585 \r
586         return 0;\r
587 }\r
588 \r
589 /**\r
590  * \brief Reduce the reference count on an inode\r
591  */\r
592 void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode)\r
593 {\r
594         Log_Warning("Ext2", "TODO: Impliment Ext2_int_DereferenceInode");\r
595 }\r
596 \r
597 /**\r
598  * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
599  * \brief Updates the superblock\r
600  */\r
601 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
602 {\r
603          int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
604          int    ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
605          int    i;\r
606          \r
607         // Update Primary\r
608         VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
609         \r
610         // - Update block groups while we're at it\r
611         VFS_WriteAt(\r
612                 Disk->FD,\r
613                 Disk->SuperBlock.s_first_data_block * Disk->BlockSize + 1024,\r
614                 sizeof(tExt2_Group)*Disk->GroupCount,\r
615                 Disk->Groups\r
616                 );\r
617         \r
618         // Secondaries\r
619         // at Block Group 1, 3^n, 5^n, 7^n\r
620         \r
621         // 1\r
622         if(ngrp <= 1)   return;\r
623         VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
624         \r
625         #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
626         \r
627         // Powers of 3\r
628         for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
629                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
630         \r
631         // Powers of 5\r
632         for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
633                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
634         \r
635         // Powers of 7\r
636         for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
637                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
638 }\r

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