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, Uint Flags);
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);
23 tVFS_NodeType gExt2_DirType = {
24 .TypeName = "ext2-dir",
25 .ReadDir = Ext2_ReadDir,
26 .FindDir = Ext2_FindDir,
28 .Unlink = Ext2_Unlink,
30 .Close = Ext2_CloseFile
32 tVFS_NodeType gExt2_FileType = {
33 .TypeName = "ext2-file",
36 .Close = Ext2_CloseFile
41 * \brief Reads a directory entry
42 * \param Node Directory node
43 * \param Pos Position of desired element
45 int Ext2_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
47 tExt2_Inode *inode = (void*)(Node+1);
49 Uint64 Base; // Block's Base Address
53 tExt2_Disk *disk = Node->ImplPtr;
56 ENTER("pNode iPos", Node, Pos);
58 // Read directory's inode
61 LOG("inode={.i_block[0]= 0x%x, .i_size=0x%x}", inode->i_block[0], inode->i_size);
65 // - Do this ourselves as it is a simple operation
66 Base = inode->i_block[0] * disk->BlockSize;
68 while(Pos -- && size > 0 && size <= inode->i_size)
70 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
72 if( dirent.rec_len == 0 ) {
77 ofs += dirent.rec_len;
78 size -= dirent.rec_len;
81 if(ofs >= disk->BlockSize) {
83 if( ofs > disk->BlockSize ) {
84 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
85 entNum-1, Node->Inode);
88 Base = Ext2_int_GetBlockAddr( disk, inode->i_block, block );
96 // Check for the end of the list
97 if(size <= 0 || size > inode->i_size) {
103 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
104 LOG("dirent={.rec_len=%i,.inode=0x%x,.name_len=%i}",
105 dirent.rec_len, dirent.inode, dirent.name_len);
106 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
108 if( dirent.name_len == 0 ) {
113 // Ignore . and .. (these are done in the VFS)
114 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
115 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
120 LOG("Name '%s'", dirent.name);
121 strncpy(Dest, dirent.name, FILENAME_MAX);
127 * \brief Gets information about a file
128 * \param Node Parent Node
129 * \param Filename Name of wanted file
130 * \return VFS Node of file
132 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
134 tExt2_Disk *disk = Node->ImplPtr;
135 tExt2_Inode *inode = (void*)(Node+1);
137 Uint64 Base; // Block's Base Address
142 int filenameLen = strlen(Filename);
144 // Read directory's inode
145 size = inode->i_size;
148 // - Do this ourselves as it is a simple operation
149 Base = inode->i_block[0] * disk->BlockSize;
153 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
154 // If it matches, create a node and return it
155 if(dirent.name_len == filenameLen && strncmp(dirent.name, Filename, filenameLen) == 0)
156 return Ext2_int_CreateNode( disk, dirent.inode );
157 if( dirent.rec_len == 0 )
159 // Increment pointers
160 ofs += dirent.rec_len;
161 size -= dirent.rec_len;
164 // Check for end of block
165 if(ofs >= disk->BlockSize) {
167 if( ofs > disk->BlockSize ) {
168 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
169 entNum-1, Node->Inode);
172 Base = Ext2_int_GetBlockAddr( disk, inode->i_block, block );
182 * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
183 * \brief Create a new node
185 tVFS_Node *Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
187 ENTER("pParent sName xFlags", Parent, Name, Flags);
189 Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
190 if( inodeNum == 0 ) {
191 LOG("Inode allocation failed");
192 LEAVE_RET('n', NULL);
194 tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
196 Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
197 Log_Warning("Ext2", "Ext2_MkNod - Node creation failed");
198 LEAVE_RET('n', NULL);
201 child->Flags = Flags & (VFS_FFLAG_DIRECTORY|VFS_FFLAG_SYMLINK|VFS_FFLAG_READONLY);
202 child->UID = Threads_GetUID();
203 child->GID = Threads_GetGID();
208 child->ImplInt = 0; // ImplInt is the link count
211 int rv = Ext2_Link(Parent, Name, child);
213 Ext2_CloseFile(child);
216 LEAVE_RET('p', child);
220 * \brief Rename a file
221 * \param Node This (directory) node
222 * \param OldName Old name of file
223 * \param NewName New name for file
224 * \return Boolean Failure - See ::tVFS_Node.Unlink for info
226 int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
228 Log_Warning("Ext2", "TODO: Impliment Ext2_Unlink");
233 * \brief Links an existing node to a new name
234 * \param Parent Parent (directory) node
235 * \param Name New name for the node
236 * \param Node Node to link
237 * \return Boolean Failure - See ::tVFS_Node.Link for info
239 int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
241 tExt2_Disk *disk = Node->ImplPtr;
242 tExt2_Inode *inode = (void*)(Node+1);
243 tExt2_DirEnt *dirent;
244 tExt2_DirEnt newEntry;
245 Uint64 base; // Block's Base Address
246 int block = 0, ofs = 0;
249 int bestSize=0, bestBlock=0, bestOfs=0, bestNeedsSplit=0;
252 ENTER("pNode sName pChild",
255 void *blockData = malloc(disk->BlockSize);
257 // Create a stub entry
258 newEntry.inode = Child->Inode;
259 newEntry.name_len = strlen(Name);
260 newEntry.rec_len = ((newEntry.name_len+3)&~3) + EXT2_DIRENT_SIZE;
261 newEntry.type = inode->i_mode >> 12;
262 memcpy(newEntry.name, Name, newEntry.name_len);
264 // Read directory's inode
265 size = inode->i_size;
267 // Get a lock on the inode
268 //Ext2_int_LockInode(disk, Node->Inode);
269 Mutex_Acquire(&Node->Lock);
271 // if( !Node->Data ) {
275 // - Do this ourselves as it is a simple operation
276 base = inode->i_block[0] * disk->BlockSize;
277 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
283 dirent = blockData + ofs;
284 // Sanity Check the entry
285 if(ofs + dirent->rec_len > disk->BlockSize) {
287 "Directory entry %i of inode 0x%x extends over a block boundary",
288 nEntries, (Uint)Node->Inode);
292 LOG("Entry %i: %x %i bytes", nEntries, dirent->type, dirent->rec_len);
294 if(dirent->type == 0)
296 if( dirent->rec_len >= newEntry.rec_len
297 && (bestMatch == -1 || bestSize > dirent->rec_len) )
299 bestMatch = nEntries;
300 bestSize = dirent->rec_len;
306 // Non free - check name to avoid duplicates
309 LOG(" name='%.*s'", dirent->name_len, dirent->name);
310 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
311 //Ext2_int_UnlockInode(disk, Node->Inode);
315 int spare_space = dirent->rec_len - (dirent->name_len + EXT2_DIRENT_SIZE);
316 if( spare_space > newEntry.rec_len
317 && (bestMatch == -1 || bestSize > spare_space) )
319 bestMatch = nEntries;
320 bestSize = spare_space;
328 // Increment the pointer
330 ofs += dirent->rec_len;
331 size -= dirent->rec_len;
332 if( size > 0 && ofs >= disk->BlockSize ) {
333 // Read the next block if needed
334 // BLOCK_DIR_OFS(Node->Data, block) = nEntries;
337 base = Ext2_int_GetBlockAddr(disk, inode->i_block, block);
338 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
342 LOG("bestMatch = %i", bestMatch);
343 // If EOF was reached with no space, check if we can fit one on the end
344 if( bestMatch < 0 && ofs + newEntry.rec_len < disk->BlockSize ) {
345 Node->Size += newEntry.rec_len;
346 Node->Flags |= VFS_FFLAG_DIRTY;
349 bestSize = newEntry.rec_len;
352 // Check if a free slot was found
356 base = Ext2_int_GetBlockAddr(disk, inode->i_block, bestBlock);
357 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
358 dirent = blockData + bestOfs;
359 // Shorten a pre-existing entry
362 dirent->rec_len = EXT2_DIRENT_SIZE + dirent->name_len;
363 bestOfs += dirent->rec_len;
364 //bestSize -= dirent->rec_len; // (not needed, bestSize is the spare space after)
365 dirent = blockData + bestOfs;
367 // Insert new file entry
368 memcpy(dirent, &newEntry, newEntry.rec_len);
369 // Create a new blank entry
370 if( bestSize != newEntry.rec_len )
372 bestOfs += newEntry.rec_len;
373 dirent = blockData + bestOfs;
375 dirent->rec_len = bestSize - newEntry.rec_len;
379 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
382 // Allocate block, Write
383 Uint32 newblock = Ext2_int_AllocateBlock(disk, base / disk->BlockSize);
384 Ext2_int_AppendBlock(Node, inode, newblock);
385 base = newblock * disk->BlockSize;
386 Node->Size += newEntry.rec_len;
387 Node->Flags |= VFS_FFLAG_DIRTY;
388 memcpy(blockData, &newEntry, newEntry.rec_len);
389 memset(blockData + newEntry.rec_len, 0, disk->BlockSize - newEntry.rec_len);
390 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
394 Child->Flags |= VFS_FFLAG_DIRTY;
396 //Ext2_int_UnlockInode(disk, Node->Inode);
398 Mutex_Release(&Node->Lock);
403 Mutex_Release(&Node->Lock);