#
#
-OBJ = fat.o
+OBJ = fat.o dir.o fatio.o nodecache.o
NAME = FAT
-include ../Makefile.tpl
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
* \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
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
.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
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
\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
\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
// 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
*/\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
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
\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
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
}\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
* \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
}\r
\r
// Finish off\r
- tmpBuf = malloc( disk->BytesPerCluster );\r
if( bNewCluster )\r
memset(tmpBuf, 0, disk->BytesPerCluster);\r
else\r
}\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
// 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
#endif\r
}\r
\r
- Inode_UncacheNode(disk->inodeHandle, Node->Inode);\r
+ FAT_int_DerefNode(Node);\r
return ;\r
}\r
--- /dev/null
+/*
+ * 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
#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
* \}\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
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
--- /dev/null
+/*
+ * 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
+}