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

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