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
111 // FAT Type Determining
\r
112 // - From Microsoft FAT Specifcation
\r
113 RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;
\r
115 if(bs->fatSz16 != 0)
\r
116 FATSz = bs->fatSz16;
\r
118 FATSz = bs->spec.fat32.fatSz32;
\r
120 if(bs->totalSect16 != 0)
\r
121 TotSec = bs->totalSect16;
\r
123 TotSec = bs->totalSect32;
\r
125 diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;
\r
127 if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)
\r
128 diskInfo->type = FAT12;
\r
129 else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)
\r
130 diskInfo->type = FAT16;
\r
132 diskInfo->type = FAT32;
\r
136 char *sFatType, *sSize;
\r
137 Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;
\r
139 switch(diskInfo->type)
\r
141 case FAT12: sFatType = "FAT12"; break;
\r
142 case FAT16: sFatType = "FAT16"; break;
\r
143 case FAT32: sFatType = "FAT32"; break;
\r
144 default: sFatType = "UNKNOWN"; break;
\r
146 if(iSize <= 2*1024) {
\r
149 else if(iSize <= 2*1024*1024) {
\r
157 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);
\r
162 if(diskInfo->type == FAT32) {
\r
164 diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);
\r
168 diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);
\r
170 diskInfo->name[11] = '\0';
\r
172 // Compute Root directory offset
\r
173 if(diskInfo->type == FAT32)
\r
174 diskInfo->rootOffset = bs->spec.fat32.rootClust;
\r
176 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;
\r
178 diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;
\r
180 //Allow for Caching the FAT
\r
182 if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )
\r
185 diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);
\r
186 if(diskInfo->FATCache == NULL) {
\r
187 Log_Warning("FAT", "Heap Exhausted");
\r
190 Ofs = bs->resvSectCount*512;
\r
191 if(diskInfo->type == FAT12)
\r
196 for(i = 0; i < diskInfo->ClusterCount/2; i++) {
\r
197 j = i & 511; //%512
\r
199 VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);
\r
202 val = *((int*)(buf+j*3));
\r
203 diskInfo->FATCache[i*2] = val & 0xFFF;
\r
204 diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;
\r
207 else if(diskInfo->type == FAT16)
\r
210 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
211 if( (i & 255) == 0 ) {
\r
212 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
215 diskInfo->FATCache[i] = buf[i&255];
\r
218 else if(diskInfo->type == FAT32)
\r
221 for(i=0;i<diskInfo->ClusterCount;i++) {
\r
222 if( (i & 127) == 0 ) {
\r
223 VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);
\r
226 diskInfo->FATCache[i] = buf[i&127];
\r
229 LOG("FAT Fully Cached");
\r
231 #endif /*CACHE_FAT*/
\r
233 diskInfo->BytesPerCluster = bs->spc * bs->bps;
\r
235 // == VFS Interface
\r
236 node = &diskInfo->rootNode;
\r
237 //node->Size = bs->files_in_root;
\r
239 node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster
\r
240 node->ImplPtr = diskInfo; // Disk info pointer
\r
241 node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag
\r
243 node->ReferenceCount = 1;
\r
245 node->UID = 0; node->GID = 0;
\r
247 node->ACLs = &gVFS_ACL_EveryoneRWX;
\r
248 node->Flags = VFS_FFLAG_DIRECTORY;
\r
249 node->CTime = node->MTime = node->ATime = now();
\r
251 node->Type = &gFAT_DirType;
\r
253 giFAT_PartCount ++;
\r
258 * \brief Closes a mount and marks it as free
\r
259 * \param Node Mount Root
\r
261 * \todo Remove FAT Cache
\r
262 * \todo Clear LFN Cache
\r
263 * \todo Check that all files are closed and flushed
\r
265 void FAT_Unmount(tVFS_Node *Node)
\r
267 tFAT_VolInfo *disk = Node->ImplPtr;
\r
269 // Close Disk Handle
\r
270 VFS_Close( disk->fileHandle );
\r
271 // Clear Node Cache
\r
272 FAT_int_ClearNodeCache(disk);
\r
274 disk->fileHandle = -2;
\r
279 * \brief Converts an offset in a file into a disk address
\r
280 * \param Node File (or directory) node
\r
281 * \param Offset Offset in the file
\r
282 * \param Addr Return Address
\r
283 * \param Cluster Set to the current cluster (or the last one if \a Offset
\r
284 * is past EOC) - Not touched if the node is the root
\r
286 * \return Zero on success, non-zero on error
\r
288 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)
\r
290 Uint32 cluster, base_cluster;
\r
293 tFAT_VolInfo *disk = Node->ImplPtr;
\r
295 ENTER("pNode XOffset", Node, Offset);
\r
297 cluster = base_cluster = Node->Inode & 0xFFFFFFF; // Cluster ID
\r
298 // LOG("base cluster = 0x%07x", cluster);
\r
301 // - Pre FAT32 had a reserved area for the root.
\r
302 if( disk->type == FAT32 || cluster != disk->rootOffset )
\r
304 skip = Offset / disk->BytesPerCluster;
\r
305 LOG("skip = %i", skip);
\r
306 // Skip previous clusters
\r
309 if(Cluster) *Cluster = cluster;
\r
310 cluster = FAT_int_GetFatValue(disk, cluster);
\r
311 // Check for end of cluster chain
\r
312 if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;}
\r
314 if(Cluster) *Cluster = cluster;
\r
317 // TODO: Bounds checking on root
\r
318 // LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);
\r
319 // Increment by clusters in offset
\r
320 cluster += Offset / disk->BytesPerCluster;
\r
323 // LOG("cluster = 0x%07x", cluster);
\r
325 // Bounds Checking (Used to spot corruption)
\r
326 if(cluster > disk->ClusterCount + 2)
\r
328 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",
\r
329 cluster, disk->ClusterCount+2);
\r
335 // - Pre FAT32 cluster base (in sectors)
\r
336 if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {
\r
337 addr = disk->bootsect.resvSectCount * disk->bootsect.bps;
\r
338 addr += cluster * disk->BytesPerCluster;
\r
341 addr = disk->firstDataSect * disk->bootsect.bps;
\r
342 addr += (cluster - 2) * disk->BytesPerCluster;
\r
344 // In-cluster offset
\r
345 addr += Offset % disk->BytesPerCluster;
\r
347 LOG("addr = 0x%08x", addr);
\r
353 /* ====================
\r
355 * ====================
\r
358 * \brief Reads data from a specified file
\r
360 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
\r
362 int preSkip, count;
\r
363 Uint64 final_bytes;
\r
364 int i, cluster, pos;
\r
365 tFAT_VolInfo *disk = Node->ImplPtr;
\r
366 char tmpBuf[disk->BytesPerCluster];
\r
367 int bpc = disk->BytesPerCluster;
\r
369 ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);
\r
371 // Sanity Check offset
\r
372 if(Offset > Node->Size) {
\r
373 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);
\r
378 // Cluster is stored in the low 32-bits of the Inode field
\r
379 cluster = Node->Inode & 0xFFFFFFFF;
\r
382 if(Offset + Length > Node->Size) {
\r
383 LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",
\r
384 Offset, Length, Node->Size, Node->Size - Offset);
\r
385 Length = Node->Size - Offset;
\r
388 // Skip previous clusters
\r
389 preSkip = Offset / bpc;
\r
391 LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);
\r
392 for(i = preSkip; i--; )
\r
394 cluster = FAT_int_GetFatValue(disk, cluster);
\r
395 if(cluster == -1) {
\r
396 Log_Warning("FAT", "Offset is past end of cluster chain mark");
\r
402 // Reading from within one cluster
\r
403 if((int)Offset + (int)Length <= bpc)
\r
405 LOG("single cluster only");
\r
406 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
407 memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );
\r
408 LEAVE('X', Length);
\r
412 // Align read to a cluster
\r
415 pos = bpc - Offset;
\r
416 FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);
\r
417 memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );
\r
418 LOG("pos = %i, Reading the rest of the clusters");
\r
419 // Get next cluster in the chain
\r
420 cluster = FAT_int_GetFatValue(disk, cluster);
\r
421 if(cluster == -1) {
\r
422 Log_Warning("FAT", "Read past End of Cluster Chain (Align)");
\r
430 // Get Count of Clusters to read
\r
431 // count = DivMod64U(Length - pos, bpc, &final_bytes);
\r
432 count = (Length - pos) / bpc;
\r
433 final_bytes = (Length - pos) % bpc;
\r
434 LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);
\r
436 // Read the rest of the cluster data
\r
437 for( ; count; count -- )
\r
439 if(cluster == -1) {
\r
440 Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");
\r
445 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));
\r
447 // Get next cluster in the chain
\r
448 cluster = FAT_int_GetFatValue(disk, cluster);
\r
451 if( final_bytes > 0 )
\r
453 if(cluster == -1) {
\r
454 Log_Warning("FAT", "Read past End of Cluster Chain (Final)");
\r
458 // Read final cluster
\r
459 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );
\r
460 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );
\r
464 //Debug_HexDump("FAT_Read", Buffer, Length);
\r
467 LEAVE('X', Length);
\r
473 * \brief Write to a file
\r
474 * \param Node File Node
\r
475 * \param Offset Offset within file
\r
476 * \param Length Size of data to write
\r
477 * \param Buffer Data source
\r
479 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)
\r
481 tFAT_VolInfo *disk = Node->ImplPtr;
\r
482 char tmpBuf[disk->BytesPerCluster];
\r
483 int remLength = Length;
\r
484 Uint32 cluster, tmpCluster;
\r
485 int bNewCluster = 0;
\r
487 if(Offset > Node->Size) return 0;
\r
490 cluster = Node->Inode & 0xFFFFFFFF;
\r
491 while( Offset > disk->BytesPerCluster )
\r
493 cluster = FAT_int_GetFatValue( disk, cluster );
\r
494 if(cluster == -1) {
\r
495 Log_Warning("FAT", "EOC Unexpectedly Reached");
\r
498 Offset -= disk->BytesPerCluster;
\r
500 if( Offset == disk->BytesPerCluster )
\r
502 Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);
\r
505 Offset -= disk->BytesPerCluster;
\r
508 if( Offset + Length < disk->BytesPerCluster )
\r
510 char tmpBuf[disk->BytesPerCluster];
\r
512 // Read-Modify-Write
\r
513 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
514 memcpy( tmpBuf + Offset, Buffer, Length );
\r
515 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
520 // Clean up changes within a cluster
\r
523 // Read-Modify-Write
\r
524 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
525 memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );
\r
526 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
528 remLength -= disk->BytesPerCluster - Offset;
\r
529 Buffer += disk->BytesPerCluster - Offset;
\r
531 // Get next cluster (allocating if needed)
\r
532 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
533 if(tmpCluster == -1) {
\r
534 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
535 if( tmpCluster == 0 ) {
\r
536 return Length - remLength;
\r
539 cluster = tmpCluster;
\r
542 while( remLength > disk->BytesPerCluster )
\r
544 FAT_int_WriteCluster( disk, cluster, Buffer );
\r
545 Buffer += disk->BytesPerCluster;
\r
547 // Get next cluster (allocating if needed)
\r
548 tmpCluster = FAT_int_GetFatValue(disk, cluster);
\r
549 if(tmpCluster == -1) {
\r
551 tmpCluster = FAT_int_AllocateCluster(disk, cluster);
\r
552 if( tmpCluster == 0 ) {
\r
553 return Length - remLength;
\r
556 cluster = tmpCluster;
\r
561 memset(tmpBuf, 0, disk->BytesPerCluster);
\r
563 FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );
\r
564 memcpy( tmpBuf, Buffer, remLength );
\r
565 FAT_int_WriteCluster( disk, cluster, tmpBuf );
\r
573 * \fn void FAT_CloseFile(tVFS_Node *Node)
\r
574 * \brief Close an open file
\r
576 void FAT_CloseFile(tVFS_Node *Node)
\r
578 tFAT_VolInfo *disk = Node->ImplPtr;
\r
579 if(Node == NULL) return ;
\r
582 // Update the node if it's dirty (don't bother if it's marked for
\r
584 if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )
\r
587 tVFS_Node *dirnode;
\r
589 dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);
\r
591 Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);
\r
595 int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);
\r
596 ft.size = Node->Size;
\r
597 // TODO: update adate, mtime, mdate
\r
598 FAT_int_WriteDirEntry(dirnode, id, &ft);
\r
600 dirnode->Type->Close(dirnode);
\r
602 Node->ImplInt &= ~FAT_FLAG_DIRTY;
\r
606 Uint32 cluster = Node->Inode;
\r
607 Uint32 implint = Node->ImplInt;
\r
610 if( FAT_int_DerefNode(Node) == 1 )
\r
612 LOG("implint = %x", implint);
\r
614 if( implint & FAT_FLAG_DELETE ) {
\r
615 Log_Debug("FAT", "Deallocating chain stating at 0x%07x", cluster);
\r
616 // Since the node is marked, we only need to remove it's data
\r
617 while( cluster != -1 )
\r
618 cluster = FAT_int_FreeCluster(disk, cluster);
\r