3 * - By John Hodge (thePowersGang)
10 #include "ext2_common.h"
13 #define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
16 char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
17 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
18 int 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 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
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", inode.i_block[0]);
68 // - Do this ourselves as it is a simple operation
69 Base = inode.i_block[0] * disk->BlockSize;
71 while(Pos -- && size > 0)
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 );
89 // Check for the end of the list
96 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
97 //LOG("dirent.inode = %i", dirent.inode);
98 //LOG("dirent.rec_len = %i", dirent.rec_len);
99 //LOG("dirent.name_len = %i", dirent.name_len);
100 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
103 // Ignore . and .. (these are done in the VFS)
104 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
105 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
106 LEAVE('p', VFS_SKIP);
107 return VFS_SKIP; // Skip
110 LEAVE('s', dirent.name);
112 return strdup(dirent.name);
116 * \brief Gets information about a file
117 * \param Node Parent Node
118 * \param Filename Name of wanted file
119 * \return VFS Node of file
121 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
123 tExt2_Disk *disk = Node->ImplPtr;
126 Uint64 Base; // Block's Base Address
131 int filenameLen = strlen(Filename);
133 // Read directory's inode
134 Ext2_int_ReadInode(disk, Node->Inode, &inode);
138 // - Do this ourselves as it is a simple operation
139 Base = inode.i_block[0] * disk->BlockSize;
143 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
144 // TODO: Possible overrun if name_len == 255?
145 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
146 // If it matches, create a node and return it
147 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
148 return Ext2_int_CreateNode( disk, dirent.inode );
149 // Increment pointers
150 ofs += dirent.rec_len;
151 size -= dirent.rec_len;
154 // Check for end of block
155 if(ofs >= disk->BlockSize) {
157 if( ofs > disk->BlockSize ) {
158 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
159 entNum-1, Node->Inode);
162 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
170 * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
171 * \brief Create a new node
173 int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
179 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
181 memset(&inode, 0, sizeof(tExt2_Inode));
185 if( Flags & VFS_FFLAG_READONLY )
186 inode.i_mode &= ~0222;
187 if( Flags & VFS_FFLAG_SYMLINK )
188 inode.i_mode |= EXT2_S_IFLNK;
189 else if( Flags & VFS_FFLAG_DIRECTORY )
190 inode.i_mode |= EXT2_S_IFDIR | 0111;
192 inode.i_uid = Threads_GetUID();
193 inode.i_gid = Threads_GetGID();
196 inode.i_atime = now() / 1000;
198 child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
199 return Ext2_Link(Parent, child, Name);
206 * \brief Rename a file
207 * \param Node This (directory) node
208 * \param OldName Old name of file
209 * \param NewName New name for file
210 * \return Boolean Failure - See ::tVFS_Node.Unlink for info
212 int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
218 * \brief Links an existing node to a new name
219 * \param Parent Parent (directory) node
220 * \param Name New name for the node
221 * \param Node Node to link
222 * \return Boolean Failure - See ::tVFS_Node.Link for info
224 int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
227 tExt2_Disk *disk = Node->ImplPtr;
230 tExt2_DirEnt newEntry;
231 Uint64 Base; // Block's Base Address
232 int block = 0, ofs = 0;
235 int bestMatch = -1, bestSize, bestBlock, bestOfs;
238 blockData = malloc(disk->BlockSize);
240 // Read child inode (get's the file type)
241 Ext2_int_ReadInode(disk, Child->Inode, &inode);
243 // Create a stub entry
244 newEntry.inode = Child->Inode;
245 newEntry.name_len = strlen(Name);
246 newEntry.rec_len = (newEntry.name_len+3+8)&~3;
247 newEntry.type = inode.i_mode >> 12;
248 memcpy(newEntry.name, Name, newEntry.name_len);
250 // Read directory's inode
251 Ext2_int_ReadInode(disk, Node->Inode, &inode);
254 // Get a lock on the inode
255 Ext2_int_LockInode(disk, Node->Inode);
258 // - Do this ourselves as it is a simple operation
259 base = inode.i_block[0] * disk->BlockSize;
260 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
265 dirent = blockData + ofs;
266 // Sanity Check the entry
267 if(ofs + dirent->rec_len > disk->BlockSize) {
269 "Directory entry %i of inode 0x%x extends over a block boundary",
270 nEntries, (Uint)Node->Inode);
275 if(dirent->type == 0) {
276 if( dirent->rec_len >= newEntry.rec_len
277 && (bestMatch == -1 || bestSize > dirent->rec_len) )
279 bestMatch = nEntries;
280 bestSize = dirent->rec_len;
285 // Non free - check name to avoid duplicates
287 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
288 Ext2_int_UnlockInode(disk, Node->Inode);
294 // Increment the pointer
296 ofs += dirent->rec_len;
297 if( ofs >= disk->BlockSize ) {
298 // Read the next block if needed
299 BLOCK_DIR_OFS(Node->Data, block) = nEntries;
302 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
303 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
307 // Check if a free slot was found
308 if( bestMatch >= 0 ) {
310 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
312 bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
313 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
314 dirent = blockData + bestOfs;
315 memcpy(dirent, newEntry, newEntry.rec_len);
316 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
319 // Allocate block, Write
320 block = Ext2_int_AllocateBlock(Disk, block);
321 Log_Warning("EXT2", "");
324 Ext2_int_UnlockInode(disk, Node->Inode);
331 // ---- INTERNAL FUNCTIONS ----
333 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
334 * \brief Create a new VFS Node
336 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
342 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
345 if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
350 retNode.Inode = InodeID;
351 retNode.ImplPtr = Disk;
354 retNode.Size = inode.i_size;
357 // Set Access Permissions
358 retNode.UID = inode.i_uid;
359 retNode.GID = inode.i_gid;
361 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
363 // Set Function Pointers
364 retNode.Type = &gExt2_FileType;
366 switch(inode.i_mode & EXT2_S_IFMT)
370 retNode.Flags = VFS_FFLAG_SYMLINK;
375 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
379 retNode.Type = &gExt2_DirType;
380 retNode.Flags = VFS_FFLAG_DIRECTORY;
381 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
383 // Unknown, Write protect it to be safe
385 retNode.Flags = VFS_FFLAG_READONLY;
390 retNode.ATime = inode.i_atime * 1000;
391 retNode.MTime = inode.i_mtime * 1000;
392 retNode.CTime = inode.i_ctime * 1000;
394 // Save in node cache and return saved node
395 return Inode_CacheNode(Disk->CacheID, &retNode);