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
102 Log_Debug("FAT", "_Detect: Media type = %02x", bs.mediaDesc);
\r
103 if( bs.mediaDesc < 0xF0 )
\r
109 * \brief Reads the boot sector of a disk and prepares the structures for it
\r
111 tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
\r
115 Uint32 FATSz, RootDirSectors, TotSec;
\r
116 tVFS_Node *node = NULL;
\r
117 tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];
\r
119 memset(diskInfo, 0, sizeof(*diskInfo));
\r
121 // Temporary Pointer
\r
122 bs = &diskInfo->bootsect;
\r
124 // Open device and read boot sector
\r
125 diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
\r
126 if(diskInfo->fileHandle == -1) {
\r
127 Log_Notice("FAT", "Unable to open device '%s'", Device);
\r
131 VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);
\r
133 if(bs->bps == 0 || bs->spc == 0) {
\r
134 Log_Notice("FAT", "Error in FAT Boot Sector (zero BPS/SPC)");
\r
135 VFS_Close(diskInfo->fileHandle);
\r
139 // FAT Type Determining
\r
140 // - From Microsoft FAT Specifcation
\r
141 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
143 if(bs->fatSz16 != 0)
\r
144 FATSz = bs->fatSz16;
\r
146 FATSz = bs->spec.fat32.fatSz32;
\r
148 if(bs->totalSect16 != 0)
\r
149 TotSec = bs->totalSect16;
\r
151 TotSec = bs->totalSect32;
\r
153 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
155 if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)
\r
156 diskInfo->type = FAT12;
\r
157 else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)
\r
158 diskInfo->type = FAT16;
\r
160 diskInfo->type = FAT32;
\r
164 char *sFatType, *sSize;
\r
165 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
167 switch(diskInfo->type)
\r
169 case FAT12: sFatType = "FAT12"; break;
\r
170 case FAT16: sFatType = "FAT16"; break;
\r
171 case FAT32: sFatType = "FAT32"; break;
\r
172 default: sFatType = "UNKNOWN"; break;
\r
174 if(iSize <= 2*1024) {
\r
177 else if(iSize <= 2*1024*1024) {
\r
185 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
190 if(diskInfo->type == FAT32) {
\r
192 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
196 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
198 diskInfo->name[11] = '\0';
\r
200 // Compute Root directory offset
\r
201 if(diskInfo->type == FAT32)
\r
202 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
204 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
206 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
208 //Allow for Caching the FAT
\r
210 if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )
\r
213 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
214 if(diskInfo->FATCache == NULL) {
\r
215 Log_Warning("FAT", "Heap Exhausted");
\r
216 VFS_Cose(diskInfo->fileHandle);
\r
219 Ofs = bs->resvSectCount*512;
\r
220 if(diskInfo->type == FAT12)
\r
225 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
226 j = i & 511; //%512
\r
228 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
231 val = *((int*)(buf+j*3));
\r
232 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
233 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
236 else if(diskInfo->type == FAT16)
\r
239 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
240 if( (i & 255) == 0 ) {
\r
241 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
244 diskInfo->FATCache[i] = buf[i&255];
\r
247 else if(diskInfo->type == FAT32)
\r
250 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
251 if( (i & 127) == 0 ) {
\r
252 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
255 diskInfo->FATCache[i] = buf[i&127];
\r
258 LOG("FAT Fully Cached");
\r
260 #endif /*CACHE_FAT*/
\r
262 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
264 // == VFS Interface
\r
265 node = &diskInfo->rootNode;
\r
266 //node->Size = bs->files_in_root;
\r
268 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
269 node->ImplPtr = diskInfo; // Disk info pointer
\r
270 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
272 node->ReferenceCount = 1;
\r
274 node->UID = 0; node->GID = 0;
\r
276 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
277 node->Flags = VFS_FFLAG_DIRECTORY;
\r
278 node->CTime = node->MTime = node->ATime = now();
\r
280 node->Type = &gFAT_DirType;
\r
282 giFAT_PartCount ++;
\r
287 * \brief Closes a mount and marks it as free
\r
288 * \param Node Mount Root
\r
290 * \todo Remove FAT Cache
\r
291 * \todo Clear LFN Cache
\r
292 * \todo Check that all files are closed and flushed
\r
294 void FAT_Unmount(tVFS_Node *Node)
\r
296 tFAT_VolInfo *disk = Node->ImplPtr;
\r
298 // Close Disk Handle
\r
299 VFS_Close( disk->fileHandle );
\r
300 // Clear Node Cache
\r
301 FAT_int_ClearNodeCache(disk);
\r
303 disk->fileHandle = -2;
\r
308 * \brief Converts an offset in a file into a disk address
\r
309 * \param Node File (or directory) node
\r
310 * \param Offset Offset in the file
\r
311 * \param Addr Return Address
\r
312 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
313 * is past EOC) - Not touched if the node is the root
\r
315 * \return Zero on success, non-zero on error
\r
317 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
319 Uint32 cluster, base_cluster;
\r
322 tFAT_VolInfo *disk = Node->ImplPtr;
\r
324 ENTER("pNode XOffset", Node, Offset);
\r
326 cluster = base_cluster = Node->Inode & 0xFFFFFFF; // Cluster ID
\r
327 // LOG("base cluster = 0x%07x", cluster);
\r
330 // - Pre FAT32 had a reserved area for the root.
\r
331 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
333 skip = Offset / disk->BytesPerCluster;
\r
334 LOG("skip = %i", skip);
\r
335 // Skip previous clusters
\r
338 if(Cluster) *Cluster = cluster;
\r
339 cluster = FAT_int_GetFatValue(disk, cluster);
\r
340 // Check for end of cluster chain
\r
341 if(cluster == GETFATVALUE_EOC) { LEAVE('i', 1); return 1; }
\r
343 if(Cluster) *Cluster = cluster;
\r
346 // TODO: Bounds checking on root
\r
347 // LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);
\r
348 // Increment by clusters in offset
\r
349 cluster += Offset / disk->BytesPerCluster;
\r
352 // LOG("cluster = 0x%07x", cluster);
\r
354 // Bounds Checking (Used to spot corruption)
\r
355 if(cluster > disk->ClusterCount + 2)
\r
357 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
358 cluster, disk->ClusterCount+2);
\r
364 // - Pre FAT32 cluster base (in sectors)
\r
365 if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
366 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
367 addr += cluster * disk->BytesPerCluster;
\r
370 addr = disk->firstDataSect * disk->bootsect.bps;
\r
371 addr += (cluster - 2) * disk->BytesPerCluster;
\r
373 // In-cluster offset
\r
374 addr += Offset % disk->BytesPerCluster;
\r
376 LOG("addr = 0x%08x", addr);
\r
382 /* ====================
\r
384 * ====================
\r
387 * \brief Reads data from a specified file
\r
389 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
\r
391 int preSkip, count;
\r
392 Uint64 final_bytes;
\r
393 int i, cluster, pos;
\r
394 tFAT_VolInfo *disk = Node->ImplPtr;
\r
395 char tmpBuf[disk->BytesPerCluster];
\r
396 int bpc = disk->BytesPerCluster;
\r
398 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
400 // Sanity Check offset
\r
401 if(Offset > Node->Size) {
\r
402 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);
\r
407 // Cluster is stored in the low 32-bits of the Inode field
\r
408 cluster = Node->Inode & 0xFFFFFFFF;
\r
411 if(Offset + Length > Node->Size) {
\r
412 LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
413 Offset, Length, Node->Size, Node->Size - Offset);
\r
414 Length = Node->Size - Offset;
\r
417 // Skip previous clusters
\r
418 preSkip = Offset / bpc;
\r
420 LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);
\r
421 for(i = preSkip; i--; )
\r
423 cluster = FAT_int_GetFatValue(disk, cluster);
\r
424 if(cluster == GETFATVALUE_EOC) {
\r
425 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
431 // Reading from within one cluster
\r
432 if((int)Offset + (int)Length <= bpc)
\r
434 LOG("single cluster only");
\r
435 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
436 memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );
\r
437 LEAVE('X', Length);
\r
441 // Align read to a cluster
\r
444 pos = bpc - Offset;
\r
445 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
446 memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );
\r
447 LOG("pos = %i, Reading the rest of the clusters");
\r
448 // Get next cluster in the chain
\r
449 cluster = FAT_int_GetFatValue(disk, cluster);
\r
450 if(cluster == GETFATVALUE_EOC) {
\r
451 Log_Warning("FAT", "Read past End of Cluster Chain (Align)");
\r
459 // Get Count of Clusters to read
\r
460 // count = DivMod64U(Length - pos, bpc, &final_bytes);
\r
461 count = (Length - pos) / bpc;
\r
462 final_bytes = (Length - pos) % bpc;
\r
463 LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);
\r
465 // Read the rest of the cluster data
\r
466 for( ; count; count -- )
\r
468 if(cluster == GETFATVALUE_EOC) {
\r
469 Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");
\r
474 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));
\r
476 // Get next cluster in the chain
\r
477 cluster = FAT_int_GetFatValue(disk, cluster);
\r
480 if( final_bytes > 0 )
\r
482 if(cluster == -1) {
\r
483 Log_Warning("FAT", "Read past End of Cluster Chain (Final)");
\r
487 // Read final cluster
\r
488 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );
\r
489 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );
\r
493 //Debug_HexDump("FAT_Read", Buffer, Length);
\r
496 LEAVE('X', Length);
\r
502 * \brief Write to a file
\r
503 * \param Node File Node
\r
504 * \param Offset Offset within file
\r
505 * \param Length Size of data to write
\r
506 * \param Buffer Data source
\r
508 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)
\r
510 tFAT_VolInfo *disk = Node->ImplPtr;
\r
511 char tmpBuf[disk->BytesPerCluster];
\r
512 int remLength = Length;
\r
513 Uint32 cluster, tmpCluster;
\r
514 int bNewCluster = 0;
\r
515 off_t original_offset = Offset;
\r
517 if(Offset > Node->Size) return 0;
\r
519 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
522 cluster = Node->Inode & 0xFFFFFFFF;
\r
523 while( Offset > disk->BytesPerCluster )
\r
525 cluster = FAT_int_GetFatValue( disk, cluster );
\r
526 if(cluster == GETFATVALUE_EOC) {
\r
527 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
531 Offset -= disk->BytesPerCluster;
\r
533 if( Offset == disk->BytesPerCluster )
\r
535 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
541 Offset -= disk->BytesPerCluster;
\r
544 if( Offset + Length < disk->BytesPerCluster )
\r
546 char tmpBuf[disk->BytesPerCluster];
\r
548 LOG("Read-Modify-Write single");
\r
550 // Read-Modify-Write
\r
551 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
552 memcpy( tmpBuf + Offset, Buffer, Length );
\r
553 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
557 // Clean up changes within a cluster
\r
560 LOG("Read-Modify-Write first");
\r
562 // Read-Modify-Write
\r
563 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
564 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
565 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
567 remLength -= disk->BytesPerCluster - Offset;
\r
568 Buffer += disk->BytesPerCluster - Offset;
\r
570 // Get next cluster (allocating if needed)
\r
571 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
572 if(tmpCluster == GETFATVALUE_EOC) {
\r
573 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
574 if( tmpCluster == 0 )
\r
575 goto ret_incomplete;
\r
577 cluster = tmpCluster;
\r
580 while( remLength > disk->BytesPerCluster )
\r
582 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
583 Buffer += disk->BytesPerCluster;
\r
584 remLength -= disk->BytesPerCluster;
\r
586 // Get next cluster (allocating if needed)
\r
587 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
588 if(tmpCluster == GETFATVALUE_EOC) {
\r
590 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
591 if( tmpCluster == 0 )
\r
592 goto ret_incomplete;
\r
594 cluster = tmpCluster;
\r
601 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
603 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
604 memcpy( tmpBuf, Buffer, remLength );
\r
605 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
609 if( original_offset + Length > Node->Size ) {
\r
610 Node->Size = original_offset + Length;
\r
611 LOG("Updated size to %x", Node->Size);
\r
612 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
615 LEAVE('i', Length);
\r
618 LOG("Write incomplete");
\r
619 Length -= remLength;
\r
620 if( original_offset + Length > Node->Size ) {
\r
621 Node->Size = original_offset + Length;
\r
622 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
624 LEAVE('i', Length);
\r
630 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
631 * \brief Close an open file
\r
633 void FAT_CloseFile(tVFS_Node *Node)
\r
635 tFAT_VolInfo *disk = Node->ImplPtr;
\r
636 if(Node == NULL) return ;
\r
638 ENTER("pNode", Node);
\r
641 // Update the node if it's dirty (don't bother if it's marked for
\r
643 if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
646 tVFS_Node *dirnode;
\r
648 dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);
\r
650 Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);
\r
655 int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);
\r
656 ft.size = Node->Size;
\r
657 // TODO: update adate, mtime, mdate
\r
658 FAT_int_WriteDirEntry(dirnode, id, &ft);
\r
660 dirnode->Type->Close(dirnode);
\r
662 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
666 Uint32 cluster = Node->Inode;
\r
667 Uint32 implint = Node->ImplInt;
\r
670 if( FAT_int_DerefNode(Node) == 1 )
\r
672 LOG("implint = %x", implint);
\r
674 if( implint & FAT_FLAG_DELETE ) {
\r
675 Log_Debug("FAT", "Deallocating chain stating at 0x%07x", cluster);
\r
676 // Since the node is marked, we only need to remove it's data
\r
677 while( cluster != -1 )
\r
678 cluster = FAT_int_FreeCluster(disk, cluster);
\r