Sync with trunk head
[reactos.git] / lib / 3rdparty / fullfat / ff_fat.c
1 /*****************************************************************************
2 * FullFAT - High Performance, Thread-Safe Embedded FAT File-System *
3 * Copyright (C) 2009 James Walmsley (james@worm.me.uk) *
4 * *
5 * This program is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 * *
18 * IMPORTANT NOTICE: *
19 * ================= *
20 * Alternative Licensing is available directly from the Copyright holder, *
21 * (James Walmsley). For more information consult LICENSING.TXT to obtain *
22 * a Commercial license. *
23 * *
24 * See RESTRICTIONS.TXT for extra restrictions on the use of FullFAT. *
25 * *
26 * Removing the above notice is illegal and will invalidate this license. *
27 *****************************************************************************
28 * See http://worm.me.uk/fullfat for more information. *
29 * Or http://fullfat.googlecode.com/ for latest releases and the wiki. *
30 *****************************************************************************/
31
32 /**
33 * @file ff_fat.c
34 * @author James Walmsley
35 * @ingroup FAT
36 *
37 * @defgroup FAT Fat File-System
38 * @brief Handles FAT access and traversal.
39 *
40 * Provides file-system interfaces for the FAT file-system.
41 **/
42
43 #include "ff_fat.h"
44 #include "ff_config.h"
45 #include <string.h>
46
47 void FF_lockFAT(FF_IOMAN *pIoman) {
48 FF_PendSemaphore(pIoman->pSemaphore); // Use Semaphore to protect FAT modifications.
49 {
50 while((pIoman->Locks & FF_FAT_LOCK)) {
51 FF_ReleaseSemaphore(pIoman->pSemaphore);
52 FF_Yield(); // Keep Releasing and Yielding until we have the Fat protector.
53 FF_PendSemaphore(pIoman->pSemaphore);
54 }
55 pIoman->Locks |= FF_FAT_LOCK;
56 }
57 FF_ReleaseSemaphore(pIoman->pSemaphore);
58 }
59
60 void FF_unlockFAT(FF_IOMAN *pIoman) {
61 FF_PendSemaphore(pIoman->pSemaphore);
62 {
63 pIoman->Locks &= ~FF_FAT_LOCK;
64 }
65 FF_ReleaseSemaphore(pIoman->pSemaphore);
66 }
67
68 /**
69 * @private
70 **/
71 FF_T_UINT32 FF_getRealLBA(FF_IOMAN *pIoman, FF_T_UINT32 LBA) {
72 return LBA * pIoman->pPartition->BlkFactor;
73 }
74
75 /**
76 * @private
77 **/
78 FF_T_UINT32 FF_Cluster2LBA(FF_IOMAN *pIoman, FF_T_UINT32 Cluster) {
79 FF_T_UINT32 lba = 0;
80 FF_PARTITION *pPart;
81 if(pIoman) {
82 pPart = pIoman->pPartition;
83
84 if(Cluster > 1) {
85 lba = ((Cluster - 2) * pPart->SectorsPerCluster) + pPart->FirstDataSector;
86 } else {
87 lba = pPart->ClusterBeginLBA;
88 }
89 }
90 return lba;
91 }
92
93 /**
94 * @private
95 **/
96 FF_T_UINT32 FF_LBA2Cluster(FF_IOMAN *pIoman, FF_T_UINT32 Address) {
97 FF_T_UINT32 cluster = 0;
98 FF_PARTITION *pPart;
99 if(pIoman) {
100 pPart = pIoman->pPartition;
101 if(pPart->Type == FF_T_FAT32) {
102 cluster = ((Address - pPart->ClusterBeginLBA) / pPart->SectorsPerCluster) + 2;
103 } else {
104 cluster = ((Address - pPart->ClusterBeginLBA) / pPart->SectorsPerCluster);
105 }
106 }
107 return cluster;
108 }
109
110 /**
111 * @private
112 **/
113 FF_T_SINT32 FF_getFatEntry(FF_IOMAN *pIoman, FF_T_UINT32 nCluster) {
114
115 FF_BUFFER *pBuffer;
116 FF_T_UINT32 FatOffset;
117 FF_T_UINT32 FatSector;
118 FF_T_UINT32 FatSectorEntry;
119 FF_T_UINT32 FatEntry;
120 FF_T_UINT8 LBAadjust;
121 FF_T_UINT16 relClusterEntry;
122
123 #ifdef FF_FAT12_SUPPORT
124 FF_T_UINT8 F12short[2]; // For FAT12 FAT Table Across sector boundary traversal.
125 #endif
126
127 if(pIoman->pPartition->Type == FF_T_FAT32) {
128 FatOffset = nCluster * 4;
129 } else if(pIoman->pPartition->Type == FF_T_FAT16) {
130 FatOffset = nCluster * 2;
131 }else {
132 FatOffset = nCluster + (nCluster / 2);
133 }
134
135 FatSector = pIoman->pPartition->FatBeginLBA + (FatOffset / pIoman->pPartition->BlkSize);
136 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
137
138 LBAadjust = (FF_T_UINT8) (FatSectorEntry / pIoman->BlkSize);
139 relClusterEntry = (FF_T_UINT16) (FatSectorEntry % pIoman->BlkSize);
140
141 FatSector = FF_getRealLBA(pIoman, FatSector);
142
143 #ifdef FF_FAT12_SUPPORT
144 if(pIoman->pPartition->Type == FF_T_FAT12) {
145 if(relClusterEntry == (pIoman->BlkSize - 1)) {
146 // Fat Entry SPANS a Sector!
147 // First Buffer get the last Byte in buffer (first byte of our address)!
148 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust, FF_MODE_READ);
149 {
150 if(!pBuffer) {
151 return FF_ERR_DEVICE_DRIVER_FAILED;
152 }
153 F12short[0] = FF_getChar(pBuffer->pBuffer, (FF_T_UINT16)(pIoman->BlkSize - 1));
154 }
155 FF_ReleaseBuffer(pIoman, pBuffer);
156 // Second Buffer get the first Byte in buffer (second byte of out address)!
157 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust + 1, FF_MODE_READ);
158 {
159 if(!pBuffer) {
160 return FF_ERR_DEVICE_DRIVER_FAILED;
161 }
162 F12short[1] = FF_getChar(pBuffer->pBuffer, 0);
163 }
164 FF_ReleaseBuffer(pIoman, pBuffer);
165
166 FatEntry = (FF_T_UINT32) FF_getShort((FF_T_UINT8*)&F12short, 0); // Guarantee correct Endianess!
167
168 if(nCluster & 0x0001) {
169 FatEntry = FatEntry >> 4;
170 }
171 FatEntry &= 0x0FFF;
172 return (FF_T_SINT32) FatEntry;
173 }
174 }
175 #endif
176 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust, FF_MODE_READ);
177 {
178 if(!pBuffer) {
179 return FF_ERR_DEVICE_DRIVER_FAILED;
180 }
181
182 switch(pIoman->pPartition->Type) {
183 case FF_T_FAT32:
184 FatEntry = FF_getLong(pBuffer->pBuffer, relClusterEntry);
185 FatEntry &= 0x0fffffff; // Clear the top 4 bits.
186 break;
187
188 case FF_T_FAT16:
189 FatEntry = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, relClusterEntry);
190 break;
191
192 #ifdef FF_FAT12_SUPPORT
193 case FF_T_FAT12:
194 FatEntry = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, relClusterEntry);
195 if(nCluster & 0x0001) {
196 FatEntry = FatEntry >> 4;
197 }
198 FatEntry &= 0x0FFF;
199 break;
200 #endif
201 default:
202 FatEntry = 0;
203 break;
204 }
205 }
206 FF_ReleaseBuffer(pIoman, pBuffer);
207
208 return (FF_T_SINT32) FatEntry;
209 }
210
211 FF_T_SINT8 FF_ClearCluster(FF_IOMAN *pIoman, FF_T_UINT32 nCluster) {
212 FF_BUFFER *pBuffer;
213 FF_T_UINT16 i;
214 FF_T_UINT32 BaseLBA;
215 FF_T_SINT8 RetVal = 0;
216
217 BaseLBA = FF_Cluster2LBA(pIoman, nCluster);
218 BaseLBA = FF_getRealLBA(pIoman, BaseLBA);
219
220 for(i = 0; i < pIoman->pPartition->SectorsPerCluster; i++) {
221 pBuffer = FF_GetBuffer(pIoman, BaseLBA++, FF_MODE_WRITE);
222 {
223 if(pBuffer) {
224 memset(pBuffer->pBuffer, 0x00, 512);
225 } else {
226 RetVal = FF_ERR_DEVICE_DRIVER_FAILED;
227 }
228 }
229 FF_ReleaseBuffer(pIoman, pBuffer);
230 }
231
232 return RetVal;
233 }
234
235 /**
236 * @private
237 * @brief Returns the Cluster address of the Cluster number from the beginning of a chain.
238 *
239 * @param pIoman FF_IOMAN Object
240 * @param Start Cluster address of the first cluster in the chain.
241 * @param Count Number of Cluster in the chain,
242 *
243 * @return FF_TRUE if it is an end of chain, otherwise FF_FALSE.
244 *
245 **/
246 FF_T_UINT32 FF_TraverseFAT(FF_IOMAN *pIoman, FF_T_UINT32 Start, FF_T_UINT32 Count) {
247
248 FF_T_UINT32 i;
249 FF_T_UINT32 fatEntry = Start, currentCluster = Start;
250
251 for(i = 0; i < Count; i++) {
252 fatEntry = FF_getFatEntry(pIoman, currentCluster);
253 if(fatEntry == (FF_T_UINT32) FF_ERR_DEVICE_DRIVER_FAILED) {
254 return 0;
255 }
256
257 if(FF_isEndOfChain(pIoman, fatEntry)) {
258 return currentCluster;
259 } else {
260 currentCluster = fatEntry;
261 }
262 }
263
264 return fatEntry;
265 }
266
267 FF_T_UINT32 FF_FindEndOfChain(FF_IOMAN *pIoman, FF_T_UINT32 Start) {
268
269 FF_T_UINT32 fatEntry = Start, currentCluster = Start;
270
271 while(!FF_isEndOfChain(pIoman, fatEntry)) {
272 fatEntry = FF_getFatEntry(pIoman, currentCluster);
273 if(fatEntry == (FF_T_UINT32) FF_ERR_DEVICE_DRIVER_FAILED) {
274 return 0;
275 }
276
277 if(FF_isEndOfChain(pIoman, fatEntry)) {
278 return currentCluster;
279 } else {
280 currentCluster = fatEntry;
281 }
282 }
283
284 return fatEntry;
285 }
286
287
288 /**
289 * @private
290 * @brief Tests if the fatEntry is an End of Chain Marker.
291 *
292 * @param pIoman FF_IOMAN Object
293 * @param fatEntry The fat entry from the FAT table to be checked.
294 *
295 * @return FF_TRUE if it is an end of chain, otherwise FF_FALSE.
296 *
297 **/
298 FF_T_BOOL FF_isEndOfChain(FF_IOMAN *pIoman, FF_T_UINT32 fatEntry) {
299 FF_T_BOOL result = FF_FALSE;
300 if(pIoman->pPartition->Type == FF_T_FAT32) {
301 if((fatEntry & 0x0fffffff) >= 0x0ffffff8) {
302 result = FF_TRUE;
303 }
304 } else if(pIoman->pPartition->Type == FF_T_FAT16) {
305 if(fatEntry >= 0x0000fff8) {
306 result = FF_TRUE;
307 }
308 } else {
309 if(fatEntry >= 0x00000ff8) {
310 result = FF_TRUE;
311 }
312 }
313 if(fatEntry == 0x00000000) {
314 result = FF_TRUE; //Perhaps trying to read a deleted file!
315 }
316 return result;
317 }
318
319
320 /**
321 * @private
322 * @brief Writes a new Entry to the FAT Tables.
323 *
324 * @param pIoman IOMAN object.
325 * @param nCluster Cluster Number to be modified.
326 * @param Value The Value to store.
327 **/
328 FF_T_SINT8 FF_putFatEntry(FF_IOMAN *pIoman, FF_T_UINT32 nCluster, FF_T_UINT32 Value) {
329
330 FF_BUFFER *pBuffer;
331 FF_T_UINT32 FatOffset;
332 FF_T_UINT32 FatSector;
333 FF_T_UINT32 FatSectorEntry;
334 FF_T_UINT32 FatEntry;
335 FF_T_UINT8 LBAadjust;
336 FF_T_UINT16 relClusterEntry;
337 #ifdef FF_FAT12_SUPPORT
338 FF_T_UINT8 F12short[2]; // For FAT12 FAT Table Across sector boundary traversal.
339 #endif
340
341 if(pIoman->pPartition->Type == FF_T_FAT32) {
342 FatOffset = nCluster * 4;
343 } else if(pIoman->pPartition->Type == FF_T_FAT16) {
344 FatOffset = nCluster * 2;
345 }else {
346 FatOffset = nCluster + (nCluster / 2);
347 }
348
349 FatSector = pIoman->pPartition->FatBeginLBA + (FatOffset / pIoman->pPartition->BlkSize);
350 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
351
352 LBAadjust = (FF_T_UINT8) (FatSectorEntry / pIoman->BlkSize);
353 relClusterEntry = (FF_T_UINT16)(FatSectorEntry % pIoman->BlkSize);
354
355 FatSector = FF_getRealLBA(pIoman, FatSector);
356
357 #ifdef FF_FAT12_SUPPORT
358 if(pIoman->pPartition->Type == FF_T_FAT12) {
359 if(relClusterEntry == (FF_T_UINT16) (pIoman->BlkSize - 1)) {
360 // Fat Entry SPANS a Sector!
361 // First Buffer get the last Byte in buffer (first byte of our address)!
362 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust, FF_MODE_READ);
363 {
364 if(!pBuffer) {
365 return FF_ERR_DEVICE_DRIVER_FAILED;
366 }
367 F12short[0] = FF_getChar(pBuffer->pBuffer, (FF_T_UINT16)(pIoman->BlkSize - 1));
368 }
369 FF_ReleaseBuffer(pIoman, pBuffer);
370 // Second Buffer get the first Byte in buffer (second byte of out address)!
371 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust + 1, FF_MODE_READ);
372 {
373 if(!pBuffer) {
374 return FF_ERR_DEVICE_DRIVER_FAILED;
375 }
376 F12short[1] = FF_getChar(pBuffer->pBuffer, (FF_T_UINT16) 0x0000);
377 }
378 FF_ReleaseBuffer(pIoman, pBuffer);
379
380
381 FatEntry = FF_getShort((FF_T_UINT8*)&F12short, (FF_T_UINT16) 0x0000); // Guarantee correct Endianess!
382 if(nCluster & 0x0001) {
383 FatEntry &= 0x000F;
384 Value = (Value << 4);
385 Value &= 0xFFF0;
386 } else {
387 FatEntry &= 0xF000;
388 Value &= 0x0FFF;
389 }
390
391 FF_putShort((FF_T_UINT8 *)F12short, 0x0000, (FF_T_UINT16) (FatEntry | Value));
392
393 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust, FF_MODE_WRITE);
394 {
395 if(!pBuffer) {
396 return FF_ERR_DEVICE_DRIVER_FAILED;
397 }
398 FF_putChar(pBuffer->pBuffer, (FF_T_UINT16)(pIoman->BlkSize - 1), F12short[0]);
399 }
400 FF_ReleaseBuffer(pIoman, pBuffer);
401 // Second Buffer get the first Byte in buffer (second byte of out address)!
402 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust + 1, FF_MODE_READ);
403 {
404 if(!pBuffer) {
405 return FF_ERR_DEVICE_DRIVER_FAILED;
406 }
407 FF_putChar(pBuffer->pBuffer, 0x0000, F12short[1]);
408 }
409 FF_ReleaseBuffer(pIoman, pBuffer);
410
411 return FF_ERR_NONE;
412 }
413 }
414 #endif
415
416 pBuffer = FF_GetBuffer(pIoman, FatSector + LBAadjust, FF_MODE_WRITE);
417 {
418 if(!pBuffer) {
419 return FF_ERR_DEVICE_DRIVER_FAILED;
420 }
421 if(pIoman->pPartition->Type == FF_T_FAT32) {
422 Value &= 0x0fffffff; // Clear the top 4 bits.
423 FF_putLong(pBuffer->pBuffer, relClusterEntry, Value);
424 } else if(pIoman->pPartition->Type == FF_T_FAT16) {
425 FF_putShort(pBuffer->pBuffer, relClusterEntry, (FF_T_UINT16) Value);
426 } else {
427 FatEntry = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, relClusterEntry);
428 if(nCluster & 0x0001) {
429 FatEntry &= 0x000F;
430 Value = (Value << 4);
431 Value &= 0xFFF0;
432 } else {
433 FatEntry &= 0xF000;
434 Value &= 0x0FFF;
435 }
436
437 FF_putShort(pBuffer->pBuffer, relClusterEntry, (FF_T_UINT16) (FatEntry | Value));
438 }
439 }
440 FF_ReleaseBuffer(pIoman, pBuffer);
441
442 return 0;
443 }
444
445
446
447 /**
448 * @private
449 * @brief Finds a Free Cluster and returns its number.
450 *
451 * @param pIoman IOMAN Object.
452 *
453 * @return The number of the cluster found to be free.
454 * @return 0 on error.
455 **/
456 #ifdef FF_FAT12_SUPPORT
457 FF_T_UINT32 FF_FindFreeClusterOLD(FF_IOMAN *pIoman) {
458 FF_T_UINT32 nCluster;
459 FF_T_UINT32 fatEntry;
460
461 for(nCluster = pIoman->pPartition->LastFreeCluster; nCluster < pIoman->pPartition->NumClusters; nCluster++) {
462 fatEntry = FF_getFatEntry(pIoman, nCluster);
463 if(fatEntry == 0x00000000) {
464 pIoman->pPartition->LastFreeCluster = nCluster;
465 return nCluster;
466 }
467 }
468 return 0;
469 }
470 #endif
471
472 FF_T_UINT32 FF_FindFreeCluster(FF_IOMAN *pIoman) {
473 FF_BUFFER *pBuffer;
474 FF_T_UINT32 i, x, nCluster = pIoman->pPartition->LastFreeCluster;
475 FF_T_UINT32 FatOffset;
476 FF_T_UINT32 FatSector;
477 FF_T_UINT32 FatSectorEntry;
478 FF_T_UINT32 EntriesPerSector;
479 FF_T_UINT32 FatEntry = 1;
480
481 #ifdef FF_FAT12_SUPPORT
482 if(pIoman->pPartition->Type == FF_T_FAT12) { // FAT12 tables are too small to optimise, and would make it very complicated!
483 return FF_FindFreeClusterOLD(pIoman);
484 }
485 #endif
486
487 if(pIoman->pPartition->Type == FF_T_FAT32) {
488 EntriesPerSector = pIoman->BlkSize / 4;
489 FatOffset = nCluster * 4;
490 } else {
491 EntriesPerSector = pIoman->BlkSize / 2;
492 FatOffset = nCluster * 2;
493 }
494
495 FatSector = (FatOffset / pIoman->pPartition->BlkSize);
496
497 for(i = FatSector; i < pIoman->pPartition->SectorsPerFAT; i++) {
498 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA + i, FF_MODE_READ);
499 {
500 for(x = nCluster % EntriesPerSector; x < EntriesPerSector; x++) {
501 if(pIoman->pPartition->Type == FF_T_FAT32) {
502 FatOffset = x * 4;
503 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
504 FatEntry = FF_getLong(pBuffer->pBuffer, (FF_T_UINT16)FatSectorEntry);
505 FatEntry &= 0x0fffffff; // Clear the top 4 bits.
506 } else {
507 FatOffset = x * 2;
508 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
509 FatEntry = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, (FF_T_UINT16)FatSectorEntry);
510 }
511 if(FatEntry == 0x00000000) {
512 FF_ReleaseBuffer(pIoman, pBuffer);
513 pIoman->pPartition->LastFreeCluster = nCluster;
514
515 return nCluster;
516 }
517
518 nCluster++;
519 }
520 }
521 FF_ReleaseBuffer(pIoman, pBuffer);
522 }
523
524 return 0;
525 }
526
527 /**
528 * @private
529 * @brief Create's a Cluster Chain
530 **/
531 FF_T_UINT32 FF_CreateClusterChain(FF_IOMAN *pIoman) {
532 FF_T_UINT32 iStartCluster;
533 FF_lockFAT(pIoman);
534 {
535 iStartCluster = FF_FindFreeCluster(pIoman);
536 FF_putFatEntry(pIoman, iStartCluster, 0xFFFFFFFF); // Mark the cluster as EOC
537 }
538 FF_unlockFAT(pIoman);
539 return iStartCluster;
540 }
541
542 FF_T_UINT32 FF_GetChainLength(FF_IOMAN *pIoman, FF_T_UINT32 pa_nStartCluster, FF_T_UINT32 *piEndOfChain) {
543 FF_T_UINT32 iLength = 0;
544
545 FF_lockFAT(pIoman);
546 {
547 while(!FF_isEndOfChain(pIoman, pa_nStartCluster)) {
548 pa_nStartCluster = FF_getFatEntry(pIoman, pa_nStartCluster);
549 iLength++;
550 }
551 if(piEndOfChain) {
552 *piEndOfChain = pa_nStartCluster;
553 }
554 }
555 FF_unlockFAT(pIoman);
556
557 return iLength;
558 }
559
560 /**
561 * @private
562 * @brief Extend a Cluster chain by Count number of Clusters
563 *
564 * @param pIoman IOMAN object.
565 * @param StartCluster Cluster Number that starts the chain.
566 * @param Count Number of clusters to extend the chain with.
567 *
568 **/
569 /*
570 FF_T_UINT32 FF_ExtendClusterChain(FF_IOMAN *pIoman, FF_T_UINT32 StartCluster, FF_T_UINT32 Count) {
571
572 FF_T_UINT32 currentCluster = StartCluster, nextCluster;
573 FF_T_UINT32 clusEndOfChain;
574 FF_T_UINT32 i;
575
576 clusEndOfChain = FF_FindEndOfChain(pIoman, StartCluster);
577
578 nextCluster = FF_FindFreeCluster(pIoman); // Find Free clusters!
579
580 FF_putFatEntry(pIoman, clusEndOfChain, nextCluster);
581
582 for(i = 0; i <= Count; i++) {
583 currentCluster = nextCluster;
584 if(i == Count) {
585 FF_putFatEntry(pIoman, currentCluster, 0xFFFFFFFF);
586 break;
587 }
588
589 nextCluster = FF_FindFreeCluster(pIoman);
590 FF_putFatEntry(pIoman, currentCluster, ++nextCluster);
591 }
592 FF_FlushCache(pIoman);
593 return currentCluster;
594 }*/
595
596
597 /**
598 * @private
599 * @brief Free's Disk space by freeing unused links on Cluster Chains
600 *
601 * @param pIoman, IOMAN object.
602 * @param StartCluster Cluster Number that starts the chain.
603 * @param Count Number of Clusters from the end of the chain to unlink.
604 * @param Count 0 Means Free the entire chain (delete file).
605 *
606 * @return 0 On Success.
607 * @return -1 If the device driver failed to provide access.
608 *
609 **/
610 FF_T_SINT8 FF_UnlinkClusterChain(FF_IOMAN *pIoman, FF_T_UINT32 StartCluster, FF_T_UINT16 Count) {
611
612 FF_T_UINT32 fatEntry;
613 FF_T_UINT32 currentCluster, chainLength = 0;
614 FF_T_UINT32 iLen = 0;
615
616 fatEntry = StartCluster;
617
618 if(Count == 0) {
619 // Free all clusters in the chain!
620 currentCluster = StartCluster;
621 fatEntry = currentCluster;
622 do {
623 fatEntry = FF_getFatEntry(pIoman, fatEntry);
624 FF_putFatEntry(pIoman, currentCluster, 0x00000000);
625 currentCluster = fatEntry;
626 iLen ++;
627 }while(!FF_isEndOfChain(pIoman, fatEntry));
628 FF_IncreaseFreeClusters(pIoman, iLen);
629 } else {
630 // Truncation - This is quite hard, because we can only do it backwards.
631 do {
632 fatEntry = FF_getFatEntry(pIoman, fatEntry);
633 chainLength++;
634 }while(!FF_isEndOfChain(pIoman, fatEntry));
635 }
636
637 return FF_ERR_NONE;
638 }
639
640 #ifdef FF_FAT12_SUPPORT
641 FF_T_UINT32 FF_CountFreeClustersOLD(FF_IOMAN *pIoman) {
642 FF_T_UINT32 i;
643 FF_T_UINT32 TotalClusters = pIoman->pPartition->DataSectors / pIoman->pPartition->SectorsPerCluster;
644 FF_T_UINT32 FatEntry;
645 FF_T_UINT32 FreeClusters = 0;
646
647 for(i = 0; i < TotalClusters; i++) {
648 FatEntry = FF_getFatEntry(pIoman, i);
649 if(!FatEntry) {
650 FreeClusters++;
651 }
652 }
653
654 return FreeClusters;
655 }
656 #endif
657
658
659 FF_T_UINT32 FF_CountFreeClusters(FF_IOMAN *pIoman) {
660 FF_BUFFER *pBuffer;
661 FF_T_UINT32 i, x, nCluster = 0;
662 FF_T_UINT32 FatOffset;
663 FF_T_UINT32 FatSector;
664 FF_T_UINT32 FatSectorEntry;
665 FF_T_UINT32 EntriesPerSector;
666 FF_T_UINT32 FatEntry = 1;
667 FF_T_UINT32 FreeClusters = 0;
668
669 #ifdef FF_FAT12_SUPPORT
670 if(pIoman->pPartition->Type == FF_T_FAT12) { // FAT12 tables are too small to optimise, and would make it very complicated!
671 return FF_CountFreeClustersOLD(pIoman);
672 }
673 #endif
674
675 if(pIoman->pPartition->Type == FF_T_FAT32) {
676 EntriesPerSector = pIoman->BlkSize / 4;
677 FatOffset = nCluster * 4;
678 } else {
679 EntriesPerSector = pIoman->BlkSize / 2;
680 FatOffset = nCluster * 2;
681 }
682
683 FatSector = (FatOffset / pIoman->pPartition->BlkSize);
684
685 for(i = 0; i < pIoman->pPartition->SectorsPerFAT; i++) {
686 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA + i, FF_MODE_READ);
687 {
688 for(x = nCluster % EntriesPerSector; x < EntriesPerSector; x++) {
689 if(pIoman->pPartition->Type == FF_T_FAT32) {
690 FatOffset = x * 4;
691 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
692 FatEntry = FF_getLong(pBuffer->pBuffer, (FF_T_UINT16)FatSectorEntry);
693 FatEntry &= 0x0fffffff; // Clear the top 4 bits.
694 } else {
695 FatOffset = x * 2;
696 FatSectorEntry = FatOffset % pIoman->pPartition->BlkSize;
697 FatEntry = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, (FF_T_UINT16)FatSectorEntry);
698 }
699 if(FatEntry == 0x00000000) {
700 FreeClusters += 1;
701 }
702
703 nCluster++;
704 }
705 }
706 FF_ReleaseBuffer(pIoman, pBuffer);
707 }
708
709 return FreeClusters;
710 }
711
712 #ifdef FF_64_NUM_SUPPORT
713 FF_T_UINT64 FF_GetFreeSize(FF_IOMAN *pIoman) {
714 FF_T_UINT32 FreeClusters;
715 FF_T_UINT64 FreeSize;
716
717 if(pIoman) {
718 FF_lockFAT(pIoman);
719 {
720 if(!pIoman->pPartition->FreeClusterCount) {
721 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
722 }
723 FreeClusters = pIoman->pPartition->FreeClusterCount;
724 }
725 FF_unlockFAT(pIoman);
726 FreeSize = (FF_T_UINT64) ((FF_T_UINT64)FreeClusters * (FF_T_UINT64)((FF_T_UINT64)pIoman->pPartition->SectorsPerCluster * (FF_T_UINT64)pIoman->pPartition->BlkSize));
727 return FreeSize;
728 }
729 return 0;
730 }
731 #else
732 FF_T_UINT32 FF_GetFreeSize(FF_IOMAN *pIoman) {
733 FF_T_UINT32 FreeClusters;
734 FF_T_UINT32 FreeSize;
735
736 if(pIoman) {
737 FF_lockFAT(pIoman);
738 {
739 if(!pIoman->pPartition->FreeClusterCount) {
740 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
741 }
742 FreeClusters = pIoman->pPartition->FreeClusterCount;
743 }
744 FF_unlockFAT(pIoman);
745 FreeSize = (FF_T_UINT32) ((FF_T_UINT32)FreeClusters * (FF_T_UINT32)((FF_T_UINT32)pIoman->pPartition->SectorsPerCluster * (FF_T_UINT32)pIoman->pPartition->BlkSize));
746 return FreeSize;
747 }
748 return 0;
749 }
750 #endif