Modules/FAT - Slight improvement to detection code
[tpg/acess2.git] / KernelLand / Modules / Filesystems / FAT / fat.c
1 /*\r
2  * Acess 2\r
3  * FAT12/16/32 Driver Version (Incl LFN)\r
4  * \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
7  * \r
8  * Known Bugs:\r
9  * - LFN Is buggy in FAT_ReadDir\r
10  * \r
11  * Notes:\r
12  * - There's hard-coded 512 byte sectors everywhere, that needs to be\r
13  *   cleaned.\r
14  * - Thread safety is out the window with the write and LFN code\r
15  */\r
16 /**\r
17  * \todo Implement changing of the parent directory when a file is written to\r
18  * \todo Implement file creation / deletion\r
19  */\r
20 #define DEBUG   0\r
21 #define VERBOSE 1\r
22 \r
23 #include <acess.h>\r
24 #include <modules.h>\r
25 #include "common.h"\r
26 \r
27 // === PROTOTYPES ===\r
28 // --- Driver Core\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
33 // --- Helpers\r
34  int    FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);\r
35 // --- File IO\r
36 size_t  FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);\r
37 #if SUPPORT_WRITE\r
38 size_t  FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer);\r
39 #endif\r
40 void    FAT_CloseFile(tVFS_Node *node);\r
41 \r
42 \r
43 // === Options ===\r
44  int    giFAT_MaxCachedClusters = 1024*512/4;\r
45 \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
51         .Name = "fat",\r
52         .Detect = FAT_Detect,\r
53         .InitDevice = FAT_InitDevice,\r
54         .Unmount = FAT_Unmount,\r
55         .GetNodeFromINode = FAT_GetNodeFromINode\r
56 };\r
57 tVFS_NodeType   gFAT_DirType = {\r
58         .TypeName = "FAT-Dir",\r
59         .ReadDir = FAT_ReadDir,\r
60         .FindDir = FAT_FindDir,\r
61         #if SUPPORT_WRITE\r
62         .MkNod = FAT_Mknod,\r
63         .Link = FAT_Link,\r
64         .Unlink = FAT_Unlink,\r
65         #endif\r
66         .Close = FAT_CloseFile\r
67         };\r
68 tVFS_NodeType   gFAT_FileType = {\r
69         .TypeName = "FAT-File",\r
70         .Read = FAT_Read,\r
71         #if SUPPORT_WRITE\r
72         .Write = FAT_Write,\r
73         #endif\r
74         .Close = FAT_CloseFile\r
75         };\r
76 \r
77 // === CODE ===\r
78 /**\r
79  * \fn int FAT_Install(char **Arguments)\r
80  * \brief Install the FAT Driver\r
81  */\r
82 int FAT_Install(char **Arguments)\r
83 {\r
84         VFS_AddDriver( &gFAT_FSInfo );\r
85         return MODULE_ERR_OK;\r
86 }\r
87 \r
88 /**\r
89  * \brief Detect if a file is a FAT device\r
90  */\r
91 int FAT_Detect(int FD)\r
92 {\r
93         fat_bootsect bs;\r
94         \r
95         if( VFS_ReadAt(FD, 0, 512, &bs) != 512) {\r
96                 return 0;\r
97         }\r
98 \r
99         if(bs.bps == 0 || bs.spc == 0)\r
100                 return 0;\r
101 \r
102         Log_Debug("FAT", "_Detect: Media type = %02x", bs.mediaDesc);\r
103         if( bs.mediaDesc < 0xF0 )\r
104                 return 0;\r
105 \r
106         return 1;\r
107 }\r
108 /**\r
109  * \brief Reads the boot sector of a disk and prepares the structures for it\r
110  */\r
111 tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)\r
112 {\r
113         fat_bootsect *bs;\r
114          int    i;\r
115         Uint32  FATSz, RootDirSectors, TotSec;\r
116         tVFS_Node       *node = NULL;\r
117         tFAT_VolInfo    *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
118 \r
119         memset(diskInfo, 0, sizeof(*diskInfo));\r
120         \r
121         // Temporary Pointer\r
122         bs = &diskInfo->bootsect;\r
123         \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
128                 return NULL;\r
129         }\r
130         \r
131         VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);\r
132         \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
136                 return NULL;\r
137         }\r
138         \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
142         \r
143         if(bs->fatSz16 != 0)\r
144                 FATSz = bs->fatSz16;\r
145         else\r
146                 FATSz = bs->spec.fat32.fatSz32;\r
147         \r
148         if(bs->totalSect16 != 0)\r
149                 TotSec = bs->totalSect16;\r
150         else\r
151                 TotSec = bs->totalSect32;\r
152         \r
153         diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
154         \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
159         else\r
160                 diskInfo->type = FAT32;\r
161         \r
162         #if VERBOSE\r
163         {\r
164                 char    *sFatType, *sSize;\r
165                 Uint    iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;\r
166                 \r
167                 switch(diskInfo->type)\r
168                 {\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
173                 }\r
174                 if(iSize <= 2*1024) {\r
175                         sSize = "KiB";\r
176                 }\r
177                 else if(iSize <= 2*1024*1024) {\r
178                         sSize = "MiB";\r
179                         iSize >>= 10;\r
180                 }\r
181                 else {\r
182                         sSize = "GiB";\r
183                         iSize >>= 20;\r
184                 }\r
185                 Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);\r
186         }\r
187         #endif\r
188         \r
189         // Get Name\r
190         if(diskInfo->type == FAT32) {\r
191                 for(i=0;i<11;i++)\r
192                         diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);\r
193         }\r
194         else {\r
195                 for(i=0;i<11;i++)\r
196                         diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);\r
197         }\r
198         diskInfo->name[11] = '\0';\r
199         \r
200         // Compute Root directory offset\r
201         if(diskInfo->type == FAT32)\r
202                 diskInfo->rootOffset = bs->spec.fat32.rootClust;\r
203         else\r
204                 diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;\r
205         \r
206         diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;\r
207         \r
208         //Allow for Caching the FAT\r
209         #if CACHE_FAT\r
210         if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )\r
211         {\r
212                 Uint32  Ofs;\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
217                         return NULL;\r
218                 }\r
219                 Ofs = bs->resvSectCount*512;\r
220                 if(diskInfo->type == FAT12)\r
221                 {\r
222                         Uint32  val;\r
223                          int    j;\r
224                         char    buf[1536];\r
225                         for(i = 0; i < diskInfo->ClusterCount/2; i++) {\r
226                                 j = i & 511;    //%512\r
227                                 if( j == 0 ) {\r
228                                         VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);\r
229                                         Ofs += 3*512;\r
230                                 }\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
234                         }\r
235                 }\r
236                 else if(diskInfo->type == FAT16)\r
237                 {\r
238                         Uint16  buf[256];\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
242                                         Ofs += 512;\r
243                                 }\r
244                                 diskInfo->FATCache[i] = buf[i&255];\r
245                         }\r
246                 }\r
247                 else if(diskInfo->type == FAT32)\r
248                 {\r
249                         Uint32  buf[128];\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
253                                         Ofs += 512;\r
254                                 }\r
255                                 diskInfo->FATCache[i] = buf[i&127];\r
256                         }\r
257                 }\r
258                 LOG("FAT Fully Cached");\r
259         }\r
260         #endif /*CACHE_FAT*/\r
261         \r
262         diskInfo->BytesPerCluster = bs->spc * bs->bps;\r
263         \r
264         // == VFS Interface\r
265         node = &diskInfo->rootNode;\r
266         //node->Size = bs->files_in_root;\r
267         node->Size = -1;\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
271         \r
272         node->ReferenceCount = 1;\r
273         \r
274         node->UID = 0;  node->GID = 0;\r
275         node->NumACLs = 1;\r
276         node->ACLs = &gVFS_ACL_EveryoneRWX;\r
277         node->Flags = VFS_FFLAG_DIRECTORY;\r
278         node->CTime = node->MTime = node->ATime = now();\r
279 \r
280         node->Type = &gFAT_DirType;     \r
281         \r
282         giFAT_PartCount ++;\r
283         return node;\r
284 }\r
285 \r
286 /**\r
287  * \brief Closes a mount and marks it as free\r
288  * \param Node  Mount Root\r
289  * \r
290  * \todo Remove FAT Cache\r
291  * \todo Clear LFN Cache\r
292  * \todo Check that all files are closed and flushed\r
293  */\r
294 void FAT_Unmount(tVFS_Node *Node)\r
295 {\r
296         tFAT_VolInfo    *disk = Node->ImplPtr;\r
297         \r
298         // Close Disk Handle\r
299         VFS_Close( disk->fileHandle );\r
300         // Clear Node Cache\r
301         FAT_int_ClearNodeCache(disk);\r
302         // Mark as unused\r
303         disk->fileHandle = -2;\r
304         return;\r
305 }\r
306 \r
307 /**\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
314  *                  directory.\r
315  * \return Zero on success, non-zero on error\r
316  */\r
317 int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)\r
318 {\r
319         Uint32  cluster, base_cluster;\r
320         Uint64  addr;\r
321          int    skip;\r
322         tFAT_VolInfo    *disk = Node->ImplPtr;\r
323         \r
324         ENTER("pNode XOffset", Node, Offset);\r
325         \r
326         cluster = base_cluster = Node->Inode & 0xFFFFFFF;       // Cluster ID\r
327 //      LOG("base cluster = 0x%07x", cluster);\r
328         \r
329         // Do Cluster Skip\r
330         // - Pre FAT32 had a reserved area for the root.\r
331         if( disk->type == FAT32 || cluster != disk->rootOffset )\r
332         {\r
333                 skip = Offset / disk->BytesPerCluster;\r
334                 LOG("skip = %i", skip);\r
335                 // Skip previous clusters\r
336                 for(; skip-- ; )\r
337                 {\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
342                 }\r
343                 if(Cluster)     *Cluster = cluster;\r
344         }\r
345         else {\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
350         }\r
351         \r
352 //      LOG("cluster = 0x%07x", cluster);\r
353         \r
354         // Bounds Checking (Used to spot corruption)\r
355         if(cluster > disk->ClusterCount + 2)\r
356         {\r
357                 Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",\r
358                         cluster, disk->ClusterCount+2);\r
359                 LEAVE('i', 1);\r
360                 return 1;\r
361         }\r
362         \r
363         // Compute Offsets\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
368         }\r
369         else {\r
370                 addr = disk->firstDataSect * disk->bootsect.bps;\r
371                 addr += (cluster - 2) * disk->BytesPerCluster;\r
372         }\r
373         // In-cluster offset\r
374         addr += Offset % disk->BytesPerCluster;\r
375         \r
376         LOG("addr = 0x%08x", addr);\r
377         *Addr = addr;\r
378         LEAVE('i', 0);\r
379         return 0;\r
380 }\r
381 \r
382 /* ====================\r
383  *       File IO\r
384  * ====================\r
385  */\r
386 /**\r
387  * \brief Reads data from a specified file\r
388  */\r
389 size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)\r
390 {\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
397         \r
398         ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);\r
399         \r
400         // Sanity Check offset\r
401         if(Offset > Node->Size) {\r
402                 LOG("Seek past EOF (%i > %i)", Offset, Node->Size);\r
403                 LEAVE('i', 0);\r
404                 return 0;\r
405         }\r
406         \r
407         // Cluster is stored in the low 32-bits of the Inode field\r
408         cluster = Node->Inode & 0xFFFFFFFF;\r
409         \r
410         // Clamp Size\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
415         }\r
416         \r
417         // Skip previous clusters\r
418         preSkip = Offset / bpc;\r
419         Offset %= bpc;\r
420         LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);\r
421         for(i = preSkip; i--; )\r
422         {\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
426                         LEAVE('i', 0);\r
427                         return 0;\r
428                 }\r
429         }\r
430 \r
431         // Reading from within one cluster\r
432         if((int)Offset + (int)Length <= bpc)\r
433         {\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
438                 return Length;\r
439         }\r
440         \r
441         // Align read to a cluster\r
442         if( Offset > 0 )\r
443         {\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
452                         LEAVE('X', pos);\r
453                         return pos;\r
454                 }\r
455         }\r
456         else\r
457                 pos = 0;\r
458 \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
464         \r
465         // Read the rest of the cluster data\r
466         for( ; count; count -- )\r
467         {\r
468                 if(cluster == GETFATVALUE_EOC) {\r
469                         Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");\r
470                         LEAVE('X', pos);\r
471                         return pos;\r
472                 }\r
473                 // Read cluster\r
474                 FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));\r
475                 pos += bpc;\r
476                 // Get next cluster in the chain\r
477                 cluster = FAT_int_GetFatValue(disk, cluster);\r
478         }\r
479 \r
480         if( final_bytes > 0 )\r
481         {\r
482                 if(cluster == -1) {\r
483                         Log_Warning("FAT", "Read past End of Cluster Chain (Final)");\r
484                         LEAVE('X', pos);\r
485                         return pos;\r
486                 }\r
487                 // Read final cluster\r
488                 FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );\r
489                 memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );\r
490         }\r
491                 \r
492         #if DEBUG\r
493         //Debug_HexDump("FAT_Read", Buffer, Length);\r
494         #endif\r
495         \r
496         LEAVE('X', Length);\r
497         return Length;\r
498 }\r
499 \r
500 #if SUPPORT_WRITE\r
501 /**\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
507  */\r
508 size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)\r
509 {\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
516         \r
517         if(Offset > Node->Size) return 0;\r
518         \r
519         ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);\r
520         \r
521         // Seek Clusters\r
522         cluster = Node->Inode & 0xFFFFFFFF;\r
523         while( Offset > disk->BytesPerCluster )\r
524         {\r
525                 cluster = FAT_int_GetFatValue( disk, cluster );\r
526                 if(cluster == GETFATVALUE_EOC) {\r
527                         Log_Warning("FAT", "EOC Unexpectedly Reached");\r
528                         LEAVE('i', 0);\r
529                         return 0;\r
530                 }\r
531                 Offset -= disk->BytesPerCluster;\r
532         }\r
533         if( Offset == disk->BytesPerCluster )\r
534         {\r
535                 Uint32  tmp = FAT_int_AllocateCluster(disk, cluster);\r
536                 if(!tmp) {\r
537                         LEAVE('i', 0);\r
538                         return 0;\r
539                 }\r
540                 cluster = tmp;\r
541                 Offset -= disk->BytesPerCluster;\r
542         }\r
543         \r
544         if( Offset + Length < disk->BytesPerCluster )\r
545         {\r
546                 char    tmpBuf[disk->BytesPerCluster];\r
547                 \r
548                 LOG("Read-Modify-Write single");\r
549                 \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
554                 goto return_full;\r
555         }\r
556         \r
557         // Clean up changes within a cluster\r
558         if( Offset )\r
559         {       \r
560                 LOG("Read-Modify-Write first");\r
561 \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
566                 \r
567                 remLength -= disk->BytesPerCluster - Offset;\r
568                 Buffer += disk->BytesPerCluster - Offset;\r
569                 \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
576                 }\r
577                 cluster = tmpCluster;\r
578         }\r
579         \r
580         while( remLength > disk->BytesPerCluster )\r
581         {\r
582                 FAT_int_WriteCluster( disk, cluster, Buffer );\r
583                 Buffer += disk->BytesPerCluster;\r
584                 remLength -= disk->BytesPerCluster;\r
585                 \r
586                 // Get next cluster (allocating if needed)\r
587                 tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
588                 if(tmpCluster == GETFATVALUE_EOC) {\r
589                         bNewCluster = 1;\r
590                         tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
591                         if( tmpCluster == 0 )\r
592                                 goto ret_incomplete;\r
593                 }\r
594                 cluster = tmpCluster;\r
595         }\r
596         \r
597         // Finish off\r
598         if( remLength )\r
599         {\r
600                 if( bNewCluster )\r
601                         memset(tmpBuf, 0, disk->BytesPerCluster);\r
602                 else\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
606         }\r
607 \r
608 return_full:\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
613         }\r
614 \r
615         LEAVE('i', Length);\r
616         return Length;\r
617 ret_incomplete:\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
623         }\r
624         LEAVE('i', Length);\r
625         return Length;\r
626 }\r
627 #endif\r
628 \r
629 /**\r
630  * \fn void FAT_CloseFile(tVFS_Node *Node)\r
631  * \brief Close an open file\r
632  */\r
633 void FAT_CloseFile(tVFS_Node *Node)\r
634 {\r
635         tFAT_VolInfo    *disk = Node->ImplPtr;\r
636         if(Node == NULL)        return ;\r
637 \r
638         ENTER("pNode", Node);   \r
639 \r
640         #if SUPPORT_WRITE\r
641         // Update the node if it's dirty (don't bother if it's marked for\r
642         // deletion)\r
643         if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )\r
644         {\r
645                 fat_filetable   ft;\r
646                 tVFS_Node       *dirnode;\r
647 \r
648                 dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);\r
649                 if( !dirnode ) {\r
650                         Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);\r
651                         LEAVE('-');\r
652                         return ;\r
653                 }\r
654 \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
659                 \r
660                 dirnode->Type->Close(dirnode);\r
661                 \r
662                 Node->ImplInt &= ~FAT_FLAG_DIRTY;\r
663         }\r
664         #endif\r
665 \r
666         Uint32  cluster = Node->Inode;\r
667         Uint32  implint = Node->ImplInt;\r
668         \r
669         #if SUPPORT_WRITE\r
670         if( FAT_int_DerefNode(Node) == 1 )\r
671         {\r
672                 LOG("implint = %x", implint);\r
673                 // Delete File\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
679                 }\r
680         }\r
681         #endif\r
682         LEAVE('-');\r
683 }\r

UCC git Repository :: git.ucc.asn.au