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

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