]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_file.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-Plus-FAT / ff_file.c
diff --git a/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_file.c b/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_file.c
new file mode 100644 (file)
index 0000000..8d19c1b
--- /dev/null
@@ -0,0 +1,3080 @@
+/*\r
+ * FreeRTOS+FAT build 191128 - Note:  FreeRTOS+FAT is still in the lab!\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
+ * Authors include James Walmsley, Hein Tibosch and Richard Barry\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * https://www.FreeRTOS.org\r
+ *\r
+ */\r
+\r
+/**\r
+ *     @file           ff_file.c\r
+ *     @ingroup        FILEIO\r
+ *\r
+ *     @defgroup       FILEIO FILE I/O Access\r
+ *     @brief          Provides an interface to allow File I/O on a mounted IOMAN.\r
+ *\r
+ *     Provides file-system interfaces for the FAT file-system.\r
+ **/\r
+\r
+#include "ff_headers.h"\r
+\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+#include <wchar.h>\r
+#endif\r
+\r
+static FF_Error_t FF_Truncate( FF_FILE *pxFile, BaseType_t bClosing );\r
+\r
+static int32_t FF_ReadPartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,\r
+       uint8_t *pucBuffer, FF_Error_t *pxError );\r
+\r
+static int32_t FF_WritePartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,\r
+       const uint8_t *pucBuffer, FF_Error_t *pxError );\r
+\r
+static uint32_t FF_SetCluster( FF_FILE *pxFile, FF_Error_t *pxError );\r
+static uint32_t FF_FileLBA( FF_FILE *pxFile );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Converts STDIO mode strings into the equivalent FreeRTOS+FAT mode.\r
+ *\r
+ *     @param  Mode    The mode string e.g. "rb" "rb+" "w" "a" "r" "w+" "a+" etc\r
+ *\r
+ *     @return Returns the mode bits that should be passed to the FF_Open function.\r
+ **/\r
+uint8_t FF_GetModeBits( const char *pcMode )\r
+{\r
+uint8_t ucModeBits = 0x00;\r
+\r
+       while( *pcMode != '\0' )\r
+       {\r
+               switch( *pcMode )\r
+               {\r
+                       case 'r':                                               /* Allow Read. */\r
+                       case 'R':\r
+                               ucModeBits |= FF_MODE_READ;\r
+                               break;\r
+\r
+                       case 'w':                                               /* Allow Write. */\r
+                       case 'W':\r
+                               ucModeBits |= FF_MODE_WRITE;\r
+                               ucModeBits |= FF_MODE_CREATE; /* Create if not exist. */\r
+                               ucModeBits |= FF_MODE_TRUNCATE;\r
+                               break;\r
+\r
+                       case 'a':                                               /* Append new writes to the end of the file. */\r
+                       case 'A':\r
+                               ucModeBits |= FF_MODE_WRITE;\r
+                               ucModeBits |= FF_MODE_APPEND;\r
+                               ucModeBits |= FF_MODE_CREATE; /* Create if not exist. */\r
+                               break;\r
+\r
+                       case '+':                                               /* Update the file, don't Append! */\r
+                               ucModeBits |= FF_MODE_READ;\r
+                               ucModeBits |= FF_MODE_WRITE;    /* RW Mode. */\r
+                               break;\r
+\r
+                       case 'D':\r
+                               /* Internal use only! */\r
+                               ucModeBits |= FF_MODE_DIR;\r
+                               break;\r
+\r
+                       case 'b':\r
+                       case 'B':\r
+                               /* b|B flags not supported (Binary mode is native anyway). */\r
+                               break;\r
+\r
+                       default:\r
+                               break;\r
+               }\r
+\r
+               pcMode++;\r
+       }\r
+\r
+       return ucModeBits;\r
+}      /* FF_GetModeBits() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static FF_FILE *prvAllocFileHandle( FF_IOManager_t *pxIOManager, FF_Error_t *pxError )\r
+{\r
+FF_FILE *pxFile;\r
+\r
+       pxFile = ffconfigMALLOC( sizeof( FF_FILE ) );\r
+       if( pxFile == NULL )\r
+       {\r
+               *pxError = ( FF_Error_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_OPEN );\r
+       }\r
+       else\r
+       {\r
+               memset( pxFile, 0, sizeof( *pxFile ) );\r
+\r
+               #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+               {\r
+                       pxFile->pucBuffer = ( uint8_t * ) ffconfigMALLOC( pxIOManager->usSectorSize );\r
+                       if( pxFile->pucBuffer != NULL )\r
+                       {\r
+                               memset( pxFile->pucBuffer, 0, pxIOManager->usSectorSize );\r
+                       }\r
+                       else\r
+                       {\r
+                               *pxError = ( FF_Error_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_OPEN );\r
+                               ffconfigFREE( pxFile );\r
+                               /* Make sure that NULL will be returned. */\r
+                               pxFile = NULL;\r
+                       }\r
+               }\r
+               #else\r
+               {\r
+                       /* Remove compiler warnings. */\r
+                       ( void ) pxIOManager;\r
+               }\r
+               #endif\r
+       }\r
+\r
+       return pxFile;\r
+}      /* prvAllocFileHandle() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * FF_Open() Mode Information\r
+ * - FF_MODE_WRITE\r
+ *   - Allows WRITE access to the file.\r
+ *   .\r
+ * - FF_MODE_READ\r
+ *   - Allows READ access to the file.\r
+ *   .\r
+ * - FF_MODE_CREATE\r
+ *   - Create file if it doesn't exist.\r
+ *   .\r
+ * - FF_MODE_TRUNCATE\r
+ *   - Erase the file if it already exists and overwrite.\r
+ *   *\r
+ * - FF_MODE_APPEND\r
+ *   - Causes all writes to occur at the end of the file. (Its impossible to overwrite other data in a file with this flag set).\r
+ *   .\r
+ * .\r
+ *\r
+ * Some sample modes:\r
+ * - (FF_MODE_WRITE | FF_MODE_CREATE | FF_MODE_TRUNCATE)\r
+ *   - Write access to the file. (Equivalent to "w").\r
+ *   .\r
+ * - (FF_MODE_WRITE | FF_MODE_READ)\r
+ *   - Read and Write access to the file. (Equivalent to "rb+").\r
+ *   .\r
+ * - (FF_MODE_WRITE | FF_MODE_READ | FF_MODE_APPEND | FF_MODE_CREATE)\r
+ *   - Read and Write append mode access to the file. (Equivalent to "a+").\r
+ *   .\r
+ * .\r
+ * Be careful when choosing modes. For those using FF_Open() at the application layer\r
+ * its best to use the provided FF_GetModeBits() function, as this complies to the same\r
+ * behaviour as the stdio.h fopen() function.\r
+ *\r
+ **/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Opens a File for Access\r
+ *\r
+ *     @param  pxIOManager     FF_IOManager_t object that was created by FF_CreateIOManger().\r
+ *     @param  pcPath          Path to the File or object.\r
+ *     @param  ucMode          Access Mode required. Modes are a little complicated, the function FF_GetModeBits()\r
+ *     @param  ucMode          will convert a stdio Mode string into the equivalent Mode bits for this parameter.\r
+ *     @param  pxError         Pointer to a signed byte for error checking. Can be NULL if not required.\r
+ *     @param  pxError         To be checked when a NULL pointer is returned.\r
+ *\r
+ *     @return NULL pointer on error, in which case pxError should be checked for more information.\r
+ *     @return pxError can be:\r
+ **/\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+FF_FILE *FF_Open( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath, uint8_t ucMode, FF_Error_t *pxError )\r
+#else\r
+FF_FILE *FF_Open( FF_IOManager_t *pxIOManager, const char *pcPath, uint8_t ucMode, FF_Error_t *pxError )\r
+#endif\r
+{\r
+FF_FILE *pxFile = NULL;\r
+FF_FILE *pxFileChain;\r
+FF_DirEnt_t xDirEntry;\r
+uint32_t ulFileCluster;\r
+FF_Error_t xError;\r
+BaseType_t xIndex;\r
+FF_FindParams_t xFindParams;\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+       FF_T_WCHAR pcFileName[ ffconfigMAX_FILENAME ];\r
+#else\r
+       char pcFileName[ ffconfigMAX_FILENAME ];\r
+#endif\r
+\r
+       memset( &xFindParams, '\0', sizeof( xFindParams ) );\r
+\r
+       /* Inform the functions that the entry will be created if not found. */\r
+       if( ( ucMode & FF_MODE_CREATE ) != 0 )\r
+       {\r
+               xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG;\r
+       }\r
+\r
+       if( pxIOManager == NULL )\r
+       {\r
+               /* Use the error function code 'FF_OPEN' as this static\r
+               function is only called from that function. */\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_OPEN );\r
+       }\r
+#if( ffconfigREMOVABLE_MEDIA != 0 )\r
+       else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_OPEN );\r
+       }\r
+#endif /* ffconfigREMOVABLE_MEDIA */\r
+       else\r
+       {\r
+               xError = FF_ERR_NONE;\r
+\r
+               /* Let xIndex point to the last occurrence of '/' or '\',\r
+               to separate the path from the file name. */\r
+               xIndex = ( BaseType_t ) STRLEN( pcPath );\r
+               while( xIndex != 0 )\r
+               {\r
+                       if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) )\r
+                       {\r
+                               break;\r
+                       }\r
+                       xIndex--;\r
+               }\r
+\r
+               /* Copy the file name, i.e. the string that comes after the last separator. */\r
+               STRNCPY( pcFileName, pcPath + xIndex + 1, ffconfigMAX_FILENAME );\r
+\r
+               if( xIndex == 0 )\r
+               {\r
+                       /* Only for the root, the slash is part of the directory name.\r
+                       'xIndex' now equals to the length of the path name. */\r
+                       xIndex = 1;\r
+               }\r
+\r
+               /* FF_CreateShortName() might set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK. */\r
+               FF_CreateShortName( &xFindParams, pcFileName );\r
+\r
+               /* Lookup the path and find the cluster pointing to the directory: */\r
+               xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, xIndex, &xError );\r
+               if( xFindParams.ulDirCluster == 0ul )\r
+               {\r
+                       if( ( ucMode & FF_MODE_WRITE ) != 0 )\r
+                       {\r
+                               FF_PRINTF( "FF_Open[%s]: Path not found\n", pcPath );\r
+                       }\r
+                       /* The user tries to open a file but the path leading to the file does not exist. */\r
+               }\r
+               else if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       /* Allocate an empty file handle and buffer space for 'unaligned access'. */\r
+                       pxFile = prvAllocFileHandle( pxIOManager, &xError );\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               /* Copy the Mode Bits. */\r
+               pxFile->ucMode = ucMode;\r
+\r
+               /* See if the file does exist within the given directory. */\r
+               ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0x00, &xDirEntry, &xError );\r
+\r
+               if( ulFileCluster == 0ul )\r
+               {\r
+                       /* If cluster 0 was returned, it might be because the file has no allocated cluster,\r
+                       i.e. only a directory entry and no stored data. */\r
+                       if( STRLEN( pcFileName ) == STRLEN( xDirEntry.pcFileName ) )\r
+                       {\r
+                               if( ( xDirEntry.ulFileSize == 0 ) && ( FF_strmatch( pcFileName, xDirEntry.pcFileName, ( BaseType_t ) STRLEN( pcFileName ) ) == pdTRUE ) )\r
+                               {\r
+                                       /* It is the file, give it a pseudo cluster number '1'. */\r
+                                       ulFileCluster = 1;\r
+                                       /* And reset any error. */\r
+                                       xError = FF_ERR_NONE;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /* Test 'ulFileCluster' again, it might have been changed. */\r
+               if( ulFileCluster == 0ul )\r
+               {\r
+                       /* The path is found, but it does not contain the file name yet.\r
+                       Maybe the user wants to create it? */\r
+                       if( ( ucMode & FF_MODE_CREATE ) == 0 )\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_OPEN );\r
+                       }\r
+                       else\r
+                       {\r
+                               ulFileCluster = FF_CreateFile( pxIOManager, &xFindParams, pcFileName, &xDirEntry, &xError );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xDirEntry.usCurrentItem += 1;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               /* Now the file exists, or it has been created.\r
+               Check if the Mode flags are allowed: */\r
+               if( ( xDirEntry.ucAttrib == FF_FAT_ATTR_DIR ) && ( ( ucMode & FF_MODE_DIR ) == 0 ) )\r
+               {\r
+                       /* Not the object, File Not Found! */\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_OBJECT_IS_A_DIR | FF_OPEN );\r
+               }\r
+               /*---------- Ensure Read-Only files don't get opened for Writing. */\r
+               else if( ( ( ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 ) && ( ( xDirEntry.ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_IS_READ_ONLY | FF_OPEN );\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               pxFile->pxIOManager = pxIOManager;\r
+               pxFile->ulFilePointer = 0;\r
+               /* Despite the warning output by MSVC - it is not possible to get here\r
+               if xDirEntry has not been initialised. */\r
+               pxFile->ulObjectCluster = xDirEntry.ulObjectCluster;\r
+               pxFile->ulFileSize = xDirEntry.ulFileSize;\r
+               pxFile->ulCurrentCluster = 0;\r
+               pxFile->ulAddrCurrentCluster = pxFile->ulObjectCluster;\r
+\r
+               pxFile->pxNext = NULL;\r
+               pxFile->ulDirCluster = xFindParams.ulDirCluster;\r
+               pxFile->usDirEntry = xDirEntry.usCurrentItem - 1;\r
+               pxFile->ulChainLength = 0;\r
+               pxFile->ulEndOfChain = 0;\r
+               pxFile->ulValidFlags &= ~( FF_VALID_FLAG_DELETED );\r
+\r
+               /* Add pxFile onto the end of our linked list of FF_FILE objects.\r
+               But first make sure that there are not 2 handles with write access\r
+               to the same object. */\r
+               FF_PendSemaphore( pxIOManager->pvSemaphore );\r
+               {\r
+                       pxFileChain = ( FF_FILE * ) pxIOManager->FirstFile;\r
+                       if( pxFileChain == NULL )\r
+                       {\r
+                               pxIOManager->FirstFile = pxFile;\r
+                       }\r
+                       else\r
+                       {\r
+                               for( ; ; )\r
+                               {\r
+                                       /* See if two file handles point to the same object. */\r
+                                       if( ( pxFileChain->ulObjectCluster == pxFile->ulObjectCluster ) &&\r
+                                               ( pxFileChain->ulDirCluster == pxFile->ulDirCluster ) &&\r
+                                               ( pxFileChain->usDirEntry == pxFile->usDirEntry ) )\r
+                                       {\r
+                                               /* Fail if any of the two has write access to the object. */\r
+                                               if( ( ( pxFileChain->ucMode | pxFile->ucMode ) & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 )\r
+                                               {\r
+                                                       /* File is already open! DON'T ALLOW IT! */\r
+                                                       xError = ( FF_Error_t ) ( FF_ERR_FILE_ALREADY_OPEN | FF_OPEN );\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+\r
+                                       if( pxFileChain->pxNext == NULL )\r
+                                       {\r
+                                               pxFileChain->pxNext = pxFile;\r
+                                               break;\r
+                                       }\r
+\r
+                                       pxFileChain = ( FF_FILE * ) pxFileChain->pxNext;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               FF_ReleaseSemaphore( pxIOManager->pvSemaphore );\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               /* If the file is opened with the truncate flag, truncate its contents. */\r
+               if( ( ucMode & FF_MODE_TRUNCATE ) != 0 )\r
+               {\r
+                       /* Set the current size and position to zero. */\r
+                       pxFile->ulFileSize = 0;\r
+                       pxFile->ulFilePointer = 0;\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) != pdFALSE )\r
+       {\r
+               if( pxFile != NULL )\r
+               {\r
+                       #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+                       {\r
+                               ffconfigFREE( pxFile->pucBuffer );\r
+                       }\r
+                       #endif\r
+                       ffconfigFREE( pxFile );\r
+               }\r
+               pxFile = NULL;\r
+       }\r
+\r
+       if( pxError != NULL )\r
+       {\r
+               *pxError = xError;\r
+       }\r
+\r
+       return pxFile;\r
+}  /* FF_Open() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Tests if a Directory contains any other files or folders.\r
+ *\r
+ *     @param  pxIOManager     FF_IOManager_t object returned from the FF_CreateIOManger() function.\r
+ *\r
+ **/\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+BaseType_t FF_isDirEmpty ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR *pcPath )\r
+#else\r
+BaseType_t FF_isDirEmpty ( FF_IOManager_t * pxIOManager, const char *pcPath )\r
+#endif\r
+{\r
+FF_DirEnt_t    xDirEntry;\r
+FF_Error_t     xError = FF_ERR_NONE;\r
+BaseType_t xReturn;\r
+\r
+       if( pxIOManager == NULL )\r
+       {\r
+               xReturn = pdFALSE;\r
+       }\r
+       else\r
+       {\r
+               xError = FF_FindFirst( pxIOManager, &xDirEntry, pcPath );\r
+\r
+               /* Assume the directory is empty until a file is\r
+               encountered with a name other than ".." or "." */\r
+               xReturn = pdTRUE;\r
+\r
+               while( xError == 0 )\r
+               {\r
+                       /* As we can't be sure the first 2 entries contain\r
+                        * "." and "..", check it, not just count them\r
+                        */\r
+               #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+                       if( ( wcscmp( xDirEntry.pcFileName, L".." ) != 0 ) && ( wcscmp( xDirEntry.pcFileName, L"." ) != 0 ) )\r
+               #else\r
+                       if( ( strcmp( xDirEntry.pcFileName, ".." ) != 0 ) && ( strcmp( xDirEntry.pcFileName, "." ) != 0 ) )\r
+               #endif\r
+                       {\r
+                               xReturn = pdFALSE;\r
+                               break;\r
+                       }\r
+                       xError = FF_FindNext( pxIOManager, &xDirEntry );\r
+               }\r
+       }\r
+\r
+       return xReturn;\r
+}      /* FF_isDirEmpty() */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigPATH_CACHE != 0 )\r
+       /* _HT_ After a directory has been renamed, the path cache becomes out-of-date */\r
+       #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+       static void FF_RmPathCache ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath )\r
+       #else\r
+       static void FF_RmPathCache ( FF_IOManager_t * pxIOManager, const char *pcPath )\r
+       #endif\r
+       {\r
+               /*\r
+       * The directory 'path' will be removed or renamed\r
+       * now clear all entries starting with 'path' in the path cache\r
+       */\r
+               BaseType_t      xIndex;\r
+               BaseType_t      pathLen = STRLEN( pcPath );\r
+\r
+               FF_PendSemaphore( pxIOManager->pvSemaphore );\r
+               {\r
+                       for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ )\r
+                       {\r
+                               BaseType_t      len2 = STRLEN( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath );\r
+\r
+                               if( len2 >= pathLen && FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) )\r
+                               {\r
+                                       pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath[ 0 ] = '\0';\r
+                                       pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster = 0;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               FF_ReleaseSemaphore( pxIOManager->pvSemaphore );\r
+       }\r
+#endif /* ffconfigPATH_CACHE */\r
+/*-----------------------------------------------------------*/\r
+\r
+\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+FF_Error_t FF_RmDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )\r
+#else\r
+FF_Error_t FF_RmDir( FF_IOManager_t *pxIOManager, const char *pcPath )\r
+#endif\r
+{\r
+FF_FILE                                *pxFile;\r
+uint8_t                                ucEntryBuffer[32];\r
+FF_FetchContext_t      xFetchContext;\r
+FF_Error_t                     xError = FF_ERR_NONE;\r
+\r
+       if( pxIOManager == NULL )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_RMDIR );\r
+       }\r
+#if( ffconfigREMOVABLE_MEDIA != 0 )\r
+       else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_RMDIR );\r
+       }\r
+#endif /* ffconfigREMOVABLE_MEDIA */\r
+       else\r
+       {\r
+               pxFile = FF_Open( pxIOManager, pcPath, FF_MODE_DIR, &xError );\r
+\r
+               if( pxFile != NULL )\r
+               {\r
+                       pxFile->ulValidFlags |= FF_VALID_FLAG_DELETED;\r
+\r
+                       /* Clear the struct to allow a call to FF_CleanupEntryFetch() in any\r
+                       state. */\r
+                       memset( &xFetchContext, '\0', sizeof( xFetchContext ) );\r
+\r
+                       /* This task will get the unique right to change directories. */\r
+                       FF_LockDirectory( pxIOManager );\r
+                       do /* while( pdFALSE ) */\r
+                       {\r
+                               /* This while loop is only introduced to be able to use break\r
+                               statements. */\r
+                               if( FF_isDirEmpty( pxIOManager, pcPath ) == pdFALSE )\r
+                               {\r
+                                       xError = ( FF_ERR_DIR_NOT_EMPTY | FF_RMDIR );\r
+                                       break;\r
+                               }\r
+                               FF_LockFAT( pxIOManager );\r
+                               #if( ffconfigHASH_CACHE != 0 )\r
+                               {\r
+                                       /* A directory is removed so invalidate any hash table\r
+                                       referring to this directory. */\r
+                                       FF_UnHashDir( pxIOManager, pxFile->ulObjectCluster );\r
+                               }\r
+                               #endif  /* ffconfigHASH_CACHE */\r
+                               {\r
+                                       /* Add parameter 0 to delete the entire chain!\r
+                                       The actual directory entries on disk will be freed. */\r
+                                       xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );\r
+                               }\r
+                               FF_UnlockFAT( pxIOManager );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               /* Now remove this directory from its parent directory.\r
+                               Initialise the dirent Fetch Context object for faster removal of\r
+                               dirents. */\r
+                               xError = FF_InitEntryFetch( pxIOManager, pxFile->ulDirCluster, &xFetchContext );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               #if( ffconfigHASH_CACHE != 0 )\r
+                               {\r
+                                       /* Invalidate any hash table of the parent directory\r
+                                       as well. */\r
+                                       FF_UnHashDir( pxIOManager, pxFile->ulDirCluster );\r
+                               }\r
+                               #endif  /* ffconfigHASH_CACHE */\r
+\r
+                               /* Edit the Directory Entry, so it will show as deleted.\r
+                               First remove the LFN entries: */\r
+                               xError = FF_RmLFNs( pxIOManager, pxFile->usDirEntry, &xFetchContext );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               /* And remove the Short file name entry: */\r
+                               xError = FF_FetchEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       ucEntryBuffer[0] = FF_FAT_DELETED;\r
+                                       FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );\r
+                                       FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW,  ( uint32_t ) 0ul );\r
+\r
+                                       xError = FF_PushEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                               }\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               #if( ffconfigPATH_CACHE != 0 )\r
+                               {\r
+                                       /* We're removing a directory which might contain\r
+                                       subdirectories.  Instead of iterating through all\r
+                                       subdirectories, just clear the path cache. */\r
+                                       FF_RmPathCache( pxIOManager, pcPath );\r
+                               }\r
+                               #endif\r
+                       } while( pdFALSE );\r
+                       {\r
+                       FF_Error_t xTempError;\r
+                               xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+                               FF_UnlockDirectory( pxIOManager );\r
+\r
+                               /* Free the file pointer resources. */\r
+                               xTempError = FF_Close( pxFile );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+\r
+                               xTempError = FF_FlushCache( pxIOManager );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+                       }\r
+               }       /* if( pxFile != NULL ) */\r
+       }       /* else if( pxIOManager != NULL ) */\r
+\r
+       return xError;\r
+}      /* FF_RmDir() */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+FF_Error_t FF_RmFile( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )\r
+#else\r
+FF_Error_t FF_RmFile( FF_IOManager_t *pxIOManager, const char *pcPath )\r
+#endif\r
+{\r
+FF_FILE *pxFile;\r
+FF_Error_t xError = FF_ERR_NONE;\r
+uint8_t ucEntryBuffer[32];\r
+FF_FetchContext_t xFetchContext;\r
+\r
+       /* Opening the file-to-be-deleted in WR mode has two advantages:\r
+       1. The file handle gives all necessary information to delete it such\r
+          as the data clusters and directory entries.\r
+       2. The file is now locked, it can not be opened by another task. */\r
+       pxFile = FF_Open( pxIOManager, pcPath, FF_MODE_WRITE, &xError );\r
+\r
+       if( pxFile != NULL )\r
+       {\r
+               /* FF_Close() will see this flag and won't do any disc access. */\r
+               pxFile->ulValidFlags |= FF_VALID_FLAG_DELETED;\r
+\r
+               /* Ensure there is actually a cluster chain to delete! */\r
+               if( pxFile->ulObjectCluster != 0 )\r
+               {\r
+                       /* Lock the FAT so its thread-safe. */\r
+                       FF_LockFAT( pxIOManager );\r
+                       {\r
+                               /* 0 to delete the entire chain! */\r
+                               xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );\r
+                       }\r
+                       FF_UnlockFAT( pxIOManager );\r
+               }\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       /* Clear the struct to allow a call to FF_CleanupEntryFetch() in any\r
+                       state. */\r
+                       memset( &xFetchContext, '\0', sizeof( xFetchContext ) );\r
+\r
+                       /* Get sole access to "directory changes" */\r
+                       FF_LockDirectory( pxIOManager );\r
+\r
+                       /* Edit the Directory Entry! (So it appears as deleted); */\r
+                       do {\r
+                               xError = FF_InitEntryFetch( pxIOManager, pxFile->ulDirCluster, &xFetchContext );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               #if( ffconfigHASH_CACHE != 0 )\r
+                               {\r
+                                       FF_UnHashDir( pxIOManager, pxFile->ulDirCluster );\r
+                               }\r
+                               #endif  /* ffconfigHASH_CACHE */\r
+                               /* Remove LFN entries, if any. */\r
+                               xError = FF_RmLFNs( pxIOManager, ( uint16_t ) pxFile->usDirEntry, &xFetchContext );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               /* Remove the Short file name entry. */\r
+                               xError = FF_FetchEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       ucEntryBuffer[0] = FF_FAT_DELETED;\r
+                                       FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );\r
+                                       FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW,  ( uint32_t ) 0ul );\r
+\r
+                                       xError = FF_PushEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                               }\r
+                       } while( pdFALSE );\r
+                       {\r
+                       FF_Error_t xTempError;\r
+                               xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+                               FF_UnlockDirectory( pxIOManager );\r
+\r
+                               /* Free the file pointer resources. */\r
+                               xTempError = FF_Close( pxFile );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+\r
+                               xTempError = FF_FlushCache( pxIOManager );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = xTempError;\r
+                               }\r
+                       }\r
+               }\r
+       }       /* if( pxFile != NULL ) */\r
+\r
+       return xError;\r
+}      /* FF_RmFile() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Moves a file or directory from source to destination.\r
+ *\r
+ *     @param  pxIOManager                             The FF_IOManager_t object pointer.\r
+ *     @param  szSourceFile            String of the source file to be moved or renamed.\r
+ *     @param  szDestinationFile       String of the destination file to where the source should be moved or renamed.\r
+ *\r
+ *     @return FF_ERR_NONE on success.\r
+ *     @return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.\r
+ *     @return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).\r
+ *     @return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.\r
+ *     @return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.\r
+ *\r
+ **/\r
+\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+FF_Error_t FF_Move( FF_IOManager_t *pxIOManager, const FF_T_WCHAR*szSourceFile,\r
+       const FF_T_WCHAR *szDestinationFile, BaseType_t xDeleteIfExists )\r
+#else\r
+FF_Error_t FF_Move( FF_IOManager_t *pxIOManager, const char    *szSourceFile,\r
+       const char      *szDestinationFile, BaseType_t  xDeleteIfExists )\r
+#endif\r
+{\r
+FF_Error_t xError;\r
+FF_FILE *pSrcFile, *pxDestFile;\r
+FF_DirEnt_t xMyFile;\r
+uint8_t ucEntryBuffer[32];\r
+BaseType_t xIndex;\r
+uint32_t ulDirCluster = 0ul;\r
+FF_FetchContext_t xFetchContext;\r
+#if( ffconfigPATH_CACHE != 0 )\r
+       BaseType_t xIsDirectory = pdFALSE;\r
+#endif\r
+\r
+       memset( &xFetchContext, '\0', sizeof( xFetchContext ) );\r
+\r
+       if( pxIOManager == NULL )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );\r
+       }\r
+#if( ffconfigREMOVABLE_MEDIA != 0 )\r
+       else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_MOVE );\r
+       }\r
+#endif /* ffconfigREMOVABLE_MEDIA */\r
+       else\r
+       {\r
+               /* Check destination file doesn't exist! */\r
+               pxDestFile = FF_Open( pxIOManager, szDestinationFile, FF_MODE_READ, &xError );\r
+\r
+               if( ( pxDestFile != NULL) || ( FF_GETERROR( xError ) == FF_ERR_FILE_OBJECT_IS_A_DIR ) )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_DESTINATION_EXISTS | FF_MOVE );\r
+                       if( pxDestFile != NULL )\r
+                       {\r
+                               FF_Close( pxDestFile );\r
+                               if( xDeleteIfExists != pdFALSE )\r
+                               {\r
+                                       xError = FF_RmFile( pxIOManager, szDestinationFile );\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       /* Discard the error set by FF_Open().\r
+                       The target file (or directory) is not found: continue renaming. */\r
+                       xError = FF_ERR_NONE;\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               /* About to move/rename 'szSourceFile'.  When opening it with 'FF_MODE_WRITE'\r
+               only succeeds if it has no other open handle to it. */\r
+               pSrcFile = FF_Open( pxIOManager, szSourceFile, FF_MODE_WRITE, &xError );\r
+\r
+               if( FF_GETERROR( xError ) == FF_ERR_FILE_OBJECT_IS_A_DIR )\r
+               {\r
+                       /* Open a directory for moving! */\r
+                       pSrcFile = FF_Open( pxIOManager, szSourceFile, FF_MODE_DIR, &xError );\r
+       #if( ffconfigPATH_CACHE != 0 )\r
+                       xIsDirectory = pdTRUE;\r
+       #endif\r
+               }\r
+\r
+               if( pSrcFile != NULL )\r
+               {\r
+                       /* Collect information about the current directory entry. */\r
+                       xError = FF_InitEntryFetch( pxIOManager, pSrcFile->ulDirCluster, &xFetchContext );\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               xError = FF_FetchEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xMyFile.ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );\r
+                                       xMyFile.ulFileSize = pSrcFile->ulFileSize;\r
+                                       xMyFile.ulObjectCluster = pSrcFile->ulObjectCluster;\r
+                                       xMyFile.usCurrentItem = 0;\r
+\r
+                                       xIndex = ( BaseType_t ) STRLEN( szDestinationFile );\r
+\r
+                                       while( xIndex != 0 )\r
+                                       {\r
+                                               if( ( szDestinationFile[ xIndex ] == '\\' ) || ( szDestinationFile[ xIndex ] == '/' ) )\r
+                                               {\r
+                                                       break;\r
+                                               }\r
+\r
+                                               xIndex--;\r
+                                       }\r
+\r
+                                       /* Copy the base name of the destination file. */\r
+                                       STRNCPY( xMyFile.pcFileName, ( szDestinationFile + xIndex + 1 ), ffconfigMAX_FILENAME );\r
+\r
+                                       if( xIndex == 0 )\r
+                                       {\r
+                                               xIndex = 1;\r
+                                       }\r
+\r
+                                       /* Find the (cluster of the) directory in which the target file will be located.\r
+                                       It must exist before calling FF_Move(). */\r
+                                       ulDirCluster = FF_FindDir( pxIOManager, szDestinationFile, xIndex, &xError );\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( ulDirCluster != 0ul )\r
+                       {\r
+                               FF_FindParams_t xFindParams;\r
+                               memset( &xFindParams, '\0', sizeof( xFindParams ) );\r
+\r
+                               /* Clean up because FF_CreateDirent might want to write to the same sector. */\r
+                               xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       /* Destination directory was found, we can now create the new entry. */\r
+                                       xFindParams.ulDirCluster = ulDirCluster;\r
+                                       xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyFile );\r
+                               }\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       /* Edit the Directory Entry! (So it appears as deleted); */\r
+                                       FF_LockDirectory( pxIOManager );\r
+                                       {\r
+                                               xError = FF_RmLFNs( pxIOManager, pSrcFile->usDirEntry, &xFetchContext );\r
+\r
+                                               if( FF_isERR( xError ) == pdFALSE )\r
+                                               {\r
+                                                       xError = FF_FetchEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+\r
+                                                       if( FF_isERR( xError ) == pdFALSE )\r
+                                                       {\r
+                                                       FF_Error_t xTempError;\r
+                                                               ucEntryBuffer[0] = FF_FAT_DELETED;\r
+                                                               FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );\r
+                                                               FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW,  ( uint32_t ) 0ul );\r
+\r
+                                                               xError = FF_PushEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );\r
+                                                               /* The contents of 'xFetchContext' has changed, flush it to disk now. */\r
+                                                               xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );\r
+                                                               if( FF_isERR( xError ) == pdFALSE )\r
+                                                               {\r
+                                                                       xError = xTempError;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       FF_UnlockDirectory( pxIOManager );\r
+                               }\r
+\r
+                               #if( ffconfigPATH_CACHE != 0 )\r
+                               {\r
+                                       if( xIsDirectory != 0 )\r
+                                       {\r
+                                               /* We've renamed a directory which might contain\r
+                                               subdirectories.  To avoid having false entries, clear\r
+                                               the path cache. */\r
+                                               FF_RmPathCache( pxIOManager, szSourceFile );\r
+                                       }\r
+                               }\r
+                               #endif\r
+                       }\r
+                       else    /* ulDirCluster == 0ul */\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_DIR_NOT_FOUND | FF_MOVE );\r
+                       }\r
+               }\r
+\r
+               if( pSrcFile != NULL )\r
+               {\r
+                       /* The source file was opened in WRITE mode just to lock it.\r
+                       Now clear the write flags to avoid writing back any changes. */\r
+                       pSrcFile->ucMode &= ~( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE );\r
+                       FF_Close( pSrcFile );\r
+               }\r
+       }\r
+\r
+       {\r
+       FF_Error_t xTempError;\r
+\r
+               xTempError = FF_FlushCache( pxIOManager );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       xError = xTempError;\r
+               }\r
+               xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       xError = xTempError;\r
+               }\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_Move() */\r
+/*-----------------------------------------------------------*/\r
+\r
+                                       /**\r
+ *     @public\r
+ *     @brief  Get's the next Entry based on the data recorded in the FF_DirEnt_t object.\r
+ *\r
+ *     @param  pxFile  FF_FILE object that was created by FF_Open().\r
+ *\r
+ *     @return pdTRUE if End of File was reached. pdFALSE if not.\r
+ *     @return pdFALSE if a null pointer was provided.\r
+ *\r
+ **/\r
+BaseType_t FF_isEOF( FF_FILE *pxFile )\r
+{\r
+BaseType_t xReturn;\r
+\r
+       if( ( pxFile != NULL ) && ( pxFile->ulFilePointer >= pxFile->ulFileSize ) )\r
+       {\r
+               xReturn = pdTRUE;\r
+       }\r
+       else\r
+       {\r
+               xReturn = pdFALSE;\r
+       }\r
+\r
+       return xReturn;\r
+}      /* FF_isEOF() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Checks the number of bytes left on a read handle\r
+ *\r
+ *     @param  pxFile          An open file handle\r
+ *\r
+ *     @return Less than zero: an error code\r
+ *     @return Number of bytes left to read from handle\r
+ **/\r
+int32_t FF_BytesLeft( FF_FILE *pxFile )\r
+{\r
+BaseType_t xReturn;\r
+\r
+       if( pxFile == NULL )\r
+       {\r
+               xReturn = FF_ERR_NULL_POINTER | FF_BYTESLEFT;\r
+       }\r
+       else if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )\r
+       {\r
+               xReturn = FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_BYTESLEFT;\r
+       }\r
+       else if( pxFile->ulFilePointer >= pxFile->ulFileSize )\r
+       {\r
+               xReturn = 0;\r
+       }\r
+       else\r
+       {\r
+               xReturn = pxFile->ulFileSize - pxFile->ulFilePointer;\r
+       }\r
+\r
+       return xReturn;\r
+}      /* FF_BytesLeft() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Returns the file size of a read handle\r
+ *\r
+ *     @param  pxFile          An open file handle\r
+ *\r
+ *     @return Less than zero: an error code\r
+ *     @return Number of bytes left to read from handle\r
+ **/\r
+FF_Error_t FF_GetFileSize( FF_FILE *pxFile, uint32_t *pulSize ) /* Writes # of bytes in a file to the parameter. */\r
+{\r
+BaseType_t xReturn;\r
+\r
+       if( pxFile == NULL )\r
+       {\r
+               xReturn = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_BYTESLEFT );\r
+               *( pulSize ) = ( uint32_t ) 0u;\r
+       }\r
+       else if( FF_isERR( FF_CheckValid( pxFile ) ) )\r
+       {\r
+               xReturn = ( FF_Error_t ) ( FF_ERR_FILE_BAD_HANDLE | FF_BYTESLEFT );\r
+               *( pulSize ) = ( uint32_t ) 0u;\r
+       }\r
+       else\r
+       {\r
+               xReturn = 0;\r
+               *( pulSize ) = pxFile->ulFileSize;\r
+       }\r
+\r
+       return xReturn;\r
+}      /* FF_GetFileSize */\r
+\r
+int32_t FF_FileSize( FF_FILE *pxFile )\r
+{\r
+uint32_t ulLength;\r
+FF_Error_t xResult;\r
+\r
+       /* Function is deprecated. Please use FF_GetFileSize(). */\r
+       xResult = FF_GetFileSize( pxFile, &( ulLength ) );\r
+\r
+       if( FF_isERR( xResult ) == 0 )\r
+       {\r
+               xResult = ( int32_t ) ulLength;\r
+       }\r
+\r
+       return ( int32_t ) xResult;\r
+}      /* FF_FileSize() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static uint32_t FF_GetSequentialClusters( FF_IOManager_t *pxIOManager, uint32_t ulStartCluster, uint32_t ulLimit, FF_Error_t *pxError )\r
+{\r
+uint32_t ulCurrentCluster;\r
+uint32_t ulNextCluster = ulStartCluster;\r
+uint32_t ulIndex = 0;\r
+\r
+       FF_FATBuffers_t xFATBuffers;\r
+       FF_InitFATBuffers( &xFATBuffers, FF_MODE_READ );\r
+\r
+       *pxError = FF_ERR_NONE;\r
+\r
+       FF_LockFAT( pxIOManager );\r
+       do\r
+       {\r
+               ulCurrentCluster = ulNextCluster;\r
+               ulNextCluster = FF_getFATEntry( pxIOManager, ulCurrentCluster, pxError, &xFATBuffers );\r
+               if( FF_isERR( *pxError ) )\r
+               {\r
+                       ulIndex = 0;\r
+                       break;\r
+               }\r
+\r
+               if( ulNextCluster == ( ulCurrentCluster + 1 ) )\r
+               {\r
+                       ulIndex++;\r
+               }\r
+               else\r
+               {\r
+                       break;\r
+               }\r
+\r
+               if( ( ulLimit != 0 ) && ( ulIndex == ulLimit ) )\r
+               {\r
+                       break;\r
+               }\r
+       }\r
+       while( ulNextCluster == ( ulCurrentCluster + 1 ) );\r
+\r
+       FF_UnlockFAT( pxIOManager );\r
+\r
+       *pxError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );\r
+\r
+       return ulIndex;\r
+}      /* FF_GetSequentialClusters() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static FF_Error_t FF_ReadClusters( FF_FILE *pxFile, uint32_t ulCount, uint8_t *buffer )\r
+{\r
+uint32_t ulSectors;\r
+uint32_t ulSequentialClusters = 0;\r
+uint32_t ulItemLBA;\r
+FF_Error_t xError = FF_ERR_NONE;\r
+\r
+       while( ulCount != 0 )\r
+       {\r
+               if( ( ulCount - 1 ) > 0 )\r
+               {\r
+                       ulSequentialClusters =\r
+                               FF_GetSequentialClusters( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulCount - 1, &xError );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               ulSectors = ( ulSequentialClusters + 1 ) * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;\r
+               ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );\r
+               ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );\r
+\r
+               xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, ulSectors, buffer, pdFALSE );\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               ulCount -= ( ulSequentialClusters + 1 );\r
+\r
+               FF_LockFAT( pxFile->pxIOManager );\r
+               {\r
+                       pxFile->ulAddrCurrentCluster =\r
+                               FF_TraverseFAT( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulSequentialClusters + 1, &xError );\r
+               }\r
+               FF_UnlockFAT( pxFile->pxIOManager );\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               pxFile->ulCurrentCluster += ( ulSequentialClusters + 1 );\r
+               buffer += ulSectors * pxFile->pxIOManager->usSectorSize;\r
+               ulSequentialClusters = 0;\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_ReadClusters ()*/\r
+/*-----------------------------------------------------------*/\r
+\r
+static FF_Error_t FF_ExtendFile( FF_FILE *pxFile, uint32_t ulSize )\r
+{\r
+FF_IOManager_t *pxIOManager = pxFile->pxIOManager;\r
+uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster;\r
+uint32_t ulTotalClustersNeeded = ( ulSize + ulBytesPerCluster - 1 ) / ulBytesPerCluster;\r
+uint32_t ulClusterToExtend;\r
+/* Initialise xIndex just for the compiler. */\r
+BaseType_t xIndex = 0;\r
+FF_DirEnt_t xOriginalEntry;\r
+FF_Error_t xError = FF_ERR_NONE;\r
+FF_FATBuffers_t xFATBuffers;\r
+\r
+       if( ( pxFile->ucMode & FF_MODE_WRITE ) != FF_MODE_WRITE )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_EXTENDFILE );\r
+       }\r
+       else\r
+       {\r
+               if( ( pxFile->ulFileSize == 0 ) && ( pxFile->ulObjectCluster == 0 ) )\r
+               {\r
+                       /* If there is no object cluster yet, create it.*/\r
+                       pxFile->ulAddrCurrentCluster = FF_CreateClusterChain( pxFile->pxIOManager, &xError );\r
+\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               /* The directory denotes the address of the first data cluster of every file.\r
+                               Now change it to 'ulAddrCurrentCluster': */\r
+                               xError = FF_GetEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xOriginalEntry.ulObjectCluster = pxFile->ulAddrCurrentCluster;\r
+                                       xError = FF_PutEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );\r
+\r
+                                       if( FF_isERR( xError ) == pdFALSE )\r
+                                       {\r
+                                               pxFile->ulObjectCluster = pxFile->ulAddrCurrentCluster;\r
+                                               pxFile->ulChainLength = 1;\r
+                                               pxFile->ulCurrentCluster = 0;\r
+                                               pxFile->ulEndOfChain = pxFile->ulAddrCurrentCluster;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       /* This file already has at least one cluster. */\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               if( pxFile->ulChainLength == 0 )\r
+               {\r
+                       /* This is the first extension requiring the chain length.\r
+                       Calculate it now: */\r
+                       pxFile->ulChainLength = FF_GetChainLength( pxIOManager, pxFile->ulObjectCluster, &pxFile->ulEndOfChain, &xError );\r
+               }\r
+       }\r
+\r
+       if( ( FF_isERR( xError ) == pdFALSE ) && ( ulTotalClustersNeeded > pxFile->ulChainLength ) )\r
+       {\r
+       uint32_t ulCurrentCluster, ulNextCluster;\r
+\r
+               ulClusterToExtend = ( ulTotalClustersNeeded - pxFile->ulChainLength );\r
+               /* Now the file has at least 1 cluster, but it needs more clusters. */\r
+               ulNextCluster = pxFile->ulAddrCurrentCluster;\r
+               FF_LockFAT( pxIOManager );\r
+\r
+               ulCurrentCluster = FF_FindEndOfChain( pxIOManager, ulNextCluster, &xError );\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       for( xIndex = 0; xIndex < ( BaseType_t ) ulClusterToExtend; xIndex++ )\r
+                       {\r
+                               /* In FF_ExtendFile() */\r
+                               ulNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE );\r
+                               if( ( FF_isERR( xError ) == pdFALSE ) && ( ulNextCluster == 0UL ) )\r
+                               {\r
+                                       xError = ( FF_Error_t ) ( FF_ERR_FAT_NO_FREE_CLUSTERS | FF_EXTENDFILE );\r
+                               }\r
+\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               /* Can not use this buffer earlier because of FF_FindEndOfChain/FF_FindFreeCluster */\r
+                               FF_InitFATBuffers( &xFATBuffers, FF_MODE_WRITE );\r
+                               xError = FF_putFATEntry( pxIOManager, ulCurrentCluster, ulNextCluster, &xFATBuffers );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               xError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulCurrentCluster = ulNextCluster;\r
+                       }\r
+\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               pxFile->ulEndOfChain = ulCurrentCluster;\r
+                       }\r
+                       pxFile->ulChainLength += xIndex;\r
+               }\r
+               FF_UnlockFAT( pxIOManager );\r
+\r
+               {\r
+               FF_Error_t xTempError;\r
+                       xTempError = FF_DecreaseFreeClusters( pxIOManager, ( uint32_t ) xIndex );       /* Keep Tab of Numbers for fast FreeSize() */\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               xError = xTempError;\r
+                       }\r
+               }\r
+\r
+               /* We must ensure that the ulAddrCurrentCluster is not out-of-sync with the CurrentCluster number.\r
+               This could have occurred in append mode, where the file was opened with a filesize % clustersize == 0\r
+               because of a seek, where the ulAddrCurrentCluster was not updated after extending. This caused the data to\r
+               be written to the previous cluster(s). */\r
+               if( ( pxFile->ulCurrentCluster == pxFile->ulChainLength - 1 ) &&\r
+                       ( pxFile->ulAddrCurrentCluster != pxFile->ulEndOfChain ) )\r
+               {\r
+                       pxFile->ulAddrCurrentCluster = pxFile->ulEndOfChain;\r
+               }\r
+\r
+               /* By default, 'ffconfigFILE_EXTEND_FLUSHES_BUFFERS' is\r
+               defined as 1.\r
+               Users may set it to zero in order to increase the\r
+               speed of writing to disk. */\r
+\r
+               #if( ffconfigFILE_EXTEND_FLUSHES_BUFFERS != 0 )\r
+               {\r
+               FF_Error_t xTempError;\r
+\r
+                       xTempError = FF_FlushCache( pxIOManager );\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               xError = xTempError;\r
+                       }\r
+               }\r
+               #endif  /* ffconfigFILE_EXTEND_FLUSHES_BUFFERS */\r
+       } /* if( ulTotalClustersNeeded > pxFile->ulChainLength ) */\r
+\r
+       return xError;\r
+}      /* FF_ExtendFile() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static FF_Error_t FF_WriteClusters( FF_FILE *pxFile, uint32_t ulCount, uint8_t *buffer )\r
+{\r
+uint32_t ulSectors;\r
+uint32_t ulSequentialClusters = 0;\r
+uint32_t ulItemLBA;\r
+FF_Error_t xError = FF_ERR_NONE;\r
+\r
+       while( ulCount != 0 )\r
+       {\r
+               if( ( ulCount - 1 ) > 0 )\r
+               {\r
+                       ulSequentialClusters =\r
+                               FF_GetSequentialClusters( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulCount - 1, &xError );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               ulSectors = ( ulSequentialClusters + 1 ) * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;\r
+               ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );\r
+               ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );\r
+\r
+               xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, ulSectors, buffer, pdFALSE );\r
+\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               ulCount -= ulSequentialClusters + 1;\r
+\r
+               FF_LockFAT( pxFile->pxIOManager );\r
+               {\r
+                       pxFile->ulAddrCurrentCluster =\r
+                               FF_TraverseFAT( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulSequentialClusters + 1, &xError );\r
+               }\r
+               FF_UnlockFAT( pxFile->pxIOManager );\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               pxFile->ulCurrentCluster += ( ulSequentialClusters + 1 );\r
+               buffer += ulSectors * pxFile->pxIOManager->usSectorSize;\r
+               ulSequentialClusters = 0;\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_WriteClusters */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @private\r
+ *     @brief  Calculate the Logical Block Address (LBA)\r
+ *\r
+ *     @param  pxFile       The file handle\r
+ *\r
+ *     @return LBA\r
+ *\r
+ *  Must be set:\r
+ *    - pxFile->ulFilePointer        : byte offset in file\r
+ *    - pxFile->ulAddrCurrentCluster : physical cluster on the partition\r
+ **/\r
+static uint32_t FF_FileLBA( FF_FILE *pxFile )\r
+{\r
+       uint32_t        ulItemLBA;\r
+       ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );\r
+       ulItemLBA += FF_getMajorBlockNumber( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );\r
+       ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );\r
+       ulItemLBA += FF_getMinorBlockNumber( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );\r
+\r
+       return ulItemLBA;\r
+}      /* FF_FileLBA() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @private\r
+ *     @brief  Depending on FilePointer, calculate CurrentCluster\r
+ *  @brief     and traverse the FAT to find the right ulAddrCurrentCluster\r
+ *\r
+ *     @param  pxFile       The file handle\r
+ *\r
+ *     @return FF_ERR_NONE on success\r
+ *     @return Possible error returned by FF_TraverseFAT() or END_OF_DIR\r
+ *\r
+ *  Side effects:\r
+ *    - pxFile->ulCurrentCluster     : relative cluster number (0 <= Num < ulChainLength)\r
+ *    - pxFile->ulAddrCurrentCluster : fysical cluster on the partition\r
+ **/\r
+static uint32_t FF_SetCluster( FF_FILE *pxFile, FF_Error_t *pxError )\r
+{\r
+FF_IOManager_t *pxIOManager = pxFile->pxIOManager;\r
+uint32_t ulNewCluster = FF_getClusterChainNumber( pxIOManager, pxFile->ulFilePointer, 1 );\r
+FF_Error_t xResult = FF_ERR_NONE;\r
+uint32_t ulReturn;\r
+\r
+       if( ulNewCluster > pxFile->ulCurrentCluster )\r
+       {\r
+               FF_LockFAT( pxIOManager );\r
+               {\r
+                       pxFile->ulAddrCurrentCluster = FF_TraverseFAT( pxIOManager, pxFile->ulAddrCurrentCluster,\r
+                               ulNewCluster - pxFile->ulCurrentCluster, &xResult );\r
+               }\r
+               FF_UnlockFAT( pxIOManager );\r
+       }\r
+       else if( ulNewCluster < pxFile->ulCurrentCluster )\r
+       {\r
+               FF_LockFAT( pxIOManager );\r
+               {\r
+                       pxFile->ulAddrCurrentCluster = FF_TraverseFAT( pxIOManager, pxFile->ulObjectCluster, ulNewCluster, &xResult );\r
+               }\r
+               FF_UnlockFAT( pxIOManager );\r
+       }\r
+       else\r
+       {\r
+               /* Well positioned. */\r
+       }\r
+\r
+       if( FF_isERR( xResult ) == pdFALSE )\r
+       {\r
+               pxFile->ulCurrentCluster = ulNewCluster;\r
+               ulReturn = FF_FileLBA( pxFile );\r
+       }\r
+       else\r
+       {\r
+               ulReturn = 0;\r
+       }\r
+       *pxError = xResult;\r
+\r
+       return ulReturn;\r
+}      /* FF_SetCluster() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static int32_t FF_ReadPartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,\r
+       uint8_t *pucBuffer, FF_Error_t *pxError )\r
+{\r
+FF_Error_t xError = FF_ERR_NONE;\r
+uint32_t ulBytesRead;\r
+\r
+       /* Bytes to read are within a block and less than a block size. */\r
+       #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+       {\r
+       BaseType_t xLastRead;\r
+\r
+               /* Optimised method: each file handle holds one data block\r
+               in cache: 'pxFile->pucBuffer'. */\r
+               /* See if the current block will be accessed after this read: */\r
+               if( ( ulRelBlockPos + ulCount ) >= ( uint32_t ) pxFile->pxIOManager->usSectorSize )\r
+               {\r
+                       /* After this read, ulFilePointer will point to the next block/sector. */\r
+                       xLastRead = pdTRUE;\r
+               }\r
+               else\r
+               {\r
+                       /* It is not the last read within this block/sector. */\r
+                       xLastRead = pdFALSE;\r
+               }\r
+\r
+               if( ( pxFile->ucState & FF_BUFSTATE_VALID ) == 0 )\r
+               {\r
+                       xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               pxFile->ucState = FF_BUFSTATE_VALID;\r
+                       }\r
+               }\r
+\r
+               if( ( pxFile->ucState & FF_BUFSTATE_VALID ) != 0 )\r
+               {\r
+                       memcpy( pucBuffer, pxFile->pucBuffer + ulRelBlockPos, ulCount );\r
+                       pxFile->ulFilePointer += ulCount;\r
+                       ulBytesRead = ulCount;\r
+                       if( ( xLastRead == pdTRUE ) && ( ( pxFile->ucState & FF_BUFSTATE_WRITTEN ) != 0 ) )\r
+                       {\r
+                               /* If the data was changed (file in 'update' mode), store the changes: */\r
+                               xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       ulBytesRead = 0ul;\r
+               }\r
+               if( xLastRead == pdTRUE )\r
+               {\r
+                       /* As the next FF_Read() will go passed the current block, invalidate the buffer now. */\r
+                       pxFile->ucState = FF_BUFSTATE_INVALID;\r
+               }\r
+       }\r
+       #else\r
+       {\r
+       FF_Buffer_t *pxBuffer;\r
+               /* Reading in the standard way, using FF_Buffer_t. */\r
+               pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_READ );\r
+               if( pxBuffer == NULL )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_READ );\r
+                       ulBytesRead = 0ul;\r
+               }\r
+               else\r
+               {\r
+                       memcpy( pucBuffer, pxBuffer->pucBuffer + ulRelBlockPos, ulCount );\r
+                       /* Releasing a buffer in FF_MODE_READ mode will not lead to an error,\r
+                       because no disk access is needed. */\r
+                       xError = FF_ReleaseBuffer( pxFile->pxIOManager, pxBuffer );\r
+                       pxFile->ulFilePointer += ulCount;\r
+                       ulBytesRead = ulCount;\r
+               }\r
+       }\r
+       #endif\r
+\r
+       *pxError = xError;\r
+\r
+       return ulBytesRead;\r
+}      /* FF_ReadPartial() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Equivalent to fread()\r
+ *\r
+ *     @param  pxFile                  FF_FILE object that was created by FF_Open().\r
+ *     @param  ulElementSize   The size of an element to read.\r
+ *     @param  ulCount                 The number of elements to read.\r
+ *     @param  buffer                  A pointer to a buffer of adequate size to be filled with the requested data.\r
+ *\r
+ *     @return Number of bytes read.\r
+ *\r
+ * FF_Read() and FF_Write() work very similar. They both complete their task in 5 steps:\r
+ *     1. Read bytes up to a sector border:  FF_ReadPartial()\r
+ *     2. Read sectors up to cluster border: FF_BlockRead()\r
+ *     3. Read complete clusters:            FF_ReadClusters()\r
+ *     4. Read remaining sectors:            FF_BlockRead()\r
+ *     5. Read remaining bytes:              FF_ReadPartial()\r
+ **/\r
+int32_t FF_Read( FF_FILE *pxFile, uint32_t ulElementSize, uint32_t ulCount, uint8_t *pucBuffer )\r
+{\r
+uint32_t ulBytesLeft = ulElementSize * ulCount;\r
+uint32_t ulBytesRead = 0;\r
+uint32_t ulBytesToRead;\r
+FF_IOManager_t *pxIOManager;\r
+uint32_t ulRelBlockPos;\r
+uint32_t ulItemLBA;\r
+int32_t lResult;\r
+uint32_t ulSectors;\r
+uint32_t ulRelClusterPos;\r
+uint32_t ulBytesPerCluster;\r
+FF_Error_t xError;\r
+\r
+       if( pxFile == NULL )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_READ );\r
+       }\r
+       else\r
+       {\r
+               /* Check validity of the handle and the current position within the file. */\r
+               xError = FF_CheckValid( pxFile );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )\r
+                       {\r
+                               /* File was not opened with READ mode access. */\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_READ );\r
+                       }\r
+                       else if( pxFile->ulFilePointer >= pxFile->ulFileSize )\r
+                       {\r
+                               /* The end-of-file is reached.  The error READ_ZERO will not be\r
+                               returned, it is just used to avoid further processing. */\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_READ_ZERO | FF_READ );\r
+                       }\r
+                       else if( ( pxFile->ulFilePointer + ulBytesLeft ) > pxFile->ulFileSize )\r
+                       {\r
+                               /* Note that many bytes can be read. */\r
+                               ulBytesLeft = pxFile->ulFileSize - pxFile->ulFilePointer;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       /* The file handle is not valid. */\r
+               }\r
+       }       /* else pxFile != NULL */\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               pxIOManager = pxFile->pxIOManager;\r
+\r
+               /* And calculate the Logical Block Address. */\r
+               ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+\r
+               /* Get the position within a block. */\r
+               ulRelBlockPos = FF_getMinorBlockEntry( pxIOManager, pxFile->ulFilePointer, 1 );\r
+               /* Open a do {} while( 0 ) loop to allow easy breaks: */\r
+               do\r
+               {\r
+                       if( ( ulRelBlockPos + ulBytesLeft ) <= ( uint32_t ) pxIOManager->usSectorSize )\r
+                       {\r
+                               /*---------- A small read within the current block only. */\r
+                               ulBytesRead = FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesLeft, pucBuffer, &xError );\r
+                               break;\r
+                       }\r
+                       /*---------- Read (memcpy) to a Sector Boundary. */\r
+                       if( ulRelBlockPos != 0 )\r
+                       {\r
+                               /* Not on a sector boundary, at this point the LBA is known. */\r
+                               ulBytesToRead = pxIOManager->usSectorSize - ulRelBlockPos;\r
+                               ulBytesRead = FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesToRead, pucBuffer, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulBytesLeft -= ulBytesRead;\r
+                               pucBuffer += ulBytesRead;\r
+                       }\r
+\r
+                       /*---------- Read sectors, up to a Cluster Boundary. */\r
+                       ulBytesPerCluster = ( pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->usSectorSize );\r
+                       ulRelClusterPos = pxFile->ulFilePointer % ( ulBytesPerCluster * pxIOManager->xPartition.ucBlkFactor );\r
+\r
+                       if( ( ulRelClusterPos != 0 ) && ( ( ulRelClusterPos + ulBytesLeft ) >= ulBytesPerCluster ) )\r
+                       {\r
+                               /* Need to get to cluster boundary. */\r
+                               ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulSectors = pxIOManager->xPartition.ulSectorsPerCluster - ( ulRelClusterPos / pxIOManager->usSectorSize );\r
+                               xError = FF_BlockRead( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulBytesToRead = ulSectors * pxIOManager->usSectorSize;\r
+                               ulBytesLeft -= ulBytesToRead;\r
+                               pucBuffer += ulBytesToRead;\r
+                               ulBytesRead += ulBytesToRead;\r
+                               pxFile->ulFilePointer += ulBytesToRead;\r
+                       }\r
+\r
+                       /*---------- Read entire clusters. */\r
+                       if( ulBytesLeft >= ulBytesPerCluster )\r
+                       {\r
+                       uint32_t ulClusters;\r
+\r
+                       FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               ulClusters = ulBytesLeft / ulBytesPerCluster;\r
+\r
+                               xError = FF_ReadClusters( pxFile, ulClusters, pucBuffer );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulBytesToRead = ulBytesPerCluster * ulClusters;\r
+                               pxFile->ulFilePointer += ulBytesToRead;\r
+                               ulBytesLeft -= ulBytesToRead;\r
+                               pucBuffer += ulBytesToRead;\r
+                               ulBytesRead += ulBytesToRead;\r
+                       }\r
+\r
+                       /*---------- Read Remaining Blocks. */\r
+                       while( ulBytesLeft >= ( uint32_t ) pxIOManager->usSectorSize )\r
+                       {\r
+                               ulSectors = ulBytesLeft / pxIOManager->usSectorSize;\r
+                               {\r
+                                       /* HT: I'd leave these pPart/ulOffset for readability */\r
+                                       /* and shorter code lines */\r
+                                       FF_Partition_t *pPart = &( pxIOManager->xPartition );\r
+                                       uint32_t ulOffset = ( pxFile->ulFilePointer / pxIOManager->usSectorSize ) % pPart->ulSectorsPerCluster;\r
+                                       uint32_t ulRemain = pPart->ulSectorsPerCluster - ulOffset;\r
+                                       if( ulSectors > ulRemain )\r
+                                       {\r
+                                               ulSectors = ulRemain;\r
+                                       }\r
+                               }\r
+\r
+                               ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               xError = FF_BlockRead( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );\r
+\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+                               ulBytesToRead = ulSectors * pxIOManager->usSectorSize;\r
+                               pxFile->ulFilePointer += ulBytesToRead;\r
+                               ulBytesLeft -= ulBytesToRead;\r
+                               pucBuffer += ulBytesToRead;\r
+                               ulBytesRead += ulBytesToRead;\r
+                       }\r
+\r
+                       /*---------- Read (memcpy) Remaining Bytes */\r
+                       if( ulBytesLeft == 0 )\r
+                       {\r
+                               break;\r
+                       }\r
+                       ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               break;\r
+                       }\r
+                       /* Bytes to read are within a block and less than a block size. */\r
+                       FF_ReadPartial( pxFile, ulItemLBA, 0, ulBytesLeft, pucBuffer, &xError );\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               ulBytesRead += ulBytesLeft;\r
+                       }\r
+               }\r
+               while( pdFALSE );\r
+       } /* if( FF_isERR( xError ) == pdFALSE ) */\r
+\r
+       if( FF_GETERROR( xError ) == FF_ERR_FILE_READ_ZERO )\r
+       {\r
+               lResult = 0;\r
+       }\r
+       else if( FF_isERR( xError ) )\r
+       {\r
+               lResult = xError;\r
+       }\r
+       else\r
+       {\r
+               lResult = ( int32_t )( ulBytesRead / ulElementSize );\r
+       }\r
+\r
+       return lResult;\r
+}      /* FF_Read() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Equivalent to fgetc()\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*\r
+*      @return The character that was read (cast as a 32-bit interger). -1 on EOF.\r
+*      @return FF_Error_t code. (Check with if(FF_isERR(xRetVal)) {}).\r
+*      @return -1 EOF (end of file).\r
+*\r
+**/\r
+int32_t FF_GetC( FF_FILE *pxFile )\r
+{\r
+uint32_t ulItemLBA;\r
+uint8_t ucReturnedChar;\r
+uint32_t ulRelBlockPos;\r
+FF_Error_t xResult;\r
+\r
+       if( pxFile == NULL )\r
+       {\r
+               xResult = FF_ERR_NULL_POINTER | FF_GETC;        /* Ensure this is a signed error. */\r
+       }\r
+       else if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )\r
+       {\r
+               xResult = FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_GETC;\r
+       }\r
+       else if( pxFile->ulFilePointer >= pxFile->ulFileSize )\r
+       {\r
+               /* The end-of-file is reached.  The error READ_ZERO will not be\r
+               returned, it is just used to avoid further processing. */\r
+               xResult = FF_ERR_FILE_READ_ZERO | FF_READ;\r
+       }\r
+       else\r
+       {\r
+               ulRelBlockPos = FF_getMinorBlockEntry( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );\r
+\r
+               ulItemLBA = FF_SetCluster( pxFile, &xResult );\r
+               if( FF_isERR( xResult ) == pdFALSE )\r
+               {\r
+                       FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, 1, &ucReturnedChar, &xResult );\r
+                       if( FF_isERR( xResult ) == pdFALSE )\r
+                       {\r
+                               xResult = ( int32_t ) ( ( uint32_t ) ucReturnedChar );\r
+                       }\r
+               }\r
+       }\r
+\r
+       return ( int32_t ) xResult;\r
+}      /* FF_GetC() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+* @public\r
+* @brief       Gets a Line from a Text File, but no more than ulLimit characters. The line will be NULL terminated.\r
+*\r
+*                      The behaviour of this function is undefined when called on a binary file.\r
+*                      It should just read in ulLimit bytes of binary, and ZERO terminate the line.\r
+*\r
+*                      This function works for both UNIX line feeds, and Windows CRLF type files.\r
+*\r
+* @param       pxFile  The FF_FILE object pointer.\r
+* @param       szLine  The character buffer where the line should be stored.\r
+* @param       ulLimit This should be the max number of characters that szLine can hold.\r
+*\r
+* @return      The number of characters read from the line, on success.\r
+* @return      0 when no more lines are available, or when ulLimit is 0.\r
+* @return      FF_ERR_NULL_POINTER if pxFile or szLine are NULL;\r
+*\r
+**/\r
+int32_t FF_GetLine( FF_FILE *pxFile, char *pcLine, uint32_t ulLimit )\r
+{\r
+int32_t iChar = 0;\r
+BaseType_t xIndex;\r
+FF_Error_t xResult = FF_ERR_NONE;\r
+\r
+       if( ( pxFile == NULL ) || ( pcLine == NULL ) )\r
+       {\r
+               xResult = FF_ERR_NULL_POINTER | FF_GETLINE;\r
+       }\r
+       else\r
+       {\r
+               for( xIndex = 0; xIndex < ( BaseType_t ) ( ulLimit - 1 ); ++xIndex )\r
+               {\r
+                       iChar = FF_GetC( pxFile );\r
+\r
+                       if( FF_isERR( iChar ) == pdFALSE )\r
+                       {\r
+                               pcLine[ xIndex ] = ( char ) iChar;\r
+\r
+                               if( iChar == '\n' )\r
+                               {\r
+                                       /* Read until the first linefeed.  Move xIndex forward so the\r
+                                       null terminator does not overwrite the \n.  xIndex must be less\r
+                                       thank ( ulLimit - 1 ), so incrementing it here cannot make it\r
+                                       greater than ulLimit - 1, so the NULL can be inserted without\r
+                                       overflowing the buffer. */\r
+                                       xIndex++;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if( ( FF_GETERROR( iChar ) == FF_ERR_FILE_READ_ZERO ) && ( xIndex > 0 ) )\r
+                               {\r
+                                       /* Although FF_GetC() returns an End Of File,\r
+                                       the last few characters will be returned first. */\r
+                                       iChar = xIndex;\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               /* Make sure that the resulting string always ends with a zero: */\r
+               pcLine[ xIndex ] = '\0';\r
+\r
+               /*_RB_ In some paths this will be the second time FF_isERR() is called\r
+               on the same value. */\r
+               if( FF_isERR( iChar ) == pdFALSE )\r
+               {\r
+                       /* Return the number of bytes read. */\r
+                       xResult = xIndex;\r
+               }\r
+               else\r
+               {\r
+                       /* Return iChar as an error code (see FF_GetC()). */\r
+                       xResult = iChar;\r
+               }\r
+       }\r
+\r
+       return xResult;\r
+}      /* FF_GetLine() */\r
+/*-----------------------------------------------------------*/\r
+\r
+static int32_t FF_WritePartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,\r
+       const uint8_t *pucBuffer, FF_Error_t *pxError )\r
+{\r
+FF_Error_t xError;\r
+uint32_t ulBytesWritten;\r
+\r
+       #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+       {\r
+               BaseType_t xLastRead;\r
+               if( ( ulRelBlockPos + ulCount ) >= ( uint32_t ) pxFile->pxIOManager->usSectorSize )\r
+               {\r
+                       /* After this read, ulFilePointer will point to the next block/sector. */\r
+                       xLastRead = pdTRUE;\r
+               }\r
+               else\r
+               {\r
+                       /* It is not the last read within this block/sector. */\r
+                       xLastRead = pdFALSE;\r
+               }\r
+\r
+               if( ( ( pxFile->ucState & FF_BUFSTATE_VALID ) == 0 ) &&\r
+                       ( ( ulRelBlockPos != 0 ) || ( pxFile->ulFilePointer < pxFile->ulFileSize ) ) )\r
+               {\r
+                       xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );\r
+                       /* pxFile->ucState will be set later on. */\r
+               }\r
+               else\r
+               {\r
+                       xError = FF_ERR_NONE;\r
+                       /* the buffer is valid or a whole block/sector will be written, so it is\r
+                       not necessary to read the contents first. */\r
+               }\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       memcpy( pxFile->pucBuffer + ulRelBlockPos, pucBuffer, ulCount );\r
+                       if( xLastRead == pdTRUE )\r
+                       {\r
+                               xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );\r
+                               pxFile->ucState = FF_BUFSTATE_INVALID;\r
+                       }\r
+                       else\r
+                       {\r
+                               pxFile->ucState |= FF_BUFSTATE_WRITTEN | FF_BUFSTATE_VALID;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       pxFile->ucState = FF_BUFSTATE_INVALID;\r
+               }\r
+       }\r
+       #else\r
+       {\r
+       FF_Buffer_t *pxBuffer;\r
+               if( ( ulRelBlockPos == 0 ) && ( pxFile->ulFilePointer >= pxFile->ulFileSize ) )\r
+               {\r
+                       /* An entire sector will be written. */\r
+                       pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_WR_ONLY );\r
+               }\r
+               else\r
+               {\r
+                       /* A partial write will be done, make sure to read the contents before\r
+                       changing anything. */\r
+                       pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_WRITE );\r
+               }\r
+\r
+               if( pxBuffer == NULL )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_WRITE );\r
+               }\r
+               else\r
+               {\r
+                       /* Here we copy to the sector boundary. */\r
+                       memcpy( ( pxBuffer->pucBuffer + ulRelBlockPos ), pucBuffer, ulCount );\r
+\r
+                       xError = FF_ReleaseBuffer( pxFile->pxIOManager, pxBuffer );\r
+               }\r
+       }\r
+       #endif\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               pxFile->ulFilePointer += ulCount;\r
+               ulBytesWritten = ulCount;\r
+\r
+               if( pxFile->ulFilePointer > pxFile->ulFileSize )\r
+               {\r
+                       pxFile->ulFileSize = pxFile->ulFilePointer;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               ulBytesWritten = 0ul;\r
+       }\r
+       *pxError = xError;\r
+\r
+       return ulBytesWritten;\r
+}      /* FF_WritePartial() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ *     @public\r
+ *     @brief  Writes data to a File.\r
+ *\r
+ *     @param  pxFile                  FILE Pointer.\r
+ *     @param  ulElementSize           Size of an Element of Data to be copied. (in bytes).\r
+ *     @param  ulCount                 Number of Elements of Data to be copied. (ulElementSize * ulCount must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best.\r
+ *     @param  pucBuffer                       Byte-wise pucBuffer containing the data to be written.\r
+ *\r
+ * FF_Read() and FF_Write() work very similar. They both complete their task in 5 steps:\r
+ *     1. Write bytes up to a sector border:  FF_WritePartial()\r
+ *     2. Write sectors up to cluster border: FF_BlockWrite()\r
+ *     3. Write complete clusters:            FF_WriteClusters()\r
+ *     4. Write remaining sectors:            FF_BlockWrite()\r
+ *     5. Write remaining bytes:              FF_WritePartial()\r
+ *     @return\r
+**/\r
+int32_t FF_Write( FF_FILE *pxFile, uint32_t ulElementSize, uint32_t ulCount, uint8_t *pucBuffer )\r
+{\r
+uint32_t ulBytesLeft = ulElementSize * ulCount;\r
+uint32_t nBytesWritten = 0;\r
+uint32_t nBytesToWrite;\r
+FF_IOManager_t *pxIOManager;\r
+uint32_t ulRelBlockPos;\r
+uint32_t ulItemLBA;\r
+int32_t lResult;\r
+uint32_t ulSectors;\r
+uint32_t ulRelClusterPos;\r
+uint32_t ulBytesPerCluster;\r
+FF_Error_t xError;\r
+\r
+       if( pxFile == NULL )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_READ );\r
+       }\r
+       else\r
+       {\r
+               /* Check validity of the handle and the current position within the file. */\r
+               xError = FF_CheckValid( pxFile );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( ( pxFile->ucMode & FF_MODE_WRITE ) == 0 )\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_WRITE );\r
+                       }\r
+                       /* Make sure a write is after the append point. */\r
+                       else if( ( pxFile->ucMode & FF_MODE_APPEND ) != 0 )\r
+                       {\r
+                               if( pxFile->ulFilePointer < pxFile->ulFileSize )\r
+                               {\r
+                                       xError = FF_Seek( pxFile, 0, FF_SEEK_END );\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               pxIOManager = pxFile->pxIOManager;\r
+\r
+               /* Open a do{} while( 0 ) loop to allow the use of breaks */\r
+               do\r
+               {\r
+                       /* Extend File for at least ulBytesLeft!\r
+                       Handle file-space allocation\r
+                       + 1 byte because the code assumes there is always a next cluster */\r
+                       xError = FF_ExtendFile( pxFile, pxFile->ulFilePointer + ulBytesLeft + 1 );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               /* On every error, break from the while( 0 ) loop. */\r
+                               break;\r
+                       }\r
+\r
+                       ulRelBlockPos = FF_getMinorBlockEntry( pxIOManager, pxFile->ulFilePointer, 1 ); /* Get the position within a block. */\r
+                       ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               break;\r
+                       }\r
+\r
+                       if( ( ulRelBlockPos + ulBytesLeft ) <= ( uint32_t ) pxIOManager->usSectorSize )\r
+                       {\r
+                               /* Bytes to write are within a block and and do not go passed the current block. */\r
+                               nBytesWritten = FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesLeft, pucBuffer, &xError );\r
+                               break;\r
+                       }\r
+\r
+                       /*---------- Write (memcpy) to a Sector Boundary. */\r
+                       if( ulRelBlockPos != 0 )\r
+                       {\r
+                               /* Not writing on a sector boundary, at this point the LBA is known. */\r
+                               nBytesToWrite = pxIOManager->usSectorSize - ulRelBlockPos;\r
+                               nBytesWritten = FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, nBytesToWrite, pucBuffer, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               ulBytesLeft -= nBytesWritten;\r
+                               pucBuffer += nBytesWritten;\r
+                       }\r
+\r
+                       /*---------- Write sectors, up to a Cluster Boundary. */\r
+                       ulBytesPerCluster = ( pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->usSectorSize );\r
+                       ulRelClusterPos = FF_getClusterPosition( pxIOManager, pxFile->ulFilePointer, 1 );\r
+\r
+                       if( ( ulRelClusterPos != 0 ) && ( ( ulRelClusterPos + ulBytesLeft ) >= ulBytesPerCluster ) )\r
+                       {\r
+                               /* Need to get to cluster boundary */\r
+                               ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               ulSectors = pxIOManager->xPartition.ulSectorsPerCluster - ( ulRelClusterPos / pxIOManager->usSectorSize );\r
+                               xError = FF_BlockWrite( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               nBytesToWrite = ulSectors * pxIOManager->usSectorSize;\r
+                               ulBytesLeft -= nBytesToWrite;\r
+                               pucBuffer += nBytesToWrite;\r
+                               nBytesWritten += nBytesToWrite;\r
+                               pxFile->ulFilePointer += nBytesToWrite;\r
+                               if( pxFile->ulFilePointer > pxFile->ulFileSize )\r
+                               {\r
+                                       pxFile->ulFileSize = pxFile->ulFilePointer;\r
+                               }\r
+                       }\r
+\r
+                       /*---------- Write entire Clusters. */\r
+                       if( ulBytesLeft >= ulBytesPerCluster )\r
+                       {\r
+                       uint32_t ulClusters;\r
+\r
+                               FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               ulClusters = ( ulBytesLeft / ulBytesPerCluster );\r
+\r
+                               xError = FF_WriteClusters( pxFile, ulClusters, pucBuffer );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               nBytesToWrite = ulBytesPerCluster * ulClusters;\r
+                               ulBytesLeft -= nBytesToWrite;\r
+                               pucBuffer += nBytesToWrite;\r
+                               nBytesWritten += nBytesToWrite;\r
+                               pxFile->ulFilePointer += nBytesToWrite;\r
+                               if( pxFile->ulFilePointer > pxFile->ulFileSize )\r
+                               {\r
+                                       pxFile->ulFileSize = pxFile->ulFilePointer;\r
+                               }\r
+                       }\r
+\r
+                       /*---------- Write Remaining Blocks */\r
+                       while( ulBytesLeft >= ( uint32_t ) pxIOManager->usSectorSize )\r
+                       {\r
+                               ulSectors = ulBytesLeft / pxIOManager->usSectorSize;\r
+                               {\r
+                                       /* HT: I'd leave these pPart/ulOffset for readability... */\r
+                                       FF_Partition_t  *pPart = &( pxIOManager->xPartition );\r
+                                       uint32_t ulOffset = ( pxFile->ulFilePointer / pxIOManager->usSectorSize ) % pPart->ulSectorsPerCluster;\r
+                                       uint32_t ulRemain = pPart->ulSectorsPerCluster - ulOffset;\r
+                                       if( ulSectors > ulRemain )\r
+                                       {\r
+                                               ulSectors = ulRemain;\r
+                                       }\r
+                               }\r
+\r
+                               ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               xError = FF_BlockWrite( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );\r
+                               if( FF_isERR( xError ) )\r
+                               {\r
+                                       break;\r
+                               }\r
+\r
+                               nBytesToWrite = ulSectors * pxIOManager->usSectorSize;\r
+                               ulBytesLeft -= nBytesToWrite;\r
+                               pucBuffer += nBytesToWrite;\r
+                               nBytesWritten += nBytesToWrite;\r
+                               pxFile->ulFilePointer += nBytesToWrite;\r
+                               if( pxFile->ulFilePointer > pxFile->ulFileSize )\r
+                               {\r
+                                       pxFile->ulFileSize = pxFile->ulFilePointer;\r
+                               }\r
+                       }\r
+\r
+                       /*---------- Write (memcpy) Remaining Bytes */\r
+                       if( ulBytesLeft == 0 )\r
+                       {\r
+                               break;\r
+                       }\r
+\r
+                       ulItemLBA = FF_SetCluster( pxFile, &xError );\r
+                       if( FF_isERR( xError ) )\r
+                       {\r
+                               break;\r
+                       }\r
+                       FF_WritePartial( pxFile, ulItemLBA, 0, ulBytesLeft, pucBuffer, &xError );\r
+                       nBytesWritten += ulBytesLeft;\r
+               }\r
+               while( pdFALSE );\r
+       }\r
+\r
+       if( FF_isERR( xError ) )\r
+       {\r
+               lResult = xError;\r
+       }\r
+       else\r
+       {\r
+               lResult = ( int32_t )( nBytesWritten / ulElementSize );\r
+       }\r
+\r
+       return lResult;\r
+}      /* FF_Write() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Writes a char to a FILE.\r
+*\r
+*      @param  pxFile          FILE Pointer.\r
+*      @param  ucValue Char to be placed in the file.\r
+*\r
+*      @return Returns the value written to the file, or a value less than 0.\r
+*\r
+**/\r
+int32_t FF_PutC( FF_FILE *pxFile, uint8_t ucValue )\r
+{\r
+uint32_t ulItemLBA;\r
+uint32_t ulRelBlockPos;\r
+FF_Error_t xResult;\r
+\r
+       if( pxFile == NULL )\r
+       {       /* Ensure we don't have a Null file pointer on a Public interface. */\r
+               xResult = FF_ERR_NULL_POINTER | FF_PUTC;\r
+       }\r
+       else if( ( pxFile->ucMode & FF_MODE_WRITE ) == 0 )\r
+       {\r
+               xResult = FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_PUTC;\r
+       }\r
+       else\r
+       {\r
+               xResult = FF_ERR_NONE;\r
+               do\r
+               {\r
+                       /* Make sure a write is after the append point. */\r
+                       if( ( pxFile->ucMode & FF_MODE_APPEND ) != 0 )\r
+                       {\r
+                               if( pxFile->ulFilePointer < pxFile->ulFileSize )\r
+                               {\r
+                                       xResult = FF_Seek( pxFile, 0, FF_SEEK_END );\r
+                                       if( FF_isERR( xResult ) )\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       ulRelBlockPos = FF_getMinorBlockEntry( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );\r
+\r
+                       /* Handle File Space Allocation. */\r
+                       /* We'll write 1 byte and always have a next cluster reserved. */\r
+                       xResult = FF_ExtendFile( pxFile, pxFile->ulFilePointer + 2 );\r
+                       if( FF_isERR( xResult ) )\r
+                       {\r
+                               break;\r
+                       }\r
+\r
+                       ulItemLBA = FF_SetCluster( pxFile, &xResult );\r
+                       if( FF_isERR( xResult ) )\r
+                       {\r
+                               break;\r
+                       }\r
+                       FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, 1, &ucValue, &xResult );\r
+\r
+                       if( FF_isERR( xResult ) == pdFALSE )\r
+                       {\r
+                               xResult = ( FF_Error_t ) ucValue;\r
+                       }\r
+\r
+               } while( pdFALSE );\r
+       }\r
+\r
+       return xResult;\r
+}      /* FF_PutC() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Equivalent to fseek()\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*      @param  ulOffset        An integer (+/-) to seek to, from the specified origin.\r
+*      @param  xOrigin         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).\r
+*\r
+*      @return 0 on Sucess,\r
+*      @return -2 if offset results in an invalid position in the file.\r
+*      @return FF_ERR_NULL_POINTER if a FF_FILE pointer was not received.\r
+*      @return -3 if an invalid origin was provided.\r
+*\r
+**/\r
+FF_Error_t FF_Seek( FF_FILE *pxFile, int32_t lOffset, BaseType_t xOrigin )\r
+{\r
+FF_Error_t xError;\r
+uint32_t ulPosition = 0ul;\r
+\r
+       xError = FF_CheckValid( pxFile );\r
+\r
+       if( FF_isERR( xError ) == pdFALSE )\r
+       {\r
+               xError = FF_FlushCache( pxFile->pxIOManager );\r
+\r
+               #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+               {\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               /* Here we must ensure that if the user tries to seek, and we had data in the file's\r
+                               write buffer that this is written to disk. */\r
+                               if( ( pxFile->ucState & FF_BUFSTATE_WRITTEN ) != 0 )\r
+                               {\r
+                                       xError = FF_BlockWrite( pxFile->pxIOManager, FF_FileLBA( pxFile ), 1, pxFile->pucBuffer, pdFALSE );\r
+                               }\r
+                               pxFile->ucState = FF_BUFSTATE_INVALID;\r
+                       }\r
+               }\r
+               #endif  /* ffconfigOPTIMISE_UNALIGNED_ACCESS */\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( xOrigin == FF_SEEK_SET )\r
+                       {\r
+                               ulPosition = ( uint32_t )lOffset;\r
+                       }\r
+                       else if( xOrigin == FF_SEEK_CUR )\r
+                       {\r
+                               if( lOffset >= ( int32_t ) 0 )\r
+                               {\r
+                                       ulPosition = pxFile->ulFilePointer + ( ( uint32_t ) lOffset );\r
+                               }\r
+                               else\r
+                               {\r
+                                       ulPosition = pxFile->ulFilePointer - ( ( uint32_t ) ( -lOffset ) );\r
+                               }\r
+                       }\r
+                       else if( xOrigin == FF_SEEK_END )\r
+                       {\r
+                               /* 'FF_SEEK_END' only allows zero or negative values. */\r
+                               if( lOffset <= ( int32_t ) 0 )\r
+                               {\r
+                                       ulPosition = pxFile->ulFileSize - ( ( uint32_t ) ( -lOffset ) );\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_SEEK | FF_ERR_FILE_SEEK_INVALID_ORIGIN );\r
+                               /* To supress a compiler warning. */\r
+                               ulPosition = ( uint32_t ) 0u;\r
+                       }\r
+\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               if( ulPosition <= ( uint32_t ) pxFile->ulFileSize )\r
+                               {\r
+                                       if( ulPosition != ( uint32_t ) pxFile->ulFilePointer )\r
+                                       {\r
+                                               pxFile->ulFilePointer = ulPosition;\r
+                                               FF_SetCluster( pxFile, &xError );\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       xError = ( FF_Error_t ) ( FF_SEEK | FF_ERR_FILE_SEEK_INVALID_POSITION );\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_Seek() */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigREMOVABLE_MEDIA != 0 )\r
+       /**\r
+       *       @public\r
+       *       @brief  Invalidate all file handles belonging to pxIOManager\r
+       *\r
+       *       @param  pIoMan          FF_IOManager_t object that was created by FF_CreateIOManger().\r
+       *\r
+       *       @return 0 if no handles were open\r
+       *       @return >0 the amount of handles that were invalidated\r
+       *       @return <0 probably an invalid FF_IOManager_t pointer\r
+       *\r
+       **/\r
+       int32_t FF_Invalidate( FF_IOManager_t *pxIOManager )\r
+       {\r
+       int32_t xResult;\r
+       FF_FILE *pxFileChain;\r
+\r
+               if( pxIOManager == NULL )\r
+               {\r
+                       xResult = FF_ERR_NULL_POINTER | FF_INVALIDATE;\r
+               }\r
+               else\r
+               {\r
+                       xResult = 0;\r
+                       FF_PendSemaphore( pxIOManager->pvSemaphore );\r
+                       {\r
+                               pxIOManager->ucFlags |= FF_IOMAN_DEVICE_IS_EXTRACTED;\r
+                               /* Semaphore is required, or linked list might change */\r
+                               pxFileChain = ( FF_FILE * ) pxIOManager->FirstFile;\r
+                               if( pxFileChain != NULL )\r
+                               {\r
+                                       /* Count elements in FirstFile */\r
+                                       do\r
+                                       {\r
+                                               pxFileChain->ulValidFlags |= FF_VALID_FLAG_INVALID;\r
+                                               xResult++;\r
+                                               pxFileChain = pxFileChain->pxNext;\r
+                                       }\r
+                                       while( pxFileChain != NULL );\r
+                               }\r
+                       }\r
+\r
+                       FF_ReleaseSemaphore( pxIOManager->pvSemaphore );\r
+               }\r
+\r
+               return xResult;\r
+       }       /* FF_Invalidate() */\r
+#endif /* ffconfigREMOVABLE_MEDIA */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Check validity of file handle\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*\r
+*      @return 0 on sucess.\r
+*      @return FF_ERR_NULL_POINTER       if a null pointer was provided.\r
+*      @return FF_ERR_FILE_BAD_HANDLE    if handle is not recognized\r
+*      @return FF_ERR_FILE_MEDIA_REMOVED please call FF_Close\r
+*\r
+**/\r
+FF_Error_t FF_CheckValid( FF_FILE *pxFile )\r
+{\r
+       FF_FILE         *pxFileChain;\r
+       FF_Error_t      xError;\r
+\r
+       if( ( pxFile == NULL ) || ( pxFile->pxIOManager == NULL ) )\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_CHECKVALID );\r
+       }\r
+       else\r
+       {\r
+               FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+               {\r
+                       pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_BAD_HANDLE | FF_CHECKVALID );\r
+                       while( pxFileChain != NULL )\r
+                       {\r
+                               if( pxFileChain == pxFile )\r
+                               {\r
+                               #if( ffconfigREMOVABLE_MEDIA != 0 )\r
+                                       if( ( pxFileChain->ulValidFlags & FF_VALID_FLAG_INVALID ) != 0 )\r
+                                       {\r
+                                               /* The medium has been removed while this file handle was open. */\r
+                                               xError = ( FF_Error_t ) ( FF_ERR_FILE_MEDIA_REMOVED | FF_CHECKVALID );\r
+                                       }\r
+                                       else\r
+                               #endif\r
+                                       {\r
+                                               /* Found the handle, so it is a valid / existing handle. */\r
+                                               xError = FF_ERR_NONE;\r
+                                       }\r
+\r
+                                       break;\r
+                               }\r
+\r
+                               pxFileChain = pxFileChain->pxNext;\r
+                       }\r
+               }\r
+               FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_CheckValid() */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigTIME_SUPPORT != 0 )\r
+       /**\r
+       *       @public\r
+       *       @brief  Set the time-stamp(s) of a file entry\r
+       *\r
+       *       @param  pxFile          FF_FILE object that was created by FF_Open().\r
+       *       @param  pxTime      FF_SystemTime_t the time stamp\r
+       *       @param  uxWhat       UBaseType_t a combination of enum ETimeMask\r
+       *\r
+       *       @return 0 or FF_Error_t\r
+       *\r
+       **/\r
+       FF_Error_t FF_SetFileTime( FF_FILE *pxFile, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )\r
+       {\r
+       FF_DirEnt_t     xOriginalEntry;\r
+       FF_Error_t      xError;\r
+\r
+               xError = FF_CheckValid( pxFile );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED )\r
+                       {       /*if (pxFile->FileDeleted) */\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETFILETIME );\r
+                       }\r
+                       else if( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) == 0 )\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_SETFILETIME );\r
+                       }\r
+                       else\r
+                       {\r
+                               /* Update the Dirent! */\r
+                               xError = FF_GetEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       if( uxWhat & ETimeCreate )\r
+                                       {\r
+                                               xOriginalEntry.xCreateTime = *pxTime;           /*/< Date and Time Created. */\r
+                                       }\r
+\r
+                                       if( uxWhat & ETimeMod )\r
+                                       {\r
+                                               xOriginalEntry.xModifiedTime = *pxTime; /*/< Date and Time Modified. */\r
+                                       }\r
+\r
+                                       if( uxWhat & ETimeAccess )\r
+                                       {\r
+                                               xOriginalEntry.xAccessedTime = *pxTime; /*/< Date of Last Access. */\r
+                                       }\r
+\r
+                                       xError = FF_PutEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );\r
+                               }\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = FF_FlushCache( pxFile->pxIOManager );          /* Ensure all modfied blocks are flushed to disk! */\r
+                               }\r
+                       }\r
+               }\r
+\r
+               return xError;\r
+       }       /* FF_SetFileTime() */\r
+#endif /* ffconfigTIME_SUPPORT */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigTIME_SUPPORT != 0 )\r
+       /**\r
+       *       @public\r
+       *       @brief  Set the time-stamp(s) of a file entry (by name)\r
+       *\r
+       *       @param  pxIOManager             FF_IOManager_t device handle\r
+       *       @param  pcPath          int8_t/FF_T_WCHAR name of the file\r
+       *       @param  pxTime       FF_SystemTime_t the time stamp\r
+       *       @param  uxWhat       UBaseType_t a combination of enum ETimeMask\r
+       *\r
+       *       @return 0 or FF_Error_t\r
+       *\r
+       **/\r
+       #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+       FF_Error_t FF_SetTime ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )\r
+       #else\r
+       FF_Error_t FF_SetTime ( FF_IOManager_t * pxIOManager, const char *pcPath, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )\r
+       #endif  /* ffconfigUNICODE_UTF16_SUPPORT */\r
+       {\r
+       FF_DirEnt_t xOriginalEntry;\r
+       FF_Error_t xError;\r
+       uint32_t ulFileCluster;\r
+       BaseType_t xIndex;\r
+       FF_FindParams_t xFindParams;\r
+       #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+               FF_T_WCHAR pcFileName[ffconfigMAX_FILENAME];\r
+       #else\r
+               char pcFileName[ffconfigMAX_FILENAME];\r
+       #endif  /* ffconfigUNICODE_UTF16_SUPPORT */\r
+\r
+               xIndex = ( BaseType_t ) STRLEN( pcPath );\r
+\r
+               memset( &xFindParams, '\0', sizeof( xFindParams ) );\r
+\r
+               while( xIndex != 0 )\r
+               {\r
+                       if( pcPath[ xIndex ] == '\\' || pcPath[ xIndex ] == '/' )\r
+                       {\r
+                               break;\r
+                       }\r
+\r
+                       xIndex--;\r
+               }\r
+\r
+               STRNCPY( pcFileName, ( pcPath + xIndex + 1 ), ffconfigMAX_FILENAME );\r
+\r
+               if( xIndex == 0 )\r
+               {\r
+                       xIndex = 1;\r
+               }\r
+\r
+               xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       if( xFindParams.ulDirCluster == 0 )\r
+                       {\r
+                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );\r
+                       }\r
+                       else\r
+                       {\r
+                               ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0, &xOriginalEntry, &xError );\r
+                               if( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) )\r
+                               {\r
+                                       if( ulFileCluster == 0ul )\r
+                                       {\r
+                                               /*FF_PRINTF ("FF_SetTime: Can not find '%s'\n", pcFileName); */\r
+                                               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       /* Update the Dirent! */\r
+                       if( uxWhat & ETimeCreate )\r
+                       {\r
+                               xOriginalEntry.xCreateTime = *pxTime;                   /*/< Date and Time Created. */\r
+                       }\r
+\r
+                       if( uxWhat & ETimeMod )\r
+                       {\r
+                               xOriginalEntry.xModifiedTime = *pxTime;         /*/< Date and Time Modified. */\r
+                       }\r
+\r
+                       if( uxWhat & ETimeAccess )\r
+                       {\r
+                               xOriginalEntry.xAccessedTime = *pxTime;         /*/< Date of Last Access. */\r
+                       }\r
+\r
+                       xError = FF_PutEntry( pxIOManager, xOriginalEntry.usCurrentItem - 1, xFindParams.ulDirCluster, &xOriginalEntry, NULL );\r
+\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               xError = FF_FlushCache( pxIOManager );                  /* Ensure all modified blocks are flushed to disk! */\r
+                       }\r
+               }\r
+\r
+               return xError;\r
+       }       /* FF_SetTime() */\r
+#endif /* ffconfigTIME_SUPPORT */\r
+/*-----------------------------------------------------------*/\r
+\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+FF_Error_t FF_SetPerm( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath, UBaseType_t aPerm )\r
+#else\r
+FF_Error_t FF_SetPerm( FF_IOManager_t * pxIOManager, const char *pcPath, UBaseType_t aPerm )\r
+#endif\r
+{\r
+FF_DirEnt_t xOriginalEntry;\r
+FF_Error_t xError;\r
+uint32_t ulFileCluster;\r
+BaseType_t xIndex;\r
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
+       FF_T_WCHAR pcFileName[ffconfigMAX_FILENAME];\r
+#else\r
+       char pcFileName[ffconfigMAX_FILENAME];\r
+#endif\r
+FF_FindParams_t xFindParams;\r
+\r
+       xIndex = ( BaseType_t ) STRLEN( pcPath );\r
+\r
+       memset( &xFindParams, '\0', sizeof( xFindParams ) );\r
+\r
+       while( xIndex != 0 )\r
+       {\r
+               if( pcPath[ xIndex ] == '\\' || pcPath[ xIndex ] == '/' )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               xIndex--;\r
+       }\r
+\r
+       STRNCPY( pcFileName, ( pcPath + xIndex + 1 ), ffconfigMAX_FILENAME );\r
+\r
+       if( xIndex == 0 )\r
+       {\r
+               xIndex = 1;\r
+       }\r
+\r
+       /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */\r
+       do\r
+       {\r
+               xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );\r
+               if( xError )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               if( !xFindParams.ulDirCluster )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );\r
+                       break;\r
+               }\r
+\r
+               ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0, &xOriginalEntry, &xError );\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       break;\r
+               }\r
+\r
+               if( ulFileCluster == 0ul )\r
+               {\r
+                       /*FF_PRINTF ("FF_SetTime: Can not find '%s'\n", pcFileName); */\r
+                       xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );\r
+                       break;\r
+               }\r
+\r
+               /*      #define FF_FAT_ATTR_READONLY            0x01 */\r
+               /*      #define FF_FAT_ATTR_HIDDEN                      0x02 */\r
+               /*      #define FF_FAT_ATTR_SYSTEM                      0x04 */\r
+               /*      #define FF_FAT_ATTR_VOLID                       0x08 */\r
+               /*      #define FF_FAT_ATTR_DIR                         0x10 */\r
+               /*      #define FF_FAT_ATTR_ARCHIVE                     0x20 */\r
+               /*      #define FF_FAT_ATTR_LFN                         0x0F */\r
+       #define FF_FAT_ATTR_USER        ( ( uint8_t ) FF_FAT_ATTR_READONLY | FF_FAT_ATTR_HIDDEN | FF_FAT_ATTR_SYSTEM | FF_FAT_ATTR_ARCHIVE )\r
+               /* Update the Dirent! */\r
+               xOriginalEntry.ucAttrib &= ~FF_FAT_ATTR_USER;\r
+               xOriginalEntry.ucAttrib |= ( aPerm & FF_FAT_ATTR_USER );\r
+               xError = FF_PutEntry( pxIOManager, xOriginalEntry.usCurrentItem - 1, xFindParams.ulDirCluster, &xOriginalEntry, NULL );\r
+\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       xError = FF_FlushCache( pxIOManager );                  /* Ensure all modfied blocks are flushed to disk! */\r
+               }\r
+       }\r
+       while( pdFALSE );\r
+\r
+       return xError;\r
+}      /* FF_SetPerm() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Equivalent to fclose()\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*\r
+*      @return 0 on sucess.\r
+*      @return -1 if a null pointer was provided.\r
+*\r
+**/\r
+FF_Error_t FF_Close( FF_FILE *pxFile )\r
+{\r
+FF_FILE *pxFileChain;\r
+FF_DirEnt_t xOriginalEntry;\r
+FF_Error_t xError;\r
+\r
+       /* Opening a do {} while( 0 )  loop to allow the use of the break statement. */\r
+       do\r
+       {\r
+               if( pxFile == NULL )\r
+               {\r
+                       xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_CLOSE );\r
+                       break;\r
+               }\r
+\r
+               /* It is important to check that user doesn't supply invalid\r
+               handle or a handle invalid because of "media removed" */\r
+               xError = FF_CheckValid( pxFile );\r
+\r
+               #if( ffconfigREMOVABLE_MEDIA != 0 )\r
+               {\r
+                       if( FF_GETERROR( xError ) == FF_ERR_FILE_MEDIA_REMOVED )\r
+                       {\r
+                               FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+                               {\r
+                                       pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;\r
+                                       if( pxFileChain == pxFile )\r
+                                       {\r
+                                               pxFile->pxIOManager->FirstFile = pxFile->pxNext;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               while( pxFileChain )\r
+                                               {\r
+                                                       if( pxFileChain->pxNext == pxFile )\r
+                                                       {\r
+                                                               pxFileChain->pxNext = pxFile->pxNext;\r
+                                                               break;\r
+                                                       }\r
+\r
+                                                       pxFileChain = pxFileChain->pxNext;      /* Forgot this one */\r
+                                               }\r
+                                       }\r
+                               }                                       /* Semaphore released, linked list was shortened! */\r
+\r
+                               FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+                               #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+                               {\r
+                                       ffconfigFREE( pxFile->pucBuffer );\r
+                               }\r
+                               #endif  /* ffconfigOPTIMISE_UNALIGNED_ACCESS */\r
+                               ffconfigFREE( pxFile ); /* So at least we have freed the pointer. */\r
+                               xError = FF_ERR_NONE;\r
+                               break;\r
+                       }\r
+               }\r
+               #endif  /* ffconfigREMOVABLE_MEDIA */\r
+\r
+               if( FF_isERR( xError ) )\r
+               {\r
+                       /* FF_ERR_FILE_BAD_HANDLE or FF_ERR_NULL_POINTER */\r
+                       break;\r
+               }\r
+\r
+               /* So here we have a normal valid file handle. */\r
+\r
+               /* Sometimes FreeRTOS+FAT will leave a trailing cluster on the end of a cluster chain.\r
+               To ensure we're compliant we shall now check for this condition and truncate it. */\r
+               if( ( ( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED ) == 0 ) &&\r
+                       ( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE ) ) != 0 ) )\r
+               {\r
+               uint32_t ulClusterSize;\r
+\r
+                       /* File is not deleted and it was opened for writing or updating */\r
+                       ulClusterSize = pxFile->pxIOManager->xPartition.usBlkSize * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;\r
+\r
+                       if( ( ( pxFile->ulFileSize % ulClusterSize ) == 0 ) && ( pxFile->ulObjectCluster != 0ul ) )\r
+                       {\r
+                               /* The file's length is a multiple of cluster size.  This means\r
+                               that an extra cluster has been reserved, which wasn't necessary. */\r
+                               xError = FF_Truncate( pxFile, pdTRUE );\r
+                       }\r
+\r
+                       /* Get the directory entry and update it to show the new file size */\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                               xError = FF_GetEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );\r
+\r
+                               /* Now update the directory entry */\r
+                               if( ( FF_isERR( xError ) == pdFALSE ) &&\r
+                                       ( ( pxFile->ulFileSize != xOriginalEntry.ulFileSize ) || ( pxFile->ulFileSize == 0UL ) ) )\r
+                               {\r
+                                       if( pxFile->ulFileSize == 0UL )\r
+                                       {\r
+                                               xOriginalEntry.ulObjectCluster = 0;\r
+                                       }\r
+\r
+                                       xOriginalEntry.ulFileSize = pxFile->ulFileSize;\r
+                                       xError = FF_PutEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /* Handle Linked list! */\r
+               FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+               {       /* Semaphore is required, or linked list could become corrupted. */\r
+                       pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;\r
+                       if( pxFileChain == pxFile )\r
+                       {\r
+                               pxFile->pxIOManager->FirstFile = pxFile->pxNext;\r
+                       }\r
+                       else\r
+                       {\r
+                               while( pxFileChain )\r
+                               {\r
+                                       if( pxFileChain->pxNext == pxFile )\r
+                                       {\r
+                                               /* Found it, remove it from the list. */\r
+                                               pxFileChain->pxNext = pxFile->pxNext;\r
+                                               break;\r
+                                       }\r
+                                       pxFileChain = pxFileChain->pxNext;\r
+                               }\r
+                       }\r
+               }       /* Semaphore released, linked list was shortened! */\r
+               FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );\r
+\r
+               #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )\r
+               {\r
+                       if( pxFile->pucBuffer != NULL )\r
+                       {\r
+                               /* Ensure any unaligned points are pushed to the disk! */\r
+                               if( pxFile->ucState & FF_BUFSTATE_WRITTEN )\r
+                               {\r
+                               FF_Error_t xTempError;\r
+\r
+                                       xTempError = FF_BlockWrite( pxFile->pxIOManager, FF_FileLBA( pxFile ), 1, pxFile->pucBuffer, pdFALSE );\r
+                                       if( FF_isERR( xError ) == pdFALSE )\r
+                                       {\r
+                                               xError = xTempError;\r
+                                       }\r
+                               }\r
+\r
+                               ffconfigFREE( pxFile->pucBuffer );\r
+                       }\r
+               }\r
+               #endif\r
+               if( FF_isERR( xError ) == pdFALSE )\r
+               {\r
+                       xError = FF_FlushCache( pxFile->pxIOManager ); /* Ensure all modified blocks are flushed to disk! */\r
+               }\r
+               ffconfigFREE( pxFile );\r
+       }\r
+       while( pdFALSE );\r
+\r
+       return xError;\r
+}      /* FF_Close() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Make Filesize equal to the FilePointer and truncates the file to this position\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*\r
+*      @return 0 on sucess.\r
+*      @return negative if some error occurred\r
+*\r
+**/\r
+FF_Error_t FF_SetEof( FF_FILE *pxFile )\r
+{\r
+       FF_Error_t xError;\r
+\r
+       /* Check if the file was not deleted and if it was opened with write permissions: */\r
+       if( ( ( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED ) == 0 ) &&\r
+               ( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE ) ) != 0 ) )\r
+       {\r
+               pxFile->ulFileSize = pxFile->ulFilePointer;\r
+               if( pxFile->ulObjectCluster != 0ul )\r
+               {\r
+                       xError = FF_Truncate( pxFile, pdFALSE );\r
+               }\r
+               else\r
+               {\r
+                       xError = FF_ERR_NONE;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_SETEOF );\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_SetEof() */\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+*      @public\r
+*      @brief  Truncate a file to 'pxFile->ulFileSize'\r
+*\r
+*      @param  pxFile          FF_FILE object that was created by FF_Open().\r
+*\r
+*      @return 0 on sucess.\r
+*      @return negative if some error occurred\r
+*\r
+**/\r
+static FF_Error_t FF_Truncate( FF_FILE *pxFile, BaseType_t bClosing )\r
+{\r
+FF_Error_t xError;\r
+FF_IOManager_t *pxIOManager = pxFile->pxIOManager;\r
+\r
+uint32_t ulClusterSize;\r
+uint32_t ulClusterCount;\r
+uint32_t ulClustersNeeded;\r
+\r
+       /* The number of bytes contained in a cluster. */\r
+       ulClusterSize = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster;\r
+\r
+       /* See how many clusters have been allocated. */\r
+       ulClusterCount = FF_GetChainLength( pxIOManager, pxFile->ulObjectCluster, NULL, &xError );\r
+\r
+       /* Calculate the actual number of clusters needed, rounding up */\r
+       ulClustersNeeded = ( pxFile->ulFileSize + ulClusterSize - 1 ) / ulClusterSize;\r
+       if( bClosing != pdFALSE )\r
+       {\r
+               /* The handle will be closed after truncating.  This function is called\r
+               because Filesize is an exact multiple of ulClusterSize. */\r
+               ulClustersNeeded = pxFile->ulFileSize / ulClusterSize;\r
+       }\r
+       else\r
+       {\r
+               /* This function is called to make the file size equal to the current\r
+               position within the file. Always keep an extra cluster to write to. */\r
+               ulClustersNeeded = ( pxFile->ulFileSize + ulClusterSize ) / ulClusterSize;\r
+       }\r
+\r
+       /* First change the FAT chain. */\r
+       if( ( FF_isERR( xError ) == pdFALSE ) && ( ulClusterCount > ulClustersNeeded ) )\r
+       {\r
+               if( ulClustersNeeded == 0ul )\r
+               {\r
+                       FF_LockFAT( pxIOManager );\r
+                       {\r
+                               /* In FF_Truncate() */\r
+                               xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );\r
+                       }\r
+                       FF_UnlockFAT( pxIOManager );\r
+\r
+                       if( FF_isERR( xError ) == pdFALSE )\r
+                       {\r
+                       FF_DirEnt_t xOriginalEntry;\r
+\r
+                               /* The directory denotes the address of the first data cluster of every file.\r
+                               Now change it to 'ulAddrCurrentCluster': */\r
+                               xError = FF_GetEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xOriginalEntry.ulObjectCluster = 0ul;\r
+                                       xError = FF_PutEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );\r
+\r
+                                       if( FF_isERR( xError ) == pdFALSE )\r
+                                       {\r
+                                               pxFile->ulObjectCluster = 0ul;\r
+                                               pxFile->ulChainLength = 0ul;\r
+                                               pxFile->ulCurrentCluster = 0ul;\r
+                                               pxFile->ulEndOfChain = 0ul;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       FF_LockFAT( pxIOManager );\r
+                       {\r
+                               uint32_t ulTruncateCluster = FF_TraverseFAT( pxIOManager, pxFile->ulObjectCluster, ulClustersNeeded - 1, &xError );\r
+\r
+                               if( FF_isERR( xError ) == pdFALSE )\r
+                               {\r
+                                       xError = FF_UnlinkClusterChain( pxIOManager, ulTruncateCluster, 1 );\r
+                               }\r
+                       }\r
+                       FF_UnlockFAT( pxIOManager );\r
+               }\r
+       }\r
+\r
+       return xError;\r
+}      /* FF_Truncate() */\r
+/*-----------------------------------------------------------*/\r