#define VERBOSE 0
#include "ext2_common.h"
+// === MACROS ===
+#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
// === PROTOTYPES ===
-char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *FileName);
- int Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
-tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name);
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
+ int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
+ int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
+// --- Helpers ---
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
// === CODE ===
/**
- \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
- \brief Reads a directory entry
-*/
+ * \brief Reads a directory entry
+ * \param Node Directory node
+ * \param Pos Position of desired element
+ */
char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
{
tExt2_Inode inode;
- char namebuf[EXT2_NAME_LEN+1];
tExt2_DirEnt dirent;
Uint64 Base; // Block's Base Address
- int block = 0, ofs = 0;
+ int block = 0;
+ Uint ofs = 0;
int entNum = 0;
tExt2_Disk *disk = Node->ImplPtr;
Uint size;
ENTER("pNode iPos", Node, Pos);
// Read directory's inode
- //Ext2_int_GetInode(Node, &inode);
Ext2_int_ReadInode(disk, Node->Inode, &inode);
size = inode.i_size;
// Get First Block
// - Do this ourselves as it is a simple operation
Base = inode.i_block[0] * disk->BlockSize;
+ // Scan directory
while(Pos -- && size > 0)
{
VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
if(ofs >= disk->BlockSize) {
block ++;
if( ofs > disk->BlockSize ) {
- Warning("[EXT2] Directory Entry %i of inode %i extends over a block boundary, ignoring",
+ Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
entNum-1, Node->Inode);
}
ofs = 0;
//LOG("dirent.inode = %i", dirent.inode);
//LOG("dirent.rec_len = %i", dirent.rec_len);
//LOG("dirent.name_len = %i", dirent.name_len);
- VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
- namebuf[ dirent.name_len ] = '\0'; // Cap off string
+ dirent.name[ dirent.name_len ] = '\0'; // Cap off string
// Ignore . and .. (these are done in the VFS)
- if( (namebuf[0] == '.' && namebuf[1] == '\0')
- || (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0')) {
+ if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
+ || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
LEAVE('p', VFS_SKIP);
return VFS_SKIP; // Skip
}
- LEAVE('s', namebuf);
+ LEAVE('s', dirent.name);
// Create new node
- return strdup(namebuf);
+ return strdup(dirent.name);
}
/**
- \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)
- \brief Gets information about a file
- \param node vfs node - Parent Node
- \param filename String - Name of file
- \return VFS Node of file
-*/
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)
+ * \brief Gets information about a file
+ * \param Node Parent Node
+ * \param Filename Name of wanted file
+ * \return VFS Node of file
+ */
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
{
tExt2_Disk *disk = Node->ImplPtr;
tExt2_Inode inode;
- char namebuf[EXT2_NAME_LEN+1];
tExt2_DirEnt dirent;
Uint64 Base; // Block's Base Address
- int block = 0, ofs = 0;
+ int block = 0;
+ Uint ofs = 0;
int entNum = 0;
Uint size;
+ int filenameLen = strlen(Filename);
// Read directory's inode
Ext2_int_ReadInode(disk, Node->Inode, &inode);
while(size > 0)
{
VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
- VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
- namebuf[ dirent.name_len ] = '\0'; // Cap off string
+ dirent.name[ dirent.name_len ] = '\0'; // Cap off string
// If it matches, create a node and return it
- if(strcmp(namebuf, Filename) == 0)
- return Ext2_int_CreateNode( disk, dirent.inode, namebuf );
+ if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
+ return Ext2_int_CreateNode( disk, dirent.inode );
// Increment pointers
ofs += dirent.rec_len;
size -= dirent.rec_len;
if(ofs >= disk->BlockSize) {
block ++;
if( ofs > disk->BlockSize ) {
- Warning("[EXT2 ] Directory Entry %i of inode %i extends over a block boundary, ignoring",
+ Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
entNum-1, Node->Inode);
}
ofs = 0;
}
/**
- * \fn int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
+ * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
* \brief Create a new node
*/
-int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
+int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
+{
+ #if 0
+ tVFS_Node *child;
+ Uint64 inodeNum;
+ tExt2_Inode inode;
+ inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
+
+ memset(&inode, 0, sizeof(tExt2_Inode));
+
+ // File type
+ inode.i_mode = 0664;
+ if( Flags & VFS_FFLAG_READONLY )
+ inode.i_mode &= ~0222;
+ if( Flags & VFS_FFLAG_SYMLINK )
+ inode.i_mode |= EXT2_S_IFLNK;
+ else if( Flags & VFS_FFLAG_DIRECTORY )
+ inode.i_mode |= EXT2_S_IFDIR | 0111;
+
+ inode.i_uid = Threads_GetUID();
+ inode.i_gid = Threads_GetGID();
+ inode.i_ctime =
+ inode.i_mtime =
+ inode.i_atime = now() / 1000;
+
+ child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
+ return Ext2_Link(Parent, child, Name);
+ #else
+ return 1;
+ #endif
+}
+
+/**
+ * \brief Rename a file
+ * \param Node This (directory) node
+ * \param OldName Old name of file
+ * \param NewName New name for file
+ * \return Boolean Failure - See ::tVFS_Node.Relink for info
+ */
+int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
{
+ return 1;
+}
+
+/**
+ * \brief Links an existing node to a new name
+ * \param Parent Parent (directory) node
+ * \param Node Node to link
+ * \param Name New name for the node
+ * \return Boolean Failure - See ::tVFS_Node.Link for info
+ */
+int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
+{
+ #if 0
+ tExt2_Disk *disk = Node->ImplPtr;
+ tExt2_Inode inode;
+ tExt2_DirEnt dirent;
+ tExt2_DirEnt newEntry;
+ Uint64 Base; // Block's Base Address
+ int block = 0, ofs = 0;
+ Uint size;
+ void *blockData;
+ int bestMatch = -1, bestSize, bestBlock, bestOfs;
+ int nEntries;
+
+ blockData = malloc(disk->BlockSize);
+
+ // Read child inode (get's the file type)
+ Ext2_int_ReadInode(disk, Child->Inode, &inode);
+
+ // Create a stub entry
+ newEntry.inode = Child->Inode;
+ newEntry.name_len = strlen(Name);
+ newEntry.rec_len = (newEntry.name_len+3+8)&~3;
+ newEntry.type = inode.i_mode >> 12;
+ memcpy(newEntry.name, Name, newEntry.name_len);
+
+ // Read directory's inode
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+ size = inode.i_size;
+
+ // Get a lock on the inode
+ Ext2_int_LockInode(disk, Node->Inode);
+
+ // Get First Block
+ // - Do this ourselves as it is a simple operation
+ base = inode.i_block[0] * disk->BlockSize;
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ block = 0;
+ // Find File
+ while(size > 0)
+ {
+ dirent = blockData + ofs;
+ // Sanity Check the entry
+ if(ofs + dirent->rec_len > disk->BlockSize) {
+ Log_Warning("EXT2",
+ "Directory entry %i of inode 0x%x extends over a block boundary",
+ nEntries, (Uint)Node->Inode);
+ }
+ else {
+
+ // Free entry
+ if(dirent->type == 0) {
+ if( dirent->rec_len >= newEntry.rec_len
+ && (bestMatch == -1 || bestSize > dirent->rec_len) )
+ {
+ bestMatch = nEntries;
+ bestSize = dirent->rec_len;
+ bestBlock = block;
+ bestOfs = ofs;
+ }
+ }
+ // Non free - check name to avoid duplicates
+ else {
+ if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
+ Ext2_int_UnlockInode(disk, Node->Inode);
+ return 1; // ERR_???
+ }
+ }
+ }
+
+ // Increment the pointer
+ nEntries ++;
+ ofs += dirent->rec_len;
+ if( ofs >= disk->BlockSize ) {
+ // Read the next block if needed
+ BLOCK_DIR_OFS(Node->Data, block) = nEntries;
+ block ++;
+ ofs = 0;
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ }
+ }
+
+ // Check if a free slot was found
+ if( bestMatch >= 0 ) {
+ // Read-Modify-Write
+ bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
+ if( block > 0 )
+ bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ dirent = blockData + bestOfs;
+ memcpy(dirent, newEntry, newEntry.rec_len);
+ VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
+ }
+ else {
+ // Allocate block, Write
+ block = Ext2_int_AllocateBlock(Disk, block);
+ Log_Warning("EXT2", "");
+ }
+
+ Ext2_int_UnlockInode(disk, Node->Inode);
return 0;
+ #else
+ return 1;
+ #endif
}
// ---- INTERNAL FUNCTIONS ----
/**
- * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
+ * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
* \brief Create a new VFS Node
*/
-tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
{
tExt2_Inode inode;
tVFS_Node retNode;
// Set file length
retNode.Size = inode.i_size;
+ retNode.Data = NULL;
// Set Access Permissions
retNode.UID = inode.i_uid;
retNode.ReadDir = Ext2_ReadDir;
retNode.FindDir = Ext2_FindDir;
retNode.MkNod = Ext2_MkNod;
- //retNode.Relink = Ext2_Relink;
+ retNode.Relink = Ext2_Relink;
+ retNode.Link = Ext2_Link;
retNode.Flags = VFS_FFLAG_DIRECTORY;
+ retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
break;
- // Unknown, Write protect and hide it to be safe
+ // Unknown, Write protect it to be safe
default:
- retNode.Flags = VFS_FFLAG_READONLY;//|VFS_FFLAG_HIDDEN;
+ retNode.Flags = VFS_FFLAG_READONLY;
break;
}
- // Check if the file should be hidden
- //if(Name[0] == '.') retNode.Flags |= VFS_FFLAG_HIDDEN;
-
// Set Timestamps
- retNode.ATime = now();
+ retNode.ATime = inode.i_atime * 1000;
retNode.MTime = inode.i_mtime * 1000;
retNode.CTime = inode.i_ctime * 1000;