3 * FAT12/16/32 Driver Version (Incl LFN)
\r
5 * NOTE: This driver will only support _reading_ long file names, not
\r
6 * writing. I don't even know why I'm adding write-support. FAT sucks.
\r
9 * - LFN Is buggy in FAT_ReadDir
\r
12 * - There's hard-coded 512 byte sectors everywhere, that needs to be
\r
14 * - Thread safety is out the window with the write and LFN code
\r
17 * \todo Implement changing of the parent directory when a file is written to
\r
18 * \todo Implement file creation / deletion
\r
23 #define CACHE_FAT 0 //!< Caches the FAT in memory
\r
24 #define USE_LFN 1 //!< Enables the use of Long File Names
\r
25 #define SUPPORT_WRITE 0 //!< Enables write support
\r
28 #include <modules.h>
\r
32 #define FAT_FLAG_DIRTY 0x10000
\r
33 #define FAT_FLAG_DELETE 0x20000
\r
38 * \brief Long-Filename cache entry
\r
40 typedef struct sFAT_LFNCacheEnt
\r
43 // TODO: Handle UTF16 names correctly
\r
47 * \brief Long-Filename cache
\r
49 typedef struct sFAT_LFNCache
\r
52 tFAT_LFNCacheEnt Entries[];
\r
56 // === PROTOTYPES ===
\r
58 int FAT_Install(char **Arguments);
\r
59 tVFS_Node *FAT_InitDevice(const char *device, const char **options);
\r
60 void FAT_Unmount(tVFS_Node *Node);
\r
62 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);
\r
63 Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);
\r
65 Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);
\r
66 Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);
\r
68 void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);
\r
70 Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
\r
72 void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);
\r
73 Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
\r
76 char *FAT_ReadDir(tVFS_Node *Node, int ID);
\r
77 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);
\r
78 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
\r
80 int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
\r
81 int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);
\r
83 void FAT_CloseFile(tVFS_Node *node);
\r
86 int giFAT_MaxCachedClusters = 1024*512/4;
\r
88 // === SEMI-GLOBALS ===
\r
89 MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);
\r
90 tFAT_VolInfo gFAT_Disks[8];
\r
91 int giFAT_PartCount = 0;
\r
92 tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};
\r
96 * \fn int FAT_Install(char **Arguments)
\r
97 * \brief Install the FAT Driver
\r
99 int FAT_Install(char **Arguments)
\r
101 VFS_AddDriver( &gFAT_FSInfo );
\r
102 return MODULE_ERR_OK;
\r
106 * \brief Reads the boot sector of a disk and prepares the structures for it
\r
108 tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
\r
112 Uint32 FATSz, RootDirSectors, TotSec;
\r
113 tVFS_Node *node = NULL;
\r
114 tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];
\r
116 // Temporary Pointer
\r
117 bs = &diskInfo->bootsect;
\r
119 // Open device and read boot sector
\r
120 diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
\r
121 if(diskInfo->fileHandle == -1) {
\r
122 Log_Notice("FAT", "Unable to open device '%s'", Device);
\r
126 VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);
\r
128 if(bs->bps == 0 || bs->spc == 0) {
\r
129 Log_Notice("FAT", "Error in FAT Boot Sector");
\r
133 // FAT Type Determining
\r
134 // - From Microsoft FAT Specifcation
\r
135 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
137 if(bs->fatSz16 != 0)
\r
138 FATSz = bs->fatSz16;
\r
140 FATSz = bs->spec.fat32.fatSz32;
\r
142 if(bs->totalSect16 != 0)
\r
143 TotSec = bs->totalSect16;
\r
145 TotSec = bs->totalSect32;
\r
147 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
149 if(diskInfo->ClusterCount < 4085)
\r
150 diskInfo->type = FAT12;
\r
151 else if(diskInfo->ClusterCount < 65525)
\r
152 diskInfo->type = FAT16;
\r
154 diskInfo->type = FAT32;
\r
158 char *sFatType, *sSize;
\r
159 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
161 switch(diskInfo->type)
\r
163 case FAT12: sFatType = "FAT12"; break;
\r
164 case FAT16: sFatType = "FAT16"; break;
\r
165 case FAT32: sFatType = "FAT32"; break;
\r
166 default: sFatType = "UNKNOWN"; break;
\r
168 if(iSize <= 2*1024) {
\r
171 else if(iSize <= 2*1024*1024) {
\r
179 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
184 if(diskInfo->type == FAT32) {
\r
186 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
190 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
192 diskInfo->name[11] = '\0';
\r
194 // Compute Root directory offset
\r
195 if(diskInfo->type == FAT32)
\r
196 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
198 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
200 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
202 //Allow for Caching the FAT
\r
204 if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )
\r
207 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
208 if(diskInfo->FATCache == NULL) {
\r
209 Log_Warning("FAT", "Heap Exhausted");
\r
212 Ofs = bs->resvSectCount*512;
\r
213 if(diskInfo->type == FAT12)
\r
218 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
219 j = i & 511; //%512
\r
221 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
224 val = *((int*)(buf+j*3));
\r
225 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
226 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
229 else if(diskInfo->type == FAT16)
\r
232 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
233 if( (i & 255) == 0 ) {
\r
234 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
237 diskInfo->FATCache[i] = buf[i&255];
\r
240 else if(diskInfo->type == FAT32)
\r
243 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
244 if( (i & 127) == 0 ) {
\r
245 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
248 diskInfo->FATCache[i] = buf[i&127];
\r
251 LOG("FAT Fully Cached");
\r
253 #endif /*CACHE_FAT*/
\r
255 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
257 // Initalise inode cache for filesystem
\r
258 diskInfo->inodeHandle = Inode_GetHandle();
\r
259 LOG("Inode Cache handle is %i", diskInfo->inodeHandle);
\r
261 // == VFS Interface
\r
262 node = &diskInfo->rootNode;
\r
263 //node->Size = bs->files_in_root;
\r
265 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
266 node->ImplPtr = diskInfo; // Disk info pointer
\r
267 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
269 node->ReferenceCount = 1;
\r
271 node->UID = 0; node->GID = 0;
\r
273 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
274 node->Flags = VFS_FFLAG_DIRECTORY;
\r
275 node->CTime = node->MTime = node->ATime = now();
\r
277 node->Read = node->Write = NULL;
\r
278 node->ReadDir = FAT_ReadDir;
\r
279 node->FindDir = FAT_FindDir;
\r
281 node->Relink = FAT_Relink;
\r
282 node->MkNod = FAT_Mknod;
\r
284 node->Relink = NULL;
\r
285 node->MkNod = NULL;
\r
287 //node->Close = FAT_Unmount;
\r
289 giFAT_PartCount ++;
\r
294 * \brief Closes a mount and marks it as free
\r
295 * \param Node Mount Root
\r
297 * \todo Remove FAT Cache
\r
298 * \todo Clear LFN Cache
\r
299 * \todo Check that all files are closed and flushed
\r
301 void FAT_Unmount(tVFS_Node *Node)
\r
303 tFAT_VolInfo *disk = Node->ImplPtr;
\r
305 // Close Disk Handle
\r
306 VFS_Close( disk->fileHandle );
\r
307 // Clear Node Cache
\r
308 Inode_ClearCache(disk->inodeHandle);
\r
310 disk->fileHandle = -2;
\r
315 * \brief Converts an offset in a file into a disk address
\r
316 * \param Node File (or directory) node
\r
317 * \param Offset Offset in the file
\r
318 * \param Addr Return Address
\r
319 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
320 * is past EOC) - Not touched if the node is the root
\r
322 * \return Zero on success, non-zero on error
\r
324 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
329 tFAT_VolInfo *disk = Node->ImplPtr;
\r
331 ENTER("pNode XOffset", Node, Offset);
\r
333 cluster = Node->Inode & 0xFFFFFFF; // Cluster ID
\r
334 LOG("cluster = 0x%07x", cluster);
\r
337 // - Pre FAT32 had a reserved area for the root.
\r
338 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
340 skip = Offset / disk->BytesPerCluster;
\r
341 LOG("skip = %i", skip);
\r
342 // Skip previous clusters
\r
345 if(Cluster) *Cluster = cluster;
\r
346 cluster = FAT_int_GetFatValue(disk, cluster);
\r
347 // Check for end of cluster chain
\r
348 if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;}
\r
350 if(Cluster) *Cluster = cluster;
\r
353 // Increment by clusters in offset
\r
354 cluster += Offset / disk->BytesPerCluster;
\r
357 LOG("cluster = %08x", cluster);
\r
359 // Bounds Checking (Used to spot corruption)
\r
360 if(cluster > disk->ClusterCount + 2)
\r
362 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
363 cluster, disk->ClusterCount+2);
\r
369 // - Pre FAT32 cluster base (in sectors)
\r
370 if( cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
371 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
372 addr += cluster * disk->BytesPerCluster;
\r
375 addr = disk->firstDataSect * disk->bootsect.bps;
\r
376 addr += (cluster - 2) * disk->BytesPerCluster;
\r
378 // In-cluster offset
\r
379 addr += Offset % disk->BytesPerCluster;
\r
381 LOG("addr = 0x%08x", addr);
\r
388 * ====================
\r
390 * ====================
\r
393 * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
\r
394 * \brief Fetches a value from the FAT
\r
396 Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
\r
400 ENTER("pDisk xCluster", Disk, cluster);
\r
401 Mutex_Acquire( &Disk->lFAT );
\r
403 if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
\r
405 val = Disk->FATCache[cluster];
\r
406 if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;
\r
407 if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;
\r
408 if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;
\r
413 ofs = Disk->bootsect.resvSectCount*512;
\r
414 if(Disk->type == FAT12) {
\r
415 VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);
\r
416 val = (cluster & 1 ? val>>12 : val & 0xFFF);
\r
417 if(val == EOC_FAT12) val = -1;
\r
418 } else if(Disk->type == FAT16) {
\r
419 VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);
\r
420 if(val == EOC_FAT16) val = -1;
\r
422 VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);
\r
423 if(val == EOC_FAT32) val = -1;
\r
427 #endif /*CACHE_FAT*/
\r
428 Mutex_Release( &Disk->lFAT );
\r
435 * \brief Allocate a new cluster
\r
437 Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)
\r
439 Uint32 ret = Previous;
\r
441 if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
\r
446 for(ret = Previous; ret < Disk->ClusterCount; ret++)
\r
448 if(Disk->FATCache[ret] == 0)
\r
451 for(ret = 0; ret < Previous; ret++)
\r
453 if(Disk->FATCache[ret] == 0)
\r
457 RELEASE(Disk->lFAT);
\r
463 case FAT12: eoc = EOC_FAT12; break;
\r
464 case FAT16: eoc = EOC_FAT16; break;
\r
465 case FAT32: eoc = EOC_FAT32; break;
\r
469 Disk->FATCache[ret] = eoc;
\r
470 Disk->FATCache[Previous] = ret;
\r
472 RELEASE(Disk->lFAT);
\r
479 Uint32 ofs = Disk->bootsect.resvSectCount*512;
\r
480 Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");
\r
486 VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
487 if( Previous & 1 ) {
\r
495 VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
497 VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);
\r
498 if( Cluster & 1 ) {
\r
506 VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);
\r
509 VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
\r
510 VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);
\r
513 VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
\r
514 VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);
\r
524 * \brief Free's a cluster
\r
525 * \return The original contents of the cluster
\r
527 Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)
\r
531 if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
\r
535 ret = Disk->FATCache[Cluster];
\r
536 Disk->FATCache[Cluster] = 0;
\r
538 RELEASE(Disk->lFAT);
\r
544 Uint32 ofs = Disk->bootsect.resvSectCount*512;
\r
549 VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
\r
550 if( Cluster & 1 ) {
\r
551 ret = val & 0xFFF0000;
\r
558 VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
561 VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
\r
563 VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
\r
566 VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
\r
568 VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
\r
571 RELEASE(Disk->lFAT);
\r
575 if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;
\r
576 if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;
\r
577 if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;
\r
583 * ====================
\r
585 * ====================
\r
588 * \brief Read a cluster
\r
589 * \param Disk Disk (Volume) to read from
\r
590 * \param Length Length to read
\r
591 * \param Buffer Destination for read data
\r
593 void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)
\r
595 ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);
\r
598 (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
\r
599 * Disk->bootsect.bps,
\r
606 /* ====================
\r
608 * ====================
\r
611 * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)
\r
612 * \brief Reads data from a specified file
\r
614 Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
616 int preSkip, count;
\r
617 Uint64 final_bytes;
\r
618 int i, cluster, pos;
\r
619 tFAT_VolInfo *disk = Node->ImplPtr;
\r
620 char tmpBuf[disk->BytesPerCluster];
\r
621 int bpc = disk->BytesPerCluster;
\r
623 ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer);
\r
625 // Sanity Check offset
\r
626 if(Offset > Node->Size) {
\r
627 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);
\r
632 // Cluster is stored in the low 32-bits of the Inode field
\r
633 cluster = Node->Inode & 0xFFFFFFFF;
\r
636 if(Offset + Length > Node->Size) {
\r
637 LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
638 Offset, Length, Node->Size, Node->Size - Offset);
\r
639 Length = Node->Size - Offset;
\r
642 // Skip previous clusters
\r
643 preSkip = Offset / bpc;
\r
645 LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);
\r
646 for(i = preSkip; i--; )
\r
648 cluster = FAT_int_GetFatValue(disk, cluster);
\r
649 if(cluster == -1) {
\r
650 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
656 // Reading from within one cluster
\r
657 if((int)Offset + (int)Length <= bpc)
\r
659 LOG("single cluster only");
\r
660 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
661 memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );
\r
662 LEAVE('X', Length);
\r
666 // Align read to a cluster
\r
669 pos = bpc - Offset;
\r
670 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
671 memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );
\r
672 LOG("pos = %i, Reading the rest of the clusters");
\r
673 // Get next cluster in the chain
\r
674 cluster = FAT_int_GetFatValue(disk, cluster);
\r
675 if(cluster == -1) {
\r
676 Log_Warning("FAT", "Read past End of Cluster Chain (Align)");
\r
684 // Get Count of Clusters to read
\r
685 // count = DivMod64U(Length - pos, bpc, &final_bytes);
\r
686 count = (Length - pos) / bpc;
\r
687 final_bytes = (Length - pos) % bpc;
\r
688 LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);
\r
690 // Read the rest of the cluster data
\r
691 for( ; count; count -- )
\r
693 if(cluster == -1) {
\r
694 Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");
\r
699 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));
\r
701 // Get next cluster in the chain
\r
702 cluster = FAT_int_GetFatValue(disk, cluster);
\r
705 if( final_bytes > 0 )
\r
707 if(cluster == -1) {
\r
708 Log_Warning("FAT", "Read past End of Cluster Chain (Final)");
\r
712 // Read final cluster
\r
713 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );
\r
714 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );
\r
718 //Debug_HexDump("FAT_Read", Buffer, Length);
\r
721 LEAVE('X', Length);
\r
727 * \brief Write a cluster to disk
\r
729 void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)
\r
731 ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);
\r
734 (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
\r
735 * Disk->bootsect.bps,
\r
736 Disk->BytesPerCluster,
\r
743 * \brief Write to a file
\r
744 * \param Node File Node
\r
745 * \param Offset Offset within file
\r
746 * \param Length Size of data to write
\r
747 * \param Buffer Data source
\r
749 Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
751 tFAT_VolInfo *disk = Node->ImplPtr;
\r
752 char tmpBuf[disk->BytesPerCluster];
\r
753 int remLength = Length;
\r
754 Uint32 cluster, tmpCluster;
\r
755 int bNewCluster = 0;
\r
757 if(Offset > Node->Size) return 0;
\r
760 cluster = Node->Inode & 0xFFFFFFFF;
\r
761 while( Offset > disk->BytesPerCluster )
\r
763 cluster = FAT_int_GetFatValue( disk, cluster );
\r
764 if(cluster == -1) {
\r
765 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
768 Offset -= disk->BytesPerCluster;
\r
770 if( Offset == disk->BytesPerCluster )
\r
772 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
775 Offset -= disk->BytesPerCluster;
\r
778 if( Offset + Length < disk->BytesPerCluster )
\r
780 char tmpBuf[disk->BytesPerCluster];
\r
782 // Read-Modify-Write
\r
783 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
784 memcpy( tmpBuf + Offset, Buffer, Length );
\r
785 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
790 // Clean up changes within a cluster
\r
793 // Read-Modify-Write
\r
794 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
795 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
796 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
798 remLength -= disk->BytesPerCluster - Offset;
\r
799 Buffer += disk->BytesPerCluster - Offset;
\r
801 // Get next cluster (allocating if needed)
\r
802 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
803 if(tmpCluster == -1) {
\r
804 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
805 if( tmpCluster == 0 ) {
\r
806 return Length - remLength;
\r
809 cluster = tmpCluster;
\r
812 while( remLength > disk->BytesPerCluster )
\r
814 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
815 Buffer += disk->BytesPerCluster;
\r
817 // Get next cluster (allocating if needed)
\r
818 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
819 if(tmpCluster == -1) {
\r
821 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
822 if( tmpCluster == 0 ) {
\r
823 return Length - remLength;
\r
826 cluster = tmpCluster;
\r
830 tmpBuf = malloc( disk->BytesPerCluster );
\r
832 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
834 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
835 memcpy( tmpBuf, Buffer, remLength );
\r
836 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
843 /* ====================
\r
844 * File Names & Nodes
\r
845 * ====================
\r
848 * \brief Converts a FAT directory entry name into a proper filename
\r
849 * \param dest Destination array (must be at least 13 bytes in size)
\r
850 * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')
\r
852 void FAT_int_ProperFilename(char *dest, const char *src)
\r
858 for( inpos = 0; inpos < 8; inpos++ ) {
\r
859 if(src[inpos] == ' ') break;
\r
860 dest[outpos++] = src[inpos];
\r
863 // Check for empty extensions
\r
866 dest[outpos++] = '.';
\r
867 for( ; inpos < 11; inpos++) {
\r
868 if(src[inpos] == ' ') break;
\r
869 dest[outpos++] = src[inpos];
\r
872 dest[outpos++] = '\0';
\r
874 //LOG("dest='%s'", dest);
\r
878 * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)
\r
879 * \brief Converts either a LFN or a 8.3 Name into a proper name
\r
880 * \param ft Pointer to the file's entry in the parent directory
\r
881 * \param LongFileName Long file name pointer
\r
882 * \return Filename as a heap string
\r
884 char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)
\r
887 ENTER("pft sLongFileName", ft, LongFileName);
\r
888 //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);
\r
890 if(LongFileName && LongFileName[0] != '\0')
\r
892 ret = strdup(LongFileName);
\r
897 ret = (char*) malloc(13);
\r
899 Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");
\r
902 FAT_int_ProperFilename(ret, ft->name);
\r
911 * \brief Creates a tVFS_Node structure for a given file entry
\r
912 * \param Parent Parent directory VFS node
\r
913 * \param Entry File table entry for the new node
\r
914 * \param Pos Position in the parent of the new node
\r
916 tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos)
\r
920 tFAT_VolInfo *disk = Parent->ImplPtr;
\r
922 ENTER("pParent pFT", Parent, Entry);
\r
923 LOG("disk = %p", disk);
\r
925 memset(&node, 0, sizeof(tVFS_Node));
\r
928 // 0-27: Cluster, 32-59: Parent Cluster
\r
929 node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);
\r
930 LOG("node.Inode = %llx", node.Inode);
\r
931 // Position in parent directory
\r
932 node.ImplInt = Pos & 0xFFFF;
\r
934 node.ImplPtr = disk;
\r
935 node.Size = Entry->size;
\r
936 LOG("Entry->size = %i", Entry->size);
\r
938 node.UID = 0; node.GID = 0;
\r
942 if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;
\r
943 if(Entry->attrib & ATTR_READONLY) {
\r
944 node.Flags |= VFS_FFLAG_READONLY;
\r
945 node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X
\r
948 node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX
\r
951 // Create timestamps
\r
952 node.ATime = timestamp(0,0,0,
\r
953 ((Entry->adate&0x1F) - 1), // Days
\r
954 ((Entry->adate&0x1E0) - 1), // Months
\r
955 1980+((Entry->adate&0xFF00)>>8) // Years
\r
958 node.CTime = Entry->ctimems * 10; // Miliseconds
\r
959 node.CTime += timestamp(
\r
960 ((Entry->ctime&0x1F)<<1), // Seconds
\r
961 ((Entry->ctime&0x3F0)>>5), // Minutes
\r
962 ((Entry->ctime&0xF800)>>11), // Hours
\r
963 ((Entry->cdate&0x1F)-1), // Days
\r
964 ((Entry->cdate&0x1E0)-1), // Months
\r
965 1980+((Entry->cdate&0xFF00)>>8) // Years
\r
968 node.MTime = timestamp(
\r
969 ((Entry->mtime&0x1F)<<1), // Seconds
\r
970 ((Entry->mtime&0x3F0)>>5), // Minutes
\r
971 ((Entry->mtime&0xF800)>>11), // Hours
\r
972 ((Entry->mdate&0x1F)-1), // Days
\r
973 ((Entry->mdate&0x1E0)-1), // Months
\r
974 1980+((Entry->mdate&0xFF00)>>8) // Years
\r
978 if(node.Flags & VFS_FFLAG_DIRECTORY) {
\r
979 //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);
\r
980 node.ReadDir = FAT_ReadDir;
\r
981 node.FindDir = FAT_FindDir;
\r
983 node.MkNod = FAT_Mknod;
\r
984 node.Relink = FAT_Relink;
\r
989 node.Read = FAT_Read;
\r
991 node.Write = FAT_Write;
\r
994 node.Close = FAT_CloseFile;
\r
996 ret = Inode_CacheNode(disk->inodeHandle, &node);
\r
1002 * ====================
\r
1004 * ====================
\r
1008 * \brief Reads a sector from the disk
\r
1009 * \param Node Directory node to read
\r
1010 * \param Sector Sector number in the directory to read
\r
1011 * \param Buffer Destination buffer for the read data
\r
1013 int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
\r
1016 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1018 ENTER("pNode iSector pEntry", Node, Sector, Buffer);
\r
1021 if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
\r
1027 LOG("addr = 0x%llx", addr);
\r
1029 if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)
\r
1041 * \brief Writes an entry to the disk
\r
1042 * \todo Support expanding a directory
\r
1043 * \param Node Directory node
\r
1044 * \param ID ID of entry to update
\r
1045 * \param Entry Entry data
\r
1046 * \return Zero on success, non-zero on error
\r
1048 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
\r
1052 Uint32 cluster = 0;
\r
1053 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1055 ENTER("pNode iID pEntry", Node, ID, Entry);
\r
1057 tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
\r
1060 //TODO: Allocate a cluster
\r
1061 cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
\r
1062 if(cluster == -1) {
\r
1063 Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
\r
1067 FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
\r
1071 LOG("addr = 0x%llx", addr);
\r
1074 VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data
\r
1083 * \fn char *FAT_int_GetLFN(tVFS_Node *node)
\r
1084 * \brief Return pointer to LFN cache entry
\r
1085 * \param Node Directory node
\r
1086 * \param ID ID of the short name
\r
1087 * \return Pointer to the LFN cache entry
\r
1089 char *FAT_int_GetLFN(tVFS_Node *Node, int ID)
\r
1091 tFAT_LFNCache *cache;
\r
1094 Mutex_Acquire( &Node->Lock );
\r
1096 // TODO: Thread Safety (Lock things)
\r
1097 cache = Node->Data;
\r
1099 // Create a cache if it isn't there
\r
1101 cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
\r
1102 cache->NumEntries = 1;
\r
1103 cache->Entries[0].ID = ID;
\r
1104 cache->Entries[0].Data[0] = '\0';
\r
1105 Mutex_Release( &Node->Lock );
\r
1106 //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
\r
1107 return cache->Entries[0].Data;
\r
1110 // Scan for this entry
\r
1112 for( i = 0; i < cache->NumEntries; i++ )
\r
1114 if( cache->Entries[i].ID == ID ) {
\r
1115 Mutex_Release( &Node->Lock );
\r
1116 //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
\r
1117 return cache->Entries[i].Data;
\r
1119 if( cache->Entries[i].ID == -1 && firstFree == -1 )
\r
1123 if(firstFree == -1) {
\r
1124 // Use `i` for temp length
\r
1125 i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
\r
1126 Node->Data = realloc( Node->Data, i );
\r
1127 if( !Node->Data ) {
\r
1128 Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
\r
1129 Mutex_Release( &Node->Lock );
\r
1132 //Log_Debug("FAT", "Realloc (%i)\n", i);
\r
1133 cache = Node->Data;
\r
1134 i = cache->NumEntries;
\r
1135 cache->NumEntries ++;
\r
1141 // Create new entry
\r
1142 cache->Entries[ i ].ID = ID;
\r
1143 cache->Entries[ i ].Data[0] = '\0';
\r
1145 Mutex_Release( &Node->Lock );
\r
1146 //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
\r
1147 return cache->Entries[ i ].Data;
\r
1151 * \fn void FAT_int_DelLFN(tVFS_Node *node)
\r
1152 * \brief Delete a LFN cache entry
\r
1153 * \param Node Directory node
\r
1154 * \param ID File Entry ID
\r
1156 void FAT_int_DelLFN(tVFS_Node *Node, int ID)
\r
1158 tFAT_LFNCache *cache = Node->Data;
\r
1162 if(!cache) return;
\r
1164 // Scan for a current entry
\r
1165 for( i = 0; i < cache->NumEntries; i++ )
\r
1167 if( cache->Entries[i].ID == ID )
\r
1168 cache->Entries[i].ID = -1;
\r
1175 * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
\r
1176 * \param Node Node structure of directory
\r
1177 * \param ID Directory position
\r
1178 * \return Filename as a heap string, NULL or VFS_SKIP
\r
1180 char *FAT_ReadDir(tVFS_Node *Node, int ID)
\r
1182 fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector
\r
1189 ENTER("pNode iID", Node, ID);
\r
1191 if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
\r
1193 LOG("End of chain, end of dir");
\r
1198 // Offset in sector
\r
1201 LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
\r
1203 // Check if this is the last entry
\r
1204 if( fileinfo[a].name[0] == '\0' ) {
\r
1206 LOG("End of list");
\r
1208 return NULL; // break
\r
1211 // Check for empty entry
\r
1212 if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
\r
1213 LOG("Empty Entry");
\r
1214 #if 0 // Stop on empty entry?
\r
1216 return NULL; // Stop
\r
1218 LEAVE('p', VFS_SKIP);
\r
1219 return VFS_SKIP; // Skip
\r
1224 // Get Long File Name Cache
\r
1225 if(fileinfo[a].attrib == ATTR_LFN)
\r
1227 fat_longfilename *lfnInfo;
\r
1229 lfnInfo = (fat_longfilename *) &fileinfo[a];
\r
1231 // Get cache for corresponding file
\r
1232 // > ID + Index gets the corresponding short node
\r
1233 lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
\r
1235 // Bit 6 indicates the start of an entry
\r
1236 if(lfnInfo->id & 0x40) memset(lfn, 0, 256);
\r
1238 a = ((lfnInfo->id & 0x3F) - 1) * 13;
\r
1239 //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a);
\r
1241 // Sanity Check (FAT implementations should not allow >255 character names)
\r
1242 if(a > 255) return VFS_SKIP;
\r
1244 // Append new bytes
\r
1245 lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1];
\r
1246 lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3];
\r
1247 lfn[a+ 4] = lfnInfo->name1[4];
\r
1248 lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1];
\r
1249 lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3];
\r
1250 lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5];
\r
1251 lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1];
\r
1252 LOG("lfn = '%s'", lfn);
\r
1253 //Log_Debug("FAT", "lfn = '%s'", lfn);
\r
1254 LEAVE('p', VFS_SKIP);
\r
1259 // Check if it is a volume entry
\r
1260 if(fileinfo[a].attrib & 0x08) {
\r
1261 LEAVE('p', VFS_SKIP);
\r
1265 if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
\r
1266 LEAVE('p', VFS_SKIP);
\r
1270 if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
\r
1271 LEAVE('p', VFS_SKIP);
\r
1275 LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
\r
1276 fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
\r
1277 fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
\r
1278 fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
\r
1281 lfn = FAT_int_GetLFN(Node, ID);
\r
1282 //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
\r
1283 ret = FAT_int_CreateName(&fileinfo[a], lfn);
\r
1285 ret = FAT_int_CreateName(&fileinfo[a], NULL);
\r
1293 * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
\r
1294 * \brief Finds an entry in the current directory
\r
1296 tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
\r
1298 fat_filetable fileinfo[16];
\r
1301 fat_longfilename *lfnInfo;
\r
1303 int lfnPos=255, lfnId = -1;
\r
1306 tVFS_Node *tmpNode;
\r
1307 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1310 ENTER("pNode sname", Node, Name);
\r
1313 if(!Name || Name[0] == '\0') {
\r
1318 for( i = 0; ; i++ )
\r
1320 if((i & 0xF) == 0) {
\r
1321 if(FAT_int_ReadDirSector(Node, i/16, fileinfo))
\r
1328 //Check if the files are free
\r
1329 if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker
\r
1330 if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry
\r
1334 // Long File Name Entry
\r
1335 if(fileinfo[i & 0xF].attrib == ATTR_LFN)
\r
1337 lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];
\r
1338 if(lfnInfo->id & 0x40) {
\r
1339 memset(lfn, 0, 256);
\r
1340 lfnPos = (lfnInfo->id & 0x3F) * 13 - 1;
\r
1342 // Sanity check the position so we don't overflow
\r
1345 lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];
\r
1346 lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];
\r
1347 lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];
\r
1348 lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];
\r
1349 lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];
\r
1350 lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];
\r
1351 lfn[lfnPos--] = lfnInfo->name1[0];
\r
1352 if((lfnInfo->id&0x3F) == 1)
\r
1359 // Remove LFN if it does not apply
\r
1360 if(lfnId != i) lfn[0] = '\0';
\r
1362 if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;
\r
1364 // Get Real Filename
\r
1365 FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
\r
1366 LOG("tmpName = '%s'", tmpName);
\r
1368 // Only the long name is case sensitive, 8.3 is not
\r
1370 if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0)
\r
1372 if(strucmp(tmpName, Name) == 0)
\r
1375 cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);
\r
1376 tmpNode = Inode_GetCache(disk->inodeHandle, cluster);
\r
1377 if(tmpNode == NULL) // Node is not cached
\r
1379 tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i);
\r
1381 LEAVE('p', tmpNode);
\r
1393 tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
\r
1395 tFAT_VolInfo *disk = Root->ImplPtr;
\r
1396 int ents_per_sector = 512 / sizeof(fat_filetable);
\r
1397 fat_filetable fileinfo[ents_per_sector];
\r
1398 int sector = 0, i;
\r
1399 tVFS_Node stub_node;
\r
1401 ENTER("pRoot XInode", Root, Inode);
\r
1403 stub_node.ImplPtr = disk;
\r
1404 stub_node.Size = -1;
\r
1405 stub_node.Inode = Inode >> 32;
\r
1407 for( i = 0; ; i ++ )
\r
1409 if( i == 0 || i == ents_per_sector )
\r
1411 if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo))
\r
1413 LOG("ReadDirSector failed");
\r
1421 // Check for free/end of list
\r
1422 if(fileinfo[i].name[0] == '\0') break; // End of List marker
\r
1423 if(fileinfo[i].name[0] == '\xE5') continue; // Free entry
\r
1425 if(fileinfo[i].attrib == ATTR_LFN) continue;
\r
1427 LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);
\r
1431 FAT_int_ProperFilename(tmpName, fileinfo[i].name);
\r
1432 LOG("tmpName = '%s'", tmpName);
\r
1437 if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue;
\r
1438 if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue;
\r
1440 LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i));
\r
1442 LOG("sector = %i, i = %i", sector, i);
\r
1449 * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)
\r
1450 * \brief Create a new node
\r
1452 int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)
\r
1458 * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
\r
1459 * \brief Rename / Delete a file
\r
1461 int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
\r
1464 fat_filetable ft = {0};
\r
1467 child = FAT_FindDir(Node, OldName);
\r
1468 if(!child) return ENOTFOUND;
\r
1471 if( NewName == NULL )
\r
1473 child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close
\r
1475 // Delete from the directory
\r
1476 ft.name[0] = '\xE9';
\r
1477 FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);
\r
1485 Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",
\r
1486 Node, OldName, NewName);
\r
1491 child->Close( child );
\r
1497 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
1498 * \brief Close an open file
\r
1500 void FAT_CloseFile(tVFS_Node *Node)
\r
1502 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1503 if(Node == NULL) return ;
\r
1506 // Update the node if it's dirty (don't bother if it's marked for
\r
1508 if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
1510 tFAT_VolInfo buf[16];
\r
1511 tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];
\r
1513 FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);
\r
1514 ft->size = Node->Size;
\r
1515 // TODO: update adate, mtime, mdate
\r
1516 FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);
\r
1518 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
1522 // TODO: Make this more thread safe somehow, probably by moving the
\r
1523 // Inode_UncacheNode higher up and saving the cluster value somewhere
\r
1524 if( Node->ReferenceCount == 1 )
\r
1528 if( Node->ImplInt & FAT_FLAG_DELETE ) {
\r
1529 // Since the node is marked, we only need to remove it's data
\r
1530 Uint32 cluster = Node->Inode & 0xFFFFFFFF;
\r
1531 while( cluster != -1 )
\r
1532 cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster);
\r
1537 Inode_UncacheNode(disk->inodeHandle, Node->Inode);
\r