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

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