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);
27 tVFS_NodeType gExt2_DirType = {
28 .TypeName = "ext2-dir",
29 .ReadDir = Ext2_ReadDir,
30 .FindDir = Ext2_FindDir,
32 .Relink = Ext2_Relink,
34 .Close = Ext2_CloseFile
36 tVFS_NodeType gExt2_FileType = {
37 .TypeName = "ext2-file",
40 .Close = Ext2_CloseFile
45 * \brief Reads a directory entry
46 * \param Node Directory node
47 * \param Pos Position of desired element
49 char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
53 Uint64 Base; // Block's Base Address
57 tExt2_Disk *disk = Node->ImplPtr;
60 ENTER("pNode iPos", Node, Pos);
62 // Read directory's inode
63 Ext2_int_ReadInode(disk, Node->Inode, &inode);
66 LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
70 // - Do this ourselves as it is a simple operation
71 Base = inode.i_block[0] * disk->BlockSize;
73 while(Pos -- && size > 0)
75 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
76 ofs += dirent.rec_len;
77 size -= dirent.rec_len;
80 if(ofs >= disk->BlockSize) {
82 if( ofs > disk->BlockSize ) {
83 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
84 entNum-1, Node->Inode);
87 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
91 // Check for the end of the list
98 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
99 //LOG("dirent.inode = %i", dirent.inode);
100 //LOG("dirent.rec_len = %i", dirent.rec_len);
101 //LOG("dirent.name_len = %i", dirent.name_len);
102 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
105 // Ignore . and .. (these are done in the VFS)
106 if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
107 || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
108 LEAVE('p', VFS_SKIP);
109 return VFS_SKIP; // Skip
112 LEAVE('s', dirent.name);
114 return strdup(dirent.name);
118 * \brief Gets information about a file
119 * \param Node Parent Node
120 * \param Filename Name of wanted file
121 * \return VFS Node of file
123 tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
125 tExt2_Disk *disk = Node->ImplPtr;
128 Uint64 Base; // Block's Base Address
133 int filenameLen = strlen(Filename);
135 // Read directory's inode
136 Ext2_int_ReadInode(disk, Node->Inode, &inode);
140 // - Do this ourselves as it is a simple operation
141 Base = inode.i_block[0] * disk->BlockSize;
145 VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
146 dirent.name[ dirent.name_len ] = '\0'; // Cap off string
147 // If it matches, create a node and return it
148 if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
149 return Ext2_int_CreateNode( disk, dirent.inode );
150 // Increment pointers
151 ofs += dirent.rec_len;
152 size -= dirent.rec_len;
155 // Check for end of block
156 if(ofs >= disk->BlockSize) {
158 if( ofs > disk->BlockSize ) {
159 Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
160 entNum-1, Node->Inode);
163 Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
171 * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
172 * \brief Create a new node
174 int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
180 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
182 memset(&inode, 0, sizeof(tExt2_Inode));
186 if( Flags & VFS_FFLAG_READONLY )
187 inode.i_mode &= ~0222;
188 if( Flags & VFS_FFLAG_SYMLINK )
189 inode.i_mode |= EXT2_S_IFLNK;
190 else if( Flags & VFS_FFLAG_DIRECTORY )
191 inode.i_mode |= EXT2_S_IFDIR | 0111;
193 inode.i_uid = Threads_GetUID();
194 inode.i_gid = Threads_GetGID();
197 inode.i_atime = now() / 1000;
199 child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
200 return Ext2_Link(Parent, child, Name);
207 * \brief Rename a file
208 * \param Node This (directory) node
209 * \param OldName Old name of file
210 * \param NewName New name for file
211 * \return Boolean Failure - See ::tVFS_Node.Relink for info
213 int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
219 * \brief Links an existing node to a new name
220 * \param Parent Parent (directory) node
221 * \param Node Node to link
222 * \param Name New name for the node
223 * \return Boolean Failure - See ::tVFS_Node.Link for info
225 int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
228 tExt2_Disk *disk = Node->ImplPtr;
231 tExt2_DirEnt newEntry;
232 Uint64 Base; // Block's Base Address
233 int block = 0, ofs = 0;
236 int bestMatch = -1, bestSize, bestBlock, bestOfs;
239 blockData = malloc(disk->BlockSize);
241 // Read child inode (get's the file type)
242 Ext2_int_ReadInode(disk, Child->Inode, &inode);
244 // Create a stub entry
245 newEntry.inode = Child->Inode;
246 newEntry.name_len = strlen(Name);
247 newEntry.rec_len = (newEntry.name_len+3+8)&~3;
248 newEntry.type = inode.i_mode >> 12;
249 memcpy(newEntry.name, Name, newEntry.name_len);
251 // Read directory's inode
252 Ext2_int_ReadInode(disk, Node->Inode, &inode);
255 // Get a lock on the inode
256 Ext2_int_LockInode(disk, Node->Inode);
259 // - Do this ourselves as it is a simple operation
260 base = inode.i_block[0] * disk->BlockSize;
261 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
266 dirent = blockData + ofs;
267 // Sanity Check the entry
268 if(ofs + dirent->rec_len > disk->BlockSize) {
270 "Directory entry %i of inode 0x%x extends over a block boundary",
271 nEntries, (Uint)Node->Inode);
276 if(dirent->type == 0) {
277 if( dirent->rec_len >= newEntry.rec_len
278 && (bestMatch == -1 || bestSize > dirent->rec_len) )
280 bestMatch = nEntries;
281 bestSize = dirent->rec_len;
286 // Non free - check name to avoid duplicates
288 if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
289 Ext2_int_UnlockInode(disk, Node->Inode);
295 // Increment the pointer
297 ofs += dirent->rec_len;
298 if( ofs >= disk->BlockSize ) {
299 // Read the next block if needed
300 BLOCK_DIR_OFS(Node->Data, block) = nEntries;
303 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
304 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
308 // Check if a free slot was found
309 if( bestMatch >= 0 ) {
311 bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
313 bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
314 VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
315 dirent = blockData + bestOfs;
316 memcpy(dirent, newEntry, newEntry.rec_len);
317 VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
320 // Allocate block, Write
321 block = Ext2_int_AllocateBlock(Disk, block);
322 Log_Warning("EXT2", "");
325 Ext2_int_UnlockInode(disk, Node->Inode);
332 // ---- INTERNAL FUNCTIONS ----
334 * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
335 * \brief Create a new VFS Node
337 tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
343 if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
346 if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
351 retNode.Inode = InodeID;
352 retNode.ImplPtr = Disk;
355 retNode.Size = inode.i_size;
358 // Set Access Permissions
359 retNode.UID = inode.i_uid;
360 retNode.GID = inode.i_gid;
362 retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
364 // Set Function Pointers
365 retNode.Type = &gExt2_FileType;
367 switch(inode.i_mode & EXT2_S_IFMT)
371 retNode.Flags = VFS_FFLAG_SYMLINK;
376 retNode.Size |= (Uint64)inode.i_dir_acl << 32;
380 retNode.Type = &gExt2_DirType;
381 retNode.Flags = VFS_FFLAG_DIRECTORY;
382 retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
384 // Unknown, Write protect it to be safe
386 retNode.Flags = VFS_FFLAG_READONLY;
391 retNode.ATime = inode.i_atime * 1000;
392 retNode.MTime = inode.i_mtime * 1000;
393 retNode.CTime = inode.i_ctime * 1000;
395 // Save in node cache and return saved node
396 return Inode_CacheNode(Disk->CacheID, &retNode);