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

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