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
24 #include <modules.h>
\r
27 // === PROTOTYPES ===
\r
29 int FAT_Install(char **Arguments);
\r
30 int FAT_Detect(int FD);
\r
31 tVFS_Node *FAT_InitDevice(const char *device, const char **options);
\r
32 void FAT_Unmount(tVFS_Node *Node);
\r
34 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);
\r
36 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);
\r
38 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer);
\r
40 void FAT_CloseFile(tVFS_Node *node);
\r
44 int giFAT_MaxCachedClusters = 1024*512/4;
\r
46 // === SEMI-GLOBALS ===
\r
47 MODULE_DEFINE(0, VER2(0,80) /*v0.80*/, VFAT, FAT_Install, NULL, NULL);
\r
48 tFAT_VolInfo gFAT_Disks[8];
\r
49 int giFAT_PartCount = 0;
\r
50 tVFS_Driver gFAT_FSInfo = {
\r
52 .Detect = FAT_Detect,
\r
53 .InitDevice = FAT_InitDevice,
\r
54 .Unmount = FAT_Unmount,
\r
55 .GetNodeFromINode = FAT_GetNodeFromINode
\r
57 tVFS_NodeType gFAT_DirType = {
\r
58 .TypeName = "FAT-Dir",
\r
59 .ReadDir = FAT_ReadDir,
\r
60 .FindDir = FAT_FindDir,
\r
64 .Unlink = FAT_Unlink,
\r
66 .Close = FAT_CloseFile
\r
68 tVFS_NodeType gFAT_FileType = {
\r
69 .TypeName = "FAT-File",
\r
74 .Close = FAT_CloseFile
\r
79 * \fn int FAT_Install(char **Arguments)
\r
80 * \brief Install the FAT Driver
\r
82 int FAT_Install(char **Arguments)
\r
84 VFS_AddDriver( &gFAT_FSInfo );
\r
85 return MODULE_ERR_OK;
\r
89 * \brief Detect if a file is a FAT device
\r
91 int FAT_Detect(int FD)
\r
95 if( VFS_ReadAt(FD, 0, 512, &bs) != 512) {
\r
99 if(bs.bps == 0 || bs.spc == 0)
\r
105 * \brief Reads the boot sector of a disk and prepares the structures for it
\r
107 tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
\r
111 Uint32 FATSz, RootDirSectors, TotSec;
\r
112 tVFS_Node *node = NULL;
\r
113 tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];
\r
115 memset(diskInfo, 0, sizeof(*diskInfo));
\r
117 // Temporary Pointer
\r
118 bs = &diskInfo->bootsect;
\r
120 // Open device and read boot sector
\r
121 diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
\r
122 if(diskInfo->fileHandle == -1) {
\r
123 Log_Notice("FAT", "Unable to open device '%s'", Device);
\r
127 VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);
\r
129 if(bs->bps == 0 || bs->spc == 0) {
\r
130 Log_Notice("FAT", "Error in FAT Boot Sector (zero BPS/SPC)");
\r
131 VFS_Close(diskInfo->fileHandle);
\r
135 // FAT Type Determining
\r
136 // - From Microsoft FAT Specifcation
\r
137 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
139 if(bs->fatSz16 != 0)
\r
140 FATSz = bs->fatSz16;
\r
142 FATSz = bs->spec.fat32.fatSz32;
\r
144 if(bs->totalSect16 != 0)
\r
145 TotSec = bs->totalSect16;
\r
147 TotSec = bs->totalSect32;
\r
149 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
151 if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)
\r
152 diskInfo->type = FAT12;
\r
153 else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)
\r
154 diskInfo->type = FAT16;
\r
156 diskInfo->type = FAT32;
\r
160 char *sFatType, *sSize;
\r
161 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
163 switch(diskInfo->type)
\r
165 case FAT12: sFatType = "FAT12"; break;
\r
166 case FAT16: sFatType = "FAT16"; break;
\r
167 case FAT32: sFatType = "FAT32"; break;
\r
168 default: sFatType = "UNKNOWN"; break;
\r
170 if(iSize <= 2*1024) {
\r
173 else if(iSize <= 2*1024*1024) {
\r
181 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
186 if(diskInfo->type == FAT32) {
\r
188 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
192 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
194 diskInfo->name[11] = '\0';
\r
196 // Compute Root directory offset
\r
197 if(diskInfo->type == FAT32)
\r
198 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
200 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
202 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
204 //Allow for Caching the FAT
\r
206 if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )
\r
209 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
210 if(diskInfo->FATCache == NULL) {
\r
211 Log_Warning("FAT", "Heap Exhausted");
\r
212 VFS_Cose(diskInfo->fileHandle);
\r
215 Ofs = bs->resvSectCount*512;
\r
216 if(diskInfo->type == FAT12)
\r
221 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
222 j = i & 511; //%512
\r
224 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
227 val = *((int*)(buf+j*3));
\r
228 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
229 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
232 else if(diskInfo->type == FAT16)
\r
235 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
236 if( (i & 255) == 0 ) {
\r
237 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
240 diskInfo->FATCache[i] = buf[i&255];
\r
243 else if(diskInfo->type == FAT32)
\r
246 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
247 if( (i & 127) == 0 ) {
\r
248 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
251 diskInfo->FATCache[i] = buf[i&127];
\r
254 LOG("FAT Fully Cached");
\r
256 #endif /*CACHE_FAT*/
\r
258 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
260 // == VFS Interface
\r
261 node = &diskInfo->rootNode;
\r
262 //node->Size = bs->files_in_root;
\r
264 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
265 node->ImplPtr = diskInfo; // Disk info pointer
\r
266 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
268 node->ReferenceCount = 1;
\r
270 node->UID = 0; node->GID = 0;
\r
272 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
273 node->Flags = VFS_FFLAG_DIRECTORY;
\r
274 node->CTime = node->MTime = node->ATime = now();
\r
276 node->Type = &gFAT_DirType;
\r
278 giFAT_PartCount ++;
\r
283 * \brief Closes a mount and marks it as free
\r
284 * \param Node Mount Root
\r
286 * \todo Remove FAT Cache
\r
287 * \todo Clear LFN Cache
\r
288 * \todo Check that all files are closed and flushed
\r
290 void FAT_Unmount(tVFS_Node *Node)
\r
292 tFAT_VolInfo *disk = Node->ImplPtr;
\r
294 // Close Disk Handle
\r
295 VFS_Close( disk->fileHandle );
\r
296 // Clear Node Cache
\r
297 FAT_int_ClearNodeCache(disk);
\r
299 disk->fileHandle = -2;
\r
304 * \brief Converts an offset in a file into a disk address
\r
305 * \param Node File (or directory) node
\r
306 * \param Offset Offset in the file
\r
307 * \param Addr Return Address
\r
308 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
309 * is past EOC) - Not touched if the node is the root
\r
311 * \return Zero on success, non-zero on error
\r
313 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
315 Uint32 cluster, base_cluster;
\r
318 tFAT_VolInfo *disk = Node->ImplPtr;
\r
320 ENTER("pNode XOffset", Node, Offset);
\r
322 cluster = base_cluster = Node->Inode & 0xFFFFFFF; // Cluster ID
\r
323 // LOG("base cluster = 0x%07x", cluster);
\r
326 // - Pre FAT32 had a reserved area for the root.
\r
327 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
329 skip = Offset / disk->BytesPerCluster;
\r
330 LOG("skip = %i", skip);
\r
331 // Skip previous clusters
\r
334 if(Cluster) *Cluster = cluster;
\r
335 cluster = FAT_int_GetFatValue(disk, cluster);
\r
336 // Check for end of cluster chain
\r
337 if(cluster == GETFATVALUE_EOC) { LEAVE('i', 1); return 1; }
\r
339 if(Cluster) *Cluster = cluster;
\r
342 // TODO: Bounds checking on root
\r
343 // LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);
\r
344 // Increment by clusters in offset
\r
345 cluster += Offset / disk->BytesPerCluster;
\r
348 // LOG("cluster = 0x%07x", cluster);
\r
350 // Bounds Checking (Used to spot corruption)
\r
351 if(cluster > disk->ClusterCount + 2)
\r
353 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
354 cluster, disk->ClusterCount+2);
\r
360 // - Pre FAT32 cluster base (in sectors)
\r
361 if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
362 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
363 addr += cluster * disk->BytesPerCluster;
\r
366 addr = disk->firstDataSect * disk->bootsect.bps;
\r
367 addr += (cluster - 2) * disk->BytesPerCluster;
\r
369 // In-cluster offset
\r
370 addr += Offset % disk->BytesPerCluster;
\r
372 LOG("addr = 0x%08x", addr);
\r
378 /* ====================
\r
380 * ====================
\r
383 * \brief Reads data from a specified file
\r
385 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
\r
387 int preSkip, count;
\r
388 Uint64 final_bytes;
\r
389 int i, cluster, pos;
\r
390 tFAT_VolInfo *disk = Node->ImplPtr;
\r
391 char tmpBuf[disk->BytesPerCluster];
\r
392 int bpc = disk->BytesPerCluster;
\r
394 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
396 // Sanity Check offset
\r
397 if(Offset > Node->Size) {
\r
398 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);
\r
403 // Cluster is stored in the low 32-bits of the Inode field
\r
404 cluster = Node->Inode & 0xFFFFFFFF;
\r
407 if(Offset + Length > Node->Size) {
\r
408 LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
409 Offset, Length, Node->Size, Node->Size - Offset);
\r
410 Length = Node->Size - Offset;
\r
413 // Skip previous clusters
\r
414 preSkip = Offset / bpc;
\r
416 LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);
\r
417 for(i = preSkip; i--; )
\r
419 cluster = FAT_int_GetFatValue(disk, cluster);
\r
420 if(cluster == GETFATVALUE_EOC) {
\r
421 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
427 // Reading from within one cluster
\r
428 if((int)Offset + (int)Length <= bpc)
\r
430 LOG("single cluster only");
\r
431 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
432 memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );
\r
433 LEAVE('X', Length);
\r
437 // Align read to a cluster
\r
440 pos = bpc - Offset;
\r
441 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
442 memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );
\r
443 LOG("pos = %i, Reading the rest of the clusters");
\r
444 // Get next cluster in the chain
\r
445 cluster = FAT_int_GetFatValue(disk, cluster);
\r
446 if(cluster == GETFATVALUE_EOC) {
\r
447 Log_Warning("FAT", "Read past End of Cluster Chain (Align)");
\r
455 // Get Count of Clusters to read
\r
456 // count = DivMod64U(Length - pos, bpc, &final_bytes);
\r
457 count = (Length - pos) / bpc;
\r
458 final_bytes = (Length - pos) % bpc;
\r
459 LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);
\r
461 // Read the rest of the cluster data
\r
462 for( ; count; count -- )
\r
464 if(cluster == GETFATVALUE_EOC) {
\r
465 Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");
\r
470 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));
\r
472 // Get next cluster in the chain
\r
473 cluster = FAT_int_GetFatValue(disk, cluster);
\r
476 if( final_bytes > 0 )
\r
478 if(cluster == -1) {
\r
479 Log_Warning("FAT", "Read past End of Cluster Chain (Final)");
\r
483 // Read final cluster
\r
484 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );
\r
485 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );
\r
489 //Debug_HexDump("FAT_Read", Buffer, Length);
\r
492 LEAVE('X', Length);
\r
498 * \brief Write to a file
\r
499 * \param Node File Node
\r
500 * \param Offset Offset within file
\r
501 * \param Length Size of data to write
\r
502 * \param Buffer Data source
\r
504 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)
\r
506 tFAT_VolInfo *disk = Node->ImplPtr;
\r
507 char tmpBuf[disk->BytesPerCluster];
\r
508 int remLength = Length;
\r
509 Uint32 cluster, tmpCluster;
\r
510 int bNewCluster = 0;
\r
511 off_t original_offset = Offset;
\r
513 if(Offset > Node->Size) return 0;
\r
515 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
518 cluster = Node->Inode & 0xFFFFFFFF;
\r
519 while( Offset > disk->BytesPerCluster )
\r
521 cluster = FAT_int_GetFatValue( disk, cluster );
\r
522 if(cluster == GETFATVALUE_EOC) {
\r
523 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
527 Offset -= disk->BytesPerCluster;
\r
529 if( Offset == disk->BytesPerCluster )
\r
531 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
537 Offset -= disk->BytesPerCluster;
\r
540 if( Offset + Length < disk->BytesPerCluster )
\r
542 char tmpBuf[disk->BytesPerCluster];
\r
544 LOG("Read-Modify-Write single");
\r
546 // Read-Modify-Write
\r
547 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
548 memcpy( tmpBuf + Offset, Buffer, Length );
\r
549 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
553 // Clean up changes within a cluster
\r
556 LOG("Read-Modify-Write first");
\r
558 // Read-Modify-Write
\r
559 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
560 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
561 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
563 remLength -= disk->BytesPerCluster - Offset;
\r
564 Buffer += disk->BytesPerCluster - Offset;
\r
566 // Get next cluster (allocating if needed)
\r
567 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
568 if(tmpCluster == GETFATVALUE_EOC) {
\r
569 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
570 if( tmpCluster == 0 )
\r
571 goto ret_incomplete;
\r
573 cluster = tmpCluster;
\r
576 while( remLength > disk->BytesPerCluster )
\r
578 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
579 Buffer += disk->BytesPerCluster;
\r
580 remLength -= disk->BytesPerCluster;
\r
582 // Get next cluster (allocating if needed)
\r
583 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
584 if(tmpCluster == GETFATVALUE_EOC) {
\r
586 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
587 if( tmpCluster == 0 )
\r
588 goto ret_incomplete;
\r
590 cluster = tmpCluster;
\r
597 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
599 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
600 memcpy( tmpBuf, Buffer, remLength );
\r
601 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
605 if( original_offset + Length > Node->Size ) {
\r
606 Node->Size = original_offset + Length;
\r
607 LOG("Updated size to %x", Node->Size);
\r
608 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
611 LEAVE('i', Length);
\r
614 LOG("Write incomplete");
\r
615 Length -= remLength;
\r
616 if( original_offset + Length > Node->Size ) {
\r
617 Node->Size = original_offset + Length;
\r
618 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
620 LEAVE('i', Length);
\r
626 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
627 * \brief Close an open file
\r
629 void FAT_CloseFile(tVFS_Node *Node)
\r
631 tFAT_VolInfo *disk = Node->ImplPtr;
\r
632 if(Node == NULL) return ;
\r
634 ENTER("pNode", Node);
\r
637 // Update the node if it's dirty (don't bother if it's marked for
\r
639 if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
642 tVFS_Node *dirnode;
\r
644 dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);
\r
646 Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);
\r
651 int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);
\r
652 ft.size = Node->Size;
\r
653 // TODO: update adate, mtime, mdate
\r
654 FAT_int_WriteDirEntry(dirnode, id, &ft);
\r
656 dirnode->Type->Close(dirnode);
\r
658 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
662 Uint32 cluster = Node->Inode;
\r
663 Uint32 implint = Node->ImplInt;
\r
666 if( FAT_int_DerefNode(Node) == 1 )
\r
668 LOG("implint = %x", implint);
\r
670 if( implint & FAT_FLAG_DELETE ) {
\r
671 Log_Debug("FAT", "Deallocating chain stating at 0x%07x", cluster);
\r
672 // Since the node is marked, we only need to remove it's data
\r
673 while( cluster != -1 )
\r
674 cluster = FAT_int_FreeCluster(disk, cluster);
\r