X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Modules%2FFilesystems%2FFAT%2Ffat.c;h=9a3c79e8519f7ad7b3f9b2bfa4cd47cfd339d74e;hb=48c3c3735e3445cb2f182a12be4b24507b4a995a;hp=c444fa380f34dc4998191e3dc006964f5fb1fe1a;hpb=4ab9a574c9301d9590c91209f62c348e1d8c8883;p=tpg%2Facess2.git diff --git a/Modules/Filesystems/FAT/fat.c b/Modules/Filesystems/FAT/fat.c index c444fa38..9a3c79e8 100644 --- a/Modules/Filesystems/FAT/fat.c +++ b/Modules/Filesystems/FAT/fat.c @@ -2,6 +2,10 @@ * Acess 2 * FAT12/16/32 Driver Version (Incl LFN) */ +/** + * \todo Implement changing of the parent directory when a file is written to + * \todo Implement file creation / deletion + */ #define DEBUG 0 #define VERBOSE 1 @@ -13,6 +17,8 @@ #include #include "fs_fat.h" +#define FAT_FLAG_DIRTY 0x10000 +#define FAT_FLAG_DELETE 0x20000 // === TYPES === #if USE_LFN @@ -83,14 +89,14 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) //Open device and read boot sector diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); if(diskInfo->fileHandle == -1) { - Log_Warning("FAT", "Unable to open device '%s'", Device); + Log_Notice("FAT", "Unable to open device '%s'", Device); return NULL; } VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs); if(bs->bps == 0 || bs->spc == 0) { - Log_Warning("FAT", "Error in FAT Boot Sector\n"); + Log_Notice("FAT", "Error in FAT Boot Sector\n"); return NULL; } @@ -136,7 +142,7 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) sSize = "GiB"; iSize >>= 20; } - Log_Log("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize); + Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize); } #endif @@ -222,7 +228,7 @@ tVFS_Node *FAT_InitDevice(char *Device, char **Options) node->Size = bs->files_in_root; 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; @@ -264,8 +270,73 @@ 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 & 0xFFFFFFFF; // Cluster ID + + // Do Cluster Skip + // - Pre FAT32 had a reserved area for the root. + if( disk->type == FAT32 || cluster != disk->rootOffset ) + { + skip = Offset / disk->BytesPerCluster; + // Skip previous clusters + for(; skip-- ; ) + { + if(Cluster) *Cluster = cluster; + cluster = FAT_int_GetFatValue(disk, cluster); + // Check for end of cluster chain + if(cluster == -1) { LEAVE('i', 1); return 1;} + } + if(Cluster) *Cluster = 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; + addr += (cluster - 2) * disk->BytesPerCluster; + } + addr += Offset % disk->BytesPerCluster; + + *Addr = addr; + LEAVE('i', 0); + return 0; +} + /* - * === FILE IO === + * ==================== + * FAT Manipulation + * ==================== */ /** * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) @@ -346,7 +417,9 @@ append: Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT"); return 0; - if(Disk->type == FAT12) { + switch(Disk->type) + { + case FAT12: VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); if( Previous & 1 ) { val &= 0xFFF000; @@ -358,7 +431,7 @@ append: } VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); - VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); + VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); if( Cluster & 1 ) { val &= 0xFFF000; val |= eoc; @@ -367,18 +440,76 @@ append: val &= 0x000FFF; val |= eoc<<12; } - VFS_WriteAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); - } else if(Disk->type == FAT16) { + VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); + break; + case FAT16: VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); - VFS_ReadAt(Disk->fileHandle, ofs+Cluster*2, 2, &eoc); - } else { + VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc); + break; + case FAT32: VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); - VFS_ReadAt(Disk->fileHandle, ofs+Cluster*4, 4, &eoc); + VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc); + break; } return ret; #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 + LOCK(Disk->lFAT); + + ret = Disk->FATCache[Cluster]; + Disk->FATCache[Cluster] = 0; + + RELEASE(Disk->lFAT); + #else + Uint32 val; + 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; + } + RELEASE(Disk->lFAT); + #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; +} + +/* + * ==================== + * Cluster IO + * ==================== + */ /** * \brief Read a cluster */ @@ -412,6 +543,10 @@ void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) LEAVE('-'); } +/* ==================== + * File IO + * ==================== + */ /** * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) * \brief Reads data from a specified file @@ -457,13 +592,22 @@ Uint64 FAT_Read(tVFS_Node *Node, Uint64 offset, Uint64 length, void *buffer) return length; } + #if 0 + if( FAT_int_GetAddress(Node, offset, &addr) ) + { + Log_Warning("FAT", "Offset is past end of cluster chain mark"); + LEAVE('i', 0); + return 0; + } + #endif + preSkip = offset / bpc; //Skip previous clusters 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; } @@ -629,6 +773,10 @@ Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) return Length; } +/* ==================== + * File Names & Nodes + * ==================== + */ /** * \fn void FAT_int_ProperFilename(char *dest, char *src) * \brief Converts a FAT directory entry name into a proper filename @@ -751,10 +899,80 @@ tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFi return ret; } +/* ==================== + * Directory IO + * ==================== + */ + +/** + * \brief Reads a sector from the disk + */ +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) + + if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL)) + { + LEAVE('i', 1); + return 1; + } + + // Read Sector + VFS_ReadAt(disk->fileHandle, addr, 512, Buffer); // Read Dir Data + + LEAVE('i', 0); + return 0; +} + +/** + * \brief Writes an entry to the disk + * \todo Support expanding a directory + * \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; +} + #if USE_LFN +// I should probably more tightly associate the LFN cache with the node +// somehow, maybe by adding a field to tVFS_Node before locking it +// Maybe .Cache or something like that (something that is free'd by the +// Inode_UncacheNode function) + /** - \fn char *FAT_int_GetLFN(tVFS_Node *node) - \brief Return pointer to LFN cache entry + * \fn char *FAT_int_GetLFN(tVFS_Node *node) + * \brief Return pointer to LFN cache entry */ char *FAT_int_GetLFN(tVFS_Node *node) { @@ -778,9 +996,9 @@ char *FAT_int_GetLFN(tVFS_Node *node) } /** - \fn void FAT_int_DelLFN(tVFS_Node *node) - \brief Delete a LFN cache entry -*/ + * \fn void FAT_int_DelLFN(tVFS_Node *node) + * \brief Delete a LFN cache entry + */ void FAT_int_DelLFN(tVFS_Node *node) { t_lfncache *tmp; @@ -809,17 +1027,14 @@ void FAT_int_DelLFN(tVFS_Node *node) #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 + */ 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; char *ret; #if USE_LFN char *lfn = NULL; @@ -827,58 +1042,20 @@ 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, fileinfo)) { - Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)", - cluster, disk->ClusterCount+2); 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); - - // Read Sector - VFS_ReadAt(disk->fileHandle, offset*512, 512, fileinfo); // Read Dir Data + LOG("a = %i", a); 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"); @@ -941,11 +1118,9 @@ char *FAT_ReadDir(tVFS_Node *Node, int ID) 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'; #else - //node = FAT_int_CreateNode(Node, &fileinfo[a], NULL); ret = FAT_int_CreateName(Node, &fileinfo[a], NULL); #endif @@ -963,14 +1138,12 @@ tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *name) char tmpName[11]; #if USE_LFN fat_longfilename *lfnInfo; - char *lfn = NULL; + char lfn[256]; int lfnPos=255, lfnId = -1; #endif int i=0; tVFS_Node *tmpNode; - Uint64 diskOffset; tFAT_VolInfo *disk = Node->ImplPtr; - Uint32 dirCluster; Uint32 cluster; ENTER("pNode sname", Node, name); @@ -981,29 +1154,19 @@ tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *name) 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++) { - // 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, 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; // Free and last + if(fileinfo[i&0xF].name[0] == '\xE5') continue; //Free #if USE_LFN @@ -1063,17 +1226,6 @@ tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *name) #if USE_LFN } #endif - - loadCluster: - //Load Next cluster? - if( ((i+1) >> 4) % disk->bootsect.spc == 0 && ((i+1) & 0xF) == 0) - { - 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; - } } LEAVE('n'); @@ -1095,7 +1247,36 @@ 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; } /** @@ -1107,13 +1288,40 @@ void FAT_CloseFile(tVFS_Node *Node) tFAT_VolInfo *disk = Node->ImplPtr; if(Node == NULL) return ; + // 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; + } + + // 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 ) + { + // Delete LFN Cache + #if USE_LFN + if( Node->Flags & VFS_FFLAG_DIRECTORY) + FAT_int_DelLFN(Node); + #endif + + // 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); + } + } + 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); - #endif return ; }