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, const char *FileName);
20 int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
21 int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
22 int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const 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
40 tExt2_Disk *disk = Node->ImplPtr;
43 ENTER("pNode iPos", Node, Pos);
45 // Read directory's inode
46 Ext2_int_ReadInode(disk, Node->Inode, &inode);
49 LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
53 // - Do this ourselves as it is a simple operation
54 Base = inode.i_block[0] * disk->BlockSize;
56 while(Pos -- && size > 0)
58 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
59 ofs += dirent.rec_len;
60 size -= dirent.rec_len;
63 if(ofs >= disk->BlockSize) {
65 if( ofs > disk->BlockSize ) {
66 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
67 entNum-1, Node->Inode);
70 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
74 // Check for the end of the list
81 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
82 //LOG("dirent.inode = %i", dirent.inode);
83 //LOG("dirent.rec_len = %i", dirent.rec_len);
84 //LOG("dirent.name_len = %i", dirent.name_len);
85 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
88 // Ignore . and .. (these are done in the VFS)
89 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
90 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
92 return VFS_SKIP; // Skip
95 LEAVE('s', dirent.name);
97 return strdup(dirent.name);
101 * \brief Gets information about a file
102 * \param Node Parent Node
103 * \param Filename Name of wanted file
104 * \return VFS Node of file
106 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
108 tExt2_Disk *disk = Node->ImplPtr;
111 Uint64 Base; // Block's Base Address
116 int filenameLen = strlen(Filename);
118 // Read directory's inode
119 Ext2_int_ReadInode(disk, Node->Inode, &inode);
123 // - Do this ourselves as it is a simple operation
124 Base = inode.i_block[0] * disk->BlockSize;
128 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
129 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
130 // If it matches, create a node and return it
131 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
132 return Ext2_int_CreateNode( disk, dirent.inode );
133 // Increment pointers
134 ofs += dirent.rec_len;
135 size -= dirent.rec_len;
138 // Check for end of block
139 if(ofs >= disk->BlockSize) {
141 if( ofs > disk->BlockSize ) {
142 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
143 entNum-1, Node->Inode);
146 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
154 * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
155 * \brief Create a new node
157 int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
163 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
165 memset(&inode, 0, sizeof(tExt2_Inode));
169 if( Flags & VFS_FFLAG_READONLY )
170 inode.i_mode &= ~0222;
171 if( Flags & VFS_FFLAG_SYMLINK )
172 inode.i_mode |= EXT2_S_IFLNK;
173 else if( Flags & VFS_FFLAG_DIRECTORY )
174 inode.i_mode |= EXT2_S_IFDIR | 0111;
176 inode.i_uid = Threads_GetUID();
177 inode.i_gid = Threads_GetGID();
180 inode.i_atime = now() / 1000;
182 child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
183 return Ext2_Link(Parent, child, Name);
190 * \brief Rename a file
191 * \param Node This (directory) node
192 * \param OldName Old name of file
193 * \param NewName New name for file
194 * \return Boolean Failure - See ::tVFS_Node.Relink for info
196 int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
202 * \brief Links an existing node to a new name
203 * \param Parent Parent (directory) node
204 * \param Node Node to link
205 * \param Name New name for the node
206 * \return Boolean Failure - See ::tVFS_Node.Link for info
208 int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
211 tExt2_Disk *disk = Node->ImplPtr;
214 tExt2_DirEnt newEntry;
215 Uint64 Base; // Block's Base Address
216 int block = 0, ofs = 0;
219 int bestMatch = -1, bestSize, bestBlock, bestOfs;
222 blockData = malloc(disk->BlockSize);
224 // Read child inode (get's the file type)
225 Ext2_int_ReadInode(disk, Child->Inode, &inode);
227 // Create a stub entry
228 newEntry.inode = Child->Inode;
229 newEntry.name_len = strlen(Name);
230 newEntry.rec_len = (newEntry.name_len+3+8)&~3;
231 newEntry.type = inode.i_mode >> 12;
232 memcpy(newEntry.name, Name, newEntry.name_len);
234 // Read directory's inode
235 Ext2_int_ReadInode(disk, Node->Inode, &inode);
238 // Get a lock on the inode
239 Ext2_int_LockInode(disk, Node->Inode);
242 // - Do this ourselves as it is a simple operation
243 base = inode.i_block[0] * disk->BlockSize;
244 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
249 dirent = blockData + ofs;
250 // Sanity Check the entry
251 if(ofs + dirent->rec_len > disk->BlockSize) {
253 "Directory entry %i of inode 0x%x extends over a block boundary",
254 nEntries, (Uint)Node->Inode);
259 if(dirent->type == 0) {
260 if( dirent->rec_len >= newEntry.rec_len
261 && (bestMatch == -1 || bestSize > dirent->rec_len) )
263 bestMatch = nEntries;
264 bestSize = dirent->rec_len;
269 // Non free - check name to avoid duplicates
271 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
272 Ext2_int_UnlockInode(disk, Node->Inode);
278 // Increment the pointer
280 ofs += dirent->rec_len;
281 if( ofs >= disk->BlockSize ) {
282 // Read the next block if needed
283 BLOCK_DIR_OFS(Node->Data, block) = nEntries;
286 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
287 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
291 // Check if a free slot was found
292 if( bestMatch >= 0 ) {
294 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
296 bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
297 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
298 dirent = blockData + bestOfs;
299 memcpy(dirent, newEntry, newEntry.rec_len);
300 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
303 // Allocate block, Write
304 block = Ext2_int_AllocateBlock(Disk, block);
305 Log_Warning("EXT2", "");
308 Ext2_int_UnlockInode(disk, Node->Inode);
315 // ---- INTERNAL FUNCTIONS ----
317 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
318 * \brief Create a new VFS Node
320 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
326 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
329 if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
334 retNode.Inode = InodeID;
335 retNode.ImplPtr = Disk;
338 retNode.Size = inode.i_size;
341 // Set Access Permissions
342 retNode.UID = inode.i_uid;
343 retNode.GID = inode.i_gid;
345 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
347 // Set Function Pointers
348 retNode.Read = Ext2_Read;
349 retNode.Write = Ext2_Write;
350 retNode.Close = Ext2_CloseFile;
352 switch(inode.i_mode & EXT2_S_IFMT)
356 retNode.Flags = VFS_FFLAG_SYMLINK;
361 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
365 retNode.ReadDir = Ext2_ReadDir;
366 retNode.FindDir = Ext2_FindDir;
367 retNode.MkNod = Ext2_MkNod;
368 retNode.Relink = Ext2_Relink;
369 retNode.Link = Ext2_Link;
370 retNode.Flags = VFS_FFLAG_DIRECTORY;
371 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
373 // Unknown, Write protect it to be safe
375 retNode.Flags = VFS_FFLAG_READONLY;
380 retNode.ATime = inode.i_atime * 1000;
381 retNode.MTime = inode.i_mtime * 1000;
382 retNode.CTime = inode.i_ctime * 1000;
384 // Save in node cache and return saved node
385 return Inode_CacheNode(Disk->CacheID, &retNode);