7c4bf973ae16656cce01b9d14c0f3ee3dd21ad72
[tpg/acess2.git] / Modules / Filesystems / 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 // === IMPORTS ===\r
16 extern tVFS_NodeType    gExt2_DirType;\r
17 \r
18 // === PROTOTYPES ===\r
19  int    Ext2_Install(char **Arguments);\r
20 // Interface Functions\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, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);\r
32 tExt2_Disk      gExt2_disks[6];\r
33  int    giExt2_count = 0;\r
34 tVFS_Driver     gExt2_FSInfo = {\r
35         "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL\r
36         };\r
37 \r
38 // === CODE ===\r
39 /**\r
40  * \fn int Ext2_Install(char **Arguments)\r
41  * \brief Install the Ext2 Filesystem Driver\r
42  */\r
43 int Ext2_Install(char **Arguments)\r
44 {\r
45         VFS_AddDriver( &gExt2_FSInfo );\r
46         return MODULE_ERR_OK;\r
47 }\r
48 \r
49 /**\r
50  \brief Initializes a device to be read by by the driver\r
51  \param Device  String - Device to read from\r
52  \param Options NULL Terminated array of option strings\r
53  \return Root Node\r
54 */\r
55 tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
56 {\r
57         tExt2_Disk      *disk;\r
58          int    fd;\r
59          int    groupCount;\r
60         tExt2_SuperBlock        sb;\r
61         tExt2_Inode     inode;\r
62         \r
63         ENTER("sDevice pOptions", Device, Options);\r
64         \r
65         // Open Disk\r
66         fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);            //Open Device\r
67         if(fd == -1) {\r
68                 Log_Warning("EXT2", "Unable to open '%s'", Device);\r
69                 LEAVE('n');\r
70                 return NULL;\r
71         }\r
72         \r
73         // Read Superblock at offset 1024\r
74         VFS_ReadAt(fd, 1024, 1024, &sb);        // Read Superblock\r
75         \r
76         // Sanity Check Magic value\r
77         if(sb.s_magic != 0xEF53) {\r
78                 Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
79                         Device, sb.s_magic);\r
80                 VFS_Close(fd);\r
81                 LEAVE('n');\r
82                 return NULL;\r
83         }\r
84         \r
85         // Get Group count\r
86         groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
87         LOG("groupCount = %i", groupCount);\r
88         \r
89         // Allocate Disk Information\r
90         disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
91         if(!disk) {\r
92                 Log_Warning("EXT2", "Unable to allocate disk structure");\r
93                 VFS_Close(fd);\r
94                 LEAVE('n');\r
95                 return NULL;\r
96         }\r
97         disk->FD = fd;\r
98         memcpy(&disk->SuperBlock, &sb, 1024);\r
99         disk->GroupCount = groupCount;\r
100         \r
101         // Get an inode cache handle\r
102         disk->CacheID = Inode_GetHandle();\r
103         \r
104         // Get Block Size\r
105         LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
106         disk->BlockSize = 1024 << sb.s_log_block_size;\r
107         \r
108         // Read Group Information\r
109         VFS_ReadAt(\r
110                 disk->FD,\r
111                 sb.s_first_data_block * disk->BlockSize + 1024,\r
112                 sizeof(tExt2_Group)*groupCount,\r
113                 disk->Groups\r
114                 );\r
115         \r
116         #if VERBOSE\r
117         LOG("Block Group 0");\r
118         LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
119         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
120         LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
121         LOG("Block Group 1");\r
122         LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
123         LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
124         LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
125         #endif\r
126         \r
127         // Get root Inode\r
128         Ext2_int_ReadInode(disk, 2, &inode);\r
129         \r
130         // Create Root Node\r
131         memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
132         disk->RootNode.Inode = 2;       // Root inode ID\r
133         disk->RootNode.ImplPtr = disk;  // Save disk pointer\r
134         disk->RootNode.Size = -1;       // Fill in later (on readdir)\r
135         disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
136 \r
137         disk->RootNode.Type = &gExt2_DirType;\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) {\r
220                 LEAVE('i', 0);\r
221                 return 0;\r
222         }\r
223         \r
224         InodeId --;     // Inodes are numbered starting at 1\r
225         \r
226         group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
227         subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
228         \r
229         LOG("group=%i, subId = %i", group, subId);\r
230         \r
231         // Write Inode\r
232         VFS_WriteAt(Disk->FD,\r
233                 Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
234                 sizeof(tExt2_Inode),\r
235                 Inode\r
236                 );\r
237         \r
238         LEAVE('i', 1);\r
239         return 1;\r
240 }\r
241 \r
242 /**\r
243  * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
244  * \brief Get the address of a block from an inode's list\r
245  * \param Disk  Disk information structure\r
246  * \param Blocks        Pointer to an inode's block list\r
247  * \param BlockNum      Block index in list\r
248  */\r
249 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
250 {\r
251         Uint32  *iBlocks;\r
252          int    dwPerBlock = Disk->BlockSize / 4;\r
253         \r
254         // Direct Blocks\r
255         if(BlockNum < 12)\r
256                 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
257         \r
258         // Single Indirect Blocks\r
259         iBlocks = malloc( Disk->BlockSize );\r
260         VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
261         \r
262         BlockNum -= 12;\r
263         if(BlockNum < dwPerBlock)\r
264         {\r
265                 BlockNum = iBlocks[BlockNum];\r
266                 free(iBlocks);\r
267                 return (Uint64)BlockNum * Disk->BlockSize;\r
268         }\r
269         \r
270         BlockNum -= dwPerBlock;\r
271         // Double Indirect Blocks\r
272         if(BlockNum < dwPerBlock*dwPerBlock)\r
273         {\r
274                 VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
275                 VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
276                 BlockNum = iBlocks[BlockNum%dwPerBlock];\r
277                 free(iBlocks);\r
278                 return (Uint64)BlockNum * Disk->BlockSize;\r
279         }\r
280         \r
281         BlockNum -= dwPerBlock*dwPerBlock;\r
282         // Triple Indirect Blocks\r
283         VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
284         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
285         VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
286         BlockNum = iBlocks[BlockNum%dwPerBlock];\r
287         free(iBlocks);\r
288         return (Uint64)BlockNum * Disk->BlockSize;\r
289 }\r
290 \r
291 /**\r
292  * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
293  * \brief Allocate an inode (from the current group preferably)\r
294  * \param Disk  EXT2 Disk Information Structure\r
295  * \param Parent        Inode ID of the parent (used to locate the child nearby)\r
296  */\r
297 Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
298 {\r
299 //      Uint    block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
300         Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
301         return 0;\r
302 }\r
303 \r
304 /**\r
305  * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
306  * \brief Updates the superblock\r
307  */\r
308 void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
309 {\r
310          int    bpg = Disk->SuperBlock.s_blocks_per_group;\r
311          int    ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
312          int    i;\r
313          \r
314         // Update Primary\r
315         VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
316         \r
317         // Secondaries\r
318         // at Block Group 1, 3^n, 5^n, 7^n\r
319         \r
320         // 1\r
321         if(ngrp <= 1)   return;\r
322         VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
323         \r
324         #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
325         \r
326         // Powers of 3\r
327         for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
328                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
329         \r
330         // Powers of 5\r
331         for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
332                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
333         \r
334         // Powers of 7\r
335         for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
336                 VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
337 }\r

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