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

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