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

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