PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/3rdparty/fullfat/ff_file.c

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