3 * Ext2 Driver Version 1
7 * \brief Second Extended Filesystem Driver
8 * \todo Implement file full write support
12 #include "ext2_common.h"
15 #define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
18 char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
19 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *FileName);
20 int Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
21 int Ext2_Relink(tVFS_Node *Node, char *OldName, char *NewName);
22 int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, char *Name);
24 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
28 * \brief Reads a directory entry
29 * \param Node Directory node
30 * \param Pos Position of desired element
32 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
36 Uint64 Base; // Block's Base Address
37 int block = 0, ofs = 0;
39 tExt2_Disk *disk = Node->ImplPtr;
42 ENTER("pNode iPos", Node, Pos);
44 // Read directory's inode
45 Ext2_int_ReadInode(disk, Node->Inode, &inode);
48 LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
52 // - Do this ourselves as it is a simple operation
53 Base = inode.i_block[0] * disk->BlockSize;
55 while(Pos -- && size > 0)
57 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
58 ofs += dirent.rec_len;
59 size -= dirent.rec_len;
62 if(ofs >= disk->BlockSize) {
64 if( ofs > disk->BlockSize ) {
65 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
66 entNum-1, Node->Inode);
69 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
73 // Check for the end of the list
80 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
81 //LOG("dirent.inode = %i", dirent.inode);
82 //LOG("dirent.rec_len = %i", dirent.rec_len);
83 //LOG("dirent.name_len = %i", dirent.name_len);
84 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
87 // Ignore . and .. (these are done in the VFS)
88 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
89 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
91 return VFS_SKIP; // Skip
94 LEAVE('s', dirent.name);
96 return strdup(dirent.name);
100 * \brief Gets information about a file
101 * \param Node Parent Node
102 * \param Filename Name of wanted file
103 * \return VFS Node of file
105 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)
107 tExt2_Disk *disk = Node->ImplPtr;
110 Uint64 Base; // Block's Base Address
111 int block = 0, ofs = 0;
114 int filenameLen = strlen(Filename);
116 // Read directory's inode
117 Ext2_int_ReadInode(disk, Node->Inode, &inode);
121 // - Do this ourselves as it is a simple operation
122 Base = inode.i_block[0] * disk->BlockSize;
126 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
127 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
128 // If it matches, create a node and return it
129 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
130 return Ext2_int_CreateNode( disk, dirent.inode );
131 // Increment pointers
132 ofs += dirent.rec_len;
133 size -= dirent.rec_len;
136 // Check for end of block
137 if(ofs >= disk->BlockSize) {
139 if( ofs > disk->BlockSize ) {
140 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
141 entNum-1, Node->Inode);
144 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
152 * \fn int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
153 * \brief Create a new node
155 int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
161 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
163 memset(&inode, 0, sizeof(tExt2_Inode));
167 if( Flags & VFS_FFLAG_READONLY )
168 inode.i_mode &= ~0222;
169 if( Flags & VFS_FFLAG_SYMLINK )
170 inode.i_mode |= EXT2_S_IFLNK;
171 else if( Flags & VFS_FFLAG_DIRECTORY )
172 inode.i_mode |= EXT2_S_IFDIR | 0111;
174 inode.i_uid = Threads_GetUID();
175 inode.i_gid = Threads_GetGID();
178 inode.i_atime = now() / 1000;
180 child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
181 return Ext2_Link(Parent, child, Name);
188 * \brief Rename a file
189 * \param Node This (directory) node
190 * \param OldName Old name of file
191 * \param NewName New name for file
192 * \return Boolean Failure - See ::tVFS_Node.Relink for info
194 int Ext2_Relink(tVFS_Node *Node, char *OldName, char *NewName)
200 * \brief Links an existing node to a new name
201 * \param Parent Parent (directory) node
202 * \param Node Node to link
203 * \param Name New name for the node
204 * \return Boolean Failure - See ::tVFS_Node.Link for info
206 int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, char *Name)
209 tExt2_Disk *disk = Node->ImplPtr;
212 tExt2_DirEnt newEntry;
213 Uint64 Base; // Block's Base Address
214 int block = 0, ofs = 0;
217 int bestMatch = -1, bestSize, bestBlock, bestOfs;
220 blockData = malloc(disk->BlockSize);
222 // Read child inode (get's the file type)
223 Ext2_int_ReadInode(disk, Child->Inode, &inode);
225 // Create a stub entry
226 newEntry.inode = Child->Inode;
227 newEntry.name_len = strlen(Name);
228 newEntry.rec_len = (newEntry.name_len+3+8)&~3;
229 newEntry.type = inode.i_mode >> 12;
230 memcpy(newEntry.name, Name, newEntry.name_len);
232 // Read directory's inode
233 Ext2_int_ReadInode(disk, Node->Inode, &inode);
236 // Get a lock on the inode
237 Ext2_int_LockInode(disk, Node->Inode);
240 // - Do this ourselves as it is a simple operation
241 base = inode.i_block[0] * disk->BlockSize;
242 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
247 dirent = blockData + ofs;
248 // Sanity Check the entry
249 if(ofs + dirent->rec_len > disk->BlockSize) {
251 "Directory entry %i of inode 0x%x extends over a block boundary",
252 nEntries, (Uint)Node->Inode);
257 if(dirent->type == 0) {
258 if( dirent->rec_len >= newEntry.rec_len
259 && (bestMatch == -1 || bestSize > dirent->rec_len) )
261 bestMatch = nEntries;
262 bestSize = dirent->rec_len;
267 // Non free - check name to avoid duplicates
269 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
270 Ext2_int_UnlockInode(disk, Node->Inode);
276 // Increment the pointer
278 ofs += dirent->rec_len;
279 if( ofs >= disk->BlockSize ) {
280 // Read the next block if needed
281 BLOCK_DIR_OFS(Node->Data, block) = nEntries;
284 base = Ext2_int_GetBlockAddr(disk, inode.i_blocks, block);
285 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
289 // Check if a free slot was found
290 if( bestMatch >= 0 ) {
292 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_blocks, bestBlock);
294 bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
295 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
296 dirent = blockData + bestOfs;
297 memcpy(dirent, newEntry, newEntry.rec_len);
298 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
301 // Allocate block, Write
302 block = Ext2_int_AllocateBlock(Disk, block);
303 Log_Warning("EXT2", "");
306 Ext2_int_UnlockInode(disk, Node->Inode);
313 // ---- INTERNAL FUNCTIONS ----
315 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
316 * \brief Create a new VFS Node
318 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
324 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
327 if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
332 retNode.Inode = InodeID;
333 retNode.ImplPtr = Disk;
336 retNode.Size = inode.i_size;
339 // Set Access Permissions
340 retNode.UID = inode.i_uid;
341 retNode.GID = inode.i_gid;
343 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
345 // Set Function Pointers
346 retNode.Read = Ext2_Read;
347 retNode.Write = Ext2_Write;
348 retNode.Close = Ext2_CloseFile;
350 switch(inode.i_mode & EXT2_S_IFMT)
354 retNode.Flags = VFS_FFLAG_SYMLINK;
359 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
363 retNode.ReadDir = Ext2_ReadDir;
364 retNode.FindDir = Ext2_FindDir;
365 retNode.MkNod = Ext2_MkNod;
366 retNode.Relink = Ext2_Relink;
367 retNode.Link = Ext2_Link;
368 retNode.Flags = VFS_FFLAG_DIRECTORY;
369 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
371 // Unknown, Write protect it to be safe
373 retNode.Flags = VFS_FFLAG_READONLY;
378 retNode.ATime = inode.i_atime * 1000;
379 retNode.MTime = inode.i_mtime * 1000;
380 retNode.CTime = inode.i_ctime * 1000;
382 // Save in node cache and return saved node
383 return Inode_CacheNode(Disk->CacheID, &retNode);