f639a0b62aebadf6872f3548154cad38069c3d14
[tpg/acess2.git] / KernelLand / Modules / Filesystems / FAT / dir.c
1 /*
2  * Acess2 FAT12/16/32 Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * dir.c
6  * - Directory access/manipulation code
7  */
8 #define DEBUG   1
9 #include <acess.h>
10 #include <vfs.h>
11 #include "common.h"
12
13 // === PROTOTYPES ===
14 void    FAT_int_ProperFilename(char *dest, const char *src);
15 char    *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName);
16  int    FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source);
17  int    FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source);
18
19  int    FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry);
20  int    FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
21  int    FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer);
22 #if SUPPORT_WRITE
23  int    FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
24 #endif
25 #if USE_LFN
26 Uint16  *FAT_int_GetLFN(tVFS_Node *Node, int ID);
27 void    FAT_int_DelLFN(tVFS_Node *Node, int ID);
28 #endif
29 char    *FAT_ReadDir(tVFS_Node *Node, int ID);
30 tVFS_Node       *FAT_FindDir(tVFS_Node *Node, const char *Name);
31 tVFS_Node       *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
32 #if SUPPORT_WRITE
33  int    FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
34  int    FAT_int_IsValid83Filename(const char *Name);
35  int    FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
36  int    FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);
37 #endif
38
39 // === CODE ===
40
41 /**
42  * \brief Converts a FAT directory entry name into a proper filename
43  * \param dest  Destination array (must be at least 13 bytes in size)
44  * \param src   8.3 filename (concatenated, e.g 'FILE1   TXT')
45  */
46 void FAT_int_ProperFilename(char *dest, const char *src)
47 {
48          int    inpos, outpos;
49         
50         // Name
51         outpos = 0;
52         for( inpos = 0; inpos < 8; inpos++ ) {
53                 if(src[inpos] == ' ')   break;
54                 dest[outpos++] = src[inpos];
55         }
56         inpos = 8;
57         // Check for empty extensions
58         if(src[8] != ' ')
59         {
60                 dest[outpos++] = '.';
61                 for( ; inpos < 11; inpos++)     {
62                         if(src[inpos] == ' ')   break;
63                         dest[outpos++] = src[inpos];
64                 }
65         }
66         dest[outpos++] = '\0';
67         
68         //LOG("dest='%s'", dest);
69 }
70
71 /**
72  * \fn char *FAT_int_CreateName(fat_filetable *ft, Uint8 *LongFileName)
73  * \brief Converts either a LFN or a 8.3 Name into a proper name
74  * \param ft    Pointer to the file's entry in the parent directory
75  * \param LongFileName  Long file name pointer
76  * \return Filename as a heap string
77  */
78 char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName)
79 {
80         char    *ret;
81         ENTER("pft sLongFileName", ft, LongFileName);
82         //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);
83         #if USE_LFN
84         if(LongFileName && LongFileName[0] != 0)
85         {
86                  int    len = FAT_int_ConvertUTF16_to_UTF8(NULL, LongFileName);
87                 ret = malloc( len + 1 );
88                 FAT_int_ConvertUTF16_to_UTF8((Uint8*)ret, LongFileName);
89         }
90         else
91         {
92         #endif
93                 ret = (char*) malloc(13);
94                 if( !ret ) {
95                         Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");
96                         return NULL;
97                 }
98                 FAT_int_ProperFilename(ret, ft->name);
99         #if USE_LFN
100         }
101         #endif
102         LEAVE('s', ret);
103         return ret;
104 }
105
106 #if USE_LFN
107 int FAT_int_CompareUTF16_UTF8(const Uint16 *Str16, const char *Str8)
108 {
109          int    pos16 = 0, pos8 = 0;
110         const Uint8     *str8 = (const Uint8 *)Str8;
111         
112         while( Str16[pos16] && str8[pos8] )
113         {
114                 Uint32  cp8, cp16;
115                 if( Str16[pos16] & 0x8000 ) {
116                         // Do something!
117                 }
118                 else {
119                         cp16 = Str16[pos16];
120                         pos16 ++;
121                 }
122                 pos8 += ReadUTF8(str8 + pos8, &cp8);
123         
124                 if(cp16 == cp8) continue ;
125                 
126                 if(cp16 < cp8)
127                         return -1;
128                 else
129                         return 1;
130         }
131         if(Str16[pos16] == str8[pos8])
132                 return 0;
133         if(Str16[pos16] < str8[pos8])
134                 return -1;
135         else
136                 return 1;
137 }
138
139 int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source)
140 {
141          int    len = 0;
142         for( ; *Source; Source ++ )
143         {
144                 // TODO: Decode/Reencode
145                 if( Dest )
146                         Dest[len] = *Source;
147                 len += 1;
148         }
149         if( Dest )
150                 Dest[len] = 0;
151         return len;
152 }
153
154 int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source)
155 {
156          int    len = 0;
157         for( ; *Source; Source ++ )
158         {
159                 Uint32  cp;
160                 int cpl;
161                 
162                 cpl = ReadUTF8(Source, &cp);
163                 if(cp < 0x8000) {
164                         if( Dest )
165                                 Dest[len] = cp;
166                         len ++;
167                 }
168                 else {
169                         // TODO!
170                 }
171                 Source += cpl;
172         }
173         Dest[len] = 0;
174         return len;
175 }
176
177 int FAT_int_ParseLFN(const fat_filetable *Entry, Uint16 *Buffer)
178 {
179         const fat_longfilename  *lfnInfo;
180          int    ofs;
181         
182         lfnInfo = (const void*)Entry;
183         
184         if(lfnInfo->id & 0x40) {
185                 memset(Buffer, 0, 256*2);
186         }
187         ofs = (lfnInfo->id & 0x3F) * 13 - 1;
188         if( ofs >= 255 )
189                 return -1;
190         
191         Buffer[ofs--] = lfnInfo->name3[1];      Buffer[ofs--] = lfnInfo->name3[0];
192         Buffer[ofs--] = lfnInfo->name2[5];      Buffer[ofs--] = lfnInfo->name2[4];
193         Buffer[ofs--] = lfnInfo->name2[3];      Buffer[ofs--] = lfnInfo->name2[2];
194         Buffer[ofs--] = lfnInfo->name2[1];      Buffer[ofs--] = lfnInfo->name2[0];
195         Buffer[ofs--] = lfnInfo->name1[4];      Buffer[ofs--] = lfnInfo->name1[3];
196         Buffer[ofs--] = lfnInfo->name1[2];      Buffer[ofs--] = lfnInfo->name1[1];
197         Buffer[ofs--] = lfnInfo->name1[0];
198         
199         if((lfnInfo->id&0x3F) == 1)
200                 return 1;
201         return 0;
202 }
203 #endif
204
205 int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry)
206 {
207         fat_filetable   fileinfo[16];
208         char    tmpName[13];
209         #if USE_LFN
210         Uint16  lfn[256];
211          int    lfnId = -1;
212         #endif
213
214         for( int i = 0; ; i++ )
215         {
216                 if((i & 0xF) == 0) {
217                         if(FAT_int_ReadDirSector(DirNode, i/16, fileinfo))
218                         {
219                                 LEAVE('i', -1);
220                                 return -1;
221                         }
222                 }
223                 
224                 //Check if the files are free
225                 if(fileinfo[i&0xF].name[0] == '\0')     break;  // End of List marker
226                 if(fileinfo[i&0xF].name[0] == '\xE5')   continue;       // Free entry
227                 
228                 
229                 #if USE_LFN
230                 // Long File Name Entry
231                 if(fileinfo[i & 0xF].attrib == ATTR_LFN)
232                 {
233                         if( FAT_int_ParseLFN(&fileinfo[i&0xF], lfn) )
234                                 lfnId = i+1;
235                         continue ;
236                 }
237                 // Remove LFN if it does not apply
238                 if(lfnId != i)  lfn[0] = 0;
239                 #else
240                 if(fileinfo[i&0xF].attrib == ATTR_LFN)  continue;
241                 #endif
242
243                 // Get Real Filename
244                 FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
245 //              LOG("tmpName = '%s'", tmpName);
246 //              #if DEBUG
247 //              Debug_HexDump("FAT tmpName", tmpName, strlen(tmpName));
248 //              #endif
249 /*
250                 #if DEBUG && USE_LFN
251                 if(lfnId == i)
252                 {
253                         Uint8 lfntmp[256*3+1];
254                         FAT_int_ConvertUTF16_to_UTF8(lfntmp, lfn);
255                         LOG("lfntmp = '%s'", lfntmp);
256                 }
257                 #endif
258 */
259         
260                 // Only the long name is case sensitive, 8.3 is not
261                 #if USE_LFN
262                 if(strucmp(tmpName, Name) == 0 || FAT_int_CompareUTF16_UTF8(lfn, Name) == 0)
263                 #else
264                 if(strucmp(tmpName, Name) == 0)
265                 #endif
266                 {
267                         memcpy(Entry, fileinfo + (i&0xF), sizeof(*Entry));
268                         LOG("Found %s at %i", Name, i);
269                         LEAVE('i', i);
270                         return i;
271                 }
272         }
273         
274         LEAVE('i', -1);
275         return -1;
276 }
277
278 int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry)
279 {
280          int    ents_per_sector = 512 / sizeof(fat_filetable); 
281         fat_filetable   fileinfo[ents_per_sector];
282          int    i, sector;
283
284         Mutex_Acquire(&DirNode->Lock);
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 -1;
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 Writes an entry to the disk
368  * \todo Support expanding a directory
369  * \param Node  Directory node
370  * \param ID    ID of entry to update
371  * \param Entry Entry data
372  * \return Zero on success, non-zero on error
373  */
374 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
375 {
376         Uint64  addr = 0;
377          int    tmp;
378         Uint32  cluster = 0;
379         tFAT_VolInfo    *disk = Node->ImplPtr;
380         
381         ENTER("pNode iID pEntry", Node, ID, Entry);
382         
383         tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
384         if( tmp )
385         {
386                 //TODO: Allocate a cluster
387                 cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
388                 if(cluster == -1) {
389                         Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
390                         LEAVE('i', 1);
391                         return 1;
392                 }
393                 FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
394         }
395         
396
397         LOG("addr = 0x%llx", addr);
398         
399         // Read Sector
400         VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);      // Read Dir Data
401         
402         LEAVE('i', 0);
403         return 0;
404 }
405 #endif
406
407 #if USE_LFN     
408 /**
409  * \fn Uint16 *FAT_int_GetLFN(tVFS_Node *node)
410  * \brief Return pointer to LFN cache entry
411  * \param Node  Directory node
412  * \param ID    ID of the short name
413  * \return Pointer to the LFN cache entry
414  */
415 Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID)
416 {
417         tFAT_LFNCache   *cache;
418          int    i, firstFree;
419         
420         Mutex_Acquire( &Node->Lock );
421         
422         // TODO: Thread Safety (Lock things)
423         cache = Node->Data;
424         
425         // Create a cache if it isn't there
426         if(!cache) {
427                 cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
428                 cache->NumEntries = 1;
429                 cache->Entries[0].ID = ID;
430                 cache->Entries[0].Data[0] = 0;
431                 Mutex_Release( &Node->Lock );
432                 //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
433                 return cache->Entries[0].Data;
434         }
435         
436         // Scan for this entry
437         firstFree = -1;
438         for( i = 0; i < cache->NumEntries; i++ )
439         {
440                 if( cache->Entries[i].ID == ID ) {
441                         Mutex_Release( &Node->Lock );
442                         //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
443                         return cache->Entries[i].Data;
444                 }
445                 if( cache->Entries[i].ID == -1 && firstFree == -1 )
446                         firstFree = i;
447         }
448         
449         if(firstFree == -1) {
450                 // Use `i` for temp length
451                 i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
452                 Node->Data = realloc( Node->Data, i );
453                 if( !Node->Data ) {
454                         Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
455                         Mutex_Release( &Node->Lock );
456                         return NULL;
457                 }
458                 //Log_Debug("FAT", "Realloc (%i)\n", i);
459                 cache = Node->Data;
460                 i = cache->NumEntries;
461                 cache->NumEntries ++;
462         }
463         else {
464                 i = firstFree;
465         }
466         
467         // Create new entry
468         cache->Entries[ i ].ID = ID;
469         cache->Entries[ i ].Data[0] = '\0';
470         
471         Mutex_Release( &Node->Lock );
472         //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
473         return cache->Entries[ i ].Data;
474 }
475
476 /**
477  * \fn void FAT_int_DelLFN(tVFS_Node *node)
478  * \brief Delete a LFN cache entry
479  * \param Node  Directory node
480  * \param ID    File Entry ID
481  */
482 void FAT_int_DelLFN(tVFS_Node *Node, int ID)
483 {
484         tFAT_LFNCache   *cache = Node->Data;
485          int    i;
486         
487         // Fast return
488         if(!cache)      return;
489         
490         // Scan for a current entry
491         for( i = 0; i < cache->NumEntries; i++ )
492         {
493                 if( cache->Entries[i].ID == ID )
494                         cache->Entries[i].ID = -1;
495         }
496         return ;
497 }
498 #endif
499
500 /**
501  * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
502  * \param Node  Node structure of directory
503  * \param ID    Directory position
504  * \return Filename as a heap string, NULL or VFS_SKIP
505  */
506 char *FAT_ReadDir(tVFS_Node *Node, int ID)
507 {
508         fat_filetable   fileinfo[16];   // sizeof(fat_filetable)=32, so 16 per sector
509          int    a;
510         char    *ret;
511         #if USE_LFN
512         Uint16  *lfn = NULL;
513         #endif
514         
515         ENTER("pNode iID", Node, ID);
516         
517         if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
518         {
519                 LOG("End of chain, end of dir");
520                 LEAVE('n');
521                 return NULL;
522         }
523         
524         // Offset in sector
525         a = ID % 16;
526
527         LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
528         
529         // Check if this is the last entry
530         if( fileinfo[a].name[0] == '\0' ) {
531                 Node->Size = ID;
532                 LOG("End of list");
533                 LEAVE('n');
534                 return NULL;    // break
535         }
536         
537         // Check for empty entry
538         if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
539                 LOG("Empty Entry");
540                 #if 0   // Stop on empty entry?
541                 LEAVE('n');
542                 return NULL;    // Stop
543                 #else
544                 LEAVE('p', VFS_SKIP);
545                 return VFS_SKIP;        // Skip
546                 #endif
547         }
548         
549         #if USE_LFN
550         // Get Long File Name Cache
551         if(fileinfo[a].attrib == ATTR_LFN)
552         {
553                 fat_longfilename        *lfnInfo;
554                 
555                 lfnInfo = (fat_longfilename *) &fileinfo[a];
556                 
557                 // Get cache for corresponding file
558                 // > ID + Index gets the corresponding short node
559                 lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
560
561                 a = FAT_int_ParseLFN(&fileinfo[a], lfn);
562                 if( a < 0 ) {
563                         LOG("Invalid LFN, error");
564                         LEAVE('n');
565                         return NULL;
566                 }
567
568 //              LOG("lfn = '%s'", lfn);
569                 //Log_Debug("FAT", "lfn = '%s'", lfn);
570                 LEAVE('p', VFS_SKIP);
571                 return VFS_SKIP;
572         }
573         #endif
574         
575         // Check if it is a volume entry
576         if(fileinfo[a].attrib & 0x08) {
577                 LEAVE('p', VFS_SKIP);
578                 return VFS_SKIP;
579         }
580         // Ignore .
581         if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
582                 LEAVE('p', VFS_SKIP);
583                 return VFS_SKIP;
584         }
585         // and ..
586         if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
587                 LEAVE('p', VFS_SKIP);
588                 return VFS_SKIP;
589         }
590         
591         LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
592                 fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
593                 fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
594                 fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
595         
596         #if USE_LFN
597         lfn = FAT_int_GetLFN(Node, ID);
598         //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
599         ret = FAT_int_CreateName(&fileinfo[a], lfn);
600         #else
601         ret = FAT_int_CreateName(&fileinfo[a], NULL);
602         #endif
603         
604         LEAVE('s', ret);
605         return ret;
606 }
607
608 /**
609  * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
610  * \brief Finds an entry in the current directory
611  */
612 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
613 {
614         fat_filetable   fileent;
615         
616         ENTER("pNode sname", Node, Name);       
617
618         // Fast Returns
619         if(!Name || Name[0] == '\0') {
620                 LEAVE('n');
621                 return NULL;
622         }
623
624         if( FAT_int_GetEntryByName(Node, Name, &fileent) == -1 ) {
625                 LEAVE('n');
626                 return NULL;
627         }
628         
629
630         tVFS_Node *ret = FAT_int_CreateNode(Node, &fileent);
631         LOG("Found %s as %p", Name, ret);
632         LEAVE('p', ret);
633         return ret;
634 }
635
636 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
637 {
638         tFAT_VolInfo    *disk = Root->ImplPtr;
639         tVFS_Node       *dirnode, *ret;
640         fat_filetable   ft;
641
642         ENTER("pRoot XInode", Root, Inode);
643
644         ret = FAT_int_GetNode(disk, Inode & 0xFFFFFFFF);
645         if( ret ) {
646                 if( (ret->Inode >> 32) != 0 ) {
647                         LOG("Node in cache, quick return");
648                         return ret;
649                 }
650                 else {
651                         LOG("Node cached, but incomplete");
652                         // Fall on through
653                 }
654                 ret = NULL;
655         }
656         
657         dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
658
659         int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
660         if( id != -1 ) {
661                 ret = FAT_int_CreateNode(dirnode, &ft);
662         }
663
664         dirnode->Type->Close(dirnode);
665
666         LEAVE('p', ret);
667         return ret;
668 }
669
670 #if SUPPORT_WRITE
671 /**
672  * \brief Create a new node
673  */
674 int FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
675 {
676         tFAT_VolInfo    *disk = DirNode->ImplPtr;
677          int    rv;
678         fat_filetable   ft;
679         memset(&ft, 0, sizeof(ft));
680         
681         // Allocate a cluster
682         Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
683         LOG("Cluster 0x%07x allocated", cluster);
684         
685         // Create a temporary file table entry for an empty node
686         ft.cluster = cluster & 0xFFFF;
687         ft.clusterHi = cluster >> 16;
688         ft.size = 0;
689         if( Flags & VFS_FFLAG_DIRECTORY )
690                 ft.attrib = ATTR_DIRECTORY;
691         else
692                 ft.attrib = 0;
693         
694         tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
695         if( !newnode ) {
696                 return -1;
697         }
698         LOG("newnode = %p", newnode);
699
700         // Call link
701         if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
702                 newnode->Flags |= FAT_FLAG_DELETE;
703         }
704         LOG("rv = %i", rv);
705         FAT_CloseFile(newnode);
706         return rv;
707 }
708
709 /**
710  * \brief Internal - Checks if a character is valid in an 8.3 filename
711  */
712 static inline int is_valid_83_char(char ch)
713 {
714         if( '0' <= ch && ch <= '9' )
715                 return 1;
716         if( 'A' <= ch && ch <= 'Z' )
717                 return 1;
718         return 0;
719 }
720
721 /**
722  * \brief Internal - Determines if a filename is a valid 8.3 filename
723  */
724 int FAT_int_IsValid83Filename(const char *Name)
725 {
726          int    i, j;
727         // Check filename portion
728         for( i = 0; Name[i] && i < 8; i ++ )
729         {
730                 if( Name[i] == '.' )
731                         break;
732                 if( !is_valid_83_char(Name[i]) )
733                         return 0;
734         }
735         // If the next char is not \0 or '.', it's not valid
736         if( Name[i] && Name[i++] != '.' )
737                 return 0;
738         
739         // Check the extension portion
740         for( j = 0; Name[i+j] && j < 3; j ++ )
741         {
742                 if( !is_valid_83_char(Name[i+j]) )
743                         return 0;
744         }
745         
746         // After the extension must be the end
747         if( !Name[i+j] )
748                 return 0;
749         
750         return 1;
751 }
752
753 /**
754  * \brief Create a new name for a file
755  * \note Since FAT doesn't support reference counting, this will cause double-references if
756  *       a file is hardlinked and not unlinked
757  */
758 int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
759 {
760         Uint16  lfn[256];
761         fat_filetable   ft;
762          int    nLFNEnt = 0;
763         
764         // -- Create filetable entry --
765          int    bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
766         if( bNeedsLFN )
767         {
768                 int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
769                 nLFNEnt = DivUp(lfnlen, 13);
770         
771                 // Create mangled filetable entry
772                 // - Requires checking for duplicates
773                 Log_Warning("FAT", "FAT_Link - LFN Mangling unimplimented");
774         }
775         else
776         {
777                 // Create pure filetable entry
778                 Log_Warning("FAT", "FAT_Link - Filename translation unimplimented");
779         }
780         
781         ft.size = NewNode->Size;
782
783         // -- Add entry to the directory --
784         Mutex_Acquire( &DirNode->Lock );
785
786         // Locate a range of nLFNEnt + 1 free entries
787         // - If there are none, defragment the directory?
788         // - Else, expand the directory
789         // - and if that fails, return an error
790         Log_Warning("FAT", "FAT_Link - Free entry scanning unimplimented");
791
792         Mutex_Release( &DirNode->Lock );
793         return ENOTIMPL;
794 }
795
796 /**
797  * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
798  * \brief Rename / Delete a file
799  */
800 int FAT_Unlink(tVFS_Node *Node, const char *OldName)
801 {
802         tVFS_Node       *child;
803         fat_filetable   ft;
804         
805         Mutex_Acquire(&Node->Lock);
806
807         int id = FAT_int_GetEntryByName(Node, OldName, &ft);
808         if(id == -1) {
809                 Mutex_Release(&Node->Lock);
810                 return ENOTFOUND;
811         }
812
813         child = FAT_int_CreateNode(Node->ImplPtr, &ft);
814         if( !child ) {
815                 Mutex_Release(&Node->Lock);
816                 return EINVAL;
817         }
818         child->ImplInt |= FAT_FLAG_DELETE;      // Mark for deletion on close
819
820         // TODO: If it has a LFN, remove that too
821
822         // Delete from the directory
823         ft.name[0] = '\xE9';
824         FAT_int_WriteDirEntry(Node, id, &ft);
825
826         // Close child
827         child->Type->Close( child );
828         Mutex_Release( &Node->Lock );
829         return EOK;
830 }
831 #endif

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