Implemented Dependency resolving for compile-time modules and Updated the README
[tpg/acess2.git] / Kernel / vfs / fs / ext2.c
1 /*\r
2  * Acess OS\r
3  * Ext2 Driver Version 1\r
4  */\r
5 /**\r
6  * \file fs/ext2.c\r
7  * \brief Second Extended Filesystem Driver\r
8  * \todo Implement file full write support\r
9  */\r
10 #define DEBUG   1\r
11 #define VERBOSE 0\r
12 #include <common.h>\r
13 #include <vfs.h>\r
14 #include <modules.h>\r
15 #include "fs_ext2.h"\r
16 \r
17 #define EXT2_UPDATE_WRITEBACK   1\r
18 \r
19 // === STRUCTURES ===\r
20 typedef struct {\r
21          int    FD;\r
22          int    CacheID;\r
23         tVFS_Node       RootNode;\r
24         \r
25         tExt2_SuperBlock        SuperBlock;\r
26          int    BlockSize;\r
27          \r
28          int    GroupCount;\r
29         tExt2_Group             Groups[];\r
30 } tExt2_Disk;\r
31 \r
32 // === PROTOTYPES ===\r
33  int    Ext2_Install(char **Arguments);\r
34 // Interface Functions\r
35 tVFS_Node       *Ext2_InitDevice(char *Device, char **Options);\r
36 void            Ext2_Unmount(tVFS_Node *Node);\r
37 Uint64          Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
38 Uint64          Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
39 void            Ext2_CloseFile(tVFS_Node *Node);\r
40 char            *Ext2_ReadDir(tVFS_Node *Node, int Pos);\r
41 tVFS_Node       *Ext2_FindDir(tVFS_Node *Node, char *FileName);\r
42  int            Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);\r
43 // Internal Helpers\r
44  int            Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
45 tVFS_Node       *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name);\r
46  int            Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);\r
47 Uint64          Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
48 Uint32          Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
49 Uint32          Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);\r
50 void            Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
51 \r
52 // === SEMI-GLOBALS ===\r
53 MODULE_DEFINE(0, 0x5B /*v0.90*/, EXT2, Ext2_Install, NULL);\r
54 tExt2_Disk      gExt2_disks[6];\r
55  int    giExt2_count = 0;\r
56 tVFS_Driver     gExt2_FSInfo = {\r
57         "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL\r
58         };\r
59 \r
60 // === CODE ===\r
61 \r
62 /**\r
63  * \fn int Ext2_Install(char **Arguments)\r
64  * \brief Install the Ext2 Filesystem Driver\r
65  */\r
66 int Ext2_Install(char **Arguments)\r
67 {\r
68         VFS_AddDriver( &gExt2_FSInfo );\r
69         return 1;\r
70 }\r
71 \r
72 /**\r
73  \fn tVFS_Node *Ext2_InitDevice(char *Device, char **Options)\r
74  \brief Initializes a device to be read by by the driver\r
75  \param Device  String - Device to read from\r
76  \param Options NULL Terminated array of option strings\r
77  \return Root Node\r
78 */\r
79 tVFS_Node *Ext2_InitDevice(char *Device, char **Options)\r
80 {\r
81         tExt2_Disk      *disk;\r
82          int    fd;\r
83          int    groupCount;\r
84         tExt2_SuperBlock        sb;\r
85         tExt2_Inode     inode;\r
86         \r
87         ENTER("sDevice pOptions", Device, Options);\r
88         \r
89         // Open Disk\r
90         fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
91         if(fd == -1) {\r
92                 Warning("[EXT2 ] Unable to open '%s'", Device);\r
93                 LEAVE('n');\r
94                 return NULL;\r
95         }\r
96         \r
97         // Read Superblock at offset 1024\r
98         VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
99         \r
100         // Sanity Check Magic value\r
101         if(sb.s_magic != 0xEF53) {\r
102                 Warning("[EXT2 ] Volume '%s' is not an EXT2 volume", Device);\r
103                 VFS_Close(fd);\r
104                 LEAVE('n');\r
105                 return NULL;\r
106         }\r
107         \r
108         // Get Group count\r
109         groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
110         LOG("groupCount = %i", groupCount);\r
111         \r
112         // Allocate Disk Information\r
113         disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
114         if(!disk) {\r
115                 Warning("[EXT2 ] Unable to allocate disk structure");\r
116                 VFS_Close(fd);\r
117                 LEAVE('n');\r
118                 return NULL;\r
119         }\r
120         disk->FD = fd;\r
121         memcpy(&disk->SuperBlock, &sb, 1024);\r
122         disk->GroupCount = groupCount;\r
123         \r
124         // Get an inode cache handle\r
125         disk->CacheID = Inode_GetHandle();\r
126         \r
127         // Get Block Size\r
128         LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
129         disk->BlockSize = 1024 << sb.s_log_block_size;\r
130         \r
131         // Read Group Information\r
132         VFS_ReadAt(\r
133                 disk->FD,\r
134                 sb.s_first_data_block * disk->BlockSize + 1024,\r
135                 sizeof(tExt2_Group)*groupCount,\r
136                 disk->Groups\r
137                 );\r
138         \r
139         #if VERBOSE\r
140         LOG("Block Group 0");\r
141         LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
142         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
143         LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
144         LOG("Block Group 1");\r
145         LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
146         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
147         LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
148         #endif\r
149         \r
150         // Get root Inode\r
151         Ext2_int_ReadInode(disk, 2, &inode);\r
152         \r
153         // Create Root Node\r
154         memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
155         disk->RootNode.Inode = 2;       // Root inode ID\r
156         disk->RootNode.ImplPtr = disk;  // Save disk pointer\r
157         disk->RootNode.Size = -1;       // Fill in later (on readdir)\r
158         disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
159         \r
160         disk->RootNode.ReadDir = Ext2_ReadDir;\r
161         disk->RootNode.FindDir = Ext2_FindDir;\r
162         //disk->RootNode.Relink = Ext2_Relink;\r
163         \r
164         // Complete root node\r
165         disk->RootNode.UID = inode.i_uid;\r
166         disk->RootNode.GID = inode.i_gid;\r
167         disk->RootNode.NumACLs = 1;\r
168         disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
169         \r
170         #if DEBUG\r
171         LOG("inode.i_size = 0x%x", inode.i_size);\r
172         LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
173         #endif\r
174         \r
175         LEAVE('p', &disk->RootNode);\r
176         return &disk->RootNode;\r
177 }\r
178 \r
179 /**\r
180  * \fn void Ext2_Unmount(tVFS_Node *Node)\r
181  * \brief Close a mounted device\r
182  */\r
183 void Ext2_Unmount(tVFS_Node *Node)\r
184 {\r
185         tExt2_Disk      *disk = Node->ImplPtr;\r
186         \r
187         VFS_Close( disk->FD );\r
188         Inode_ClearCache( disk->CacheID );\r
189         memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
190         free(disk);\r
191 }\r
192 \r
193 /**\r
194  * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
195  * \brief Read from a file\r
196  */\r
197 Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
198 {\r
199         tExt2_Disk      *disk = Node->ImplPtr;\r
200         tExt2_Inode     inode;\r
201         Uint64  base;\r
202         Uint    block;\r
203         Uint64  remLen;\r
204         \r
205         ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);\r
206         \r
207         // Get Inode\r
208         Ext2_int_GetInode(Node, &inode);\r
209         \r
210         // Sanity Checks\r
211         if(Offset >= inode.i_size) {\r
212                 LEAVE('i', 0);\r
213                 return 0;\r
214         }\r
215         if(Offset + Length > inode.i_size)\r
216                 Length = inode.i_size - Offset;\r
217         \r
218         block = Offset / disk->BlockSize;\r
219         Offset = Offset / disk->BlockSize;\r
220         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
221         if(base == 0) {\r
222                 Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);\r
223                 LEAVE('i', 0);\r
224                 return 0;\r
225         }\r
226         \r
227         // Read only block\r
228         if(Length <= disk->BlockSize - Offset)\r
229         {\r
230                 VFS_ReadAt( disk->FD, base+Offset, Length, Buffer);\r
231                 LEAVE('X', Length);\r
232                 return Length;\r
233         }\r
234         \r
235         // Read first block\r
236         remLen = Length;\r
237         VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer);\r
238         remLen -= disk->BlockSize - Offset;\r
239         Buffer += disk->BlockSize - Offset;\r
240         block ++;\r
241         \r
242         // Read middle blocks\r
243         while(remLen > disk->BlockSize)\r
244         {\r
245                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
246                 if(base == 0) {\r
247                         Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);\r
248                         LEAVE('i', 0);\r
249                         return 0;\r
250                 }\r
251                 VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer);\r
252                 Buffer += disk->BlockSize;\r
253                 remLen -= disk->BlockSize;\r
254                 block ++;\r
255         }\r
256         \r
257         // Read last block\r
258         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
259         VFS_ReadAt( disk->FD, base, remLen, Buffer);\r
260         \r
261         LEAVE('X', Length);\r
262         return Length;\r
263 }\r
264 \r
265 /**\r
266  * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
267  * \brief Write to a file\r
268  */\r
269 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
270 {\r
271         tExt2_Disk      *disk = Node->ImplPtr;\r
272         tExt2_Inode     inode;\r
273         Uint64  base;\r
274         Uint64  retLen;\r
275         Uint    block;\r
276         Uint64  allocSize;\r
277          int    bNewBlocks = 0;\r
278         \r
279         Debug_HexDump("Ext2_Write", Buffer, Length);\r
280         \r
281         Ext2_int_GetInode(Node, &inode);\r
282         \r
283         // Get the ammount of space already allocated\r
284         // - Round size up to block size\r
285         // - block size is a power of two, so this will work\r
286         allocSize = (inode.i_size + disk->BlockSize) & ~(disk->BlockSize-1);\r
287         \r
288         // Are we writing to inside the allocated space?\r
289         if( Offset < allocSize )\r
290         {\r
291                 // Will we go out of it?\r
292                 if(Offset + Length > allocSize) {\r
293                         bNewBlocks = 1;\r
294                         retLen = allocSize - Offset;\r
295                 } else\r
296                         retLen = Length;\r
297                 \r
298                 // Within the allocated space\r
299                 block = Offset / disk->BlockSize;\r
300                 Offset %= disk->BlockSize;\r
301                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
302                 \r
303                 // Write only block (if only one)\r
304                 if(Offset + retLen <= disk->BlockSize) {\r
305                         VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);\r
306                         if(!bNewBlocks) return Length;\r
307                         goto addBlocks; // Ugh! A goto, but it seems unavoidable\r
308                 }\r
309                 \r
310                 // Write First Block\r
311                 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);\r
312                 Buffer += disk->BlockSize-Offset;\r
313                 retLen -= disk->BlockSize-Offset;\r
314                 block ++;\r
315                 \r
316                 // Write middle blocks\r
317                 while(retLen > disk->BlockSize)\r
318                 {\r
319                         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
320                         VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);\r
321                         Buffer += disk->BlockSize;\r
322                         retLen -= disk->BlockSize;\r
323                         block ++;\r
324                 }\r
325                 \r
326                 // Write last block\r
327                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);\r
328                 VFS_WriteAt(disk->FD, base, retLen, Buffer);\r
329                 if(!bNewBlocks) return Length;  // Writing in only allocated space\r
330         }\r
331         \r
332 addBlocks:\r
333         ///\todo Implement block allocation\r
334         Warning("[EXT2] File extending is not yet supported");\r
335         \r
336         return 0;\r
337 }\r
338 \r
339 /**\r
340  * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
341  * \brief Close a file (Remove it from the cache)\r
342  */\r
343 void Ext2_CloseFile(tVFS_Node *Node)\r
344 {\r
345         tExt2_Disk      *disk = Node->ImplPtr;\r
346         Inode_UncacheNode(disk->CacheID, Node->Inode);\r
347         return ;\r
348 }\r
349 \r
350 /**\r
351  \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)\r
352  \brief Reads a directory entry\r
353 */\r
354 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)\r
355 {\r
356         tExt2_Inode     inode;\r
357         char    namebuf[EXT2_NAME_LEN+1];\r
358         tExt2_DirEnt    dirent;\r
359         Uint64  Base;   // Block's Base Address\r
360          int    block = 0, ofs = 0;\r
361          int    entNum = 0;\r
362         tExt2_Disk      *disk = Node->ImplPtr;\r
363         Uint    size;\r
364         \r
365         ENTER("pNode iPos", Node, Pos);\r
366         \r
367         // Read directory's inode\r
368         Ext2_int_GetInode(Node, &inode);\r
369         size = inode.i_size;\r
370         \r
371         LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
372         \r
373         // Find Entry\r
374         // Get First Block\r
375         // - Do this ourselves as it is a simple operation\r
376         Base = inode.i_block[0] * disk->BlockSize;\r
377         while(Pos -- && size > 0)\r
378         {\r
379                 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);\r
380                 ofs += dirent.rec_len;\r
381                 size -= dirent.rec_len;\r
382                 entNum ++;\r
383                 \r
384                 if(ofs >= disk->BlockSize) {\r
385                         block ++;\r
386                         if( ofs > disk->BlockSize ) {\r
387                                 Warning("[EXT2] Directory Entry %i of inode %i extends over a block boundary, ignoring",\r
388                                         entNum-1, Node->Inode);\r
389                         }\r
390                         ofs = 0;\r
391                         Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );\r
392                 }\r
393         }\r
394         \r
395         // Check for the end of the list\r
396         if(size <= 0) {\r
397                 LEAVE('n');\r
398                 return NULL;\r
399         }\r
400         \r
401         // Read Entry\r
402         VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );\r
403         //LOG("dirent.inode = %i", dirent.inode);\r
404         //LOG("dirent.rec_len = %i", dirent.rec_len);\r
405         //LOG("dirent.name_len = %i", dirent.name_len);\r
406         VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );\r
407         namebuf[ dirent.name_len ] = '\0';      // Cap off string\r
408         \r
409         \r
410         // Ignore . and .. (these are done in the VFS)\r
411         if( (namebuf[0] == '.' && namebuf[1] == '\0')\r
412         ||  (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0')) {\r
413                 LEAVE('p', VFS_SKIP);\r
414                 return VFS_SKIP;        // Skip\r
415         }\r
416         \r
417         LEAVE('s', namebuf);\r
418         // Create new node\r
419         return strdup(namebuf);\r
420 }\r
421 \r
422 /**\r
423  \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)\r
424  \brief Gets information about a file\r
425  \param node    vfs node - Parent Node\r
426  \param filename        String - Name of file\r
427  \return VFS Node of file\r
428 */\r
429 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)\r
430 {\r
431         tExt2_Disk      *disk = Node->ImplPtr;\r
432         tExt2_Inode     inode;\r
433         char    namebuf[EXT2_NAME_LEN+1];\r
434         tExt2_DirEnt    dirent;\r
435         Uint64  Base;   // Block's Base Address\r
436          int    block = 0, ofs = 0;\r
437          int    entNum = 0;\r
438         Uint    size;\r
439         \r
440         // Read directory's inode\r
441         Ext2_int_GetInode(Node, &inode);\r
442         size = inode.i_size;\r
443         \r
444         // Get First Block\r
445         // - Do this ourselves as it is a simple operation\r
446         Base = inode.i_block[0] * disk->BlockSize;\r
447         // Find File\r
448         while(size > 0)\r
449         {\r
450                 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);\r
451                 VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );\r
452                 namebuf[ dirent.name_len ] = '\0';      // Cap off string\r
453                 // If it matches, create a node and return it\r
454                 if(strcmp(namebuf, Filename) == 0)\r
455                         return Ext2_int_CreateNode( disk, dirent.inode, namebuf );\r
456                 // Increment pointers\r
457                 ofs += dirent.rec_len;\r
458                 size -= dirent.rec_len;\r
459                 entNum ++;\r
460                 \r
461                 // Check for end of block\r
462                 if(ofs >= disk->BlockSize) {\r
463                         block ++;\r
464                         if( ofs > disk->BlockSize ) {\r
465                                 Warning("[EXT2 ] Directory Entry %i of inode %i extends over a block boundary, ignoring",\r
466                                         entNum-1, Node->Inode);\r
467                         }\r
468                         ofs = 0;\r
469                         Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );\r
470                 }\r
471         }\r
472         \r
473         return NULL;\r
474 }\r
475 \r
476 /**\r
477  * \fn int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)\r
478  * \brief Create a new node\r
479  */\r
480 int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)\r
481 {\r
482         return 0;\r
483 }\r
484 \r
485 //==================================\r
486 //=       INTERNAL FUNCTIONS       =\r
487 //==================================\r
488 \r
489 \r
490 /**\r
491  * \fn int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)\r
492  * \brief Gets the inode descriptor for a node\r
493  * \param Node  node to get the Inode of\r
494  * \param Inode Destination\r
495  */\r
496 int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)\r
497 {\r
498         return Ext2_int_ReadInode(Node->ImplPtr, Node->Inode, Inode);\r
499 }\r
500 \r
501 /**\r
502  * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)\r
503  * \brief Create a new VFS Node\r
504  */\r
505 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)\r
506 {\r
507         tExt2_Inode     inode;\r
508         tVFS_Node       retNode;\r
509         tVFS_Node       *tmpNode;\r
510         \r
511         if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )\r
512                 return NULL;\r
513         \r
514         if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )\r
515                 return tmpNode;\r
516         \r
517         \r
518         // Set identifiers\r
519         retNode.Inode = InodeID;\r
520         retNode.ImplPtr = Disk;\r
521         \r
522         // Set file length\r
523         retNode.Size = inode.i_size;\r
524         \r
525         // Set Access Permissions\r
526         retNode.UID = inode.i_uid;\r
527         retNode.GID = inode.i_gid;\r
528         retNode.NumACLs = 3;\r
529         retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);\r
530         \r
531         //  Set Function Pointers\r
532         retNode.Read = Ext2_Read;\r
533         retNode.Write = Ext2_Write;\r
534         retNode.Close = Ext2_CloseFile;\r
535         \r
536         switch(inode.i_mode & EXT2_S_IFMT)\r
537         {\r
538         // Symbolic Link\r
539         case EXT2_S_IFLNK:\r
540                 retNode.Flags = VFS_FFLAG_SYMLINK;\r
541                 break;\r
542         // Regular File\r
543         case EXT2_S_IFREG:\r
544                 retNode.Flags = 0;\r
545                 retNode.Size |= (Uint64)inode.i_dir_acl << 32;\r
546                 break;\r
547         // Directory\r
548         case EXT2_S_IFDIR:\r
549                 retNode.ReadDir = Ext2_ReadDir;\r
550                 retNode.FindDir = Ext2_FindDir;\r
551                 retNode.MkNod = Ext2_MkNod;\r
552                 //retNode.Relink = Ext2_Relink;\r
553                 retNode.Flags = VFS_FFLAG_DIRECTORY;\r
554                 break;\r
555         // Unknown, Write protect and hide it to be safe \r
556         default:\r
557                 retNode.Flags = VFS_FFLAG_READONLY;//|VFS_FFLAG_HIDDEN;\r
558                 break;\r
559         }\r
560         \r
561         // Check if the file should be hidden\r
562         //if(Name[0] == '.')    retNode.Flags |= VFS_FFLAG_HIDDEN;\r
563         \r
564         // Set Timestamps\r
565         retNode.ATime = now();\r
566         retNode.MTime = inode.i_mtime * 1000;\r
567         retNode.CTime = inode.i_ctime * 1000;\r
568         \r
569         // Save in node cache and return saved node\r
570         return Inode_CacheNode(Disk->CacheID, &retNode);\r
571 }\r
572 \r
573 /**\r
574  * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
575  * \brief Read an inode into memory\r
576  */\r
577 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
578 {\r
579          int    group, subId;\r
580         \r
581         //LogF("Ext2_int_ReadInode: (Disk=%p, InodeId=%i, Inode=%p)", Disk, InodeId, Inode);\r
582         //ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
583         \r
584         if(InodeId == 0)        return 0;\r
585         \r
586         InodeId --;     // Inodes are numbered starting at 1\r
587         \r
588         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
589         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
590         \r
591         //LOG("group=%i, subId = %i", group, subId);\r
592         \r
593         // Read Inode\r
594         VFS_ReadAt(Disk->FD,\r
595                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
596                 sizeof(tExt2_Inode),\r
597                 Inode);\r
598         return 1;\r
599 }\r
600 \r
601 /**\r
602  * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
603  * \brief Get the address of a block from an inode's list\r
604  * \param Disk  Disk information structure\r
605  * \param Blocks        Pointer to an inode's block list\r
606  * \param BlockNum      Block index in list\r
607  */\r
608 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
609 {\r
610         Uint32  *iBlocks;\r
611         // Direct Blocks\r
612         if(BlockNum < 12)\r
613                 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
614         \r
615         // Single Indirect Blocks\r
616         iBlocks = malloc( Disk->BlockSize );\r
617         VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
618         \r
619         BlockNum -= 12;\r
620         if(BlockNum < 256) {\r
621                 BlockNum = iBlocks[BlockNum];\r
622                 free(iBlocks);\r
623                 return (Uint64)BlockNum * Disk->BlockSize;\r
624         }\r
625         \r
626         // Double Indirect Blocks\r
627         if(BlockNum < 256*256)\r
628         {\r
629                 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
630                 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/256]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
631                 BlockNum = iBlocks[BlockNum%256];\r
632                 free(iBlocks);\r
633                 return (Uint64)BlockNum * Disk->BlockSize;\r
634         }\r
635         // Triple Indirect Blocks\r
636         VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
637         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(256*256)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
638         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/256)%256]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
639         BlockNum = iBlocks[BlockNum%256];\r
640         free(iBlocks);\r
641         return (Uint64)BlockNum * Disk->BlockSize;\r
642 }\r
643 \r
644 /**\r
645  * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
646  * \brief Allocate an inode (from the current group preferably)\r
647  * \param Disk  EXT2 Disk Information Structure\r
648  * \param Parent        Inode ID of the parent (used to locate the child nearby)\r
649  */\r
650 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
651 {\r
652 //      Uint    block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
653         return 0;\r
654 }\r
655 \r
656 /**\r
657  * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)\r
658  * \brief Allocate a block from the best possible location\r
659  * \param Disk  EXT2 Disk Information Structure\r
660  * \param PrevBlock     Previous block ID in the file\r
661  */\r
662 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)\r
663 {\r
664          int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
665         Uint    blockgroup = PrevBlock / bpg;\r
666         Uint    bitmap[Disk->BlockSize/sizeof(Uint)];\r
667         Uint    bitsperblock = 8*Disk->BlockSize;\r
668          int    i, j = 0;\r
669         Uint    block;\r
670         \r
671         // Are there any free blocks?\r
672         if(Disk->SuperBlock.s_free_blocks_count == 0)   return 0;\r
673         \r
674         if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)\r
675         {\r
676                 // Search block group's bitmap\r
677                 for(i = 0; i < bpg; i++)\r
678                 {\r
679                         // Get the block in the bitmap block\r
680                         j = i & (bitsperblock-1);\r
681                         \r
682                         // Read in if needed\r
683                         if(j == 0) {\r
684                                 VFS_ReadAt(\r
685                                         Disk->FD,\r
686                                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,\r
687                                         Disk->BlockSize,\r
688                                         bitmap\r
689                                         );\r
690                         }\r
691                         \r
692                         // Fast Check\r
693                         if( bitmap[j/32] == -1 ) {\r
694                                 j = (j + 31) & ~31;\r
695                                 continue;\r
696                         }\r
697                         \r
698                         // Is the bit set?\r
699                         if( bitmap[j/32] & (1 << (j%32)) )\r
700                                 continue;\r
701                         \r
702                         // Ooh! We found one\r
703                         break;\r
704                 }\r
705                 if( i < bpg ) {\r
706                         Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");\r
707                         goto    checkAll;       // Search the entire filesystem for a free block\r
708                         // Goto needed for neatness\r
709                 }\r
710                 \r
711                 // Mark as used\r
712                 bitmap[j/32] |= (1 << (j%32));\r
713                 VFS_WriteAt(\r
714                         Disk->FD,\r
715                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,\r
716                         Disk->BlockSize,\r
717                         bitmap\r
718                         );\r
719                 block = i;\r
720                 Disk->Groups[blockgroup].bg_free_blocks_count --;\r
721         }\r
722         else\r
723         {\r
724         checkAll:\r
725                 Warning("[EXT2 ] TODO - Implement using blocks outside the current block group");\r
726                 return 0;\r
727         }\r
728         \r
729         // Reduce global count\r
730         Disk->SuperBlock.s_free_blocks_count --;\r
731         #if EXT2_UPDATE_WRITEBACK\r
732         Ext2_int_UpdateSuperblock(Disk);\r
733         #endif\r
734         \r
735         return block;\r
736 }\r
737 \r
738 /**\r
739  * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
740  */\r
741 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
742 {\r
743         VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
744 }\r

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