9d9d579b652243146c1afa5981512c8716d46ebb
[reactos.git] / reactos / lib / 3rdparty / fullfat / ff_file.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_file.c
34 * @author James Walmsley
35 * @ingroup FILEIO
36 *
37 * @defgroup FILEIO FILE I/O Access
38 * @brief Provides an interface to allow File I/O on a mounted IOMAN.
39 *
40 * Provides file-system interfaces for the FAT file-system.
41 **/
42
43 #include "ff_file.h"
44 #include "ff_string.h"
45
46 /**
47 * @public
48 * @brief Converts STDIO mode strings into the equivalent FullFAT mode.
49 *
50 * @param Mode The mode string e.g. "rb" "rb+" "w" "a" "r" "w+" "a+" etc
51 *
52 * @return Returns the mode bits that should be passed to the FF_Open function.
53 **/
54 FF_T_UINT8 FF_GetModeBits(FF_T_INT8 *Mode) {
55 FF_T_UINT8 ModeBits = 0x00;
56 while(*Mode) {
57 switch(*Mode) {
58 case 'r': // Allow Read
59 case 'R':
60 ModeBits |= FF_MODE_READ;
61 break;
62
63 case 'w': // Allow Write
64 case 'W':
65 ModeBits |= FF_MODE_WRITE;
66 ModeBits |= FF_MODE_CREATE; // Create if not exist.
67 ModeBits |= FF_MODE_TRUNCATE;
68 break;
69
70 case 'a': // Append new writes to the end of the file.
71 case 'A':
72 ModeBits |= FF_MODE_WRITE;
73 ModeBits |= FF_MODE_APPEND;
74 ModeBits |= FF_MODE_CREATE; // Create if not exist.
75 break;
76
77 case '+': // Update the file, don't Append!
78 ModeBits |= FF_MODE_READ; // RW Mode
79 ModeBits |= FF_MODE_WRITE; // RW Mode
80 break;
81
82 /*case 'D': // Internal use only!
83 ModeBits |= FF_MODE_DIR;
84 break;*/
85
86 default: // b|B flags not supported (Binary mode is native anyway).
87 break;
88 }
89 Mode++;
90 }
91
92 return ModeBits;
93 }
94
95 /**
96 * FF_Open() Mode Information
97 * - FF_MODE_WRITE
98 * - Allows WRITE access to the file.
99 * .
100 * - FF_MODE_READ
101 * - Allows READ access to the file.
102 * .
103 * - FF_MODE_CREATE
104 * - Create file if it doesn't exist.
105 * .
106 * - FF_MODE_TRUNCATE
107 * - Erase the file if it already exists and overwrite.
108 * *
109 * - FF_MODE_APPEND
110 * - Causes all writes to occur at the end of the file. (Its impossible to overwrite other data in a file with this flag set).
111 * .
112 * .
113 *
114 * Some sample modes:
115 * - (FF_MODE_WRITE | FF_MODE_CREATE | FF_MODE_TRUNCATE)
116 * - Write access to the file. (Equivalent to "w").
117 * .
118 * - (FF_MODE_WRITE | FF_MODE_READ)
119 * - Read and Write access to the file. (Equivalent to "rb+").
120 * .
121 * - (FF_MODE_WRITE | FF_MODE_READ | FF_MODE_APPEND | FF_MODE_CREATE)
122 * - Read and Write append mode access to the file. (Equivalent to "a+").
123 * .
124 * .
125 * Be careful when choosing modes. For those using FF_Open() at the application layer
126 * its best to use the provided FF_GetModeBits() function, as this complies to the same
127 * behaviour as the stdio.h fopen() function.
128 *
129 **/
130
131
132 /**
133 * @public
134 * @brief Opens a File for Access
135 *
136 * @param pIoman FF_IOMAN object that was created by FF_CreateIOMAN().
137 * @param path Path to the File or object.
138 * @param Mode Access Mode required. Modes are a little complicated, the function FF_GetModeBits()
139 * @param Mode will convert a stdio Mode string into the equivalent Mode bits for this parameter.
140 * @param pError Pointer to a signed byte for error checking. Can be NULL if not required.
141 * @param pError To be checked when a NULL pointer is returned.
142 *
143 * @return NULL pointer on Error, in which case pError should be checked for more information.
144 * @return pError can be:
145 **/
146 FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_INT8 *path, FF_T_UINT8 Mode, FF_ERROR *pError) {
147 FF_FILE *pFile;
148 FF_FILE *pFileChain;
149 FF_DIRENT Object;
150 FF_T_UINT32 DirCluster, FileCluster;
151 FF_T_UINT32 nBytesPerCluster;
152 FF_T_INT8 filename[FF_MAX_FILENAME];
153
154 FF_T_UINT16 i;
155
156 if(pError) {
157 *pError = 0;
158 }
159
160 if(!pIoman) {
161 if(pError) {
162 *pError = FF_ERR_NULL_POINTER;
163 }
164 return (FF_FILE *)NULL;
165 }
166 pFile = FF_Malloc(sizeof(FF_FILE));
167 if(!pFile) {
168 if(pError) {
169 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
170 }
171 return (FF_FILE *)NULL;
172 }
173
174 // Get the Mode Bits.
175 pFile->Mode = Mode;
176
177 i = (FF_T_UINT16) strlen(path);
178
179 while(i != 0) {
180 if(path[i] == '\\' || path[i] == '/') {
181 break;
182 }
183 i--;
184 }
185
186 strncpy(filename, (path + i + 1), FF_MAX_FILENAME);
187
188 if(i == 0) {
189 i = 1;
190 }
191
192
193 DirCluster = FF_FindDir(pIoman, path, i);
194
195 if(DirCluster) {
196 //RetVal = //FF_FindEntry(pIoman, DirCluster, filename, &Object, FF_TRUE);
197 //if(RetVal >= 0) {
198 //FileCluster = Object.ObjectCluster;//FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object);
199 //} else {
200 // FileCluster = 0;
201 //}
202
203 FileCluster = FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object);
204
205 if(!FileCluster) { // If 0 was returned, it might be because the file has no allocated cluster
206 if(strlen(filename) == strlen(Object.FileName)) {
207 if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) strlen(filename)) == FF_TRUE) {
208 // The file really was found!
209 FileCluster = 1;
210 }
211 }
212 }
213
214 if(!FileCluster) {
215 if((pFile->Mode & FF_MODE_CREATE)) {
216 FileCluster = FF_CreateFile(pIoman, DirCluster, filename, &Object);
217 Object.CurrentItem += 1;
218 }
219 }
220
221 if(FileCluster) {
222 if(Object.Attrib == FF_FAT_ATTR_DIR) {
223 if(!(pFile->Mode & FF_MODE_DIR)) {
224 // Not the object, File Not Found!
225 free(pFile);
226 if(pError) {
227 *pError = FF_ERR_FILE_OBJECT_IS_A_DIR;
228 }
229 return (FF_FILE *) NULL;
230 }
231 }
232
233 //---------- Ensure Read-Only files don't get opened for Writing.
234 if((pFile->Mode & FF_MODE_WRITE) || (pFile->Mode & FF_MODE_APPEND)) {
235 if((Object.Attrib & FF_FAT_ATTR_READONLY)) {
236 free(pFile);
237 if(pError) {
238 *pError = FF_ERR_FILE_IS_READ_ONLY;
239 }
240 return (FF_FILE *) NULL;
241 }
242 }
243 pFile->pIoman = pIoman;
244 pFile->FilePointer = 0;
245 pFile->ObjectCluster = Object.ObjectCluster;
246 pFile->Filesize = Object.Filesize;
247 pFile->CurrentCluster = 0;
248 pFile->AddrCurrentCluster = pFile->ObjectCluster;
249 //pFile->Mode = Mode;
250 pFile->Next = NULL;
251 pFile->DirCluster = DirCluster;
252 pFile->DirEntry = Object.CurrentItem - 1;
253 nBytesPerCluster = pFile->pIoman->pPartition->SectorsPerCluster / pIoman->BlkSize;
254 pFile->iChainLength = 0;
255 pFile->iEndOfChain = 0;
256 pFile->FileDeleted = FF_FALSE;
257
258 // File Permission Processing
259 // Only "w" and "w+" mode strings can erase a file's contents.
260 // Any other combinations will not cause an erase.
261 if((pFile->Mode & FF_MODE_TRUNCATE)) {
262 pFile->Filesize = 0;
263 pFile->FilePointer = 0;
264 }
265
266 /*
267 Add pFile onto the end of our linked list of FF_FILE objects.
268 */
269 FF_PendSemaphore(pIoman->pSemaphore);
270 {
271 if(!pIoman->FirstFile) {
272 pIoman->FirstFile = pFile;
273 } else {
274 pFileChain = (FF_FILE *) pIoman->FirstFile;
275 do {
276 if(pFileChain->ObjectCluster == pFile->ObjectCluster) {
277 // File is already open! DON'T ALLOW IT!
278 FF_ReleaseSemaphore(pIoman->pSemaphore);
279 free(pFile);
280 if(pError) {
281 *pError = FF_ERR_FILE_ALREADY_OPEN;
282 }
283 return (FF_FILE *) NULL;
284 }
285 if(!pFileChain->Next) {
286 pFileChain->Next = pFile;
287 break;
288 }
289 pFileChain = (FF_FILE *) pFileChain->Next;
290 }while(pFileChain != NULL);
291 }
292 }
293 FF_ReleaseSemaphore(pIoman->pSemaphore);
294
295 return pFile;
296 }else {
297 free(pFile);
298 if(pError) {
299 *pError = FF_ERR_FILE_NOT_FOUND;
300 }
301 return (FF_FILE *) NULL;
302 }
303 }
304 if(pError) {
305 *pError = FF_ERR_FILE_INVALID_PATH;
306 }
307
308 free(pFile);
309
310 return (FF_FILE *)NULL;
311 }
312
313
314 /**
315 * @public
316 * @brief Tests if a Directory contains any other files or folders.
317 *
318 * @param pIoman FF_IOMAN object returned from the FF_CreateIOMAN() function.
319 *
320 **/
321 FF_T_BOOL FF_isDirEmpty(FF_IOMAN *pIoman, const FF_T_INT8 *Path) {
322
323 FF_DIRENT MyDir;
324 FF_ERROR RetVal = FF_ERR_NONE;
325 FF_T_UINT8 i = 0;
326
327 if(!pIoman) {
328 return FF_FALSE;
329 }
330
331 RetVal = FF_FindFirst(pIoman, &MyDir, Path);
332 while(RetVal == 0) {
333 i++;
334 RetVal = FF_FindNext(pIoman, &MyDir);
335 if(i > 2) {
336 return FF_FALSE;
337 }
338 }
339
340 return FF_TRUE;
341 }
342
343 FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_INT8 *path) {
344 FF_FILE *pFile;
345 FF_ERROR Error = FF_ERR_NONE;
346 FF_T_UINT8 EntryBuffer[32];
347 FF_T_SINT8 RetVal = FF_ERR_NONE;
348 #ifdef FF_PATH_CACHE
349 FF_T_UINT32 i;
350 #endif
351
352 if(!pIoman) {
353 return FF_ERR_NULL_POINTER;
354 }
355
356 pFile = FF_Open(pIoman, path, FF_MODE_DIR, &Error);
357
358 if(!pFile) {
359 return Error; // File in use or File not found!
360 }
361
362 pFile->FileDeleted = FF_TRUE;
363
364 FF_lockDIR(pIoman);
365 {
366 if(FF_isDirEmpty(pIoman, path)) {
367 FF_lockFAT(pIoman);
368 {
369 FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0); // 0 to delete the entire chain!
370 }
371 FF_unlockFAT(pIoman);
372
373 // Edit the Directory Entry! (So it appears as deleted);
374 FF_RmLFNs(pIoman, pFile->DirCluster, pFile->DirEntry);
375 FF_FetchEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer);
376 EntryBuffer[0] = 0xE5;
377 FF_PushEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer);
378 #ifdef FF_PATH_CACHE
379 FF_PendSemaphore(pIoman->pSemaphore); // Thread safety on shared object!
380 {
381 for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) {
382 if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)strlen(path))) {
383 pIoman->pPartition->PathCache[i].Path[0] = '\0';
384 pIoman->pPartition->PathCache[i].DirCluster = 0;
385 FF_ReleaseSemaphore(pIoman->pSemaphore);
386 }
387 }
388 }
389 FF_ReleaseSemaphore(pIoman->pSemaphore);
390 #endif
391
392 FF_IncreaseFreeClusters(pIoman, pFile->iChainLength);
393
394 FF_FlushCache(pIoman);
395 } else {
396 RetVal = FF_ERR_DIR_NOT_EMPTY;
397 }
398 }
399 FF_unlockDIR(pIoman);
400
401 FF_Close(pFile); // Free the file pointer resources
402 // File is now lost!
403 return RetVal;
404 }
405
406 FF_ERROR FF_RmFile(FF_IOMAN *pIoman, const FF_T_INT8 *path) {
407 FF_FILE *pFile;
408 FF_ERROR Error = 0;
409 FF_T_UINT8 EntryBuffer[32];
410
411 pFile = FF_Open(pIoman, path, FF_MODE_READ, &Error);
412
413 if(!pFile) {
414 return Error; // File in use or File not found!
415 }
416
417 pFile->FileDeleted = FF_TRUE;
418
419 FF_lockFAT(pIoman); // Lock the FAT so its thread-safe.
420 {
421 FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0); // 0 to delete the entire chain!
422 }
423 FF_unlockFAT(pIoman);
424
425 // Edit the Directory Entry! (So it appears as deleted);
426 FF_lockDIR(pIoman);
427 {
428 FF_RmLFNs(pIoman, pFile->DirCluster, pFile->DirEntry);
429 FF_FetchEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer);
430 EntryBuffer[0] = 0xE5;
431 FF_PushEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer);
432 }
433 FF_unlockDIR(pIoman);
434
435 FF_FlushCache(pIoman);
436
437 FF_Close(pFile); // Free the file pointer resources
438 return 0;
439 }
440
441 /**
442 * @public
443 * @brief Moves a file or directory from source to destination.
444 *
445 * @param pIoman The FF_IOMAN object pointer.
446 * @param szSourceFile String of the source file to be moved or renamed.
447 * @param szDestinationFile String of the destination file to where the source should be moved or renamed.
448 *
449 * @return FF_ERR_NONE on success.
450 * @return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.
451 * @return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).
452 * @return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.
453 * @return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.
454 *
455 **/
456 FF_ERROR FF_Move(FF_IOMAN *pIoman, const FF_T_INT8 *szSourceFile, const FF_T_INT8 *szDestinationFile) {
457 FF_ERROR Error;
458 FF_FILE *pSrcFile, *pDestFile;
459 FF_DIRENT MyFile;
460 FF_T_UINT8 EntryBuffer[32];
461 FF_T_UINT16 i;
462 FF_T_UINT32 DirCluster;
463
464 if(!pIoman) {
465 return FF_ERR_NULL_POINTER;
466 }
467
468 // Check destination file doesn't exist!
469 pDestFile = FF_Open(pIoman, szDestinationFile, FF_MODE_READ, &Error);
470
471 if(pDestFile || (Error == FF_ERR_FILE_OBJECT_IS_A_DIR)) {
472 FF_Close(pDestFile);
473 return FF_ERR_FILE_DESTINATION_EXISTS; // YES -- FAIL
474 }
475
476 pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_READ, &Error);
477
478 if(Error == FF_ERR_FILE_OBJECT_IS_A_DIR) {
479 // Open a directory for moving!
480 pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_DIR, &Error);
481 }
482
483 if(pSrcFile) {
484
485 // Create the new dirent.
486 FF_FetchEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
487 MyFile.Attrib = FF_getChar(EntryBuffer, (FF_T_UINT16)(FF_FAT_DIRENT_ATTRIB));
488 MyFile.Filesize = pSrcFile->Filesize;
489 MyFile.ObjectCluster = pSrcFile->ObjectCluster;
490 MyFile.CurrentItem = 0;
491
492 i = (FF_T_UINT16) strlen(szDestinationFile);
493
494 while(i != 0) {
495 if(szDestinationFile[i] == '\\' || szDestinationFile[i] == '/') {
496 break;
497 }
498 i--;
499 }
500
501 strncpy(MyFile.FileName, (szDestinationFile + i + 1), FF_MAX_FILENAME);
502
503 if(i == 0) {
504 i = 1;
505 }
506
507
508 DirCluster = FF_FindDir(pIoman, szDestinationFile, i);
509
510 if(DirCluster) {
511
512 // Destination Dir was found, we can now create the new entry.
513 if(FF_CreateDirent(pIoman, DirCluster, &MyFile)) {
514 FF_Close(pSrcFile);
515 return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT; // FAILED
516 }
517
518 // Edit the Directory Entry! (So it appears as deleted);
519 FF_lockDIR(pIoman);
520 {
521 FF_RmLFNs(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry);
522 FF_FetchEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
523 EntryBuffer[0] = 0xE5;
524 FF_PushEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
525 }
526 FF_unlockDIR(pIoman);
527 FF_Close(pSrcFile);
528
529 FF_FlushCache(pIoman);
530
531 return FF_ERR_NONE;
532 }
533
534 return FF_ERR_FILE_DIR_NOT_FOUND;
535
536 }
537
538 return FF_ERR_FILE_SOURCE_NOT_FOUND; // Source not found!
539 }
540
541
542 /**
543 * @public
544 * @brief Get's the next Entry based on the data recorded in the FF_DIRENT object.
545 *
546 * @param pFile FF_FILE object that was created by FF_Open().
547 *
548 * @return FF_TRUE if End of File was reached. FF_FALSE if not.
549 * @return FF_FALSE if a null pointer was provided.
550 *
551 **/
552 FF_T_BOOL FF_isEOF(FF_FILE *pFile) {
553 if(!pFile) {
554 return FF_FALSE;
555 }
556 if(pFile->FilePointer >= pFile->Filesize) {
557 return FF_TRUE;
558 } else {
559 return FF_FALSE;
560 }
561 }
562
563 static FF_T_UINT32 FF_GetSequentialClusters(FF_IOMAN *pIoman, FF_T_UINT32 StartCluster, FF_T_UINT32 Limit) {
564 FF_T_UINT32 CurrentCluster;
565 FF_T_UINT32 NextCluster = StartCluster;
566 FF_T_UINT32 i = 0;
567
568 do {
569 CurrentCluster = NextCluster;
570 NextCluster = FF_getFatEntry(pIoman, CurrentCluster);
571 if(NextCluster == (CurrentCluster + 1)) {
572 i++;
573 } else {
574 break;
575 }
576
577 if(Limit) {
578 if(i == Limit) {
579 break;
580 }
581 }
582 }while(NextCluster == (CurrentCluster + 1));
583
584 return i;
585 }
586
587 static FF_T_SINT32 FF_ReadClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
588 FF_T_UINT32 Sectors;
589 FF_T_UINT32 SequentialClusters = 0;
590 FF_T_UINT32 nItemLBA;
591 FF_T_SINT32 RetVal;
592
593 while(Count != 0) {
594 if((Count - 1) > 0) {
595 SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1));
596 }
597 Sectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster;
598 nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster);
599 nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA);
600
601 do {
602 if(pFile->pIoman->pBlkDevice->fnReadBlocks) {
603 RetVal = pFile->pIoman->pBlkDevice->fnReadBlocks(buffer, nItemLBA, Sectors, pFile->pIoman->pBlkDevice->pParam);
604 if(RetVal == FF_ERR_DRIVER_BUSY) {
605 FF_Yield();
606 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
607 }
608 } else {
609 RetVal = FF_ERR_DEVICE_DRIVER_FAILED;
610 }
611
612 }while(RetVal == FF_ERR_DRIVER_BUSY);
613
614 Count -= (SequentialClusters + 1);
615 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1));
616 pFile->CurrentCluster += (SequentialClusters + 1);
617 buffer += Sectors * pFile->pIoman->BlkSize;
618 SequentialClusters = 0;
619 }
620
621 return 0;
622 }
623
624
625 static FF_ERROR FF_ExtendFile(FF_FILE *pFile, FF_T_UINT32 Size) {
626 FF_IOMAN *pIoman = pFile->pIoman;
627 FF_T_UINT32 nBytesPerCluster = pIoman->pPartition->BlkSize * pIoman->pPartition->SectorsPerCluster;
628 FF_T_UINT32 nTotalClustersNeeded = Size / nBytesPerCluster;
629 FF_T_UINT32 nClusterToExtend;
630 FF_T_UINT32 CurrentCluster, NextCluster;
631 FF_T_UINT32 i;
632 FF_DIRENT OriginalEntry;
633
634 if((pFile->Mode & FF_MODE_WRITE) != FF_MODE_WRITE) {
635 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
636 }
637
638 if(pFile->Filesize == 0 && pFile->ObjectCluster == 0) { // No Allocated clusters.
639 // Create a Cluster chain!
640 pFile->AddrCurrentCluster = FF_CreateClusterChain(pFile->pIoman);
641 if(!FF_GetEntry(pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry)) {
642 OriginalEntry.ObjectCluster = pFile->AddrCurrentCluster;
643 FF_PutEntry(pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
644 } else {
645 return FF_ERR_FILE_EXTEND_FAILED;
646 }
647 pFile->ObjectCluster = pFile->AddrCurrentCluster;
648 pFile->iChainLength = 1;
649 pFile->CurrentCluster = 0;
650 pFile->iEndOfChain = pFile->AddrCurrentCluster;
651 }
652
653 if(Size % nBytesPerCluster) {
654 nTotalClustersNeeded += 1;
655 }
656
657 if(pFile->iChainLength == 0) { // First extension requiring the chain length,
658 pFile->iChainLength = FF_GetChainLength(pIoman, pFile->ObjectCluster, &pFile->iEndOfChain);
659 }
660
661 nClusterToExtend = (nTotalClustersNeeded - pFile->iChainLength);
662
663 if(nTotalClustersNeeded > pFile->iChainLength) {
664
665 NextCluster = pFile->AddrCurrentCluster;
666 FF_lockFAT(pIoman);
667 {
668 for(i = 0; i <= nClusterToExtend; i++) {
669 CurrentCluster = FF_FindEndOfChain(pIoman, NextCluster);
670 NextCluster = FF_FindFreeCluster(pIoman);
671 if(!NextCluster) {
672 FF_unlockFAT(pIoman);
673 return FF_ERR_FAT_NO_FREE_CLUSTERS;
674 }
675 FF_putFatEntry(pIoman, CurrentCluster, NextCluster);
676 FF_putFatEntry(pIoman, NextCluster, 0xFFFFFFFF);
677 }
678
679 pFile->iEndOfChain = FF_FindEndOfChain(pIoman, NextCluster);
680 }
681 FF_unlockFAT(pIoman);
682
683 pFile->iChainLength += i;
684 FF_DecreaseFreeClusters(pIoman, i); // Keep Tab of Numbers for fast FreeSize()
685 }
686
687 return FF_ERR_NONE;
688 }
689
690 static FF_T_SINT32 FF_WriteClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
691 FF_T_UINT32 Sectors;
692 FF_T_UINT32 SequentialClusters = 0;
693 FF_T_UINT32 nItemLBA;
694 FF_T_SINT32 RetVal;
695
696 while(Count != 0) {
697 if((Count - 1) > 0) {
698 SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1));
699 }
700 Sectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster;
701 nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster);
702 nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA);
703
704 do {
705 if(pFile->pIoman->pBlkDevice->fnWriteBlocks) {
706 RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, Sectors, pFile->pIoman->pBlkDevice->pParam);
707 if(RetVal == FF_ERR_DRIVER_BUSY) {
708 FF_Yield();
709 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
710 }
711 } else {
712 RetVal = FF_ERR_DEVICE_DRIVER_FAILED;
713 }
714
715 }while(RetVal == FF_ERR_DRIVER_BUSY);
716
717 Count -= (SequentialClusters + 1);
718 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1));
719 pFile->CurrentCluster += (SequentialClusters + 1);
720 buffer += Sectors * pFile->pIoman->BlkSize;
721 SequentialClusters = 0;
722 }
723
724 return 0;
725 }
726
727 /**
728 * @public
729 * @brief Equivalent to fread()
730 *
731 * @param pFile FF_FILE object that was created by FF_Open().
732 * @param ElementSize The size of an element to read.
733 * @param Count The number of elements to read.
734 * @param buffer A pointer to a buffer of adequate size to be filled with the requested data.
735 *
736 * @return Number of bytes read.
737 *
738 **/
739 FF_T_SINT32 FF_Read(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
740 FF_T_UINT32 nBytes = ElementSize * Count;
741 FF_T_UINT32 nBytesRead = 0;
742 FF_T_UINT32 nBytesToRead;
743 FF_IOMAN *pIoman;
744 FF_BUFFER *pBuffer;
745 FF_T_UINT32 nRelBlockPos;
746 FF_T_UINT32 nItemLBA;
747 FF_T_SINT32 RetVal = 0;
748 FF_T_UINT16 sSectors;
749 FF_T_UINT32 nRelClusterPos;
750 FF_T_UINT32 nBytesPerCluster;
751 FF_T_UINT32 nClusterDiff;
752
753 if(!pFile) {
754 return FF_ERR_NULL_POINTER;
755 }
756
757 if(!(pFile->Mode & FF_MODE_READ)) {
758 return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE;
759 }
760
761 pIoman = pFile->pIoman;
762
763 if(pFile->FilePointer == pFile->Filesize) {
764 return 0;
765 }
766
767 if((pFile->FilePointer + nBytes) > pFile->Filesize) {
768 nBytes = pFile->Filesize - pFile->FilePointer;
769 }
770
771 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
772 if(nClusterDiff) {
773 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
774 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
775 pFile->CurrentCluster += nClusterDiff;
776 }
777 }
778
779 nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block.
780
781 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
782 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
783
784 if((nRelBlockPos + nBytes) < pIoman->BlkSize) { // Bytes to read are within a block and less than a block size.
785 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
786 {
787 memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytes);
788 }
789 FF_ReleaseBuffer(pIoman, pBuffer);
790
791 pFile->FilePointer += nBytes;
792
793 return nBytes; // Return the number of bytes read.
794
795 } else {
796
797 //---------- Read (memcpy) to a Sector Boundary
798 if(nRelBlockPos != 0) { // Not on a sector boundary, at this point the LBA is known.
799 nBytesToRead = pIoman->BlkSize - nRelBlockPos;
800 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
801 {
802 // Here we copy to the sector boudary.
803 memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytesToRead);
804 }
805 FF_ReleaseBuffer(pIoman, pBuffer);
806
807 nBytes -= nBytesToRead;
808 nBytesRead += nBytesToRead;
809 pFile->FilePointer += nBytesToRead;
810 buffer += nBytesToRead;
811
812 }
813
814 //---------- Read to a Cluster Boundary
815
816 nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1);
817 nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize);
818 if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary
819
820 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
821 if(nClusterDiff) {
822 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
823 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
824 pFile->CurrentCluster += nClusterDiff;
825 }
826 }
827
828 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
829 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
830
831 sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize));
832
833 do {
834 if(pIoman->pBlkDevice->fnReadBlocks) {
835 RetVal = pFile->pIoman->pBlkDevice->fnReadBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam);
836 }
837 if(RetVal == FF_ERR_DRIVER_BUSY) {
838 FF_Yield();
839 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
840 }
841 }while(RetVal == FF_ERR_DRIVER_BUSY);
842
843 nBytesToRead = sSectors * pIoman->BlkSize;
844 nBytes -= nBytesToRead;
845 buffer += nBytesToRead;
846 nBytesRead += nBytesToRead;
847 pFile->FilePointer += nBytesToRead;
848
849 }
850
851 //---------- Read Clusters
852 if(nBytes >= nBytesPerCluster) {
853 //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
854 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
855 if(nClusterDiff) {
856 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
857 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
858 pFile->CurrentCluster += nClusterDiff;
859 }
860 }
861 //----- End of Contributor fix.
862
863 FF_ReadClusters(pFile, (nBytes / nBytesPerCluster), buffer);
864 nBytesToRead = (nBytesPerCluster * (nBytes / nBytesPerCluster));
865
866 pFile->FilePointer += nBytesToRead;
867
868 nBytes -= nBytesToRead;
869 buffer += nBytesToRead;
870 nBytesRead += nBytesToRead;
871 }
872
873 //---------- Read Remaining Blocks
874 if(nBytes >= pIoman->BlkSize) {
875 sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize);
876
877 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
878 if(nClusterDiff) {
879 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
880 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
881 pFile->CurrentCluster += nClusterDiff;
882 }
883 }
884
885 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
886 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
887
888 do {
889 if(pIoman->pBlkDevice->fnReadBlocks) {
890 RetVal = pFile->pIoman->pBlkDevice->fnReadBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam);
891 }
892 if(RetVal == FF_ERR_DRIVER_BUSY) {
893 FF_Yield();
894 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
895 }
896 }while(RetVal == FF_ERR_DRIVER_BUSY);
897
898 nBytesToRead = sSectors * pIoman->BlkSize;
899 pFile->FilePointer += nBytesToRead;
900 nBytes -= nBytesToRead;
901 buffer += nBytesToRead;
902 nBytesRead += nBytesToRead;
903 }
904
905 //---------- Read (memcpy) Remaining Bytes
906 if(nBytes > 0) {
907
908 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
909 if(nClusterDiff) {
910 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
911 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
912 pFile->CurrentCluster += nClusterDiff;
913 }
914 }
915
916 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
917 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
918 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ);
919 {
920 memcpy(buffer, pBuffer->pBuffer, nBytes);
921 }
922 FF_ReleaseBuffer(pIoman, pBuffer);
923
924 nBytesToRead = nBytes;
925 pFile->FilePointer += nBytesToRead;
926 nBytes -= nBytesToRead;
927 buffer += nBytesToRead;
928 nBytesRead += nBytesToRead;
929
930 }
931 }
932
933 return nBytesRead;
934 }
935
936
937
938
939 /**
940 * @public
941 * @brief Equivalent to fgetc()
942 *
943 * @param pFile FF_FILE object that was created by FF_Open().
944 *
945 * @return The character that was read (cast as a 32-bit interger). -1 on EOF.
946 * @return -2 If a null file pointer was provided.
947 * @return -3 Device access failed.
948 *
949 **/
950 FF_T_SINT32 FF_GetC(FF_FILE *pFile) {
951 FF_T_UINT32 fileLBA;
952 FF_BUFFER *pBuffer;
953 FF_T_UINT8 retChar;
954 FF_T_UINT32 relMinorBlockPos;
955 FF_T_UINT32 clusterNum;
956 FF_T_UINT32 nClusterDiff;
957
958
959 if(!pFile) {
960 return FF_ERR_NULL_POINTER;
961 }
962
963 if(!(pFile->Mode & FF_MODE_READ)) {
964 return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE;
965 }
966
967 if(pFile->FilePointer >= pFile->Filesize) {
968 return -1; // EOF!
969 }
970
971 relMinorBlockPos = FF_getMinorBlockEntry(pFile->pIoman, pFile->FilePointer, 1);
972 clusterNum = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
973
974 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
975 if(nClusterDiff) {
976 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
977 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, nClusterDiff);
978 pFile->CurrentCluster += nClusterDiff;
979 }
980 }
981
982
983 fileLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster) + FF_getMajorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
984 fileLBA = FF_getRealLBA (pFile->pIoman, fileLBA) + FF_getMinorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
985
986 pBuffer = FF_GetBuffer(pFile->pIoman, fileLBA, FF_MODE_READ);
987 {
988 if(!pBuffer) {
989 return -3;
990 }
991 retChar = pBuffer->pBuffer[relMinorBlockPos];
992 }
993 FF_ReleaseBuffer(pFile->pIoman, pBuffer);
994
995 pFile->FilePointer += 1;
996
997 return (FF_T_INT32) retChar;
998 }
999
1000 FF_T_UINT32 FF_Tell(FF_FILE *pFile) {
1001 return pFile->FilePointer;
1002 }
1003
1004
1005 /**
1006 * @public
1007 * @brief Writes data to a File.
1008 *
1009 * @param pFile FILE Pointer.
1010 * @param ElementSize Size of an Element of Data to be copied. (in bytes).
1011 * @param Count Number of Elements of Data to be copied. (ElementSize * Count must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best.
1012 * @param buffer Byte-wise buffer containing the data to be written.
1013 *
1014 * @return
1015 **/
1016 FF_T_SINT32 FF_Write(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) {
1017 FF_T_UINT32 nBytes = ElementSize * Count;
1018 FF_T_UINT32 nBytesWritten = 0;
1019 FF_T_UINT32 nBytesToWrite;
1020 FF_IOMAN *pIoman;
1021 FF_BUFFER *pBuffer;
1022 FF_T_UINT32 nRelBlockPos;
1023 FF_T_UINT32 nItemLBA;
1024 FF_T_SINT32 RetVal = 0;
1025 FF_T_UINT16 sSectors;
1026 FF_T_UINT32 nRelClusterPos;
1027 FF_T_UINT32 nBytesPerCluster, nClusterDiff, nClusters;
1028 FF_ERROR Error;
1029
1030 if(!pFile) {
1031 return FF_ERR_NULL_POINTER;
1032 }
1033
1034 if(!(pFile->Mode & FF_MODE_WRITE)) {
1035 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
1036 }
1037
1038 // Make sure a write is after the append point.
1039 if((pFile->Mode & FF_MODE_APPEND)) {
1040 if(pFile->FilePointer < pFile->Filesize) {
1041 FF_Seek(pFile, 0, FF_SEEK_END);
1042 }
1043 }
1044
1045 pIoman = pFile->pIoman;
1046
1047 nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize);
1048
1049 // Extend File for atleast nBytes!
1050 // Handle file-space allocation
1051 Error = FF_ExtendFile(pFile, pFile->FilePointer + nBytes);
1052
1053 if(Error) {
1054 return Error;
1055 }
1056
1057 nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block.
1058
1059 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1060 if(nClusterDiff) {
1061 if(pFile->CurrentCluster != FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1062 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1063 pFile->CurrentCluster += nClusterDiff;
1064 }
1065 }
1066
1067 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1068 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1069
1070 if((nRelBlockPos + nBytes) < pIoman->BlkSize) { // Bytes to read are within a block and less than a block size.
1071 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1072 {
1073 memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytes);
1074 }
1075 FF_ReleaseBuffer(pIoman, pBuffer);
1076
1077 pFile->FilePointer += nBytes;
1078 nBytesWritten = nBytes;
1079 //return nBytes; // Return the number of bytes read.
1080
1081 } else {
1082
1083 //---------- Write (memcpy) to a Sector Boundary
1084 if(nRelBlockPos != 0) { // Not on a sector boundary, at this point the LBA is known.
1085 nBytesToWrite = pIoman->BlkSize - nRelBlockPos;
1086 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1087 {
1088 // Here we copy to the sector boudary.
1089 memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytesToWrite);
1090 }
1091 FF_ReleaseBuffer(pIoman, pBuffer);
1092
1093 nBytes -= nBytesToWrite;
1094 nBytesWritten += nBytesToWrite;
1095 pFile->FilePointer += nBytesToWrite;
1096 buffer += nBytesToWrite;
1097 }
1098
1099 //---------- Write to a Cluster Boundary
1100
1101 nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1);
1102 if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary
1103
1104 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1105 if(nClusterDiff) {
1106 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1107 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1108 pFile->CurrentCluster += nClusterDiff;
1109 }
1110 }
1111
1112 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1113 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1114
1115 sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize));
1116
1117 do {
1118 if(pIoman->pBlkDevice->fnWriteBlocks) {
1119 RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam);
1120 }
1121 if(RetVal == FF_ERR_DRIVER_BUSY) {
1122 FF_Yield();
1123 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
1124 }
1125 }while(RetVal == FF_ERR_DRIVER_BUSY);
1126
1127 nBytesToWrite = sSectors * pIoman->BlkSize;
1128 nBytes -= nBytesToWrite;
1129 buffer += nBytesToWrite;
1130 nBytesWritten += nBytesToWrite;
1131 pFile->FilePointer += nBytesToWrite;
1132
1133 }
1134
1135 //---------- Write Clusters
1136 if(nBytes >= nBytesPerCluster) {
1137 //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
1138 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1139 if(nClusterDiff) {
1140 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1141 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1142 pFile->CurrentCluster += nClusterDiff;
1143 }
1144 }
1145 //----- End of Contributor fix.
1146
1147 nClusters = (nBytes / nBytesPerCluster);
1148
1149 FF_WriteClusters(pFile, nClusters, buffer);
1150
1151 nBytesToWrite = (nBytesPerCluster * nClusters);
1152
1153 pFile->FilePointer += nBytesToWrite;
1154
1155 nBytes -= nBytesToWrite;
1156 buffer += nBytesToWrite;
1157 nBytesWritten += nBytesToWrite;
1158 }
1159
1160 //---------- Write Remaining Blocks
1161 if(nBytes >= pIoman->BlkSize) {
1162 sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize);
1163
1164 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1165 if(nClusterDiff) {
1166 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1167 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1168 pFile->CurrentCluster += nClusterDiff;
1169 }
1170 }
1171
1172 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1173 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1174
1175 do {
1176 if(pIoman->pBlkDevice->fnWriteBlocks) {
1177 RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam);
1178 }
1179 if(RetVal == FF_ERR_DRIVER_BUSY) {
1180 FF_Yield();
1181 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
1182 }
1183 }while(RetVal == FF_ERR_DRIVER_BUSY);
1184
1185 nBytesToWrite = sSectors * pIoman->BlkSize;
1186 pFile->FilePointer += nBytesToWrite;
1187 nBytes -= nBytesToWrite;
1188 buffer += nBytesToWrite;
1189 nBytesWritten += nBytesToWrite;
1190
1191 }
1192
1193 //---------- Write (memcpy) Remaining Bytes
1194 if(nBytes > 0) {
1195
1196 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1197 if(nClusterDiff) {
1198 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1199 pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1200 pFile->CurrentCluster += nClusterDiff;
1201 }
1202 }
1203
1204 nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster);
1205 nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1);
1206 pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE);
1207 {
1208 memcpy(pBuffer->pBuffer, buffer, nBytes);
1209 }
1210 FF_ReleaseBuffer(pIoman, pBuffer);
1211
1212 nBytesToWrite = nBytes;
1213 pFile->FilePointer += nBytesToWrite;
1214 nBytes -= nBytesToWrite;
1215 buffer += nBytesToWrite;
1216 nBytesWritten += nBytesToWrite;
1217
1218 }
1219 }
1220
1221 if(pFile->FilePointer > pFile->Filesize) {
1222 pFile->Filesize = pFile->FilePointer;
1223 }
1224
1225 return nBytesWritten;
1226 }
1227
1228
1229 /**
1230 * @public
1231 * @brief Writes a char to a FILE.
1232 *
1233 * @param pFile FILE Pointer.
1234 * @param pa_cValue Char to be placed in the file.
1235 *
1236 * @return Returns the value written to the file, or a value less than 0.
1237 *
1238 **/
1239 FF_T_SINT32 FF_PutC(FF_FILE *pFile, FF_T_UINT8 pa_cValue) {
1240 FF_BUFFER *pBuffer;
1241 FF_T_UINT32 iItemLBA;
1242 FF_T_UINT32 iRelPos;
1243 FF_T_UINT32 nClusterDiff;
1244
1245 if(!pFile) { // Ensure we don't have a Null file pointer on a Public interface.
1246 return FF_ERR_NULL_POINTER;
1247 }
1248
1249 if(!(pFile->Mode & FF_MODE_WRITE)) {
1250 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE;
1251 }
1252
1253 // Make sure a write is after the append point.
1254 if((pFile->Mode & FF_MODE_APPEND)) {
1255 if(pFile->FilePointer < pFile->Filesize) {
1256 FF_Seek(pFile, 0, FF_SEEK_END);
1257 }
1258 }
1259
1260 iRelPos = FF_getMinorBlockEntry(pFile->pIoman, pFile->FilePointer, 1);
1261
1262 // Handle File Space Allocation.
1263 FF_ExtendFile(pFile, pFile->FilePointer + 1);
1264
1265 nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster;
1266 if(nClusterDiff) {
1267 if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) {
1268 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, nClusterDiff);
1269 pFile->CurrentCluster += nClusterDiff;
1270 }
1271 }
1272
1273 iItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster) + FF_getMajorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1274 iItemLBA = FF_getRealLBA (pFile->pIoman, iItemLBA) + FF_getMinorBlockNumber(pFile->pIoman, pFile->FilePointer, (FF_T_UINT16) 1);
1275
1276 pBuffer = FF_GetBuffer(pFile->pIoman, iItemLBA, FF_MODE_WRITE);
1277 {
1278 if(!pBuffer) {
1279 return FF_ERR_DEVICE_DRIVER_FAILED;
1280 }
1281 FF_putChar(pBuffer->pBuffer, (FF_T_UINT16) iRelPos, pa_cValue);
1282 }
1283 FF_ReleaseBuffer(pFile->pIoman, pBuffer);
1284
1285 pFile->FilePointer += 1;
1286 if(pFile->Filesize < (pFile->FilePointer)) {
1287 pFile->Filesize += 1;
1288 }
1289 return pa_cValue;
1290 }
1291
1292
1293
1294 /**
1295 * @public
1296 * @brief Equivalent to fseek()
1297 *
1298 * @param pFile FF_FILE object that was created by FF_Open().
1299 * @param Offset An integer (+/-) to seek to, from the specified origin.
1300 * @param Origin Where to seek from. (FF_SEEK_SET seek from start, FF_SEEK_CUR seek from current position, or FF_SEEK_END seek from end of file).
1301 *
1302 * @return 0 on Sucess,
1303 * @return -2 if offset results in an invalid position in the file.
1304 * @return -1 if a FF_FILE pointer was not recieved.
1305 * @return -3 if an invalid origin was provided.
1306 *
1307 **/
1308 FF_ERROR FF_Seek(FF_FILE *pFile, FF_T_SINT32 Offset, FF_T_INT8 Origin) {
1309
1310 if(!pFile) {
1311 return FF_ERR_NULL_POINTER;
1312 }
1313
1314 switch(Origin) {
1315 case FF_SEEK_SET:
1316 if((FF_T_UINT32) Offset <= pFile->Filesize && Offset >= 0) {
1317 pFile->FilePointer = Offset;
1318 pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1319 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster);
1320 } else {
1321 return -2;
1322 }
1323 break;
1324
1325 case FF_SEEK_CUR:
1326 if((Offset + pFile->FilePointer) <= pFile->Filesize && (Offset + (FF_T_SINT32) pFile->FilePointer) >= 0) {
1327 pFile->FilePointer = Offset + pFile->FilePointer;
1328 pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1329 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster);
1330 } else {
1331 return -2;
1332 }
1333 break;
1334
1335 case FF_SEEK_END:
1336 if((Offset + (FF_T_SINT32) pFile->Filesize) >= 0 && (Offset + pFile->Filesize) <= pFile->Filesize) {
1337 pFile->FilePointer = Offset + pFile->Filesize;
1338 pFile->CurrentCluster = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1);
1339 pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->ObjectCluster, pFile->CurrentCluster);
1340 } else {
1341 return -2;
1342 }
1343 break;
1344
1345 default:
1346 return -3;
1347
1348 }
1349
1350 return 0;
1351 }
1352
1353
1354 /**
1355 * @public
1356 * @brief Equivalent to fclose()
1357 *
1358 * @param pFile FF_FILE object that was created by FF_Open().
1359 *
1360 * @return 0 on sucess.
1361 * @return -1 if a null pointer was provided.
1362 *
1363 **/
1364 FF_ERROR FF_Close(FF_FILE *pFile) {
1365
1366 FF_FILE *pFileChain;
1367 FF_DIRENT OriginalEntry;
1368 FF_ERROR Error;
1369
1370 if(!pFile) {
1371 return FF_ERR_NULL_POINTER;
1372 }
1373 // UpDate Dirent if File-size has changed?
1374
1375 // Update the Dirent!
1376 Error = FF_GetEntry(pFile->pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
1377 if(Error) {
1378 return Error;
1379 }
1380
1381 if(!pFile->FileDeleted) {
1382 if(pFile->Filesize != OriginalEntry.Filesize) {
1383 OriginalEntry.Filesize = pFile->Filesize;
1384 FF_PutEntry(pFile->pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry);
1385 }
1386 }
1387
1388 //if(pFile->Mode == FF_MODE_WRITE) {
1389 FF_FlushCache(pFile->pIoman); // Ensure all modfied blocks are flushed to disk!
1390 //}
1391
1392 // Handle Linked list!
1393 FF_PendSemaphore(pFile->pIoman->pSemaphore);
1394 { // Semaphore is required, or linked list could become corrupted.
1395 if(pFile->pIoman->FirstFile == pFile) {
1396 pFile->pIoman->FirstFile = pFile->Next;
1397 } else {
1398 pFileChain = (FF_FILE *) pFile->pIoman->FirstFile;
1399 while(pFileChain->Next != pFile) {
1400 pFileChain = pFileChain->Next;
1401 }
1402 pFileChain->Next = pFile->Next;
1403 }
1404 } // Semaphore released, linked list was shortened!
1405 FF_ReleaseSemaphore(pFile->pIoman->pSemaphore);
1406
1407 // If file written, flush to disk
1408 free(pFile);
1409 // Simply free the pointer!
1410 return FF_ERR_NONE;
1411 }