Modules/FAT - HUGE Rewrite to driver, with experimental write support
authorJohn Hodge <[email protected]>
Wed, 11 Jul 2012 15:38:38 +0000 (23:38 +0800)
committerJohn Hodge <[email protected]>
Wed, 11 Jul 2012 15:38:38 +0000 (23:38 +0800)
KernelLand/Modules/Filesystems/FAT/Makefile
KernelLand/Modules/Filesystems/FAT/common.h [new file with mode: 0644]
KernelLand/Modules/Filesystems/FAT/dir.c [new file with mode: 0644]
KernelLand/Modules/Filesystems/FAT/fat.c
KernelLand/Modules/Filesystems/FAT/fatio.c [new file with mode: 0644]
KernelLand/Modules/Filesystems/FAT/fs_fat.h
KernelLand/Modules/Filesystems/FAT/nodecache.c [new file with mode: 0644]

index dfa290b..cde4c72 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-OBJ = fat.o
+OBJ = fat.o dir.o fatio.o nodecache.o
 NAME = FAT
 
 -include ../Makefile.tpl
diff --git a/KernelLand/Modules/Filesystems/FAT/common.h b/KernelLand/Modules/Filesystems/FAT/common.h
new file mode 100644 (file)
index 0000000..340fff0
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Acess2 FAT Filesystem Driver
+ * - By John Hodge (thePowersGang)
+ * 
+ * common.h
+ * - FAT internal common header
+ */
+#ifndef _FS__FAT__COMMON_H_
+#define _FS__FAT__COMMON_H_
+
+#include "fs_fat.h"
+#include <vfs.h>
+
+#define CACHE_FAT      0       //!< Caches the FAT in memory
+#define USE_LFN                1       //!< Enables the use of Long File Names
+#define        SUPPORT_WRITE   1       //!< Enables write support
+
+#define FAT_FLAG_DIRTY 0x10000
+#define FAT_FLAG_DELETE        0x20000
+
+typedef struct sFAT_VolInfo tFAT_VolInfo;
+#if USE_LFN
+typedef struct sFAT_LFNCacheEnt        tFAT_LFNCacheEnt;
+typedef struct sFAT_LFNCache   tFAT_LFNCache;
+#endif
+typedef struct sFAT_CachedNode tFAT_CachedNode;
+
+/**
+ * \brief Internal IDs for FAT types
+ */
+enum eFatType
+{
+       FAT12,  //!< FAT12 Volume
+       FAT16,  //!< FAT16 Volume
+       FAT32,  //!< FAT32 Volume
+};
+
+// === TYPES ===
+struct sFAT_VolInfo
+{
+        int    fileHandle;     //!< File Handle
+       enum eFatType   type;   //!< FAT Variant
+       char    name[12];       //!< Volume Name (With NULL Terminator)
+       Uint32  firstDataSect;  //!< First data sector
+       Uint32  rootOffset;     //!< Root Offset (clusters)
+       Uint32  ClusterCount;   //!< Total Cluster Count
+       fat_bootsect    bootsect;       //!< Boot Sector
+       tVFS_Node       rootNode;       //!< Root Node
+        int    BytesPerCluster;
+       
+       tMutex  lNodeCache;
+       tFAT_CachedNode *NodeCache;
+       
+       tMutex  lFAT;           //!< Lock to prevent double-writing to the FAT
+       #if CACHE_FAT
+       Uint32  *FATCache;      //!< FAT Cache
+       #endif
+};
+
+#if USE_LFN
+/**
+ * \brief Long-Filename cache entry
+ */
+struct sFAT_LFNCacheEnt
+{
+        int    ID;
+       Uint16  Data[256];
+};
+/**
+ * \brief Long-Filename cache
+ */
+struct sFAT_LFNCache
+{
+        int    NumEntries;
+       tFAT_LFNCacheEnt        Entries[];
+};
+#endif
+
+struct sFAT_CachedNode
+{
+       struct sFAT_CachedNode  *Next;
+       tVFS_Node       Node;
+};
+
+// --- General Helpers ---
+extern int     FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);
+
+// --- Node Caching ---
+// NOTE: FAT uses its own node cache that references by cluster (not the inode value that the Inode_* cache uses)
+//       because tVFS_Node.Inode contains the parent directory inode
+extern tVFS_Node       *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry);
+extern tVFS_Node       *FAT_int_CreateIncompleteDirNode(tFAT_VolInfo *Disk, Uint32 Cluster);
+extern tVFS_Node       *FAT_int_GetNode(tFAT_VolInfo *Disk, Uint32 Cluster);
+extern void    FAT_int_DerefNode(tVFS_Node *Node);
+extern void    FAT_int_ClearNodeCache(tFAT_VolInfo *Disk);
+
+// --- FAT Access ---
+extern Uint32  FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);
+#if SUPPORT_WRITE
+extern Uint32  FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);
+extern Uint32  FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);
+#endif
+extern void    FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);
+extern void    FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, const void *Buffer);
+
+// --- Directory Access ---
+extern char    *FAT_ReadDir(tVFS_Node *Node, int ID);
+extern tVFS_Node       *FAT_FindDir(tVFS_Node *Node, const char *Name);
+extern tVFS_Node       *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
+extern int     FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
+#if SUPPORT_WRITE
+extern int     FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
+extern int     FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
+extern int     FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
+extern int     FAT_Unlink(tVFS_Node *DirNode, const char *OldName);
+#endif
+extern void    FAT_CloseFile(tVFS_Node *node);
+
+// === GLOBALS ===
+extern tVFS_NodeType   gFAT_DirType;
+extern tVFS_NodeType   gFAT_FileType;
+
+#endif
+
diff --git a/KernelLand/Modules/Filesystems/FAT/dir.c b/KernelLand/Modules/Filesystems/FAT/dir.c
new file mode 100644 (file)
index 0000000..f639a0b
--- /dev/null
@@ -0,0 +1,831 @@
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * dir.c
+ * - Directory access/manipulation code
+ */
+#define DEBUG  1
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+void   FAT_int_ProperFilename(char *dest, const char *src);
+char   *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName);
+ int   FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source);
+ int   FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source);
+
+ int   FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry);
+ int   FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
+ int   FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer);
+#if SUPPORT_WRITE
+ int   FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
+#endif
+#if USE_LFN
+Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID);
+void   FAT_int_DelLFN(tVFS_Node *Node, int ID);
+#endif
+char   *FAT_ReadDir(tVFS_Node *Node, int ID);
+tVFS_Node      *FAT_FindDir(tVFS_Node *Node, const char *Name);
+tVFS_Node      *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
+#if SUPPORT_WRITE
+ int   FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int   FAT_int_IsValid83Filename(const char *Name);
+ int   FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
+ int   FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);
+#endif
+
+// === CODE ===
+
+/**
+ * \brief Converts a FAT directory entry name into a proper filename
+ * \param dest Destination array (must be at least 13 bytes in size)
+ * \param src  8.3 filename (concatenated, e.g 'FILE1   TXT')
+ */
+void FAT_int_ProperFilename(char *dest, const char *src)
+{
+        int    inpos, outpos;
+       
+       // Name
+       outpos = 0;
+       for( inpos = 0; inpos < 8; inpos++ ) {
+               if(src[inpos] == ' ')   break;
+               dest[outpos++] = src[inpos];
+       }
+       inpos = 8;
+       // Check for empty extensions
+       if(src[8] != ' ')
+       {
+               dest[outpos++] = '.';
+               for( ; inpos < 11; inpos++)     {
+                       if(src[inpos] == ' ')   break;
+                       dest[outpos++] = src[inpos];
+               }
+       }
+       dest[outpos++] = '\0';
+       
+       //LOG("dest='%s'", dest);
+}
+
+/**
+ * \fn char *FAT_int_CreateName(fat_filetable *ft, Uint8 *LongFileName)
+ * \brief Converts either a LFN or a 8.3 Name into a proper name
+ * \param ft   Pointer to the file's entry in the parent directory
+ * \param LongFileName Long file name pointer
+ * \return Filename as a heap string
+ */
+char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName)
+{
+       char    *ret;
+       ENTER("pft sLongFileName", ft, LongFileName);
+       //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);
+       #if USE_LFN
+       if(LongFileName && LongFileName[0] != 0)
+       {
+                int    len = FAT_int_ConvertUTF16_to_UTF8(NULL, LongFileName);
+               ret = malloc( len + 1 );
+               FAT_int_ConvertUTF16_to_UTF8((Uint8*)ret, LongFileName);
+       }
+       else
+       {
+       #endif
+               ret = (char*) malloc(13);
+               if( !ret ) {
+                       Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");
+                       return NULL;
+               }
+               FAT_int_ProperFilename(ret, ft->name);
+       #if USE_LFN
+       }
+       #endif
+       LEAVE('s', ret);
+       return ret;
+}
+
+#if USE_LFN
+int FAT_int_CompareUTF16_UTF8(const Uint16 *Str16, const char *Str8)
+{
+        int    pos16 = 0, pos8 = 0;
+       const Uint8     *str8 = (const Uint8 *)Str8;
+       
+       while( Str16[pos16] && str8[pos8] )
+       {
+               Uint32  cp8, cp16;
+               if( Str16[pos16] & 0x8000 ) {
+                       // Do something!
+               }
+               else {
+                       cp16 = Str16[pos16];
+                       pos16 ++;
+               }
+               pos8 += ReadUTF8(str8 + pos8, &cp8);
+       
+               if(cp16 == cp8) continue ;
+               
+               if(cp16 < cp8)
+                       return -1;
+               else
+                       return 1;
+       }
+       if(Str16[pos16] == str8[pos8])
+               return 0;
+       if(Str16[pos16] < str8[pos8])
+               return -1;
+       else
+               return 1;
+}
+
+int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source)
+{
+        int    len = 0;
+       for( ; *Source; Source ++ )
+       {
+               // TODO: Decode/Reencode
+               if( Dest )
+                       Dest[len] = *Source;
+               len += 1;
+       }
+       if( Dest )
+               Dest[len] = 0;
+       return len;
+}
+
+int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source)
+{
+        int    len = 0;
+       for( ; *Source; Source ++ )
+       {
+               Uint32  cp;
+               int cpl;
+               
+               cpl = ReadUTF8(Source, &cp);
+               if(cp < 0x8000) {
+                       if( Dest )
+                               Dest[len] = cp;
+                       len ++;
+               }
+               else {
+                       // TODO!
+               }
+               Source += cpl;
+       }
+       Dest[len] = 0;
+       return len;
+}
+
+int FAT_int_ParseLFN(const fat_filetable *Entry, Uint16 *Buffer)
+{
+       const fat_longfilename  *lfnInfo;
+        int    ofs;
+       
+       lfnInfo = (const void*)Entry;
+       
+       if(lfnInfo->id & 0x40) {
+               memset(Buffer, 0, 256*2);
+       }
+       ofs = (lfnInfo->id & 0x3F) * 13 - 1;
+       if( ofs >= 255 )
+               return -1;
+       
+       Buffer[ofs--] = lfnInfo->name3[1];      Buffer[ofs--] = lfnInfo->name3[0];
+       Buffer[ofs--] = lfnInfo->name2[5];      Buffer[ofs--] = lfnInfo->name2[4];
+       Buffer[ofs--] = lfnInfo->name2[3];      Buffer[ofs--] = lfnInfo->name2[2];
+       Buffer[ofs--] = lfnInfo->name2[1];      Buffer[ofs--] = lfnInfo->name2[0];
+       Buffer[ofs--] = lfnInfo->name1[4];      Buffer[ofs--] = lfnInfo->name1[3];
+       Buffer[ofs--] = lfnInfo->name1[2];      Buffer[ofs--] = lfnInfo->name1[1];
+       Buffer[ofs--] = lfnInfo->name1[0];
+       
+       if((lfnInfo->id&0x3F) == 1)
+               return 1;
+       return 0;
+}
+#endif
+
+int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry)
+{
+       fat_filetable   fileinfo[16];
+       char    tmpName[13];
+       #if USE_LFN
+       Uint16  lfn[256];
+        int    lfnId = -1;
+       #endif
+
+       for( int i = 0; ; i++ )
+       {
+               if((i & 0xF) == 0) {
+                       if(FAT_int_ReadDirSector(DirNode, i/16, fileinfo))
+                       {
+                               LEAVE('i', -1);
+                               return -1;
+                       }
+               }
+               
+               //Check if the files are free
+               if(fileinfo[i&0xF].name[0] == '\0')     break;  // End of List marker
+               if(fileinfo[i&0xF].name[0] == '\xE5')   continue;       // Free entry
+               
+               
+               #if USE_LFN
+               // Long File Name Entry
+               if(fileinfo[i & 0xF].attrib == ATTR_LFN)
+               {
+                       if( FAT_int_ParseLFN(&fileinfo[i&0xF], lfn) )
+                               lfnId = i+1;
+                       continue ;
+               }
+               // Remove LFN if it does not apply
+               if(lfnId != i)  lfn[0] = 0;
+               #else
+               if(fileinfo[i&0xF].attrib == ATTR_LFN)  continue;
+               #endif
+
+               // Get Real Filename
+               FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
+//             LOG("tmpName = '%s'", tmpName);
+//             #if DEBUG
+//             Debug_HexDump("FAT tmpName", tmpName, strlen(tmpName));
+//             #endif
+/*
+               #if DEBUG && USE_LFN
+               if(lfnId == i)
+               {
+                       Uint8 lfntmp[256*3+1];
+                       FAT_int_ConvertUTF16_to_UTF8(lfntmp, lfn);
+                       LOG("lfntmp = '%s'", lfntmp);
+               }
+               #endif
+*/
+       
+               // Only the long name is case sensitive, 8.3 is not
+               #if USE_LFN
+               if(strucmp(tmpName, Name) == 0 || FAT_int_CompareUTF16_UTF8(lfn, Name) == 0)
+               #else
+               if(strucmp(tmpName, Name) == 0)
+               #endif
+               {
+                       memcpy(Entry, fileinfo + (i&0xF), sizeof(*Entry));
+                       LOG("Found %s at %i", Name, i);
+                       LEAVE('i', i);
+                       return i;
+               }
+       }
+       
+       LEAVE('i', -1);
+       return -1;
+}
+
+int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry)
+{
+        int    ents_per_sector = 512 / sizeof(fat_filetable); 
+       fat_filetable   fileinfo[ents_per_sector];
+        int    i, sector;
+
+       Mutex_Acquire(&DirNode->Lock);
+       sector = 0;
+       for( i = 0; ; i ++ )
+       {
+               if( i == 0 || i == ents_per_sector )
+               {
+                       if(FAT_int_ReadDirSector(DirNode, sector, fileinfo))
+                       {
+                               LOG("ReadDirSector failed");
+                               break ;
+                       }
+                       i = 0;
+                       sector ++;
+               }
+       
+               // Check for free/end of list
+               if(fileinfo[i].name[0] == '\0') break;  // End of List marker
+               if(fileinfo[i].name[0] == '\xE5')       continue;       // Free entry
+               
+               if(fileinfo[i].attrib == ATTR_LFN)      continue;
+
+               LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);
+               #if DEBUG
+               {
+                       char    tmpName[13];
+                       FAT_int_ProperFilename(tmpName, fileinfo[i].name);
+                       LOG("tmpName = '%s'", tmpName);
+               }
+               #endif
+               
+       
+               if(fileinfo[i].cluster != (Cluster & 0xFFFF))   continue;
+               if(fileinfo[i].clusterHi != ((Cluster >> 16) & 0xFFFF)) continue;
+       
+               memcpy(Entry, &fileinfo[i], sizeof(*Entry));
+               Mutex_Release(&DirNode->Lock);
+               return i;
+       }
+       
+       Mutex_Release(&DirNode->Lock);
+       return -1;
+}
+
+/* 
+ * ====================
+ *     Directory IO
+ * ====================
+ */
+
+/**
+ * \brief Reads a sector from the disk
+ * \param Node Directory node to read
+ * \param Sector       Sector number in the directory to read
+ * \param Buffer       Destination buffer for the read data
+ */
+int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
+{
+       Uint64  addr;
+       tFAT_VolInfo    *disk = Node->ImplPtr;
+       
+       ENTER("pNode iSector pEntry", Node, Sector, Buffer);
+       
+       // Parse address
+       if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
+       {
+               LEAVE('i', 1);
+               return 1;
+       }
+       
+       LOG("addr = 0x%llx", addr);
+       // Read Sector
+       if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)
+       {
+               LEAVE('i', 1);
+               return 1;
+       }
+       
+       LEAVE('i', 0);
+       return 0;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Writes an entry to the disk
+ * \todo Support expanding a directory
+ * \param Node Directory node
+ * \param ID   ID of entry to update
+ * \param Entry        Entry data
+ * \return Zero on success, non-zero on error
+ */
+int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
+{
+       Uint64  addr = 0;
+        int    tmp;
+       Uint32  cluster = 0;
+       tFAT_VolInfo    *disk = Node->ImplPtr;
+       
+       ENTER("pNode iID pEntry", Node, ID, Entry);
+       
+       tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
+       if( tmp )
+       {
+               //TODO: Allocate a cluster
+               cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
+               if(cluster == -1) {
+                       Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
+                       LEAVE('i', 1);
+                       return 1;
+               }
+               FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
+       }
+       
+
+       LOG("addr = 0x%llx", addr);
+       
+       // Read Sector
+       VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);      // Read Dir Data
+       
+       LEAVE('i', 0);
+       return 0;
+}
+#endif
+
+#if USE_LFN    
+/**
+ * \fn Uint16 *FAT_int_GetLFN(tVFS_Node *node)
+ * \brief Return pointer to LFN cache entry
+ * \param Node Directory node
+ * \param ID   ID of the short name
+ * \return Pointer to the LFN cache entry
+ */
+Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID)
+{
+       tFAT_LFNCache   *cache;
+        int    i, firstFree;
+       
+       Mutex_Acquire( &Node->Lock );
+       
+       // TODO: Thread Safety (Lock things)
+       cache = Node->Data;
+       
+       // Create a cache if it isn't there
+       if(!cache) {
+               cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
+               cache->NumEntries = 1;
+               cache->Entries[0].ID = ID;
+               cache->Entries[0].Data[0] = 0;
+               Mutex_Release( &Node->Lock );
+               //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
+               return cache->Entries[0].Data;
+       }
+       
+       // Scan for this entry
+       firstFree = -1;
+       for( i = 0; i < cache->NumEntries; i++ )
+       {
+               if( cache->Entries[i].ID == ID ) {
+                       Mutex_Release( &Node->Lock );
+                       //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
+                       return cache->Entries[i].Data;
+               }
+               if( cache->Entries[i].ID == -1 && firstFree == -1 )
+                       firstFree = i;
+       }
+       
+       if(firstFree == -1) {
+               // Use `i` for temp length
+               i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
+               Node->Data = realloc( Node->Data, i );
+               if( !Node->Data ) {
+                       Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
+                       Mutex_Release( &Node->Lock );
+                       return NULL;
+               }
+               //Log_Debug("FAT", "Realloc (%i)\n", i);
+               cache = Node->Data;
+               i = cache->NumEntries;
+               cache->NumEntries ++;
+       }
+       else {
+               i = firstFree;
+       }
+       
+       // Create new entry
+       cache->Entries[ i ].ID = ID;
+       cache->Entries[ i ].Data[0] = '\0';
+       
+       Mutex_Release( &Node->Lock );
+       //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
+       return cache->Entries[ i ].Data;
+}
+
+/**
+ * \fn void FAT_int_DelLFN(tVFS_Node *node)
+ * \brief Delete a LFN cache entry
+ * \param Node Directory node
+ * \param ID   File Entry ID
+ */
+void FAT_int_DelLFN(tVFS_Node *Node, int ID)
+{
+       tFAT_LFNCache   *cache = Node->Data;
+        int    i;
+       
+       // Fast return
+       if(!cache)      return;
+       
+       // Scan for a current entry
+       for( i = 0; i < cache->NumEntries; i++ )
+       {
+               if( cache->Entries[i].ID == ID )
+                       cache->Entries[i].ID = -1;
+       }
+       return ;
+}
+#endif
+
+/**
+ * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
+ * \param Node Node structure of directory
+ * \param ID   Directory position
+ * \return Filename as a heap string, NULL or VFS_SKIP
+ */
+char *FAT_ReadDir(tVFS_Node *Node, int ID)
+{
+       fat_filetable   fileinfo[16];   // sizeof(fat_filetable)=32, so 16 per sector
+        int    a;
+       char    *ret;
+       #if USE_LFN
+       Uint16  *lfn = NULL;
+       #endif
+       
+       ENTER("pNode iID", Node, ID);
+       
+       if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
+       {
+               LOG("End of chain, end of dir");
+               LEAVE('n');
+               return NULL;
+       }
+       
+       // Offset in sector
+       a = ID % 16;
+
+       LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
+       
+       // Check if this is the last entry
+       if( fileinfo[a].name[0] == '\0' ) {
+               Node->Size = ID;
+               LOG("End of list");
+               LEAVE('n');
+               return NULL;    // break
+       }
+       
+       // Check for empty entry
+       if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
+               LOG("Empty Entry");
+               #if 0   // Stop on empty entry?
+               LEAVE('n');
+               return NULL;    // Stop
+               #else
+               LEAVE('p', VFS_SKIP);
+               return VFS_SKIP;        // Skip
+               #endif
+       }
+       
+       #if USE_LFN
+       // Get Long File Name Cache
+       if(fileinfo[a].attrib == ATTR_LFN)
+       {
+               fat_longfilename        *lfnInfo;
+               
+               lfnInfo = (fat_longfilename *) &fileinfo[a];
+               
+               // Get cache for corresponding file
+               // > ID + Index gets the corresponding short node
+               lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
+
+               a = FAT_int_ParseLFN(&fileinfo[a], lfn);
+               if( a < 0 ) {
+                       LOG("Invalid LFN, error");
+                       LEAVE('n');
+                       return NULL;
+               }
+
+//             LOG("lfn = '%s'", lfn);
+               //Log_Debug("FAT", "lfn = '%s'", lfn);
+               LEAVE('p', VFS_SKIP);
+               return VFS_SKIP;
+       }
+       #endif
+       
+       // Check if it is a volume entry
+       if(fileinfo[a].attrib & 0x08) {
+               LEAVE('p', VFS_SKIP);
+               return VFS_SKIP;
+       }
+       // Ignore .
+       if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
+               LEAVE('p', VFS_SKIP);
+               return VFS_SKIP;
+       }
+       // and ..
+       if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
+               LEAVE('p', VFS_SKIP);
+               return VFS_SKIP;
+       }
+       
+       LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
+               fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
+               fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
+               fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
+       
+       #if USE_LFN
+       lfn = FAT_int_GetLFN(Node, ID);
+       //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
+       ret = FAT_int_CreateName(&fileinfo[a], lfn);
+       #else
+       ret = FAT_int_CreateName(&fileinfo[a], NULL);
+       #endif
+       
+       LEAVE('s', ret);
+       return ret;
+}
+
+/**
+ * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
+ * \brief Finds an entry in the current directory
+ */
+tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
+{
+       fat_filetable   fileent;
+       
+       ENTER("pNode sname", Node, Name);       
+
+       // Fast Returns
+       if(!Name || Name[0] == '\0') {
+               LEAVE('n');
+               return NULL;
+       }
+
+       if( FAT_int_GetEntryByName(Node, Name, &fileent) == -1 ) {
+               LEAVE('n');
+               return NULL;
+       }
+       
+
+       tVFS_Node *ret = FAT_int_CreateNode(Node, &fileent);
+       LOG("Found %s as %p", Name, ret);
+       LEAVE('p', ret);
+       return ret;
+}
+
+tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
+{
+       tFAT_VolInfo    *disk = Root->ImplPtr;
+       tVFS_Node       *dirnode, *ret;
+       fat_filetable   ft;
+
+       ENTER("pRoot XInode", Root, Inode);
+
+       ret = FAT_int_GetNode(disk, Inode & 0xFFFFFFFF);
+       if( ret ) {
+               if( (ret->Inode >> 32) != 0 ) {
+                       LOG("Node in cache, quick return");
+                       return ret;
+               }
+               else {
+                       LOG("Node cached, but incomplete");
+                       // Fall on through
+               }
+               ret = NULL;
+       }
+       
+       dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
+
+       int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
+       if( id != -1 ) {
+               ret = FAT_int_CreateNode(dirnode, &ft);
+       }
+
+       dirnode->Type->Close(dirnode);
+
+       LEAVE('p', ret);
+       return ret;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Create a new node
+ */
+int FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
+{
+       tFAT_VolInfo    *disk = DirNode->ImplPtr;
+        int    rv;
+       fat_filetable   ft;
+       memset(&ft, 0, sizeof(ft));
+       
+       // Allocate a cluster
+       Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
+       LOG("Cluster 0x%07x allocated", cluster);
+       
+       // Create a temporary file table entry for an empty node
+       ft.cluster = cluster & 0xFFFF;
+       ft.clusterHi = cluster >> 16;
+       ft.size = 0;
+       if( Flags & VFS_FFLAG_DIRECTORY )
+               ft.attrib = ATTR_DIRECTORY;
+       else
+               ft.attrib = 0;
+       
+       tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
+       if( !newnode ) {
+               return -1;
+       }
+       LOG("newnode = %p", newnode);
+
+       // Call link
+       if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
+               newnode->Flags |= FAT_FLAG_DELETE;
+       }
+       LOG("rv = %i", rv);
+       FAT_CloseFile(newnode);
+       return rv;
+}
+
+/**
+ * \brief Internal - Checks if a character is valid in an 8.3 filename
+ */
+static inline int is_valid_83_char(char ch)
+{
+       if( '0' <= ch && ch <= '9' )
+               return 1;
+       if( 'A' <= ch && ch <= 'Z' )
+               return 1;
+       return 0;
+}
+
+/**
+ * \brief Internal - Determines if a filename is a valid 8.3 filename
+ */
+int FAT_int_IsValid83Filename(const char *Name)
+{
+        int    i, j;
+       // Check filename portion
+       for( i = 0; Name[i] && i < 8; i ++ )
+       {
+               if( Name[i] == '.' )
+                       break;
+               if( !is_valid_83_char(Name[i]) )
+                       return 0;
+       }
+       // If the next char is not \0 or '.', it's not valid
+       if( Name[i] && Name[i++] != '.' )
+               return 0;
+       
+       // Check the extension portion
+       for( j = 0; Name[i+j] && j < 3; j ++ )
+       {
+               if( !is_valid_83_char(Name[i+j]) )
+                       return 0;
+       }
+       
+       // After the extension must be the end
+       if( !Name[i+j] )
+               return 0;
+       
+       return 1;
+}
+
+/**
+ * \brief Create a new name for a file
+ * \note Since FAT doesn't support reference counting, this will cause double-references if
+ *       a file is hardlinked and not unlinked
+ */
+int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
+{
+       Uint16  lfn[256];
+       fat_filetable   ft;
+        int    nLFNEnt = 0;
+       
+       // -- Create filetable entry --
+        int    bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
+       if( bNeedsLFN )
+       {
+               int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
+               nLFNEnt = DivUp(lfnlen, 13);
+       
+               // Create mangled filetable entry
+               // - Requires checking for duplicates
+               Log_Warning("FAT", "FAT_Link - LFN Mangling unimplimented");
+       }
+       else
+       {
+               // Create pure filetable entry
+               Log_Warning("FAT", "FAT_Link - Filename translation unimplimented");
+       }
+       
+       ft.size = NewNode->Size;
+
+       // -- Add entry to the directory --
+       Mutex_Acquire( &DirNode->Lock );
+
+       // Locate a range of nLFNEnt + 1 free entries
+       // - If there are none, defragment the directory?
+       // - Else, expand the directory
+       // - and if that fails, return an error
+       Log_Warning("FAT", "FAT_Link - Free entry scanning unimplimented");
+
+       Mutex_Release( &DirNode->Lock );
+       return ENOTIMPL;
+}
+
+/**
+ * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
+ * \brief Rename / Delete a file
+ */
+int FAT_Unlink(tVFS_Node *Node, const char *OldName)
+{
+       tVFS_Node       *child;
+       fat_filetable   ft;
+       
+       Mutex_Acquire(&Node->Lock);
+
+       int id = FAT_int_GetEntryByName(Node, OldName, &ft);
+       if(id == -1) {
+               Mutex_Release(&Node->Lock);
+               return ENOTFOUND;
+       }
+
+       child = FAT_int_CreateNode(Node->ImplPtr, &ft);
+       if( !child ) {
+               Mutex_Release(&Node->Lock);
+               return EINVAL;
+       }
+       child->ImplInt |= FAT_FLAG_DELETE;      // Mark for deletion on close
+
+       // TODO: If it has a LFN, remove that too
+
+       // Delete from the directory
+       ft.name[0] = '\xE9';
+       FAT_int_WriteDirEntry(Node, id, &ft);
+
+       // Close child
+       child->Type->Close( child );
+       Mutex_Release( &Node->Lock );
+       return EOK;
+}
+#endif
index 3708e44..0706697 100644 (file)
  * \todo Implement changing of the parent directory when a file is written to\r
  * \todo Implement file creation / deletion\r
  */\r
-#define DEBUG  0\r
+#define DEBUG  1\r
 #define VERBOSE        1\r
 \r
-#define CACHE_FAT      0       //!< Caches the FAT in memory\r
-#define USE_LFN                1       //!< Enables the use of Long File Names\r
-#define        SUPPORT_WRITE   0       //!< Enables write support\r
-\r
 #include <acess.h>\r
 #include <modules.h>\r
-#include <vfs.h>\r
-#include "fs_fat.h"\r
-\r
-#define FAT_FLAG_DIRTY 0x10000\r
-#define FAT_FLAG_DELETE        0x20000\r
-\r
-// === TYPES ===\r
-#if USE_LFN\r
-/**\r
- * \brief Long-Filename cache entry\r
- */\r
-typedef struct sFAT_LFNCacheEnt\r
-{\r
-        int    ID;\r
-       // TODO: Handle UTF16 names correctly\r
-       char    Data[256];\r
-}      tFAT_LFNCacheEnt;\r
-/**\r
- * \brief Long-Filename cache\r
- */\r
-typedef struct sFAT_LFNCache\r
-{\r
-        int    NumEntries;\r
-       tFAT_LFNCacheEnt        Entries[];\r
-}      tFAT_LFNCache;\r
-#endif\r
+#include "common.h"\r
 \r
 // === PROTOTYPES ===\r
 // --- Driver Core\r
@@ -60,33 +31,19 @@ tVFS_Node   *FAT_InitDevice(const char *device, const char **options);
 void   FAT_Unmount(tVFS_Node *Node);\r
 // --- Helpers\r
  int   FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#if SUPPORT_WRITE\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#endif\r
-void   FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);\r
 // --- File IO\r
 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);\r
 #if SUPPORT_WRITE\r
-void   FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);\r
-size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);\r
-#endif\r
-// --- Directory IO\r
-char   *FAT_ReadDir(tVFS_Node *Node, int ID);\r
-tVFS_Node      *FAT_FindDir(tVFS_Node *Node, const char *Name);\r
-tVFS_Node      *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);\r
-#if SUPPORT_WRITE\r
- int   FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);\r
- int   FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);\r
+size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer);\r
 #endif\r
 void   FAT_CloseFile(tVFS_Node *node);\r
 \r
+\r
 // === Options ===\r
  int   giFAT_MaxCachedClusters = 1024*512/4;\r
 \r
 // === SEMI-GLOBALS ===\r
-MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);\r
+MODULE_DEFINE(0, VER2(1,1) /*v1.01*/, VFAT, FAT_Install, NULL, NULL);\r
 tFAT_VolInfo   gFAT_Disks[8];\r
  int   giFAT_PartCount = 0;\r
 tVFS_Driver    gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};\r
@@ -96,7 +53,8 @@ tVFS_NodeType gFAT_DirType = {
        .FindDir = FAT_FindDir,\r
        #if SUPPORT_WRITE\r
        .MkNod = FAT_Mknod,\r
-       .Relink = FAT_Relink,\r
+       .Link = FAT_Link,\r
+       .Unlink = FAT_Unlink,\r
        #endif\r
        .Close = FAT_CloseFile\r
        };\r
@@ -130,6 +88,8 @@ tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
        Uint32  FATSz, RootDirSectors, TotSec;\r
        tVFS_Node       *node = NULL;\r
        tFAT_VolInfo    *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
+\r
+       memset(diskInfo, 0, sizeof(*diskInfo));\r
        \r
        // Temporary Pointer\r
        bs = &diskInfo->bootsect;\r
@@ -164,9 +124,9 @@ tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
        \r
        diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
        \r
-       if(diskInfo->ClusterCount < 4085)\r
+       if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)\r
                diskInfo->type = FAT12;\r
-       else if(diskInfo->ClusterCount < 65525)\r
+       else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)\r
                diskInfo->type = FAT16;\r
        else\r
                diskInfo->type = FAT32;\r
@@ -272,10 +232,6 @@ tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
        \r
        diskInfo->BytesPerCluster = bs->spc * bs->bps;\r
        \r
-       // Initalise inode cache for filesystem\r
-       diskInfo->inodeHandle = Inode_GetHandle();\r
-       LOG("Inode Cache handle is %i", diskInfo->inodeHandle);\r
-       \r
        // == VFS Interface\r
        node = &diskInfo->rootNode;\r
        //node->Size = bs->files_in_root;\r
@@ -313,7 +269,7 @@ void FAT_Unmount(tVFS_Node *Node)
        // Close Disk Handle\r
        VFS_Close( disk->fileHandle );\r
        // Clear Node Cache\r
-       Inode_ClearCache(disk->inodeHandle);\r
+       FAT_int_ClearNodeCache(disk);\r
        // Mark as unused\r
        disk->fileHandle = -2;\r
        return;\r
@@ -331,15 +287,15 @@ void FAT_Unmount(tVFS_Node *Node)
  */\r
 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)\r
 {\r
-       Uint32  cluster;\r
+       Uint32  cluster, base_cluster;\r
        Uint64  addr;\r
         int    skip;\r
        tFAT_VolInfo    *disk = Node->ImplPtr;\r
        \r
        ENTER("pNode XOffset", Node, Offset);\r
        \r
-       cluster = Node->Inode & 0xFFFFFFF;      // Cluster ID\r
-       LOG("cluster = 0x%07x", cluster);\r
+       cluster = base_cluster = Node->Inode & 0xFFFFFFF;       // Cluster ID\r
+       LOG("base cluster = 0x%07x", cluster);\r
        \r
        // Do Cluster Skip\r
        // - Pre FAT32 had a reserved area for the root.\r
@@ -358,11 +314,13 @@ int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Clu
                if(Cluster)     *Cluster = cluster;\r
        }\r
        else {\r
+               // TODO: Bounds checking on root\r
+               LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);\r
                // Increment by clusters in offset\r
                cluster += Offset / disk->BytesPerCluster;\r
        }\r
        \r
-       LOG("cluster = %08x", cluster);\r
+       LOG("cluster = 0x%07x", cluster);\r
        \r
        // Bounds Checking (Used to spot corruption)\r
        if(cluster > disk->ClusterCount + 2)\r
@@ -375,7 +333,7 @@ int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Clu
        \r
        // Compute Offsets\r
        // - Pre FAT32 cluster base (in sectors)\r
-       if( cluster == disk->rootOffset && disk->type != FAT32 ) {\r
+       if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {\r
                addr = disk->bootsect.resvSectCount * disk->bootsect.bps;\r
                addr += cluster * disk->BytesPerCluster;\r
        }\r
@@ -392,225 +350,6 @@ int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Clu
        return 0;\r
 }\r
 \r
-/*\r
- * ====================\r
- *   FAT Manipulation\r
- * ====================\r
- */\r
-/**\r
- * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
- * \brief Fetches a value from the FAT\r
- */\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
-{\r
-       Uint32  val = 0;\r
-       Uint32  ofs;\r
-       ENTER("pDisk xCluster", Disk, cluster);\r
-       Mutex_Acquire( &Disk->lFAT );\r
-       #if CACHE_FAT\r
-       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
-       {\r
-               val = Disk->FATCache[cluster];\r
-               if(Disk->type == FAT12 && val == EOC_FAT12)     val = -1;\r
-               if(Disk->type == FAT16 && val == EOC_FAT16)     val = -1;\r
-               if(Disk->type == FAT32 && val == EOC_FAT32)     val = -1;\r
-       }\r
-       else\r
-       {\r
-       #endif\r
-               ofs = Disk->bootsect.resvSectCount*512;\r
-               if(Disk->type == FAT12) {\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);\r
-                       val = (cluster & 1 ? val>>12 : val & 0xFFF);\r
-                       if(val == EOC_FAT12)    val = -1;\r
-               } else if(Disk->type == FAT16) {\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);\r
-                       if(val == EOC_FAT16)    val = -1;\r
-               } else {\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);\r
-                       if(val == EOC_FAT32)    val = -1;\r
-               }\r
-       #if CACHE_FAT\r
-       }\r
-       #endif /*CACHE_FAT*/\r
-       Mutex_Release( &Disk->lFAT );\r
-       LEAVE('x', val);\r
-       return val;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Allocate a new cluster\r
- */\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)\r
-{\r
-       Uint32  ret = Previous;\r
-       #if CACHE_FAT\r
-       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
-       {\r
-               Uint32  eoc;\r
-               \r
-               LOCK(Disk->lFAT);\r
-               for(ret = Previous; ret < Disk->ClusterCount; ret++)\r
-               {\r
-                       if(Disk->FATCache[ret] == 0)\r
-                               goto append;\r
-               }\r
-               for(ret = 0; ret < Previous; ret++)\r
-               {\r
-                       if(Disk->FATCache[ret] == 0)\r
-                               goto append;\r
-               }\r
-               \r
-               RELEASE(Disk->lFAT);\r
-               return 0;\r
-       \r
-       append:\r
-               switch(Disk->type)\r
-               {\r
-               case FAT12:     eoc = EOC_FAT12;        break;\r
-               case FAT16:     eoc = EOC_FAT16;        break;\r
-               case FAT32:     eoc = EOC_FAT32;        break;\r
-               default:        return 0;\r
-               }\r
-               \r
-               Disk->FATCache[ret] = eoc;\r
-               Disk->FATCache[Previous] = ret;\r
-               \r
-               RELEASE(Disk->lFAT);\r
-               return ret;\r
-       }\r
-       else\r
-       {\r
-       #endif\r
-               Uint32  val;\r
-               Uint32  ofs = Disk->bootsect.resvSectCount*512;\r
-               Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");\r
-               return 0;\r
-               \r
-               switch(Disk->type)\r
-               {\r
-               case FAT12:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
-                       if( Previous & 1 ) {\r
-                               val &= 0xFFF000;\r
-                               val |= ret;\r
-                       }\r
-                       else {\r
-                               val &= 0xFFF;\r
-                               val |= ret<<12;\r
-                       }\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
-                       \r
-                       VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
-                       if( Cluster & 1 ) {\r
-                               val &= 0xFFF000;\r
-                               val |= eoc;\r
-                       }\r
-                       else {\r
-                               val &= 0x000FFF;\r
-                               val |= eoc<<12;\r
-                       }\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
-                       break;\r
-               case FAT16:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);\r
-                       break;\r
-               case FAT32:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);\r
-                       break;\r
-               }\r
-               return ret;\r
-       #if CACHE_FAT\r
-       }\r
-       #endif\r
-}\r
-\r
-/**\r
- * \brief Free's a cluster\r
- * \return The original contents of the cluster\r
- */\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)\r
-{\r
-       Uint32  ret;\r
-       #if CACHE_FAT\r
-       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
-       {\r
-               LOCK(Disk->lFAT);\r
-               \r
-               ret = Disk->FATCache[Cluster];\r
-               Disk->FATCache[Cluster] = 0;\r
-               \r
-               RELEASE(Disk->lFAT);\r
-       }\r
-       else\r
-       {\r
-       #endif\r
-               Uint32  val;\r
-               Uint32  ofs = Disk->bootsect.resvSectCount*512;\r
-               LOCK(Disk->lFAT);\r
-               switch(Disk->type)\r
-               {\r
-               case FAT12:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);\r
-                       if( Cluster & 1 ) {\r
-                               ret = val & 0xFFF0000;\r
-                               val &= 0xFFF;\r
-                       }\r
-                       else {\r
-                               ret = val & 0xFFF;\r
-                               val &= 0xFFF000;\r
-                       }\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
-                       break;\r
-               case FAT16:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
-                       val = 0;\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
-                       break;\r
-               case FAT32:\r
-                       VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
-                       val = 0;\r
-                       VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
-                       break;\r
-               }\r
-               RELEASE(Disk->lFAT);\r
-       #if CACHE_FAT\r
-       }\r
-       #endif\r
-       if(Disk->type == FAT12 && ret == EOC_FAT12)     ret = -1;\r
-       if(Disk->type == FAT16 && ret == EOC_FAT16)     ret = -1;\r
-       if(Disk->type == FAT32 && ret == EOC_FAT32)     ret = -1;\r
-       return ret;\r
-}\r
-#endif\r
-\r
-/*\r
- * ====================\r
- *      Cluster IO\r
- * ====================\r
- */\r
-/**\r
- * \brief Read a cluster\r
- * \param Disk Disk (Volume) to read from\r
- * \param Length       Length to read\r
- * \param Buffer       Destination for read data\r
- */\r
-void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)\r
-{\r
-       ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);\r
-       VFS_ReadAt(\r
-               Disk->fileHandle,\r
-               (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
-                       * Disk->bootsect.bps,\r
-               Length,\r
-               Buffer\r
-               );\r
-       LEAVE('-');\r
-}\r
-\r
 /* ====================\r
  *       File IO\r
  * ====================\r
@@ -730,22 +469,6 @@ size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
 }\r
 \r
 #if SUPPORT_WRITE\r
-/**\r
- * \brief Write a cluster to disk\r
- */\r
-void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)\r
-{\r
-       ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);\r
-       VFS_ReadAt(\r
-               Disk->fileHandle,\r
-               (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
-                       * Disk->bootsect.bps,\r
-               Disk->BytesPerCluster,\r
-               Buffer\r
-               );\r
-       LEAVE('-');\r
-}\r
-\r
 /**\r
  * \brief Write to a file\r
  * \param Node File Node\r
@@ -753,7 +476,7 @@ void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)
  * \param Length       Size of data to write\r
  * \param Buffer       Data source\r
  */\r
-size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)\r
+size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)\r
 {\r
        tFAT_VolInfo    *disk = Node->ImplPtr;\r
        char    tmpBuf[disk->BytesPerCluster];\r
@@ -834,7 +557,6 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
        }\r
        \r
        // Finish off\r
-       tmpBuf = malloc( disk->BytesPerCluster );\r
        if( bNewCluster )\r
                memset(tmpBuf, 0, disk->BytesPerCluster);\r
        else\r
@@ -847,650 +569,6 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
 }\r
 #endif\r
 \r
-/* ====================\r
- *  File Names & Nodes\r
- * ====================\r
- */\r
-/**\r
- * \brief Converts a FAT directory entry name into a proper filename\r
- * \param dest Destination array (must be at least 13 bytes in size)\r
- * \param src  8.3 filename (concatenated, e.g 'FILE1   TXT')\r
- */\r
-void FAT_int_ProperFilename(char *dest, const char *src)\r
-{\r
-        int    inpos, outpos;\r
-       \r
-       // Name\r
-       outpos = 0;\r
-       for( inpos = 0; inpos < 8; inpos++ ) {\r
-               if(src[inpos] == ' ')   break;\r
-               dest[outpos++] = src[inpos];\r
-       }\r
-       inpos = 8;\r
-       // Check for empty extensions\r
-       if(src[8] != ' ')\r
-       {\r
-               dest[outpos++] = '.';\r
-               for( ; inpos < 11; inpos++)     {\r
-                       if(src[inpos] == ' ')   break;\r
-                       dest[outpos++] = src[inpos];\r
-               }\r
-       }\r
-       dest[outpos++] = '\0';\r
-       \r
-       //LOG("dest='%s'", dest);\r
-}\r
-\r
-/**\r
- * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
- * \brief Converts either a LFN or a 8.3 Name into a proper name\r
- * \param ft   Pointer to the file's entry in the parent directory\r
- * \param LongFileName Long file name pointer\r
- * \return Filename as a heap string\r
- */\r
-char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
-{\r
-       char    *ret;\r
-       ENTER("pft sLongFileName", ft, LongFileName);\r
-       //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);\r
-       #if USE_LFN\r
-       if(LongFileName && LongFileName[0] != '\0')\r
-       {       \r
-               ret = strdup(LongFileName);\r
-       }\r
-       else\r
-       {\r
-       #endif\r
-               ret = (char*) malloc(13);\r
-               if( !ret ) {\r
-                       Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");\r
-                       return NULL;\r
-               }\r
-               FAT_int_ProperFilename(ret, ft->name);\r
-       #if USE_LFN\r
-       }\r
-       #endif\r
-       LEAVE('s', ret);\r
-       return ret;\r
-}\r
-\r
-/**\r
- * \brief Creates a tVFS_Node structure for a given file entry\r
- * \param Parent       Parent directory VFS node\r
- * \param Entry        File table entry for the new node\r
- * \param Pos  Position in the parent of the new node\r
- */\r
-tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos)\r
-{\r
-       tVFS_Node       node;\r
-       tVFS_Node       *ret;\r
-       tFAT_VolInfo    *disk = Parent->ImplPtr;\r
-       \r
-       ENTER("pParent pFT", Parent, Entry);\r
-       LOG("disk = %p", disk);\r
-       \r
-       memset(&node, 0, sizeof(tVFS_Node));\r
-       \r
-       // Set Other Data\r
-       // 0-27: Cluster, 32-59: Parent Cluster\r
-       node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);\r
-       LOG("node.Inode = %llx", node.Inode);\r
-       // Position in parent directory\r
-       node.ImplInt = Pos & 0xFFFF;\r
-       // Disk Pointer\r
-       node.ImplPtr = disk;\r
-       node.Size = Entry->size;\r
-       LOG("Entry->size = %i", Entry->size);\r
-       // root:root\r
-       node.UID = 0;   node.GID = 0;\r
-       node.NumACLs = 1;\r
-       \r
-       node.Flags = 0;\r
-       if(Entry->attrib & ATTR_DIRECTORY)      node.Flags |= VFS_FFLAG_DIRECTORY;\r
-       if(Entry->attrib & ATTR_READONLY) {\r
-               node.Flags |= VFS_FFLAG_READONLY;\r
-               node.ACLs = &gVFS_ACL_EveryoneRX;       // R-XR-XR-X\r
-       }\r
-       else {\r
-               node.ACLs = &gVFS_ACL_EveryoneRWX;      // RWXRWXRWX\r
-       }\r
-       \r
-       // Create timestamps\r
-       node.ATime = timestamp(0,0,0,\r
-                       ((Entry->adate&0x1F) - 1),      // Days\r
-                       ((Entry->adate&0x1E0) - 1),     // Months\r
-                       1980+((Entry->adate&0xFF00)>>8) // Years\r
-                       );\r
-       \r
-       node.CTime = Entry->ctimems * 10;       // Miliseconds\r
-       node.CTime += timestamp(\r
-                       ((Entry->ctime&0x1F)<<1),       // Seconds\r
-                       ((Entry->ctime&0x3F0)>>5),      // Minutes\r
-                       ((Entry->ctime&0xF800)>>11),    // Hours\r
-                       ((Entry->cdate&0x1F)-1),                // Days\r
-                       ((Entry->cdate&0x1E0)-1),               // Months\r
-                       1980+((Entry->cdate&0xFF00)>>8) // Years\r
-                       );\r
-                       \r
-       node.MTime = timestamp(\r
-                       ((Entry->mtime&0x1F)<<1),       // Seconds\r
-                       ((Entry->mtime&0x3F0)>>5),      // Minutes\r
-                       ((Entry->mtime&0xF800)>>11),    // Hours\r
-                       ((Entry->mdate&0x1F)-1),                // Days\r
-                       ((Entry->mdate&0x1E0)-1),               // Months\r
-                       1980+((Entry->mdate&0xFF00)>>8) // Years\r
-                       );\r
-       \r
-       // Set pointers\r
-       if(node.Flags & VFS_FFLAG_DIRECTORY) {\r
-               //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);\r
-               node.Type = &gFAT_DirType;      \r
-               node.Size = -1;\r
-       }\r
-       else {\r
-               node.Type = &gFAT_FileType;\r
-       }\r
-       \r
-       ret = Inode_CacheNode(disk->inodeHandle, &node);\r
-       LEAVE('p', ret);\r
-       return ret;\r
-}\r
-\r
-/* \r
- * ====================\r
- *     Directory IO\r
- * ====================\r
- */\r
-\r
-/**\r
- * \brief Reads a sector from the disk\r
- * \param Node Directory node to read\r
- * \param Sector       Sector number in the directory to read\r
- * \param Buffer       Destination buffer for the read data\r
- */\r
-int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)\r
-{\r
-       Uint64  addr;\r
-       tFAT_VolInfo    *disk = Node->ImplPtr;\r
-       \r
-       ENTER("pNode iSector pEntry", Node, Sector, Buffer);\r
-       \r
-       // Parse address\r
-       if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))\r
-       {\r
-               LEAVE('i', 1);\r
-               return 1;\r
-       }\r
-       \r
-       LOG("addr = 0x%llx", addr);\r
-       // Read Sector\r
-       if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)\r
-       {\r
-               LEAVE('i', 1);\r
-               return 1;\r
-       }\r
-       \r
-       LEAVE('i', 0);\r
-       return 0;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Writes an entry to the disk\r
- * \todo Support expanding a directory\r
- * \param Node Directory node\r
- * \param ID   ID of entry to update\r
- * \param Entry        Entry data\r
- * \return Zero on success, non-zero on error\r
- */\r
-int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)\r
-{\r
-       Uint64  addr = 0;\r
-        int    tmp;\r
-       Uint32  cluster = 0;\r
-       tFAT_VolInfo    *disk = Node->ImplPtr;\r
-       \r
-       ENTER("pNode iID pEntry", Node, ID, Entry);\r
-       \r
-       tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
-       if( tmp )\r
-       {\r
-               //TODO: Allocate a cluster\r
-               cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);\r
-               if(cluster == -1) {\r
-                       Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);\r
-                       LEAVE('i', 1);\r
-                       return 1;\r
-               }\r
-               FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
-       }\r
-       \r
-\r
-       LOG("addr = 0x%llx", addr);\r
-       \r
-       // Read Sector\r
-       VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);      // Read Dir Data\r
-       \r
-       LEAVE('i', 0);\r
-       return 0;\r
-}\r
-#endif\r
-\r
-#if USE_LFN    \r
-/**\r
- * \fn char *FAT_int_GetLFN(tVFS_Node *node)\r
- * \brief Return pointer to LFN cache entry\r
- * \param Node Directory node\r
- * \param ID   ID of the short name\r
- * \return Pointer to the LFN cache entry\r
- */\r
-char *FAT_int_GetLFN(tVFS_Node *Node, int ID)\r
-{\r
-       tFAT_LFNCache   *cache;\r
-        int    i, firstFree;\r
-       \r
-       Mutex_Acquire( &Node->Lock );\r
-       \r
-       // TODO: Thread Safety (Lock things)\r
-       cache = Node->Data;\r
-       \r
-       // Create a cache if it isn't there\r
-       if(!cache) {\r
-               cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );\r
-               cache->NumEntries = 1;\r
-               cache->Entries[0].ID = ID;\r
-               cache->Entries[0].Data[0] = '\0';\r
-               Mutex_Release( &Node->Lock );\r
-               //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);\r
-               return cache->Entries[0].Data;\r
-       }\r
-       \r
-       // Scan for this entry\r
-       firstFree = -1;\r
-       for( i = 0; i < cache->NumEntries; i++ )\r
-       {\r
-               if( cache->Entries[i].ID == ID ) {\r
-                       Mutex_Release( &Node->Lock );\r
-                       //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);\r
-                       return cache->Entries[i].Data;\r
-               }\r
-               if( cache->Entries[i].ID == -1 && firstFree == -1 )\r
-                       firstFree = i;\r
-       }\r
-       \r
-       if(firstFree == -1) {\r
-               // Use `i` for temp length\r
-               i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);\r
-               Node->Data = realloc( Node->Data, i );\r
-               if( !Node->Data ) {\r
-                       Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);\r
-                       Mutex_Release( &Node->Lock );\r
-                       return NULL;\r
-               }\r
-               //Log_Debug("FAT", "Realloc (%i)\n", i);\r
-               cache = Node->Data;\r
-               i = cache->NumEntries;\r
-               cache->NumEntries ++;\r
-       }\r
-       else {\r
-               i = firstFree;\r
-       }\r
-       \r
-       // Create new entry\r
-       cache->Entries[ i ].ID = ID;\r
-       cache->Entries[ i ].Data[0] = '\0';\r
-       \r
-       Mutex_Release( &Node->Lock );\r
-       //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);\r
-       return cache->Entries[ i ].Data;\r
-}\r
-\r
-/**\r
- * \fn void FAT_int_DelLFN(tVFS_Node *node)\r
- * \brief Delete a LFN cache entry\r
- * \param Node Directory node\r
- * \param ID   File Entry ID\r
- */\r
-void FAT_int_DelLFN(tVFS_Node *Node, int ID)\r
-{\r
-       tFAT_LFNCache   *cache = Node->Data;\r
-        int    i;\r
-       \r
-       // Fast return\r
-       if(!cache)      return;\r
-       \r
-       // Scan for a current entry\r
-       for( i = 0; i < cache->NumEntries; i++ )\r
-       {\r
-               if( cache->Entries[i].ID == ID )\r
-                       cache->Entries[i].ID = -1;\r
-       }\r
-       return ;\r
-}\r
-#endif\r
-\r
-/**\r
- * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
- * \param Node Node structure of directory\r
- * \param ID   Directory position\r
- * \return Filename as a heap string, NULL or VFS_SKIP\r
- */\r
-char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
-{\r
-       fat_filetable   fileinfo[16];   // sizeof(fat_filetable)=32, so 16 per sector\r
-        int    a = 0;\r
-       char    *ret;\r
-       #if USE_LFN\r
-       char    *lfn = NULL;\r
-       #endif\r
-       \r
-       ENTER("pNode iID", Node, ID);\r
-       \r
-       if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))\r
-       {\r
-               LOG("End of chain, end of dir");\r
-               LEAVE('n');\r
-               return NULL;\r
-       }\r
-       \r
-       // Offset in sector\r
-       a = ID % 16;\r
-\r
-       LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);\r
-       \r
-       // Check if this is the last entry\r
-       if( fileinfo[a].name[0] == '\0' ) {\r
-               Node->Size = ID;\r
-               LOG("End of list");\r
-               LEAVE('n');\r
-               return NULL;    // break\r
-       }\r
-       \r
-       // Check for empty entry\r
-       if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {\r
-               LOG("Empty Entry");\r
-               #if 0   // Stop on empty entry?\r
-               LEAVE('n');\r
-               return NULL;    // Stop\r
-               #else\r
-               LEAVE('p', VFS_SKIP);\r
-               return VFS_SKIP;        // Skip\r
-               #endif\r
-       }\r
-       \r
-       #if USE_LFN\r
-       // Get Long File Name Cache\r
-       if(fileinfo[a].attrib == ATTR_LFN)\r
-       {\r
-               fat_longfilename        *lfnInfo;\r
-               \r
-               lfnInfo = (fat_longfilename *) &fileinfo[a];\r
-               \r
-               // Get cache for corresponding file\r
-               // > ID + Index gets the corresponding short node\r
-               lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );\r
-               \r
-               // Bit 6 indicates the start of an entry\r
-               if(lfnInfo->id & 0x40)  memset(lfn, 0, 256);\r
-               \r
-               a = ((lfnInfo->id & 0x3F) - 1) * 13;\r
-               //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a);\r
-               \r
-               // Sanity Check (FAT implementations should not allow >255 character names)\r
-               if(a > 255)     return VFS_SKIP;\r
-               \r
-               // Append new bytes\r
-               lfn[a+ 0] = lfnInfo->name1[0];  lfn[a+ 1] = lfnInfo->name1[1];\r
-               lfn[a+ 2] = lfnInfo->name1[2];  lfn[a+ 3] = lfnInfo->name1[3];\r
-               lfn[a+ 4] = lfnInfo->name1[4];  \r
-               lfn[a+ 5] = lfnInfo->name2[0];  lfn[a+ 6] = lfnInfo->name2[1];\r
-               lfn[a+ 7] = lfnInfo->name2[2];  lfn[a+ 8] = lfnInfo->name2[3];\r
-               lfn[a+ 9] = lfnInfo->name2[4];  lfn[a+10] = lfnInfo->name2[5];\r
-               lfn[a+11] = lfnInfo->name3[0];  lfn[a+12] = lfnInfo->name3[1];\r
-               LOG("lfn = '%s'", lfn);\r
-               //Log_Debug("FAT", "lfn = '%s'", lfn);\r
-               LEAVE('p', VFS_SKIP);\r
-               return VFS_SKIP;\r
-       }\r
-       #endif\r
-       \r
-       // Check if it is a volume entry\r
-       if(fileinfo[a].attrib & 0x08) {\r
-               LEAVE('p', VFS_SKIP);\r
-               return VFS_SKIP;\r
-       }\r
-       // Ignore .\r
-       if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {\r
-               LEAVE('p', VFS_SKIP);\r
-               return VFS_SKIP;\r
-       }\r
-       // and ..\r
-       if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {\r
-               LEAVE('p', VFS_SKIP);\r
-               return VFS_SKIP;\r
-       }\r
-       \r
-       LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",\r
-               fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],\r
-               fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],\r
-               fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );\r
-       \r
-       #if USE_LFN\r
-       lfn = FAT_int_GetLFN(Node, ID);\r
-       //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);\r
-       ret = FAT_int_CreateName(&fileinfo[a], lfn);\r
-       #else\r
-       ret = FAT_int_CreateName(&fileinfo[a], NULL);\r
-       #endif\r
-       \r
-       LEAVE('s', ret);\r
-       return ret;\r
-}\r
-\r
-/**\r
- * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
- * \brief Finds an entry in the current directory\r
- */\r
-tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)\r
-{\r
-       fat_filetable   fileinfo[16];\r
-       char    tmpName[13];\r
-       #if USE_LFN\r
-       fat_longfilename        *lfnInfo;\r
-       char    lfn[256];\r
-        int    lfnPos=255, lfnId = -1;\r
-       #endif\r
-        int    i;\r
-       tVFS_Node       *tmpNode;\r
-       tFAT_VolInfo    *disk = Node->ImplPtr;\r
-       Uint32  cluster;\r
-       \r
-       ENTER("pNode sname", Node, Name);       \r
-\r
-       // Fast Returns\r
-       if(!Name || Name[0] == '\0') {\r
-               LEAVE('n');\r
-               return NULL;\r
-       }\r
-       \r
-       for( i = 0; ; i++ )\r
-       {\r
-               if((i & 0xF) == 0) {\r
-                       if(FAT_int_ReadDirSector(Node, i/16, fileinfo))\r
-                       {\r
-                               LEAVE('n');\r
-                               return NULL;\r
-                       }\r
-               }\r
-               \r
-               //Check if the files are free\r
-               if(fileinfo[i&0xF].name[0] == '\0')     break;  // End of List marker\r
-               if(fileinfo[i&0xF].name[0] == '\xE5')   continue;       // Free entry\r
-               \r
-               \r
-               #if USE_LFN\r
-               // Long File Name Entry\r
-               if(fileinfo[i & 0xF].attrib == ATTR_LFN)\r
-               {\r
-                       lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];\r
-                       if(lfnInfo->id & 0x40) {\r
-                               memset(lfn, 0, 256);\r
-                               lfnPos = (lfnInfo->id & 0x3F) * 13 - 1;\r
-                       }\r
-                       // Sanity check the position so we don't overflow\r
-                       if( lfnPos < 12 )\r
-                               continue ;\r
-                       lfn[lfnPos--] = lfnInfo->name3[1];      lfn[lfnPos--] = lfnInfo->name3[0];\r
-                       lfn[lfnPos--] = lfnInfo->name2[5];      lfn[lfnPos--] = lfnInfo->name2[4];\r
-                       lfn[lfnPos--] = lfnInfo->name2[3];      lfn[lfnPos--] = lfnInfo->name2[2];\r
-                       lfn[lfnPos--] = lfnInfo->name2[1];      lfn[lfnPos--] = lfnInfo->name2[0];\r
-                       lfn[lfnPos--] = lfnInfo->name1[4];      lfn[lfnPos--] = lfnInfo->name1[3];\r
-                       lfn[lfnPos--] = lfnInfo->name1[2];      lfn[lfnPos--] = lfnInfo->name1[1];\r
-                       lfn[lfnPos--] = lfnInfo->name1[0];\r
-                       if((lfnInfo->id&0x3F) == 1)\r
-                       {\r
-                               lfnId = i+1;\r
-                       }\r
-               }\r
-               else\r
-               {\r
-                       // Remove LFN if it does not apply\r
-                       if(lfnId != i)  lfn[0] = '\0';\r
-               #else\r
-               if(fileinfo[i&0xF].attrib == ATTR_LFN)  continue;\r
-               #endif\r
-                       // Get Real Filename\r
-                       FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);\r
-                       LOG("tmpName = '%s'", tmpName);\r
-               \r
-                       // Only the long name is case sensitive, 8.3 is not\r
-                       #if USE_LFN\r
-                       if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0)\r
-                       #else\r
-                       if(strucmp(tmpName, Name) == 0)\r
-                       #endif\r
-                       {\r
-                               cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);\r
-                               tmpNode = Inode_GetCache(disk->inodeHandle, cluster);\r
-                               if(tmpNode == NULL)     // Node is not cached\r
-                               {\r
-                                       tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i);\r
-                               }\r
-                               LEAVE('p', tmpNode);\r
-                               return tmpNode;\r
-                       }\r
-               #if USE_LFN\r
-               }\r
-               #endif\r
-       }\r
-       \r
-       LEAVE('n');\r
-       return NULL;\r
-}\r
-\r
-tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)\r
-{\r
-       tFAT_VolInfo    *disk = Root->ImplPtr;\r
-        int    ents_per_sector = 512 / sizeof(fat_filetable); \r
-       fat_filetable   fileinfo[ents_per_sector];\r
-        int    sector = 0, i;\r
-       tVFS_Node       stub_node;\r
-\r
-       ENTER("pRoot XInode", Root, Inode);\r
-\r
-       stub_node.ImplPtr = disk;\r
-       stub_node.Size = -1;\r
-       stub_node.Inode = Inode >> 32;\r
-\r
-       for( i = 0; ; i ++ )\r
-       {\r
-               if( i == 0 || i == ents_per_sector )\r
-               {\r
-                       if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo))\r
-                       {\r
-                               LOG("ReadDirSector failed");\r
-                               LEAVE('n');\r
-                               return NULL;\r
-                       }\r
-                       i = 0;\r
-                       sector ++;\r
-               }\r
-       \r
-               // Check for free/end of list\r
-               if(fileinfo[i].name[0] == '\0') break;  // End of List marker\r
-               if(fileinfo[i].name[0] == '\xE5')       continue;       // Free entry\r
-               \r
-               if(fileinfo[i].attrib == ATTR_LFN)      continue;\r
-\r
-               LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);\r
-               #if DEBUG\r
-               {\r
-                       char    tmpName[13];\r
-                       FAT_int_ProperFilename(tmpName, fileinfo[i].name);\r
-                       LOG("tmpName = '%s'", tmpName);\r
-               }\r
-               #endif\r
-               \r
-       \r
-               if(fileinfo[i].cluster != (Inode & 0xFFFF))     continue;\r
-               if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF))   continue;\r
-\r
-               LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i));\r
-       }\r
-       LOG("sector = %i, i = %i", sector, i);\r
-       LEAVE('n');\r
-       return NULL;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
- * \brief Create a new node\r
- */\r
-int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
-{\r
-       return 0;\r
-}\r
-\r
-/**\r
- * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
- * \brief Rename / Delete a file\r
- */\r
-int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
-{\r
-       tVFS_Node       *child;\r
-       fat_filetable   ft = {0};\r
-        int    ret;\r
-       \r
-       child = FAT_FindDir(Node, OldName);\r
-       if(!child)      return ENOTFOUND;\r
-       \r
-       // Delete?\r
-       if( NewName == NULL )\r
-       {\r
-               child->ImplInt |= FAT_FLAG_DELETE;      // Mark for deletion on close\r
-               \r
-               // Delete from the directory\r
-               ft.name[0] = '\xE9';\r
-               FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);\r
-               \r
-               // Return success\r
-               ret = EOK;\r
-       }\r
-       // Rename\r
-       else\r
-       {\r
-               Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",\r
-                       Node, OldName, NewName);\r
-               ret = ENOTIMPL;\r
-       }\r
-       \r
-       // Close child\r
-       child->Close( child );\r
-       return ret;\r
-}\r
-#endif\r
-\r
 /**\r
  * \fn void FAT_CloseFile(tVFS_Node *Node)\r
  * \brief Close an open file\r
@@ -1505,13 +583,21 @@ void FAT_CloseFile(tVFS_Node *Node)
        // deletion)\r
        if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )\r
        {\r
-               tFAT_VolInfo    buf[16];\r
-               tFAT_VolInfo    *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];\r
-               \r
-               FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);\r
-               ft->size = Node->Size;\r
+               fat_filetable   ft;\r
+               tVFS_Node       *dirnode;\r
+\r
+               dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);\r
+               if( !dirnode ) {\r
+                       Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);\r
+                       return ;\r
+               }\r
+\r
+               int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);\r
+               ft.size = Node->Size;\r
                // TODO: update adate, mtime, mdate\r
-               FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);\r
+               FAT_int_WriteDirEntry(dirnode, id, &ft);\r
+               \r
+               dirnode->Type->Close(dirnode);\r
                \r
                Node->ImplInt &= ~FAT_FLAG_DIRTY;\r
        }\r
@@ -1532,6 +618,6 @@ void FAT_CloseFile(tVFS_Node *Node)
                #endif\r
        }\r
        \r
-       Inode_UncacheNode(disk->inodeHandle, Node->Inode);\r
+       FAT_int_DerefNode(Node);\r
        return ;\r
 }\r
diff --git a/KernelLand/Modules/Filesystems/FAT/fatio.c b/KernelLand/Modules/Filesystems/FAT/fatio.c
new file mode 100644 (file)
index 0000000..43bd622
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * fatio.c
+ * - FAT Manipulation and Cluster IO
+ */
+#define DEBUG  1
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === CODE ===
+/**
+ * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+ * \brief Fetches a value from the FAT
+ */
+Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+{
+       Uint32  val = 0;
+       Uint32  ofs;
+       ENTER("pDisk xCluster", Disk, cluster);
+       Mutex_Acquire( &Disk->lFAT );
+       #if CACHE_FAT
+       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+       {
+               val = Disk->FATCache[cluster];
+               if(Disk->type == FAT12 && val == EOC_FAT12)     val = -1;
+               if(Disk->type == FAT16 && val == EOC_FAT16)     val = -1;
+               if(Disk->type == FAT32 && val == EOC_FAT32)     val = -1;
+       }
+       else
+       {
+       #endif
+               ofs = Disk->bootsect.resvSectCount*512;
+               if(Disk->type == FAT12) {
+                       VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);
+                       val = (cluster & 1 ? val>>12 : val & 0xFFF);
+                       if(val == EOC_FAT12)    val = -1;
+               } else if(Disk->type == FAT16) {
+                       VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);
+                       if(val == EOC_FAT16)    val = -1;
+               } else {
+                       VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);
+                       if(val == EOC_FAT32)    val = -1;
+               }
+       #if CACHE_FAT
+       }
+       #endif /*CACHE_FAT*/
+       Mutex_Release( &Disk->lFAT );
+       LEAVE('x', val);
+       return val;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Allocate a new cluster
+ */
+Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)
+{
+       Uint32  ret = -1, eoc;
+
+       switch(Disk->type)
+       {
+       case FAT12:     eoc = EOC_FAT12;        break;
+       case FAT16:     eoc = EOC_FAT16;        break;
+       case FAT32:     eoc = EOC_FAT32;        break;
+       default:        return 0;
+       }
+       
+       #if CACHE_FAT
+       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+       {
+                int    bFoundCluster = 0;
+               Mutex_Acquire(&Disk->lFAT);
+               if( Previous != -1 )
+               {
+                       for(ret = Previous; ret < Disk->ClusterCount; ret++)
+                       {
+                               if(Disk->FATCache[ret] != 0) {
+                                       bFoundCluster = 1;
+                                       break;
+                               }
+                       }
+               }
+               if( !bFoundCluster )
+               {
+                       for(ret = 0; ret < Previous; ret++)
+                       {
+                               if(Disk->FATCache[ret] == 0) {
+                                       bFoundCluster = 1;
+                                       break;
+                               }
+                       }
+               }
+               
+               if(bFoundCluster)
+               { 
+                       Disk->FATCache[ret] = eoc;
+                       if( Previous != -1 )
+                               Disk->FATCache[Previous] = ret;
+               }
+               else
+               {
+                       ret = 0;
+               }
+                       
+               Mutex_Release(&Disk->lFAT);
+               return ret;
+       }
+       else
+       {
+       #endif
+               Uint32  val = 0;
+               Uint32  base = Disk->bootsect.resvSectCount*512;
+                int    block = 0, block_ofs = 0;
+                int    first_block;
+               const int       block_size = 512*3;
+               const int       ents_per_block_12 = block_size * 2 / 3; // 1.5 bytes per entry
+//             const int       ents_per_block_16 = block_size / 2;     // 2 bytes per entry
+//             const int       ents_per_block_32 = block_size / 4;     // 4 bytes per entry
+                int    block_count_12 = DivUp(Disk->ClusterCount, ents_per_block_12);
+               Uint8   sector_data[block_size+1];
+               sector_data[block_size] = 0;
+               
+               Mutex_Acquire(&Disk->lFAT);
+               switch(Disk->type)
+               {
+               case FAT12:
+                       if( Previous != -1 )
+                               block = Previous / ents_per_block_12;
+                       else
+                               block = 0;
+                       first_block = block;
+                       
+                       // Search within the same block as the previous cluster first
+                       do {
+                               VFS_ReadAt(Disk->fileHandle, base + block, block_size, sector_data);
+                               for( block_ofs = 0; block_ofs < ents_per_block_12; block_ofs ++ )
+                               {
+                                       Uint32  *valptr = (void*)( sector_data + block_ofs / 2 * 3 );
+                                        int    bitofs = 12 * (block_ofs % 2);
+//                                     LOG("%i:%i - FAT Ent 0x%03x", block, block_ofs, (*valptr>>bitofs) & 0xFFF);
+                                       if( ((*valptr >> bitofs) & 0xFFF) == 0 ) {
+                                               // Found a free cluster
+                                               *valptr |= EOC_FAT12 << bitofs;
+                                               ret = block * ents_per_block_12 + block_ofs;
+                                               break;
+                                       }
+                               }
+                               // Check for early break from the above loop
+                               if( block_ofs != ents_per_block_12 )
+                                       break;
+
+                               // Next block please
+                               block ++;
+                               if( block == block_count_12 )
+                                       block = 0;
+                       } while( block != first_block );
+                       
+                       if( ret != 0 )  // TODO: Could cluster 0 be valid?
+                       {
+                               // Write back changes to this part of the FAT
+                               VFS_WriteAt(Disk->fileHandle, base + block, block_size, sector_data);
+       
+                               // Note the new cluster in the chain
+                               VFS_ReadAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val); 
+                               if( ret & 1 ) {
+                                       val &= 0x000FFF;
+                                       val |= ret << 12;
+                               }
+                               else {
+                                       val &= 0xFFF000;
+                                       val |= ret << 0;
+                               }
+                               VFS_WriteAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val);
+                       }
+                       break;
+               case FAT16:
+                       Log_Warning("FAT", "TODO: Implement cluster allocation with FAT16");
+//                     VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
+//                     VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);
+                       break;
+               case FAT32:
+                       Log_Warning("FAT", "TODO: Implement cluster allocation with FAT32");
+//                     VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
+//                     VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);
+                       break;
+               }
+               Mutex_Release(&Disk->lFAT);
+               return ret;
+       #if CACHE_FAT
+       }
+       #endif
+}
+
+/**
+ * \brief Free's a cluster
+ * \return The original contents of the cluster
+ */
+Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+       Uint32  ret;
+       
+       Mutex_Acquire(&Disk->lFAT);
+       #if CACHE_FAT
+       if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+       {
+               
+               ret = Disk->FATCache[Cluster];
+               Disk->FATCache[Cluster] = 0;
+       }
+       else
+       {
+       #endif
+               Uint32  val;
+               Uint32  ofs = Disk->bootsect.resvSectCount*512;
+               switch(Disk->type)
+               {
+               case FAT12:
+                       VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+                       if( Cluster & 1 ) {
+                               ret = val & 0xFFF0000;
+                               val &= 0xFFF;
+                       }
+                       else {
+                               ret = val & 0xFFF;
+                               val &= 0xFFF000;
+                       }
+                       VFS_WriteAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+                       break;
+               case FAT16:
+                       VFS_ReadAt(Disk->fileHandle, ofs+Cluster*2, 2, &ret);
+                       val = 0;
+                       VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
+                       break;
+               case FAT32:
+                       VFS_ReadAt(Disk->fileHandle, ofs+Cluster*4, 4, &ret);
+                       val = 0;
+                       VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
+                       break;
+               }
+       #if CACHE_FAT
+       }
+       #endif
+       Mutex_Release(&Disk->lFAT);
+       if(Disk->type == FAT12 && ret == EOC_FAT12)     ret = -1;
+       if(Disk->type == FAT16 && ret == EOC_FAT16)     ret = -1;
+       if(Disk->type == FAT32 && ret == EOC_FAT32)     ret = -1;
+       return ret;
+}
+#endif
+
+/*
+ * ====================
+ *      Cluster IO
+ * ====================
+ */
+/**
+ * \brief Read a cluster
+ * \param Disk Disk (Volume) to read from
+ * \param Length       Length to read
+ * \param Buffer       Destination for read data
+ */
+void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)
+{
+       ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);
+       VFS_ReadAt(
+               Disk->fileHandle,
+               (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+                       * Disk->bootsect.bps,
+               Length,
+               Buffer
+               );
+       LEAVE('-');
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Write a cluster to disk
+ */
+void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, const void *Buffer)
+{
+       ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);
+       VFS_WriteAt(
+               Disk->fileHandle,
+               (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+                       * Disk->bootsect.bps,
+               Disk->BytesPerCluster,
+               Buffer
+               );
+       LEAVE('-');
+}
+#endif
index 6896945..9911563 100644 (file)
@@ -6,6 +6,9 @@
 #ifndef _FS_FAT_H_\r
 #define _FS_FAT_H_\r
 \r
+#define FAT16_MIN_SECTORS      4085\r
+#define FAT32_MIN_CLUSTERS     65525\r
+\r
 // === On Disk Structures ===\r
 /**\r
  * \struct fat_bootsect_s\r
@@ -116,16 +119,6 @@ struct fat_longfilename_s {
  * \}\r
  */\r
 \r
-/**\r
- * \brief Internal IDs for FAT types\r
- */\r
-enum eFatType\r
-{\r
-       FAT12,  //!< FAT12 Volume\r
-       FAT16,  //!< FAT16 Volume\r
-       FAT32,  //!< FAT32 Volume\r
-};\r
-\r
 /**\r
  * \name End of Cluster marks\r
  * \brief FAT values that indicate the end of a cluster chain in\r
@@ -143,29 +136,4 @@ typedef struct fat_bootsect_s fat_bootsect;
 typedef struct fat_filetable_s fat_filetable;\r
 typedef struct fat_longfilename_s fat_longfilename;\r
 \r
-// === Memory Structures ===\r
-/**\r
- * \struct drv_fat_volinfo_s\r
- * \brief Representation of a volume in memory\r
- */\r
-struct drv_fat_volinfo_s\r
-{\r
-        int    fileHandle;     //!< File Handle\r
-        int    type;   //!< FAT Type. See eFatType\r
-       char    name[12];       //!< Volume Name (With NULL Terminator)\r
-       tMutex  lFAT;   //!< Lock to prevent double-writing to the FAT\r
-       Uint32  firstDataSect;  //!< First data sector\r
-       Uint32  rootOffset;     //!< Root Offset (clusters)\r
-       Uint32  ClusterCount;   //!< Total Cluster Count\r
-       fat_bootsect    bootsect;       //!< Boot Sector\r
-       tVFS_Node       rootNode;       //!< Root Node\r
-        int    BytesPerCluster;\r
-        int    inodeHandle;    //!< Inode Cache Handle\r
-       #if CACHE_FAT\r
-       Uint32  *FATCache;      //!< FAT Cache\r
-       #endif\r
-};\r
-\r
-typedef struct drv_fat_volinfo_s tFAT_VolInfo;\r
-\r
 #endif\r
diff --git a/KernelLand/Modules/Filesystems/FAT/nodecache.c b/KernelLand/Modules/Filesystems/FAT/nodecache.c
new file mode 100644 (file)
index 0000000..e181e98
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * nodecache.c
+ * - FAT-Specific node caching
+ */
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+extern tVFS_Node       *FAT_int_CacheNode(tFAT_VolInfo *Disk, const tVFS_Node *Node);
+
+// === CODE ===
+/**
+ * \brief Creates a tVFS_Node structure for a given file entry
+ * \param Parent       Parent directory VFS node
+ * \param Entry        File table entry for the new node
+ */
+tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry)
+{
+       tVFS_Node       node;
+       tVFS_Node       *ret;
+       tFAT_VolInfo    *disk = Parent->ImplPtr;
+
+       ENTER("pParent pEntry", Parent, Entry);
+       LOG("disk = %p", disk);
+       
+       if( (ret = FAT_int_GetNode(disk, Entry->cluster | (Entry->clusterHi<<16))) ) {
+               LEAVE('p', ret);
+               return ret;
+       }
+
+       memset(&node, 0, sizeof(tVFS_Node));
+       
+       // Set Other Data
+       // 0-27: Cluster, 32-59: Parent Cluster
+       node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);
+       LOG("node.Inode = %llx", node.Inode);
+       node.ImplInt = 0;
+       // Disk Pointer
+       node.ImplPtr = disk;
+       node.Size = Entry->size;
+       LOG("Entry->size = %i", Entry->size);
+       // root:root
+       node.UID = 0;   node.GID = 0;
+       node.NumACLs = 1;
+       
+       node.Flags = 0;
+       if(Entry->attrib & ATTR_DIRECTORY)      node.Flags |= VFS_FFLAG_DIRECTORY;
+       if(Entry->attrib & ATTR_READONLY) {
+               node.Flags |= VFS_FFLAG_READONLY;
+               node.ACLs = &gVFS_ACL_EveryoneRX;       // R-XR-XR-X
+       }
+       else {
+               node.ACLs = &gVFS_ACL_EveryoneRWX;      // RWXRWXRWX
+       }
+       
+       // Create timestamps
+       node.ATime = timestamp(0,0,0,
+                       ((Entry->adate&0x1F) - 1),      // Days
+                       ((Entry->adate&0x1E0) - 1),     // Months
+                       1980+((Entry->adate&0xFF00)>>8) // Years
+                       );
+       
+       node.CTime = Entry->ctimems * 10;       // Miliseconds
+       node.CTime += timestamp(
+                       ((Entry->ctime&0x1F)<<1),       // Seconds
+                       ((Entry->ctime&0x3F0)>>5),      // Minutes
+                       ((Entry->ctime&0xF800)>>11),    // Hours
+                       ((Entry->cdate&0x1F)-1),                // Days
+                       ((Entry->cdate&0x1E0)-1),               // Months
+                       1980+((Entry->cdate&0xFF00)>>8) // Years
+                       );
+                       
+       node.MTime = timestamp(
+                       ((Entry->mtime&0x1F)<<1),       // Seconds
+                       ((Entry->mtime&0x3F0)>>5),      // Minutes
+                       ((Entry->mtime&0xF800)>>11),    // Hours
+                       ((Entry->mdate&0x1F)-1),                // Days
+                       ((Entry->mdate&0x1E0)-1),               // Months
+                       1980+((Entry->mdate&0xFF00)>>8) // Years
+                       );
+       
+       // Set pointers
+       if(node.Flags & VFS_FFLAG_DIRECTORY) {
+               //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);
+               node.Type = &gFAT_DirType;      
+               node.Size = -1;
+       }
+       else {
+               node.Type = &gFAT_FileType;
+       }
+
+       // TODO: Cache node     
+       ret = FAT_int_CacheNode(disk, &node);
+       LEAVE('p', ret);
+       return ret;
+}
+
+tVFS_Node *FAT_int_CreateIncompleteDirNode(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+       // If the directory isn't in the cache, what do?
+       // - we want to lock it such that we don't collide, but don't want to put crap data in the cache
+       // - Put a temp node in with a flag that indicates it's incomplete?
+       return NULL;
+}
+
+tVFS_Node *FAT_int_GetNode(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+       if( Cluster == Disk->rootOffset )
+               return &Disk->rootNode;
+       Mutex_Acquire(&Disk->lNodeCache);
+       tFAT_CachedNode *cnode;
+
+       for(cnode = Disk->NodeCache; cnode; cnode = cnode->Next)
+       {
+               if( (cnode->Node.Inode & 0xFFFFFFFF) == Cluster ) {
+                       cnode->Node.ReferenceCount ++;
+                       Mutex_Release(&Disk->lNodeCache);
+                       return &cnode->Node;
+               }
+       }       
+
+       Mutex_Release(&Disk->lNodeCache);
+       return NULL;
+}
+
+tVFS_Node *FAT_int_CacheNode(tFAT_VolInfo *Disk, const tVFS_Node *Node)
+{
+       tFAT_CachedNode *cnode, *prev = NULL;
+       Mutex_Acquire(&Disk->lNodeCache);
+       
+       for(cnode = Disk->NodeCache; cnode; prev = cnode, cnode = cnode->Next )
+       {
+               if( cnode->Node.Inode == Node->Inode ) {
+                       cnode->Node.ReferenceCount ++;
+                       Mutex_Release(&Disk->lNodeCache);
+                       return &cnode->Node;
+               }
+       }
+       
+       cnode = malloc(sizeof(tFAT_CachedNode));
+       cnode->Next = NULL;
+       memcpy(&cnode->Node, Node, sizeof(tVFS_Node));
+       cnode->Node.ReferenceCount = 1;
+       
+       if( prev )
+               prev->Next = cnode;
+       else
+               Disk->NodeCache = cnode;
+       
+       Mutex_Release(&Disk->lNodeCache);
+       return &cnode->Node;
+}
+
+void FAT_int_DerefNode(tVFS_Node *Node)
+{
+       tFAT_VolInfo    *Disk = Node->ImplPtr;
+       tFAT_CachedNode *cnode, *prev = NULL;
+
+       if( Node == &Disk->rootNode )
+               return ;        
+
+       Mutex_Acquire(&Disk->lNodeCache);
+       Node->ReferenceCount --;
+       for(cnode = Disk->NodeCache; cnode; prev = cnode, cnode = cnode->Next )
+       {
+               if(Node == &cnode->Node) {
+                       if(prev)
+                               prev->Next = cnode->Next;
+                       else
+                               Disk->NodeCache = cnode->Next;
+                       break;
+               }
+       }
+       Mutex_Release(&Disk->lNodeCache);
+       if( !cnode ) {
+               // Not here?
+               return ;
+       }
+       
+       // Already out of the list :)
+       free(cnode->Node.Data);
+       free(cnode);
+}
+
+void FAT_int_ClearNodeCache(tFAT_VolInfo *Disk)
+{
+       // TODO: In theory when this is called, all handles will be closed
+}

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