Kernel - VFS API Update - ReadDir caller provided buffer
[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                         return ret;
663                 }
664                 else {
665                         LOG("Node cached, but incomplete");
666                         // Fall on through
667                 }
668                 ret = NULL;
669         }
670         
671         dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
672
673         int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
674         if( id != -1 ) {
675                 ret = FAT_int_CreateNode(dirnode, &ft);
676         }
677
678         dirnode->Type->Close(dirnode);
679
680         LEAVE('p', ret);
681         return ret;
682 }
683
684 #if SUPPORT_WRITE
685 /**
686  * \brief Create a new node
687  */
688 tVFS_Node *FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
689 {
690         tFAT_VolInfo    *disk = DirNode->ImplPtr;
691          int    rv;
692         fat_filetable   ft;
693         memset(&ft, 0, sizeof(ft));
694
695         ENTER("pDirNode sName xFlags", DirNode, Name, Flags);
696         
697         // Allocate a cluster
698         Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
699         LOG("Cluster 0x%07x allocated", cluster);
700         
701         // Create a temporary file table entry for an empty node
702         ft.cluster = cluster & 0xFFFF;
703         ft.clusterHi = cluster >> 16;
704         ft.size = 0;
705         if( Flags & VFS_FFLAG_DIRECTORY )
706                 ft.attrib = ATTR_DIRECTORY;
707         else
708                 ft.attrib = 0;
709         
710         tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
711         if( !newnode ) {
712                 errno = -EINTERNAL;
713                 return NULL;
714         }
715         LOG("newnode = %p", newnode);
716
717         // Call link
718         if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
719                 newnode->ImplInt |= FAT_FLAG_DELETE;
720         }
721         LEAVE('p', newnode);
722         return newnode;
723 }
724
725 /**
726  * \brief Internal - Checks if a character is valid in an 8.3 filename
727  */
728 static inline int is_valid_83_char(char ch)
729 {
730         if( '0' <= ch && ch <= '9' )
731                 return 1;
732         if( 'A' <= ch && ch <= 'Z' )
733                 return 1;
734         if( 'a' <= ch && ch <= 'z' )
735                 return 1;
736         if( strchr("$%'-_@~`#!(){}^#&", ch) )
737                 return 1;
738         if( ch > 128 )
739                 return 1;
740         return 0;
741 }
742
743 Uint8 FAT_int_UnicodeTo83(Uint32 Input)
744 {
745         Input = toupper(Input);
746         // Input = unicode_to_oem(Input);
747         if( Input > 256 )
748                 Input = '_';
749         if(!is_valid_83_char(Input))
750                 Input = '_';
751         return Input;
752 }
753
754 /**
755  * \brief Internal - Determines if a filename is a valid 8.3 filename
756  */
757 int FAT_int_IsValid83Filename(const char *Name)
758 {
759          int    i, j;
760
761         if( !Name[0] || Name[0] == '.' )
762                 return 0;
763
764         // Check filename portion
765         for( i = 0; Name[i] && i < 8; i ++ )
766         {
767                 if( Name[i] == '.' )
768                         break;
769                 if( !is_valid_83_char(Name[i]) )
770                         return 0;
771         }
772         // If the next char is not \0 or '.', it's not valid
773         if( Name[i] && Name[i++] != '.' )
774                 return 0;
775         
776         // Check the extension portion
777         for( j = 0; Name[i+j] && j < 3; j ++ )
778         {
779                 if( !is_valid_83_char(Name[i+j]) )
780                         return 0;
781         }
782         
783         // After the extension must be the end
784         if( Name[i+j] )
785                 return 0;
786         
787         return 1;
788 }
789
790 Uint8 FAT_int_MakeLFNChecksum(const char *ShortName)
791 {
792         Uint8 ret = 0;
793         for( int i = 0; i < 11; i++ )
794         {
795                 // ret = (ret >>> 1) + ShortName[i]
796                 // where >>> is rotate right
797                 ret = ((ret & 1) ? 0x80 : 0x00) + (ret >> 1) + ShortName[i];
798         }
799         return ret;
800 }
801
802 /**
803  * \brief Create a new name for a file
804  * \note Since FAT doesn't support reference counting, this will cause double-references if
805  *       a file is hardlinked and not unlinked
806  */
807 int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
808 {
809         Uint16  lfn[256];
810         fat_filetable   ft;
811          int    nLFNEnt = 0;
812         const int eps = 512 / sizeof(fat_filetable);
813         fat_filetable   fileinfo[eps];
814         
815         Mutex_Acquire( &DirNode->Lock );
816
817         // -- Ensure duplicates aren't created --
818         if( FAT_int_GetEntryByName(DirNode, NewName, &ft) >= 0 ) {
819                 Mutex_Release( &DirNode->Lock );
820                 return EEXIST;
821         }
822         
823         // -- Create filetable entry --
824         #if 0
825         {
826                  int    bDirty = 0;
827                  int    inofs = 0;
828                 while( NewName[inofs] && NewName[inofs] == '.' )
829                         inofs ++, bDirty = 1;
830                 for( int i = 0; i < 8 && NewName[inofs] && NewName[inofs] != '.'; i ++ )
831                 {
832                         Uint32  cp;
833                         inofs += ReadUTF8(NewName + inofs, &cp);
834                         // Spaces are silently skipped
835                         if(isspace(cp)) {
836                                 i --, bDirty = 1;
837                                 continue ;
838                         }
839                         ft.name[i] = FAT_int_UnicodeTo83(cp);
840                         if(ft.name[i] != cp)
841                                 bDirty = 1;
842                 }
843                 while( NewName[inofs] && NewName[inofs] != '.' )
844                         inofs ++, bDirty = 1;
845                 for( ; i < 8+3 && NewName[inofs]; i ++ )
846                 {
847                         Uint32  cp;
848                         inofs += ReadUTF8(NewName + inofs, &cp);
849                         // Spaces are silently skipped
850                         if(isspace(cp)) {
851                                 i --, bDirty = 1;
852                                 continue ;
853                         }
854                         ft.name[i] = FAT_int_UnicodeTo83(cp);
855                         if(ft.name[i] != cp)
856                                 bDirty = 1;
857                 }
858                 if( !NewName[inofs] )   bDirty = 1;
859                 
860                 if( bDirty )
861                 {
862                         int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
863                         lfn[lfnlen] = 0;
864                         nLFNEnt = DivUp(lfnlen, 13);
865                 }
866         }
867         #endif
868          int    bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
869         if( bNeedsLFN )
870         {
871                 int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
872                 lfn[lfnlen] = 0;
873                 nLFNEnt = DivUp(lfnlen, 13);
874         
875                 // Create a base mangled filetable entry
876                 int i, j = 0;
877                 while(NewName[j] == '.')        j ++;   // Eat leading dots
878                 for( i = 0; i < 6 && NewName[j] && NewName[j] != '.'; i ++, j ++ )
879                 {
880                         if( !isalpha(NewName[j]) && !is_valid_83_char(NewName[j]) )
881                                 ft.name[i] = '_';
882                         else
883                                 ft.name[i] = toupper(NewName[j]);
884                 }
885                 ft.name[i++] = '~';
886                 ft.name[i++] = '1';
887                 while(i < 8)    ft.name[i++] = ' ';
888                 while(NewName[j] && NewName[j] != '.')  j ++;
889                 for( ; i < 8+3 && NewName[j]; i ++, j ++ )
890                 {
891                         if( NewName[j] == '.' )
892                                 i --;
893                         else if( !is_valid_83_char(NewName[j]) )
894                                 ft.name[i] = '_';
895                         else
896                                 ft.name[i] = toupper(NewName[j]);
897                 }
898                 while(i < 8+3)  ft.name[i++] = ' ';
899                 
900                 // - Ensure there isn't a duplicate short-name
901                  int    bIsDuplicate = 1;
902                 while( bIsDuplicate )
903                 {
904                         bIsDuplicate = 0;       // Assume none                  
905
906                         // Scan directory
907                         for( int id = 0; ; id ++ )
908                         {
909                                 if( id % eps == 0 )
910                                 {
911                                         if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
912                                                 break;  // end of cluster chain
913                                 }
914                                 
915                                 // End of file list
916                                 if( fileinfo[id%eps].name[0] == '\0' )  break;
917                                 // Empty entry
918                                 if( fileinfo[id%eps].name[0] == '\xE5' )        continue;
919                                 // LFN entry
920                                 if( fileinfo[id%eps].attrib == ATTR_LFN )       continue;
921                                 
922                                 // Is this a duplicate?
923                                 if( memcmp(ft.name, fileinfo[id%eps].name, 8+3) == 0 ) {
924                                         bIsDuplicate = 1;
925                                         break;
926                                 }
927                                 
928                                 // No - move a long
929                         }
930                         
931                         // If a duplicate was found, increment the suffix
932                         if( bIsDuplicate )
933                         {
934                                 if( ft.name[7] == '9' ) {
935                                         // TODO: Expand into ~00
936                                         Log_Error("FAT", "TODO: Use two digit LFN suffixes");
937                                         Mutex_Release(&DirNode->Lock);
938                                         return ENOTIMPL;
939                                 }
940                                 
941                                 ft.name[7] += 1;
942                         }
943                 }
944         }
945         else
946         {
947                 // Create pure filetable entry
948                  int    i;
949                 // - Copy filename
950                 for( i = 0; i < 8 && *NewName && *NewName != '.'; i ++, NewName++ )
951                         ft.name[i] = *NewName;
952                 // - Pad with spaces
953                 for( ; i < 8; i ++ )
954                         ft.name[i] = ' ';
955                 // - Eat '.'
956                 if(*NewName)
957                         NewName ++;
958                 // - Copy extension
959                 for( ; i < 8+3 && *NewName; i ++, NewName++ )
960                         ft.name[i] = *NewName;
961                 // - Pad with spaces
962                 for( ; i < 8+3; i ++ )
963                         ft.name[i] = ' ';
964         }
965
966         ft.attrib = 0;
967         if(NewNode->Flags & VFS_FFLAG_DIRECTORY )
968                 ft.attrib |= ATTR_DIRECTORY;    
969         ft.ntres     = 0;
970         FAT_int_GetFATTimestamp(NewNode->CTime, &ft.cdate, &ft.ctime, &ft.ctimems);
971 //      ft.ctimems   = ft.ctimems;
972         ft.ctime     = LittleEndian16(ft.ctime);
973         ft.cdate     = LittleEndian16(ft.cdate);
974         FAT_int_GetFATTimestamp(NewNode->MTime, &ft.mdate, &ft.mtime, NULL);
975         ft.mtime     = LittleEndian16(ft.mtime);
976         ft.mdate     = LittleEndian16(ft.mdate);
977         FAT_int_GetFATTimestamp(NewNode->ATime, &ft.adate, NULL, NULL);
978         ft.adate     = LittleEndian16(ft.adate);
979         ft.clusterHi = LittleEndian16((NewNode->Inode >> 16) & 0xFFFF);
980         ft.cluster   = LittleEndian16(NewNode->Inode & 0xFFFF);
981         ft.size      = LittleEndian32(NewNode->Size);
982
983         LOG("ft.name = '%.11s'", ft.name);
984
985         // -- Add entry to the directory --
986         // Locate a range of nLFNEnt + 1 free entries
987          int    end_id = -1;
988          int    range_first = 0, range_last = -1;
989         for( int id = 0; ; id ++ )
990         {
991                 if( id % eps == 0 )
992                 {
993                         if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
994                                 break;  // end of cluster chain
995                 }
996                 
997                 // End of file list, break out
998                 if( fileinfo[id%eps].name[0] == '\0' ) {
999                         if( id - range_first == nLFNEnt )
1000                                 range_last = id;
1001                         end_id = id;
1002                         break;
1003                 }
1004                 
1005                 // If an entry is occupied, clear the range
1006                 if( fileinfo[id%eps].name[0] != '\xE5' ) {
1007                         range_first = id + 1;
1008                         continue ;
1009                 }
1010                 
1011                 // Free entry, check if we have enough
1012                 if( id - range_first == nLFNEnt ) {
1013                         range_last = id;
1014                         break;
1015                 }
1016                 // Check the next one
1017         }
1018         if( range_last == -1 )
1019         {
1020                 // - If there are none, defragment the directory?
1021                 
1022                 // - Else, expand the directory
1023                 if( end_id == -1 ) {
1024                         // End of cluster chain
1025                 }
1026                 else {
1027                         // Just end of block
1028                 }
1029                 // - and if that fails, return an error
1030                 Log_Warning("FAT", "TODO: Impliment directory expansion / defragmenting");
1031                 Mutex_Release(&DirNode->Lock);
1032                 return ENOTIMPL;
1033         }
1034
1035         // Calculate the checksum used for LFN
1036         Uint8   lfn_checksum = 0;
1037         if( nLFNEnt )
1038         {
1039                 lfn_checksum = FAT_int_MakeLFNChecksum(ft.name);
1040         }
1041
1042         // Insert entries       
1043         if( range_first % eps != 0 )
1044                 FAT_int_ReadDirSector(DirNode, range_first/eps, fileinfo);
1045         for( int id = range_first; id <= range_last; id ++ )
1046         {
1047                 if( id % eps == 0 ) {
1048                         if( id != range_first )
1049                                 FAT_int_WriteDirSector(DirNode, (id-1)/eps, fileinfo);
1050                         FAT_int_ReadDirSector(DirNode, id/eps, fileinfo);
1051                 }
1052                 
1053                 if( id == range_last ) {
1054                         // Actual entry
1055                         memcpy(fileinfo + id % eps, &ft, sizeof(fat_filetable));
1056                 }
1057                 else {
1058                         // Long filename
1059                         int lfnid = (nLFNEnt - (id - range_first));
1060                         int ofs = (lfnid-1) * 13;
1061                          int    i=0, j=0;
1062                         fat_longfilename *lfnent = (void*)( fileinfo + id%eps );
1063                         
1064                         lfnent->id = 0x40 | lfnid;
1065                         lfnent->attrib = ATTR_LFN;
1066                         lfnent->type = 0;
1067                         lfnent->firstCluster = 0;
1068                         lfnent->checksum = lfn_checksum;        // ???
1069
1070                         for( i = 0; i < 13; i ++ )
1071                         {
1072                                 Uint16  wd;
1073                                 if( (wd = lfn[ofs+j]) ) j ++;
1074                                 wd = LittleEndian16(wd);
1075                                 if(i < 5)
1076                                         lfnent->name1[i    ] = wd;
1077                                 else if( i < 5+6 )
1078                                         lfnent->name2[i-5  ] = wd;
1079                                 else
1080                                         lfnent->name3[i-5-6] = wd;
1081                         }
1082                 }
1083         }
1084         FAT_int_WriteDirSector(DirNode, range_last/eps, fileinfo);
1085
1086         Mutex_Release( &DirNode->Lock );
1087         return 0;
1088 }
1089
1090 /**
1091  * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
1092  * \brief Rename / Delete a file
1093  */
1094 int FAT_Unlink(tVFS_Node *Node, const char *OldName)
1095 {
1096         tVFS_Node       *child;
1097         fat_filetable   ft;
1098         
1099         Mutex_Acquire(&Node->Lock);
1100
1101         int id = FAT_int_GetEntryByName(Node, OldName, &ft);
1102         if(id == -1) {
1103                 Mutex_Release(&Node->Lock);
1104                 return ENOTFOUND;
1105         }
1106
1107         child = FAT_int_CreateNode(Node->ImplPtr, &ft);
1108         if( !child ) {
1109                 Mutex_Release(&Node->Lock);
1110                 return EINVAL;
1111         }
1112         child->ImplInt |= FAT_FLAG_DELETE;      // Mark for deletion on close
1113
1114         // TODO: If it has a LFN, remove that too
1115
1116         // Delete from the directory
1117         ft.name[0] = '\xE5';
1118         FAT_int_WriteDirEntry(Node, id, &ft);
1119
1120         // Close child
1121         child->Type->Close( child );
1122         Mutex_Release( &Node->Lock );
1123         return EOK;
1124 }
1125 #endif

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