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

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