Modules/FAT - (minor) Add forgotten LEAVE
[tpg/acess2.git] / KernelLand / Modules / Filesystems / FAT / dir.c
1 /*
2  * Acess2 FAT12/16/32 Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * dir.c
6  * - Directory access/manipulation code
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <vfs.h>
11 #include "common.h"
12
13 // === PROTOTYPES ===
14 void    FAT_int_ProperFilename(char *dest, const char *src);
15  int    FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName, char *Dest);
16  int    FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source);
17  int    FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source);
18
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);
22 #if SUPPORT_WRITE
23  int    FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
24 #endif
25 #if USE_LFN
26 Uint16  *FAT_int_GetLFN(tVFS_Node *Node, int ID);
27 void    FAT_int_DelLFN(tVFS_Node *Node, int ID);
28 #endif
29  int    FAT_ReadDir(tVFS_Node *Node, int ID, char Dest[FILENAME_MAX]);
30 tVFS_Node       *FAT_FindDir(tVFS_Node *Node, const char *Name);
31 tVFS_Node       *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
32 #if SUPPORT_WRITE
33 tVFS_Node       *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);
37 #endif
38
39 // === CODE ===
40
41 /**
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')
45  */
46 void FAT_int_ProperFilename(char *dest, const char *src)
47 {
48          int    inpos, outpos;
49         
50         // Name
51         outpos = 0;
52         for( inpos = 0; inpos < 8; inpos++ ) {
53                 if(src[inpos] == ' ')   break;
54                 dest[outpos++] = src[inpos];
55         }
56         inpos = 8;
57         // Check for empty extensions
58         if(src[8] != ' ')
59         {
60                 dest[outpos++] = '.';
61                 for( ; inpos < 11; inpos++)     {
62                         if(src[inpos] == ' ')   break;
63                         dest[outpos++] = src[inpos];
64                 }
65         }
66         dest[outpos++] = '\0';
67         
68         //LOG("dest='%s'", dest);
69 }
70
71 /**
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
77  */
78 int FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName, char *Dest)
79 {
80         ENTER("pft sLongFileName", ft, LongFileName);
81         #if USE_LFN
82         if(LongFileName && LongFileName[0] != 0)
83         {
84                  int    len = FAT_int_ConvertUTF16_to_UTF8(NULL, LongFileName);
85                 if( len > FILENAME_MAX ) {
86                         return -1;
87                 }
88                 FAT_int_ConvertUTF16_to_UTF8((Uint8*)Dest, LongFileName);
89         }
90         else
91         {
92         #endif
93                 FAT_int_ProperFilename(Dest, ft->name);
94         #if USE_LFN
95         }
96         #endif
97         return 0;
98 }
99
100 #if USE_LFN
101 int FAT_int_CompareUTF16_UTF8(const Uint16 *Str16, const char *Str8)
102 {
103          int    pos16 = 0, pos8 = 0;
104         const Uint8     *str8 = (const Uint8 *)Str8;
105         
106         while( Str16[pos16] && str8[pos8] )
107         {
108                 Uint32  cp8, cp16;
109                 if( Str16[pos16] & 0x8000 ) {
110                         // Do something!
111                         cp16 = 0;
112                 }
113                 else {
114                         cp16 = Str16[pos16];
115                         pos16 ++;
116                 }
117                 pos8 += ReadUTF8(str8 + pos8, &cp8);
118         
119                 if(cp16 == cp8) continue ;
120                 
121                 if(cp16 < cp8)
122                         return -1;
123                 else
124                         return 1;
125         }
126         if(Str16[pos16] == str8[pos8])
127                 return 0;
128         if(Str16[pos16] < str8[pos8])
129                 return -1;
130         else
131                 return 1;
132 }
133
134 int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source)
135 {
136          int    len = 0;
137         for( ; *Source; Source ++ )
138         {
139                 // TODO: Decode/Reencode
140                 if( Dest )
141                         Dest[len] = *Source;
142                 len += 1;
143         }
144         if( Dest )
145                 Dest[len] = 0;
146         return len;
147 }
148
149 int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source)
150 {
151          int    len = 0;
152         while( *Source )
153         {
154                 Uint32  cp;
155                 int cpl;
156                 
157                 cpl = ReadUTF8(Source, &cp);
158                 if(cp < 0x8000) {
159                         if( Dest )
160                                 Dest[len] = cp;
161                         len ++;
162                 }
163                 else {
164                         // TODO!
165                 }
166                 Source += cpl;
167         }
168         Dest[len] = 0;
169         return len;
170 }
171
172 int FAT_int_ParseLFN(const fat_filetable *Entry, Uint16 *Buffer)
173 {
174         const fat_longfilename  *lfnInfo;
175          int    ofs;
176         
177         lfnInfo = (const void*)Entry;
178         
179         if(lfnInfo->id & 0x40) {
180                 memset(Buffer, 0, 256*2);
181         }
182         ofs = (lfnInfo->id & 0x3F) * 13 - 1;
183         if( ofs >= 255 )
184                 return -1;
185         
186         Buffer[ofs--] = lfnInfo->name3[1];      Buffer[ofs--] = lfnInfo->name3[0];
187         Buffer[ofs--] = lfnInfo->name2[5];      Buffer[ofs--] = lfnInfo->name2[4];
188         Buffer[ofs--] = lfnInfo->name2[3];      Buffer[ofs--] = lfnInfo->name2[2];
189         Buffer[ofs--] = lfnInfo->name2[1];      Buffer[ofs--] = lfnInfo->name2[0];
190         Buffer[ofs--] = lfnInfo->name1[4];      Buffer[ofs--] = lfnInfo->name1[3];
191         Buffer[ofs--] = lfnInfo->name1[2];      Buffer[ofs--] = lfnInfo->name1[1];
192         Buffer[ofs--] = lfnInfo->name1[0];
193         
194         if((lfnInfo->id&0x3F) == 1)
195                 return 1;
196         return 0;
197 }
198 #endif
199
200 int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry)
201 {
202         fat_filetable   fileinfo[16];
203         char    tmpName[13];
204         #if USE_LFN
205         Uint16  lfn[256];
206          int    lfnId = -1;
207         #endif
208
209         ENTER("pDirNode sName pEntry", DirNode, Name, Entry);
210
211         for( int i = 0; ; i++ )
212         {
213                 if((i & 0xF) == 0) {
214                         if(FAT_int_ReadDirSector(DirNode, i/16, fileinfo))
215                         {
216                                 LEAVE('i', -1);
217                                 return -1;
218                         }
219                 }
220                 
221                 //Check if the files are free
222                 if(fileinfo[i&0xF].name[0] == '\0')     break;  // End of List marker
223                 if(fileinfo[i&0xF].name[0] == '\xE5')   continue;       // Free entry
224                 
225                 
226                 #if USE_LFN
227                 // Long File Name Entry
228                 if(fileinfo[i & 0xF].attrib == ATTR_LFN)
229                 {
230                         if( FAT_int_ParseLFN(&fileinfo[i&0xF], lfn) )
231                                 lfnId = i+1;
232                         continue ;
233                 }
234                 // Remove LFN if it does not apply
235                 if(lfnId != i)  lfn[0] = 0;
236                 #else
237                 if(fileinfo[i&0xF].attrib == ATTR_LFN)  continue;
238                 #endif
239
240                 // Get Real Filename
241                 FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
242 //              LOG("tmpName = '%s'", tmpName);
243 //              #if DEBUG
244 //              Debug_HexDump("FAT tmpName", tmpName, strlen(tmpName));
245 //              #endif
246 /*
247                 #if DEBUG && USE_LFN
248                 if(lfnId == i)
249                 {
250                         Uint8 lfntmp[256*3+1];
251                         FAT_int_ConvertUTF16_to_UTF8(lfntmp, lfn);
252                         LOG("lfntmp = '%s'", lfntmp);
253                 }
254                 #endif
255 */
256         
257                 // Only the long name is case sensitive, 8.3 is not
258                 #if USE_LFN
259                 if(strucmp(tmpName, Name) == 0 || FAT_int_CompareUTF16_UTF8(lfn, Name) == 0)
260                 #else
261                 if(strucmp(tmpName, Name) == 0)
262                 #endif
263                 {
264                         memcpy(Entry, fileinfo + (i&0xF), sizeof(*Entry));
265                         LOG("Found %s at %i", Name, i);
266                         LEAVE('i', i);
267                         return i;
268                 }
269         }
270         
271         LEAVE('i', -1);
272         return -1;
273 }
274
275 int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry)
276 {
277          int    ents_per_sector = 512 / sizeof(fat_filetable); 
278         fat_filetable   fileinfo[ents_per_sector];
279          int    i, sector;
280
281         Mutex_Acquire(&DirNode->Lock);
282         sector = 0;
283         for( i = 0; ; i ++ )
284         {
285                 if( i == 0 || i == ents_per_sector )
286                 {
287                         if(FAT_int_ReadDirSector(DirNode, sector, fileinfo))
288                         {
289                                 LOG("ReadDirSector failed");
290                                 break ;
291                         }
292                         i = 0;
293                         sector ++;
294                 }
295         
296                 // Check for free/end of list
297                 if(fileinfo[i].name[0] == '\0') break;  // End of List marker
298                 if(fileinfo[i].name[0] == '\xE5')       continue;       // Free entry
299                 
300                 if(fileinfo[i].attrib == ATTR_LFN)      continue;
301
302                 LOG("fileinfo[i].cluster = %x:%04x", fileinfo[i].clusterHi, fileinfo[i].cluster);
303                 #if DEBUG
304                 {
305                         char    tmpName[13];
306                         FAT_int_ProperFilename(tmpName, fileinfo[i].name);
307                         LOG("tmpName = '%s'", tmpName);
308                 }
309                 #endif
310                 
311         
312                 if(fileinfo[i].cluster != (Cluster & 0xFFFF))   continue;
313                 if(fileinfo[i].clusterHi != ((Cluster >> 16) & 0xFFFF)) continue;
314         
315                 memcpy(Entry, &fileinfo[i], sizeof(*Entry));
316                 Mutex_Release(&DirNode->Lock);
317                 return i;
318         }
319         
320         Mutex_Release(&DirNode->Lock);
321         return -1;
322 }
323
324 /* 
325  * ====================
326  *     Directory IO
327  * ====================
328  */
329
330 /**
331  * \brief Reads a sector from the disk
332  * \param Node  Directory node to read
333  * \param Sector        Sector number in the directory to read
334  * \param Buffer        Destination buffer for the read data
335  */
336 int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
337 {
338         Uint64  addr;
339         tFAT_VolInfo    *disk = Node->ImplPtr;
340         
341         ENTER("pNode iSector pEntry", Node, Sector, Buffer);
342         
343         // Parse address
344         if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
345         {
346                 LEAVE('i', 1);
347                 return 1;
348         }
349         
350         LOG("addr = 0x%llx", addr);
351         // Read Sector
352         if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)
353         {
354                 LEAVE('i', 1);
355                 return 1;
356         }
357         
358         LEAVE('i', 0);
359         return 0;
360 }
361
362 #if SUPPORT_WRITE
363 /**
364  * \brief Write a sector to the disk
365  * \param Node  Directory node to write
366  * \param Sector        Sector number in the directory to write
367  * \param Buffer        Source data
368  */
369 int FAT_int_WriteDirSector(tVFS_Node *Node, int Sector, const fat_filetable *Buffer)
370 {
371         Uint64  addr;
372         tFAT_VolInfo    *disk = Node->ImplPtr;
373         
374         ENTER("pNode iSector pEntry", Node, Sector, Buffer);
375         
376         // Parse address
377         if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
378         {
379                 LEAVE('i', 1);
380                 return 1;
381         }
382         
383         // Read Sector
384         if(VFS_WriteAt(disk->fileHandle, addr, 512, Buffer) != 512)
385         {
386                 LEAVE('i', 1);
387                 return 1;
388         }
389         
390         LEAVE('i', 0);
391         return 0;
392 }
393
394 /**
395  * \brief Writes an entry to the disk
396  * \todo Support expanding a directory
397  * \param Node  Directory node
398  * \param ID    ID of entry to update
399  * \param Entry Entry data
400  * \return Zero on success, non-zero on error
401  */
402 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
403 {
404         Uint64  addr = 0;
405          int    tmp;
406         Uint32  cluster = 0;
407         tFAT_VolInfo    *disk = Node->ImplPtr;
408         
409         ENTER("pNode iID pEntry", Node, ID, Entry);
410         
411         tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
412         if( tmp )
413         {
414                 //TODO: Allocate a cluster
415                 cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
416                 if(cluster == -1) {
417                         Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
418                         LEAVE('i', 1);
419                         return 1;
420                 }
421                 FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
422         }
423         
424
425         LOG("addr = 0x%llx", addr);
426         
427         // Wriet data to disk
428         VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);
429         
430         LEAVE('i', 0);
431         return 0;
432 }
433 #endif
434
435 #if USE_LFN     
436 /**
437  * \fn Uint16 *FAT_int_GetLFN(tVFS_Node *node)
438  * \brief Return pointer to LFN cache entry
439  * \param Node  Directory node
440  * \param ID    ID of the short name
441  * \return Pointer to the LFN cache entry
442  */
443 Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID)
444 {
445         tFAT_LFNCache   *cache;
446          int    i, firstFree;
447         
448         Mutex_Acquire( &Node->Lock );
449         
450         // TODO: Thread Safety (Lock things)
451         cache = Node->Data;
452         
453         // Create a cache if it isn't there
454         if(!cache) {
455                 cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
456                 cache->NumEntries = 1;
457                 cache->Entries[0].ID = ID;
458                 cache->Entries[0].Data[0] = 0;
459                 Mutex_Release( &Node->Lock );
460                 //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
461                 return cache->Entries[0].Data;
462         }
463         
464         // Scan for this entry
465         firstFree = -1;
466         for( i = 0; i < cache->NumEntries; i++ )
467         {
468                 if( cache->Entries[i].ID == ID ) {
469                         Mutex_Release( &Node->Lock );
470                         //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
471                         return cache->Entries[i].Data;
472                 }
473                 if( cache->Entries[i].ID == -1 && firstFree == -1 )
474                         firstFree = i;
475         }
476         
477         if(firstFree == -1) {
478                 // Use `i` for temp length
479                 i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
480                 Node->Data = realloc( Node->Data, i );
481                 if( !Node->Data ) {
482                         Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
483                         Mutex_Release( &Node->Lock );
484                         return NULL;
485                 }
486                 //Log_Debug("FAT", "Realloc (%i)\n", i);
487                 cache = Node->Data;
488                 i = cache->NumEntries;
489                 cache->NumEntries ++;
490         }
491         else {
492                 i = firstFree;
493         }
494         
495         // Create new entry
496         cache->Entries[ i ].ID = ID;
497         cache->Entries[ i ].Data[0] = '\0';
498         
499         Mutex_Release( &Node->Lock );
500         //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
501         return cache->Entries[ i ].Data;
502 }
503
504 /**
505  * \fn void FAT_int_DelLFN(tVFS_Node *node)
506  * \brief Delete a LFN cache entry
507  * \param Node  Directory node
508  * \param ID    File Entry ID
509  */
510 void FAT_int_DelLFN(tVFS_Node *Node, int ID)
511 {
512         tFAT_LFNCache   *cache = Node->Data;
513          int    i;
514         
515         // Fast return
516         if(!cache)      return;
517         
518         // Scan for a current entry
519         for( i = 0; i < cache->NumEntries; i++ )
520         {
521                 if( cache->Entries[i].ID == ID )
522                         cache->Entries[i].ID = -1;
523         }
524         return ;
525 }
526 #endif
527
528 /**
529  * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
530  * \param Node  Node structure of directory
531  * \param ID    Directory position
532  * \return Filename as a heap string, NULL or VFS_SKIP
533  */
534 int FAT_ReadDir(tVFS_Node *Node, int ID, char Dest[FILENAME_MAX])
535 {
536         fat_filetable   fileinfo[16];   // sizeof(fat_filetable)=32, so 16 per sector
537          int    a;
538         #if USE_LFN
539         Uint16  *lfn = NULL;
540         #endif
541         
542         ENTER("pNode iID", Node, ID);
543         
544         if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
545         {
546                 LOG("End of chain, end of dir");
547                 LEAVE('i', -EIO);
548                 return -EIO;
549         }
550         
551         // Offset in sector
552         a = ID % 16;
553
554         LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
555         
556         // Check if this is the last entry
557         if( fileinfo[a].name[0] == '\0' ) {
558                 Node->Size = ID;
559                 LOG("End of list");
560                 LEAVE('i', -ENOENT);
561                 return -ENOENT; // break
562         }
563         
564         // Check for empty entry
565         if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
566                 LOG("Empty Entry");
567                 LEAVE_RET('i', 1);      // Skip
568         }
569         
570         #if USE_LFN
571         // Get Long File Name Cache
572         if(fileinfo[a].attrib == ATTR_LFN)
573         {
574                 fat_longfilename        *lfnInfo;
575                 
576                 lfnInfo = (fat_longfilename *) &fileinfo[a];
577                 
578                 // Get cache for corresponding file
579                 // > ID + Index gets the corresponding short node
580                 lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
581
582                 a = FAT_int_ParseLFN(&fileinfo[a], lfn);
583                 if( a < 0 ) {
584                         LOG("Invalid LFN, error");
585                         LEAVE_RET('i', -EIO);
586                 }
587
588                 LEAVE_RET('i', 1);      // Skip
589         }
590         #endif
591         
592         // Check if it is a volume entry
593         if(fileinfo[a].attrib & 0x08) {
594                 LEAVE_RET('i', 1);      // Skip
595         }
596         // Ignore .
597         if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
598                 LEAVE_RET('i', 1);      // Skip
599         }
600         // and ..
601         if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
602                 LEAVE_RET('i', 1);      // Skip
603         }
604         
605         LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
606                 fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
607                 fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
608                 fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
609         
610         #if USE_LFN
611         lfn = FAT_int_GetLFN(Node, ID);
612         //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
613         FAT_int_CreateName(&fileinfo[a], lfn, Dest);
614         #else
615         FAT_int_CreateName(&fileinfo[a], NULL, Dest);
616         #endif
617         
618         LEAVE('i', 0);
619         return 0;
620 }
621
622 /**
623  * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
624  * \brief Finds an entry in the current directory
625  */
626 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
627 {
628         fat_filetable   fileent;
629         
630         ENTER("pNode sname", Node, Name);       
631
632         // Fast Returns
633         if(!Name || Name[0] == '\0') {
634                 LEAVE('n');
635                 return NULL;
636         }
637
638         if( FAT_int_GetEntryByName(Node, Name, &fileent) == -1 ) {
639                 LEAVE('n');
640                 return NULL;
641         }
642         
643
644         tVFS_Node *ret = FAT_int_CreateNode(Node, &fileent);
645         LOG("Found %s as %p", Name, ret);
646         LEAVE('p', ret);
647         return ret;
648 }
649
650 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
651 {
652         tFAT_VolInfo    *disk = Root->ImplPtr;
653         tVFS_Node       *dirnode, *ret;
654         fat_filetable   ft;
655
656         ENTER("pRoot XInode", Root, Inode);
657
658         ret = FAT_int_GetNode(disk, Inode & 0xFFFFFFFF);
659         if( ret ) {
660                 if( (ret->Inode >> 32) != 0 ) {
661                         LOG("Node in cache, quick return");
662                         LEAVE('p', ret);
663                         return ret;
664                 }
665                 else {
666                         LOG("Node cached, but incomplete");
667                         // Fall on through
668                 }
669                 ret = NULL;
670         }
671         
672         dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
673
674         int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
675         if( id != -1 ) {
676                 ret = FAT_int_CreateNode(dirnode, &ft);
677         }
678
679         dirnode->Type->Close(dirnode);
680
681         LEAVE('p', ret);
682         return ret;
683 }
684
685 #if SUPPORT_WRITE
686 /**
687  * \brief Create a new node
688  */
689 tVFS_Node *FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
690 {
691         tFAT_VolInfo    *disk = DirNode->ImplPtr;
692          int    rv;
693         fat_filetable   ft;
694         memset(&ft, 0, sizeof(ft));
695
696         ENTER("pDirNode sName xFlags", DirNode, Name, Flags);
697         
698         // Allocate a cluster
699         Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
700         LOG("Cluster 0x%07x allocated", cluster);
701         
702         // Create a temporary file table entry for an empty node
703         ft.cluster = cluster & 0xFFFF;
704         ft.clusterHi = cluster >> 16;
705         ft.size = 0;
706         if( Flags & VFS_FFLAG_DIRECTORY )
707                 ft.attrib = ATTR_DIRECTORY;
708         else
709                 ft.attrib = 0;
710         
711         tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
712         if( !newnode ) {
713                 errno = -EINTERNAL;
714                 return NULL;
715         }
716         LOG("newnode = %p", newnode);
717
718         // Call link
719         if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
720                 newnode->ImplInt |= FAT_FLAG_DELETE;
721         }
722         LEAVE('p', newnode);
723         return newnode;
724 }
725
726 /**
727  * \brief Internal - Checks if a character is valid in an 8.3 filename
728  */
729 static inline int is_valid_83_char(char ch)
730 {
731         if( '0' <= ch && ch <= '9' )
732                 return 1;
733         if( 'A' <= ch && ch <= 'Z' )
734                 return 1;
735         if( 'a' <= ch && ch <= 'z' )
736                 return 1;
737         if( strchr("$%'-_@~`#!(){}^#&", ch) )
738                 return 1;
739         if( ch > 128 )
740                 return 1;
741         return 0;
742 }
743
744 Uint8 FAT_int_UnicodeTo83(Uint32 Input)
745 {
746         Input = toupper(Input);
747         // Input = unicode_to_oem(Input);
748         if( Input > 256 )
749                 Input = '_';
750         if(!is_valid_83_char(Input))
751                 Input = '_';
752         return Input;
753 }
754
755 /**
756  * \brief Internal - Determines if a filename is a valid 8.3 filename
757  */
758 int FAT_int_IsValid83Filename(const char *Name)
759 {
760          int    i, j;
761
762         if( !Name[0] || Name[0] == '.' )
763                 return 0;
764
765         // Check filename portion
766         for( i = 0; Name[i] && i < 8; i ++ )
767         {
768                 if( Name[i] == '.' )
769                         break;
770                 if( !is_valid_83_char(Name[i]) )
771                         return 0;
772         }
773         // If the next char is not \0 or '.', it's not valid
774         if( Name[i] && Name[i++] != '.' )
775                 return 0;
776         
777         // Check the extension portion
778         for( j = 0; Name[i+j] && j < 3; j ++ )
779         {
780                 if( !is_valid_83_char(Name[i+j]) )
781                         return 0;
782         }
783         
784         // After the extension must be the end
785         if( Name[i+j] )
786                 return 0;
787         
788         return 1;
789 }
790
791 Uint8 FAT_int_MakeLFNChecksum(const char *ShortName)
792 {
793         Uint8 ret = 0;
794         for( int i = 0; i < 11; i++ )
795         {
796                 // ret = (ret >>> 1) + ShortName[i]
797                 // where >>> is rotate right
798                 ret = ((ret & 1) ? 0x80 : 0x00) + (ret >> 1) + ShortName[i];
799         }
800         return ret;
801 }
802
803 /**
804  * \brief Create a new name for a file
805  * \note Since FAT doesn't support reference counting, this will cause double-references if
806  *       a file is hardlinked and not unlinked
807  */
808 int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
809 {
810         Uint16  lfn[256];
811         fat_filetable   ft;
812          int    nLFNEnt = 0;
813         const int eps = 512 / sizeof(fat_filetable);
814         fat_filetable   fileinfo[eps];
815         
816         Mutex_Acquire( &DirNode->Lock );
817
818         // -- Ensure duplicates aren't created --
819         if( FAT_int_GetEntryByName(DirNode, NewName, &ft) >= 0 ) {
820                 Mutex_Release( &DirNode->Lock );
821                 return EEXIST;
822         }
823         
824         // -- Create filetable entry --
825         #if 0
826         {
827                  int    bDirty = 0;
828                  int    inofs = 0;
829                 while( NewName[inofs] && NewName[inofs] == '.' )
830                         inofs ++, bDirty = 1;
831                 for( int i = 0; i < 8 && NewName[inofs] && NewName[inofs] != '.'; i ++ )
832                 {
833                         Uint32  cp;
834                         inofs += ReadUTF8(NewName + inofs, &cp);
835                         // Spaces are silently skipped
836                         if(isspace(cp)) {
837                                 i --, bDirty = 1;
838                                 continue ;
839                         }
840                         ft.name[i] = FAT_int_UnicodeTo83(cp);
841                         if(ft.name[i] != cp)
842                                 bDirty = 1;
843                 }
844                 while( NewName[inofs] && NewName[inofs] != '.' )
845                         inofs ++, bDirty = 1;
846                 for( ; i < 8+3 && NewName[inofs]; i ++ )
847                 {
848                         Uint32  cp;
849                         inofs += ReadUTF8(NewName + inofs, &cp);
850                         // Spaces are silently skipped
851                         if(isspace(cp)) {
852                                 i --, bDirty = 1;
853                                 continue ;
854                         }
855                         ft.name[i] = FAT_int_UnicodeTo83(cp);
856                         if(ft.name[i] != cp)
857                                 bDirty = 1;
858                 }
859                 if( !NewName[inofs] )   bDirty = 1;
860                 
861                 if( bDirty )
862                 {
863                         int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
864                         lfn[lfnlen] = 0;
865                         nLFNEnt = DivUp(lfnlen, 13);
866                 }
867         }
868         #endif
869          int    bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
870         if( bNeedsLFN )
871         {
872                 int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
873                 lfn[lfnlen] = 0;
874                 nLFNEnt = DivUp(lfnlen, 13);
875         
876                 // Create a base mangled filetable entry
877                 int i, j = 0;
878                 while(NewName[j] == '.')        j ++;   // Eat leading dots
879                 for( i = 0; i < 6 && NewName[j] && NewName[j] != '.'; i ++, j ++ )
880                 {
881                         if( !isalpha(NewName[j]) && !is_valid_83_char(NewName[j]) )
882                                 ft.name[i] = '_';
883                         else
884                                 ft.name[i] = toupper(NewName[j]);
885                 }
886                 ft.name[i++] = '~';
887                 ft.name[i++] = '1';
888                 while(i < 8)    ft.name[i++] = ' ';
889                 while(NewName[j] && NewName[j] != '.')  j ++;
890                 for( ; i < 8+3 && NewName[j]; i ++, j ++ )
891                 {
892                         if( NewName[j] == '.' )
893                                 i --;
894                         else if( !is_valid_83_char(NewName[j]) )
895                                 ft.name[i] = '_';
896                         else
897                                 ft.name[i] = toupper(NewName[j]);
898                 }
899                 while(i < 8+3)  ft.name[i++] = ' ';
900                 
901                 // - Ensure there isn't a duplicate short-name
902                  int    bIsDuplicate = 1;
903                 while( bIsDuplicate )
904                 {
905                         bIsDuplicate = 0;       // Assume none                  
906
907                         // Scan directory
908                         for( int id = 0; ; id ++ )
909                         {
910                                 if( id % eps == 0 )
911                                 {
912                                         if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
913                                                 break;  // end of cluster chain
914                                 }
915                                 
916                                 // End of file list
917                                 if( fileinfo[id%eps].name[0] == '\0' )  break;
918                                 // Empty entry
919                                 if( fileinfo[id%eps].name[0] == '\xE5' )        continue;
920                                 // LFN entry
921                                 if( fileinfo[id%eps].attrib == ATTR_LFN )       continue;
922                                 
923                                 // Is this a duplicate?
924                                 if( memcmp(ft.name, fileinfo[id%eps].name, 8+3) == 0 ) {
925                                         bIsDuplicate = 1;
926                                         break;
927                                 }
928                                 
929                                 // No - move a long
930                         }
931                         
932                         // If a duplicate was found, increment the suffix
933                         if( bIsDuplicate )
934                         {
935                                 if( ft.name[7] == '9' ) {
936                                         // TODO: Expand into ~00
937                                         Log_Error("FAT", "TODO: Use two digit LFN suffixes");
938                                         Mutex_Release(&DirNode->Lock);
939                                         return ENOTIMPL;
940                                 }
941                                 
942                                 ft.name[7] += 1;
943                         }
944                 }
945         }
946         else
947         {
948                 // Create pure filetable entry
949                  int    i;
950                 // - Copy filename
951                 for( i = 0; i < 8 && *NewName && *NewName != '.'; i ++, NewName++ )
952                         ft.name[i] = *NewName;
953                 // - Pad with spaces
954                 for( ; i < 8; i ++ )
955                         ft.name[i] = ' ';
956                 // - Eat '.'
957                 if(*NewName)
958                         NewName ++;
959                 // - Copy extension
960                 for( ; i < 8+3 && *NewName; i ++, NewName++ )
961                         ft.name[i] = *NewName;
962                 // - Pad with spaces
963                 for( ; i < 8+3; i ++ )
964                         ft.name[i] = ' ';
965         }
966
967         ft.attrib = 0;
968         if(NewNode->Flags & VFS_FFLAG_DIRECTORY )
969                 ft.attrib |= ATTR_DIRECTORY;    
970         ft.ntres     = 0;
971         FAT_int_GetFATTimestamp(NewNode->CTime, &ft.cdate, &ft.ctime, &ft.ctimems);
972 //      ft.ctimems   = ft.ctimems;
973         ft.ctime     = LittleEndian16(ft.ctime);
974         ft.cdate     = LittleEndian16(ft.cdate);
975         FAT_int_GetFATTimestamp(NewNode->MTime, &ft.mdate, &ft.mtime, NULL);
976         ft.mtime     = LittleEndian16(ft.mtime);
977         ft.mdate     = LittleEndian16(ft.mdate);
978         FAT_int_GetFATTimestamp(NewNode->ATime, &ft.adate, NULL, NULL);
979         ft.adate     = LittleEndian16(ft.adate);
980         ft.clusterHi = LittleEndian16((NewNode->Inode >> 16) & 0xFFFF);
981         ft.cluster   = LittleEndian16(NewNode->Inode & 0xFFFF);
982         ft.size      = LittleEndian32(NewNode->Size);
983
984         LOG("ft.name = '%.11s'", ft.name);
985
986         // -- Add entry to the directory --
987         // Locate a range of nLFNEnt + 1 free entries
988          int    end_id = -1;
989          int    range_first = 0, range_last = -1;
990         for( int id = 0; ; id ++ )
991         {
992                 if( id % eps == 0 )
993                 {
994                         if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
995                                 break;  // end of cluster chain
996                 }
997                 
998                 // End of file list, break out
999                 if( fileinfo[id%eps].name[0] == '\0' ) {
1000                         if( id - range_first == nLFNEnt )
1001                                 range_last = id;
1002                         end_id = id;
1003                         break;
1004                 }
1005                 
1006                 // If an entry is occupied, clear the range
1007                 if( fileinfo[id%eps].name[0] != '\xE5' ) {
1008                         range_first = id + 1;
1009                         continue ;
1010                 }
1011                 
1012                 // Free entry, check if we have enough
1013                 if( id - range_first == nLFNEnt ) {
1014                         range_last = id;
1015                         break;
1016                 }
1017                 // Check the next one
1018         }
1019         if( range_last == -1 )
1020         {
1021                 // - If there are none, defragment the directory?
1022                 
1023                 // - Else, expand the directory
1024                 if( end_id == -1 ) {
1025                         // End of cluster chain
1026                 }
1027                 else {
1028                         // Just end of block
1029                 }
1030                 // - and if that fails, return an error
1031                 Log_Warning("FAT", "TODO: Impliment directory expansion / defragmenting");
1032                 Mutex_Release(&DirNode->Lock);
1033                 return ENOTIMPL;
1034         }
1035
1036         // Calculate the checksum used for LFN
1037         Uint8   lfn_checksum = 0;
1038         if( nLFNEnt )
1039         {
1040                 lfn_checksum = FAT_int_MakeLFNChecksum(ft.name);
1041         }
1042
1043         // Insert entries       
1044         if( range_first % eps != 0 )
1045                 FAT_int_ReadDirSector(DirNode, range_first/eps, fileinfo);
1046         for( int id = range_first; id <= range_last; id ++ )
1047         {
1048                 if( id % eps == 0 ) {
1049                         if( id != range_first )
1050                                 FAT_int_WriteDirSector(DirNode, (id-1)/eps, fileinfo);
1051                         FAT_int_ReadDirSector(DirNode, id/eps, fileinfo);
1052                 }
1053                 
1054                 if( id == range_last ) {
1055                         // Actual entry
1056                         memcpy(fileinfo + id % eps, &ft, sizeof(fat_filetable));
1057                 }
1058                 else {
1059                         // Long filename
1060                         int lfnid = (nLFNEnt - (id - range_first));
1061                         int ofs = (lfnid-1) * 13;
1062                          int    i=0, j=0;
1063                         fat_longfilename *lfnent = (void*)( fileinfo + id%eps );
1064                         
1065                         lfnent->id = 0x40 | lfnid;
1066                         lfnent->attrib = ATTR_LFN;
1067                         lfnent->type = 0;
1068                         lfnent->firstCluster = 0;
1069                         lfnent->checksum = lfn_checksum;        // ???
1070
1071                         for( i = 0; i < 13; i ++ )
1072                         {
1073                                 Uint16  wd;
1074                                 if( (wd = lfn[ofs+j]) ) j ++;
1075                                 wd = LittleEndian16(wd);
1076                                 if(i < 5)
1077                                         lfnent->name1[i    ] = wd;
1078                                 else if( i < 5+6 )
1079                                         lfnent->name2[i-5  ] = wd;
1080                                 else
1081                                         lfnent->name3[i-5-6] = wd;
1082                         }
1083                 }
1084         }
1085         FAT_int_WriteDirSector(DirNode, range_last/eps, fileinfo);
1086
1087         Mutex_Release( &DirNode->Lock );
1088         return 0;
1089 }
1090
1091 /**
1092  * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
1093  * \brief Rename / Delete a file
1094  */
1095 int FAT_Unlink(tVFS_Node *Node, const char *OldName)
1096 {
1097         tVFS_Node       *child;
1098         fat_filetable   ft;
1099         
1100         Mutex_Acquire(&Node->Lock);
1101
1102         int id = FAT_int_GetEntryByName(Node, OldName, &ft);
1103         if(id == -1) {
1104                 Mutex_Release(&Node->Lock);
1105                 return ENOTFOUND;
1106         }
1107
1108         child = FAT_int_CreateNode(Node->ImplPtr, &ft);
1109         if( !child ) {
1110                 Mutex_Release(&Node->Lock);
1111                 return EINVAL;
1112         }
1113         child->ImplInt |= FAT_FLAG_DELETE;      // Mark for deletion on close
1114
1115         // TODO: If it has a LFN, remove that too
1116
1117         // Delete from the directory
1118         ft.name[0] = '\xE5';
1119         FAT_int_WriteDirEntry(Node, id, &ft);
1120
1121         // Close child
1122         child->Type->Close( child );
1123         Mutex_Release( &Node->Lock );
1124         return EOK;
1125 }
1126 #endif

UCC git Repository :: git.ucc.asn.au