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
27 /* FreeRTOS includes. */
\r
28 #include "FreeRTOS.h"
\r
30 #include "portable.h"
\r
32 /* FreeRTOS+FAT includes. */
\r
33 #include "ff_headers.h"
\r
34 #include "ff_stdio.h"
\r
36 #if( ffconfigTIME_SUPPORT != 0 )
\r
42 #define SIZE_MAX ( ( size_t ) -1 )
\r
45 /* The number of bytes to write at a time when extending the length of a file
\r
46 in a call to ff_truncate(). */
\r
47 #define stdioTRUNCATE_WRITE_LENGTH 512
\r
49 /* Bits set to indicate whether ".." should be included as well as ".". */
\r
50 #define stdioDIR_ENTRY_DOT_1 ( 1U & 0x03U )
\r
51 #define stdioDIR_ENTRY_DOT_2 ( 2U & 0x03U )
\r
53 /* The directory entries '.' and '..' will show a file size of 1 KB. */
\r
54 #define stdioDOT_ENTRY_FILE_SIZE 1024
\r
56 /*-----------------------------------------------------------*/
\r
58 #if( ffconfigHAS_CWD == 1 )
\r
60 /* FreeRTOS+FAT requires two thread local storage pointers. One for errno
\r
61 and one for the CWD structure. */
\r
62 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )
\r
63 #error FreeRTOS+FAT requires two thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 2 in FreeRTOSConfig.h
\r
66 /* Each task has its own Current Working Directory (CWD). The CWD is used
\r
67 to extend relative paths to absolute paths. */
\r
68 typedef struct WORKING_DIR
\r
70 char pcCWD[ ffconfigMAX_FILENAME ]; /* The current working directory. */
\r
71 char pcFileName[ ffconfigMAX_FILENAME ]; /* The created absolute path. */
\r
72 } WorkingDirectory_t;
\r
75 * Add the CWD to the beginning of a relative path, and copy the resultant
\r
76 * absolute path into a thread local non const buffer.
\r
78 /*static*/ const char *prvABSPath( const char *pcPath );
\r
81 * Lookup the CWD of the current task.
\r
83 static WorkingDirectory_t *pxFindCWD( void );
\r
86 * Convert a string which may contain a relative path into a string that
\r
87 * will only contain an absolute path.
\r
89 static const char *prvProcessRelativePaths( const char *pcPath );
\r
91 #else /* ffconfigHAS_CWD */
\r
93 /* FreeRTOS+FAT requires one thread local storage pointers for errno. */
\r
94 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )
\r
95 #error FreeRTOS+FAT requires one thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 1 in FreeRTOSConfig.h
\r
98 /* Only absolute paths are supported so define away the prvABSPath()
\r
100 /*static*/ const char *prvABSPath( const char *pcPath )
\r
105 #endif /* ffconfigHAS_CWD */
\r
108 #if( ffconfigUSE_DELTREE != 0 )
\r
110 * Remove all files and directories starting from a certain path.
\r
111 * This function uses recursion - which breaches the coding standard. USE
\r
114 static int ff_deltree_recurse( char *pcPath );
\r
118 * Translate a +FAT error to a value compatible with errno.h
\r
119 * If the value represents an error, it is negative
\r
120 * The return value of this function will always be positive
\r
122 int prvFFErrorToErrno( FF_Error_t xError );
\r
125 * Generate a time stamp for the file.
\r
127 #if( ffconfigTIME_SUPPORT == 1 )
\r
128 static uint32_t prvFileTime( FF_SystemTime_t *pxTime );
\r
131 /*-----------------------------------------------------------*/
\r
133 FF_FILE *ff_fopen( const char *pcFile, const char *pcMode )
\r
135 FF_FILE *pxStream = NULL;
\r
136 FF_DirHandler_t xHandler;
\r
140 /* Insert the current working directory in front of relative paths. */
\r
141 pcFile = prvABSPath( pcFile );
\r
143 /* Look-up the I/O manager for the file system. */
\r
144 if( FF_FS_Find( pcFile, &xHandler ) == pdFALSE )
\r
146 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO ); /* No such device or address. */
\r
150 /* Now 'xHandler.pcPath' contains an absolute path within the file system.
\r
151 Translate a type string "r|w|a[+]" to +FAT's mode bits. */
\r
152 ucMode = FF_GetModeBits( pcMode );
\r
154 pxStream = FF_Open( xHandler.pxManager, xHandler.pcPath, ucMode, &xError );
\r
155 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
\r
157 #if( ffconfigUSE_NOTIFY != 0 )
\r
159 if( ( pxStream != NULL ) && ( ( ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 ) )
\r
161 /*_RB_ Function name needs updating. */
\r
162 callFileEvents( pcFile, eFileCreate );
\r
165 #endif /* ffconfigUSE_NOTIFY */
\r
167 #if( ffconfigDEV_SUPPORT != 0 )
\r
169 if( pxStream != NULL )
\r
171 FF_Device_Open( pcFile, pxStream );
\r
174 #endif /* ffconfigDEV_SUPPORT */
\r
179 /*-----------------------------------------------------------*/
\r
181 int ff_fclose( FF_FILE *pxStream )
\r
184 int iReturn, ff_errno;
\r
186 #if( ffconfigDEV_SUPPORT != 0 )
\r
188 /* Currently device support is in an experimental state. It will allow
\r
189 to create virtual files. The I/O data to those files will be redirected
\r
190 to their connected "drivers". */
\r
191 if( pxStream != NULL )
\r
193 FF_Device_Close( pxStream );
\r
198 xError = FF_Close( pxStream );
\r
199 ff_errno = prvFFErrorToErrno( xError );
\r
201 if( ff_errno == 0 )
\r
207 /* Return -1 for error as per normal fclose() semantics. */
\r
211 /* Store the errno to thread local storage. */
\r
212 stdioSET_ERRNO( ff_errno );
\r
216 /*-----------------------------------------------------------*/
\r
218 int ff_fseek( FF_FILE *pxStream, long lOffset, int iWhence )
\r
221 int iReturn, ff_errno;
\r
223 #if( ffconfigDEV_SUPPORT != 0 )
\r
224 if( pxStream->pxDevNode != NULL )
\r
226 xError = FF_Device_Seek( pxStream, lOffset, iWhence );
\r
231 xError = FF_Seek( pxStream, lOffset, iWhence );
\r
234 ff_errno = prvFFErrorToErrno( xError );
\r
236 if( ff_errno == 0 )
\r
242 if( xError == FF_ERR_FILE_SEEK_INVALID_POSITION )
\r
244 /* Illegal position, outside the file's space */
\r
245 ff_errno = pdFREERTOS_ERRNO_ESPIPE;
\r
247 else if( xError == FF_ERR_FILE_SEEK_INVALID_ORIGIN )
\r
249 /* Illegal parameter value for iWhence: SET,CUR,END. */
\r
250 ff_errno = pdFREERTOS_ERRNO_EINVAL;
\r
253 /* Return -1 for error as per normal fseek() semantics. */
\r
257 /* Store the errno to thread local storage. */
\r
258 stdioSET_ERRNO( ff_errno );
\r
262 /*-----------------------------------------------------------*/
\r
264 void ff_rewind( FF_FILE *pxStream )
\r
266 ff_fseek( pxStream, 0, FF_SEEK_SET );
\r
268 /* Rewind is supposed to reset errno unconditionally. Store the errno to
\r
269 thread local storage. */
\r
270 stdioSET_ERRNO( 0 );
\r
272 /*-----------------------------------------------------------*/
\r
274 long ff_ftell( FF_FILE *pxStream )
\r
278 if( pxStream == NULL )
\r
280 /* Store the errno to thread local storage. */
\r
281 stdioSET_ERRNO( pdFREERTOS_ERRNO_EBADF );
\r
283 /* Return -1 for error as per normal ftell() semantics. */
\r
288 lResult = ( long ) pxStream->ulFilePointer;
\r
293 /*-----------------------------------------------------------*/
\r
295 int ff_feof( FF_FILE *pxStream )
\r
300 xError = FF_CheckValid( pxStream );
\r
301 if( FF_isERR( xError ) == pdFALSE )
\r
303 /* Store the errno to thread local storage. */
\r
304 stdioSET_ERRNO( 0 );
\r
305 if( pxStream->ulFilePointer >= pxStream->ulFileSize )
\r
316 /* Store the errno to thread local storage. */
\r
317 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
\r
319 /* The file was invalid so a non-zero value cannot be returned. */
\r
325 /*-----------------------------------------------------------*/
\r
327 size_t ff_fread( void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream )
\r
333 #if( ffconfigDEV_SUPPORT != 0 )
\r
334 if( pxStream->pxDevNode != NULL )
\r
336 iReturned = FF_Device_Read( pvBuffer, xSize, xItems, pxStream );
\r
341 iReturned = FF_Read( pxStream, xSize, xItems, (uint8_t *)pvBuffer );
\r
344 ff_errno = prvFFErrorToErrno( iReturned );
\r
346 if( ff_errno == pdFREERTOS_ERRNO_NONE )
\r
348 /* As per the standard fread() semantics, the return value is the number
\r
349 of complete items read, which will only equal the number of bytes
\r
350 transferred when the item size is 1. */
\r
351 xReturn = ( size_t ) iReturned;
\r
358 /* Store the errno to thread local storage. */
\r
359 stdioSET_ERRNO( ff_errno );
\r
363 /*-----------------------------------------------------------*/
\r
365 size_t ff_fwrite( const void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream )
\r
371 #if( ffconfigDEV_SUPPORT != 0 )
\r
372 if( pxStream->pxDevNode != NULL )
\r
374 iReturned = FF_Device_Write( pvBuffer, xSize, xItems, pxStream );
\r
379 iReturned = FF_Write( pxStream, xSize, xItems, (uint8_t *)pvBuffer );
\r
382 ff_errno = prvFFErrorToErrno( iReturned );
\r
384 if( ff_errno == pdFREERTOS_ERRNO_NONE )
\r
386 /* As per the standard fwrite() semantics, the return value is the
\r
387 number of complete items read, which will only equal the number of bytes
\r
388 transferred when the item size is 1. */
\r
389 xReturn = ( size_t ) iReturned;
\r
396 /* Store the errno to thread local storage. */
\r
397 stdioSET_ERRNO( ff_errno );
\r
401 /*-----------------------------------------------------------*/
\r
403 int ff_fgetc( FF_FILE * pxStream )
\r
408 iResult = FF_GetC( pxStream );
\r
409 ff_errno = prvFFErrorToErrno( iResult );
\r
411 if( ff_errno != 0 )
\r
416 /* Store the errno to thread local storage. */
\r
417 stdioSET_ERRNO( ff_errno );
\r
421 /*-----------------------------------------------------------*/
\r
423 int ff_fputc( int iChar, FF_FILE *pxStream )
\r
425 int iResult, ff_errno;
\r
427 iResult = FF_PutC( pxStream, ( uint8_t ) iChar );
\r
428 ff_errno = prvFFErrorToErrno( iResult );
\r
430 if( ff_errno != 0 )
\r
435 /* Store the errno to thread local storage. */
\r
436 stdioSET_ERRNO( ff_errno );
\r
440 /*-----------------------------------------------------------*/
\r
442 #if( ffconfigFPRINTF_SUPPORT == 1 )
\r
444 int ff_fprintf( FF_FILE * pxStream, const char *pcFormat, ... )
\r
451 pcBuffer = ( char * ) ffconfigMALLOC( ffconfigFPRINTF_BUFFER_LENGTH );
\r
452 if( pcBuffer == NULL )
\r
454 /* Store the errno to thread local storage. */
\r
455 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
\r
460 va_start( xArgs, pcFormat );
\r
461 iCount = vsnprintf( pcBuffer, ffconfigFPRINTF_BUFFER_LENGTH, pcFormat, xArgs );
\r
464 /* ff_fwrite() will set ff_errno. */
\r
467 xResult = ff_fwrite( pcBuffer, ( size_t ) 1, ( size_t ) iCount, pxStream );
\r
468 if( xResult < ( size_t ) iCount )
\r
474 ffconfigFREE( pcBuffer );
\r
481 /*-----------------------------------------------------------*/
\r
483 /*_RB_ to comply with the norm, the second parameter should be an int, but size_t
\r
484 is more appropriate. */
\r
485 char *ff_fgets( char *pcBuffer, size_t xCount, FF_FILE *pxStream )
\r
490 xResult = FF_GetLine( pxStream, ( char * ) pcBuffer, xCount );
\r
492 /* This call seems to result in errno being incorrectly set to
\r
493 FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION when an EOF is encountered. */
\r
494 ff_errno = prvFFErrorToErrno( xResult );
\r
496 if( ff_errno != 0 )
\r
501 /* Store the errno to thread local storage. */
\r
502 stdioSET_ERRNO( ff_errno );
\r
506 /*-----------------------------------------------------------*/
\r
508 int ff_seteof( FF_FILE *pxStream )
\r
510 FF_Error_t iResult;
\r
511 int iReturn, ff_errno;
\r
513 iResult = FF_SetEof( pxStream );
\r
515 ff_errno = prvFFErrorToErrno( iResult );
\r
517 if( ff_errno == 0 )
\r
526 /* Store the errno to thread local storage. */
\r
527 stdioSET_ERRNO( ff_errno );
\r
531 /*-----------------------------------------------------------*/
\r
533 /*_RB_ The norm would be to return an int, but in either case it is not clear
\r
534 what state the file is left in (open/closed). */
\r
535 FF_FILE *ff_truncate( const char * pcFileName, long lTruncateSize )
\r
537 FF_Error_t xResult = 0;
\r
540 uint32_t ulLength, ulBytesLeftToAdd, ulBytesToWrite;
\r
541 char *pcBufferToWrite;
\r
543 pxStream = ff_fopen( pcFileName, "a+");
\r
545 if( pxStream != NULL )
\r
547 ulLength = pxStream->ulFileSize;
\r
554 if( pxStream == NULL )
\r
556 /* Store the errno to thread local storage. */
\r
557 stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );
\r
559 else if( ulLength > ( uint32_t ) lTruncateSize )
\r
561 /* Seek the desired position */
\r
562 xResult = FF_Seek( pxStream, lTruncateSize, FF_SEEK_SET );
\r
564 /* Make the current position equal to its length */
\r
565 if( FF_isERR( xResult ) == pdFALSE )
\r
567 xResult = FF_SetEof( pxStream );
\r
570 if( FF_isERR( xResult ) != pdFALSE )
\r
572 ff_fclose( pxStream );
\r
576 /* Store the errno to thread local storage. */
\r
577 stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );
\r
579 else if( ulLength == ( uint32_t ) lTruncateSize )
\r
581 /* Nothing to do, the file has the desired size
\r
582 and the open handle will be returned. */
\r
586 /* lTruncateSize > ulLength. The user wants to open this file with a
\r
587 larger size than it currently has. Fill it with zeros. */
\r
588 pcBufferToWrite = ( char * ) ffconfigMALLOC( stdioTRUNCATE_WRITE_LENGTH );
\r
590 if( pcBufferToWrite == NULL )
\r
592 ff_fclose( pxStream );
\r
595 /* Store the errno to thread local storage. */
\r
596 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
\r
600 /* File has to grow */
\r
601 ulBytesLeftToAdd = ( ( uint32_t ) lTruncateSize ) - ulLength;
\r
603 /* Zeros must be written. */
\r
604 memset( pcBufferToWrite, '\0', stdioTRUNCATE_WRITE_LENGTH );
\r
606 while( ulBytesLeftToAdd > 0UL )
\r
608 if( ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH ) != 0 )
\r
610 /* Although +FAT's FF_Write() can handle any size at any
\r
611 offset, the driver puts data more efficiently if blocks are
\r
612 written at block boundaries. */
\r
613 ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH - ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH );
\r
615 if( ulBytesToWrite > ulBytesLeftToAdd )
\r
617 ulBytesToWrite = ulBytesLeftToAdd;
\r
622 ulBytesToWrite = ulBytesLeftToAdd;
\r
624 if( ulBytesToWrite > stdioTRUNCATE_WRITE_LENGTH )
\r
626 ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH;
\r
630 xReturned = ff_fwrite( pcBufferToWrite, sizeof( char ), ulBytesToWrite, pxStream );
\r
632 if( xReturned != ( size_t ) ulBytesToWrite )
\r
634 /* Write error. Close the stream and set the proper .
\r
636 ff_fclose( pxStream );
\r
639 /* Not setting ff_errno because it has been set by other
\r
640 functions from this ff_stdio. */
\r
645 ulBytesLeftToAdd -= ulBytesToWrite;
\r
648 ffconfigFREE( pcBufferToWrite );
\r
654 /*-----------------------------------------------------------*/
\r
656 #if( ffconfigMKDIR_RECURSIVE == 0 )
\r
658 /* The normal mkdir() : if assumes that the directories leading to the last
\r
659 element of pcDirectory already exists. For instance: mkdir( "/a/b/c" ) will
\r
660 succeed if the path "/a/b" already exists. */
\r
661 int ff_mkdir( const char *pcDirectory )
\r
663 int iResult, ff_errno;
\r
664 FF_DirHandler_t xHandler;
\r
666 /* In case a CWD is used, get the absolute path. */
\r
667 pcDirectory = prvABSPath( pcDirectory );
\r
669 /* Find the i/o manager for this path */
\r
670 if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
\r
672 /* No such device or address. */
\r
673 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );
\r
674 /* Return -1 for error as per normal mkdir() semantics. */
\r
679 /* A simple non-recursive make of a directory. */
\r
680 iResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );
\r
682 if( FF_GETERROR( iResult ) == FF_ERR_DIR_OBJECT_EXISTS )
\r
684 /* No error if the target directory already exists. */
\r
685 iResult = FF_ERR_NONE;
\r
687 ff_errno = prvFFErrorToErrno( iResult );
\r
689 /* Store the errno to thread local storage. */
\r
690 stdioSET_ERRNO( ff_errno );
\r
692 if( ff_errno == pdFREERTOS_ERRNO_NONE )
\r
698 /* Return -1 for error as per normal mkdir() semantics. */
\r
705 #else /* ffconfigMKDIR_RECURSIVE */
\r
707 #warning This path is not yet included in the regression tests.
\r
709 /* The 'recursive mkdir() : if the parameter 'xRecursive' is non-zero,
\r
710 the function will try to create the complete path. */
\r
711 int ff_mkdir( const char *pcDirectory, int xRecursive )
\r
714 FF_DirHandler_t xHandler;
\r
716 /* In case a CWD is used, get the absolute path. */
\r
717 pcDirectory = prvABSPath( pcDirectory );
\r
719 /* Find the i/o manager for this path */
\r
720 if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
\r
722 /* No such device or address. Store the errno to thread local
\r
724 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );
\r
725 /* Return -1 for error as per normal mkdir() semantics. */
\r
730 if( xRecursive == pdFALSE )
\r
732 /* A simple non-recursive make of a directory. */
\r
733 lResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );
\r
735 if( FF_GETERROR( lResult ) == FF_ERR_DIR_OBJECT_EXISTS )
\r
737 /* No error if the target directory already exists. */
\r
743 /* The recursive option is used. */
\r
744 char pcTempPath[ffconfigMAX_FILENAME];
\r
745 FF_Error_t errCode;
\r
746 int iLength = snprintf( pcTempPath, sizeof( pcTempPath ), "%s", xHandler.pcPath );
\r
747 char *pcPtr = pcTempPath + 1, *pcPrev;
\r
748 const char *pcLast = pcTempPath + iLength;
\r
750 lResult = FF_ERR_NONE;
\r
754 for ( pcPrev = pcPtr; pcPtr < pcLast; pcPtr++ )
\r
756 if( *pcPtr == '/' )
\r
763 if( pcPrev == pcPtr )
\r
768 errCode = FF_MkDir( xHandler.pxManager, pcTempPath );
\r
770 if( FF_isERR( errCode ) && FF_GETERROR( errCode ) != FF_ERR_DIR_OBJECT_EXISTS )
\r
776 if( pcPtr >= ( pcLast - 1 ) )
\r
781 *( pcPtr++ ) = '/';
\r
785 /* Store the errno to thread local storage. */
\r
786 stdioSET_ERRNO( prvFFErrorToErrno( lResult ) );
\r
791 #endif /* ffconfigMKDIR_RECURSIVE */
\r
792 /*-----------------------------------------------------------*/
\r
794 int ff_rmdir( const char *pcDirectory )
\r
797 int iReturn, ff_errno;
\r
798 FF_DirHandler_t xHandler;
\r
800 /* In case a CWD is used, get the absolute path */
\r
801 pcDirectory = prvABSPath( pcDirectory );
\r
803 /* Find the i/o manager which can handle this path. */
\r
804 if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )
\r
806 ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
\r
808 /* Return -1 for error as per normal rmdir() semantics. */
\r
813 lResult = FF_RmDir( xHandler.pxManager, xHandler.pcPath );
\r
814 ff_errno = prvFFErrorToErrno( lResult );
\r
816 if( ff_errno == 0 )
\r
822 /* Return -1 for error as per normal rmdir() semantics. */
\r
827 /* Store the errno to thread local storage. */
\r
828 stdioSET_ERRNO( ff_errno );
\r
832 /*-----------------------------------------------------------*/
\r
834 int ff_remove( const char *pcPath )
\r
836 FF_DirHandler_t xHandler;
\r
838 int iReturn, ff_errno;
\r
840 /* In case a CWD is used, get the absolute path */
\r
841 pcPath = prvABSPath( pcPath );
\r
843 /* Find the i/o manager which can handle this path. */
\r
844 if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
\r
846 /* No such device or address */
\r
847 ff_errno = pdFREERTOS_ERRNO_ENXIO;
\r
849 /* Return -1 for error as per normal remove() semantics. */
\r
854 xError = FF_RmFile( xHandler.pxManager, xHandler.pcPath );
\r
855 ff_errno = prvFFErrorToErrno( xError );
\r
857 #if ffconfigUSE_NOTIFY
\r
859 if( FF_isERR( xError ) == pdFALSE )
\r
861 callFileEvents( pcPath, eFileRemove );
\r
866 if( ff_errno == 0 )
\r
872 /* Return -1 for error as per normal remove() semantics. */
\r
877 /* Store the errno to thread local storage. */
\r
878 stdioSET_ERRNO( ff_errno );
\r
881 /*-----------------------------------------------------------*/
\r
882 /*_RB_ Last parameter not documented. */
\r
883 int ff_rename( const char *pcOldName, const char *pcNewName, int bDeleteIfExists )
\r
885 FF_DirHandler_t xHandlers[ 2 ];
\r
886 FF_Error_t xError = FF_ERR_NONE;
\r
887 int ff_errno = 0, iReturn;
\r
888 #if( ffconfigHAS_CWD != 0 )
\r
893 /* In case a CWD is used, get the absolute path */
\r
894 pcOldName = prvABSPath( pcOldName );
\r
896 /* Find the i/o manager which can handle this path */
\r
897 if( FF_FS_Find( pcOldName, &xHandlers[ 0 ] ) == pdFALSE )
\r
899 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
\r
900 ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
\r
904 #if( ffconfigHAS_CWD != 0 )
\r
906 xSize = strlen( xHandlers[0].pcPath ) + 1;
\r
907 pcOldCopy = ( char *)ffconfigMALLOC( xSize );
\r
909 if( pcOldCopy == NULL )
\r
911 /* Could not allocate space to store a file name. */
\r
912 ff_errno = pdFREERTOS_ERRNO_ENOMEM;
\r
913 xError = ( int32_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_MOVE );
\r
917 /* The function prvABSPath() returns a pointer to the task
\r
918 storage space. Rename needs to call it twice and therefore the
\r
919 path must be stored before it gets overwritten. */
\r
920 memcpy( pcOldCopy, xHandlers[0].pcPath, xSize );
\r
921 xHandlers[0].pcPath = pcOldCopy;
\r
924 #endif /* ffconfigHAS_CWD != 0 */
\r
926 #if( ffconfigHAS_CWD != 0 )
\r
927 if( pcOldCopy != NULL )
\r
928 #endif /* ffconfigHAS_CWD != 0 */
\r
930 pcNewName = prvABSPath( pcNewName );
\r
932 /* Find the i/o manager which can handle this path */
\r
933 if( FF_FS_Find( pcNewName, &( xHandlers[ 1 ] ) ) == pdFALSE )
\r
935 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
\r
936 ff_errno = pdFREERTOS_ERRNO_ENXIO; /* No such device or address */
\r
938 else if( xHandlers[ 0 ].pxManager != xHandlers[ 1 ].pxManager )
\r
940 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );
\r
941 /* Cross-device link, which can not be done. */
\r
942 ff_errno = pdFREERTOS_ERRNO_EXDEV;
\r
946 xError = FF_Move( xHandlers[ 0 ].pxManager, xHandlers[ 0 ].pcPath, xHandlers[ 1 ].pcPath, bDeleteIfExists );
\r
948 ff_errno = prvFFErrorToErrno( xError );
\r
950 #if ffconfigUSE_NOTIFY
\r
952 if( FF_isERR( xError ) == pdFALSE )
\r
954 callFileEvents( pcNewName, eFileChange );
\r
960 #if( ffconfigHAS_CWD != 0 )
\r
962 ffconfigFREE( pcOldCopy );
\r
968 /* Store the errno to thread local storage. */
\r
969 stdioSET_ERRNO( ff_errno );
\r
971 if( ff_errno == 0 )
\r
977 /* Return -1 for error as per normal rmdir() semantics. */
\r
983 /*-----------------------------------------------------------*/
\r
985 int ff_stat( const char *pcName, FF_Stat_t *pxStatBuffer )
\r
987 FF_DirEnt_t xDirEntry;
\r
988 uint32_t ulFileCluster;
\r
991 FF_DirHandler_t xHandler;
\r
993 FF_FindParams_t xFindParams;
\r
994 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
995 const FF_T_WCHAR *pcFileName = NULL;
\r
997 /* Initialised to prevent MSVC incorrectly claiming the variable is used
\r
998 without being initialised. */
\r
999 const char *pcFileName = NULL;
\r
1002 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
1004 /* Insert the current working directory in front of relative paths. */
\r
1005 pcName = prvABSPath( pcName );
\r
1007 /* Look-up the I/O manager for the file system. */
\r
1008 if( FF_FS_Find( pcName, &xHandler ) == pdFALSE )
\r
1010 /* No such device or address. */
\r
1011 xError = ( FF_Error_t ) ( pdFREERTOS_ERRNO_ENXIO | FF_STAT_FUNC );
\r
1015 xError = FF_ERR_NONE;
\r
1016 pcName = xHandler.pcPath;
\r
1018 /* Let xIndex point to the last occurrence of '/' or '\', to separate
\r
1019 the path from the file name. */
\r
1020 xIndex = ( BaseType_t ) STRLEN( pcName );
\r
1021 while( xIndex != 0 )
\r
1023 if( ( pcName[ xIndex ] == '\\' ) || ( pcName[ xIndex ] == '/' ) )
\r
1031 /* Copy the file name, i.e. the string that comes after the last
\r
1033 pcFileName = pcName + xIndex + 1;
\r
1037 /* Only for the root, the slash is part of the directory name.
\r
1038 'xIndex' now equals to the length of the path name. */
\r
1042 /* FF_CreateShortName() might set flags FIND_FLAG_FITS_SHORT and
\r
1043 FIND_FLAG_SIZE_OK. */
\r
1044 FF_CreateShortName( &xFindParams, pcFileName );
\r
1046 /* Lookup the path and find the cluster pointing to the directory: */
\r
1047 xFindParams.ulDirCluster = FF_FindDir( xHandler.pxManager, pcName, xIndex, &xError );
\r
1050 if( FF_isERR( xError ) == pdFALSE )
\r
1052 /* See if the file does exist within the given directory. */
\r
1053 ulFileCluster = FF_FindEntryInDir( xHandler.pxManager, &xFindParams, pcFileName, 0x00, &xDirEntry, &xError );
\r
1055 if( ulFileCluster == 0ul )
\r
1057 /* If cluster 0 was returned, it might be because the file has no allocated cluster,
\r
1058 i.e. only a directory entry and no stored data. */
\r
1059 if( STRLEN( pcFileName ) == STRLEN( xDirEntry.pcFileName ) )
\r
1061 if( ( xDirEntry.ulFileSize == 0 ) && ( FF_strmatch( pcFileName, xDirEntry.pcFileName, ( BaseType_t ) STRLEN( pcFileName ) ) == pdTRUE ) )
\r
1063 /* It is the file, give it a pseudo cluster number '1'. */
\r
1064 ulFileCluster = 1;
\r
1065 /* And reset any error. */
\r
1066 xError = FF_ERR_NONE;
\r
1071 /* Test 'ulFileCluster' again, it might have been changed. */
\r
1072 if( ulFileCluster == 0ul )
\r
1074 xError = FF_ERR_FILE_NOT_FOUND | FF_STAT_FUNC;
\r
1078 if( ( pxStatBuffer != NULL ) && ( FF_isERR( xError ) == pdFALSE ) )
\r
1080 if( ( xDirEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0 )
\r
1082 pxStatBuffer->st_mode = ( unsigned short ) FF_IFDIR;
\r
1086 pxStatBuffer->st_mode = ( unsigned short ) FF_IFREG;
\r
1089 #if( ffconfigDEV_SUPPORT != 0 )
\r
1091 BaseType_t bIsDeviceDir = xCheckDevicePath( pcFileName );
\r
1093 if( bIsDeviceDir != pdFALSE )
\r
1095 FF_Device_GetDirEnt( xHandler.pcPath, &( xDirEntry ) );
\r
1100 /* Despite the warning output by MSVC - it is not possible to get here
\r
1101 if xDirEntry has not been initialised. */
\r
1102 pxStatBuffer->st_size = xDirEntry.ulFileSize;
\r
1103 pxStatBuffer->st_ino = xDirEntry.ulObjectCluster;
\r
1104 pxStatBuffer->st_dev = ( short ) xHandler.xFSIndex;
\r
1106 #if( ffconfigTIME_SUPPORT == 1 )
\r
1108 pxStatBuffer->st_atime = ( unsigned long ) prvFileTime( &( xDirEntry.xAccessedTime ) );
\r
1109 pxStatBuffer->st_mtime = ( unsigned long ) prvFileTime( &( xDirEntry.xModifiedTime ) );
\r
1110 pxStatBuffer->st_ctime = ( unsigned long ) prvFileTime( &( xDirEntry.xCreateTime ) );
\r
1115 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
\r
1117 if( FF_isERR( xError ) == pdFALSE )
\r
1128 /*-----------------------------------------------------------*/
\r
1130 #if( ffconfigHAS_CWD == 1 )
\r
1132 int ff_chdir( const char *pcDirectoryName )
\r
1134 int iResult, iLength, iValid = pdFALSE;
\r
1135 WorkingDirectory_t *pxDir = NULL;
\r
1137 /* Not all paths set an errno. */
\r
1138 stdioSET_ERRNO( 0 );
\r
1140 /* Is there a file system mounted? */
\r
1141 if( FF_FS_Count() != 0 )
\r
1143 /* In case a CWD is used, get the absolute path. */
\r
1144 pcDirectoryName = prvABSPath( pcDirectoryName );
\r
1145 pxDir = pxFindCWD();
\r
1147 if( pxDir == NULL )
\r
1149 /* Store the errno to thread local storage. */
\r
1150 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
\r
1152 /* Return -1 for error as per normal chdir() semantics. */
\r
1157 /* The CWD will be stored without a trailing '/'. If "/"
\r
1158 happens to be the CWD, it will be stored as an empty string. */
\r
1159 iLength = strlen( pcDirectoryName );
\r
1161 /* Knock off the trailing / if one exits - being careful not to
\r
1162 remove the trailing slash if this is the root directory. */
\r
1163 if( ( iLength > 1 ) && ( pxDir->pcFileName[ iLength - 1 ] == '/' ) )
\r
1165 pxDir->pcFileName[ iLength - 1 ] = '\0';
\r
1168 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOENT );
\r
1170 /* Does the directory exist? */
\r
1171 if( strcmp( pcDirectoryName, "/" ) == 0 )
\r
1173 /* Moving to the root - which exists. */
\r
1176 else if( ff_finddir( pxDir->pcFileName ) != pdFALSE )
\r
1183 if( iValid == pdTRUE )
\r
1185 /* The generated name becomes the CWD. No need to test for overflow
\r
1186 as pcPath and pcFileName are the same size. */
\r
1187 strcpy( pxDir->pcCWD, pxDir->pcFileName );
\r
1189 /* chdir returns 0 for success. */
\r
1190 iResult = FF_ERR_NONE;
\r
1194 /* Return -1 for error as per normal chdir() semantics. */
\r
1201 #endif /* ffconfigHAS_CWD == 1 */
\r
1202 /*-----------------------------------------------------------*/
\r
1204 #if( ffconfigHAS_CWD == 1 )
\r
1206 char *ff_getcwd( char *pcBuffer, size_t xBufferLength )
\r
1208 WorkingDirectory_t *pxDir = pxFindCWD();
\r
1210 stdioSET_ERRNO( 0 );
\r
1212 if( ( pxDir == NULL ) || ( pxDir->pcCWD[0] == '\0' ) )
\r
1214 if( xBufferLength > strlen( "/" ) )
\r
1216 strncpy( pcBuffer, "/", xBufferLength );
\r
1225 if( strlen( pxDir->pcCWD ) < xBufferLength )
\r
1227 strncpy( pcBuffer, pxDir->pcCWD, xBufferLength );
\r
1238 #endif /* ffconfigHAS_CWD */
\r
1239 /*-----------------------------------------------------------*/
\r
1241 int ff_findfirst( const char *pcPath, FF_FindData_t *pxFindData )
\r
1243 int iIsRootDir, iReturn;
\r
1244 const char *pcDirectory;
\r
1248 memset( pxFindData, '\0', sizeof( *pxFindData ) );
\r
1250 pxFindData->pcFileName = pxFindData->xDirectoryEntry.pcFileName;
\r
1252 /* In case a CWD is used, get the absolute path. */
\r
1253 pcDirectory = prvABSPath( pcPath );
\r
1255 if( ( pcDirectory[ 0 ] == '/' ) && ( pcDirectory[ 1 ] == 0x00 ) )
\r
1257 iIsRootDir = pdTRUE;
\r
1261 iIsRootDir = pdFALSE;
\r
1264 /* Find the i/o manager that can handle this path. */
\r
1265 if( FF_FS_Find( pcDirectory, &( pxFindData->xDirectoryHandler ) ) == pdFALSE )
\r
1267 if( ( iIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) )
\r
1269 stdioSET_ERRNO( prvFFErrorToErrno( ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST ) ) );
\r
1274 /* Check no errors before continuing. */
\r
1275 if( iReturn == 0 )
\r
1277 #if( ffconfigDEV_SUPPORT != 0 )
\r
1279 pxFindData->bIsDeviceDir = xCheckDevicePath( pcDirectory );
\r
1283 if( iIsRootDir != pdFALSE )
\r
1285 /* A listing of the root directory will include pseudo entries
\r
1286 such as /ram /nand. */
\r
1287 pxFindData->xDirectoryHandler.xFSIndex = FF_FS_Count();
\r
1289 /* Only add '.' */
\r
1290 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1;
\r
1294 /* This is the root of a sub file system, add "." and ".." */
\r
1295 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1 | stdioDIR_ENTRY_DOT_2;
\r
1298 pxFindData->xDirectoryHandler.u.bits.bIsValid = pdTRUE;
\r
1299 iReturn = ff_findnext( pxFindData );
\r
1303 /* errno has already been set. */
\r
1308 /*-----------------------------------------------------------*/
\r
1310 int ff_findnext( FF_FindData_t *pxFindData )
\r
1312 FF_Error_t xError;
\r
1313 #if( ffconfigTIME_SUPPORT != 0 )
\r
1314 BaseType_t xSetTime = 0;
\r
1315 #endif /* ffconfigTIME_SUPPORT */
\r
1318 if( pxFindData->xDirectoryHandler.u.bits.bIsValid == pdFALSE )
\r
1320 xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PARAMETER | FF_FINDNEXT );
\r
1321 FF_PRINTF("ff_findnext: xDirectoryHandler not valid\n" );
\r
1325 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
\r
1326 if( pxFindData->xDirectoryHandler.pxManager != NULL )
\r
1328 if( pxFindData->xDirectoryHandler.u.bits.bFirstCalled == pdFALSE )
\r
1330 pxFindData->xDirectoryHandler.u.bits.bFirstCalled = pdTRUE;
\r
1331 xError = FF_FindFirst( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ),
\r
1332 pxFindData->xDirectoryHandler.pcPath );
\r
1334 else if( pxFindData->xDirectoryHandler.u.bits.bEndOfDir == pdFALSE )
\r
1336 xError = FF_FindNext( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ) );
\r
1339 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
\r
1341 /* Stop further calls to FF_FindNext(). */
\r
1342 pxFindData->xDirectoryHandler.u.bits.bEndOfDir = pdTRUE;
\r
1345 #if( ffconfigDEV_SUPPORT != 0 )
\r
1347 if( pxFindData->bIsDeviceDir != pdFALSE )
\r
1349 FF_Device_GetDirEnt( pxFindData->xDirectoryHandler.pcPath, &( pxFindData->xDirectoryEntry ) );
\r
1355 if( FF_isERR( xError ) == pdFALSE )
\r
1357 /* If an entry is found, see if it is a dot-entry. Dot-entries
\r
1358 ("." and "..") need a time-stamp. */
\r
1359 if( pxFindData->xDirectoryEntry.pcFileName[ 0 ] == '.' )
\r
1361 if( ( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '.' ) &&
\r
1362 ( pxFindData->xDirectoryEntry.pcFileName[ 2 ] == '\0' ) )
\r
1364 /* This is a directory "..". Clear the flag for DOT_2. */
\r
1365 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;
\r
1366 #if( ffconfigTIME_SUPPORT != 0 )
\r
1368 /* The dot-entries do not have a proper time stamp, add
\r
1370 xSetTime = pdTRUE;
\r
1372 #endif /* ffconfigTIME_SUPPORT */
\r
1374 else if( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '\0' )
\r
1376 /* This is a directory ".". Clear the flag for DOT_1. */
\r
1377 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_2;
\r
1378 #if( ffconfigTIME_SUPPORT != 0 )
\r
1380 xSetTime = pdTRUE;
\r
1382 #endif /* ffconfigTIME_SUPPORT */
\r
1387 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
\r
1389 /* No more physical entries were found. Now see if there are FS
\r
1390 entries or dot-entries to be added: */
\r
1391 while( ( pxFindData->xDirectoryHandler.xFSIndex > 0 ) ||
\r
1392 ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries != 0 ) )
\r
1394 if( pxFindData->xDirectoryHandler.xFSIndex > 0 )
\r
1396 FF_SubSystem_t xSubSystem;
\r
1399 pxFindData->xDirectoryHandler.xFSIndex--;
\r
1400 found = FF_FS_Get( pxFindData->xDirectoryHandler.xFSIndex, &xSubSystem );
\r
1402 if( ( found == pdFALSE ) || ( xSubSystem.pcPath[ 1 ] == '\0' ) )
\r
1406 snprintf( pxFindData->xDirectoryEntry.pcFileName, sizeof( pxFindData->xDirectoryEntry.pcFileName ), "%s", xSubSystem.pcPath + 1 );
\r
1408 if( xSubSystem.pxManager != NULL )
\r
1410 pxFindData->xDirectoryEntry.ulObjectCluster = xSubSystem.pxManager->xPartition.ulRootDirCluster;
\r
1414 pxFindData->xDirectoryEntry.ulObjectCluster = 0;
\r
1417 else if( ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries & stdioDIR_ENTRY_DOT_2 ) != 0 )
\r
1419 strcpy( pxFindData->xDirectoryEntry.pcFileName, "..");
\r
1421 /* Clear DOT_2 (keep DOT_1). */
\r
1422 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;
\r
1426 strcpy( pxFindData->xDirectoryEntry.pcFileName, ".");
\r
1427 pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = 0;
\r
1430 pxFindData->xDirectoryEntry.ucAttrib = FF_FAT_ATTR_READONLY | FF_FAT_ATTR_DIR;
\r
1431 pxFindData->xDirectoryEntry.ulFileSize = stdioDOT_ENTRY_FILE_SIZE;
\r
1432 #if( ffconfigTIME_SUPPORT != 0 )
\r
1434 xSetTime = pdTRUE;
\r
1436 #endif /* ffconfigTIME_SUPPORT */
\r
1438 xError = FF_ERR_NONE;
\r
1443 #if( ffconfigTIME_SUPPORT != 0 )
\r
1445 if( xSetTime != pdFALSE )
\r
1447 FF_TimeStruct_t xTimeStruct;
\r
1450 xSeconds = FreeRTOS_time( NULL );
\r
1451 FreeRTOS_gmtime_r( &xSeconds, &xTimeStruct );
\r
1453 pxFindData->xDirectoryEntry.xCreateTime.Year = ( uint16_t ) ( xTimeStruct.tm_year + 1900 ); /* Year (e.g. 2009). */
\r
1454 pxFindData->xDirectoryEntry.xCreateTime.Month = ( uint16_t ) ( xTimeStruct.tm_mon + 1 ); /* Month (e.g. 1 = Jan, 12 = Dec). */
\r
1455 pxFindData->xDirectoryEntry.xCreateTime.Day = ( uint16_t ) xTimeStruct.tm_mday; /* Day (1 - 31). */
\r
1456 pxFindData->xDirectoryEntry.xCreateTime.Hour = ( uint16_t ) xTimeStruct.tm_hour; /* Hour (0 - 23). */
\r
1457 pxFindData->xDirectoryEntry.xCreateTime.Minute = ( uint16_t ) xTimeStruct.tm_min; /* Min (0 - 59). */
\r
1458 pxFindData->xDirectoryEntry.xCreateTime.Second = ( uint16_t ) xTimeStruct.tm_sec; /* Second (0 - 59). */
\r
1459 pxFindData->xDirectoryEntry.xModifiedTime = pxFindData->xDirectoryEntry.xCreateTime; /* Date and Time Modified. */
\r
1460 pxFindData->xDirectoryEntry.xAccessedTime = pxFindData->xDirectoryEntry.xCreateTime; /* Date of Last Access. */
\r
1463 #endif /* ffconfigTIME_SUPPORT */
\r
1465 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
\r
1467 /* FF_ERR_DIR_END_OF_DIR will be returned. */
\r
1468 pxFindData->xDirectoryHandler.u.bits.bIsValid = 0;
\r
1471 pxFindData->ucAttributes = pxFindData->xDirectoryEntry.ucAttrib;
\r
1472 pxFindData->ulFileSize = pxFindData->xDirectoryEntry.ulFileSize;
\r
1475 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
\r
1479 /*-----------------------------------------------------------*/
\r
1481 /*-----------------------------------------------------------
\r
1482 * ff_isdirempty() returns 1 if a given directory is empty
\r
1483 * (has no entries)
\r
1484 *-----------------------------------------------------------*/
\r
1485 int ff_isdirempty(const char *pcPath )
\r
1487 FF_DirHandler_t xHandler;
\r
1489 /* In case a CWD is used, get the absolute path */
\r
1490 pcPath = prvABSPath( pcPath );
\r
1491 /* Find the i/o manager which can handle this path */
\r
1492 if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
\r
1494 iResult = ( int ) ( FF_ERR_NULL_POINTER | FF_ISDIREMPTY );
\r
1498 iResult = FF_isDirEmpty( xHandler.pxManager, xHandler.pcPath );
\r
1501 /* Store the errno to thread local storage. */
\r
1502 stdioSET_ERRNO( prvFFErrorToErrno( iResult ) );
\r
1505 /*-----------------------------------------------------------*/
\r
1507 #if (ffconfig64_NUM_SUPPORT != 0 )
\r
1508 int64_t ff_diskfree(const char *pcPath, uint32_t *pxSectorCount )
\r
1510 int32_t ff_diskfree(const char *pcPath, uint32_t *pxSectorCount )
\r
1513 FF_DirHandler_t xHandler;
\r
1514 FF_Error_t xError;
\r
1515 #if (ffconfig64_NUM_SUPPORT != 0 )
\r
1516 #define DISKFREE_RETURN_TYPE int64_t
\r
1519 #define DISKFREE_RETURN_TYPE int32_t
\r
1523 if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
\r
1525 /* Return cluster 0 for error. */
\r
1528 /* Store the errno to thread local storage. */
\r
1529 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO ); /* No such device or address */
\r
1533 if (pxSectorCount != NULL )
\r
1535 *pxSectorCount = xHandler.pxManager->xPartition.ulDataSectors;
\r
1538 lReturn = ( DISKFREE_RETURN_TYPE ) FF_GetFreeSize( xHandler.pxManager, &xError ) / 512;
\r
1540 /* Store the errno to thread local storage. */
\r
1541 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );
\r
1546 /*-----------------------------------------------------------*/
\r
1548 int ff_finddir(const char *pcPath )
\r
1551 FF_DirHandler_t xHandler;
\r
1552 FF_Error_t errCode;
\r
1554 if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )
\r
1556 /* Return cluster 0 for error. */
\r
1561 iResult = ( int ) FF_FindDir( xHandler.pxManager, xHandler.pcPath, ( uint16_t ) strlen( xHandler.pcPath ), &errCode );
\r
1566 /*-----------------------------------------------------------*/
\r
1568 size_t ff_filelength( FF_FILE *pxStream )
\r
1570 FF_Error_t xReturned;
\r
1571 uint32_t ulLength;
\r
1573 xReturned = FF_GetFileSize( pxStream, &( ulLength ) );
\r
1575 if( FF_isERR( xReturned ) != pdFALSE )
\r
1578 ulLength = ( uint32_t ) 0u;
\r
1579 stdioSET_ERRNO( prvFFErrorToErrno( xReturned ) );
\r
1583 stdioSET_ERRNO( pdFREERTOS_ERRNO_NONE );
\r
1586 return ( size_t ) ulLength;
\r
1588 /*-----------------------------------------------------------*/
\r
1590 /*-----------------------------------------------------------
\r
1591 * Delete a directory and, recursively, all of its contents
\r
1592 *-----------------------------------------------------------*/
\r
1593 #if( ffconfigUSE_DELTREE != 0 )
\r
1594 int ff_deltree( const char *pcDirectory )
\r
1599 pcPath = ( char * ) ffconfigMALLOC( ffconfigMAX_FILENAME );
\r
1600 if( pcPath != NULL )
\r
1602 /* In case a CWD is used, get the absolute path */
\r
1603 pcDirectory = prvABSPath( pcDirectory );
\r
1604 snprintf (pcPath, ffconfigMAX_FILENAME, "%s", pcDirectory);
\r
1605 /* This recursive function will do all the work */
\r
1606 iResult = ff_deltree_recurse (pcPath);
\r
1607 if( iResult >= 0 )
\r
1609 iResult = ff_rmdir( pcPath );
\r
1612 FF_PRINTF("ff_deltree(%s): %s\n", pcPath, strerror( stdioGET_ERRNO( ) ) );
\r
1615 ffconfigFREE( pcPath );
\r
1620 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
\r
1624 #endif /* ffconfigUSE_DELTREE */
\r
1625 /*-----------------------------------------------------------*/
\r
1627 #if( ffconfigUSE_DELTREE != 0 )
\r
1628 static int ff_deltree_recurse( char *pcPath )
\r
1630 FF_FindData_t *pxFindData;
\r
1631 BaseType_t xIsDir, xIsDotDir;
\r
1632 FF_Error_t xError;
\r
1633 int iResult, iNext, iNameLength, pass, iCount = 0;
\r
1635 pxFindData = ( FF_FindData_t * ) ffconfigMALLOC( sizeof( *pxFindData ) );
\r
1636 if( pxFindData != NULL )
\r
1638 iNameLength = ( int ) strlen( pcPath );
\r
1639 /* The directory will be scanned 2 times. First the sub-directories will be
\r
1640 entered and their contents deleted. In the second pass the files in the
\r
1641 current directory will be removed. In this way 'pcPath' can be constantly
\r
1642 used and reused recursively which is cheaper than allocating 'ffconfigMAX_FILENAME'
\r
1643 bytes within each recursion. */
\r
1644 for( pass = 0; pass < 2; pass++ )
\r
1646 for( iResult = ff_findfirst( pcPath, pxFindData );
\r
1650 xIsDir = ( pxFindData->xDirectoryEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0;
\r
1651 if( ( pass == 0 ) && ( xIsDir != pdFALSE ) )
\r
1653 /* This entry is a directory. Don't traverse '.' or '..' */
\r
1656 if( pxFindData->pcFileName[ 0 ] == '.' )
\r
1658 if( ( pxFindData->pcFileName[ 1 ] == '.' ) &&
\r
1659 ( pxFindData->pcFileName[ 2 ] == '\0' ) )
\r
1663 else if( pxFindData->pcFileName[ 1 ] == '\0' )
\r
1668 if( xIsDotDir == 0 )
\r
1670 snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ) , "%s%s",
\r
1671 pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );
\r
1673 /* Let pxFindData point to the next element before
\r
1674 the current will get removed. */
\r
1675 iNext = ff_findnext( pxFindData );
\r
1677 /* Remove the contents of this directory. */
\r
1678 iResult = ff_deltree_recurse( pcPath );
\r
1684 iCount += iResult;
\r
1685 /* remove the directory itself */
\r
1686 xError = ff_rmdir( pcPath );
\r
1689 FF_PRINTF( "ff_rmdir( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );
\r
1698 iNext = ff_findnext( pxFindData );
\r
1701 else if( ( pass == 1 ) && ( xIsDir == pdFALSE ) )
\r
1703 snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ), "%s%s",
\r
1704 pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );
\r
1706 /* Let pxFindData point to the next element before
\r
1707 the current will get removed. */
\r
1708 iNext = ff_findnext( pxFindData );
\r
1710 /* Remove a plain file. */
\r
1711 xError = ff_remove( pcPath );
\r
1714 FF_PRINTF( "ff_remove( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );
\r
1723 iNext = ff_findnext( pxFindData );
\r
1725 pcPath[ iNameLength ] = '\0';
\r
1728 if( FF_GETERROR( iResult ) == FF_ERR_DIR_INVALID_PATH )
\r
1732 if( ( FF_GETERROR( iResult ) != FF_ERR_DIR_END_OF_DIR ) && ( FF_GETERROR( iResult ) != FF_ERR_FILE_INVALID_PATH ) )
\r
1734 FF_PRINTF( "ff_deltree_recurse[%s]: %s\n", pcPath, ( const char * ) FF_GetErrMessage( iResult ) );
\r
1737 ffconfigFREE( pxFindData );
\r
1742 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );
\r
1747 #endif /* ffconfigUSE_DELTREE */
\r
1748 /*-----------------------------------------------------------*/
\r
1750 int prvFFErrorToErrno( FF_Error_t xError )
\r
1752 if( FF_isERR( xError ) == pdFALSE )
\r
1757 /* Store the last +FAT error code received. */
\r
1758 stdioSET_FF_ERROR( xError );
\r
1760 switch( FF_GETERROR( xError ) )
\r
1762 /* Global Error Codes. */
\r
1763 case FF_ERR_NONE: return 0; /* No Error. */
\r
1765 case FF_ERR_NULL_POINTER: return pdFREERTOS_ERRNO_EBADF; /* pxIOManager was NULL. */
\r
1766 case FF_ERR_NOT_ENOUGH_MEMORY: return pdFREERTOS_ERRNO_ENOMEM; /* malloc() failed! - Could not allocate handle memory. */
\r
1767 case FF_ERR_DEVICE_DRIVER_FAILED: return pdFREERTOS_ERRNO_EIO; /* The Block Device driver reported a FATAL error, cannot continue. */
\r
1769 /* User return codes for Rd/Wr functions:. */
\r
1770 case FF_ERR_IOMAN_DRIVER_BUSY: return pdFREERTOS_ERRNO_EBUSY; /* 10. */
\r
1771 case FF_ERR_IOMAN_DRIVER_FATAL_ERROR: return pdFREERTOS_ERRNO_EUNATCH; /* Protocol driver not attached. */
\r
1773 /* IOMAN Error Codes. */
\r
1774 case FF_ERR_IOMAN_BAD_BLKSIZE: return pdFREERTOS_ERRNO_EINVAL; /* The provided blocksize was not a multiple of 512. */
\r
1775 case FF_ERR_IOMAN_BAD_MEMSIZE: return pdFREERTOS_ERRNO_EINVAL; /* The memory size was not a multiple of the blocksize. */
\r
1776 case FF_ERR_IOMAN_DEV_ALREADY_REGD: return pdFREERTOS_ERRNO_EADDRINUSE; /* Device was already registered. Use FF_UnRegister() to re-use this IOMAN with another device. */
\r
1777 case FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION: return pdFREERTOS_ERRNO_ENOMEDIUM; /* A mountable partition could not be found on the device. */
\r
1778 case FF_ERR_IOMAN_INVALID_FORMAT: return pdFREERTOS_ERRNO_EFTYPE; /* The. */
\r
1779 case FF_ERR_IOMAN_INVALID_PARTITION_NUM: return pdFREERTOS_ERRNO_EINVAL; /* The partition number provided was out of range. */
\r
1780 case FF_ERR_IOMAN_NOT_FAT_FORMATTED: return pdFREERTOS_ERRNO_EFTYPE; /* The partition did not look like a FAT partition. */
\r
1781 case FF_ERR_IOMAN_DEV_INVALID_BLKSIZE: return pdFREERTOS_ERRNO_EINVAL; /* IOMAN object BlkSize is not compatible with the blocksize of this device driver. */
\r
1782 case FF_ERR_IOMAN_PARTITION_MOUNTED: return pdFREERTOS_ERRNO_EADDRINUSE; /* Device is in use by an actively mounted partition. Unmount the partition first. */
\r
1783 case FF_ERR_IOMAN_ACTIVE_HANDLES: return pdFREERTOS_ERRNO_EBUSY; /* The partition cannot be unmounted until all active file handles are closed. (There may also be active handles on the cache). */
\r
1784 case FF_ERR_IOMAN_GPT_HEADER_CORRUPT: return pdFREERTOS_ERRNO_EBADE; /* The GPT partition table appears to be corrupt, refusing to mount. */
\r
1785 case FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE: return pdFREERTOS_ERRNO_ENOSPC; /* 22. */
\r
1786 case FF_ERR_IOMAN_OUT_OF_BOUNDS_READ: return pdFREERTOS_ERRNO_ESPIPE; /* 23, return 'Illegal seek'. */
\r
1787 case FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE: return pdFREERTOS_ERRNO_ESPIPE; /* 24. */
\r
1788 case FF_ERR_IOMAN_DRIVER_NOMEDIUM: return pdFREERTOS_ERRNO_ENOMEDIUM; /* The medium (e.g. SD-card) has been extracted. */
\r
1790 /* File Error Codes 30 +. */
\r
1791 case FF_ERR_FILE_ALREADY_OPEN: return pdFREERTOS_ERRNO_EALREADY; /* File is in use. */
\r
1792 case FF_ERR_FILE_NOT_FOUND: return pdFREERTOS_ERRNO_ENOENT; /* File was not found. */
\r
1793 case FF_ERR_FILE_OBJECT_IS_A_DIR: return pdFREERTOS_ERRNO_EISDIR; /* Tried to FF_Open() a Directory. */
\r
1794 case FF_ERR_FILE_IS_READ_ONLY: return pdFREERTOS_ERRNO_EROFS; /* Tried to FF_Open() a file marked read only. */
\r
1795 case FF_ERR_FILE_INVALID_PATH: return pdFREERTOS_ERRNO_ENOTDIR; /* The path of the file was not found. */
\r
1796 case FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE: return pdFREERTOS_ERRNO_EACCES; /* 35. */
\r
1797 case FF_ERR_FILE_NOT_OPENED_IN_READ_MODE: return pdFREERTOS_ERRNO_EACCES; /* 36. */
\r
1798 case FF_ERR_FILE_EXTEND_FAILED: return pdFREERTOS_ERRNO_ENOSPC; /* Could not extend the file. */
\r
1799 case FF_ERR_FILE_DESTINATION_EXISTS: return pdFREERTOS_ERRNO_EEXIST; /* 38. */
\r
1800 case FF_ERR_FILE_SOURCE_NOT_FOUND: return pdFREERTOS_ERRNO_ENOENT; /* 39. */
\r
1801 case FF_ERR_FILE_DIR_NOT_FOUND: return pdFREERTOS_ERRNO_ENOENT; /* 40. */
\r
1802 case FF_ERR_FILE_COULD_NOT_CREATE_DIRENT: return pdFREERTOS_ERRNO_EIO; /* 41. */
\r
1803 case FF_ERR_FILE_BAD_HANDLE: return pdFREERTOS_ERRNO_EBADF; /* A file handle was invalid. */
\r
1804 case FF_ERR_FILE_MEDIA_REMOVED: return pdFREERTOS_ERRNO_ENODEV; /* File handle got invalid because media was removed. */
\r
1805 case FF_ERR_FILE_SEEK_INVALID_POSITION: return pdFREERTOS_ERRNO_ESPIPE; /* Illegal position, outside the file's space */
\r
1806 case FF_ERR_FILE_SEEK_INVALID_ORIGIN: return pdFREERTOS_ERRNO_EINVAL; /* Seeking beyond end of file. */
\r
1808 /* Directory Error Codes 50 +. */
\r
1809 case FF_ERR_DIR_OBJECT_EXISTS: return pdFREERTOS_ERRNO_EEXIST; /* A file or folder of the same name already exists in the current directory. */
\r
1810 case FF_ERR_DIR_DIRECTORY_FULL: return pdFREERTOS_ERRNO_ENOSPC; /* No more items could be added to the directory. */
\r
1811 case FF_ERR_DIR_END_OF_DIR: return pdFREERTOS_ERRNO_ENMFILE; /*/. */
\r
1812 case FF_ERR_DIR_NOT_EMPTY: return pdFREERTOS_ERRNO_ENOTEMPTY; /* Cannot delete a directory that contains files or folders. */
\r
1813 case FF_ERR_DIR_INVALID_PATH: return pdFREERTOS_ERRNO_EINVAL; /* Could not find the directory specified by the path. */
\r
1814 case FF_ERR_DIR_CANT_EXTEND_ROOT_DIR: return pdFREERTOS_ERRNO_ENOSPC; /* Can't extend the root dir. */
\r
1815 case FF_ERR_DIR_EXTEND_FAILED: return pdFREERTOS_ERRNO_ENOSPC; /* Not enough space to extend the directory. */
\r
1816 case FF_ERR_DIR_NAME_TOO_LONG: return pdFREERTOS_ERRNO_ENAMETOOLONG;/* Name exceeds the number of allowed characters for a filename. */
\r
1818 /* Fat Error Codes 70 +. */
\r
1819 case FF_ERR_FAT_NO_FREE_CLUSTERS: return pdFREERTOS_ERRNO_ENOSPC; /* No more free space is available on the disk. */
\r
1821 /* UNICODE Error Codes 100 +. */
\r
1822 case FF_ERR_UNICODE_INVALID_CODE: return pdFREERTOS_ERRNO_EBADE; /* An invalid Unicode character was provided!. */
\r
1823 case FF_ERR_UNICODE_DEST_TOO_SMALL: return pdFREERTOS_ERRNO_ENOBUFS; /* Not enough space in the UTF-16 buffer to encode the entire sequence as UTF-16. */
\r
1824 case FF_ERR_UNICODE_INVALID_SEQUENCE: return pdFREERTOS_ERRNO_EILSEQ; /* An invalid UTF-16 sequence was encountered. */
\r
1825 case FF_ERR_UNICODE_CONVERSION_EXCEEDED: return pdFREERTOS_ERRNO_ENAMETOOLONG;/* Filename exceeds MAX long-filename length when converted to UTF-16. */
\r
1828 return pdFREERTOS_ERRNO_EFAULT;
\r
1830 /*-----------------------------------------------------------*/
\r
1832 #if( ffconfigHAS_CWD == 1 )
\r
1834 void ff_free_CWD_space( void )
\r
1836 WorkingDirectory_t *pxSpace;
\r
1838 /* Obtain the CWD used by the current task. */
\r
1839 pxSpace = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );
\r
1840 if( pxSpace != NULL )
\r
1842 vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) NULL );
\r
1843 ffconfigFREE( pxSpace );
\r
1847 #endif /* ffconfigHAS_CWD */
\r
1848 /*-----------------------------------------------------------*/
\r
1850 #if( ffconfigHAS_CWD == 1 )
\r
1852 static WorkingDirectory_t *pxFindCWD( void )
\r
1854 WorkingDirectory_t *pxReturn;
\r
1856 /* Obtain the CWD used by the current task. */
\r
1857 pxReturn = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );
\r
1859 if( pxReturn == NULL )
\r
1861 /* This task does not yet have a WorkingDirectory_t structure - create and
\r
1862 initialise one now. */
\r
1863 pxReturn = ( WorkingDirectory_t * ) ffconfigMALLOC( sizeof( WorkingDirectory_t ) );
\r
1865 if( pxReturn != NULL )
\r
1867 pxReturn->pcCWD[ 0 ] = '\0';
\r
1868 vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) pxReturn );
\r
1875 #endif /* ffconfigHAS_CWD */
\r
1876 /*-----------------------------------------------------------*/
\r
1878 #if( ffconfigHAS_CWD == 1 )
\r
1880 static const char *prvProcessRelativePaths( const char *pcPath )
\r
1882 const char *pcReturn;
\r
1883 char *pcChar, *pcTokenStart = NULL, *pcFollowingToken, cPreviousChar = 0x00;
\r
1886 /* Scan the string looking for a relative path. */
\r
1887 pcReturn = pcPath;
\r
1888 pcChar = ( char * ) pcReturn;
\r
1890 configASSERT( pcPath );
\r
1892 while( *pcChar != 0x00 )
\r
1894 if( *pcChar == '.' )
\r
1896 /* A potential relative path was found. Is this a "." or a "..". */
\r
1897 if( *( pcChar + 1 ) == '.' )
\r
1899 /* Nothing can be done if this is at the start of the string. */
\r
1900 if( pcTokenStart != NULL )
\r
1902 /* A ".." was found. Where does the next token start? */
\r
1903 pcFollowingToken = pcChar + 2;
\r
1905 if( *pcFollowingToken == '/' )
\r
1907 /* The next token starts after the "../" */
\r
1908 pcFollowingToken += sizeof( char );
\r
1911 /* Remove the ".." and the previous token. */
\r
1913 while( pcFollowingToken[ xByte ] != 0x00 )
\r
1915 pcTokenStart[ xByte ] = pcFollowingToken[ xByte ];
\r
1920 pcTokenStart[ xByte ] = 0x00;
\r
1922 /* The pointer to the previous token will now be wrong if
\r
1923 there are multiple if "../.." appears in the string. So
\r
1924 reset the variables to continue scanning. */
\r
1925 pcChar = ( char * ) pcReturn;
\r
1926 cPreviousChar = 0x00;
\r
1927 pcTokenStart = NULL;
\r
1933 /* A "." was found. Remove it. */
\r
1937 if( cPreviousChar == '/' )
\r
1939 /* This is the start of a new token. */
\r
1940 pcTokenStart = pcChar;
\r
1943 cPreviousChar = *pcChar;
\r
1947 /* Make sure there is no / on the end of the string, being careful not to
\r
1948 remove the / at the beginning of the string. */
\r
1949 if( *( pcChar - 1 ) == '/' )
\r
1951 if( ( pcChar - 1 ) != pcReturn )
\r
1953 *( pcChar - 1 ) = 0x00;
\r
1960 #endif /* ffconfigHAS_CWD */
\r
1961 /*-----------------------------------------------------------*/
\r
1963 #if( ffconfigHAS_CWD == 1 )
\r
1965 /*static*/ const char *prvABSPath( const char *pcPath )
\r
1968 WorkingDirectory_t *pxWorkingDirectory = pxFindCWD();
\r
1970 configASSERT( pxWorkingDirectory );
\r
1972 if( ( pcPath[ 0 ] ) == '/' )
\r
1974 /* If the path starts with a slash it does not start with a relative
\r
1975 path. Copy the string into a thread local buffer so it can be
\r
1976 manipulated without risk of attempting to write to read only
\r
1978 snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s", pcPath );
\r
1979 pcReturn = pxWorkingDirectory->pcFileName;
\r
1983 /* Insert the working directory into the front of the path. */
\r
1984 if( pxWorkingDirectory->pcCWD[ 1 ] == 0x00 )
\r
1986 /* In the root, so don't add a '/' between the CWD and the
\r
1988 snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "/%s", pcPath );
\r
1992 snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s/%s", pxWorkingDirectory->pcCWD, pcPath );
\r
1995 pcReturn = pxWorkingDirectory->pcFileName;
\r
1998 /* Make any adjustments necessitated by relative paths. */
\r
1999 prvProcessRelativePaths( pcReturn );
\r
2004 #endif /* ffconfigHAS_CWD */
\r
2006 #if( ffconfigTIME_SUPPORT == 1 )
\r
2008 static uint32_t prvFileTime( FF_SystemTime_t *pxTime )
\r
2010 FF_TimeStruct_t xTime;
\r
2013 xTime.tm_sec = pxTime->Second;
\r
2014 xTime.tm_min = pxTime->Minute;
\r
2015 xTime.tm_hour = pxTime->Hour;
\r
2016 xTime.tm_mday = pxTime->Day;
\r
2017 xTime.tm_mon = pxTime->Month - 1;
\r
2018 xTime.tm_year = pxTime->Year - 1900;
\r
2020 xReturn = FreeRTOS_mktime( &xTime );
\r
2026 /*-----------------------------------------------------------*/
\r