From 97159caf60a26cff3cc8f52e050a44d2492430f8 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 13 Jul 2012 11:28:39 +0800 Subject: [PATCH] Modules/FAT - Fixed write support - LFN is correct enough for mtools - Name mangling is a little incorrect, but works > A rework to fit the fatgen103 standard is underway - Timestamps are possibly buggy --- KernelLand/Modules/Filesystems/FAT/common.h | 2 + KernelLand/Modules/Filesystems/FAT/dir.c | 105 ++++++++++++++++-- KernelLand/Modules/Filesystems/FAT/fat.c | 70 ++++++++---- KernelLand/Modules/Filesystems/FAT/fatio.c | 30 +++-- .../Modules/Filesystems/FAT/nodecache.c | 69 ++++++++---- 5 files changed, 211 insertions(+), 65 deletions(-) diff --git a/KernelLand/Modules/Filesystems/FAT/common.h b/KernelLand/Modules/Filesystems/FAT/common.h index 5f8539c5..79513a96 100644 --- a/KernelLand/Modules/Filesystems/FAT/common.h +++ b/KernelLand/Modules/Filesystems/FAT/common.h @@ -84,6 +84,8 @@ struct sFAT_CachedNode // --- General Helpers --- extern int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster); +extern tTime FAT_int_GetAcessTimestamp(Uint16 Date, Uint16 Time, Uint8 MS); +extern void FAT_int_GetFATTimestamp(tTime AcessTimestamp, Uint16 *Date, Uint16 *Time, Uint8 *MS); // --- Node Caching --- // NOTE: FAT uses its own node cache that references by cluster (not the inode value that the Inode_* cache uses) diff --git a/KernelLand/Modules/Filesystems/FAT/dir.c b/KernelLand/Modules/Filesystems/FAT/dir.c index 3154b17c..910b337f 100644 --- a/KernelLand/Modules/Filesystems/FAT/dir.c +++ b/KernelLand/Modules/Filesystems/FAT/dir.c @@ -305,7 +305,7 @@ int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable if(fileinfo[i].attrib == ATTR_LFN) continue; - LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster); + LOG("fileinfo[i].cluster = %x:%04x", fileinfo[i].clusterHi, fileinfo[i].cluster); #if DEBUG { char tmpName[13]; @@ -752,10 +752,23 @@ static inline int is_valid_83_char(char ch) if( 'A' <= ch && ch <= 'Z' ) return 1; if( 'a' <= ch && ch <= 'z' ) - return 0; - if( strchr(";+=[]',\"*\\<>/?:| ", ch) ) - return 0; - return 1; + return 1; + if( strchr("$%'-_@~`#!(){}^#&", ch) ) + return 1; + if( ch > 128 ) + return 1; + return 0; +} + +Uint8 FAT_int_UnicodeTo83(Uint32 Input) +{ + Input = toupper(Input); + // Input = unicode_to_oem(Input); + if( Input > 256 ) + Input = '_'; + if(!is_valid_83_char(Input)) + Input = '_'; + return Input; } /** @@ -788,12 +801,24 @@ int FAT_int_IsValid83Filename(const char *Name) } // After the extension must be the end - if( !Name[i+j] ) + if( Name[i+j] ) return 0; return 1; } +Uint8 FAT_int_MakeLFNChecksum(const char *ShortName) +{ + Uint8 ret = 0; + for( int i = 0; i < 11; i++ ) + { + // ret = (ret >>> 1) + ShortName[i] + // where >>> is rotate right + ret = ((ret & 1) ? 0x80 : 0x00) + (ret >> 1) + ShortName[i]; + } + return ret; +} + /** * \brief Create a new name for a file * \note Since FAT doesn't support reference counting, this will cause double-references if @@ -816,6 +841,50 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) } // -- Create filetable entry -- + #if 0 + { + int bDirty = 0; + int inofs = 0; + while( NewName[inofs] && NewName[inofs] == '.' ) + inofs ++, bDirty = 1; + for( int i = 0; i < 8 && NewName[inofs] && NewName[inofs] != '.'; i ++ ) + { + Uint32 cp; + inofs += ReadUTF8(NewName + inofs, &cp); + // Spaces are silently skipped + if(isspace(cp)) { + i --, bDirty = 1; + continue ; + } + ft.name[i] = FAT_int_UnicodeTo83(cp); + if(ft.name[i] != cp) + bDirty = 1; + } + while( NewName[inofs] && NewName[inofs] != '.' ) + inofs ++, bDirty = 1; + for( ; i < 8+3 && NewName[inofs]; i ++ ) + { + Uint32 cp; + inofs += ReadUTF8(NewName + inofs, &cp); + // Spaces are silently skipped + if(isspace(cp)) { + i --, bDirty = 1; + continue ; + } + ft.name[i] = FAT_int_UnicodeTo83(cp); + if(ft.name[i] != cp) + bDirty = 1; + } + if( !NewName[inofs] ) bDirty = 1; + + if( bDirty ) + { + int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName); + lfn[lfnlen] = 0; + nLFNEnt = DivUp(lfnlen, 13); + } + } + #endif int bNeedsLFN = !FAT_int_IsValid83Filename(NewName); if( bNeedsLFN ) { @@ -834,7 +903,7 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) ft.name[i] = toupper(NewName[j]); } ft.name[i++] = '~'; - ft.name[i++] = '0'; + ft.name[i++] = '1'; while(i < 8) ft.name[i++] = ' '; while(NewName[j] && NewName[j] != '.') j ++; for( ; i < 8+3 && NewName[j]; i ++, j ++ ) @@ -917,7 +986,16 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) ft.attrib = 0; if(NewNode->Flags & VFS_FFLAG_DIRECTORY ) ft.attrib |= ATTR_DIRECTORY; - // TODO: Fill in creation/modifcation times + ft.ntres = 0; + FAT_int_GetFATTimestamp(NewNode->CTime, &ft.cdate, &ft.ctime, &ft.ctimems); +// ft.ctimems = ft.ctimems; + ft.ctime = LittleEndian16(ft.ctime); + ft.cdate = LittleEndian16(ft.cdate); + FAT_int_GetFATTimestamp(NewNode->MTime, &ft.mdate, &ft.mtime, NULL); + ft.mtime = LittleEndian16(ft.mtime); + ft.mdate = LittleEndian16(ft.mdate); + FAT_int_GetFATTimestamp(NewNode->ATime, &ft.adate, NULL, NULL); + ft.adate = LittleEndian16(ft.adate); ft.clusterHi = LittleEndian16((NewNode->Inode >> 16) & 0xFFFF); ft.cluster = LittleEndian16(NewNode->Inode & 0xFFFF); ft.size = LittleEndian32(NewNode->Size); @@ -974,6 +1052,13 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) return ENOTIMPL; } + // Calculate the checksum used for LFN + Uint8 lfn_checksum = 0; + if( nLFNEnt ) + { + lfn_checksum = FAT_int_MakeLFNChecksum(ft.name); + } + // Insert entries if( range_first % eps != 0 ) FAT_int_ReadDirSector(DirNode, range_first/eps, fileinfo); @@ -1000,7 +1085,7 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) lfnent->attrib = ATTR_LFN; lfnent->type = 0; lfnent->firstCluster = 0; - lfnent->checksum = 0; // ??? + lfnent->checksum = lfn_checksum; // ??? for( i = 0; i < 13; i ++ ) { @@ -1014,8 +1099,6 @@ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode) else lfnent->name3[i-5-6] = wd; } - - lfnent->checksum = 0; // ??? } } FAT_int_WriteDirSector(DirNode, range_last/eps, fileinfo); diff --git a/KernelLand/Modules/Filesystems/FAT/fat.c b/KernelLand/Modules/Filesystems/FAT/fat.c index f7dd8c52..bb90c3cf 100644 --- a/KernelLand/Modules/Filesystems/FAT/fat.c +++ b/KernelLand/Modules/Filesystems/FAT/fat.c @@ -17,7 +17,7 @@ * \todo Implement changing of the parent directory when a file is written to * \todo Implement file creation / deletion */ -#define DEBUG 0 +#define DEBUG 1 #define VERBOSE 1 #include @@ -483,9 +483,12 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe int remLength = Length; Uint32 cluster, tmpCluster; int bNewCluster = 0; + off_t original_offset = Offset; if(Offset > Node->Size) return 0; + ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer); + // Seek Clusters cluster = Node->Inode & 0xFFFFFFFF; while( Offset > disk->BytesPerCluster ) @@ -493,6 +496,7 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe cluster = FAT_int_GetFatValue( disk, cluster ); if(cluster == -1) { Log_Warning("FAT", "EOC Unexpectedly Reached"); + LEAVE('i', 0); return 0; } Offset -= disk->BytesPerCluster; @@ -500,7 +504,10 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe if( Offset == disk->BytesPerCluster ) { Uint32 tmp = FAT_int_AllocateCluster(disk, cluster); - if(!tmp) return 0; + if(!tmp) { + LEAVE('i', 0); + return 0; + } cluster = tmp; Offset -= disk->BytesPerCluster; } @@ -509,17 +516,20 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe { char tmpBuf[disk->BytesPerCluster]; + LOG("Read-Modify-Write single"); + // Read-Modify-Write FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); memcpy( tmpBuf + Offset, Buffer, Length ); FAT_int_WriteCluster( disk, cluster, tmpBuf ); - - return Length; + goto return_full; } // Clean up changes within a cluster if( Offset ) { + LOG("Read-Modify-Write first"); + // Read-Modify-Write FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset ); @@ -532,9 +542,8 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe tmpCluster = FAT_int_GetFatValue(disk, cluster); if(tmpCluster == -1) { tmpCluster = FAT_int_AllocateCluster(disk, cluster); - if( tmpCluster == 0 ) { - return Length - remLength; - } + if( tmpCluster == 0 ) + goto ret_incomplete; } cluster = tmpCluster; } @@ -543,28 +552,47 @@ size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffe { FAT_int_WriteCluster( disk, cluster, Buffer ); Buffer += disk->BytesPerCluster; + remLength -= disk->BytesPerCluster; // Get next cluster (allocating if needed) tmpCluster = FAT_int_GetFatValue(disk, cluster); if(tmpCluster == -1) { bNewCluster = 1; tmpCluster = FAT_int_AllocateCluster(disk, cluster); - if( tmpCluster == 0 ) { - return Length - remLength; - } + if( tmpCluster == 0 ) + goto ret_incomplete; } cluster = tmpCluster; } // Finish off - if( bNewCluster ) - memset(tmpBuf, 0, disk->BytesPerCluster); - else - FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); - memcpy( tmpBuf, Buffer, remLength ); - FAT_int_WriteCluster( disk, cluster, tmpBuf ); - free( tmpBuf ); - + if( remLength ) + { + if( bNewCluster ) + memset(tmpBuf, 0, disk->BytesPerCluster); + else + FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); + memcpy( tmpBuf, Buffer, remLength ); + FAT_int_WriteCluster( disk, cluster, tmpBuf ); + } + +return_full: + if( original_offset + Length > Node->Size ) { + Node->Size = original_offset + Length; + LOG("Updated size to %x", Node->Size); + Node->ImplInt |= FAT_FLAG_DIRTY; + } + + LEAVE('i', Length); + return Length; +ret_incomplete: + LOG("Write incomplete"); + Length -= remLength; + if( original_offset + Length > Node->Size ) { + Node->Size = original_offset + Length; + Node->ImplInt |= FAT_FLAG_DIRTY; + } + LEAVE('i', Length); return Length; } #endif @@ -577,7 +605,9 @@ void FAT_CloseFile(tVFS_Node *Node) { tFAT_VolInfo *disk = Node->ImplPtr; if(Node == NULL) return ; - + + ENTER("pNode", Node); + #if SUPPORT_WRITE // Update the node if it's dirty (don't bother if it's marked for // deletion) @@ -589,6 +619,7 @@ void FAT_CloseFile(tVFS_Node *Node) dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32); if( !dirnode ) { Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32); + LEAVE('-'); return ; } @@ -619,4 +650,5 @@ void FAT_CloseFile(tVFS_Node *Node) } } #endif + LEAVE('-'); } diff --git a/KernelLand/Modules/Filesystems/FAT/fatio.c b/KernelLand/Modules/Filesystems/FAT/fatio.c index ab49b30b..0652f0fa 100644 --- a/KernelLand/Modules/Filesystems/FAT/fatio.c +++ b/KernelLand/Modules/Filesystems/FAT/fatio.c @@ -35,7 +35,8 @@ Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) 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); + LOG("3 bytes at 0x%x are (Uint32)0x%x", ofs+(cluster/2)*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); @@ -108,6 +109,7 @@ Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) } Mutex_Release(&Disk->lFAT); + LOG("Allocated cluster %x", ret); return ret; } else @@ -137,7 +139,7 @@ Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) // Search within the same block as the previous cluster first do { - VFS_ReadAt(Disk->fileHandle, base + block, block_size, sector_data); + VFS_ReadAt(Disk->fileHandle, base + block*block_size, 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 ); @@ -166,16 +168,21 @@ Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) 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; + if( Previous != -1 ) + { + LOG("Updating cluster %x to point to %x (offset %x)", Previous, ret, + base + (Previous>>1)*3); + VFS_ReadAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val); + if( Previous & 1 ) { + val &= 0x000FFF; + val |= ret << 12; + } + else { + val &= 0xFFF000; + val |= ret << 0; + } + VFS_WriteAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val); } - VFS_WriteAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val); } break; case FAT16: @@ -190,6 +197,7 @@ Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) break; } Mutex_Release(&Disk->lFAT); + LOG("Allocated cluster %x", ret); return ret; #if CACHE_FAT } diff --git a/KernelLand/Modules/Filesystems/FAT/nodecache.c b/KernelLand/Modules/Filesystems/FAT/nodecache.c index c862dca5..a9cdbd68 100644 --- a/KernelLand/Modules/Filesystems/FAT/nodecache.c +++ b/KernelLand/Modules/Filesystems/FAT/nodecache.c @@ -13,6 +13,29 @@ extern tVFS_Node *FAT_int_CacheNode(tFAT_VolInfo *Disk, const tVFS_Node *Node); // === CODE === +tTime FAT_int_GetAcessTimestamp(Uint16 Date, Uint16 Time, Uint8 MS) +{ + return MS * 10 + timestamp( + // Seconds Minutes Hours + (Time & 0x1F) * 2, (Time >> 5) & 0x3F, (Time >> 11) & 0x1F, + // Day Month Year + (Date & 0x1F) - 1, ((Date >> 5) & 0xF) - 1, 1980 + ((Date >> 9) & 0xFF) + ); +} + +void FAT_int_GetFATTimestamp(tTime AcessTimestamp, Uint16 *Date, Uint16 *Time, Uint8 *MS) +{ + int y, m, d; + int h, min, s, ms; + format_date(AcessTimestamp, &y, &m, &d, &h, &min, &s, &ms); + if(Date) + *Date = (d + 1) | ((m + 1) << 5) | ((y - 1980) << 9); + if(Time) + *Time = (s / 2) | (min << 5) | (h << 11); + if(MS) + *MS = (ms / 10) + (s & 1) * 100; +} + /** * \brief Creates a tVFS_Node structure for a given file entry * \param Parent Parent directory VFS node @@ -58,30 +81,9 @@ tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry) } // 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 - ); + node.CTime = FAT_int_GetAcessTimestamp(Entry->cdate, Entry->ctime, Entry->ctimems); + node.MTime = FAT_int_GetAcessTimestamp(Entry->mdate, Entry->mtime, 0); + node.ATime = FAT_int_GetAcessTimestamp(Entry->adate, 0, 0); // Set pointers if(node.Flags & VFS_FFLAG_DIRECTORY) { @@ -101,9 +103,28 @@ tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry) tVFS_Node *FAT_int_CreateIncompleteDirNode(tFAT_VolInfo *Disk, Uint32 Cluster) { + if( Cluster == Disk->rootOffset ) + return &Disk->rootNode; + // 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? + + 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; + } + } + + // Create a temporary node? + + Mutex_Release(&Disk->lNodeCache); return NULL; } -- 2.20.1