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 tVFS_Node *FAT_InitDevice(const char *device, const char **options);
\r
31 void FAT_Unmount(tVFS_Node *Node);
\r
33 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);
\r
35 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);
\r
37 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer);
\r
39 void FAT_CloseFile(tVFS_Node *node);
\r
43 int giFAT_MaxCachedClusters = 1024*512/4;
\r
45 // === SEMI-GLOBALS ===
\r
46 MODULE_DEFINE(0, VER2(0,80) /*v0.80*/, VFAT, FAT_Install, NULL, NULL);
\r
47 tFAT_VolInfo gFAT_Disks[8];
\r
48 int giFAT_PartCount = 0;
\r
49 tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};
\r
50 tVFS_NodeType gFAT_DirType = {
\r
51 .TypeName = "FAT-Dir",
\r
52 .ReadDir = FAT_ReadDir,
\r
53 .FindDir = FAT_FindDir,
\r
57 .Unlink = FAT_Unlink,
\r
59 .Close = FAT_CloseFile
\r
61 tVFS_NodeType gFAT_FileType = {
\r
62 .TypeName = "FAT-File",
\r
67 .Close = FAT_CloseFile
\r
72 * \fn int FAT_Install(char **Arguments)
\r
73 * \brief Install the FAT Driver
\r
75 int FAT_Install(char **Arguments)
\r
77 VFS_AddDriver( &gFAT_FSInfo );
\r
78 return MODULE_ERR_OK;
\r
82 * \brief Reads the boot sector of a disk and prepares the structures for it
\r
84 tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)
\r
88 Uint32 FATSz, RootDirSectors, TotSec;
\r
89 tVFS_Node *node = NULL;
\r
90 tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];
\r
92 memset(diskInfo, 0, sizeof(*diskInfo));
\r
94 // Temporary Pointer
\r
95 bs = &diskInfo->bootsect;
\r
97 // Open device and read boot sector
\r
98 diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
\r
99 if(diskInfo->fileHandle == -1) {
\r
100 Log_Notice("FAT", "Unable to open device '%s'", Device);
\r
104 VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);
\r
106 if(bs->bps == 0 || bs->spc == 0) {
\r
107 Log_Notice("FAT", "Error in FAT Boot Sector (zero BPS/SPC)");
\r
108 VFS_Close(diskInfo->fileHandle);
\r
112 // FAT Type Determining
\r
113 // - From Microsoft FAT Specifcation
\r
114 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
116 if(bs->fatSz16 != 0)
\r
117 FATSz = bs->fatSz16;
\r
119 FATSz = bs->spec.fat32.fatSz32;
\r
121 if(bs->totalSect16 != 0)
\r
122 TotSec = bs->totalSect16;
\r
124 TotSec = bs->totalSect32;
\r
126 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
128 if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)
\r
129 diskInfo->type = FAT12;
\r
130 else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)
\r
131 diskInfo->type = FAT16;
\r
133 diskInfo->type = FAT32;
\r
137 char *sFatType, *sSize;
\r
138 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
140 switch(diskInfo->type)
\r
142 case FAT12: sFatType = "FAT12"; break;
\r
143 case FAT16: sFatType = "FAT16"; break;
\r
144 case FAT32: sFatType = "FAT32"; break;
\r
145 default: sFatType = "UNKNOWN"; break;
\r
147 if(iSize <= 2*1024) {
\r
150 else if(iSize <= 2*1024*1024) {
\r
158 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
163 if(diskInfo->type == FAT32) {
\r
165 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
169 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
171 diskInfo->name[11] = '\0';
\r
173 // Compute Root directory offset
\r
174 if(diskInfo->type == FAT32)
\r
175 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
177 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
179 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
181 //Allow for Caching the FAT
\r
183 if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )
\r
186 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
187 if(diskInfo->FATCache == NULL) {
\r
188 Log_Warning("FAT", "Heap Exhausted");
\r
189 VFS_Cose(diskInfo->fileHandle);
\r
192 Ofs = bs->resvSectCount*512;
\r
193 if(diskInfo->type == FAT12)
\r
198 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
199 j = i & 511; //%512
\r
201 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
204 val = *((int*)(buf+j*3));
\r
205 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
206 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
209 else if(diskInfo->type == FAT16)
\r
212 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
213 if( (i & 255) == 0 ) {
\r
214 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
217 diskInfo->FATCache[i] = buf[i&255];
\r
220 else if(diskInfo->type == FAT32)
\r
223 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
224 if( (i & 127) == 0 ) {
\r
225 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
228 diskInfo->FATCache[i] = buf[i&127];
\r
231 LOG("FAT Fully Cached");
\r
233 #endif /*CACHE_FAT*/
\r
235 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
237 // == VFS Interface
\r
238 node = &diskInfo->rootNode;
\r
239 //node->Size = bs->files_in_root;
\r
241 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
242 node->ImplPtr = diskInfo; // Disk info pointer
\r
243 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
245 node->ReferenceCount = 1;
\r
247 node->UID = 0; node->GID = 0;
\r
249 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
250 node->Flags = VFS_FFLAG_DIRECTORY;
\r
251 node->CTime = node->MTime = node->ATime = now();
\r
253 node->Type = &gFAT_DirType;
\r
255 giFAT_PartCount ++;
\r
260 * \brief Closes a mount and marks it as free
\r
261 * \param Node Mount Root
\r
263 * \todo Remove FAT Cache
\r
264 * \todo Clear LFN Cache
\r
265 * \todo Check that all files are closed and flushed
\r
267 void FAT_Unmount(tVFS_Node *Node)
\r
269 tFAT_VolInfo *disk = Node->ImplPtr;
\r
271 // Close Disk Handle
\r
272 VFS_Close( disk->fileHandle );
\r
273 // Clear Node Cache
\r
274 FAT_int_ClearNodeCache(disk);
\r
276 disk->fileHandle = -2;
\r
281 * \brief Converts an offset in a file into a disk address
\r
282 * \param Node File (or directory) node
\r
283 * \param Offset Offset in the file
\r
284 * \param Addr Return Address
\r
285 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
286 * is past EOC) - Not touched if the node is the root
\r
288 * \return Zero on success, non-zero on error
\r
290 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
292 Uint32 cluster, base_cluster;
\r
295 tFAT_VolInfo *disk = Node->ImplPtr;
\r
297 ENTER("pNode XOffset", Node, Offset);
\r
299 cluster = base_cluster = Node->Inode & 0xFFFFFFF; // Cluster ID
\r
300 // LOG("base cluster = 0x%07x", cluster);
\r
303 // - Pre FAT32 had a reserved area for the root.
\r
304 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
306 skip = Offset / disk->BytesPerCluster;
\r
307 LOG("skip = %i", skip);
\r
308 // Skip previous clusters
\r
311 if(Cluster) *Cluster = cluster;
\r
312 cluster = FAT_int_GetFatValue(disk, cluster);
\r
313 // Check for end of cluster chain
\r
314 if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;}
\r
316 if(Cluster) *Cluster = cluster;
\r
319 // TODO: Bounds checking on root
\r
320 // LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);
\r
321 // Increment by clusters in offset
\r
322 cluster += Offset / disk->BytesPerCluster;
\r
325 // LOG("cluster = 0x%07x", cluster);
\r
327 // Bounds Checking (Used to spot corruption)
\r
328 if(cluster > disk->ClusterCount + 2)
\r
330 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
331 cluster, disk->ClusterCount+2);
\r
337 // - Pre FAT32 cluster base (in sectors)
\r
338 if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
339 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
340 addr += cluster * disk->BytesPerCluster;
\r
343 addr = disk->firstDataSect * disk->bootsect.bps;
\r
344 addr += (cluster - 2) * disk->BytesPerCluster;
\r
346 // In-cluster offset
\r
347 addr += Offset % disk->BytesPerCluster;
\r
349 LOG("addr = 0x%08x", addr);
\r
355 /* ====================
\r
357 * ====================
\r
360 * \brief Reads data from a specified file
\r
362 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
\r
364 int preSkip, count;
\r
365 Uint64 final_bytes;
\r
366 int i, cluster, pos;
\r
367 tFAT_VolInfo *disk = Node->ImplPtr;
\r
368 char tmpBuf[disk->BytesPerCluster];
\r
369 int bpc = disk->BytesPerCluster;
\r
371 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
373 // Sanity Check offset
\r
374 if(Offset > Node->Size) {
\r
375 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);
\r
380 // Cluster is stored in the low 32-bits of the Inode field
\r
381 cluster = Node->Inode & 0xFFFFFFFF;
\r
384 if(Offset + Length > Node->Size) {
\r
385 LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
386 Offset, Length, Node->Size, Node->Size - Offset);
\r
387 Length = Node->Size - Offset;
\r
390 // Skip previous clusters
\r
391 preSkip = Offset / bpc;
\r
393 LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);
\r
394 for(i = preSkip; i--; )
\r
396 cluster = FAT_int_GetFatValue(disk, cluster);
\r
397 if(cluster == -1) {
\r
398 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
404 // Reading from within one cluster
\r
405 if((int)Offset + (int)Length <= bpc)
\r
407 LOG("single cluster only");
\r
408 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
409 memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );
\r
410 LEAVE('X', Length);
\r
414 // Align read to a cluster
\r
417 pos = bpc - Offset;
\r
418 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
419 memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );
\r
420 LOG("pos = %i, Reading the rest of the clusters");
\r
421 // Get next cluster in the chain
\r
422 cluster = FAT_int_GetFatValue(disk, cluster);
\r
423 if(cluster == -1) {
\r
424 Log_Warning("FAT", "Read past End of Cluster Chain (Align)");
\r
432 // Get Count of Clusters to read
\r
433 // count = DivMod64U(Length - pos, bpc, &final_bytes);
\r
434 count = (Length - pos) / bpc;
\r
435 final_bytes = (Length - pos) % bpc;
\r
436 LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);
\r
438 // Read the rest of the cluster data
\r
439 for( ; count; count -- )
\r
441 if(cluster == -1) {
\r
442 Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");
\r
447 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));
\r
449 // Get next cluster in the chain
\r
450 cluster = FAT_int_GetFatValue(disk, cluster);
\r
453 if( final_bytes > 0 )
\r
455 if(cluster == -1) {
\r
456 Log_Warning("FAT", "Read past End of Cluster Chain (Final)");
\r
460 // Read final cluster
\r
461 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );
\r
462 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );
\r
466 //Debug_HexDump("FAT_Read", Buffer, Length);
\r
469 LEAVE('X', Length);
\r
475 * \brief Write to a file
\r
476 * \param Node File Node
\r
477 * \param Offset Offset within file
\r
478 * \param Length Size of data to write
\r
479 * \param Buffer Data source
\r
481 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)
\r
483 tFAT_VolInfo *disk = Node->ImplPtr;
\r
484 char tmpBuf[disk->BytesPerCluster];
\r
485 int remLength = Length;
\r
486 Uint32 cluster, tmpCluster;
\r
487 int bNewCluster = 0;
\r
488 off_t original_offset = Offset;
\r
490 if(Offset > Node->Size) return 0;
\r
492 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
495 cluster = Node->Inode & 0xFFFFFFFF;
\r
496 while( Offset > disk->BytesPerCluster )
\r
498 cluster = FAT_int_GetFatValue( disk, cluster );
\r
499 if(cluster == -1) {
\r
500 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
504 Offset -= disk->BytesPerCluster;
\r
506 if( Offset == disk->BytesPerCluster )
\r
508 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
514 Offset -= disk->BytesPerCluster;
\r
517 if( Offset + Length < disk->BytesPerCluster )
\r
519 char tmpBuf[disk->BytesPerCluster];
\r
521 LOG("Read-Modify-Write single");
\r
523 // Read-Modify-Write
\r
524 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
525 memcpy( tmpBuf + Offset, Buffer, Length );
\r
526 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
530 // Clean up changes within a cluster
\r
533 LOG("Read-Modify-Write first");
\r
535 // Read-Modify-Write
\r
536 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
537 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
538 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
540 remLength -= disk->BytesPerCluster - Offset;
\r
541 Buffer += disk->BytesPerCluster - Offset;
\r
543 // Get next cluster (allocating if needed)
\r
544 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
545 if(tmpCluster == -1) {
\r
546 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
547 if( tmpCluster == 0 )
\r
548 goto ret_incomplete;
\r
550 cluster = tmpCluster;
\r
553 while( remLength > disk->BytesPerCluster )
\r
555 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
556 Buffer += disk->BytesPerCluster;
\r
557 remLength -= disk->BytesPerCluster;
\r
559 // Get next cluster (allocating if needed)
\r
560 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
561 if(tmpCluster == -1) {
\r
563 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
564 if( tmpCluster == 0 )
\r
565 goto ret_incomplete;
\r
567 cluster = tmpCluster;
\r
574 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
576 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
577 memcpy( tmpBuf, Buffer, remLength );
\r
578 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
582 if( original_offset + Length > Node->Size ) {
\r
583 Node->Size = original_offset + Length;
\r
584 LOG("Updated size to %x", Node->Size);
\r
585 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
588 LEAVE('i', Length);
\r
591 LOG("Write incomplete");
\r
592 Length -= remLength;
\r
593 if( original_offset + Length > Node->Size ) {
\r
594 Node->Size = original_offset + Length;
\r
595 Node->ImplInt |= FAT_FLAG_DIRTY;
\r
597 LEAVE('i', Length);
\r
603 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
604 * \brief Close an open file
\r
606 void FAT_CloseFile(tVFS_Node *Node)
\r
608 tFAT_VolInfo *disk = Node->ImplPtr;
\r
609 if(Node == NULL) return ;
\r
611 ENTER("pNode", Node);
\r
614 // Update the node if it's dirty (don't bother if it's marked for
\r
616 if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
619 tVFS_Node *dirnode;
\r
621 dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);
\r
623 Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);
\r
628 int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);
\r
629 ft.size = Node->Size;
\r
630 // TODO: update adate, mtime, mdate
\r
631 FAT_int_WriteDirEntry(dirnode, id, &ft);
\r
633 dirnode->Type->Close(dirnode);
\r
635 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
639 Uint32 cluster = Node->Inode;
\r
640 Uint32 implint = Node->ImplInt;
\r
643 if( FAT_int_DerefNode(Node) == 1 )
\r
645 LOG("implint = %x", implint);
\r
647 if( implint & FAT_FLAG_DELETE ) {
\r
648 Log_Debug("FAT", "Deallocating chain stating at 0x%07x", cluster);
\r
649 // Since the node is marked, we only need to remove it's data
\r
650 while( cluster != -1 )
\r
651 cluster = FAT_int_FreeCluster(disk, cluster);
\r