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