Kernel - Added .Detect method to VFS drivers
[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_UpdateSuperblock(tExt2_Disk *Disk);\r
29 \r
30 // === SEMI-GLOBALS ===\r
31 MODULE_DEFINE(0, VERSION, FS_Ext2, Ext2_Install, Ext2_Cleanup);\r
32 tExt2_Disk      gExt2_disks[6];\r
33  int    giExt2_count = 0;\r
34 tVFS_Driver     gExt2_FSInfo = {\r
35         .Name = "ext2",\r
36         .Detect = Ext2_Detect,\r
37         .InitDevice = Ext2_InitDevice,\r
38         .Unmount = Ext2_Unmount,\r
39         .GetNodeFromINode = NULL\r
40         };\r
41 \r
42 // === CODE ===\r
43 /**\r
44  * \fn int Ext2_Install(char **Arguments)\r
45  * \brief Install the Ext2 Filesystem Driver\r
46  */\r
47 int Ext2_Install(char **Arguments)\r
48 {\r
49         VFS_AddDriver( &gExt2_FSInfo );\r
50         return MODULE_ERR_OK;\r
51 }\r
52 \r
53 /**\r
54  * \brief Clean up driver state before unload\r
55  */\r
56 int Ext2_Cleanup(void)\r
57 {\r
58         return 0;\r
59 }\r
60 \r
61 /**\r
62  * Detect if a volume is Ext2 formatted\r
63  */\r
64 int Ext2_Detect(int FD)\r
65 {\r
66         tExt2_SuperBlock        sb;\r
67         size_t  len;\r
68         \r
69         len = VFS_ReadAt(FD, 1024, 1024, &sb);\r
70 \r
71         if( len != 1024 ) {\r
72                 Log_Debug("Ext2", "_Detect: Read failed? (0x%x != 1024)", len);\r
73                 return 0;\r
74         }\r
75         \r
76         switch(sb.s_magic)\r
77         {\r
78         case 0xEF53:\r
79                 return 2;\r
80         default:\r
81                 Log_Debug("Ext2", "_Detect: s_magic = 0x%x", sb.s_magic);\r
82                 return 0;\r
83         }\r
84 }\r
85 \r
86 /**\r
87  \brief Initializes a device to be read by by the driver\r
88  \param Device  String - Device to read from\r
89  \param Options NULL Terminated array of option strings\r
90  \return Root Node\r
91 */\r
92 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
93 {\r
94         tExt2_Disk      *disk;\r
95          int    fd;\r
96          int    groupCount;\r
97         tExt2_SuperBlock        sb;\r
98         tExt2_Inode     inode;\r
99         \r
100         ENTER("sDevice pOptions", Device, Options);\r
101         \r
102         // Open Disk\r
103         fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
104         if(fd == -1) {\r
105                 Log_Warning("EXT2", "Unable to open '%s'", Device);\r
106                 LEAVE('n');\r
107                 return NULL;\r
108         }\r
109         \r
110         // Read Superblock at offset 1024\r
111         VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
112         \r
113         // Sanity Check Magic value\r
114         if(sb.s_magic != 0xEF53) {\r
115                 Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
116                         Device, sb.s_magic);\r
117                 VFS_Close(fd);\r
118                 LEAVE('n');\r
119                 return NULL;\r
120         }\r
121         \r
122         // Get Group count\r
123         groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
124         LOG("groupCount = %i", groupCount);\r
125         \r
126         // Allocate Disk Information\r
127         disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
128         if(!disk) {\r
129                 Log_Warning("EXT2", "Unable to allocate disk structure");\r
130                 VFS_Close(fd);\r
131                 LEAVE('n');\r
132                 return NULL;\r
133         }\r
134         disk->FD = fd;\r
135         memcpy(&disk->SuperBlock, &sb, 1024);\r
136         disk->GroupCount = groupCount;\r
137         \r
138         // Get an inode cache handle\r
139         disk->CacheID = Inode_GetHandle();\r
140         \r
141         // Get Block Size\r
142         disk->BlockSize = 1024 << sb.s_log_block_size;\r
143         LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size);\r
144         \r
145         // Read Group Information\r
146         LOG("sb,s_first_data_block = %x", sb.s_first_data_block);\r
147         VFS_ReadAt(\r
148                 disk->FD,\r
149                 sb.s_first_data_block * disk->BlockSize + 1024,\r
150                 sizeof(tExt2_Group)*groupCount,\r
151                 disk->Groups\r
152                 );\r
153         \r
154         LOG("Block Group 0");\r
155         LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
156         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
157         LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
158         LOG("Block Group 1");\r
159         LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
160         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
161         LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
162         \r
163         // Get root Inode\r
164         Ext2_int_ReadInode(disk, 2, &inode);\r
165         \r
166         // Create Root Node\r
167         memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
168         disk->RootNode.Inode = 2;       // Root inode ID\r
169         disk->RootNode.ImplPtr = disk;  // Save disk pointer\r
170         disk->RootNode.Size = -1;       // Fill in later (on readdir)\r
171         disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
172 \r
173         disk->RootNode.Type = &gExt2_DirType;\r
174         \r
175         // Complete root node\r
176         disk->RootNode.UID = inode.i_uid;\r
177         disk->RootNode.GID = inode.i_gid;\r
178         disk->RootNode.NumACLs = 1;\r
179         disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
180         \r
181         #if DEBUG\r
182         LOG("inode.i_size = 0x%x", inode.i_size);\r
183         LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
184         #endif\r
185         \r
186         LEAVE('p', &disk->RootNode);\r
187         return &disk->RootNode;\r
188 }\r
189 \r
190 /**\r
191  * \fn void Ext2_Unmount(tVFS_Node *Node)\r
192  * \brief Close a mounted device\r
193  */\r
194 void Ext2_Unmount(tVFS_Node *Node)\r
195 {\r
196         tExt2_Disk      *disk = Node->ImplPtr;\r
197         \r
198         VFS_Close( disk->FD );\r
199         Inode_ClearCache( disk->CacheID );\r
200         memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
201         free(disk);\r
202 }\r
203 \r
204 /**\r
205  * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
206  * \brief Close a file (Remove it from the cache)\r
207  */\r
208 void Ext2_CloseFile(tVFS_Node *Node)\r
209 {\r
210         tExt2_Disk      *disk = Node->ImplPtr;\r
211         Inode_UncacheNode(disk->CacheID, Node->Inode);\r
212         return ;\r
213 }\r
214 \r
215 //==================================\r
216 //=       INTERNAL FUNCTIONS       =\r
217 //==================================\r
218 /**\r
219  * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
220  * \brief Read an inode into memory\r
221  */\r
222 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
223 {\r
224          int    group, subId;\r
225         \r
226         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
227         \r
228         if(InodeId == 0)        return 0;\r
229         \r
230         InodeId --;     // Inodes are numbered starting at 1\r
231         \r
232         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
233         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
234         \r
235         LOG("group=%i, subId = %i", group, subId);\r
236         \r
237         // Read Inode\r
238         VFS_ReadAt(Disk->FD,\r
239                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
240                 sizeof(tExt2_Inode),\r
241                 Inode);\r
242         \r
243         LEAVE('i', 1);\r
244         return 1;\r
245 }\r
246 \r
247 /**\r
248  * \brief Write a modified inode out to disk\r
249  */\r
250 int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
251 {\r
252          int    group, subId;\r
253         ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
254         \r
255         if(InodeId == 0) {\r
256                 LEAVE('i', 0);\r
257                 return 0;\r
258         }\r
259         \r
260         InodeId --;     // Inodes are numbered starting at 1\r
261         \r
262         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
263         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
264         \r
265         LOG("group=%i, subId = %i", group, subId);\r
266         \r
267         // Write Inode\r
268         VFS_WriteAt(Disk->FD,\r
269                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
270                 sizeof(tExt2_Inode),\r
271                 Inode\r
272                 );\r
273         \r
274         LEAVE('i', 1);\r
275         return 1;\r
276 }\r
277 \r
278 /**\r
279  * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
280  * \brief Get the address of a block from an inode's list\r
281  * \param Disk  Disk information structure\r
282  * \param Blocks        Pointer to an inode's block list\r
283  * \param BlockNum      Block index in list\r
284  */\r
285 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
286 {\r
287         Uint32  *iBlocks;\r
288          int    dwPerBlock = Disk->BlockSize / 4;\r
289         \r
290         // Direct Blocks\r
291         if(BlockNum < 12)\r
292                 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
293         \r
294         // Single Indirect Blocks\r
295         iBlocks = malloc( Disk->BlockSize );\r
296         VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
297         \r
298         BlockNum -= 12;\r
299         if(BlockNum < dwPerBlock)\r
300         {\r
301                 BlockNum = iBlocks[BlockNum];\r
302                 free(iBlocks);\r
303                 return (Uint64)BlockNum * Disk->BlockSize;\r
304         }\r
305         \r
306         BlockNum -= dwPerBlock;\r
307         // Double Indirect Blocks\r
308         if(BlockNum < dwPerBlock*dwPerBlock)\r
309         {\r
310                 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
311                 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
312                 BlockNum = iBlocks[BlockNum%dwPerBlock];\r
313                 free(iBlocks);\r
314                 return (Uint64)BlockNum * Disk->BlockSize;\r
315         }\r
316         \r
317         BlockNum -= dwPerBlock*dwPerBlock;\r
318         // Triple Indirect Blocks\r
319         VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
320         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
321         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
322         BlockNum = iBlocks[BlockNum%dwPerBlock];\r
323         free(iBlocks);\r
324         return (Uint64)BlockNum * Disk->BlockSize;\r
325 }\r
326 \r
327 /**\r
328  * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
329  * \brief Allocate an inode (from the current group preferably)\r
330  * \param Disk  EXT2 Disk Information Structure\r
331  * \param Parent        Inode ID of the parent (used to locate the child nearby)\r
332  */\r
333 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
334 {\r
335 //      Uint    block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
336         Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
337         return 0;\r
338 }\r
339 \r
340 /**\r
341  * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
342  * \brief Updates the superblock\r
343  */\r
344 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
345 {\r
346          int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
347          int    ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
348          int    i;\r
349          \r
350         // Update Primary\r
351         VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
352         \r
353         // Secondaries\r
354         // at Block Group 1, 3^n, 5^n, 7^n\r
355         \r
356         // 1\r
357         if(ngrp <= 1)   return;\r
358         VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
359         \r
360         #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
361         \r
362         // Powers of 3\r
363         for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
364                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
365         \r
366         // Powers of 5\r
367         for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
368                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
369         \r
370         // Powers of 7\r
371         for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
372                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
373 }\r

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