3 * - By John Hodge (thePowersGang)
10 #include "ext2_common.h"
13 #define BLOCK_DIR_OFS(_data, _block) (((Uint16*)(_data))[(_block)])
16 int Ext2_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]);
17 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
18 tVFS_Node *Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
19 int Ext2_Unlink(tVFS_Node *Node, const char *OldName);
20 int Ext2_Link(tVFS_Node *Parent, const char *Name, tVFS_Node *Node);
22 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
25 tVFS_NodeType gExt2_DirType = {
26 .TypeName = "ext2-dir",
27 .ReadDir = Ext2_ReadDir,
28 .FindDir = Ext2_FindDir,
30 .Unlink = Ext2_Unlink,
32 .Close = Ext2_CloseFile
34 tVFS_NodeType gExt2_FileType = {
35 .TypeName = "ext2-file",
38 .Close = Ext2_CloseFile
43 * \brief Reads a directory entry
44 * \param Node Directory node
45 * \param Pos Position of desired element
47 int Ext2_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
51 Uint64 Base; // Block's Base Address
55 tExt2_Disk *disk = Node->ImplPtr;
58 ENTER("pNode iPos", Node, Pos);
60 // Read directory's inode
61 Ext2_int_ReadInode(disk, Node->Inode, &inode);
64 LOG("inode={.i_block[0]= 0x%x, .i_size=0x%x}", inode.i_block[0], inode.i_size);
68 // - Do this ourselves as it is a simple operation
69 Base = inode.i_block[0] * disk->BlockSize;
71 while(Pos -- && size > 0 && size <= inode.i_size)
73 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
74 ofs += dirent.rec_len;
75 size -= dirent.rec_len;
78 if(ofs >= disk->BlockSize) {
80 if( ofs > disk->BlockSize ) {
81 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
82 entNum-1, Node->Inode);
85 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
93 // Check for the end of the list
94 if(size <= 0 || size > inode.i_size) {
100 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
101 LOG("dirent={.rec_len=%i,.inode=0x%x,.name_len=%i}",
102 dirent.rec_len, dirent.inode, dirent.name_len);
103 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
105 if( dirent.name_len == 0 ) {
110 // Ignore . and .. (these are done in the VFS)
111 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
112 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
117 LOG("Name '%s'", dirent.name);
118 strncpy(Dest, dirent.name, FILENAME_MAX);
124 * \brief Gets information about a file
125 * \param Node Parent Node
126 * \param Filename Name of wanted file
127 * \return VFS Node of file
129 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
131 tExt2_Disk *disk = Node->ImplPtr;
134 Uint64 Base; // Block's Base Address
139 int filenameLen = strlen(Filename);
141 // Read directory's inode
142 Ext2_int_ReadInode(disk, Node->Inode, &inode);
146 // - Do this ourselves as it is a simple operation
147 Base = inode.i_block[0] * disk->BlockSize;
151 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
152 // TODO: Possible overrun if name_len == 255?
153 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
154 // If it matches, create a node and return it
155 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
156 return Ext2_int_CreateNode( disk, dirent.inode );
157 // Increment pointers
158 ofs += dirent.rec_len;
159 size -= dirent.rec_len;
162 // Check for end of block
163 if(ofs >= disk->BlockSize) {
165 if( ofs > disk->BlockSize ) {
166 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
167 entNum-1, Node->Inode);
170 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
178 * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
179 * \brief Create a new node
181 tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
183 ENTER("pParent sName xFlags", Parent, Name, Flags);
185 Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
186 if( inodeNum == 0 ) {
187 LOG("Inode allocation failed");
188 LEAVE_RET('n', NULL);
190 tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
192 Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
193 Log_Warning("Ext2", "Ext2_MkNod - Node creation failed");
194 LEAVE_RET('n', NULL);
197 child->Flags = Flags & (VFS_FFLAG_DIRECTORY|VFS_FFLAG_SYMLINK|VFS_FFLAG_READONLY);
198 child->UID = Threads_GetUID();
199 child->GID = Threads_GetGID();
204 child->ImplInt = 0; // ImplInt is the link count
207 int rv = Ext2_Link(Parent, Name, child);
209 Ext2_CloseFile(child);
212 LEAVE_RET('p', child);
216 * \brief Rename a file
217 * \param Node This (directory) node
218 * \param OldName Old name of file
219 * \param NewName New name for file
220 * \return Boolean Failure - See ::tVFS_Node.Unlink for info
222 int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
224 Log_Warning("Ext2", "TODO: Impliment Ext2_Unlink");
229 * \brief Links an existing node to a new name
230 * \param Parent Parent (directory) node
231 * \param Name New name for the node
232 * \param Node Node to link
233 * \return Boolean Failure - See ::tVFS_Node.Link for info
235 int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
238 tExt2_Disk *disk = Node->ImplPtr;
240 tExt2_DirEnt *dirent;
241 tExt2_DirEnt newEntry;
242 Uint64 base; // Block's Base Address
243 int block = 0, ofs = 0;
247 int bestSize=0, bestBlock=0, bestOfs=0, bestNeedsSplit=0;
250 ENTER("pNode sName pChild",
253 blockData = malloc(disk->BlockSize);
255 // Read child inode (get's the file type)
256 Ext2_int_ReadInode(disk, Child->Inode, &inode);
258 // Create a stub entry
259 newEntry.inode = Child->Inode;
260 newEntry.name_len = strlen(Name);
261 newEntry.rec_len = ((newEntry.name_len+3)&~3) + EXT2_DIRENT_SIZE;
262 newEntry.type = inode.i_mode >> 12;
263 memcpy(newEntry.name, Name, newEntry.name_len);
265 // Read directory's inode
266 Ext2_int_ReadInode(disk, Node->Inode, &inode);
269 // Get a lock on the inode
270 //Ext2_int_LockInode(disk, Node->Inode);
271 Mutex_Acquire(&Node->Lock);
273 // if( !Node->Data ) {
277 // - Do this ourselves as it is a simple operation
278 base = inode.i_block[0] * disk->BlockSize;
279 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
285 dirent = blockData + ofs;
286 // Sanity Check the entry
287 if(ofs + dirent->rec_len > disk->BlockSize) {
289 "Directory entry %i of inode 0x%x extends over a block boundary",
290 nEntries, (Uint)Node->Inode);
294 LOG("Entry %i: %x %i bytes", nEntries, dirent->type, dirent->rec_len);
296 if(dirent->type == 0)
298 if( dirent->rec_len >= newEntry.rec_len
299 && (bestMatch == -1 || bestSize > dirent->rec_len) )
301 bestMatch = nEntries;
302 bestSize = dirent->rec_len;
308 // Non free - check name to avoid duplicates
311 LOG(" name='%.*s'", dirent->name_len, dirent->name);
312 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
313 //Ext2_int_UnlockInode(disk, Node->Inode);
314 Mutex_Release(&Node->Lock);
319 int spare_space = dirent->rec_len - (dirent->name_len + EXT2_DIRENT_SIZE);
320 if( spare_space > newEntry.rec_len
321 && (bestMatch == -1 || bestSize > spare_space) )
323 bestMatch = nEntries;
324 bestSize = spare_space;
332 // Increment the pointer
334 ofs += dirent->rec_len;
335 size -= dirent->rec_len;
336 if( size > 0 && ofs >= disk->BlockSize ) {
337 // Read the next block if needed
338 // BLOCK_DIR_OFS(Node->Data, block) = nEntries;
341 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
342 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
346 LOG("bestMatch = %i", bestMatch);
347 // If EOF was reached with no space, check if we can fit one on the end
348 if( bestMatch < 0 && ofs + newEntry.rec_len < disk->BlockSize ) {
349 Node->Size += newEntry.rec_len;
350 Node->Flags |= VFS_FFLAG_DIRTY;
353 bestSize = newEntry.rec_len;
356 // Check if a free slot was found
360 base = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
361 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
362 dirent = blockData + bestOfs;
363 // Shorten a pre-existing entry
366 dirent->rec_len = EXT2_DIRENT_SIZE + dirent->name_len;
367 bestOfs += dirent->rec_len;
368 //bestSize -= dirent->rec_len; // (not needed, bestSize is the spare space after)
369 dirent = blockData + bestOfs;
371 // Insert new file entry
372 memcpy(dirent, &newEntry, newEntry.rec_len);
373 // Create a new blank entry
374 if( bestSize != newEntry.rec_len )
376 bestOfs += newEntry.rec_len;
377 dirent = blockData + bestOfs;
379 dirent->rec_len = bestSize - newEntry.rec_len;
383 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
386 // Allocate block, Write
387 Uint32 newblock = Ext2_int_AllocateBlock(disk, base / disk->BlockSize);
388 Ext2_int_AppendBlock(disk, &inode, newblock);
389 base = newblock * disk->BlockSize;
390 Node->Size += newEntry.rec_len;
391 Node->Flags |= VFS_FFLAG_DIRTY;
392 memcpy(blockData, &newEntry, newEntry.rec_len);
393 memset(blockData + newEntry.rec_len, 0, disk->BlockSize - newEntry.rec_len);
394 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
398 Child->Flags |= VFS_FFLAG_DIRTY;
400 //Ext2_int_UnlockInode(disk, Node->Inode);
401 Mutex_Release(&Node->Lock);
405 Log_Warning("Ext2", "TODO: Impliment Ext2_Link");
410 // ---- INTERNAL FUNCTIONS ----
412 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
413 * \brief Create a new VFS Node
415 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
421 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
424 if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
427 memset(&retNode, 0, sizeof(retNode));
430 retNode.Inode = InodeID;
431 retNode.ImplPtr = Disk;
432 retNode.ImplInt = inode.i_links_count;
435 retNode.Size = inode.i_size;
437 // Set Access Permissions
438 retNode.UID = inode.i_uid;
439 retNode.GID = inode.i_gid;
441 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
443 // Set Function Pointers
444 retNode.Type = &gExt2_FileType;
446 switch(inode.i_mode & EXT2_S_IFMT)
450 retNode.Flags = VFS_FFLAG_SYMLINK;
455 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
459 retNode.Type = &gExt2_DirType;
460 retNode.Flags = VFS_FFLAG_DIRECTORY;
461 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
463 // Unknown, Write protect it to be safe
465 retNode.Flags = VFS_FFLAG_READONLY;
470 retNode.ATime = inode.i_atime * 1000;
471 retNode.MTime = inode.i_mtime * 1000;
472 retNode.CTime = inode.i_ctime * 1000;
474 // Save in node cache and return saved node
475 return Inode_CacheNode(Disk->CacheID, &retNode);
478 int Ext2_int_WritebackNode(tExt2_Disk *Disk, tVFS_Node *Node)
480 Log_Warning("Ext2","TODO: Impliment Ext2_int_WritebackNode");