Kernel - Added 'Flags' param to VFS Read/Write/FindDir
[tpg/acess2.git] / KernelLand / Modules / Filesystems / Ext2 / dir.c
index 5eb476f..2f2b72f 100644 (file)
@@ -1,27 +1,23 @@
 /*
- * Acess OS
- * Ext2 Driver Version 1
+ * Acess2 Ext2 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * dir.c
+ * - Directory Handling
  */
-/**
- * \file dir.c
- * \brief Second Extended Filesystem Driver
- * \todo Implement file full write support
- */
-#define DEBUG  1
+#define DEBUG  0
 #define VERBOSE        0
 #include "ext2_common.h"
 
 // === MACROS ===
-#define BLOCK_DIR_OFS(_data, _block)   ((Uint16*)(_data)[(_block)])
+#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, 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);
+ int   Ext2_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX]);
+tVFS_Node      *Ext2_FindDir(tVFS_Node *Node, const char *FileName, Uint Flags);
+tVFS_Node      *Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int   Ext2_Unlink(tVFS_Node *Node, const char *OldName);
+ int   Ext2_Link(tVFS_Node *Parent, const char *Name, tVFS_Node *Node);
 
 // === GLOBALS ===
 tVFS_NodeType  gExt2_DirType = {
@@ -29,7 +25,7 @@ tVFS_NodeType gExt2_DirType = {
        .ReadDir = Ext2_ReadDir,
        .FindDir = Ext2_FindDir,
        .MkNod = Ext2_MkNod,
-       .Relink = Ext2_Relink,
+       .Unlink = Ext2_Unlink,
        .Link = Ext2_Link,
        .Close = Ext2_CloseFile
        };
@@ -46,7 +42,7 @@ tVFS_NodeType gExt2_FileType = {
  * \param Node Directory node
  * \param Pos  Position of desired element
  */
-char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
+int Ext2_ReadDir(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
 {
        tExt2_Inode     inode;
        tExt2_DirEnt    dirent;
@@ -63,14 +59,14 @@ char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
        Ext2_int_ReadInode(disk, Node->Inode, &inode);
        size = inode.i_size;
        
-       LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
+       LOG("inode={.i_block[0]= 0x%x, .i_size=0x%x}", inode.i_block[0], inode.i_size);
        
        // Find Entry
        // 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)
+       while(Pos -- && size > 0 && size <= inode.i_size)
        {
                VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
                ofs += dirent.rec_len;
@@ -85,33 +81,41 @@ char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
                        }
                        ofs = 0;
                        Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
+                       if( Base == 0 ) {
+                               size = 0;
+                               break;
+                       }
                }
        }
        
        // Check for the end of the list
-       if(size <= 0) {
-               LEAVE('n');
-               return NULL;
+       if(size <= 0 || size > inode.i_size) {
+               LEAVE('i', -ENOENT);
+               return -ENOENT;
        }
        
        // Read Entry
        VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
-       //LOG("dirent.inode = %i", dirent.inode);
-       //LOG("dirent.rec_len = %i", dirent.rec_len);
-       //LOG("dirent.name_len = %i", dirent.name_len);
+       LOG("dirent={.rec_len=%i,.inode=0x%x,.name_len=%i}",
+               dirent.rec_len, dirent.inode, dirent.name_len);
        dirent.name[ dirent.name_len ] = '\0';  // Cap off string
        
+       if( dirent.name_len == 0 ) {
+               LEAVE('i', 1);
+               return 1;
+       }
        
        // Ignore . and .. (these are done in the VFS)
        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('i', 1);
+               return 1;       // Skip
        }
        
-       LEAVE('s', dirent.name);
-       // Create new node
-       return strdup(dirent.name);
+       LOG("Name '%s'", dirent.name);
+       strncpy(Dest, dirent.name, FILENAME_MAX);
+       LEAVE('i', 0);
+       return 0;
 }
 
 /**
@@ -120,7 +124,7 @@ char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
  * \param Filename     Name of wanted file
  * \return VFS Node of file
  */
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
 {
        tExt2_Disk      *disk = Node->ImplPtr;
        tExt2_Inode     inode;
@@ -143,9 +147,8 @@ tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
        while(size > 0)
        {
                VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
-               dirent.name[ dirent.name_len ] = '\0';  // Cap off string
                // If it matches, create a node and return it
-               if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
+               if(dirent.name_len == filenameLen && strncmp(dirent.name, Filename, filenameLen) == 0)
                        return Ext2_int_CreateNode( disk, dirent.inode );
                // Increment pointers
                ofs += dirent.rec_len;
@@ -171,36 +174,38 @@ tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
  * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
  * \brief Create a new node
  */
-int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
+tVFS_Node *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));
+       ENTER("pParent sName xFlags", Parent, Name, Flags);
        
-       // 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
+       Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
+       if( inodeNum == 0 ) {
+               LOG("Inode allocation failed");
+               LEAVE_RET('n', NULL);
+       }
+       tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
+       if( !child ) {
+               Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
+               Log_Warning("Ext2", "Ext2_MkNod - Node creation failed");
+               LEAVE_RET('n', NULL);
+       }
+
+       child->Flags = Flags & (VFS_FFLAG_DIRECTORY|VFS_FFLAG_SYMLINK|VFS_FFLAG_READONLY);
+       child->UID = Threads_GetUID();
+       child->GID = Threads_GetGID();
+       child->CTime =
+               child->MTime =
+               child->ATime =
+               now();
+       child->ImplInt = 0;     // ImplInt is the link count
+       // TODO: Set up ACLs
+
+       int rv = Ext2_Link(Parent, Name, child);
+       if( rv ) {
+               Ext2_CloseFile(child);
+               return NULL;
+       }
+       LEAVE_RET('p', child);
 }
 
 /**
@@ -208,33 +213,37 @@ int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
  * \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
+ * \return Boolean Failure - See ::tVFS_Node.Unlink for info
  */
-int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
+int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
 {
+       Log_Warning("Ext2", "TODO: Impliment Ext2_Unlink");
        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
+ * \param Node Node to link
  * \return Boolean Failure - See ::tVFS_Node.Link for info
  */
-int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
+int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
 {      
-       #if 0
        tExt2_Disk      *disk = Node->ImplPtr;
        tExt2_Inode     inode;
-       tExt2_DirEnt    dirent;
+       tExt2_DirEnt    *dirent;
        tExt2_DirEnt    newEntry;
-       Uint64  Base;   // Block's Base Address
+       Uint64  base;   // Block's Base Address
         int    block = 0, ofs = 0;
        Uint    size;
        void    *blockData;
-        int    bestMatch = -1, bestSize, bestBlock, bestOfs;
+        int    bestMatch = -1;
+        int    bestSize=0, bestBlock=0, bestOfs=0, bestNeedsSplit=0;
         int    nEntries;
+
+       ENTER("pNode sName pChild",
+               Node, Name, Child);
        
        blockData = malloc(disk->BlockSize);
        
@@ -244,7 +253,7 @@ int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
        // Create a stub entry
        newEntry.inode = Child->Inode;
        newEntry.name_len = strlen(Name);
-       newEntry.rec_len = (newEntry.name_len+3+8)&~3;
+       newEntry.rec_len = ((newEntry.name_len+3)&~3) + EXT2_DIRENT_SIZE;
        newEntry.type = inode.i_mode >> 12;
        memcpy(newEntry.name, Name, newEntry.name_len);
        
@@ -253,13 +262,18 @@ int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
        size = inode.i_size;
        
        // Get a lock on the inode
-       Ext2_int_LockInode(disk, Node->Inode);
-       
+       //Ext2_int_LockInode(disk, Node->Inode);
+       Mutex_Acquire(&Node->Lock);
+
+//     if( !Node->Data ) {
+//     }
+
        // 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;
+       nEntries = 0;
        // Find File
        while(size > 0)
        {
@@ -270,10 +284,12 @@ int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
                                "Directory entry %i of inode 0x%x extends over a block boundary",
                                nEntries, (Uint)Node->Inode);
                }
-               else {
-               
+               else
+               {
+                       LOG("Entry %i: %x %i bytes", nEntries, dirent->type, dirent->rec_len);
                        // Free entry
-                       if(dirent->type == 0) {
+                       if(dirent->type == 0)
+                       {
                                if( dirent->rec_len >= newEntry.rec_len
                                 && (bestMatch == -1 || bestSize > dirent->rec_len) )
                                {
@@ -281,23 +297,40 @@ int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
                                        bestSize = dirent->rec_len;
                                        bestBlock = block;
                                        bestOfs = ofs;
+                                       bestNeedsSplit = 0;
                                }
                        }
                        // Non free - check name to avoid duplicates
-                       else {
+                       else
+                       {
+                               LOG(" name='%.*s'", dirent->name_len, dirent->name);
                                if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
-                                       Ext2_int_UnlockInode(disk, Node->Inode);
+                                       //Ext2_int_UnlockInode(disk, Node->Inode);
+                                       Mutex_Release(&Node->Lock);
+                                       LEAVE('i', 1);
                                        return 1;       // ERR_???
                                }
+                               
+                                int    spare_space = dirent->rec_len - (dirent->name_len + EXT2_DIRENT_SIZE);
+                               if( spare_space > newEntry.rec_len
+                                && (bestMatch == -1 || bestSize > spare_space) )
+                               {
+                                       bestMatch = nEntries;
+                                       bestSize = spare_space;
+                                       bestBlock = block;
+                                       bestOfs = ofs;
+                                       bestNeedsSplit = 1;
+                               }
                        }
                }
                
                // Increment the pointer
                nEntries ++;
                ofs += dirent->rec_len;
-               if( ofs >= disk->BlockSize ) {
+               size -= dirent->rec_len;
+               if( size > 0 && ofs >= disk->BlockSize ) {
                        // Read the next block if needed
-                       BLOCK_DIR_OFS(Node->Data, block) = nEntries;
+               //      BLOCK_DIR_OFS(Node->Data, block) = nEntries;
                        block ++;
                        ofs = 0;
                        base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
@@ -305,93 +338,63 @@ int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
                }
        }
        
+       LOG("bestMatch = %i", bestMatch);
+       // If EOF was reached with no space, check if we can fit one on the end
+       if( bestMatch < 0 && ofs + newEntry.rec_len < disk->BlockSize ) {
+               Node->Size += newEntry.rec_len;
+               Node->Flags |= VFS_FFLAG_DIRTY;
+               bestBlock = block;
+               bestOfs = ofs;
+               bestSize = newEntry.rec_len;
+               bestNeedsSplit = 0;
+       }
        // Check if a free slot was found
-       if( bestMatch >= 0 ) {
+       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);
+               base = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
                VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
                dirent = blockData + bestOfs;
-               memcpy(dirent, newEntry, newEntry.rec_len);
+               // Shorten a pre-existing entry
+               if(bestNeedsSplit)
+               {
+                       dirent->rec_len = EXT2_DIRENT_SIZE + dirent->name_len;
+                       bestOfs += dirent->rec_len;
+                       //bestSize -= dirent->rec_len; // (not needed, bestSize is the spare space after)
+                       dirent = blockData + bestOfs;
+               }
+               // Insert new file entry
+               memcpy(dirent, &newEntry, newEntry.rec_len);
+               // Create a new blank entry
+               if( bestSize != newEntry.rec_len )
+               {
+                       bestOfs += newEntry.rec_len;
+                       dirent = blockData + bestOfs;
+
+                       dirent->rec_len = bestSize - newEntry.rec_len;                  
+                       dirent->type = 0;
+               }
+               // Save changes
                VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
        }
        else {
                // Allocate block, Write
-               block = Ext2_int_AllocateBlock(Disk, block);
-               Log_Warning("EXT2", "");
+               Uint32 newblock = Ext2_int_AllocateBlock(disk, base / disk->BlockSize);
+               Ext2_int_AppendBlock(disk, &inode, newblock);
+               base = newblock * disk->BlockSize;
+               Node->Size += newEntry.rec_len;
+               Node->Flags |= VFS_FFLAG_DIRTY;
+               memcpy(blockData, &newEntry, newEntry.rec_len);
+               memset(blockData + newEntry.rec_len, 0, disk->BlockSize - newEntry.rec_len);
+               VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
        }
 
-       Ext2_int_UnlockInode(disk, Node->Inode);
+       Child->ImplInt ++;
+       Child->Flags |= VFS_FFLAG_DIRTY;
+
+       //Ext2_int_UnlockInode(disk, Node->Inode);
+       Mutex_Release(&Node->Lock);
+       LEAVE('i', 0);
        return 0;
-       #else
-       return 1;
-       #endif
 }
 
-// ---- INTERNAL FUNCTIONS ----
-/**
- * \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)
-{
-       tExt2_Inode     inode;
-       tVFS_Node       retNode;
-       tVFS_Node       *tmpNode;
-       
-       if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
-               return NULL;
-       
-       if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
-               return tmpNode;
-       
-       
-       // Set identifiers
-       retNode.Inode = InodeID;
-       retNode.ImplPtr = Disk;
-       
-       // Set file length
-       retNode.Size = inode.i_size;
-       retNode.Data = NULL;
-       
-       // Set Access Permissions
-       retNode.UID = inode.i_uid;
-       retNode.GID = inode.i_gid;
-       retNode.NumACLs = 3;
-       retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
-       
-       //  Set Function Pointers
-       retNode.Type = &gExt2_FileType;
-       
-       switch(inode.i_mode & EXT2_S_IFMT)
-       {
-       // Symbolic Link
-       case EXT2_S_IFLNK:
-               retNode.Flags = VFS_FFLAG_SYMLINK;
-               break;
-       // Regular File
-       case EXT2_S_IFREG:
-               retNode.Flags = 0;
-               retNode.Size |= (Uint64)inode.i_dir_acl << 32;
-               break;
-       // Directory
-       case EXT2_S_IFDIR:
-               retNode.Type = &gExt2_DirType;
-               retNode.Flags = VFS_FFLAG_DIRECTORY;
-               retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
-               break;
-       // Unknown, Write protect it to be safe 
-       default:
-               retNode.Flags = VFS_FFLAG_READONLY;
-               break;
-       }
-       
-       // Set Timestamps
-       retNode.ATime = inode.i_atime * 1000;
-       retNode.MTime = inode.i_mtime * 1000;
-       retNode.CTime = inode.i_ctime * 1000;
-       
-       // Save in node cache and return saved node
-       return Inode_CacheNode(Disk->CacheID, &retNode);
-}

UCC git Repository :: git.ucc.asn.au