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

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