2 * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
4 * Authors include James Walmsley, Hein Tibosch and Richard Barry
\r
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
7 * this software and associated documentation files (the "Software"), to deal in
\r
8 * the Software without restriction, including without limitation the rights to
\r
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
10 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
11 * subject to the following conditions:
\r
13 * The above copyright notice and this permission notice shall be included in all
\r
14 * copies or substantial portions of the Software.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * https://www.FreeRTOS.org
\r
31 * @defgroup FILEIO FILE I/O Access
\r
32 * @brief Provides an interface to allow File I/O on a mounted IOMAN.
\r
34 * Provides file-system interfaces for the FAT file-system.
\r
37 #include "ff_headers.h"
\r
39 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
43 static FF_Error_t FF_Truncate( FF_FILE *pxFile, BaseType_t bClosing );
\r
45 static int32_t FF_ReadPartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,
\r
46 uint8_t *pucBuffer, FF_Error_t *pxError );
\r
48 static int32_t FF_WritePartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,
\r
49 const uint8_t *pucBuffer, FF_Error_t *pxError );
\r
51 static uint32_t FF_SetCluster( FF_FILE *pxFile, FF_Error_t *pxError );
\r
52 static uint32_t FF_FileLBA( FF_FILE *pxFile );
\r
54 /*-----------------------------------------------------------*/
\r
58 * @brief Converts STDIO mode strings into the equivalent FreeRTOS+FAT mode.
\r
60 * @param Mode The mode string e.g. "rb" "rb+" "w" "a" "r" "w+" "a+" etc
\r
62 * @return Returns the mode bits that should be passed to the FF_Open function.
\r
64 uint8_t FF_GetModeBits( const char *pcMode )
\r
66 uint8_t ucModeBits = 0x00;
\r
68 while( *pcMode != '\0' )
\r
72 case 'r': /* Allow Read. */
\r
74 ucModeBits |= FF_MODE_READ;
\r
77 case 'w': /* Allow Write. */
\r
79 ucModeBits |= FF_MODE_WRITE;
\r
80 ucModeBits |= FF_MODE_CREATE; /* Create if not exist. */
\r
81 ucModeBits |= FF_MODE_TRUNCATE;
\r
84 case 'a': /* Append new writes to the end of the file. */
\r
86 ucModeBits |= FF_MODE_WRITE;
\r
87 ucModeBits |= FF_MODE_APPEND;
\r
88 ucModeBits |= FF_MODE_CREATE; /* Create if not exist. */
\r
91 case '+': /* Update the file, don't Append! */
\r
92 ucModeBits |= FF_MODE_READ;
\r
93 ucModeBits |= FF_MODE_WRITE; /* RW Mode. */
\r
97 /* Internal use only! */
\r
98 ucModeBits |= FF_MODE_DIR;
\r
103 /* b|B flags not supported (Binary mode is native anyway). */
\r
114 } /* FF_GetModeBits() */
\r
115 /*-----------------------------------------------------------*/
\r
117 static FF_FILE *prvAllocFileHandle( FF_IOManager_t *pxIOManager, FF_Error_t *pxError )
\r
121 pxFile = ffconfigMALLOC( sizeof( FF_FILE ) );
\r
122 if( pxFile == NULL )
\r
124 *pxError = ( FF_Error_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_OPEN );
\r
128 memset( pxFile, 0, sizeof( *pxFile ) );
\r
130 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
132 pxFile->pucBuffer = ( uint8_t * ) ffconfigMALLOC( pxIOManager->usSectorSize );
\r
133 if( pxFile->pucBuffer != NULL )
\r
135 memset( pxFile->pucBuffer, 0, pxIOManager->usSectorSize );
\r
139 *pxError = ( FF_Error_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_OPEN );
\r
140 ffconfigFREE( pxFile );
\r
141 /* Make sure that NULL will be returned. */
\r
147 /* Remove compiler warnings. */
\r
148 ( void ) pxIOManager;
\r
154 } /* prvAllocFileHandle() */
\r
155 /*-----------------------------------------------------------*/
\r
158 * FF_Open() Mode Information
\r
160 * - Allows WRITE access to the file.
\r
163 * - Allows READ access to the file.
\r
166 * - Create file if it doesn't exist.
\r
168 * - FF_MODE_TRUNCATE
\r
169 * - Erase the file if it already exists and overwrite.
\r
172 * - 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
176 * Some sample modes:
\r
177 * - (FF_MODE_WRITE | FF_MODE_CREATE | FF_MODE_TRUNCATE)
\r
178 * - Write access to the file. (Equivalent to "w").
\r
180 * - (FF_MODE_WRITE | FF_MODE_READ)
\r
181 * - Read and Write access to the file. (Equivalent to "rb+").
\r
183 * - (FF_MODE_WRITE | FF_MODE_READ | FF_MODE_APPEND | FF_MODE_CREATE)
\r
184 * - Read and Write append mode access to the file. (Equivalent to "a+").
\r
187 * Be careful when choosing modes. For those using FF_Open() at the application layer
\r
188 * its best to use the provided FF_GetModeBits() function, as this complies to the same
\r
189 * behaviour as the stdio.h fopen() function.
\r
195 * @brief Opens a File for Access
\r
197 * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger().
\r
198 * @param pcPath Path to the File or object.
\r
199 * @param ucMode Access Mode required. Modes are a little complicated, the function FF_GetModeBits()
\r
200 * @param ucMode will convert a stdio Mode string into the equivalent Mode bits for this parameter.
\r
201 * @param pxError Pointer to a signed byte for error checking. Can be NULL if not required.
\r
202 * @param pxError To be checked when a NULL pointer is returned.
\r
204 * @return NULL pointer on error, in which case pxError should be checked for more information.
\r
205 * @return pxError can be:
\r
207 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
208 FF_FILE *FF_Open( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath, uint8_t ucMode, FF_Error_t *pxError )
\r
210 FF_FILE *FF_Open( FF_IOManager_t *pxIOManager, const char *pcPath, uint8_t ucMode, FF_Error_t *pxError )
\r
213 FF_FILE *pxFile = NULL;
\r
214 FF_FILE *pxFileChain;
\r
215 FF_DirEnt_t xDirEntry;
\r
216 uint32_t ulFileCluster;
\r
219 FF_FindParams_t xFindParams;
\r
220 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
221 FF_T_WCHAR pcFileName[ ffconfigMAX_FILENAME ];
\r
223 char pcFileName[ ffconfigMAX_FILENAME ];
\r
226 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
228 /* Inform the functions that the entry will be created if not found. */
\r
229 if( ( ucMode & FF_MODE_CREATE ) != 0 )
\r
231 xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG;
\r
234 if( pxIOManager == NULL )
\r
236 /* Use the error function code 'FF_OPEN' as this static
\r
237 function is only called from that function. */
\r
238 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_OPEN );
\r
240 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
241 else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
243 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_OPEN );
\r
245 #endif /* ffconfigREMOVABLE_MEDIA */
\r
248 xError = FF_ERR_NONE;
\r
250 /* Let xIndex point to the last occurrence of '/' or '\',
\r
251 to separate the path from the file name. */
\r
252 xIndex = ( BaseType_t ) STRLEN( pcPath );
\r
253 while( xIndex != 0 )
\r
255 if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) )
\r
262 /* Copy the file name, i.e. the string that comes after the last separator. */
\r
263 STRNCPY( pcFileName, pcPath + xIndex + 1, ffconfigMAX_FILENAME );
\r
267 /* Only for the root, the slash is part of the directory name.
\r
268 'xIndex' now equals to the length of the path name. */
\r
272 /* FF_CreateShortName() might set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK. */
\r
273 FF_CreateShortName( &xFindParams, pcFileName );
\r
275 /* Lookup the path and find the cluster pointing to the directory: */
\r
276 xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, xIndex, &xError );
\r
277 if( xFindParams.ulDirCluster == 0ul )
\r
279 if( ( ucMode & FF_MODE_WRITE ) != 0 )
\r
281 FF_PRINTF( "FF_Open[%s]: Path not found\n", pcPath );
\r
283 /* The user tries to open a file but the path leading to the file does not exist. */
\r
285 else if( FF_isERR( xError ) == pdFALSE )
\r
287 /* Allocate an empty file handle and buffer space for 'unaligned access'. */
\r
288 pxFile = prvAllocFileHandle( pxIOManager, &xError );
\r
292 if( FF_isERR( xError ) == pdFALSE )
\r
294 /* Copy the Mode Bits. */
\r
295 pxFile->ucMode = ucMode;
\r
297 /* See if the file does exist within the given directory. */
\r
298 ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0x00, &xDirEntry, &xError );
\r
300 if( ulFileCluster == 0ul )
\r
302 /* If cluster 0 was returned, it might be because the file has no allocated cluster,
\r
303 i.e. only a directory entry and no stored data. */
\r
304 if( STRLEN( pcFileName ) == STRLEN( xDirEntry.pcFileName ) )
\r
306 if( ( xDirEntry.ulFileSize == 0 ) && ( FF_strmatch( pcFileName, xDirEntry.pcFileName, ( BaseType_t ) STRLEN( pcFileName ) ) == pdTRUE ) )
\r
308 /* It is the file, give it a pseudo cluster number '1'. */
\r
310 /* And reset any error. */
\r
311 xError = FF_ERR_NONE;
\r
316 /* Test 'ulFileCluster' again, it might have been changed. */
\r
317 if( ulFileCluster == 0ul )
\r
319 /* The path is found, but it does not contain the file name yet.
\r
320 Maybe the user wants to create it? */
\r
321 if( ( ucMode & FF_MODE_CREATE ) == 0 )
\r
323 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_OPEN );
\r
327 ulFileCluster = FF_CreateFile( pxIOManager, &xFindParams, pcFileName, &xDirEntry, &xError );
\r
328 if( FF_isERR( xError ) == pdFALSE )
\r
330 xDirEntry.usCurrentItem += 1;
\r
336 if( FF_isERR( xError ) == pdFALSE )
\r
338 /* Now the file exists, or it has been created.
\r
339 Check if the Mode flags are allowed: */
\r
340 if( ( xDirEntry.ucAttrib == FF_FAT_ATTR_DIR ) && ( ( ucMode & FF_MODE_DIR ) == 0 ) )
\r
342 /* Not the object, File Not Found! */
\r
343 xError = ( FF_Error_t ) ( FF_ERR_FILE_OBJECT_IS_A_DIR | FF_OPEN );
\r
345 /*---------- Ensure Read-Only files don't get opened for Writing. */
\r
346 else if( ( ( ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 ) && ( ( xDirEntry.ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) )
\r
348 xError = ( FF_Error_t ) ( FF_ERR_FILE_IS_READ_ONLY | FF_OPEN );
\r
352 if( FF_isERR( xError ) == pdFALSE )
\r
354 pxFile->pxIOManager = pxIOManager;
\r
355 pxFile->ulFilePointer = 0;
\r
356 /* Despite the warning output by MSVC - it is not possible to get here
\r
357 if xDirEntry has not been initialised. */
\r
358 pxFile->ulObjectCluster = xDirEntry.ulObjectCluster;
\r
359 pxFile->ulFileSize = xDirEntry.ulFileSize;
\r
360 pxFile->ulCurrentCluster = 0;
\r
361 pxFile->ulAddrCurrentCluster = pxFile->ulObjectCluster;
\r
363 pxFile->pxNext = NULL;
\r
364 pxFile->ulDirCluster = xFindParams.ulDirCluster;
\r
365 pxFile->usDirEntry = xDirEntry.usCurrentItem - 1;
\r
366 pxFile->ulChainLength = 0;
\r
367 pxFile->ulEndOfChain = 0;
\r
368 pxFile->ulValidFlags &= ~( FF_VALID_FLAG_DELETED );
\r
370 /* Add pxFile onto the end of our linked list of FF_FILE objects.
\r
371 But first make sure that there are not 2 handles with write access
\r
372 to the same object. */
\r
373 FF_PendSemaphore( pxIOManager->pvSemaphore );
\r
375 pxFileChain = ( FF_FILE * ) pxIOManager->FirstFile;
\r
376 if( pxFileChain == NULL )
\r
378 pxIOManager->FirstFile = pxFile;
\r
384 /* See if two file handles point to the same object. */
\r
385 if( ( pxFileChain->ulObjectCluster == pxFile->ulObjectCluster ) &&
\r
386 ( pxFileChain->ulDirCluster == pxFile->ulDirCluster ) &&
\r
387 ( pxFileChain->usDirEntry == pxFile->usDirEntry ) )
\r
389 /* Fail if any of the two has write access to the object. */
\r
390 if( ( ( pxFileChain->ucMode | pxFile->ucMode ) & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 )
\r
392 /* File is already open! DON'T ALLOW IT! */
\r
393 xError = ( FF_Error_t ) ( FF_ERR_FILE_ALREADY_OPEN | FF_OPEN );
\r
398 if( pxFileChain->pxNext == NULL )
\r
400 pxFileChain->pxNext = pxFile;
\r
404 pxFileChain = ( FF_FILE * ) pxFileChain->pxNext;
\r
409 FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
\r
412 if( FF_isERR( xError ) == pdFALSE )
\r
414 /* If the file is opened with the truncate flag, truncate its contents. */
\r
415 if( ( ucMode & FF_MODE_TRUNCATE ) != 0 )
\r
417 /* Set the current size and position to zero. */
\r
418 pxFile->ulFileSize = 0;
\r
419 pxFile->ulFilePointer = 0;
\r
423 if( FF_isERR( xError ) != pdFALSE )
\r
425 if( pxFile != NULL )
\r
427 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
429 ffconfigFREE( pxFile->pucBuffer );
\r
432 ffconfigFREE( pxFile );
\r
437 if( pxError != NULL )
\r
444 /*-----------------------------------------------------------*/
\r
448 * @brief Tests if a Directory contains any other files or folders.
\r
450 * @param pxIOManager FF_IOManager_t object returned from the FF_CreateIOManger() function.
\r
453 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
454 BaseType_t FF_isDirEmpty ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR *pcPath )
\r
456 BaseType_t FF_isDirEmpty ( FF_IOManager_t * pxIOManager, const char *pcPath )
\r
459 FF_DirEnt_t xDirEntry;
\r
460 FF_Error_t xError = FF_ERR_NONE;
\r
461 BaseType_t xReturn;
\r
463 if( pxIOManager == NULL )
\r
469 xError = FF_FindFirst( pxIOManager, &xDirEntry, pcPath );
\r
471 /* Assume the directory is empty until a file is
\r
472 encountered with a name other than ".." or "." */
\r
475 while( xError == 0 )
\r
477 /* As we can't be sure the first 2 entries contain
\r
478 * "." and "..", check it, not just count them
\r
480 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
481 if( ( wcscmp( xDirEntry.pcFileName, L".." ) != 0 ) && ( wcscmp( xDirEntry.pcFileName, L"." ) != 0 ) )
\r
483 if( ( strcmp( xDirEntry.pcFileName, ".." ) != 0 ) && ( strcmp( xDirEntry.pcFileName, "." ) != 0 ) )
\r
489 xError = FF_FindNext( pxIOManager, &xDirEntry );
\r
494 } /* FF_isDirEmpty() */
\r
495 /*-----------------------------------------------------------*/
\r
497 #if( ffconfigPATH_CACHE != 0 )
\r
498 /* _HT_ After a directory has been renamed, the path cache becomes out-of-date */
\r
499 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
500 static void FF_RmPathCache ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath )
\r
502 static void FF_RmPathCache ( FF_IOManager_t * pxIOManager, const char *pcPath )
\r
506 * The directory 'path' will be removed or renamed
\r
507 * now clear all entries starting with 'path' in the path cache
\r
510 BaseType_t pathLen = STRLEN( pcPath );
\r
512 FF_PendSemaphore( pxIOManager->pvSemaphore );
\r
514 for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ )
\r
516 BaseType_t len2 = STRLEN( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath );
\r
518 if( len2 >= pathLen && FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) )
\r
520 pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath[ 0 ] = '\0';
\r
521 pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster = 0;
\r
526 FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
\r
528 #endif /* ffconfigPATH_CACHE */
\r
529 /*-----------------------------------------------------------*/
\r
532 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
533 FF_Error_t FF_RmDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )
\r
535 FF_Error_t FF_RmDir( FF_IOManager_t *pxIOManager, const char *pcPath )
\r
539 uint8_t ucEntryBuffer[32];
\r
540 FF_FetchContext_t xFetchContext;
\r
541 FF_Error_t xError = FF_ERR_NONE;
\r
543 if( pxIOManager == NULL )
\r
545 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_RMDIR );
\r
547 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
548 else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
550 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_RMDIR );
\r
552 #endif /* ffconfigREMOVABLE_MEDIA */
\r
555 pxFile = FF_Open( pxIOManager, pcPath, FF_MODE_DIR, &xError );
\r
557 if( pxFile != NULL )
\r
559 pxFile->ulValidFlags |= FF_VALID_FLAG_DELETED;
\r
561 /* Clear the struct to allow a call to FF_CleanupEntryFetch() in any
\r
563 memset( &xFetchContext, '\0', sizeof( xFetchContext ) );
\r
565 /* This task will get the unique right to change directories. */
\r
566 FF_LockDirectory( pxIOManager );
\r
567 do /* while( pdFALSE ) */
\r
569 /* This while loop is only introduced to be able to use break
\r
571 if( FF_isDirEmpty( pxIOManager, pcPath ) == pdFALSE )
\r
573 xError = ( FF_ERR_DIR_NOT_EMPTY | FF_RMDIR );
\r
576 FF_LockFAT( pxIOManager );
\r
577 #if( ffconfigHASH_CACHE != 0 )
\r
579 /* A directory is removed so invalidate any hash table
\r
580 referring to this directory. */
\r
581 FF_UnHashDir( pxIOManager, pxFile->ulObjectCluster );
\r
583 #endif /* ffconfigHASH_CACHE */
\r
585 /* Add parameter 0 to delete the entire chain!
\r
586 The actual directory entries on disk will be freed. */
\r
587 xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );
\r
589 FF_UnlockFAT( pxIOManager );
\r
590 if( FF_isERR( xError ) )
\r
595 /* Now remove this directory from its parent directory.
\r
596 Initialise the dirent Fetch Context object for faster removal of
\r
598 xError = FF_InitEntryFetch( pxIOManager, pxFile->ulDirCluster, &xFetchContext );
\r
599 if( FF_isERR( xError ) )
\r
604 #if( ffconfigHASH_CACHE != 0 )
\r
606 /* Invalidate any hash table of the parent directory
\r
608 FF_UnHashDir( pxIOManager, pxFile->ulDirCluster );
\r
610 #endif /* ffconfigHASH_CACHE */
\r
612 /* Edit the Directory Entry, so it will show as deleted.
\r
613 First remove the LFN entries: */
\r
614 xError = FF_RmLFNs( pxIOManager, pxFile->usDirEntry, &xFetchContext );
\r
615 if( FF_isERR( xError ) )
\r
620 /* And remove the Short file name entry: */
\r
621 xError = FF_FetchEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
622 if( FF_isERR( xError ) == pdFALSE )
\r
624 ucEntryBuffer[0] = FF_FAT_DELETED;
\r
625 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );
\r
626 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) 0ul );
\r
628 xError = FF_PushEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
630 if( FF_isERR( xError ) )
\r
635 #if( ffconfigPATH_CACHE != 0 )
\r
637 /* We're removing a directory which might contain
\r
638 subdirectories. Instead of iterating through all
\r
639 subdirectories, just clear the path cache. */
\r
640 FF_RmPathCache( pxIOManager, pcPath );
\r
643 } while( pdFALSE );
\r
645 FF_Error_t xTempError;
\r
646 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
647 if( FF_isERR( xError ) == pdFALSE )
\r
649 xError = xTempError;
\r
651 FF_UnlockDirectory( pxIOManager );
\r
653 /* Free the file pointer resources. */
\r
654 xTempError = FF_Close( pxFile );
\r
655 if( FF_isERR( xError ) == pdFALSE )
\r
657 xError = xTempError;
\r
660 xTempError = FF_FlushCache( pxIOManager );
\r
661 if( FF_isERR( xError ) == pdFALSE )
\r
663 xError = xTempError;
\r
666 } /* if( pxFile != NULL ) */
\r
667 } /* else if( pxIOManager != NULL ) */
\r
671 /*-----------------------------------------------------------*/
\r
673 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
674 FF_Error_t FF_RmFile( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )
\r
676 FF_Error_t FF_RmFile( FF_IOManager_t *pxIOManager, const char *pcPath )
\r
680 FF_Error_t xError = FF_ERR_NONE;
\r
681 uint8_t ucEntryBuffer[32];
\r
682 FF_FetchContext_t xFetchContext;
\r
684 /* Opening the file-to-be-deleted in WR mode has two advantages:
\r
685 1. The file handle gives all necessary information to delete it such
\r
686 as the data clusters and directory entries.
\r
687 2. The file is now locked, it can not be opened by another task. */
\r
688 pxFile = FF_Open( pxIOManager, pcPath, FF_MODE_WRITE, &xError );
\r
690 if( pxFile != NULL )
\r
692 /* FF_Close() will see this flag and won't do any disc access. */
\r
693 pxFile->ulValidFlags |= FF_VALID_FLAG_DELETED;
\r
695 /* Ensure there is actually a cluster chain to delete! */
\r
696 if( pxFile->ulObjectCluster != 0 )
\r
698 /* Lock the FAT so its thread-safe. */
\r
699 FF_LockFAT( pxIOManager );
\r
701 /* 0 to delete the entire chain! */
\r
702 xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );
\r
704 FF_UnlockFAT( pxIOManager );
\r
707 if( FF_isERR( xError ) == pdFALSE )
\r
709 /* Clear the struct to allow a call to FF_CleanupEntryFetch() in any
\r
711 memset( &xFetchContext, '\0', sizeof( xFetchContext ) );
\r
713 /* Get sole access to "directory changes" */
\r
714 FF_LockDirectory( pxIOManager );
\r
716 /* Edit the Directory Entry! (So it appears as deleted); */
\r
718 xError = FF_InitEntryFetch( pxIOManager, pxFile->ulDirCluster, &xFetchContext );
\r
719 if( FF_isERR( xError ) )
\r
724 #if( ffconfigHASH_CACHE != 0 )
\r
726 FF_UnHashDir( pxIOManager, pxFile->ulDirCluster );
\r
728 #endif /* ffconfigHASH_CACHE */
\r
729 /* Remove LFN entries, if any. */
\r
730 xError = FF_RmLFNs( pxIOManager, ( uint16_t ) pxFile->usDirEntry, &xFetchContext );
\r
731 if( FF_isERR( xError ) )
\r
736 /* Remove the Short file name entry. */
\r
737 xError = FF_FetchEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
738 if( FF_isERR( xError ) == pdFALSE )
\r
740 ucEntryBuffer[0] = FF_FAT_DELETED;
\r
741 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );
\r
742 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) 0ul );
\r
744 xError = FF_PushEntryWithContext( pxIOManager, pxFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
746 } while( pdFALSE );
\r
748 FF_Error_t xTempError;
\r
749 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
750 if( FF_isERR( xError ) == pdFALSE )
\r
752 xError = xTempError;
\r
754 FF_UnlockDirectory( pxIOManager );
\r
756 /* Free the file pointer resources. */
\r
757 xTempError = FF_Close( pxFile );
\r
758 if( FF_isERR( xError ) == pdFALSE )
\r
760 xError = xTempError;
\r
763 xTempError = FF_FlushCache( pxIOManager );
\r
764 if( FF_isERR( xError ) == pdFALSE )
\r
766 xError = xTempError;
\r
770 } /* if( pxFile != NULL ) */
\r
773 } /* FF_RmFile() */
\r
774 /*-----------------------------------------------------------*/
\r
778 * @brief Moves a file or directory from source to destination.
\r
780 * @param pxIOManager The FF_IOManager_t object pointer.
\r
781 * @param szSourceFile String of the source file to be moved or renamed.
\r
782 * @param szDestinationFile String of the destination file to where the source should be moved or renamed.
\r
784 * @return FF_ERR_NONE on success.
\r
785 * @return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.
\r
786 * @return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).
\r
787 * @return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.
\r
788 * @return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.
\r
792 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
793 FF_Error_t FF_Move( FF_IOManager_t *pxIOManager, const FF_T_WCHAR*szSourceFile,
\r
794 const FF_T_WCHAR *szDestinationFile, BaseType_t xDeleteIfExists )
\r
796 FF_Error_t FF_Move( FF_IOManager_t *pxIOManager, const char *szSourceFile,
\r
797 const char *szDestinationFile, BaseType_t xDeleteIfExists )
\r
801 FF_FILE *pSrcFile, *pxDestFile;
\r
802 FF_DirEnt_t xMyFile;
\r
803 uint8_t ucEntryBuffer[32];
\r
805 uint32_t ulDirCluster = 0ul;
\r
806 FF_FetchContext_t xFetchContext;
\r
807 #if( ffconfigPATH_CACHE != 0 )
\r
808 BaseType_t xIsDirectory = pdFALSE;
\r
811 memset( &xFetchContext, '\0', sizeof( xFetchContext ) );
\r
813 if( pxIOManager == NULL )
\r
815 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
\r
817 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
818 else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
820 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_MOVE );
\r
822 #endif /* ffconfigREMOVABLE_MEDIA */
\r
825 /* Check destination file doesn't exist! */
\r
826 pxDestFile = FF_Open( pxIOManager, szDestinationFile, FF_MODE_READ, &xError );
\r
828 if( ( pxDestFile != NULL) || ( FF_GETERROR( xError ) == FF_ERR_FILE_OBJECT_IS_A_DIR ) )
\r
830 xError = ( FF_Error_t ) ( FF_ERR_FILE_DESTINATION_EXISTS | FF_MOVE );
\r
831 if( pxDestFile != NULL )
\r
833 FF_Close( pxDestFile );
\r
834 if( xDeleteIfExists != pdFALSE )
\r
836 xError = FF_RmFile( pxIOManager, szDestinationFile );
\r
842 /* Discard the error set by FF_Open().
\r
843 The target file (or directory) is not found: continue renaming. */
\r
844 xError = FF_ERR_NONE;
\r
848 if( FF_isERR( xError ) == pdFALSE )
\r
850 /* About to move/rename 'szSourceFile'. When opening it with 'FF_MODE_WRITE'
\r
851 only succeeds if it has no other open handle to it. */
\r
852 pSrcFile = FF_Open( pxIOManager, szSourceFile, FF_MODE_WRITE, &xError );
\r
854 if( FF_GETERROR( xError ) == FF_ERR_FILE_OBJECT_IS_A_DIR )
\r
856 /* Open a directory for moving! */
\r
857 pSrcFile = FF_Open( pxIOManager, szSourceFile, FF_MODE_DIR, &xError );
\r
858 #if( ffconfigPATH_CACHE != 0 )
\r
859 xIsDirectory = pdTRUE;
\r
863 if( pSrcFile != NULL )
\r
865 /* Collect information about the current directory entry. */
\r
866 xError = FF_InitEntryFetch( pxIOManager, pSrcFile->ulDirCluster, &xFetchContext );
\r
867 if( FF_isERR( xError ) == pdFALSE )
\r
869 xError = FF_FetchEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
870 if( FF_isERR( xError ) == pdFALSE )
\r
872 xMyFile.ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
\r
873 xMyFile.ulFileSize = pSrcFile->ulFileSize;
\r
874 xMyFile.ulObjectCluster = pSrcFile->ulObjectCluster;
\r
875 xMyFile.usCurrentItem = 0;
\r
877 xIndex = ( BaseType_t ) STRLEN( szDestinationFile );
\r
879 while( xIndex != 0 )
\r
881 if( ( szDestinationFile[ xIndex ] == '\\' ) || ( szDestinationFile[ xIndex ] == '/' ) )
\r
889 /* Copy the base name of the destination file. */
\r
890 STRNCPY( xMyFile.pcFileName, ( szDestinationFile + xIndex + 1 ), ffconfigMAX_FILENAME );
\r
897 /* Find the (cluster of the) directory in which the target file will be located.
\r
898 It must exist before calling FF_Move(). */
\r
899 ulDirCluster = FF_FindDir( pxIOManager, szDestinationFile, xIndex, &xError );
\r
904 if( FF_isERR( xError ) == pdFALSE )
\r
906 if( ulDirCluster != 0ul )
\r
908 FF_FindParams_t xFindParams;
\r
909 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
911 /* Clean up because FF_CreateDirent might want to write to the same sector. */
\r
912 xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
914 if( FF_isERR( xError ) == pdFALSE )
\r
916 /* Destination directory was found, we can now create the new entry. */
\r
917 xFindParams.ulDirCluster = ulDirCluster;
\r
918 xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyFile );
\r
921 if( FF_isERR( xError ) == pdFALSE )
\r
923 /* Edit the Directory Entry! (So it appears as deleted); */
\r
924 FF_LockDirectory( pxIOManager );
\r
926 xError = FF_RmLFNs( pxIOManager, pSrcFile->usDirEntry, &xFetchContext );
\r
928 if( FF_isERR( xError ) == pdFALSE )
\r
930 xError = FF_FetchEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
932 if( FF_isERR( xError ) == pdFALSE )
\r
934 FF_Error_t xTempError;
\r
935 ucEntryBuffer[0] = FF_FAT_DELETED;
\r
936 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) 0ul );
\r
937 FF_putShort( ucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) 0ul );
\r
939 xError = FF_PushEntryWithContext( pxIOManager, pSrcFile->usDirEntry, &xFetchContext, ucEntryBuffer );
\r
940 /* The contents of 'xFetchContext' has changed, flush it to disk now. */
\r
941 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
942 if( FF_isERR( xError ) == pdFALSE )
\r
944 xError = xTempError;
\r
949 FF_UnlockDirectory( pxIOManager );
\r
952 #if( ffconfigPATH_CACHE != 0 )
\r
954 if( xIsDirectory != 0 )
\r
956 /* We've renamed a directory which might contain
\r
957 subdirectories. To avoid having false entries, clear
\r
959 FF_RmPathCache( pxIOManager, szSourceFile );
\r
964 else /* ulDirCluster == 0ul */
\r
966 xError = ( FF_Error_t ) ( FF_ERR_FILE_DIR_NOT_FOUND | FF_MOVE );
\r
970 if( pSrcFile != NULL )
\r
972 /* The source file was opened in WRITE mode just to lock it.
\r
973 Now clear the write flags to avoid writing back any changes. */
\r
974 pSrcFile->ucMode &= ~( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE );
\r
975 FF_Close( pSrcFile );
\r
980 FF_Error_t xTempError;
\r
982 xTempError = FF_FlushCache( pxIOManager );
\r
983 if( FF_isERR( xError ) == pdFALSE )
\r
985 xError = xTempError;
\r
987 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
988 if( FF_isERR( xError ) == pdFALSE )
\r
990 xError = xTempError;
\r
996 /*-----------------------------------------------------------*/
\r
1000 * @brief Get's the next Entry based on the data recorded in the FF_DirEnt_t object.
\r
1002 * @param pxFile FF_FILE object that was created by FF_Open().
\r
1004 * @return pdTRUE if End of File was reached. pdFALSE if not.
\r
1005 * @return pdFALSE if a null pointer was provided.
\r
1008 BaseType_t FF_isEOF( FF_FILE *pxFile )
\r
1010 BaseType_t xReturn;
\r
1012 if( ( pxFile != NULL ) && ( pxFile->ulFilePointer >= pxFile->ulFileSize ) )
\r
1018 xReturn = pdFALSE;
\r
1022 } /* FF_isEOF() */
\r
1023 /*-----------------------------------------------------------*/
\r
1027 * @brief Checks the number of bytes left on a read handle
\r
1029 * @param pxFile An open file handle
\r
1031 * @return Less than zero: an error code
\r
1032 * @return Number of bytes left to read from handle
\r
1034 int32_t FF_BytesLeft( FF_FILE *pxFile )
\r
1036 BaseType_t xReturn;
\r
1038 if( pxFile == NULL )
\r
1040 xReturn = FF_ERR_NULL_POINTER | FF_BYTESLEFT;
\r
1042 else if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )
\r
1044 xReturn = FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_BYTESLEFT;
\r
1046 else if( pxFile->ulFilePointer >= pxFile->ulFileSize )
\r
1052 xReturn = pxFile->ulFileSize - pxFile->ulFilePointer;
\r
1056 } /* FF_BytesLeft() */
\r
1057 /*-----------------------------------------------------------*/
\r
1061 * @brief Returns the file size of a read handle
\r
1063 * @param pxFile An open file handle
\r
1065 * @return Less than zero: an error code
\r
1066 * @return Number of bytes left to read from handle
\r
1068 FF_Error_t FF_GetFileSize( FF_FILE *pxFile, uint32_t *pulSize ) /* Writes # of bytes in a file to the parameter. */
\r
1070 BaseType_t xReturn;
\r
1072 if( pxFile == NULL )
\r
1074 xReturn = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_BYTESLEFT );
\r
1075 *( pulSize ) = ( uint32_t ) 0u;
\r
1077 else if( FF_isERR( FF_CheckValid( pxFile ) ) )
\r
1079 xReturn = ( FF_Error_t ) ( FF_ERR_FILE_BAD_HANDLE | FF_BYTESLEFT );
\r
1080 *( pulSize ) = ( uint32_t ) 0u;
\r
1085 *( pulSize ) = pxFile->ulFileSize;
\r
1089 } /* FF_GetFileSize */
\r
1091 int32_t FF_FileSize( FF_FILE *pxFile )
\r
1093 uint32_t ulLength;
\r
1094 FF_Error_t xResult;
\r
1096 /* Function is deprecated. Please use FF_GetFileSize(). */
\r
1097 xResult = FF_GetFileSize( pxFile, &( ulLength ) );
\r
1099 if( FF_isERR( xResult ) == 0 )
\r
1101 xResult = ( int32_t ) ulLength;
\r
1104 return ( int32_t ) xResult;
\r
1105 } /* FF_FileSize() */
\r
1106 /*-----------------------------------------------------------*/
\r
1108 static uint32_t FF_GetSequentialClusters( FF_IOManager_t *pxIOManager, uint32_t ulStartCluster, uint32_t ulLimit, FF_Error_t *pxError )
\r
1110 uint32_t ulCurrentCluster;
\r
1111 uint32_t ulNextCluster = ulStartCluster;
\r
1112 uint32_t ulIndex = 0;
\r
1114 FF_FATBuffers_t xFATBuffers;
\r
1115 FF_InitFATBuffers( &xFATBuffers, FF_MODE_READ );
\r
1117 *pxError = FF_ERR_NONE;
\r
1119 FF_LockFAT( pxIOManager );
\r
1122 ulCurrentCluster = ulNextCluster;
\r
1123 ulNextCluster = FF_getFATEntry( pxIOManager, ulCurrentCluster, pxError, &xFATBuffers );
\r
1124 if( FF_isERR( *pxError ) )
\r
1130 if( ulNextCluster == ( ulCurrentCluster + 1 ) )
\r
1139 if( ( ulLimit != 0 ) && ( ulIndex == ulLimit ) )
\r
1144 while( ulNextCluster == ( ulCurrentCluster + 1 ) );
\r
1146 FF_UnlockFAT( pxIOManager );
\r
1148 *pxError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );
\r
1151 } /* FF_GetSequentialClusters() */
\r
1152 /*-----------------------------------------------------------*/
\r
1154 static FF_Error_t FF_ReadClusters( FF_FILE *pxFile, uint32_t ulCount, uint8_t *buffer )
\r
1156 uint32_t ulSectors;
\r
1157 uint32_t ulSequentialClusters = 0;
\r
1158 uint32_t ulItemLBA;
\r
1159 FF_Error_t xError = FF_ERR_NONE;
\r
1161 while( ulCount != 0 )
\r
1163 if( ( ulCount - 1 ) > 0 )
\r
1165 ulSequentialClusters =
\r
1166 FF_GetSequentialClusters( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulCount - 1, &xError );
\r
1167 if( FF_isERR( xError ) )
\r
1173 ulSectors = ( ulSequentialClusters + 1 ) * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;
\r
1174 ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );
\r
1175 ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );
\r
1177 xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, ulSectors, buffer, pdFALSE );
\r
1178 if( FF_isERR( xError ) )
\r
1183 ulCount -= ( ulSequentialClusters + 1 );
\r
1185 FF_LockFAT( pxFile->pxIOManager );
\r
1187 pxFile->ulAddrCurrentCluster =
\r
1188 FF_TraverseFAT( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulSequentialClusters + 1, &xError );
\r
1190 FF_UnlockFAT( pxFile->pxIOManager );
\r
1191 if( FF_isERR( xError ) )
\r
1196 pxFile->ulCurrentCluster += ( ulSequentialClusters + 1 );
\r
1197 buffer += ulSectors * pxFile->pxIOManager->usSectorSize;
\r
1198 ulSequentialClusters = 0;
\r
1202 } /* FF_ReadClusters ()*/
\r
1203 /*-----------------------------------------------------------*/
\r
1205 static FF_Error_t FF_ExtendFile( FF_FILE *pxFile, uint32_t ulSize )
\r
1207 FF_IOManager_t *pxIOManager = pxFile->pxIOManager;
\r
1208 uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster;
\r
1209 uint32_t ulTotalClustersNeeded = ( ulSize + ulBytesPerCluster - 1 ) / ulBytesPerCluster;
\r
1210 uint32_t ulClusterToExtend;
\r
1211 /* Initialise xIndex just for the compiler. */
\r
1212 BaseType_t xIndex = 0;
\r
1213 FF_DirEnt_t xOriginalEntry;
\r
1214 FF_Error_t xError = FF_ERR_NONE;
\r
1215 FF_FATBuffers_t xFATBuffers;
\r
1217 if( ( pxFile->ucMode & FF_MODE_WRITE ) != FF_MODE_WRITE )
\r
1219 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_EXTENDFILE );
\r
1223 if( ( pxFile->ulFileSize == 0 ) && ( pxFile->ulObjectCluster == 0 ) )
\r
1225 /* If there is no object cluster yet, create it.*/
\r
1226 pxFile->ulAddrCurrentCluster = FF_CreateClusterChain( pxFile->pxIOManager, &xError );
\r
1228 if( FF_isERR( xError ) == pdFALSE )
\r
1230 /* The directory denotes the address of the first data cluster of every file.
\r
1231 Now change it to 'ulAddrCurrentCluster': */
\r
1232 xError = FF_GetEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );
\r
1234 if( FF_isERR( xError ) == pdFALSE )
\r
1236 xOriginalEntry.ulObjectCluster = pxFile->ulAddrCurrentCluster;
\r
1237 xError = FF_PutEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );
\r
1239 if( FF_isERR( xError ) == pdFALSE )
\r
1241 pxFile->ulObjectCluster = pxFile->ulAddrCurrentCluster;
\r
1242 pxFile->ulChainLength = 1;
\r
1243 pxFile->ulCurrentCluster = 0;
\r
1244 pxFile->ulEndOfChain = pxFile->ulAddrCurrentCluster;
\r
1251 /* This file already has at least one cluster. */
\r
1255 if( FF_isERR( xError ) == pdFALSE )
\r
1257 if( pxFile->ulChainLength == 0 )
\r
1259 /* This is the first extension requiring the chain length.
\r
1260 Calculate it now: */
\r
1261 pxFile->ulChainLength = FF_GetChainLength( pxIOManager, pxFile->ulObjectCluster, &pxFile->ulEndOfChain, &xError );
\r
1265 if( ( FF_isERR( xError ) == pdFALSE ) && ( ulTotalClustersNeeded > pxFile->ulChainLength ) )
\r
1267 uint32_t ulCurrentCluster, ulNextCluster;
\r
1269 ulClusterToExtend = ( ulTotalClustersNeeded - pxFile->ulChainLength );
\r
1270 /* Now the file has at least 1 cluster, but it needs more clusters. */
\r
1271 ulNextCluster = pxFile->ulAddrCurrentCluster;
\r
1272 FF_LockFAT( pxIOManager );
\r
1274 ulCurrentCluster = FF_FindEndOfChain( pxIOManager, ulNextCluster, &xError );
\r
1276 if( FF_isERR( xError ) == pdFALSE )
\r
1278 for( xIndex = 0; xIndex < ( BaseType_t ) ulClusterToExtend; xIndex++ )
\r
1280 /* In FF_ExtendFile() */
\r
1281 ulNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE );
\r
1282 if( ( FF_isERR( xError ) == pdFALSE ) && ( ulNextCluster == 0UL ) )
\r
1284 xError = ( FF_Error_t ) ( FF_ERR_FAT_NO_FREE_CLUSTERS | FF_EXTENDFILE );
\r
1287 if( FF_isERR( xError ) )
\r
1292 /* Can not use this buffer earlier because of FF_FindEndOfChain/FF_FindFreeCluster */
\r
1293 FF_InitFATBuffers( &xFATBuffers, FF_MODE_WRITE );
\r
1294 xError = FF_putFATEntry( pxIOManager, ulCurrentCluster, ulNextCluster, &xFATBuffers );
\r
1295 if( FF_isERR( xError ) )
\r
1299 xError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );
\r
1300 if( FF_isERR( xError ) )
\r
1304 ulCurrentCluster = ulNextCluster;
\r
1307 if( FF_isERR( xError ) == pdFALSE )
\r
1309 pxFile->ulEndOfChain = ulCurrentCluster;
\r
1311 pxFile->ulChainLength += xIndex;
\r
1313 FF_UnlockFAT( pxIOManager );
\r
1316 FF_Error_t xTempError;
\r
1317 xTempError = FF_DecreaseFreeClusters( pxIOManager, ( uint32_t ) xIndex ); /* Keep Tab of Numbers for fast FreeSize() */
\r
1318 if( FF_isERR( xError ) == pdFALSE )
\r
1320 xError = xTempError;
\r
1324 /* We must ensure that the ulAddrCurrentCluster is not out-of-sync with the CurrentCluster number.
\r
1325 This could have occurred in append mode, where the file was opened with a filesize % clustersize == 0
\r
1326 because of a seek, where the ulAddrCurrentCluster was not updated after extending. This caused the data to
\r
1327 be written to the previous cluster(s). */
\r
1328 if( ( pxFile->ulCurrentCluster == pxFile->ulChainLength - 1 ) &&
\r
1329 ( pxFile->ulAddrCurrentCluster != pxFile->ulEndOfChain ) )
\r
1331 pxFile->ulAddrCurrentCluster = pxFile->ulEndOfChain;
\r
1334 /* By default, 'ffconfigFILE_EXTEND_FLUSHES_BUFFERS' is
\r
1336 Users may set it to zero in order to increase the
\r
1337 speed of writing to disk. */
\r
1339 #if( ffconfigFILE_EXTEND_FLUSHES_BUFFERS != 0 )
\r
1341 FF_Error_t xTempError;
\r
1343 xTempError = FF_FlushCache( pxIOManager );
\r
1344 if( FF_isERR( xError ) == pdFALSE )
\r
1346 xError = xTempError;
\r
1349 #endif /* ffconfigFILE_EXTEND_FLUSHES_BUFFERS */
\r
1350 } /* if( ulTotalClustersNeeded > pxFile->ulChainLength ) */
\r
1353 } /* FF_ExtendFile() */
\r
1354 /*-----------------------------------------------------------*/
\r
1356 static FF_Error_t FF_WriteClusters( FF_FILE *pxFile, uint32_t ulCount, uint8_t *buffer )
\r
1358 uint32_t ulSectors;
\r
1359 uint32_t ulSequentialClusters = 0;
\r
1360 uint32_t ulItemLBA;
\r
1361 FF_Error_t xError = FF_ERR_NONE;
\r
1363 while( ulCount != 0 )
\r
1365 if( ( ulCount - 1 ) > 0 )
\r
1367 ulSequentialClusters =
\r
1368 FF_GetSequentialClusters( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulCount - 1, &xError );
\r
1369 if( FF_isERR( xError ) )
\r
1375 ulSectors = ( ulSequentialClusters + 1 ) * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;
\r
1376 ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );
\r
1377 ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );
\r
1379 xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, ulSectors, buffer, pdFALSE );
\r
1381 if( FF_isERR( xError ) )
\r
1386 ulCount -= ulSequentialClusters + 1;
\r
1388 FF_LockFAT( pxFile->pxIOManager );
\r
1390 pxFile->ulAddrCurrentCluster =
\r
1391 FF_TraverseFAT( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster, ulSequentialClusters + 1, &xError );
\r
1393 FF_UnlockFAT( pxFile->pxIOManager );
\r
1394 if( FF_isERR( xError ) )
\r
1399 pxFile->ulCurrentCluster += ( ulSequentialClusters + 1 );
\r
1400 buffer += ulSectors * pxFile->pxIOManager->usSectorSize;
\r
1401 ulSequentialClusters = 0;
\r
1405 } /* FF_WriteClusters */
\r
1406 /*-----------------------------------------------------------*/
\r
1410 * @brief Calculate the Logical Block Address (LBA)
\r
1412 * @param pxFile The file handle
\r
1417 * - pxFile->ulFilePointer : byte offset in file
\r
1418 * - pxFile->ulAddrCurrentCluster : physical cluster on the partition
\r
1420 static uint32_t FF_FileLBA( FF_FILE *pxFile )
\r
1422 uint32_t ulItemLBA;
\r
1423 ulItemLBA = FF_Cluster2LBA( pxFile->pxIOManager, pxFile->ulAddrCurrentCluster );
\r
1424 ulItemLBA += FF_getMajorBlockNumber( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );
\r
1425 ulItemLBA = FF_getRealLBA( pxFile->pxIOManager, ulItemLBA );
\r
1426 ulItemLBA += FF_getMinorBlockNumber( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );
\r
1429 } /* FF_FileLBA() */
\r
1430 /*-----------------------------------------------------------*/
\r
1434 * @brief Depending on FilePointer, calculate CurrentCluster
\r
1435 * @brief and traverse the FAT to find the right ulAddrCurrentCluster
\r
1437 * @param pxFile The file handle
\r
1439 * @return FF_ERR_NONE on success
\r
1440 * @return Possible error returned by FF_TraverseFAT() or END_OF_DIR
\r
1443 * - pxFile->ulCurrentCluster : relative cluster number (0 <= Num < ulChainLength)
\r
1444 * - pxFile->ulAddrCurrentCluster : fysical cluster on the partition
\r
1446 static uint32_t FF_SetCluster( FF_FILE *pxFile, FF_Error_t *pxError )
\r
1448 FF_IOManager_t *pxIOManager = pxFile->pxIOManager;
\r
1449 uint32_t ulNewCluster = FF_getClusterChainNumber( pxIOManager, pxFile->ulFilePointer, 1 );
\r
1450 FF_Error_t xResult = FF_ERR_NONE;
\r
1451 uint32_t ulReturn;
\r
1453 if( ulNewCluster > pxFile->ulCurrentCluster )
\r
1455 FF_LockFAT( pxIOManager );
\r
1457 pxFile->ulAddrCurrentCluster = FF_TraverseFAT( pxIOManager, pxFile->ulAddrCurrentCluster,
\r
1458 ulNewCluster - pxFile->ulCurrentCluster, &xResult );
\r
1460 FF_UnlockFAT( pxIOManager );
\r
1462 else if( ulNewCluster < pxFile->ulCurrentCluster )
\r
1464 FF_LockFAT( pxIOManager );
\r
1466 pxFile->ulAddrCurrentCluster = FF_TraverseFAT( pxIOManager, pxFile->ulObjectCluster, ulNewCluster, &xResult );
\r
1468 FF_UnlockFAT( pxIOManager );
\r
1472 /* Well positioned. */
\r
1475 if( FF_isERR( xResult ) == pdFALSE )
\r
1477 pxFile->ulCurrentCluster = ulNewCluster;
\r
1478 ulReturn = FF_FileLBA( pxFile );
\r
1484 *pxError = xResult;
\r
1487 } /* FF_SetCluster() */
\r
1488 /*-----------------------------------------------------------*/
\r
1490 static int32_t FF_ReadPartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,
\r
1491 uint8_t *pucBuffer, FF_Error_t *pxError )
\r
1493 FF_Error_t xError = FF_ERR_NONE;
\r
1494 uint32_t ulBytesRead;
\r
1496 /* Bytes to read are within a block and less than a block size. */
\r
1497 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
1499 BaseType_t xLastRead;
\r
1501 /* Optimised method: each file handle holds one data block
\r
1502 in cache: 'pxFile->pucBuffer'. */
\r
1503 /* See if the current block will be accessed after this read: */
\r
1504 if( ( ulRelBlockPos + ulCount ) >= ( uint32_t ) pxFile->pxIOManager->usSectorSize )
\r
1506 /* After this read, ulFilePointer will point to the next block/sector. */
\r
1507 xLastRead = pdTRUE;
\r
1511 /* It is not the last read within this block/sector. */
\r
1512 xLastRead = pdFALSE;
\r
1515 if( ( pxFile->ucState & FF_BUFSTATE_VALID ) == 0 )
\r
1517 xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );
\r
1518 if( FF_isERR( xError ) == pdFALSE )
\r
1520 pxFile->ucState = FF_BUFSTATE_VALID;
\r
1524 if( ( pxFile->ucState & FF_BUFSTATE_VALID ) != 0 )
\r
1526 memcpy( pucBuffer, pxFile->pucBuffer + ulRelBlockPos, ulCount );
\r
1527 pxFile->ulFilePointer += ulCount;
\r
1528 ulBytesRead = ulCount;
\r
1529 if( ( xLastRead == pdTRUE ) && ( ( pxFile->ucState & FF_BUFSTATE_WRITTEN ) != 0 ) )
\r
1531 /* If the data was changed (file in 'update' mode), store the changes: */
\r
1532 xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );
\r
1537 ulBytesRead = 0ul;
\r
1539 if( xLastRead == pdTRUE )
\r
1541 /* As the next FF_Read() will go passed the current block, invalidate the buffer now. */
\r
1542 pxFile->ucState = FF_BUFSTATE_INVALID;
\r
1547 FF_Buffer_t *pxBuffer;
\r
1548 /* Reading in the standard way, using FF_Buffer_t. */
\r
1549 pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_READ );
\r
1550 if( pxBuffer == NULL )
\r
1552 xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_READ );
\r
1553 ulBytesRead = 0ul;
\r
1557 memcpy( pucBuffer, pxBuffer->pucBuffer + ulRelBlockPos, ulCount );
\r
1558 /* Releasing a buffer in FF_MODE_READ mode will not lead to an error,
\r
1559 because no disk access is needed. */
\r
1560 xError = FF_ReleaseBuffer( pxFile->pxIOManager, pxBuffer );
\r
1561 pxFile->ulFilePointer += ulCount;
\r
1562 ulBytesRead = ulCount;
\r
1567 *pxError = xError;
\r
1569 return ulBytesRead;
\r
1570 } /* FF_ReadPartial() */
\r
1571 /*-----------------------------------------------------------*/
\r
1575 * @brief Equivalent to fread()
\r
1577 * @param pxFile FF_FILE object that was created by FF_Open().
\r
1578 * @param ulElementSize The size of an element to read.
\r
1579 * @param ulCount The number of elements to read.
\r
1580 * @param buffer A pointer to a buffer of adequate size to be filled with the requested data.
\r
1582 * @return Number of bytes read.
\r
1584 * FF_Read() and FF_Write() work very similar. They both complete their task in 5 steps:
\r
1585 * 1. Read bytes up to a sector border: FF_ReadPartial()
\r
1586 * 2. Read sectors up to cluster border: FF_BlockRead()
\r
1587 * 3. Read complete clusters: FF_ReadClusters()
\r
1588 * 4. Read remaining sectors: FF_BlockRead()
\r
1589 * 5. Read remaining bytes: FF_ReadPartial()
\r
1591 int32_t FF_Read( FF_FILE *pxFile, uint32_t ulElementSize, uint32_t ulCount, uint8_t *pucBuffer )
\r
1593 uint32_t ulBytesLeft = ulElementSize * ulCount;
\r
1594 uint32_t ulBytesRead = 0;
\r
1595 uint32_t ulBytesToRead;
\r
1596 FF_IOManager_t *pxIOManager;
\r
1597 uint32_t ulRelBlockPos;
\r
1598 uint32_t ulItemLBA;
\r
1600 uint32_t ulSectors;
\r
1601 uint32_t ulRelClusterPos;
\r
1602 uint32_t ulBytesPerCluster;
\r
1603 FF_Error_t xError;
\r
1605 if( pxFile == NULL )
\r
1607 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_READ );
\r
1611 /* Check validity of the handle and the current position within the file. */
\r
1612 xError = FF_CheckValid( pxFile );
\r
1613 if( FF_isERR( xError ) == pdFALSE )
\r
1615 if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )
\r
1617 /* File was not opened with READ mode access. */
\r
1618 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_READ );
\r
1620 else if( pxFile->ulFilePointer >= pxFile->ulFileSize )
\r
1622 /* The end-of-file is reached. The error READ_ZERO will not be
\r
1623 returned, it is just used to avoid further processing. */
\r
1624 xError = ( FF_Error_t ) ( FF_ERR_FILE_READ_ZERO | FF_READ );
\r
1626 else if( ( pxFile->ulFilePointer + ulBytesLeft ) > pxFile->ulFileSize )
\r
1628 /* Note that many bytes can be read. */
\r
1629 ulBytesLeft = pxFile->ulFileSize - pxFile->ulFilePointer;
\r
1634 /* The file handle is not valid. */
\r
1636 } /* else pxFile != NULL */
\r
1638 if( FF_isERR( xError ) == pdFALSE )
\r
1640 pxIOManager = pxFile->pxIOManager;
\r
1642 /* And calculate the Logical Block Address. */
\r
1643 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
1645 /* Get the position within a block. */
\r
1646 ulRelBlockPos = FF_getMinorBlockEntry( pxIOManager, pxFile->ulFilePointer, 1 );
\r
1647 /* Open a do {} while( 0 ) loop to allow easy breaks: */
\r
1650 if( ( ulRelBlockPos + ulBytesLeft ) <= ( uint32_t ) pxIOManager->usSectorSize )
\r
1652 /*---------- A small read within the current block only. */
\r
1653 ulBytesRead = FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesLeft, pucBuffer, &xError );
\r
1656 /*---------- Read (memcpy) to a Sector Boundary. */
\r
1657 if( ulRelBlockPos != 0 )
\r
1659 /* Not on a sector boundary, at this point the LBA is known. */
\r
1660 ulBytesToRead = pxIOManager->usSectorSize - ulRelBlockPos;
\r
1661 ulBytesRead = FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesToRead, pucBuffer, &xError );
\r
1662 if( FF_isERR( xError ) )
\r
1666 ulBytesLeft -= ulBytesRead;
\r
1667 pucBuffer += ulBytesRead;
\r
1670 /*---------- Read sectors, up to a Cluster Boundary. */
\r
1671 ulBytesPerCluster = ( pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->usSectorSize );
\r
1672 ulRelClusterPos = pxFile->ulFilePointer % ( ulBytesPerCluster * pxIOManager->xPartition.ucBlkFactor );
\r
1674 if( ( ulRelClusterPos != 0 ) && ( ( ulRelClusterPos + ulBytesLeft ) >= ulBytesPerCluster ) )
\r
1676 /* Need to get to cluster boundary. */
\r
1677 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
1678 if( FF_isERR( xError ) )
\r
1682 ulSectors = pxIOManager->xPartition.ulSectorsPerCluster - ( ulRelClusterPos / pxIOManager->usSectorSize );
\r
1683 xError = FF_BlockRead( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );
\r
1684 if( FF_isERR( xError ) )
\r
1688 ulBytesToRead = ulSectors * pxIOManager->usSectorSize;
\r
1689 ulBytesLeft -= ulBytesToRead;
\r
1690 pucBuffer += ulBytesToRead;
\r
1691 ulBytesRead += ulBytesToRead;
\r
1692 pxFile->ulFilePointer += ulBytesToRead;
\r
1695 /*---------- Read entire clusters. */
\r
1696 if( ulBytesLeft >= ulBytesPerCluster )
\r
1698 uint32_t ulClusters;
\r
1700 FF_SetCluster( pxFile, &xError );
\r
1701 if( FF_isERR( xError ) )
\r
1706 ulClusters = ulBytesLeft / ulBytesPerCluster;
\r
1708 xError = FF_ReadClusters( pxFile, ulClusters, pucBuffer );
\r
1709 if( FF_isERR( xError ) )
\r
1713 ulBytesToRead = ulBytesPerCluster * ulClusters;
\r
1714 pxFile->ulFilePointer += ulBytesToRead;
\r
1715 ulBytesLeft -= ulBytesToRead;
\r
1716 pucBuffer += ulBytesToRead;
\r
1717 ulBytesRead += ulBytesToRead;
\r
1720 /*---------- Read Remaining Blocks. */
\r
1721 while( ulBytesLeft >= ( uint32_t ) pxIOManager->usSectorSize )
\r
1723 ulSectors = ulBytesLeft / pxIOManager->usSectorSize;
\r
1725 /* HT: I'd leave these pPart/ulOffset for readability */
\r
1726 /* and shorter code lines */
\r
1727 FF_Partition_t *pPart = &( pxIOManager->xPartition );
\r
1728 uint32_t ulOffset = ( pxFile->ulFilePointer / pxIOManager->usSectorSize ) % pPart->ulSectorsPerCluster;
\r
1729 uint32_t ulRemain = pPart->ulSectorsPerCluster - ulOffset;
\r
1730 if( ulSectors > ulRemain )
\r
1732 ulSectors = ulRemain;
\r
1736 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
1737 if( FF_isERR( xError ) )
\r
1741 xError = FF_BlockRead( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );
\r
1743 if( FF_isERR( xError ) )
\r
1747 ulBytesToRead = ulSectors * pxIOManager->usSectorSize;
\r
1748 pxFile->ulFilePointer += ulBytesToRead;
\r
1749 ulBytesLeft -= ulBytesToRead;
\r
1750 pucBuffer += ulBytesToRead;
\r
1751 ulBytesRead += ulBytesToRead;
\r
1754 /*---------- Read (memcpy) Remaining Bytes */
\r
1755 if( ulBytesLeft == 0 )
\r
1759 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
1760 if( FF_isERR( xError ) )
\r
1764 /* Bytes to read are within a block and less than a block size. */
\r
1765 FF_ReadPartial( pxFile, ulItemLBA, 0, ulBytesLeft, pucBuffer, &xError );
\r
1766 if( FF_isERR( xError ) == pdFALSE )
\r
1768 ulBytesRead += ulBytesLeft;
\r
1772 } /* if( FF_isERR( xError ) == pdFALSE ) */
\r
1774 if( FF_GETERROR( xError ) == FF_ERR_FILE_READ_ZERO )
\r
1778 else if( FF_isERR( xError ) )
\r
1784 lResult = ( int32_t )( ulBytesRead / ulElementSize );
\r
1789 /*-----------------------------------------------------------*/
\r
1793 * @brief Equivalent to fgetc()
\r
1795 * @param pxFile FF_FILE object that was created by FF_Open().
\r
1797 * @return The character that was read (cast as a 32-bit interger). -1 on EOF.
\r
1798 * @return FF_Error_t code. (Check with if(FF_isERR(xRetVal)) {}).
\r
1799 * @return -1 EOF (end of file).
\r
1802 int32_t FF_GetC( FF_FILE *pxFile )
\r
1804 uint32_t ulItemLBA;
\r
1805 uint8_t ucReturnedChar;
\r
1806 uint32_t ulRelBlockPos;
\r
1807 FF_Error_t xResult;
\r
1809 if( pxFile == NULL )
\r
1811 xResult = FF_ERR_NULL_POINTER | FF_GETC; /* Ensure this is a signed error. */
\r
1813 else if( ( pxFile->ucMode & FF_MODE_READ ) == 0 )
\r
1815 xResult = FF_ERR_FILE_NOT_OPENED_IN_READ_MODE | FF_GETC;
\r
1817 else if( pxFile->ulFilePointer >= pxFile->ulFileSize )
\r
1819 /* The end-of-file is reached. The error READ_ZERO will not be
\r
1820 returned, it is just used to avoid further processing. */
\r
1821 xResult = FF_ERR_FILE_READ_ZERO | FF_READ;
\r
1825 ulRelBlockPos = FF_getMinorBlockEntry( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );
\r
1827 ulItemLBA = FF_SetCluster( pxFile, &xResult );
\r
1828 if( FF_isERR( xResult ) == pdFALSE )
\r
1830 FF_ReadPartial( pxFile, ulItemLBA, ulRelBlockPos, 1, &ucReturnedChar, &xResult );
\r
1831 if( FF_isERR( xResult ) == pdFALSE )
\r
1833 xResult = ( int32_t ) ( ( uint32_t ) ucReturnedChar );
\r
1838 return ( int32_t ) xResult;
\r
1840 /*-----------------------------------------------------------*/
\r
1844 * @brief Gets a Line from a Text File, but no more than ulLimit characters. The line will be NULL terminated.
\r
1846 * The behaviour of this function is undefined when called on a binary file.
\r
1847 * It should just read in ulLimit bytes of binary, and ZERO terminate the line.
\r
1849 * This function works for both UNIX line feeds, and Windows CRLF type files.
\r
1851 * @param pxFile The FF_FILE object pointer.
\r
1852 * @param szLine The character buffer where the line should be stored.
\r
1853 * @param ulLimit This should be the max number of characters that szLine can hold.
\r
1855 * @return The number of characters read from the line, on success.
\r
1856 * @return 0 when no more lines are available, or when ulLimit is 0.
\r
1857 * @return FF_ERR_NULL_POINTER if pxFile or szLine are NULL;
\r
1860 int32_t FF_GetLine( FF_FILE *pxFile, char *pcLine, uint32_t ulLimit )
\r
1862 int32_t iChar = 0;
\r
1863 BaseType_t xIndex;
\r
1864 FF_Error_t xResult = FF_ERR_NONE;
\r
1866 if( ( pxFile == NULL ) || ( pcLine == NULL ) )
\r
1868 xResult = FF_ERR_NULL_POINTER | FF_GETLINE;
\r
1872 for( xIndex = 0; xIndex < ( BaseType_t ) ( ulLimit - 1 ); ++xIndex )
\r
1874 iChar = FF_GetC( pxFile );
\r
1876 if( FF_isERR( iChar ) == pdFALSE )
\r
1878 pcLine[ xIndex ] = ( char ) iChar;
\r
1880 if( iChar == '\n' )
\r
1882 /* Read until the first linefeed. Move xIndex forward so the
\r
1883 null terminator does not overwrite the \n. xIndex must be less
\r
1884 thank ( ulLimit - 1 ), so incrementing it here cannot make it
\r
1885 greater than ulLimit - 1, so the NULL can be inserted without
\r
1886 overflowing the buffer. */
\r
1893 if( ( FF_GETERROR( iChar ) == FF_ERR_FILE_READ_ZERO ) && ( xIndex > 0 ) )
\r
1895 /* Although FF_GetC() returns an End Of File,
\r
1896 the last few characters will be returned first. */
\r
1903 /* Make sure that the resulting string always ends with a zero: */
\r
1904 pcLine[ xIndex ] = '\0';
\r
1906 /*_RB_ In some paths this will be the second time FF_isERR() is called
\r
1907 on the same value. */
\r
1908 if( FF_isERR( iChar ) == pdFALSE )
\r
1910 /* Return the number of bytes read. */
\r
1915 /* Return iChar as an error code (see FF_GetC()). */
\r
1921 } /* FF_GetLine() */
\r
1922 /*-----------------------------------------------------------*/
\r
1924 static int32_t FF_WritePartial( FF_FILE *pxFile, uint32_t ulItemLBA, uint32_t ulRelBlockPos, uint32_t ulCount,
\r
1925 const uint8_t *pucBuffer, FF_Error_t *pxError )
\r
1927 FF_Error_t xError;
\r
1928 uint32_t ulBytesWritten;
\r
1930 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
1932 BaseType_t xLastRead;
\r
1933 if( ( ulRelBlockPos + ulCount ) >= ( uint32_t ) pxFile->pxIOManager->usSectorSize )
\r
1935 /* After this read, ulFilePointer will point to the next block/sector. */
\r
1936 xLastRead = pdTRUE;
\r
1940 /* It is not the last read within this block/sector. */
\r
1941 xLastRead = pdFALSE;
\r
1944 if( ( ( pxFile->ucState & FF_BUFSTATE_VALID ) == 0 ) &&
\r
1945 ( ( ulRelBlockPos != 0 ) || ( pxFile->ulFilePointer < pxFile->ulFileSize ) ) )
\r
1947 xError = FF_BlockRead( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );
\r
1948 /* pxFile->ucState will be set later on. */
\r
1952 xError = FF_ERR_NONE;
\r
1953 /* the buffer is valid or a whole block/sector will be written, so it is
\r
1954 not necessary to read the contents first. */
\r
1956 if( FF_isERR( xError ) == pdFALSE )
\r
1958 memcpy( pxFile->pucBuffer + ulRelBlockPos, pucBuffer, ulCount );
\r
1959 if( xLastRead == pdTRUE )
\r
1961 xError = FF_BlockWrite( pxFile->pxIOManager, ulItemLBA, 1, pxFile->pucBuffer, pdFALSE );
\r
1962 pxFile->ucState = FF_BUFSTATE_INVALID;
\r
1966 pxFile->ucState |= FF_BUFSTATE_WRITTEN | FF_BUFSTATE_VALID;
\r
1971 pxFile->ucState = FF_BUFSTATE_INVALID;
\r
1976 FF_Buffer_t *pxBuffer;
\r
1977 if( ( ulRelBlockPos == 0 ) && ( pxFile->ulFilePointer >= pxFile->ulFileSize ) )
\r
1979 /* An entire sector will be written. */
\r
1980 pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_WR_ONLY );
\r
1984 /* A partial write will be done, make sure to read the contents before
\r
1985 changing anything. */
\r
1986 pxBuffer = FF_GetBuffer( pxFile->pxIOManager, ulItemLBA, FF_MODE_WRITE );
\r
1989 if( pxBuffer == NULL )
\r
1991 xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_WRITE );
\r
1995 /* Here we copy to the sector boundary. */
\r
1996 memcpy( ( pxBuffer->pucBuffer + ulRelBlockPos ), pucBuffer, ulCount );
\r
1998 xError = FF_ReleaseBuffer( pxFile->pxIOManager, pxBuffer );
\r
2002 if( FF_isERR( xError ) == pdFALSE )
\r
2004 pxFile->ulFilePointer += ulCount;
\r
2005 ulBytesWritten = ulCount;
\r
2007 if( pxFile->ulFilePointer > pxFile->ulFileSize )
\r
2009 pxFile->ulFileSize = pxFile->ulFilePointer;
\r
2014 ulBytesWritten = 0ul;
\r
2016 *pxError = xError;
\r
2018 return ulBytesWritten;
\r
2019 } /* FF_WritePartial() */
\r
2020 /*-----------------------------------------------------------*/
\r
2024 * @brief Writes data to a File.
\r
2026 * @param pxFile FILE Pointer.
\r
2027 * @param ulElementSize Size of an Element of Data to be copied. (in bytes).
\r
2028 * @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
2029 * @param pucBuffer Byte-wise pucBuffer containing the data to be written.
\r
2031 * FF_Read() and FF_Write() work very similar. They both complete their task in 5 steps:
\r
2032 * 1. Write bytes up to a sector border: FF_WritePartial()
\r
2033 * 2. Write sectors up to cluster border: FF_BlockWrite()
\r
2034 * 3. Write complete clusters: FF_WriteClusters()
\r
2035 * 4. Write remaining sectors: FF_BlockWrite()
\r
2036 * 5. Write remaining bytes: FF_WritePartial()
\r
2039 int32_t FF_Write( FF_FILE *pxFile, uint32_t ulElementSize, uint32_t ulCount, uint8_t *pucBuffer )
\r
2041 uint32_t ulBytesLeft = ulElementSize * ulCount;
\r
2042 uint32_t nBytesWritten = 0;
\r
2043 uint32_t nBytesToWrite;
\r
2044 FF_IOManager_t *pxIOManager;
\r
2045 uint32_t ulRelBlockPos;
\r
2046 uint32_t ulItemLBA;
\r
2048 uint32_t ulSectors;
\r
2049 uint32_t ulRelClusterPos;
\r
2050 uint32_t ulBytesPerCluster;
\r
2051 FF_Error_t xError;
\r
2053 if( pxFile == NULL )
\r
2055 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_READ );
\r
2059 /* Check validity of the handle and the current position within the file. */
\r
2060 xError = FF_CheckValid( pxFile );
\r
2061 if( FF_isERR( xError ) == pdFALSE )
\r
2063 if( ( pxFile->ucMode & FF_MODE_WRITE ) == 0 )
\r
2065 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_WRITE );
\r
2067 /* Make sure a write is after the append point. */
\r
2068 else if( ( pxFile->ucMode & FF_MODE_APPEND ) != 0 )
\r
2070 if( pxFile->ulFilePointer < pxFile->ulFileSize )
\r
2072 xError = FF_Seek( pxFile, 0, FF_SEEK_END );
\r
2078 if( FF_isERR( xError ) == pdFALSE )
\r
2080 pxIOManager = pxFile->pxIOManager;
\r
2082 /* Open a do{} while( 0 ) loop to allow the use of breaks */
\r
2085 /* Extend File for at least ulBytesLeft!
\r
2086 Handle file-space allocation
\r
2087 + 1 byte because the code assumes there is always a next cluster */
\r
2088 xError = FF_ExtendFile( pxFile, pxFile->ulFilePointer + ulBytesLeft + 1 );
\r
2089 if( FF_isERR( xError ) )
\r
2091 /* On every error, break from the while( 0 ) loop. */
\r
2095 ulRelBlockPos = FF_getMinorBlockEntry( pxIOManager, pxFile->ulFilePointer, 1 ); /* Get the position within a block. */
\r
2096 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
2097 if( FF_isERR( xError ) )
\r
2102 if( ( ulRelBlockPos + ulBytesLeft ) <= ( uint32_t ) pxIOManager->usSectorSize )
\r
2104 /* Bytes to write are within a block and and do not go passed the current block. */
\r
2105 nBytesWritten = FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, ulBytesLeft, pucBuffer, &xError );
\r
2109 /*---------- Write (memcpy) to a Sector Boundary. */
\r
2110 if( ulRelBlockPos != 0 )
\r
2112 /* Not writing on a sector boundary, at this point the LBA is known. */
\r
2113 nBytesToWrite = pxIOManager->usSectorSize - ulRelBlockPos;
\r
2114 nBytesWritten = FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, nBytesToWrite, pucBuffer, &xError );
\r
2115 if( FF_isERR( xError ) )
\r
2120 ulBytesLeft -= nBytesWritten;
\r
2121 pucBuffer += nBytesWritten;
\r
2124 /*---------- Write sectors, up to a Cluster Boundary. */
\r
2125 ulBytesPerCluster = ( pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->usSectorSize );
\r
2126 ulRelClusterPos = FF_getClusterPosition( pxIOManager, pxFile->ulFilePointer, 1 );
\r
2128 if( ( ulRelClusterPos != 0 ) && ( ( ulRelClusterPos + ulBytesLeft ) >= ulBytesPerCluster ) )
\r
2130 /* Need to get to cluster boundary */
\r
2131 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
2132 if( FF_isERR( xError ) )
\r
2137 ulSectors = pxIOManager->xPartition.ulSectorsPerCluster - ( ulRelClusterPos / pxIOManager->usSectorSize );
\r
2138 xError = FF_BlockWrite( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );
\r
2139 if( FF_isERR( xError ) )
\r
2144 nBytesToWrite = ulSectors * pxIOManager->usSectorSize;
\r
2145 ulBytesLeft -= nBytesToWrite;
\r
2146 pucBuffer += nBytesToWrite;
\r
2147 nBytesWritten += nBytesToWrite;
\r
2148 pxFile->ulFilePointer += nBytesToWrite;
\r
2149 if( pxFile->ulFilePointer > pxFile->ulFileSize )
\r
2151 pxFile->ulFileSize = pxFile->ulFilePointer;
\r
2155 /*---------- Write entire Clusters. */
\r
2156 if( ulBytesLeft >= ulBytesPerCluster )
\r
2158 uint32_t ulClusters;
\r
2160 FF_SetCluster( pxFile, &xError );
\r
2161 if( FF_isERR( xError ) )
\r
2166 ulClusters = ( ulBytesLeft / ulBytesPerCluster );
\r
2168 xError = FF_WriteClusters( pxFile, ulClusters, pucBuffer );
\r
2169 if( FF_isERR( xError ) )
\r
2174 nBytesToWrite = ulBytesPerCluster * ulClusters;
\r
2175 ulBytesLeft -= nBytesToWrite;
\r
2176 pucBuffer += nBytesToWrite;
\r
2177 nBytesWritten += nBytesToWrite;
\r
2178 pxFile->ulFilePointer += nBytesToWrite;
\r
2179 if( pxFile->ulFilePointer > pxFile->ulFileSize )
\r
2181 pxFile->ulFileSize = pxFile->ulFilePointer;
\r
2185 /*---------- Write Remaining Blocks */
\r
2186 while( ulBytesLeft >= ( uint32_t ) pxIOManager->usSectorSize )
\r
2188 ulSectors = ulBytesLeft / pxIOManager->usSectorSize;
\r
2190 /* HT: I'd leave these pPart/ulOffset for readability... */
\r
2191 FF_Partition_t *pPart = &( pxIOManager->xPartition );
\r
2192 uint32_t ulOffset = ( pxFile->ulFilePointer / pxIOManager->usSectorSize ) % pPart->ulSectorsPerCluster;
\r
2193 uint32_t ulRemain = pPart->ulSectorsPerCluster - ulOffset;
\r
2194 if( ulSectors > ulRemain )
\r
2196 ulSectors = ulRemain;
\r
2200 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
2201 if( FF_isERR( xError ) )
\r
2206 xError = FF_BlockWrite( pxIOManager, ulItemLBA, ulSectors, pucBuffer, pdFALSE );
\r
2207 if( FF_isERR( xError ) )
\r
2212 nBytesToWrite = ulSectors * pxIOManager->usSectorSize;
\r
2213 ulBytesLeft -= nBytesToWrite;
\r
2214 pucBuffer += nBytesToWrite;
\r
2215 nBytesWritten += nBytesToWrite;
\r
2216 pxFile->ulFilePointer += nBytesToWrite;
\r
2217 if( pxFile->ulFilePointer > pxFile->ulFileSize )
\r
2219 pxFile->ulFileSize = pxFile->ulFilePointer;
\r
2223 /*---------- Write (memcpy) Remaining Bytes */
\r
2224 if( ulBytesLeft == 0 )
\r
2229 ulItemLBA = FF_SetCluster( pxFile, &xError );
\r
2230 if( FF_isERR( xError ) )
\r
2234 FF_WritePartial( pxFile, ulItemLBA, 0, ulBytesLeft, pucBuffer, &xError );
\r
2235 nBytesWritten += ulBytesLeft;
\r
2240 if( FF_isERR( xError ) )
\r
2246 lResult = ( int32_t )( nBytesWritten / ulElementSize );
\r
2250 } /* FF_Write() */
\r
2251 /*-----------------------------------------------------------*/
\r
2255 * @brief Writes a char to a FILE.
\r
2257 * @param pxFile FILE Pointer.
\r
2258 * @param ucValue Char to be placed in the file.
\r
2260 * @return Returns the value written to the file, or a value less than 0.
\r
2263 int32_t FF_PutC( FF_FILE *pxFile, uint8_t ucValue )
\r
2265 uint32_t ulItemLBA;
\r
2266 uint32_t ulRelBlockPos;
\r
2267 FF_Error_t xResult;
\r
2269 if( pxFile == NULL )
\r
2270 { /* Ensure we don't have a Null file pointer on a Public interface. */
\r
2271 xResult = FF_ERR_NULL_POINTER | FF_PUTC;
\r
2273 else if( ( pxFile->ucMode & FF_MODE_WRITE ) == 0 )
\r
2275 xResult = FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_PUTC;
\r
2279 xResult = FF_ERR_NONE;
\r
2282 /* Make sure a write is after the append point. */
\r
2283 if( ( pxFile->ucMode & FF_MODE_APPEND ) != 0 )
\r
2285 if( pxFile->ulFilePointer < pxFile->ulFileSize )
\r
2287 xResult = FF_Seek( pxFile, 0, FF_SEEK_END );
\r
2288 if( FF_isERR( xResult ) )
\r
2295 ulRelBlockPos = FF_getMinorBlockEntry( pxFile->pxIOManager, pxFile->ulFilePointer, 1 );
\r
2297 /* Handle File Space Allocation. */
\r
2298 /* We'll write 1 byte and always have a next cluster reserved. */
\r
2299 xResult = FF_ExtendFile( pxFile, pxFile->ulFilePointer + 2 );
\r
2300 if( FF_isERR( xResult ) )
\r
2305 ulItemLBA = FF_SetCluster( pxFile, &xResult );
\r
2306 if( FF_isERR( xResult ) )
\r
2310 FF_WritePartial( pxFile, ulItemLBA, ulRelBlockPos, 1, &ucValue, &xResult );
\r
2312 if( FF_isERR( xResult ) == pdFALSE )
\r
2314 xResult = ( FF_Error_t ) ucValue;
\r
2317 } while( pdFALSE );
\r
2322 /*-----------------------------------------------------------*/
\r
2326 * @brief Equivalent to fseek()
\r
2328 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2329 * @param ulOffset An integer (+/-) to seek to, from the specified origin.
\r
2330 * @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
2332 * @return 0 on Sucess,
\r
2333 * @return -2 if offset results in an invalid position in the file.
\r
2334 * @return FF_ERR_NULL_POINTER if a FF_FILE pointer was not received.
\r
2335 * @return -3 if an invalid origin was provided.
\r
2338 FF_Error_t FF_Seek( FF_FILE *pxFile, int32_t lOffset, BaseType_t xOrigin )
\r
2340 FF_Error_t xError;
\r
2341 uint32_t ulPosition = 0ul;
\r
2343 xError = FF_CheckValid( pxFile );
\r
2345 if( FF_isERR( xError ) == pdFALSE )
\r
2347 xError = FF_FlushCache( pxFile->pxIOManager );
\r
2349 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
2351 if( FF_isERR( xError ) == pdFALSE )
\r
2353 /* Here we must ensure that if the user tries to seek, and we had data in the file's
\r
2354 write buffer that this is written to disk. */
\r
2355 if( ( pxFile->ucState & FF_BUFSTATE_WRITTEN ) != 0 )
\r
2357 xError = FF_BlockWrite( pxFile->pxIOManager, FF_FileLBA( pxFile ), 1, pxFile->pucBuffer, pdFALSE );
\r
2359 pxFile->ucState = FF_BUFSTATE_INVALID;
\r
2362 #endif /* ffconfigOPTIMISE_UNALIGNED_ACCESS */
\r
2364 if( FF_isERR( xError ) == pdFALSE )
\r
2366 if( xOrigin == FF_SEEK_SET )
\r
2368 ulPosition = ( uint32_t )lOffset;
\r
2370 else if( xOrigin == FF_SEEK_CUR )
\r
2372 if( lOffset >= ( int32_t ) 0 )
\r
2374 ulPosition = pxFile->ulFilePointer + ( ( uint32_t ) lOffset );
\r
2378 ulPosition = pxFile->ulFilePointer - ( ( uint32_t ) ( -lOffset ) );
\r
2381 else if( xOrigin == FF_SEEK_END )
\r
2383 /* 'FF_SEEK_END' only allows zero or negative values. */
\r
2384 if( lOffset <= ( int32_t ) 0 )
\r
2386 ulPosition = pxFile->ulFileSize - ( ( uint32_t ) ( -lOffset ) );
\r
2391 xError = ( FF_Error_t ) ( FF_SEEK | FF_ERR_FILE_SEEK_INVALID_ORIGIN );
\r
2392 /* To supress a compiler warning. */
\r
2393 ulPosition = ( uint32_t ) 0u;
\r
2396 if( FF_isERR( xError ) == pdFALSE )
\r
2398 if( ulPosition <= ( uint32_t ) pxFile->ulFileSize )
\r
2400 if( ulPosition != ( uint32_t ) pxFile->ulFilePointer )
\r
2402 pxFile->ulFilePointer = ulPosition;
\r
2403 FF_SetCluster( pxFile, &xError );
\r
2408 xError = ( FF_Error_t ) ( FF_SEEK | FF_ERR_FILE_SEEK_INVALID_POSITION );
\r
2416 /*-----------------------------------------------------------*/
\r
2418 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
2421 * @brief Invalidate all file handles belonging to pxIOManager
\r
2423 * @param pIoMan FF_IOManager_t object that was created by FF_CreateIOManger().
\r
2425 * @return 0 if no handles were open
\r
2426 * @return >0 the amount of handles that were invalidated
\r
2427 * @return <0 probably an invalid FF_IOManager_t pointer
\r
2430 int32_t FF_Invalidate( FF_IOManager_t *pxIOManager )
\r
2433 FF_FILE *pxFileChain;
\r
2435 if( pxIOManager == NULL )
\r
2437 xResult = FF_ERR_NULL_POINTER | FF_INVALIDATE;
\r
2442 FF_PendSemaphore( pxIOManager->pvSemaphore );
\r
2444 pxIOManager->ucFlags |= FF_IOMAN_DEVICE_IS_EXTRACTED;
\r
2445 /* Semaphore is required, or linked list might change */
\r
2446 pxFileChain = ( FF_FILE * ) pxIOManager->FirstFile;
\r
2447 if( pxFileChain != NULL )
\r
2449 /* Count elements in FirstFile */
\r
2452 pxFileChain->ulValidFlags |= FF_VALID_FLAG_INVALID;
\r
2454 pxFileChain = pxFileChain->pxNext;
\r
2456 while( pxFileChain != NULL );
\r
2460 FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
\r
2464 } /* FF_Invalidate() */
\r
2465 #endif /* ffconfigREMOVABLE_MEDIA */
\r
2466 /*-----------------------------------------------------------*/
\r
2470 * @brief Check validity of file handle
\r
2472 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2474 * @return 0 on sucess.
\r
2475 * @return FF_ERR_NULL_POINTER if a null pointer was provided.
\r
2476 * @return FF_ERR_FILE_BAD_HANDLE if handle is not recognized
\r
2477 * @return FF_ERR_FILE_MEDIA_REMOVED please call FF_Close
\r
2480 FF_Error_t FF_CheckValid( FF_FILE *pxFile )
\r
2482 FF_FILE *pxFileChain;
\r
2483 FF_Error_t xError;
\r
2485 if( ( pxFile == NULL ) || ( pxFile->pxIOManager == NULL ) )
\r
2487 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_CHECKVALID );
\r
2491 FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2493 pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;
\r
2494 xError = ( FF_Error_t ) ( FF_ERR_FILE_BAD_HANDLE | FF_CHECKVALID );
\r
2495 while( pxFileChain != NULL )
\r
2497 if( pxFileChain == pxFile )
\r
2499 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
2500 if( ( pxFileChain->ulValidFlags & FF_VALID_FLAG_INVALID ) != 0 )
\r
2502 /* The medium has been removed while this file handle was open. */
\r
2503 xError = ( FF_Error_t ) ( FF_ERR_FILE_MEDIA_REMOVED | FF_CHECKVALID );
\r
2508 /* Found the handle, so it is a valid / existing handle. */
\r
2509 xError = FF_ERR_NONE;
\r
2515 pxFileChain = pxFileChain->pxNext;
\r
2518 FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2522 } /* FF_CheckValid() */
\r
2523 /*-----------------------------------------------------------*/
\r
2525 #if( ffconfigTIME_SUPPORT != 0 )
\r
2528 * @brief Set the time-stamp(s) of a file entry
\r
2530 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2531 * @param pxTime FF_SystemTime_t the time stamp
\r
2532 * @param uxWhat UBaseType_t a combination of enum ETimeMask
\r
2534 * @return 0 or FF_Error_t
\r
2537 FF_Error_t FF_SetFileTime( FF_FILE *pxFile, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )
\r
2539 FF_DirEnt_t xOriginalEntry;
\r
2540 FF_Error_t xError;
\r
2542 xError = FF_CheckValid( pxFile );
\r
2543 if( FF_isERR( xError ) == pdFALSE )
\r
2545 if( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED )
\r
2546 { /*if (pxFile->FileDeleted) */
\r
2547 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETFILETIME );
\r
2549 else if( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) == 0 )
\r
2551 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_SETFILETIME );
\r
2555 /* Update the Dirent! */
\r
2556 xError = FF_GetEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );
\r
2557 if( FF_isERR( xError ) == pdFALSE )
\r
2559 if( uxWhat & ETimeCreate )
\r
2561 xOriginalEntry.xCreateTime = *pxTime; /*/< Date and Time Created. */
\r
2564 if( uxWhat & ETimeMod )
\r
2566 xOriginalEntry.xModifiedTime = *pxTime; /*/< Date and Time Modified. */
\r
2569 if( uxWhat & ETimeAccess )
\r
2571 xOriginalEntry.xAccessedTime = *pxTime; /*/< Date of Last Access. */
\r
2574 xError = FF_PutEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );
\r
2577 if( FF_isERR( xError ) == pdFALSE )
\r
2579 xError = FF_FlushCache( pxFile->pxIOManager ); /* Ensure all modfied blocks are flushed to disk! */
\r
2585 } /* FF_SetFileTime() */
\r
2586 #endif /* ffconfigTIME_SUPPORT */
\r
2587 /*-----------------------------------------------------------*/
\r
2589 #if( ffconfigTIME_SUPPORT != 0 )
\r
2592 * @brief Set the time-stamp(s) of a file entry (by name)
\r
2594 * @param pxIOManager FF_IOManager_t device handle
\r
2595 * @param pcPath int8_t/FF_T_WCHAR name of the file
\r
2596 * @param pxTime FF_SystemTime_t the time stamp
\r
2597 * @param uxWhat UBaseType_t a combination of enum ETimeMask
\r
2599 * @return 0 or FF_Error_t
\r
2602 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2603 FF_Error_t FF_SetTime ( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )
\r
2605 FF_Error_t FF_SetTime ( FF_IOManager_t * pxIOManager, const char *pcPath, FF_SystemTime_t *pxTime, UBaseType_t uxWhat )
\r
2606 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
2608 FF_DirEnt_t xOriginalEntry;
\r
2609 FF_Error_t xError;
\r
2610 uint32_t ulFileCluster;
\r
2611 BaseType_t xIndex;
\r
2612 FF_FindParams_t xFindParams;
\r
2613 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2614 FF_T_WCHAR pcFileName[ffconfigMAX_FILENAME];
\r
2616 char pcFileName[ffconfigMAX_FILENAME];
\r
2617 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
2619 xIndex = ( BaseType_t ) STRLEN( pcPath );
\r
2621 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
2623 while( xIndex != 0 )
\r
2625 if( pcPath[ xIndex ] == '\\' || pcPath[ xIndex ] == '/' )
\r
2633 STRNCPY( pcFileName, ( pcPath + xIndex + 1 ), ffconfigMAX_FILENAME );
\r
2640 xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );
\r
2641 if( FF_isERR( xError ) == pdFALSE )
\r
2643 if( xFindParams.ulDirCluster == 0 )
\r
2645 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );
\r
2649 ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0, &xOriginalEntry, &xError );
\r
2650 if( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) )
\r
2652 if( ulFileCluster == 0ul )
\r
2654 /*FF_PRINTF ("FF_SetTime: Can not find '%s'\n", pcFileName); */
\r
2655 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );
\r
2661 if( FF_isERR( xError ) == pdFALSE )
\r
2663 /* Update the Dirent! */
\r
2664 if( uxWhat & ETimeCreate )
\r
2666 xOriginalEntry.xCreateTime = *pxTime; /*/< Date and Time Created. */
\r
2669 if( uxWhat & ETimeMod )
\r
2671 xOriginalEntry.xModifiedTime = *pxTime; /*/< Date and Time Modified. */
\r
2674 if( uxWhat & ETimeAccess )
\r
2676 xOriginalEntry.xAccessedTime = *pxTime; /*/< Date of Last Access. */
\r
2679 xError = FF_PutEntry( pxIOManager, xOriginalEntry.usCurrentItem - 1, xFindParams.ulDirCluster, &xOriginalEntry, NULL );
\r
2681 if( FF_isERR( xError ) == pdFALSE )
\r
2683 xError = FF_FlushCache( pxIOManager ); /* Ensure all modified blocks are flushed to disk! */
\r
2688 } /* FF_SetTime() */
\r
2689 #endif /* ffconfigTIME_SUPPORT */
\r
2690 /*-----------------------------------------------------------*/
\r
2692 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2693 FF_Error_t FF_SetPerm( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath, UBaseType_t aPerm )
\r
2695 FF_Error_t FF_SetPerm( FF_IOManager_t * pxIOManager, const char *pcPath, UBaseType_t aPerm )
\r
2698 FF_DirEnt_t xOriginalEntry;
\r
2699 FF_Error_t xError;
\r
2700 uint32_t ulFileCluster;
\r
2701 BaseType_t xIndex;
\r
2702 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2703 FF_T_WCHAR pcFileName[ffconfigMAX_FILENAME];
\r
2705 char pcFileName[ffconfigMAX_FILENAME];
\r
2707 FF_FindParams_t xFindParams;
\r
2709 xIndex = ( BaseType_t ) STRLEN( pcPath );
\r
2711 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
2713 while( xIndex != 0 )
\r
2715 if( pcPath[ xIndex ] == '\\' || pcPath[ xIndex ] == '/' )
\r
2723 STRNCPY( pcFileName, ( pcPath + xIndex + 1 ), ffconfigMAX_FILENAME );
\r
2730 /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */
\r
2733 xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );
\r
2739 if( !xFindParams.ulDirCluster )
\r
2741 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );
\r
2745 ulFileCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcFileName, 0, &xOriginalEntry, &xError );
\r
2746 if( FF_isERR( xError ) )
\r
2751 if( ulFileCluster == 0ul )
\r
2753 /*FF_PRINTF ("FF_SetTime: Can not find '%s'\n", pcFileName); */
\r
2754 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_FOUND | FF_SETTIME );
\r
2758 /* #define FF_FAT_ATTR_READONLY 0x01 */
\r
2759 /* #define FF_FAT_ATTR_HIDDEN 0x02 */
\r
2760 /* #define FF_FAT_ATTR_SYSTEM 0x04 */
\r
2761 /* #define FF_FAT_ATTR_VOLID 0x08 */
\r
2762 /* #define FF_FAT_ATTR_DIR 0x10 */
\r
2763 /* #define FF_FAT_ATTR_ARCHIVE 0x20 */
\r
2764 /* #define FF_FAT_ATTR_LFN 0x0F */
\r
2765 #define FF_FAT_ATTR_USER ( ( uint8_t ) FF_FAT_ATTR_READONLY | FF_FAT_ATTR_HIDDEN | FF_FAT_ATTR_SYSTEM | FF_FAT_ATTR_ARCHIVE )
\r
2766 /* Update the Dirent! */
\r
2767 xOriginalEntry.ucAttrib &= ~FF_FAT_ATTR_USER;
\r
2768 xOriginalEntry.ucAttrib |= ( aPerm & FF_FAT_ATTR_USER );
\r
2769 xError = FF_PutEntry( pxIOManager, xOriginalEntry.usCurrentItem - 1, xFindParams.ulDirCluster, &xOriginalEntry, NULL );
\r
2771 if( FF_isERR( xError ) == pdFALSE )
\r
2773 xError = FF_FlushCache( pxIOManager ); /* Ensure all modfied blocks are flushed to disk! */
\r
2779 } /* FF_SetPerm() */
\r
2780 /*-----------------------------------------------------------*/
\r
2784 * @brief Equivalent to fclose()
\r
2786 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2788 * @return 0 on sucess.
\r
2789 * @return -1 if a null pointer was provided.
\r
2792 FF_Error_t FF_Close( FF_FILE *pxFile )
\r
2794 FF_FILE *pxFileChain;
\r
2795 FF_DirEnt_t xOriginalEntry;
\r
2796 FF_Error_t xError;
\r
2798 /* Opening a do {} while( 0 ) loop to allow the use of the break statement. */
\r
2801 if( pxFile == NULL )
\r
2803 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_CLOSE );
\r
2807 /* It is important to check that user doesn't supply invalid
\r
2808 handle or a handle invalid because of "media removed" */
\r
2809 xError = FF_CheckValid( pxFile );
\r
2811 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
2813 if( FF_GETERROR( xError ) == FF_ERR_FILE_MEDIA_REMOVED )
\r
2815 FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2817 pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;
\r
2818 if( pxFileChain == pxFile )
\r
2820 pxFile->pxIOManager->FirstFile = pxFile->pxNext;
\r
2824 while( pxFileChain )
\r
2826 if( pxFileChain->pxNext == pxFile )
\r
2828 pxFileChain->pxNext = pxFile->pxNext;
\r
2832 pxFileChain = pxFileChain->pxNext; /* Forgot this one */
\r
2835 } /* Semaphore released, linked list was shortened! */
\r
2837 FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2838 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
2840 ffconfigFREE( pxFile->pucBuffer );
\r
2842 #endif /* ffconfigOPTIMISE_UNALIGNED_ACCESS */
\r
2843 ffconfigFREE( pxFile ); /* So at least we have freed the pointer. */
\r
2844 xError = FF_ERR_NONE;
\r
2848 #endif /* ffconfigREMOVABLE_MEDIA */
\r
2850 if( FF_isERR( xError ) )
\r
2852 /* FF_ERR_FILE_BAD_HANDLE or FF_ERR_NULL_POINTER */
\r
2856 /* So here we have a normal valid file handle. */
\r
2858 /* Sometimes FreeRTOS+FAT will leave a trailing cluster on the end of a cluster chain.
\r
2859 To ensure we're compliant we shall now check for this condition and truncate it. */
\r
2860 if( ( ( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED ) == 0 ) &&
\r
2861 ( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE ) ) != 0 ) )
\r
2863 uint32_t ulClusterSize;
\r
2865 /* File is not deleted and it was opened for writing or updating */
\r
2866 ulClusterSize = pxFile->pxIOManager->xPartition.usBlkSize * pxFile->pxIOManager->xPartition.ulSectorsPerCluster;
\r
2868 if( ( ( pxFile->ulFileSize % ulClusterSize ) == 0 ) && ( pxFile->ulObjectCluster != 0ul ) )
\r
2870 /* The file's length is a multiple of cluster size. This means
\r
2871 that an extra cluster has been reserved, which wasn't necessary. */
\r
2872 xError = FF_Truncate( pxFile, pdTRUE );
\r
2875 /* Get the directory entry and update it to show the new file size */
\r
2876 if( FF_isERR( xError ) == pdFALSE )
\r
2878 xError = FF_GetEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );
\r
2880 /* Now update the directory entry */
\r
2881 if( ( FF_isERR( xError ) == pdFALSE ) &&
\r
2882 ( ( pxFile->ulFileSize != xOriginalEntry.ulFileSize ) || ( pxFile->ulFileSize == 0UL ) ) )
\r
2884 if( pxFile->ulFileSize == 0UL )
\r
2886 xOriginalEntry.ulObjectCluster = 0;
\r
2889 xOriginalEntry.ulFileSize = pxFile->ulFileSize;
\r
2890 xError = FF_PutEntry( pxFile->pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );
\r
2895 /* Handle Linked list! */
\r
2896 FF_PendSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2897 { /* Semaphore is required, or linked list could become corrupted. */
\r
2898 pxFileChain = ( FF_FILE * ) pxFile->pxIOManager->FirstFile;
\r
2899 if( pxFileChain == pxFile )
\r
2901 pxFile->pxIOManager->FirstFile = pxFile->pxNext;
\r
2905 while( pxFileChain )
\r
2907 if( pxFileChain->pxNext == pxFile )
\r
2909 /* Found it, remove it from the list. */
\r
2910 pxFileChain->pxNext = pxFile->pxNext;
\r
2913 pxFileChain = pxFileChain->pxNext;
\r
2916 } /* Semaphore released, linked list was shortened! */
\r
2917 FF_ReleaseSemaphore( pxFile->pxIOManager->pvSemaphore );
\r
2919 #if( ffconfigOPTIMISE_UNALIGNED_ACCESS != 0 )
\r
2921 if( pxFile->pucBuffer != NULL )
\r
2923 /* Ensure any unaligned points are pushed to the disk! */
\r
2924 if( pxFile->ucState & FF_BUFSTATE_WRITTEN )
\r
2926 FF_Error_t xTempError;
\r
2928 xTempError = FF_BlockWrite( pxFile->pxIOManager, FF_FileLBA( pxFile ), 1, pxFile->pucBuffer, pdFALSE );
\r
2929 if( FF_isERR( xError ) == pdFALSE )
\r
2931 xError = xTempError;
\r
2935 ffconfigFREE( pxFile->pucBuffer );
\r
2939 if( FF_isERR( xError ) == pdFALSE )
\r
2941 xError = FF_FlushCache( pxFile->pxIOManager ); /* Ensure all modified blocks are flushed to disk! */
\r
2943 ffconfigFREE( pxFile );
\r
2948 } /* FF_Close() */
\r
2949 /*-----------------------------------------------------------*/
\r
2953 * @brief Make Filesize equal to the FilePointer and truncates the file to this position
\r
2955 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2957 * @return 0 on sucess.
\r
2958 * @return negative if some error occurred
\r
2961 FF_Error_t FF_SetEof( FF_FILE *pxFile )
\r
2963 FF_Error_t xError;
\r
2965 /* Check if the file was not deleted and if it was opened with write permissions: */
\r
2966 if( ( ( pxFile->ulValidFlags & FF_VALID_FLAG_DELETED ) == 0 ) &&
\r
2967 ( ( pxFile->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE ) ) != 0 ) )
\r
2969 pxFile->ulFileSize = pxFile->ulFilePointer;
\r
2970 if( pxFile->ulObjectCluster != 0ul )
\r
2972 xError = FF_Truncate( pxFile, pdFALSE );
\r
2976 xError = FF_ERR_NONE;
\r
2981 xError = ( FF_Error_t ) ( FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE | FF_SETEOF );
\r
2985 } /* FF_SetEof() */
\r
2986 /*-----------------------------------------------------------*/
\r
2990 * @brief Truncate a file to 'pxFile->ulFileSize'
\r
2992 * @param pxFile FF_FILE object that was created by FF_Open().
\r
2994 * @return 0 on sucess.
\r
2995 * @return negative if some error occurred
\r
2998 static FF_Error_t FF_Truncate( FF_FILE *pxFile, BaseType_t bClosing )
\r
3000 FF_Error_t xError;
\r
3001 FF_IOManager_t *pxIOManager = pxFile->pxIOManager;
\r
3003 uint32_t ulClusterSize;
\r
3004 uint32_t ulClusterCount;
\r
3005 uint32_t ulClustersNeeded;
\r
3007 /* The number of bytes contained in a cluster. */
\r
3008 ulClusterSize = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster;
\r
3010 /* See how many clusters have been allocated. */
\r
3011 ulClusterCount = FF_GetChainLength( pxIOManager, pxFile->ulObjectCluster, NULL, &xError );
\r
3013 /* Calculate the actual number of clusters needed, rounding up */
\r
3014 ulClustersNeeded = ( pxFile->ulFileSize + ulClusterSize - 1 ) / ulClusterSize;
\r
3015 if( bClosing != pdFALSE )
\r
3017 /* The handle will be closed after truncating. This function is called
\r
3018 because Filesize is an exact multiple of ulClusterSize. */
\r
3019 ulClustersNeeded = pxFile->ulFileSize / ulClusterSize;
\r
3023 /* This function is called to make the file size equal to the current
\r
3024 position within the file. Always keep an extra cluster to write to. */
\r
3025 ulClustersNeeded = ( pxFile->ulFileSize + ulClusterSize ) / ulClusterSize;
\r
3028 /* First change the FAT chain. */
\r
3029 if( ( FF_isERR( xError ) == pdFALSE ) && ( ulClusterCount > ulClustersNeeded ) )
\r
3031 if( ulClustersNeeded == 0ul )
\r
3033 FF_LockFAT( pxIOManager );
\r
3035 /* In FF_Truncate() */
\r
3036 xError = FF_UnlinkClusterChain( pxIOManager, pxFile->ulObjectCluster, 0 );
\r
3038 FF_UnlockFAT( pxIOManager );
\r
3040 if( FF_isERR( xError ) == pdFALSE )
\r
3042 FF_DirEnt_t xOriginalEntry;
\r
3044 /* The directory denotes the address of the first data cluster of every file.
\r
3045 Now change it to 'ulAddrCurrentCluster': */
\r
3046 xError = FF_GetEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry );
\r
3048 if( FF_isERR( xError ) == pdFALSE )
\r
3050 xOriginalEntry.ulObjectCluster = 0ul;
\r
3051 xError = FF_PutEntry( pxIOManager, pxFile->usDirEntry, pxFile->ulDirCluster, &xOriginalEntry, NULL );
\r
3053 if( FF_isERR( xError ) == pdFALSE )
\r
3055 pxFile->ulObjectCluster = 0ul;
\r
3056 pxFile->ulChainLength = 0ul;
\r
3057 pxFile->ulCurrentCluster = 0ul;
\r
3058 pxFile->ulEndOfChain = 0ul;
\r
3065 FF_LockFAT( pxIOManager );
\r
3067 uint32_t ulTruncateCluster = FF_TraverseFAT( pxIOManager, pxFile->ulObjectCluster, ulClustersNeeded - 1, &xError );
\r
3069 if( FF_isERR( xError ) == pdFALSE )
\r
3071 xError = FF_UnlinkClusterChain( pxIOManager, ulTruncateCluster, 1 );
\r
3074 FF_UnlockFAT( pxIOManager );
\r
3079 } /* FF_Truncate() */
\r
3080 /*-----------------------------------------------------------*/
\r