3 * Ext2 Driver Version 1
\r
7 * \brief Second Extended Filesystem Driver
\r
8 * \todo Implement file read support
\r
12 #include "fs_ext2.h"
\r
14 // === STRUCTURES ===
\r
20 tExt2_SuperBlock SuperBlock;
\r
24 tExt2_Group Groups[];
\r
27 // === PROTOTYPES ===
\r
28 //Interface Functions
\r
29 tVFS_Node *Ext2_InitDevice(char *Device, char **Options);
\r
30 void Ext2_UnMount(tVFS_Node *Node);
\r
31 Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
\r
32 Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
\r
33 char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
\r
34 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *FileName);
\r
35 tVFS_Node *Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
\r
36 int Ext2_int_GetInode(vfs_node *Node, tExt2_Inode *Inode);
\r
37 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name, Uint64 VfsInode);
\r
38 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);
\r
39 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
\r
41 // === SEMI-GLOBALS ===
\r
42 tExt2_Disk gExt2_disks[6];
\r
43 int giExt2_count = 0;
\r
44 tVFS_Driver gExt2_FSInfo = {NULL,
\r
45 "ext2", 0, Ext2_InitDevice, Ext2_UnMount, NULL
\r
51 * \fn void Ext2_Install()
\r
52 * \brief Install the Ext2 Filesystem Driver
\r
56 VFS_AddDriver( &gExt2_FSInfo );
\r
60 \fn tVFS_Node *Ext2_initDevice(char *Device, char **Options)
\r
61 \brief Initializes a device to be read by by the driver
\r
62 \param Device String - Device to read from
\r
63 \param Options NULL Terminated array of option strings
\r
66 tVFS_Node *Ext2_InitDevice(char *Device, char **Options)
\r
71 tExt2_SuperBlock sb;
\r
75 fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device
\r
77 Warning"[EXT2] Unable to open '%s'\n", Device);
\r
81 // Read Superblock at offset 1024
\r
82 VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock
\r
84 // Sanity Check Magic value
\r
85 if(sb.s_magic != 0xEF53) {
\r
86 WarningEx("EXT2", "Volume '%s' is not an EXT2 volume\n", Device);
\r
92 groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);
\r
93 //LogF(" Ext2_initDevice: groupCount = %i\n", groupCount);
\r
95 // Allocate Disk Information
\r
96 disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);
\r
98 memcpy(&disk->SuperBlock, &sb, 1024);
\r
99 disk->GroupCount = groupCount;
\r
101 // Get an inode cache handle
\r
102 disk->CacheID = Inode_GetHandle();
\r
105 //LogF(" Ext2_initDevice: s_log_block_size = 0x%x\n", sb.s_log_block_size);
\r
106 disk->BlockSize = 1024 << sb.s_log_block_size;
\r
108 // Read Group Information
\r
109 VFS_ReadAt(disk->fd,
\r
110 sb.s_first_data_block * disk->BlockSize + 1024,
\r
111 sizeof(tExt2_Group)*groupCount,
\r
115 Log(" Ext2_initDevice: Block Group 0\n");
\r
116 Log(" Ext2_initDevice: .bg_block_bitmap = 0x%x\n", disk->Groups[0].bg_block_bitmap);
\r
117 Log(" Ext2_initDevice: .bg_inode_bitmap = 0x%x\n", disk->Groups[0].bg_inode_bitmap);
\r
118 Log(" Ext2_initDevice: .bg_inode_table = 0x%x\n", disk->Groups[0].bg_inode_table);
\r
119 Log(" Ext2_initDevice: Block Group 1\n");
\r
120 Log(" Ext2_initDevice: .bg_block_bitmap = 0x%x\n", disk->Groups[1].bg_block_bitmap);
\r
121 Log(" Ext2_initDevice: .bg_inode_bitmap = 0x%x\n", disk->Groups[1].bg_inode_bitmap);
\r
122 Log(" Ext2_initDevice: .bg_inode_table = 0x%x\n", disk->Groups[1].bg_inode_table);
\r
126 Ext2_int_ReadInode(disk, 2, &inode);
\r
128 // Create Root Node
\r
129 memset(&disk->RootNode, 0, sizeof(vfs_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
135 disk->RootNode.ReadDir = Ext2_ReadDir;
\r
136 disk->RootNode.FindDir = Ext2_FindDir;
\r
137 //disk->RootNode.Relink = Ext2_Relink;
\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
146 Log(" Ext2_InitDevice: inode.i_size = 0x%x\n", inode.i_size);
\r
147 Log(" Ext2_InitDevice: inode.i_block[0] = 0x%x\n", inode.i_block[0]);
\r
150 return &disk->RootNode;
\r
154 * \fn void Ext2_Unmount(tVFS_Node *Node)
\r
155 * \brief Close a mounted device
\r
157 void Ext2_Unmount(tVFS_Node *Node)
\r
159 tExt2_Disk *disk = Node->ImplPtr;
\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
168 * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
169 * \brief Read from a file
\r
171 Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
173 tExt2_Disk *disk = Node->ImplPtr;
\r
180 Ext2_int_GetInode(Node, &inode);
\r
182 block = Offset / disk->BlockSize;
\r
183 Offset = Offset / disk->BlockSize;
\r
184 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
187 if(Length <= disk->BlockSize - Offset)
\r
189 VFS_ReadAt( disk->fd, base+Offset, Length, Buffer);
\r
193 // Read first block
\r
195 VFS_ReadAt( disk->fd, base + Offset, disk->BlockSize - Offset, Buffer);
\r
196 remLen -= disk->BlockSize - Offset;
\r
197 Buffer += disk->BlockSize - Offset;
\r
200 // Read middle blocks
\r
201 while(remLen > disk->BlockSize)
\r
203 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
204 VFS_ReadAt( disk->fd, base, disk->BlockSize, Buffer);
\r
205 Buffer += disk->BlockSize;
\r
206 remLen -= disk->BlockSize;
\r
211 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
212 VFS_ReadAt( disk->fd, base, remLen, Buffer);
\r
218 * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
219 * \brief Write to a file
\r
221 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
223 tExt2_Disk *disk = Node->ImplPtr;
\r
229 int bNewBlocks = 0;
\r
231 Ext2_int_GetInode(Node, &inode);
\r
233 // Round size up to block size
\r
234 // block size is a power of two, so this will work
\r
235 allocSize = (inode.i_size + disk->BlockSize) & ~(disk->BlockSize-1);
\r
236 if( Offset < allocSize )
\r
238 if(Offset + Length > allocSize) {
\r
240 retLen = allocSize - Offset;
\r
243 // Within the allocated space
\r
244 block = Offset / disk->BlockSize;
\r
245 Offset %= disk->BlockSize;
\r
246 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
248 // Write only block (if only one)
\r
249 if(Offset + retLen <= disk->BlockSize) {
\r
250 VFS_WriteAt(disk->fd, base+Offset, retLen, Buffer);
\r
251 if(bNewBlocks) return Length;
\r
252 goto addBlocks; // Ugh! A goto, but it seems unavoidable
\r
255 // Write First Block
\r
256 VFS_WriteAt(disk->fd, base+Offset, disk->BlockSize-Offset, Buffer);
\r
257 Buffer += disk->BlockSize-Offset;
\r
258 retLen -= disk->BlockSize-Offset;
\r
261 // Write middle blocks
\r
262 while(retLen > disk->BlockSize)
\r
264 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
265 VFS_WriteAt(disk->fd, base, disk->BlockSize, Buffer);
\r
266 Buffer += disk->BlockSize;
\r
267 retLen -= disk->BlockSize;
\r
271 // Write last block
\r
272 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
\r
273 VFS_WriteAt(disk->fd, base, retLen, Buffer);
\r
274 if(bNewBlocks) return Length; // Writing in only allocated space
\r
278 ///\todo Implement block allocation
\r
279 WarningEx("EXT2", "File extending is not yet supported");
\r
285 * \fn int Ext2_CloseFile(vfs_node *Node)
\r
286 * \brief Close a file (Remove it from the cache)
\r
288 int Ext2_CloseFile(tVFS_Node *Node)
\r
290 tExt2_Disk *disk = Node->ImplPtr;
\r
291 inode_uncacheNode(disk->CacheID, Node->impl);
\r
296 \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
\r
297 \brief Reads a directory entry
\r
299 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
\r
302 char namebuf[EXT2_NAME_LEN+1];
\r
303 tExt2_DirEnt dirent;
\r
304 Uint64 Base; // Block's Base Address
\r
305 int block = 0, ofs = 0;
\r
307 tExt2_Disk *disk = Node->ImplPtr;
\r
308 Uint64 vfsInode = 0;
\r
309 tVFS_Node *retNode;
\r
312 ENTER("pNode iPos", Node, Pos);
\r
314 // Read directory's inode
\r
315 Ext2_int_GetInode(Node, &inode);
\r
316 size = inode.i_size;
\r
318 LOG("inode.i_block[0] = 0x%x\n", inode.i_block[0]);
\r
322 // - Do this ourselves as it is a simple operation
\r
323 Base = inode.i_block[0] * disk->BlockSize;
\r
324 while(Pos -- && size > 0)
\r
326 VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
\r
327 ofs += dirent.rec_len;
\r
328 size -= dirent.rec_len;
\r
331 if(ofs >= disk->BlockSize) {
\r
333 if( ofs > disk->BlockSize ) {
\r
334 Warning("[EXT2] Directory Entry %i of inode %i ('%s') extends over a block boundary, ignoring\n",
\r
335 entNum-1, Node->impl, Node->name);
\r
338 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
\r
342 if(size <= 0) return NULL;
\r
345 VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
\r
346 //LOG(" Ext2_ReadDir: dirent.inode = %i\n", dirent.inode);
\r
347 //LOG(" Ext2_ReadDir: dirent.rec_len = %i\n", dirent.rec_len);
\r
348 //LOG(" Ext2_ReadDir: dirent.name_len = %i\n", dirent.name_len);
\r
349 VFS_ReadAt( disk->fd, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
\r
350 namebuf[ dirent.name_len ] = '\0'; // Cap off string
\r
353 // Ignore . and .. (these are done in the VFS)
\r
354 if( (namebuf[0] == '.' && namebuf[1] == '\0')
\r
355 || (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0'))
\r
356 LEAVE('p', VFS_SKIP);
\r
357 return VFS_SKIP; // Skip
\r
360 LEAVE('s', namebuf);
\r
362 return strdup(namebuf);
\r
366 \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)
\r
367 \brief Gets information about a file
\r
368 \param node vfs node - Parent Node
\r
369 \param filename String - Name of file
\r
370 \return VFS Node of file
\r
372 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)
\r
374 tExt2_Disk *disk = Node->ImplPtr;
\r
376 char namebuf[EXT2_NAME_LEN+1];
\r
377 tExt2_DirEnt dirent;
\r
378 Uint64 Base; // Block's Base Address
\r
379 int block = 0, ofs = 0;
\r
383 // Read directory's inode
\r
384 Ext2_int_GetInode(Node, &inode);
\r
385 size = inode.i_size;
\r
388 // - Do this ourselves as it is a simple operation
\r
389 Base = inode.i_block[0] * disk->BlockSize;
\r
393 VFS_ReadAt( disk->fd, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
\r
394 VFS_ReadAt( disk->fd, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
\r
395 namebuf[ dirent.name_len ] = '\0'; // Cap off string
\r
396 // If it matches, create a node and return it
\r
397 if(strcmp(namebuf, Filename) == 0)
\r
398 return Ext2_int_CreateNode( disk, dirent.inode, namebuf );
\r
399 // Increment pointers
\r
400 ofs += dirent.rec_len;
\r
401 size -= dirent.rec_len;
\r
404 // Check for end of block
\r
405 if(ofs >= disk->BlockSize) {
\r
407 if( ofs > disk->BlockSize ) {
\r
408 Warnin("[EXT2 ] Directory Entry %i of inode %i ('%s') extends over a block boundary, ignoring\n",
\r
409 entNum-1, Node->impl, Node->name);
\r
412 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
\r
420 * \fn tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, char *Name, int Flags)
\r
421 * \brief Create a new node
\r
423 tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, char *Name, int Flags)
\r
428 //==================================
\r
429 //= INTERNAL FUNCTIONS =
\r
430 //==================================
\r
435 \fn int Ext2_int_GetInode(vfs_node *Node, tExt2_Inode *Inode)
\r
436 \brief Gets the inode descriptor for a node
\r
437 \param node node to get the Inode of
\r
438 \param inode Destination
\r
440 int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)
\r
442 return Ext2_int_ReadInode(Node->ImplPtr, Node->Inode, Inode);
\r
446 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
\r
447 * \brief Create a new VFS Node
\r
449 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
\r
453 tVFS_Node *tmpNode;
\r
455 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
\r
458 if( (tmpNode = inode_getCache(Disk->CacheID, InodeID)) )
\r
463 retNode.Inode = InodeID;
\r
464 retNode.ImplPtr = Disk;
\r
467 retNode.Size = inode.i_size;
\r
469 // Set Access Permissions
\r
470 retNode.UID = inode.i_uid;
\r
471 retNode.GID = inode.i_gid;
\r
472 retNode.NumACLs = 3;
\r
473 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
\r
475 // Set Function Pointers
\r
476 retNode.Read = Ext2_Read;
\r
477 retNode.Write = Ext2_Write;
\r
478 retNode.Close = Ext2_CloseFile;
\r
480 switch(inode.i_mode & EXT2_S_IFMT)
\r
484 retNode.Flags = VFS_FFLAG_SYMLINK;
\r
492 retNode.ReadRir = Ext2_ReadDir;
\r
493 retNode.FindDir = Ext2_FindDir;
\r
494 retNode.MkNod = Ext2_MkNod;
\r
495 //retNode.Relink = Ext2_Relink;
\r
496 retNode.Flags = VFS_FFLAG_DIRECTORY;
\r
498 // Unknown, Write protect and hide it to be safe
\r
500 retNode.flags = VFS_FFLAG_READONLY|VFS_FFLAG_HIDDEN;
\r
504 // Check if the file should be hidden
\r
505 if(Name[0] == '.') retNode.Flags |= VFS_FFLAG_HIDDEN;
\r
508 retNode.ATime = now();
\r
509 retNode.MTime = inode.i_mtime * 1000;
\r
510 retNode.CTime = inode.i_ctime * 1000;
\r
512 // Save in node cache and return saved node
\r
513 return Inode_CacheNode(Disk->CacheID, &retNode);
\r
517 * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
\r
518 * \brief Read an inode into memory
\r
520 int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
\r
524 //LogF("Ext2_int_ReadInode: (Disk=%p, InodeId=%i, Inode=%p)\n", Disk, InodeId, Inode);
\r
526 if(InodeId == 0) return 0;
\r
528 InodeId --; // Inodes are numbered starting at 1
\r
530 group = InodeId / Disk->SuperBlock.s_inodes_per_group;
\r
531 subId = InodeId % Disk->SuperBlock.s_inodes_per_group;
\r
533 //LogF(" Ext2_int_ReadInode: group=%i, subId = %i\n", group, subId);
\r
535 //Seek to Block - Absolute
\r
536 vfs_seek(Disk->fd, Disk->Groups[group].bg_inode_table * Disk->BlockSize, SEEK_SET);
\r
537 //Seeek to inode - Relative
\r
538 vfs_seek(Disk->fd, sizeof(tExt2_Inode)*subId, SEEK_CUR);
\r
539 vfs_read(Disk->fd, sizeof(tExt2_Inode), Inode);
\r
544 * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
\r
545 * \brief Get the address of a block from an inode's list
\r
546 * \param Disk Disk information structure
\r
547 * \param Blocks Pointer to an inode's block list
\r
548 * \param BlockNum Block index in list
\r
550 Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
\r
555 return (Uint64)Blocks[BlockNum] * Disk->BlockSize;
\r
557 // Single Indirect Blocks
\r
558 iBlocks = malloc( Disk->BlockSize );
\r
559 VFS_ReadAt(Disk->fd, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
562 if(BlockNum < 256) {
\r
563 BlockNum = iBlocks[BlockNum];
\r
565 return (Uint64)BlockNum * Disk->BlockSize;
\r
568 // Double Indirect Blocks
\r
569 if(BlockNum < 256*256)
\r
571 VFS_ReadAt(Disk->fd, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
572 VFS_ReadAt(Disk->fd, (Uint64)iBlocks[BlockNum/256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
573 BlockNum = iBlocks[BlockNum%256];
\r
575 return (Uint64)BlockNum * Disk->BlockSize;
\r
577 // Triple Indirect Blocks
\r
578 VFS_ReadAt(Disk->fd, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
579 VFS_ReadAt(Disk->fd, (Uint64)iBlocks[BlockNum/(256*256)]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
580 VFS_ReadAt(Disk->fd, (Uint64)iBlocks[(BlockNum/256)%256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
\r
581 BlockNum = iBlocks[BlockNum%256];
\r
583 return (Uint64)BlockNum * Disk->BlockSize;
\r