3 * FAT12/16/32 Driver Version (Incl LFN)
\r
6 * \todo Implement changing of the parent directory when a file is written to
\r
7 * \todo Implement file creation / deletion
\r
12 #define CACHE_FAT 1 //!< Caches the FAT in memory
\r
13 #define USE_LFN 1 //!< Enables the use of Long File Names
\r
16 #include <modules.h>
\r
20 #define FAT_FLAG_DIRTY 0x10000
\r
21 #define FAT_FLAG_DELETE 0x20000
\r
25 typedef struct s_lfncache
\r
31 struct s_lfncache *Next;
\r
35 // === PROTOTYPES ===
\r
36 int FAT_Install(char **Arguments);
\r
37 tVFS_Node *FAT_InitDevice(char *device, char **options);
\r
38 void FAT_Unmount(tVFS_Node *Node);
\r
40 Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);
\r
41 Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);
\r
43 void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);
\r
44 void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);
\r
46 Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
\r
47 Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
\r
48 char *FAT_ReadDir(tVFS_Node *Node, int ID);
\r
49 tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *Name);
\r
50 int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags);
\r
51 int FAT_Relink(tVFS_Node *node, char *OldName, char *NewName);
\r
52 void FAT_CloseFile(tVFS_Node *node);
\r
54 // === SEMI-GLOBALS ===
\r
55 MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);
\r
56 tFAT_VolInfo gFAT_Disks[8];
\r
57 int giFAT_PartCount = 0;
\r
59 t_lfncache *fat_lfncache;
\r
61 tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, NULL};
\r
65 * \fn int FAT_Install(char **Arguments)
\r
68 int FAT_Install(char **Arguments)
\r
70 VFS_AddDriver( &gFAT_FSInfo );
\r
71 return MODULE_ERR_OK;
\r
75 * \fn tVFS_Node *FAT_InitDevice(char *Device, char **Options)
\r
76 * \brief Reads the boot sector of a disk and prepares the structures for it
\r
78 tVFS_Node *FAT_InitDevice(char *Device, char **Options)
\r
82 Uint32 FATSz, RootDirSectors, TotSec;
\r
83 tVFS_Node *node = NULL;
\r
84 tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];
\r
86 // Temporary Pointer
\r
87 bs = &diskInfo->bootsect;
\r
89 //Open device and read boot sector
\r
90 diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
\r
91 if(diskInfo->fileHandle == -1) {
\r
92 Log_Notice("FAT", "Unable to open device '%s'", Device);
\r
96 VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);
\r
98 if(bs->bps == 0 || bs->spc == 0) {
\r
99 Log_Notice("FAT", "Error in FAT Boot Sector\n");
\r
103 // FAT Type Determining
\r
104 // - From Microsoft FAT Specifcation
\r
105 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
107 if(bs->fatSz16 != 0) FATSz = bs->fatSz16;
\r
108 else FATSz = bs->spec.fat32.fatSz32;
\r
110 if(bs->totalSect16 != 0) TotSec = bs->totalSect16;
\r
111 else TotSec = bs->totalSect32;
\r
113 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
115 if(diskInfo->ClusterCount < 4085)
\r
116 diskInfo->type = FAT12;
\r
117 else if(diskInfo->ClusterCount < 65525)
\r
118 diskInfo->type = FAT16;
\r
120 diskInfo->type = FAT32;
\r
124 char *sFatType, *sSize;
\r
125 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
127 switch(diskInfo->type)
\r
129 case FAT12: sFatType = "FAT12"; break;
\r
130 case FAT16: sFatType = "FAT16"; break;
\r
131 case FAT32: sFatType = "FAT32"; break;
\r
132 default: sFatType = "UNKNOWN"; break;
\r
134 if(iSize <= 2*1024) {
\r
137 else if(iSize <= 2*1024*1024) {
\r
145 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
150 if(diskInfo->type == FAT32) {
\r
152 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
156 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
158 diskInfo->name[11] = '\0';
\r
160 // Compute Root directory offset
\r
161 if(diskInfo->type == FAT32)
\r
162 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
164 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
166 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
168 //Allow for Caching the FAT
\r
172 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
173 if(diskInfo->FATCache == NULL) {
\r
174 Log_Warning("FAT", "Heap Exhausted\n");
\r
177 Ofs = bs->resvSectCount*512;
\r
178 if(diskInfo->type == FAT12)
\r
183 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
184 j = i & 511; //%512
\r
186 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
189 val = *((int*)(buf+j*3));
\r
190 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
191 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
194 else if(diskInfo->type == FAT16)
\r
197 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
198 if( (i & 255) == 0 ) {
\r
199 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
202 diskInfo->FATCache[i] = buf[i&255];
\r
205 else if(diskInfo->type == FAT32)
\r
208 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
209 if( (i & 127) == 0 ) {
\r
210 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
213 diskInfo->FATCache[i] = buf[i&127];
\r
216 LOG("FAT Fully Cached");
\r
218 #endif /*CACHE_FAT*/
\r
220 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
222 // Initalise inode cache for filesystem
\r
223 diskInfo->inodeHandle = Inode_GetHandle();
\r
224 LOG("Inode Cache handle is %i", diskInfo->inodeHandle);
\r
226 // == VFS Interface
\r
227 node = &diskInfo->rootNode;
\r
228 node->Size = bs->files_in_root;
\r
229 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
230 node->ImplPtr = diskInfo; // Disk info pointer
\r
231 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
233 node->ReferenceCount = 1;
\r
235 node->UID = 0; node->GID = 0;
\r
237 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
238 node->Flags = VFS_FFLAG_DIRECTORY;
\r
239 node->CTime = node->MTime = node->ATime = now();
\r
241 node->Read = node->Write = NULL;
\r
242 node->ReadDir = FAT_ReadDir;
\r
243 node->FindDir = FAT_FindDir;
\r
244 node->Relink = FAT_Relink;
\r
245 node->MkNod = FAT_Mknod;
\r
246 //node->Close = FAT_Unmount;
\r
248 giFAT_PartCount ++;
\r
253 * \brief Closes a mount and marks it as free
\r
254 * \param Node Mount Root
\r
256 * \todo Remove FAT Cache
\r
257 * \todo Clear LFN Cache
\r
258 * \todo Check that all files are closed and flushed
\r
260 void FAT_Unmount(tVFS_Node *Node)
\r
262 tFAT_VolInfo *disk = Node->ImplPtr;
\r
264 // Close Disk Handle
\r
265 VFS_Close( disk->fileHandle );
\r
266 // Clear Node Cache
\r
267 Inode_ClearCache(disk->inodeHandle);
\r
269 disk->fileHandle = -2;
\r
274 * \brief Converts an offset in a file into a disk address
\r
275 * \param Node File (or directory) node
\r
276 * \param Offset Offset in the file
\r
277 * \param Addr Return Address
\r
278 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
279 * is past EOC) - Not touched if the node is the root
\r
281 * \return Zero on success, non-zero on error
\r
283 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
288 tFAT_VolInfo *disk = Node->ImplPtr;
\r
290 ENTER("pNode XOffset", Node, Offset);
\r
292 cluster = Node->Inode & 0xFFFFFFFF; // Cluster ID
\r
295 // - Pre FAT32 had a reserved area for the root.
\r
296 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
298 skip = Offset / disk->BytesPerCluster;
\r
299 // Skip previous clusters
\r
302 if(Cluster) *Cluster = cluster;
\r
303 cluster = FAT_int_GetFatValue(disk, cluster);
\r
304 // Check for end of cluster chain
\r
305 if(cluster == -1) { LEAVE('i', 1); return 1;}
\r
307 if(Cluster) *Cluster = cluster;
\r
310 // Bounds Checking (Used to spot corruption)
\r
311 if(cluster > disk->ClusterCount + 2)
\r
313 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
314 cluster, disk->ClusterCount+2);
\r
320 // - Pre FAT32 cluster base (in sectors)
\r
321 if( cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
322 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
323 addr += cluster * disk->BytesPerCluster;
\r
326 addr = disk->firstDataSect;
\r
327 addr += (cluster - 2) * disk->BytesPerCluster;
\r
329 addr += Offset % disk->BytesPerCluster;
\r
337 * ====================
\r
339 * ====================
\r
342 * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
\r
343 * \brief Fetches a value from the FAT
\r
345 Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
\r
349 Uint32 ofs = Disk->bootsect.resvSectCount*512;
\r
351 ENTER("pDisk xCluster", Disk, cluster);
\r
352 LOCK( &Disk->lFAT );
\r
354 val = Disk->FATCache[cluster];
\r
355 if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;
\r
356 if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;
\r
357 if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;
\r
359 if(Disk->type == FAT12) {
\r
360 VFS_ReadAt(Disk->fileHandle, ofs+(cluster>>1)*3, 3, &val);
\r
361 val = (cluster&1 ? val&0xFFF : val>>12);
\r
362 if(val == EOC_FAT12) val = -1;
\r
363 } else if(Disk->type == FAT16) {
\r
364 VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);
\r
365 if(val == EOC_FAT16) val = -1;
\r
367 VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);
\r
368 if(val == EOC_FAT32) val = -1;
\r
370 #endif /*CACHE_FAT*/
\r
371 RELEASE( &Disk->lFAT );
\r
377 * \brief Allocate a new cluster
\r
379 Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)
\r
381 Uint32 ret = Previous;
\r
386 for(ret = Previous; ret < Disk->ClusterCount; ret++)
\r
388 if(Disk->FATCache[ret] == 0)
\r
391 for(ret = 0; ret < Previous; ret++)
\r
393 if(Disk->FATCache[ret] == 0)
\r
397 RELEASE(Disk->lFAT);
\r
403 case FAT12: eoc = EOC_FAT12; break;
\r
404 case FAT16: eoc = EOC_FAT16; break;
\r
405 case FAT32: eoc = EOC_FAT32; break;
\r
409 Disk->FATCache[ret] = eoc;
\r
410 Disk->FATCache[Previous] = ret;
\r
412 RELEASE(Disk->lFAT);
\r
417 Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");
\r
423 VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
424 if( Previous & 1 ) {
\r
432 VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
434 VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);
\r
435 if( Cluster & 1 ) {
\r
443 VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);
\r
446 VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
\r
447 VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);
\r
450 VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
\r
451 VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);
\r
459 * \brief Free's a cluster
\r
460 * \return The original contents of the cluster
\r
462 Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)
\r
468 ret = Disk->FATCache[Cluster];
\r
469 Disk->FATCache[Cluster] = 0;
\r
471 RELEASE(Disk->lFAT);
\r
478 VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
\r
479 if( Cluster & 1 ) {
\r
480 ret = val & 0xFFF0000;
\r
487 VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);
\r
490 VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
\r
492 VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
\r
495 VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
\r
497 VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
\r
500 RELEASE(Disk->lFAT);
\r
502 if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;
\r
503 if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;
\r
504 if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;
\r
509 * ====================
\r
511 * ====================
\r
514 * \brief Read a cluster
\r
516 void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)
\r
518 ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);
\r
519 //Log("Cluster = %i (0x%x)", Cluster, Cluster);
\r
522 (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
\r
523 * Disk->bootsect.bps,
\r
531 * \brief Write a cluster to disk
\r
533 void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)
\r
535 ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);
\r
538 (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
\r
539 * Disk->bootsect.bps,
\r
540 Disk->BytesPerCluster,
\r
546 /* ====================
\r
548 * ====================
\r
551 * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)
\r
552 * \brief Reads data from a specified file
\r
554 Uint64 FAT_Read(tVFS_Node *Node, Uint64 offset, Uint64 length, void *buffer)
\r
556 int preSkip, count;
\r
557 int i, cluster, pos;
\r
560 tFAT_VolInfo *disk = Node->ImplPtr;
\r
562 ENTER("pNode Xoffset Xlength pbuffer", Node, offset, length, buffer);
\r
564 // Calculate and Allocate Bytes Per Cluster
\r
565 bpc = disk->BytesPerCluster;
\r
566 tmpBuf = (void*) malloc(bpc);
\r
567 if( !tmpBuf ) return 0;
\r
569 // Cluster is stored in Inode Field
\r
570 cluster = Node->Inode & 0xFFFFFFFF;
\r
572 // Sanity Check offset
\r
573 if(offset > Node->Size) {
\r
574 //LOG("Reading past EOF (%i > %i)", offset, node->Size);
\r
579 if(offset + length > Node->Size) {
\r
580 //LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
581 // offset, length, node->Size, node->Size - offset);
\r
582 length = Node->Size - offset;
\r
585 // Single Cluster including offset
\r
586 if(length + offset < bpc)
\r
588 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
589 memcpy( buffer, (void*)( tmpBuf + offset%bpc ), length );
\r
596 if( FAT_int_GetAddress(Node, offset, &addr) )
\r
598 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
604 preSkip = offset / bpc;
\r
606 //Skip previous clusters
\r
607 for(i=preSkip;i--;) {
\r
608 cluster = FAT_int_GetFatValue(disk, cluster);
\r
609 if(cluster == -1) {
\r
610 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
616 // Get Count of Clusters to read
\r
617 count = ((offset%bpc+length) / bpc) + 1;
\r
619 // Get buffer Position after 1st cluster
\r
620 pos = bpc - offset%bpc;
\r
622 // Read 1st Cluster
\r
623 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
626 (void*)( tmpBuf + (bpc-pos) ),
\r
627 (pos < length ? pos : length)
\r
636 cluster = FAT_int_GetFatValue(disk, cluster);
\r
639 LOG("pos=%i\n", pos);
\r
640 LOG("Reading the rest of the clusters\n");
\r
644 //Read the rest of the cluster data
\r
645 for( i = 1; i < count-1; i++ )
\r
647 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
648 memcpy((void*)(buffer+pos), tmpBuf, bpc);
\r
650 cluster = FAT_int_GetFatValue(disk, cluster);
\r
651 if(cluster == -1) {
\r
652 Warning("FAT_Read - Read past End of Cluster Chain");
\r
659 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
660 memcpy((void*)(buffer+pos), tmpBuf, length-pos);
\r
663 LOG("Free tmpBuf(0x%x) and Return\n", tmpBuf);
\r
667 LEAVE('X', length);
\r
672 * \brief Write to a file
\r
673 * \param Node File Node
\r
674 * \param Offset Offset within file
\r
675 * \param Length Size of data to write
\r
676 * \param Buffer Data source
\r
678 Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
680 tFAT_VolInfo *disk = Node->ImplPtr;
\r
682 int remLength = Length;
\r
683 Uint32 cluster, tmpCluster;
\r
684 int bNewCluster = 0;
\r
686 if(Offset > Node->Size) return 0;
\r
689 cluster = Node->Inode & 0xFFFFFFFF;
\r
690 while( Offset > disk->BytesPerCluster )
\r
692 cluster = FAT_int_GetFatValue( disk, cluster );
\r
693 if(cluster == -1) {
\r
694 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
697 Offset -= disk->BytesPerCluster;
\r
699 if( Offset == disk->BytesPerCluster )
\r
701 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
704 Offset -= disk->BytesPerCluster;
\r
707 if( Offset + Length < disk->BytesPerCluster )
\r
709 tmpBuf = malloc( disk->BytesPerCluster );
\r
711 // Read-Modify-Write
\r
712 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
713 memcpy( tmpBuf + Offset, Buffer, Length );
\r
714 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
720 // Clean up changes within a cluster
\r
723 tmpBuf = malloc( disk->BytesPerCluster );
\r
725 // Read-Modify-Write
\r
726 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
727 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
728 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
732 remLength -= disk->BytesPerCluster - Offset;
\r
733 Buffer += disk->BytesPerCluster - Offset;
\r
735 // Get next cluster (allocating if needed)
\r
736 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
737 if(tmpCluster == -1) {
\r
738 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
739 if( tmpCluster == 0 ) {
\r
740 return Length - remLength;
\r
743 cluster = tmpCluster;
\r
746 while( remLength > disk->BytesPerCluster )
\r
748 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
749 Buffer += disk->BytesPerCluster;
\r
751 // Get next cluster (allocating if needed)
\r
752 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
753 if(tmpCluster == -1) {
\r
755 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
756 if( tmpCluster == 0 ) {
\r
757 return Length - remLength;
\r
760 cluster = tmpCluster;
\r
764 tmpBuf = malloc( disk->BytesPerCluster );
\r
766 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
768 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
769 memcpy( tmpBuf, Buffer, remLength );
\r
770 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
776 /* ====================
\r
777 * File Names & Nodes
\r
778 * ====================
\r
781 * \fn void FAT_int_ProperFilename(char *dest, char *src)
\r
782 * \brief Converts a FAT directory entry name into a proper filename
\r
784 void FAT_int_ProperFilename(char *dest, char *src)
\r
788 for( a = 0; a < 8; a++) {
\r
789 if(src[a] == ' ') break;
\r
796 for( ; a < 11; a++, b++) {
\r
797 if(src[a] == ' ') break;
\r
802 //LOG("dest='%s'", dest);
\r
807 * \fn char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)
\r
808 * \brief Converts either a LFN or a 8.3 Name into a proper name
\r
810 char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)
\r
815 if(LongFileName && LongFileName[0] != '\0')
\r
817 len = strlen(LongFileName);
\r
818 ret = malloc(len+1);
\r
819 strcpy(ret, LongFileName);
\r
824 ret = (char*) malloc(13);
\r
825 memset(ret, 13, '\0');
\r
826 FAT_int_ProperFilename(ret, ft->name);
\r
834 * \fn tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)
\r
835 * \brief Creates a tVFS_Node structure for a given file entry
\r
837 tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)
\r
839 tVFS_Node node = {0};
\r
841 tFAT_VolInfo *disk = parent->ImplPtr;
\r
843 ENTER("pParent pFT sLongFileName", parent, ft, LongFileName);
\r
846 node.Inode = ft->cluster | (ft->clusterHi<<16);
\r
847 node.Size = ft->size;
\r
848 LOG("ft->size = %i", ft->size);
\r
849 node.ImplPtr = parent->ImplPtr;
\r
850 node.UID = 0; node.GID = 0;
\r
854 if(ft->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;
\r
855 if(ft->attrib & ATTR_READONLY) {
\r
856 node.Flags |= VFS_FFLAG_READONLY;
\r
857 node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X
\r
860 node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX
\r
863 node.ATime = timestamp(0,0,0,
\r
864 ((ft->adate&0x1F)-1), //Days
\r
865 ((ft->adate&0x1E0)-1), //Months
\r
866 1980+((ft->adate&0xFF00)>>8)); //Years
\r
868 node.CTime = ft->ctimems * 10; //Miliseconds
\r
869 node.CTime += timestamp(
\r
870 (ft->ctime&0x1F)<<1, //Seconds
\r
871 ((ft->ctime&0x3F0)>>5), //Minutes
\r
872 ((ft->ctime&0xF800)>>11), //Hours
\r
873 ((ft->cdate&0x1F)-1), //Days
\r
874 ((ft->cdate&0x1E0)-1), //Months
\r
875 1980+((ft->cdate&0xFF00)>>8)); //Years
\r
877 node.MTime = timestamp(
\r
878 (ft->mtime&0x1F)<<1, //Seconds
\r
879 ((ft->mtime&0x3F0)>>5), //Minuites
\r
880 ((ft->mtime&0xF800)>>11), //Hours
\r
881 ((ft->mdate&0x1F)-1), //Days
\r
882 ((ft->mdate&0x1E0)-1), //Months
\r
883 1980+((ft->mdate&0xFF00)>>8)); //Years
\r
885 if(node.Flags & VFS_FFLAG_DIRECTORY) {
\r
886 node.ReadDir = FAT_ReadDir;
\r
887 node.FindDir = FAT_FindDir;
\r
888 node.MkNod = FAT_Mknod;
\r
891 node.Read = FAT_Read;
\r
892 node.Write = FAT_Write;
\r
894 node.Close = FAT_CloseFile;
\r
895 node.Relink = FAT_Relink;
\r
897 ret = Inode_CacheNode(disk->inodeHandle, &node);
\r
902 /* ====================
\r
904 * ====================
\r
908 * \brief Reads a sector from the disk
\r
910 int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
\r
913 tFAT_VolInfo *disk = Node->ImplPtr;
\r
915 ENTER("pNode iSector pEntry", Node, Sector, Buffer)
\r
917 if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
\r
924 VFS_ReadAt(disk->fileHandle, addr, 512, Buffer); // Read Dir Data
\r
931 * \brief Writes an entry to the disk
\r
932 * \todo Support expanding a directory
\r
933 * \return Zero on success, non-zero on error
\r
935 int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
\r
939 Uint32 cluster = 0;
\r
940 tFAT_VolInfo *disk = Node->ImplPtr;
\r
942 ENTER("pNode iID pEntry", Node, ID, Entry);
\r
944 tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
\r
947 //TODO: Allocate a cluster
\r
948 cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
\r
949 if(cluster == -1) {
\r
950 Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
\r
954 FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
\r
958 LOG("addr = 0x%llx", addr);
\r
961 VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data
\r
968 // I should probably more tightly associate the LFN cache with the node
\r
969 // somehow, maybe by adding a field to tVFS_Node before locking it
\r
970 // Maybe .Cache or something like that (something that is free'd by the
\r
971 // Inode_UncacheNode function)
\r
974 * \fn char *FAT_int_GetLFN(tVFS_Node *node)
\r
975 * \brief Return pointer to LFN cache entry
\r
977 char *FAT_int_GetLFN(tVFS_Node *node)
\r
980 tmp = fat_lfncache;
\r
983 if(tmp->Inode == node->Inode && tmp->Disk == node->ImplPtr)
\r
987 tmp = malloc(sizeof(t_lfncache));
\r
988 tmp->Inode = node->Inode;
\r
989 tmp->Disk = node->ImplPtr;
\r
990 memset(tmp->Name, 0, 256);
\r
992 tmp->Next = fat_lfncache;
\r
993 fat_lfncache = tmp;
\r
999 * \fn void FAT_int_DelLFN(tVFS_Node *node)
\r
1000 * \brief Delete a LFN cache entry
\r
1002 void FAT_int_DelLFN(tVFS_Node *node)
\r
1006 if(!fat_lfncache) return;
\r
1008 if(!fat_lfncache->Next)
\r
1010 tmp = fat_lfncache;
\r
1011 fat_lfncache = tmp->Next;
\r
1015 tmp = fat_lfncache;
\r
1016 while(tmp && tmp->Next)
\r
1018 if(tmp->Inode == node->Inode && tmp->Disk == node->ImplPtr)
\r
1021 tmp->Next = tmp->Next->Next;
\r
1030 * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
\r
1031 * \param Node Node structure of directory
\r
1032 * \param ID Directory position
\r
1034 char *FAT_ReadDir(tVFS_Node *Node, int ID)
\r
1036 fat_filetable fileinfo[16]; //Sizeof=32, 16 per sector
\r
1043 ENTER("pNode iID", Node, ID);
\r
1045 if(FAT_int_ReadDirSector(Node, ID, fileinfo))
\r
1051 // Offset in sector
\r
1056 LOG("name[0] = 0x%x", (Uint8)fileinfo[a].name[0]);
\r
1058 // Check if this is the last entry
\r
1059 if( fileinfo[a].name[0] == '\0' ) {
\r
1061 LOG("End of list");
\r
1063 return NULL; // break
\r
1066 // Check for empty entry
\r
1067 if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
\r
1068 LOG("Empty Entry");
\r
1069 LEAVE('p', VFS_SKIP);
\r
1070 return VFS_SKIP; // Skip
\r
1074 // Get Long File Name Cache
\r
1075 lfn = FAT_int_GetLFN(Node);
\r
1076 if(fileinfo[a].attrib == ATTR_LFN)
\r
1078 fat_longfilename *lfnInfo;
\r
1081 lfnInfo = (fat_longfilename *) &fileinfo[a];
\r
1082 if(lfnInfo->id & 0x40) memset(lfn, 0, 256);
\r
1083 // Get the current length
\r
1084 len = strlen(lfn);
\r
1086 // Sanity Check (FAT implementations should not allow >255 bytes)
\r
1087 if(len + 13 > 255) return VFS_SKIP;
\r
1088 // Rebase all bytes
\r
1089 for(a=len+1;a--;) lfn[a+13] = lfn[a];
\r
1091 // Append new bytes
\r
1092 lfn[ 0] = lfnInfo->name1[0]; lfn[ 1] = lfnInfo->name1[1];
\r
1093 lfn[ 2] = lfnInfo->name1[2]; lfn[ 3] = lfnInfo->name1[3];
\r
1094 lfn[ 4] = lfnInfo->name1[4];
\r
1095 lfn[ 5] = lfnInfo->name2[0]; lfn[ 6] = lfnInfo->name2[1];
\r
1096 lfn[ 7] = lfnInfo->name2[2]; lfn[ 8] = lfnInfo->name2[3];
\r
1097 lfn[ 9] = lfnInfo->name2[4]; lfn[10] = lfnInfo->name2[5];
\r
1098 lfn[11] = lfnInfo->name3[0]; lfn[12] = lfnInfo->name3[1];
\r
1099 LEAVE('p', VFS_SKIP);
\r
1104 //Check if it is a volume entry
\r
1105 if(fileinfo[a].attrib & 0x08) {
\r
1106 LEAVE('p', VFS_SKIP);
\r
1109 // Ignore . and ..
\r
1110 if(fileinfo[a].name[0] == '.') {
\r
1111 LEAVE('p', VFS_SKIP);
\r
1115 LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'\n",
\r
1116 fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
\r
1117 fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
\r
1118 fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
\r
1121 ret = FAT_int_CreateName(Node, &fileinfo[a], lfn);
\r
1124 ret = FAT_int_CreateName(Node, &fileinfo[a], NULL);
\r
1132 * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
\r
1133 * \brief Finds an entry in the current directory
\r
1135 tVFS_Node *FAT_FindDir(tVFS_Node *Node, char *name)
\r
1137 fat_filetable fileinfo[16];
\r
1140 fat_longfilename *lfnInfo;
\r
1142 int lfnPos=255, lfnId = -1;
\r
1145 tVFS_Node *tmpNode;
\r
1146 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1149 ENTER("pNode sname", Node, name);
\r
1152 if(!name || name[0] == '\0') {
\r
1159 if((i & 0xF) == 0) {
\r
1160 if(FAT_int_ReadDirSector(Node, i, fileinfo))
\r
1167 //Check if the files are free
\r
1168 if(fileinfo[i&0xF].name[0] == '\0') break; // Free and last
\r
1169 if(fileinfo[i&0xF].name[0] == '\xE5') continue; //Free
\r
1173 // Long File Name Entry
\r
1174 if(fileinfo[i&0xF].attrib == ATTR_LFN)
\r
1176 lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];
\r
1177 if(lfnInfo->id & 0x40) {
\r
1178 memset(lfn, 0, 256);
\r
1181 lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];
\r
1182 lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];
\r
1183 lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];
\r
1184 lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];
\r
1185 lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];
\r
1186 lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];
\r
1187 lfn[lfnPos--] = lfnInfo->name1[0];
\r
1188 if((lfnInfo->id&0x3F) == 1)
\r
1190 memcpy(lfn, lfn+lfnPos+1, 256-lfnPos);
\r
1196 // Remove LFN if it does not apply
\r
1197 if(lfnId != i) lfn[0] = '\0';
\r
1199 // Get Real Filename
\r
1200 FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
\r
1202 LOG("tmpName = '%s'", tmpName);
\r
1204 //Only Long name is case sensitive, 8.3 is not
\r
1206 if(strucmp(tmpName, name) == 0 || strcmp(lfn, name) == 0) {
\r
1208 if(strucmp(tmpName, name) == 0) {
\r
1210 cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);
\r
1211 tmpNode = Inode_GetCache(disk->inodeHandle, cluster);
\r
1212 if(tmpNode == NULL) // Node is not cached
\r
1215 tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], lfn);
\r
1217 tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], NULL);
\r
1223 LEAVE('p', tmpNode);
\r
1236 * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)
\r
1237 * \brief Create a new node
\r
1239 int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)
\r
1245 * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
\r
1246 * \brief Rename / Delete a file
\r
1248 int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
\r
1251 fat_filetable ft = {0};
\r
1254 child = FAT_FindDir(Node, OldName);
\r
1255 if(!child) return ENOTFOUND;
\r
1258 if( NewName == NULL )
\r
1260 child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close
\r
1262 // Delete from the directory
\r
1263 ft.name[0] = '\xE9';
\r
1264 FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);
\r
1272 Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",
\r
1273 Node, OldName, NewName);
\r
1278 child->Close( child );
\r
1283 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
1284 * \brief Close an open file
\r
1286 void FAT_CloseFile(tVFS_Node *Node)
\r
1288 tFAT_VolInfo *disk = Node->ImplPtr;
\r
1289 if(Node == NULL) return ;
\r
1291 // Update the node if it's dirty (don't bother if it's marked for
\r
1293 if( Node->ImplInt & FAT_FLAG_DIRTY && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
1295 tFAT_VolInfo buf[16];
\r
1296 tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];
\r
1298 FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);
\r
1299 ft->size = Node->Size;
\r
1300 // TODO: update adate, mtime, mdate
\r
1301 FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);
\r
1303 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
1306 // TODO: Make this more thread safe somehow, probably by moving the
\r
1307 // Inode_UncacheNode higher up and saving the cluster value somewhere
\r
1308 if( Node->ReferenceCount == 1 )
\r
1310 // Delete LFN Cache
\r
1312 if( Node->Flags & VFS_FFLAG_DIRECTORY)
\r
1313 FAT_int_DelLFN(Node);
\r
1317 if( Node->ImplInt & FAT_FLAG_DELETE ) {
\r
1318 // Since the node is marked, we only need to remove it's data
\r
1319 Uint32 cluster = Node->Inode & 0xFFFFFFFF;
\r
1320 while( cluster != -1 )
\r
1321 cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster);
\r
1325 Inode_UncacheNode(disk->inodeHandle, Node->Inode);
\r