X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Modules%2FFilesystems%2FFAT%2Ffat.c;h=97044ebbb4a28657c90ca86ce145a94d43aac1f9;hb=349e14ded0e8bf502cc9f672f3c6e2c6ec5f6fa1;hp=bfd4a6a664b2b3e2a1b9863bd743d8b5849f2aa6;hpb=5dedb7c02fe26f7a2ac05203af7d22dc01932ab7;p=tpg%2Facess2.git diff --git a/Modules/Filesystems/FAT/fat.c b/Modules/Filesystems/FAT/fat.c index bfd4a6a6..97044ebb 100644 --- a/Modules/Filesystems/FAT/fat.c +++ b/Modules/Filesystems/FAT/fat.c @@ -1,6 +1,17 @@ /* * Acess 2 * FAT12/16/32 Driver Version (Incl LFN) + * + * NOTE: This driver will only support _reading_ long file names, not + * writing. I don't even know why I'm adding write-support. FAT sucks. + * + * Known Bugs: + * - LFN Is buggy in FAT_ReadDir + * + * Notes: + * - There's hard-coded 512 byte sectors everywhere, that needs to be + * cleaned. + * - Thread safety is out the window with the write and LFN code */ /** * \todo Implement changing of the parent directory when a file is written to @@ -9,59 +20,81 @@ #define DEBUG 0 #define VERBOSE 1 +#define CACHE_FAT 0 //!< Caches the FAT in memory +#define USE_LFN 1 //!< Enables the use of Long File Names +#define SUPPORT_WRITE 0 //!< Enables write support + #include #include #include #include "fs_fat.h" -#define CACHE_FAT 1 //!< Caches the FAT in memory -#define USE_LFN 1 //!< Enables the use of Long File Names - +#define FAT_FLAG_DIRTY 0x10000 +#define FAT_FLAG_DELETE 0x20000 // === TYPES === #if USE_LFN -typedef struct s_lfncache +/** + * \brief Long-Filename cache entry + */ +typedef struct sFAT_LFNCacheEnt +{ + int ID; + // TODO: Handle UTF16 names correctly + char Data[256]; +} tFAT_LFNCacheEnt; +/** + * \brief Long-Filename cache + */ +typedef struct sFAT_LFNCache { - Uint Inode; - tFAT_VolInfo *Disk; - int id; - char Name[256]; - struct s_lfncache *Next; -} t_lfncache; + int NumEntries; + tFAT_LFNCacheEnt Entries[]; +} tFAT_LFNCache; #endif // === PROTOTYPES === +// --- Driver Core int FAT_Install(char **Arguments); -tVFS_Node *FAT_InitDevice(char *device, char **options); +tVFS_Node *FAT_InitDevice(const char *device, const char **options); void FAT_Unmount(tVFS_Node *Node); - +// --- Helpers + int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster); Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster); +#if SUPPORT_WRITE Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous); - +Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster); +#endif void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer); -void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer); - +// --- File IO Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +#if SUPPORT_WRITE +void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer); Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +#endif +// --- Directory IO char *FAT_ReadDir(tVFS_Node *Node, int ID); -tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *Name); - int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags); - int FAT_Relink(tVFS_Node *node, char *OldName, char *NewName); +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_Relink(tVFS_Node *node, const char *OldName, const char *NewName); +#endif void FAT_CloseFile(tVFS_Node *node); +// === Options === + int giFAT_MaxCachedClusters = 1024*512/4; + // === SEMI-GLOBALS === MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL); tFAT_VolInfo gFAT_Disks[8]; int giFAT_PartCount = 0; -#if USE_LFN -t_lfncache *fat_lfncache; -#endif -tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, NULL}; +tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL}; // === CODE === /** * \fn int FAT_Install(char **Arguments) - * \brief + * \brief Install the FAT Driver */ int FAT_Install(char **Arguments) { @@ -70,10 +103,9 @@ int FAT_Install(char **Arguments) } /** - * \fn tVFS_Node *FAT_InitDevice(char *Device, char **Options) * \brief Reads the boot sector of a disk and prepares the structures for it */ -tVFS_Node *FAT_InitDevice(char *Device, char **Options) +tVFS_Node *FAT_InitDevice(const char *Device, const char **Options) { fat_bootsect *bs; int i; @@ -84,7 +116,7 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) // Temporary Pointer bs = &diskInfo->bootsect; - //Open device and read boot sector + // Open device and read boot sector diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); if(diskInfo->fileHandle == -1) { Log_Notice("FAT", "Unable to open device '%s'", Device); @@ -94,7 +126,7 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs); if(bs->bps == 0 || bs->spc == 0) { - Log_Notice("FAT", "Error in FAT Boot Sector\n"); + Log_Notice("FAT", "Error in FAT Boot Sector"); return NULL; } @@ -102,11 +134,15 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) // - From Microsoft FAT Specifcation RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps; - if(bs->fatSz16 != 0) FATSz = bs->fatSz16; - else FATSz = bs->spec.fat32.fatSz32; + if(bs->fatSz16 != 0) + FATSz = bs->fatSz16; + else + FATSz = bs->spec.fat32.fatSz32; - if(bs->totalSect16 != 0) TotSec = bs->totalSect16; - else TotSec = bs->totalSect32; + if(bs->totalSect16 != 0) + TotSec = bs->totalSect16; + else + TotSec = bs->totalSect32; diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc; @@ -165,53 +201,54 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) //Allow for Caching the FAT #if CACHE_FAT + if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters ) { - Uint32 Ofs; - diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount); - if(diskInfo->FATCache == NULL) { - Log_Warning("FAT", "Heap Exhausted\n"); - return NULL; - } - Ofs = bs->resvSectCount*512; - if(diskInfo->type == FAT12) - { - Uint32 val; - int j; - char buf[1536]; - for(i = 0; i < diskInfo->ClusterCount/2; i++) { - j = i & 511; //%512 - if( j == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf); - Ofs += 3*512; + Uint32 Ofs; + diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount); + if(diskInfo->FATCache == NULL) { + Log_Warning("FAT", "Heap Exhausted"); + return NULL; + } + Ofs = bs->resvSectCount*512; + if(diskInfo->type == FAT12) + { + Uint32 val; + int j; + char buf[1536]; + for(i = 0; i < diskInfo->ClusterCount/2; i++) { + j = i & 511; //%512 + if( j == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf); + Ofs += 3*512; + } + val = *((int*)(buf+j*3)); + diskInfo->FATCache[i*2] = val & 0xFFF; + diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF; } - val = *((int*)(buf+j*3)); - diskInfo->FATCache[i*2] = val & 0xFFF; - diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF; } - } - else if(diskInfo->type == FAT16) - { - Uint16 buf[256]; - for(i=0;iClusterCount;i++) { - if( (i & 255) == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); - Ofs += 512; + else if(diskInfo->type == FAT16) + { + Uint16 buf[256]; + for(i=0;iClusterCount;i++) { + if( (i & 255) == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); + Ofs += 512; + } + diskInfo->FATCache[i] = buf[i&255]; } - diskInfo->FATCache[i] = buf[i&255]; } - } - else if(diskInfo->type == FAT32) - { - Uint32 buf[128]; - for(i=0;iClusterCount;i++) { - if( (i & 127) == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); - Ofs += 512; + else if(diskInfo->type == FAT32) + { + Uint32 buf[128]; + for(i=0;iClusterCount;i++) { + if( (i & 127) == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); + Ofs += 512; + } + diskInfo->FATCache[i] = buf[i&127]; } - diskInfo->FATCache[i] = buf[i&127]; } - } - LOG("FAT Fully Cached"); + LOG("FAT Fully Cached"); } #endif /*CACHE_FAT*/ @@ -223,10 +260,11 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) // == VFS Interface node = &diskInfo->rootNode; - node->Size = bs->files_in_root; + //node->Size = bs->files_in_root; + node->Size = -1; node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster node->ImplPtr = diskInfo; // Disk info pointer - node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag + node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag node->ReferenceCount = 1; @@ -239,8 +277,13 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) node->Read = node->Write = NULL; node->ReadDir = FAT_ReadDir; node->FindDir = FAT_FindDir; + #if SUPPORT_WRITE node->Relink = FAT_Relink; node->MkNod = FAT_Mknod; + #else + node->Relink = NULL; + node->MkNod = NULL; + #endif //node->Close = FAT_Unmount; giFAT_PartCount ++; @@ -268,6 +311,79 @@ void FAT_Unmount(tVFS_Node *Node) return; } +/** + * \brief Converts an offset in a file into a disk address + * \param Node File (or directory) node + * \param Offset Offset in the file + * \param Addr Return Address + * \param Cluster Set to the current cluster (or the last one if \a Offset + * is past EOC) - Not touched if the node is the root + * directory. + * \return Zero on success, non-zero on error + */ +int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster) +{ + Uint32 cluster; + Uint64 addr; + int skip; + tFAT_VolInfo *disk = Node->ImplPtr; + + ENTER("pNode XOffset", Node, Offset); + + cluster = Node->Inode & 0xFFFFFFF; // Cluster ID + LOG("cluster = 0x%07x", cluster); + + // Do Cluster Skip + // - Pre FAT32 had a reserved area for the root. + if( disk->type == FAT32 || cluster != disk->rootOffset ) + { + skip = Offset / disk->BytesPerCluster; + LOG("skip = %i", skip); + // Skip previous clusters + for(; skip-- ; ) + { + if(Cluster) *Cluster = cluster; + cluster = FAT_int_GetFatValue(disk, cluster); + // Check for end of cluster chain + if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;} + } + if(Cluster) *Cluster = cluster; + } + else { + // Increment by clusters in offset + cluster += Offset / disk->BytesPerCluster; + } + + LOG("cluster = %08x", cluster); + + // Bounds Checking (Used to spot corruption) + if(cluster > disk->ClusterCount + 2) + { + Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)", + cluster, disk->ClusterCount+2); + LEAVE('i', 1); + return 1; + } + + // Compute Offsets + // - Pre FAT32 cluster base (in sectors) + if( cluster == disk->rootOffset && disk->type != FAT32 ) { + addr = disk->bootsect.resvSectCount * disk->bootsect.bps; + addr += cluster * disk->BytesPerCluster; + } + else { + addr = disk->firstDataSect * disk->bootsect.bps; + addr += (cluster - 2) * disk->BytesPerCluster; + } + // In-cluster offset + addr += Offset % disk->BytesPerCluster; + + LOG("addr = 0x%08x", addr); + *Addr = addr; + LEAVE('i', 0); + return 0; +} + /* * ==================== * FAT Manipulation @@ -280,34 +396,41 @@ void FAT_Unmount(tVFS_Node *Node) Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) { Uint32 val = 0; - #if !CACHE_FAT - Uint32 ofs = Disk->bootsect.resvSectCount*512; - #endif + Uint32 ofs; ENTER("pDisk xCluster", Disk, cluster); - LOCK( &Disk->lFAT ); + 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 - 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 - if(Disk->type == FAT12) { - VFS_ReadAt(Disk->fileHandle, ofs+(cluster>>1)*3, 3, &val); - val = (cluster&1 ? val&0xFFF : val>>12); - 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; } #endif /*CACHE_FAT*/ - RELEASE( &Disk->lFAT ); + Mutex_Release( &Disk->lFAT ); LEAVE('x', val); return val; } +#if SUPPORT_WRITE /** * \brief Allocate a new cluster */ @@ -315,75 +438,146 @@ Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) { Uint32 ret = Previous; #if CACHE_FAT - Uint32 eoc; - - LOCK(Disk->lFAT); - for(ret = Previous; ret < Disk->ClusterCount; ret++) + if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) { - if(Disk->FATCache[ret] == 0) - goto append; + Uint32 eoc; + + LOCK(Disk->lFAT); + for(ret = Previous; ret < Disk->ClusterCount; ret++) + { + if(Disk->FATCache[ret] == 0) + goto append; + } + for(ret = 0; ret < Previous; ret++) + { + if(Disk->FATCache[ret] == 0) + goto append; + } + + RELEASE(Disk->lFAT); + return 0; + + append: + switch(Disk->type) + { + case FAT12: eoc = EOC_FAT12; break; + case FAT16: eoc = EOC_FAT16; break; + case FAT32: eoc = EOC_FAT32; break; + default: return 0; + } + + Disk->FATCache[ret] = eoc; + Disk->FATCache[Previous] = ret; + + RELEASE(Disk->lFAT); + return ret; } - for(ret = 0; ret < Previous; ret++) + else { - if(Disk->FATCache[ret] == 0) - goto append; + #endif + Uint32 val; + Uint32 ofs = Disk->bootsect.resvSectCount*512; + Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT"); + return 0; + + switch(Disk->type) + { + case FAT12: + VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + if( Previous & 1 ) { + val &= 0xFFF000; + val |= ret; + } + else { + val &= 0xFFF; + val |= ret<<12; + } + VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + + VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); + if( Cluster & 1 ) { + val &= 0xFFF000; + val |= eoc; + } + else { + val &= 0x000FFF; + val |= eoc<<12; + } + VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); + break; + case FAT16: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); + VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc); + break; + case FAT32: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); + VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc); + break; + } + return ret; + #if CACHE_FAT } - - RELEASE(Disk->lFAT); - return 0; - -append: - switch(Disk->type) + #endif +} + +/** + * \brief Free's a cluster + * \return The original contents of the cluster + */ +Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster) +{ + Uint32 ret; + #if CACHE_FAT + if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) { - case FAT12: eoc = EOC_FAT12; break; - case FAT16: eoc = EOC_FAT16; break; - case FAT32: eoc = EOC_FAT32; break; - default: return 0; - } - - Disk->FATCache[ret] = eoc; - Disk->FATCache[Previous] = ret; - - RELEASE(Disk->lFAT); - return ret; - #else - Uint32 val; - //Uint8 buf[512]; - Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT"); - return 0; - - if(Disk->type == FAT12) { - VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); - if( Previous & 1 ) { - val &= 0xFFF000; - val |= ret; - } - else { - val &= 0xFFF; - val |= ret<<12; - } - VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + LOCK(Disk->lFAT); - VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); - if( Cluster & 1 ) { - val &= 0xFFF000; - val |= eoc; - } - else { - val &= 0x000FFF; - val |= eoc<<12; + ret = Disk->FATCache[Cluster]; + Disk->FATCache[Cluster] = 0; + + RELEASE(Disk->lFAT); + } + else + { + #endif + Uint32 val; + Uint32 ofs = Disk->bootsect.resvSectCount*512; + LOCK(Disk->lFAT); + 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+(Previous>>1)*3, 3, &val); + break; + case FAT16: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); + val = 0; + VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); + break; + case FAT32: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); + val = 0; + VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); + break; } - VFS_WriteAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); - } else if(Disk->type == FAT16) { - VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); - VFS_ReadAt(Disk->fileHandle, ofs+Cluster*2, 2, &eoc); - } else { - VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); - VFS_ReadAt(Disk->fileHandle, ofs+Cluster*4, 4, &eoc); + RELEASE(Disk->lFAT); + #if CACHE_FAT } - return ret; #endif + 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 /* * ==================== @@ -392,11 +586,13 @@ append: */ /** * \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); - //Log("Cluster = %i (0x%x)", Cluster, Cluster); VFS_ReadAt( Disk->fileHandle, (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) @@ -407,22 +603,6 @@ void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *B LEAVE('-'); } -/** - * \brief Write a cluster to disk - */ -void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) -{ - ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer); - VFS_ReadAt( - Disk->fileHandle, - (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) - * Disk->bootsect.bps, - Disk->BytesPerCluster, - Buffer - ); - LEAVE('-'); -} - /* ==================== * File IO * ==================== @@ -431,112 +611,147 @@ void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) * \brief Reads data from a specified file */ -Uint64 FAT_Read(tVFS_Node *Node, Uint64 offset, Uint64 length, void *buffer) +Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) { int preSkip, count; int i, cluster, pos; - int bpc; - void *tmpBuf; tFAT_VolInfo *disk = Node->ImplPtr; + char tmpBuf[disk->BytesPerCluster]; + int bpc = disk->BytesPerCluster; - ENTER("pNode Xoffset Xlength pbuffer", Node, offset, length, buffer); - - // Calculate and Allocate Bytes Per Cluster - bpc = disk->BytesPerCluster; - tmpBuf = (void*) malloc(bpc); - if( !tmpBuf ) return 0; - - // Cluster is stored in Inode Field - cluster = Node->Inode & 0xFFFFFFFF; + ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer); // Sanity Check offset - if(offset > Node->Size) { - //LOG("Reading past EOF (%i > %i)", offset, node->Size); + if(Offset > Node->Size) { + LOG("Reading past EOF (%i > %i)", Offset, Node->Size); LEAVE('i', 0); return 0; } + + // Cluster is stored in the low 32-bits of the Inode field + cluster = Node->Inode & 0xFFFFFFFF; + // Clamp Size - if(offset + length > Node->Size) { - //LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli", - // offset, length, node->Size, node->Size - offset); - length = Node->Size - offset; + if(Offset >= Node->Size || Offset + Length > Node->Size) { + LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli", + Offset, Length, Node->Size, Node->Size - Offset); + Length = Node->Size - Offset; } - // Single Cluster including offset - if(length + offset < bpc) + // Reading from within the first cluster only? + if((int)Offset + (int)Length < bpc) { + LOG("First cluster only"); FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy( buffer, (void*)( tmpBuf + offset%bpc ), length ); - free(tmpBuf); + memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length ); + #if DEBUG + //Debug_HexDump("FAT_Read", Buffer, Length); + #endif LEAVE('i', 1); - return length; + return Length; } - preSkip = offset / bpc; - - //Skip previous clusters - for(i=preSkip;i--;) { + // Skip previous clusters + preSkip = Offset / bpc; + for(i = preSkip; i--; ) { cluster = FAT_int_GetFatValue(disk, cluster); if(cluster == -1) { - Warning("FAT_Read - Offset is past end of cluster chain mark"); + Log_Warning("FAT", "Offset is past end of cluster chain mark"); LEAVE('i', 0); return 0; } } // Get Count of Clusters to read - count = ((offset%bpc+length) / bpc) + 1; + count = ((Offset%bpc + Length) / bpc) + 1; // Get buffer Position after 1st cluster - pos = bpc - offset%bpc; - - // Read 1st Cluster - FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy( - buffer, - (void*)( tmpBuf + (bpc-pos) ), - (pos < length ? pos : length) - ); + pos = bpc - Offset%bpc; - if (count == 1) { - free(tmpBuf); - LEAVE('i', 1); - return length; + // Read 1st Cluster (performs alignment for us) + if( pos == bpc && (int)Length >= bpc ) { + FAT_int_ReadCluster(disk, cluster, bpc, Buffer); + } + else { + FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); + memcpy( + Buffer, + (void*)( tmpBuf + (bpc-pos) ), + (pos < (int)Length ? (Uint)pos : Length) + ); } - cluster = FAT_int_GetFatValue(disk, cluster); + // Simple return + if( count == 1 ) { + #if DEBUG + //Debug_HexDump("FAT_Read", Buffer, Length); + #endif + LEAVE('i', 1); + return Length; + } #if DEBUG - LOG("pos=%i\n", pos); - LOG("Reading the rest of the clusters\n"); + LOG("pos = %i", pos); + LOG("Reading the rest of the clusters"); #endif - - //Read the rest of the cluster data + // Read the rest of the cluster data for( i = 1; i < count-1; i++ ) { - FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy((void*)(buffer+pos), tmpBuf, bpc); - pos += bpc; + // Get next cluster in the chain cluster = FAT_int_GetFatValue(disk, cluster); if(cluster == -1) { - Warning("FAT_Read - Read past End of Cluster Chain"); - free(tmpBuf); + Log_Warning("FAT", "FAT_Read: Read past End of Cluster Chain"); LEAVE('i', 0); return 0; } + // Read cluster + FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos)); + pos += bpc; } - FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy((void*)(buffer+pos), tmpBuf, length-pos); + // Get next cluster in the chain + cluster = FAT_int_GetFatValue(disk, cluster); + if(cluster == -1) { + Log_Warning("FAT", "FAT_Read: Read past End of Cluster Chain"); + LEAVE('i', 0); + return 0; + } + + // Read final cluster + if( (int)Length - pos == bpc ) + { + FAT_int_ReadCluster( disk, cluster, bpc, (void*)(Buffer+pos) ); + } + else { + FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf ); + memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos ); + } #if DEBUG - LOG("Free tmpBuf(0x%x) and Return\n", tmpBuf); + LOG("Free tmpBuf(0x%x) and Return", tmpBuf); + //Debug_HexDump("FAT_Read", Buffer, Length); #endif - free(tmpBuf); - LEAVE('X', length); - return length; + LEAVE('X', Length); + return Length; +} + +#if SUPPORT_WRITE +/** + * \brief Write a cluster to disk + */ +void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) +{ + ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer); + VFS_ReadAt( + Disk->fileHandle, + (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) + * Disk->bootsect.bps, + Disk->BytesPerCluster, + Buffer + ); + LEAVE('-'); } /** @@ -549,7 +764,7 @@ Uint64 FAT_Read(tVFS_Node *Node, Uint64 offset, Uint64 length, void *buffer) Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) { tFAT_VolInfo *disk = Node->ImplPtr; - void *tmpBuf; + char tmpBuf[disk->BytesPerCluster]; int remLength = Length; Uint32 cluster, tmpCluster; int bNewCluster = 0; @@ -577,29 +792,24 @@ Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) if( Offset + Length < disk->BytesPerCluster ) { - tmpBuf = malloc( disk->BytesPerCluster ); + char tmpBuf[disk->BytesPerCluster]; // Read-Modify-Write FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); memcpy( tmpBuf + Offset, Buffer, Length ); FAT_int_WriteCluster( disk, cluster, tmpBuf ); - free(tmpBuf); return Length; } // Clean up changes within a cluster if( Offset ) - { - tmpBuf = malloc( disk->BytesPerCluster ); - + { // Read-Modify-Write FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset ); FAT_int_WriteCluster( disk, cluster, tmpBuf ); - free(tmpBuf); - remLength -= disk->BytesPerCluster - Offset; Buffer += disk->BytesPerCluster - Offset; @@ -643,87 +853,109 @@ Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) return Length; } +#endif /* ==================== * File Names & Nodes * ==================== */ /** - * \fn void FAT_int_ProperFilename(char *dest, char *src) * \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, char *src) +void FAT_int_ProperFilename(char *dest, const char *src) { - int a, b; + int inpos, outpos; - for( a = 0; a < 8; a++) { - if(src[a] == ' ') break; - dest[a] = src[a]; + // Name + outpos = 0; + for( inpos = 0; inpos < 8; inpos++ ) { + if(src[inpos] == ' ') break; + dest[outpos++] = src[inpos]; } - b = a; - a = 8; + inpos = 8; + // Check for empty extensions if(src[8] != ' ') - dest[b++] = '.'; - for( ; a < 11; a++, b++) { - if(src[a] == ' ') break; - dest[b] = src[a]; + { + dest[outpos++] = '.'; + for( ; inpos < 11; inpos++) { + if(src[inpos] == ' ') break; + dest[outpos++] = src[inpos]; + } } - dest[b] = '\0'; - #if DEBUG + dest[outpos++] = '\0'; + //LOG("dest='%s'", dest); - #endif } /** - * \fn char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) + * \fn char *FAT_int_CreateName(fat_filetable *ft, char *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(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) +char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName) { char *ret; - int len; + 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') { - len = strlen(LongFileName); - ret = malloc(len+1); - strcpy(ret, LongFileName); + ret = strdup(LongFileName); } else { #endif ret = (char*) malloc(13); - memset(ret, 13, '\0'); + 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; } /** - * \fn tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) * \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 + * \param Pos Position in the parent of the new node */ -tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) +tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos) { - tVFS_Node node = {0}; + tVFS_Node node; tVFS_Node *ret; - tFAT_VolInfo *disk = parent->ImplPtr; + tFAT_VolInfo *disk = Parent->ImplPtr; + + ENTER("pParent pFT", Parent, Entry); + LOG("disk = %p", disk); - ENTER("pParent pFT sLongFileName", parent, ft, LongFileName); + memset(&node, 0, sizeof(tVFS_Node)); // Set Other Data - node.Inode = ft->cluster | (ft->clusterHi<<16); - node.Size = ft->size; - LOG("ft->size = %i", ft->size); - node.ImplPtr = parent->ImplPtr; + // 0-27: Cluster, 32-59: Parent Cluster + node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32); + LOG("node.Inode = %llx", node.Inode); + // Position in parent directory + node.ImplInt = Pos & 0xFFFF; + // 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(ft->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY; - if(ft->attrib & ATTR_READONLY) { + 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 } @@ -731,118 +963,239 @@ tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFi node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX } + // Create timestamps node.ATime = timestamp(0,0,0, - ((ft->adate&0x1F)-1), //Days - ((ft->adate&0x1E0)-1), //Months - 1980+((ft->adate&0xFF00)>>8)); //Years + ((Entry->adate&0x1F) - 1), // Days + ((Entry->adate&0x1E0) - 1), // Months + 1980+((Entry->adate&0xFF00)>>8) // Years + ); - node.CTime = ft->ctimems * 10; //Miliseconds + node.CTime = Entry->ctimems * 10; // Miliseconds node.CTime += timestamp( - (ft->ctime&0x1F)<<1, //Seconds - ((ft->ctime&0x3F0)>>5), //Minutes - ((ft->ctime&0xF800)>>11), //Hours - ((ft->cdate&0x1F)-1), //Days - ((ft->cdate&0x1E0)-1), //Months - 1980+((ft->cdate&0xFF00)>>8)); //Years + ((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( - (ft->mtime&0x1F)<<1, //Seconds - ((ft->mtime&0x3F0)>>5), //Minuites - ((ft->mtime&0xF800)>>11), //Hours - ((ft->mdate&0x1F)-1), //Days - ((ft->mdate&0x1E0)-1), //Months - 1980+((ft->mdate&0xFF00)>>8)); //Years - + ((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.ReadDir = FAT_ReadDir; node.FindDir = FAT_FindDir; + #if SUPPORT_WRITE node.MkNod = FAT_Mknod; + node.Relink = FAT_Relink; + #endif node.Size = -1; - } else { + } + else { node.Read = FAT_Read; + #if SUPPORT_WRITE node.Write = FAT_Write; + #endif } node.Close = FAT_CloseFile; - node.Relink = FAT_Relink; ret = Inode_CacheNode(disk->inodeHandle, &node); LEAVE('p', ret); return ret; } -#if USE_LFN +/* + * ==================== + * Directory IO + * ==================== + */ + /** - \fn char *FAT_int_GetLFN(tVFS_Node *node) - \brief Return pointer to LFN cache entry + * \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 */ -char *FAT_int_GetLFN(tVFS_Node *node) +int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer) { - t_lfncache *tmp; - tmp = fat_lfncache; - while(tmp) + 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)) { - if(tmp->Inode == node->Inode && tmp->Disk == node->ImplPtr) - return tmp->Name; - tmp = tmp->Next; + LEAVE('i', 1); + return 1; } - tmp = malloc(sizeof(t_lfncache)); - tmp->Inode = node->Inode; - tmp->Disk = node->ImplPtr; - memset(tmp->Name, 0, 256); - tmp->Next = fat_lfncache; - fat_lfncache = tmp; + LOG("addr = 0x%llx", addr); + // Read Sector + if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512) + { + LEAVE('i', 1); + return 1; + } - return tmp->Name; + LEAVE('i', 0); + return 0; } +#if SUPPORT_WRITE /** - \fn void FAT_int_DelLFN(tVFS_Node *node) - \brief Delete a LFN cache entry -*/ -void FAT_int_DelLFN(tVFS_Node *node) + * \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) { - t_lfncache *tmp; + Uint64 addr = 0; + int tmp; + Uint32 cluster = 0; + tFAT_VolInfo *disk = Node->ImplPtr; - if(!fat_lfncache) return; + ENTER("pNode iID pEntry", Node, ID, Entry); - if(!fat_lfncache->Next) + tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster); + if( tmp ) { - tmp = fat_lfncache; - fat_lfncache = tmp->Next; - free(tmp); - return; + //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); } - tmp = fat_lfncache; - while(tmp && tmp->Next) + + + 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 char *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 + */ +char *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(tmp->Inode == node->Inode && tmp->Disk == node->ImplPtr) - { - free(tmp->Next); - tmp->Next = tmp->Next->Next; - return; + 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; } - tmp = tmp->Next; + //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; } -#endif -/* ==================== - * Directory IO - * ==================== +/** + * \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 -**/ + * \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=32, 16 per sector - int a=0; - tFAT_VolInfo *disk = Node->ImplPtr; - Uint32 cluster, offset; - int preSkip; + fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector + int a = 0; char *ret; #if USE_LFN char *lfn = NULL; @@ -850,58 +1203,19 @@ char *FAT_ReadDir(tVFS_Node *Node, int ID) ENTER("pNode iID", Node, ID); - // Get Byte Offset and skip - offset = ID * sizeof(fat_filetable); - preSkip = offset / (512 * disk->bootsect.spc); - LOG("disk->bootsect.spc = %i", disk->bootsect.spc); - LOG("Node->size = %i", Node->Size); - cluster = Node->Inode & 0xFFFFFFFF; // Cluster ID - - // Do Cluster Skip - // - Pre FAT32 had a reserved area for the root. - if( disk->type == FAT32 || cluster != disk->rootOffset ) - { - //Skip previous clusters - for(a=preSkip;a--;) { - cluster = FAT_int_GetFatValue(disk, cluster); - // Check for end of cluster chain - if(cluster == -1) { LEAVE('n'); return NULL;} - } - } - - // Bounds Checking (Used to spot heap overflows) - if(cluster > disk->ClusterCount + 2) + if(FAT_int_ReadDirSector(Node, ID/16, fileinfo)) { - Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)", - cluster, disk->ClusterCount+2); + LOG("End of chain, end of dir"); LEAVE('n'); return NULL; } - LOG("cluster=0x%x, ID=%i", cluster, ID); - - // Compute Offsets - // - Pre FAT32 cluster base (in sectors) - if( cluster == disk->rootOffset && disk->type != FAT32 ) - offset = disk->bootsect.resvSectCount + cluster*disk->bootsect.spc; - else - { // FAT32 cluster base (in sectors) - offset = disk->firstDataSect; - offset += (cluster - 2) * disk->bootsect.spc; - } - // Sector in cluster - if(disk->bootsect.spc != 1) - offset += (ID / 16) % disk->bootsect.spc; // Offset in sector a = ID % 16; - LOG("offset=%i, a=%i", offset, a); + LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]); - // Read Sector - VFS_ReadAt(disk->fileHandle, offset*512, 512, fileinfo); // Read Dir Data - - LOG("name[0] = 0x%x", (Uint8)fileinfo[a].name[0]); - //Check if this is the last entry + // Check if this is the last entry if( fileinfo[a].name[0] == '\0' ) { Node->Size = ID; LOG("End of list"); @@ -912,64 +1226,78 @@ char *FAT_ReadDir(tVFS_Node *Node, int ID) // 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 - lfn = FAT_int_GetLFN(Node); if(fileinfo[a].attrib == ATTR_LFN) { fat_longfilename *lfnInfo; - int len; 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) ); + + // Bit 6 indicates the start of an entry if(lfnInfo->id & 0x40) memset(lfn, 0, 256); - // Get the current length - len = strlen(lfn); - // Sanity Check (FAT implementations should not allow >255 bytes) - if(len + 13 > 255) return VFS_SKIP; - // Rebase all bytes - for(a=len+1;a--;) lfn[a+13] = lfn[a]; + a = ((lfnInfo->id & 0x3F) - 1) * 13; + //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a); + + // Sanity Check (FAT implementations should not allow >255 character names) + if(a > 255) return VFS_SKIP; // Append new bytes - lfn[ 0] = lfnInfo->name1[0]; lfn[ 1] = lfnInfo->name1[1]; - lfn[ 2] = lfnInfo->name1[2]; lfn[ 3] = lfnInfo->name1[3]; - lfn[ 4] = lfnInfo->name1[4]; - lfn[ 5] = lfnInfo->name2[0]; lfn[ 6] = lfnInfo->name2[1]; - lfn[ 7] = lfnInfo->name2[2]; lfn[ 8] = lfnInfo->name2[3]; - lfn[ 9] = lfnInfo->name2[4]; lfn[10] = lfnInfo->name2[5]; - lfn[11] = lfnInfo->name3[0]; lfn[12] = lfnInfo->name3[1]; + lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1]; + lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3]; + lfn[a+ 4] = lfnInfo->name1[4]; + lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1]; + lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3]; + lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5]; + lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1]; + 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 + // Check if it is a volume entry if(fileinfo[a].attrib & 0x08) { LEAVE('p', VFS_SKIP); return VFS_SKIP; } - // Ignore . and .. - if(fileinfo[a].name[0] == '.') { + // 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'\n", + 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 - //node = FAT_int_CreateNode(Node, &fileinfo[a], lfn); - ret = FAT_int_CreateName(Node, &fileinfo[a], lfn); - lfn[0] = '\0'; + lfn = FAT_int_GetLFN(Node, ID); + //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn); + ret = FAT_int_CreateName(&fileinfo[a], lfn); #else - //node = FAT_int_CreateNode(Node, &fileinfo[a], NULL); - ret = FAT_int_CreateName(Node, &fileinfo[a], NULL); + ret = FAT_int_CreateName(&fileinfo[a], NULL); #endif LEAVE('s', ret); @@ -980,58 +1308,46 @@ char *FAT_ReadDir(tVFS_Node *Node, int ID) * \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, char *name) +tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name) { fat_filetable fileinfo[16]; - char tmpName[11]; + char tmpName[13]; #if USE_LFN fat_longfilename *lfnInfo; - char *lfn = NULL; + char lfn[256]; int lfnPos=255, lfnId = -1; #endif - int i=0; + int i; tVFS_Node *tmpNode; - Uint64 diskOffset; tFAT_VolInfo *disk = Node->ImplPtr; - Uint32 dirCluster; Uint32 cluster; - ENTER("pNode sname", Node, name); + ENTER("pNode sname", Node, Name); // Fast Returns - if(!name || name[0] == '\0') { + if(!Name || Name[0] == '\0') { LEAVE('n'); return NULL; } - #if USE_LFN - lfn = FAT_int_GetLFN(Node); - #endif - - dirCluster = Node->Inode & 0xFFFFFFFF; - // Seek to Directory - if( dirCluster == disk->rootOffset && disk->type != FAT32 ) - diskOffset = (disk->bootsect.resvSectCount+dirCluster*disk->bootsect.spc) << 9; - else - diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc) << 9; - - for(;;i++) + for( i = 0; ; i++ ) { - // Load sector if((i & 0xF) == 0) { - //Log("FAT_FindDir: diskOffset = 0x%x", diskOffset); - VFS_ReadAt(disk->fileHandle, diskOffset, 512, fileinfo); - diskOffset += 512; + if(FAT_int_ReadDirSector(Node, i/16, fileinfo)) + { + LEAVE('n'); + return NULL; + } } //Check if the files are free - if(fileinfo[i&0xF].name[0] == '\0') break; //Free and last - if(fileinfo[i&0xF].name[0] == '\xE5') goto loadCluster; //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(fileinfo[i & 0xF].attrib == ATTR_LFN) { lfnInfo = (fat_longfilename *) &fileinfo[i&0xF]; if(lfnInfo->id & 0x40) { @@ -1055,54 +1371,93 @@ tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *name) { // 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); - //Only Long name is case sensitive, 8.3 is not + // Only the long name is case sensitive, 8.3 is not #if USE_LFN - if(strucmp(tmpName, name) == 0 || strcmp(lfn, name) == 0) { + if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0) #else - if(strucmp(tmpName, name) == 0) { + if(strucmp(tmpName, Name) == 0) #endif + { cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16); tmpNode = Inode_GetCache(disk->inodeHandle, cluster); if(tmpNode == NULL) // Node is not cached { - #if USE_LFN - tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], lfn); - #else - tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], NULL); - #endif + tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i); } - #if USE_LFN - lfn[0] = '\0'; - #endif LEAVE('p', tmpNode); return tmpNode; } #if USE_LFN } #endif + } + + LEAVE('n'); + return NULL; +} + +tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode) +{ + tFAT_VolInfo *disk = Root->ImplPtr; + int ents_per_sector = 512 / sizeof(fat_filetable); + fat_filetable fileinfo[ents_per_sector]; + int sector = 0, i; + tVFS_Node stub_node; + + ENTER("pRoot XInode", Root, Inode); + + stub_node.ImplPtr = disk; + stub_node.Size = -1; + stub_node.Inode = Inode >> 32; + + for( i = 0; ; i ++ ) + { + if( i == 0 || i == ents_per_sector ) + { + if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo)) + { + LOG("ReadDirSector failed"); + LEAVE('n'); + return NULL; + } + 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 - loadCluster: - //Load Next cluster? - if( ((i+1) >> 4) % disk->bootsect.spc == 0 && ((i+1) & 0xF) == 0) + if(fileinfo[i].attrib == ATTR_LFN) continue; + + LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster); + #if DEBUG { - if( dirCluster == disk->rootOffset && disk->type != FAT32 ) - continue; - dirCluster = FAT_int_GetFatValue(disk, dirCluster); - if(dirCluster == -1) break; - diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc)*512; + char tmpName[13]; + FAT_int_ProperFilename(tmpName, fileinfo[i].name); + LOG("tmpName = '%s'", tmpName); } - } + #endif + + if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue; + if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue; + + LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i)); + } + LOG("sector = %i, i = %i", sector, i); LEAVE('n'); return NULL; } +#if SUPPORT_WRITE /** * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) * \brief Create a new node @@ -1118,8 +1473,38 @@ int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) */ int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) { - return 0; + tVFS_Node *child; + fat_filetable ft = {0}; + int ret; + + child = FAT_FindDir(Node, OldName); + if(!child) return ENOTFOUND; + + // Delete? + if( NewName == NULL ) + { + child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close + + // Delete from the directory + ft.name[0] = '\xE9'; + FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft); + + // Return success + ret = EOK; + } + // Rename + else + { + Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')", + Node, OldName, NewName); + ret = ENOTIMPL; + } + + // Close child + child->Close( child ); + return ret; } +#endif /** * \fn void FAT_CloseFile(tVFS_Node *Node) @@ -1130,13 +1515,38 @@ void FAT_CloseFile(tVFS_Node *Node) tFAT_VolInfo *disk = Node->ImplPtr; if(Node == NULL) return ; - Inode_UncacheNode(disk->inodeHandle, Node->Inode); - #if USE_LFN - // If node has been uncached and is a directory, delete the LFN cache - if( !Inode_GetCache(disk->inodeHandle, Node->Inode) && Node->Flags & VFS_FFLAG_DIRECTORY) - FAT_int_DelLFN(Node); - else // Get Cache references the node, so dereference it - Inode_UncacheNode(disk->inodeHandle, Node->Inode); + #if SUPPORT_WRITE + // Update the node if it's dirty (don't bother if it's marked for + // deletion) + if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) ) + { + tFAT_VolInfo buf[16]; + tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ]; + + FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf); + ft->size = Node->Size; + // TODO: update adate, mtime, mdate + FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft); + + Node->ImplInt &= ~FAT_FLAG_DIRTY; + } #endif + + // TODO: Make this more thread safe somehow, probably by moving the + // Inode_UncacheNode higher up and saving the cluster value somewhere + if( Node->ReferenceCount == 1 ) + { + #if SUPPORT_WRITE + // Delete File + if( Node->ImplInt & FAT_FLAG_DELETE ) { + // Since the node is marked, we only need to remove it's data + Uint32 cluster = Node->Inode & 0xFFFFFFFF; + while( cluster != -1 ) + cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster); + } + #endif + } + + Inode_UncacheNode(disk->inodeHandle, Node->Inode); return ; }