2 * Acess2 FAT12/16/32 Driver
3 * - By John Hodge (thePowersGang)
6 * - Directory access/manipulation code
14 void FAT_int_ProperFilename(char *dest, const char *src);
15 char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName);
16 int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source);
17 int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source);
19 int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry);
20 int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
21 int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer);
23 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
26 Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID);
27 void FAT_int_DelLFN(tVFS_Node *Node, int ID);
29 char *FAT_ReadDir(tVFS_Node *Node, int ID);
30 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);
31 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
33 int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
34 int FAT_int_IsValid83Filename(const char *Name);
35 int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
36 int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);
42 * \brief Converts a FAT directory entry name into a proper filename
43 * \param dest Destination array (must be at least 13 bytes in size)
44 * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')
46 void FAT_int_ProperFilename(char *dest, const char *src)
52 for( inpos = 0; inpos < 8; inpos++ ) {
53 if(src[inpos] == ' ') break;
54 dest[outpos++] = src[inpos];
57 // Check for empty extensions
61 for( ; inpos < 11; inpos++) {
62 if(src[inpos] == ' ') break;
63 dest[outpos++] = src[inpos];
66 dest[outpos++] = '\0';
68 //LOG("dest='%s'", dest);
72 * \fn char *FAT_int_CreateName(fat_filetable *ft, Uint8 *LongFileName)
73 * \brief Converts either a LFN or a 8.3 Name into a proper name
74 * \param ft Pointer to the file's entry in the parent directory
75 * \param LongFileName Long file name pointer
76 * \return Filename as a heap string
78 char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName)
81 ENTER("pft sLongFileName", ft, LongFileName);
82 //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);
84 if(LongFileName && LongFileName[0] != 0)
86 int len = FAT_int_ConvertUTF16_to_UTF8(NULL, LongFileName);
87 ret = malloc( len + 1 );
88 FAT_int_ConvertUTF16_to_UTF8((Uint8*)ret, LongFileName);
93 ret = (char*) malloc(13);
95 Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");
98 FAT_int_ProperFilename(ret, ft->name);
107 int FAT_int_CompareUTF16_UTF8(const Uint16 *Str16, const char *Str8)
109 int pos16 = 0, pos8 = 0;
110 const Uint8 *str8 = (const Uint8 *)Str8;
112 while( Str16[pos16] && str8[pos8] )
115 if( Str16[pos16] & 0x8000 ) {
123 pos8 += ReadUTF8(str8 + pos8, &cp8);
125 if(cp16 == cp8) continue ;
132 if(Str16[pos16] == str8[pos8])
134 if(Str16[pos16] < str8[pos8])
140 int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source)
143 for( ; *Source; Source ++ )
145 // TODO: Decode/Reencode
155 int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source)
163 cpl = ReadUTF8(Source, &cp);
178 int FAT_int_ParseLFN(const fat_filetable *Entry, Uint16 *Buffer)
180 const fat_longfilename *lfnInfo;
183 lfnInfo = (const void*)Entry;
185 if(lfnInfo->id & 0x40) {
186 memset(Buffer, 0, 256*2);
188 ofs = (lfnInfo->id & 0x3F) * 13 - 1;
192 Buffer[ofs--] = lfnInfo->name3[1]; Buffer[ofs--] = lfnInfo->name3[0];
193 Buffer[ofs--] = lfnInfo->name2[5]; Buffer[ofs--] = lfnInfo->name2[4];
194 Buffer[ofs--] = lfnInfo->name2[3]; Buffer[ofs--] = lfnInfo->name2[2];
195 Buffer[ofs--] = lfnInfo->name2[1]; Buffer[ofs--] = lfnInfo->name2[0];
196 Buffer[ofs--] = lfnInfo->name1[4]; Buffer[ofs--] = lfnInfo->name1[3];
197 Buffer[ofs--] = lfnInfo->name1[2]; Buffer[ofs--] = lfnInfo->name1[1];
198 Buffer[ofs--] = lfnInfo->name1[0];
200 if((lfnInfo->id&0x3F) == 1)
206 int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry)
208 fat_filetable fileinfo[16];
215 ENTER("pDirNode sName pEntry", DirNode, Name, Entry);
217 for( int i = 0; ; i++ )
220 if(FAT_int_ReadDirSector(DirNode, i/16, fileinfo))
227 //Check if the files are free
228 if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker
229 if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry
233 // Long File Name Entry
234 if(fileinfo[i & 0xF].attrib == ATTR_LFN)
236 if( FAT_int_ParseLFN(&fileinfo[i&0xF], lfn) )
240 // Remove LFN if it does not apply
241 if(lfnId != i) lfn[0] = 0;
243 if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;
247 FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
248 // LOG("tmpName = '%s'", tmpName);
250 // Debug_HexDump("FAT tmpName", tmpName, strlen(tmpName));
256 Uint8 lfntmp[256*3+1];
257 FAT_int_ConvertUTF16_to_UTF8(lfntmp, lfn);
258 LOG("lfntmp = '%s'", lfntmp);
263 // Only the long name is case sensitive, 8.3 is not
265 if(strucmp(tmpName, Name) == 0 || FAT_int_CompareUTF16_UTF8(lfn, Name) == 0)
267 if(strucmp(tmpName, Name) == 0)
270 memcpy(Entry, fileinfo + (i&0xF), sizeof(*Entry));
271 LOG("Found %s at %i", Name, i);
281 int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry)
283 int ents_per_sector = 512 / sizeof(fat_filetable);
284 fat_filetable fileinfo[ents_per_sector];
287 Mutex_Acquire(&DirNode->Lock);
291 if( i == 0 || i == ents_per_sector )
293 if(FAT_int_ReadDirSector(DirNode, sector, fileinfo))
295 LOG("ReadDirSector failed");
302 // Check for free/end of list
303 if(fileinfo[i].name[0] == '\0') break; // End of List marker
304 if(fileinfo[i].name[0] == '\xE5') continue; // Free entry
306 if(fileinfo[i].attrib == ATTR_LFN) continue;
308 LOG("fileinfo[i].cluster = %x:%04x", fileinfo[i].clusterHi, fileinfo[i].cluster);
312 FAT_int_ProperFilename(tmpName, fileinfo[i].name);
313 LOG("tmpName = '%s'", tmpName);
318 if(fileinfo[i].cluster != (Cluster & 0xFFFF)) continue;
319 if(fileinfo[i].clusterHi != ((Cluster >> 16) & 0xFFFF)) continue;
321 memcpy(Entry, &fileinfo[i], sizeof(*Entry));
322 Mutex_Release(&DirNode->Lock);
326 Mutex_Release(&DirNode->Lock);
331 * ====================
333 * ====================
337 * \brief Reads a sector from the disk
338 * \param Node Directory node to read
339 * \param Sector Sector number in the directory to read
340 * \param Buffer Destination buffer for the read data
342 int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
345 tFAT_VolInfo *disk = Node->ImplPtr;
347 ENTER("pNode iSector pEntry", Node, Sector, Buffer);
350 if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
356 LOG("addr = 0x%llx", addr);
358 if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)
370 * \brief Write a sector to the disk
371 * \param Node Directory node to write
372 * \param Sector Sector number in the directory to write
373 * \param Buffer Source data
375 int FAT_int_WriteDirSector(tVFS_Node *Node, int Sector, const fat_filetable *Buffer)
378 tFAT_VolInfo *disk = Node->ImplPtr;
380 ENTER("pNode iSector pEntry", Node, Sector, Buffer);
383 if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
390 if(VFS_WriteAt(disk->fileHandle, addr, 512, Buffer) != 512)
401 * \brief Writes an entry to the disk
402 * \todo Support expanding a directory
403 * \param Node Directory node
404 * \param ID ID of entry to update
405 * \param Entry Entry data
406 * \return Zero on success, non-zero on error
408 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
413 tFAT_VolInfo *disk = Node->ImplPtr;
415 ENTER("pNode iID pEntry", Node, ID, Entry);
417 tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
420 //TODO: Allocate a cluster
421 cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
423 Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
427 FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
431 LOG("addr = 0x%llx", addr);
433 // Wriet data to disk
434 VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);
443 * \fn Uint16 *FAT_int_GetLFN(tVFS_Node *node)
444 * \brief Return pointer to LFN cache entry
445 * \param Node Directory node
446 * \param ID ID of the short name
447 * \return Pointer to the LFN cache entry
449 Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID)
451 tFAT_LFNCache *cache;
454 Mutex_Acquire( &Node->Lock );
456 // TODO: Thread Safety (Lock things)
459 // Create a cache if it isn't there
461 cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
462 cache->NumEntries = 1;
463 cache->Entries[0].ID = ID;
464 cache->Entries[0].Data[0] = 0;
465 Mutex_Release( &Node->Lock );
466 //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
467 return cache->Entries[0].Data;
470 // Scan for this entry
472 for( i = 0; i < cache->NumEntries; i++ )
474 if( cache->Entries[i].ID == ID ) {
475 Mutex_Release( &Node->Lock );
476 //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
477 return cache->Entries[i].Data;
479 if( cache->Entries[i].ID == -1 && firstFree == -1 )
483 if(firstFree == -1) {
484 // Use `i` for temp length
485 i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
486 Node->Data = realloc( Node->Data, i );
488 Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
489 Mutex_Release( &Node->Lock );
492 //Log_Debug("FAT", "Realloc (%i)\n", i);
494 i = cache->NumEntries;
495 cache->NumEntries ++;
502 cache->Entries[ i ].ID = ID;
503 cache->Entries[ i ].Data[0] = '\0';
505 Mutex_Release( &Node->Lock );
506 //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
507 return cache->Entries[ i ].Data;
511 * \fn void FAT_int_DelLFN(tVFS_Node *node)
512 * \brief Delete a LFN cache entry
513 * \param Node Directory node
514 * \param ID File Entry ID
516 void FAT_int_DelLFN(tVFS_Node *Node, int ID)
518 tFAT_LFNCache *cache = Node->Data;
524 // Scan for a current entry
525 for( i = 0; i < cache->NumEntries; i++ )
527 if( cache->Entries[i].ID == ID )
528 cache->Entries[i].ID = -1;
535 * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
536 * \param Node Node structure of directory
537 * \param ID Directory position
538 * \return Filename as a heap string, NULL or VFS_SKIP
540 char *FAT_ReadDir(tVFS_Node *Node, int ID)
542 fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector
549 ENTER("pNode iID", Node, ID);
551 if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
553 LOG("End of chain, end of dir");
561 LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
563 // Check if this is the last entry
564 if( fileinfo[a].name[0] == '\0' ) {
568 return NULL; // break
571 // Check for empty entry
572 if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
574 #if 0 // Stop on empty entry?
578 LEAVE('p', VFS_SKIP);
579 return VFS_SKIP; // Skip
584 // Get Long File Name Cache
585 if(fileinfo[a].attrib == ATTR_LFN)
587 fat_longfilename *lfnInfo;
589 lfnInfo = (fat_longfilename *) &fileinfo[a];
591 // Get cache for corresponding file
592 // > ID + Index gets the corresponding short node
593 lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
595 a = FAT_int_ParseLFN(&fileinfo[a], lfn);
597 LOG("Invalid LFN, error");
602 // LOG("lfn = '%s'", lfn);
603 //Log_Debug("FAT", "lfn = '%s'", lfn);
604 LEAVE('p', VFS_SKIP);
609 // Check if it is a volume entry
610 if(fileinfo[a].attrib & 0x08) {
611 LEAVE('p', VFS_SKIP);
615 if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
616 LEAVE('p', VFS_SKIP);
620 if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
621 LEAVE('p', VFS_SKIP);
625 LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
626 fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
627 fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
628 fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
631 lfn = FAT_int_GetLFN(Node, ID);
632 //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
633 ret = FAT_int_CreateName(&fileinfo[a], lfn);
635 ret = FAT_int_CreateName(&fileinfo[a], NULL);
643 * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
644 * \brief Finds an entry in the current directory
646 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
648 fat_filetable fileent;
650 ENTER("pNode sname", Node, Name);
653 if(!Name || Name[0] == '\0') {
658 if( FAT_int_GetEntryByName(Node, Name, &fileent) == -1 ) {
664 tVFS_Node *ret = FAT_int_CreateNode(Node, &fileent);
665 LOG("Found %s as %p", Name, ret);
670 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
672 tFAT_VolInfo *disk = Root->ImplPtr;
673 tVFS_Node *dirnode, *ret;
676 ENTER("pRoot XInode", Root, Inode);
678 ret = FAT_int_GetNode(disk, Inode & 0xFFFFFFFF);
680 if( (ret->Inode >> 32) != 0 ) {
681 LOG("Node in cache, quick return");
685 LOG("Node cached, but incomplete");
691 dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
693 int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
695 ret = FAT_int_CreateNode(dirnode, &ft);
698 dirnode->Type->Close(dirnode);
706 * \brief Create a new node
708 int FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
710 tFAT_VolInfo *disk = DirNode->ImplPtr;
713 memset(&ft, 0, sizeof(ft));
715 ENTER("pDirNode sName xFlags", DirNode, Name, Flags);
717 // Allocate a cluster
718 Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
719 LOG("Cluster 0x%07x allocated", cluster);
721 // Create a temporary file table entry for an empty node
722 ft.cluster = cluster & 0xFFFF;
723 ft.clusterHi = cluster >> 16;
725 if( Flags & VFS_FFLAG_DIRECTORY )
726 ft.attrib = ATTR_DIRECTORY;
730 tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
734 LOG("newnode = %p", newnode);
737 if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
738 newnode->ImplInt |= FAT_FLAG_DELETE;
740 FAT_CloseFile(newnode);
746 * \brief Internal - Checks if a character is valid in an 8.3 filename
748 static inline int is_valid_83_char(char ch)
750 if( '0' <= ch && ch <= '9' )
752 if( 'A' <= ch && ch <= 'Z' )
754 if( 'a' <= ch && ch <= 'z' )
756 if( strchr("$%'-_@~`#!(){}^#&", ch) )
763 Uint8 FAT_int_UnicodeTo83(Uint32 Input)
765 Input = toupper(Input);
766 // Input = unicode_to_oem(Input);
769 if(!is_valid_83_char(Input))
775 * \brief Internal - Determines if a filename is a valid 8.3 filename
777 int FAT_int_IsValid83Filename(const char *Name)
781 if( !Name[0] || Name[0] == '.' )
784 // Check filename portion
785 for( i = 0; Name[i] && i < 8; i ++ )
789 if( !is_valid_83_char(Name[i]) )
792 // If the next char is not \0 or '.', it's not valid
793 if( Name[i] && Name[i++] != '.' )
796 // Check the extension portion
797 for( j = 0; Name[i+j] && j < 3; j ++ )
799 if( !is_valid_83_char(Name[i+j]) )
803 // After the extension must be the end
810 Uint8 FAT_int_MakeLFNChecksum(const char *ShortName)
813 for( int i = 0; i < 11; i++ )
815 // ret = (ret >>> 1) + ShortName[i]
816 // where >>> is rotate right
817 ret = ((ret & 1) ? 0x80 : 0x00) + (ret >> 1) + ShortName[i];
823 * \brief Create a new name for a file
824 * \note Since FAT doesn't support reference counting, this will cause double-references if
825 * a file is hardlinked and not unlinked
827 int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
832 const int eps = 512 / sizeof(fat_filetable);
833 fat_filetable fileinfo[eps];
835 Mutex_Acquire( &DirNode->Lock );
837 // -- Ensure duplicates aren't created --
838 if( FAT_int_GetEntryByName(DirNode, NewName, &ft) >= 0 ) {
839 Mutex_Release( &DirNode->Lock );
843 // -- Create filetable entry --
848 while( NewName[inofs] && NewName[inofs] == '.' )
849 inofs ++, bDirty = 1;
850 for( int i = 0; i < 8 && NewName[inofs] && NewName[inofs] != '.'; i ++ )
853 inofs += ReadUTF8(NewName + inofs, &cp);
854 // Spaces are silently skipped
859 ft.name[i] = FAT_int_UnicodeTo83(cp);
863 while( NewName[inofs] && NewName[inofs] != '.' )
864 inofs ++, bDirty = 1;
865 for( ; i < 8+3 && NewName[inofs]; i ++ )
868 inofs += ReadUTF8(NewName + inofs, &cp);
869 // Spaces are silently skipped
874 ft.name[i] = FAT_int_UnicodeTo83(cp);
878 if( !NewName[inofs] ) bDirty = 1;
882 int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
884 nLFNEnt = DivUp(lfnlen, 13);
888 int bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
891 int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
893 nLFNEnt = DivUp(lfnlen, 13);
895 // Create a base mangled filetable entry
897 while(NewName[j] == '.') j ++; // Eat leading dots
898 for( i = 0; i < 6 && NewName[j] && NewName[j] != '.'; i ++, j ++ )
900 if( !isalpha(NewName[j]) && !is_valid_83_char(NewName[j]) )
903 ft.name[i] = toupper(NewName[j]);
907 while(i < 8) ft.name[i++] = ' ';
908 while(NewName[j] && NewName[j] != '.') j ++;
909 for( ; i < 8+3 && NewName[j]; i ++, j ++ )
911 if( NewName[j] == '.' )
913 else if( !is_valid_83_char(NewName[j]) )
916 ft.name[i] = toupper(NewName[j]);
918 while(i < 8+3) ft.name[i++] = ' ';
920 // - Ensure there isn't a duplicate short-name
921 int bIsDuplicate = 1;
922 while( bIsDuplicate )
924 bIsDuplicate = 0; // Assume none
927 for( int id = 0; ; id ++ )
931 if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
932 break; // end of cluster chain
936 if( fileinfo[id%eps].name[0] == '\0' ) break;
938 if( fileinfo[id%eps].name[0] == '\xE5' ) continue;
940 if( fileinfo[id%eps].attrib == ATTR_LFN ) continue;
942 // Is this a duplicate?
943 if( memcmp(ft.name, fileinfo[id%eps].name, 8+3) == 0 ) {
951 // If a duplicate was found, increment the suffix
954 if( ft.name[7] == '9' ) {
955 // TODO: Expand into ~00
956 Log_Error("FAT", "TODO: Use two digit LFN suffixes");
957 Mutex_Release(&DirNode->Lock);
967 // Create pure filetable entry
970 for( i = 0; i < 8 && *NewName && *NewName != '.'; i ++, NewName++ )
971 ft.name[i] = *NewName;
979 for( ; i < 8+3 && *NewName; i ++, NewName++ )
980 ft.name[i] = *NewName;
982 for( ; i < 8+3; i ++ )
987 if(NewNode->Flags & VFS_FFLAG_DIRECTORY )
988 ft.attrib |= ATTR_DIRECTORY;
990 FAT_int_GetFATTimestamp(NewNode->CTime, &ft.cdate, &ft.ctime, &ft.ctimems);
991 // ft.ctimems = ft.ctimems;
992 ft.ctime = LittleEndian16(ft.ctime);
993 ft.cdate = LittleEndian16(ft.cdate);
994 FAT_int_GetFATTimestamp(NewNode->MTime, &ft.mdate, &ft.mtime, NULL);
995 ft.mtime = LittleEndian16(ft.mtime);
996 ft.mdate = LittleEndian16(ft.mdate);
997 FAT_int_GetFATTimestamp(NewNode->ATime, &ft.adate, NULL, NULL);
998 ft.adate = LittleEndian16(ft.adate);
999 ft.clusterHi = LittleEndian16((NewNode->Inode >> 16) & 0xFFFF);
1000 ft.cluster = LittleEndian16(NewNode->Inode & 0xFFFF);
1001 ft.size = LittleEndian32(NewNode->Size);
1003 LOG("ft.name = '%.11s'", ft.name);
1005 // -- Add entry to the directory --
1006 // Locate a range of nLFNEnt + 1 free entries
1008 int range_first = 0, range_last = -1;
1009 for( int id = 0; ; id ++ )
1013 if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
1014 break; // end of cluster chain
1017 // End of file list, break out
1018 if( fileinfo[id%eps].name[0] == '\0' ) {
1019 if( id - range_first == nLFNEnt )
1025 // If an entry is occupied, clear the range
1026 if( fileinfo[id%eps].name[0] != '\xE5' ) {
1027 range_first = id + 1;
1031 // Free entry, check if we have enough
1032 if( id - range_first == nLFNEnt ) {
1036 // Check the next one
1038 if( range_last == -1 )
1040 // - If there are none, defragment the directory?
1042 // - Else, expand the directory
1043 if( end_id == -1 ) {
1044 // End of cluster chain
1047 // Just end of block
1049 // - and if that fails, return an error
1050 Log_Warning("FAT", "TODO: Impliment directory expansion / defragmenting");
1051 Mutex_Release(&DirNode->Lock);
1055 // Calculate the checksum used for LFN
1056 Uint8 lfn_checksum = 0;
1059 lfn_checksum = FAT_int_MakeLFNChecksum(ft.name);
1063 if( range_first % eps != 0 )
1064 FAT_int_ReadDirSector(DirNode, range_first/eps, fileinfo);
1065 for( int id = range_first; id <= range_last; id ++ )
1067 if( id % eps == 0 ) {
1068 if( id != range_first )
1069 FAT_int_WriteDirSector(DirNode, (id-1)/eps, fileinfo);
1070 FAT_int_ReadDirSector(DirNode, id/eps, fileinfo);
1073 if( id == range_last ) {
1075 memcpy(fileinfo + id % eps, &ft, sizeof(fat_filetable));
1079 int lfnid = (nLFNEnt - (id - range_first));
1080 int ofs = (lfnid-1) * 13;
1082 fat_longfilename *lfnent = (void*)( fileinfo + id%eps );
1084 lfnent->id = 0x40 | lfnid;
1085 lfnent->attrib = ATTR_LFN;
1087 lfnent->firstCluster = 0;
1088 lfnent->checksum = lfn_checksum; // ???
1090 for( i = 0; i < 13; i ++ )
1093 if( (wd = lfn[ofs+j]) ) j ++;
1094 wd = LittleEndian16(wd);
1096 lfnent->name1[i ] = wd;
1098 lfnent->name2[i-5 ] = wd;
1100 lfnent->name3[i-5-6] = wd;
1104 FAT_int_WriteDirSector(DirNode, range_last/eps, fileinfo);
1106 Mutex_Release( &DirNode->Lock );
1111 * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
1112 * \brief Rename / Delete a file
1114 int FAT_Unlink(tVFS_Node *Node, const char *OldName)
1119 Mutex_Acquire(&Node->Lock);
1121 int id = FAT_int_GetEntryByName(Node, OldName, &ft);
1123 Mutex_Release(&Node->Lock);
1127 child = FAT_int_CreateNode(Node->ImplPtr, &ft);
1129 Mutex_Release(&Node->Lock);
1132 child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close
1134 // TODO: If it has a LFN, remove that too
1136 // Delete from the directory
1137 ft.name[0] = '\xE5';
1138 FAT_int_WriteDirEntry(Node, id, &ft);
1141 child->Type->Close( child );
1142 Mutex_Release( &Node->Lock );