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 DIR Handles Directory Traversal
\r
32 * @brief Handles DIR access and traversal.
\r
34 * Provides FindFirst() and FindNext() Interfaces
\r
37 #include "ff_headers.h"
\r
41 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
45 #if defined( WIN32 )
\r
46 #define wcsicmp _wcsicmp
\r
48 #define wcsicmp wcscasecmp
\r
52 /* Calculate a simple LFN checmsum. */
\r
53 static uint8_t FF_CreateChkSum( const uint8_t *pa_pShortName );
\r
55 static BaseType_t FF_ShortNameExists( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcShortName, FF_Error_t *pxError );
\r
57 #if( ffconfigSHORTNAME_CASE != 0 )
\r
58 /* For short-name entries, NT/XP etc store case information in byte 0x0c
\r
59 * Use this to show proper case of "README.txt" or "source.H".
\r
61 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
62 static void FF_CaseShortName( FF_T_WCHAR *pcName, uint8_t attrib );
\r
64 static void FF_CaseShortName( char *pcName, uint8_t attrib );
\r
66 #endif /* ffconfigSHORTNAME_CASE */
\r
68 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
70 /* For unicode, the short name can be expanded to wchar
\r
71 * by inserting zero's.
\r
73 static void FF_ShortNameExpand( FF_T_WCHAR * );
\r
78 * Transform a name as stored on disk "README__TXT"
\r
79 * to a nul-terminated string: "README.TXT", "FILE001".
\r
80 * A dot is only added if an extension is present.
\r
82 static void FF_ProcessShortName( char *pcName );
\r
84 #if( ffconfigTIME_SUPPORT != 0 )
\r
85 static void FF_PlaceTime( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime );
\r
86 static void FF_PlaceDate( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime );
\r
87 static void FF_GetTime( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t ulOffset );
\r
88 static void FF_GetDate( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t ulOffset );
\r
89 #endif /* ffconfigTIME_SUPPORT */
\r
90 static FF_Error_t FF_Traverse( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext );
\r
91 static int32_t FF_FindFreeDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, uint16_t usSequential );
\r
93 #if( ffconfigLFN_SUPPORT != 0 )
\r
94 static int8_t FF_CreateLFNEntry( uint8_t *pucEntryBuffer, uint8_t *pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum );
\r
95 #endif /* ffconfigLFN_SUPPORT */
\r
97 static BaseType_t FF_ValidShortChar( char cChar );
\r
99 #if( ffconfigLFN_SUPPORT != 0 )
\r
100 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
101 static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR *pcName, uint8_t ucCheckSum, uint16_t usEntry );
\r
103 static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcName, uint8_t ucCheckSum, uint16_t usEntry );
\r
107 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
108 static void FF_MakeNameCompliant( FF_T_WCHAR *pcName );
\r
110 static void FF_MakeNameCompliant( char *pcName );
\r
113 #if ( FF_NOSTRCASECMP == 0 )
\r
114 static portINLINE unsigned char prvToLower( unsigned char c )
\r
116 unsigned char cReturnChar;
\r
117 if( c >= 'A' && c <= 'Z' )
\r
119 cReturnChar = c + 0x20;
\r
126 return cReturnChar;
\r
129 int strcasecmp( const char *pcString1, const char *pcString2 )
\r
131 unsigned char c1,c2;
\r
136 c1 = ( unsigned char ) prvToLower( ( unsigned char ) c1 );
\r
137 c2 = ( unsigned char ) prvToLower( ( unsigned char ) c2 );
\r
139 while( ( c1 == c2 ) && ( c1 != '\0' ) );
\r
141 return ( int ) c1 - c2;
\r
142 } /* strcasecmp() */
\r
144 /*-----------------------------------------------------------*/
\r
146 static uint8_t FF_CreateChkSum( const uint8_t *pa_pShortName )
\r
149 uint8_t ChkSum = 0;
\r
151 for( cNameLen = 11; cNameLen != 0; cNameLen-- )
\r
153 ChkSum = ( uint8_t )
\r
154 ( ( ( ChkSum & 1 ) ? 0x80 : 0 ) + ( ChkSum >> 1 ) + *( pa_pShortName++ ) );
\r
158 } /* FF_CreateChkSum() */
\r
159 /*-----------------------------------------------------------*/
\r
161 /* _HT_ Does not need a wchar version because a short name is treated a normal string of bytes */
\r
162 static BaseType_t FF_ShortNameExists( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcShortName, FF_Error_t *pxError )
\r
165 const uint8_t *pucEntryBuffer = NULL; /* initialisation not necessary, just for the compiler */
\r
167 FF_FetchContext_t xFetchContext;
\r
168 char pcMyShortName[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
169 BaseType_t xResult = -1;
\r
171 #if( ffconfigHASH_CACHE != 0 )
\r
175 *pxError = FF_ERR_NONE;
\r
177 #if( ffconfigHASH_CACHE != 0 )
\r
179 if( !FF_DirHashed( pxIOManager, ulDirCluster ) )
\r
181 /* Hash the directory. */
\r
182 FF_HashDir( pxIOManager, ulDirCluster );
\r
185 #if ffconfigHASH_FUNCTION == CRC16
\r
187 ulHash = ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) );
\r
189 #else /* ffconfigHASH_FUNCTION == CRC8 */
\r
191 ulHash = ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) );
\r
195 /* FF_CheckDirentHash result: 0 not found, 1 found, -1 directory not hashed */
\r
196 xResult = FF_CheckDirentHash( pxIOManager, ulDirCluster, ulHash );
\r
204 *pxError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
206 if( FF_isERR( *pxError ) == pdFALSE )
\r
208 for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
\r
210 /* Call FF_FetchEntryWithContext only once for every 512-byte block */
\r
211 if( ( xIndex == 0 ) ||
\r
212 ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
\r
214 *pxError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );
\r
215 if( FF_isERR( *pxError ) )
\r
219 pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
\r
223 /* Advance 32 bytes to get the next directory entry. */
\r
224 pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
\r
227 if( FF_isEndOfDir( pucEntryBuffer ) )
\r
231 if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
\r
233 ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );
\r
234 if( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN )
\r
236 memcpy( pcMyShortName, pucEntryBuffer, sizeof( pcMyShortName ) );
\r
237 FF_ProcessShortName( pcMyShortName );
\r
238 if( strcmp( ( const char * )pcShortName, ( const char * )pcMyShortName ) == 0 )
\r
245 } /* for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
\r
247 *pxError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
251 } /* FF_ShortNameExists() */
\r
252 /*-----------------------------------------------------------*/
\r
254 /* _HT_ When adding many files to a single directory, FF_FindEntryInDir was sometimes */
\r
255 /* _HT_ called 3 times before inserting a single file. With these changes it is called one time only */
\r
256 /* _HT_ pxFindParams caches some information: */
\r
257 /* _HT_ 1: the first free entry 2: whether the short-name version already exists */
\r
258 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
259 uint32_t FF_FindEntryInDir( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, const FF_T_WCHAR *pcName, uint8_t pa_Attrib, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
\r
261 uint32_t FF_FindEntryInDir( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, const char *pcName, uint8_t pa_Attrib, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
\r
264 FF_FetchContext_t xFetchContext;
\r
265 /* const pointer to read from pBuffer */
\r
266 const uint8_t *src = NULL;
\r
267 /* As we're walking through a directory, we might as well
\r
268 find the first free entry to help FF_FindFreeDirent( )
\r
269 The result will be stored in 'pxFindParams->lFreeEntry' */
\r
270 BaseType_t entriesNeeded;
\r
271 BaseType_t freeCount = 0;
\r
273 /* If the file name fits into a short file name
\r
274 then the existence of that short file name will be checked as well. */
\r
275 BaseType_t testShortname;
\r
276 uint32_t xResult = 0ul;
\r
277 #if( ffconfigUNICODE_UTF8_SUPPORT == 1 )
\r
281 #if( ffconfigLFN_SUPPORT != 0 )
\r
282 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
283 FF_T_WCHAR *pcCurPtr; /* Pointer to store a LFN. */
\r
284 FF_T_WCHAR *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
\r
286 char *pcCurPtr; /* Pointer to store a LFN. */
\r
287 char *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
\r
288 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
290 uint16_t lfnItem = 0;
\r
291 uint8_t ucCheckSum = 0;
\r
292 BaseType_t xLFNCount = 0;
\r
293 BaseType_t xLFNTotal = 0;
\r
294 uint8_t lastAttrib;
\r
297 #endif /* ffconfigLFN_SUPPORT */
\r
299 #if( ffconfigLFN_SUPPORT != 0 )
\r
301 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
302 BaseType_t NameLen = ( BaseType_t ) wcslen( ( const char * )pcName );
\r
304 BaseType_t NameLen = ( BaseType_t ) strlen( ( const char * )pcName );
\r
306 /* Find enough places for the LFNs and the ShortName. */
\r
307 entriesNeeded = ( uint8_t )( ( NameLen + 12 ) / 13 ) + 1;
\r
313 #endif /* ffconfigLFN_SUPPORT */
\r
315 if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
\r
317 testShortname = pdTRUE;
\r
321 testShortname = pdFALSE;
\r
324 pxDirEntry->ucAttrib = 0;
\r
326 if( ( pxFindParams->ulFlags & FIND_FLAG_CREATE_FLAG ) != 0 )
\r
328 /* A file is to be created: keep track of the first free entry big enough
\r
329 to hold this file name. */
\r
330 pxFindParams->lFreeEntry = -1;
\r
334 pxFindParams->lFreeEntry = 0;
\r
337 xError = FF_InitEntryFetch( pxIOManager, pxFindParams->ulDirCluster, &xFetchContext );
\r
339 if( FF_isERR( xError ) == pdFALSE )
\r
341 for( pxDirEntry->usCurrentItem = 0; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
\r
343 if( ( src == NULL ) ||
\r
344 ( src >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
\r
346 xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &xFetchContext, NULL );
\r
348 if( FF_isERR( xError ) != pdFALSE )
\r
352 src = xFetchContext.pxBuffer->pucBuffer;
\r
356 /* Advance 32 bytes. */
\r
357 src += FF_SIZEOF_DIRECTORY_ENTRY;
\r
360 if( FF_isEndOfDir( src ) )
\r
362 /* 0x00 end-of-dir. */
\r
366 if( FF_isDeleted( src ) )
\r
368 /* Entry not used or deleted. */
\r
369 pxDirEntry->ucAttrib = 0;
\r
370 if( ( pxFindParams->lFreeEntry < 0 ) && ( ++freeCount == entriesNeeded ) )
\r
372 /* Remember the beginning entry in the sequential sequence. */
\r
373 pxFindParams->lFreeEntry = ( pxDirEntry->usCurrentItem - ( entriesNeeded - 1 ) );
\r
378 /* The current entry is in use, so reset the free-entry-counter */
\r
380 #if( ffconfigLFN_SUPPORT != 0 )
\r
382 lastAttrib = pxDirEntry->ucAttrib;
\r
386 pxDirEntry->ucAttrib = FF_getChar( src, FF_FAT_DIRENT_ATTRIB );
\r
388 if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
\r
390 /* LFN Processing. */
\r
391 #if( ffconfigLFN_SUPPORT != 0 )
\r
393 if( ( xLFNCount == 0 ) || ( lastAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN )
\r
395 xLFNTotal = xLFNCount = ( BaseType_t )( src[ 0 ] & ~0x40 );
\r
396 lfnItem = pxDirEntry->usCurrentItem;
\r
397 ucCheckSum = FF_getChar( src, FF_FAT_LFN_CHECKSUM );
\r
398 pcLastPtr[ -1 ] = '\0';
\r
401 if( xLFNCount != 0 )
\r
404 pcCurPtr = pxDirEntry->pcFileName + ( xLFNCount * 13 );
\r
407 This section needs to extract the name and do the comparison
\r
408 dependent on UNICODE settings in the FreeRTOSFATConfig.h file.
\r
410 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
412 /* Add UTF-16 Routine here. */
\r
413 /* Copy first 5 UTF-16 chars ( 10 bytes ). */
\r
414 memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_1 ], 10 );
\r
415 /* Increment Filename pointer 5 utf16 chars. */
\r
418 /* Copy next 6 chars ( 12 bytes ). */
\r
419 memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_2 ], 12 );
\r
422 /* You're getting the idea by now! */
\r
423 memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_3 ], 4 );
\r
425 } /* ffconfigUNICODE_UTF16_SUPPORT */
\r
426 #elif( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
428 /* UTF-8 Routine here. */
\r
429 for( xIndex = 0; ( xIndex < 5 ) && ( pcCurPtr < pcLastPtr ); xIndex++ )
\r
431 /* Was there a surrogate sequence? -- Add handling here. */
\r
432 utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_1 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
\r
433 if( utf8Error > 0 )
\r
435 pcCurPtr += utf8Error;
\r
439 if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
\r
441 /* Handle potential surrogate sequence across entries. */
\r
446 for( xIndex = 0; xIndex < 6 && pcCurPtr < pcLastPtr; xIndex++ )
\r
448 /* Was there a surrogate sequence? -- To add handling here. */
\r
449 utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_2 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
\r
450 if( utf8Error > 0 )
\r
452 pcCurPtr += utf8Error;
\r
456 if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
\r
458 /* Handle potential surrogate sequence across entries. */
\r
463 for( xIndex = 0; xIndex < 2 && pcCurPtr < pcLastPtr; xIndex++ )
\r
465 /* Was there a surrogate sequence? -- To add handling here. */
\r
466 utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_3 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
\r
467 if( utf8Error > 0 )
\r
469 pcCurPtr += utf8Error;
\r
473 if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
\r
475 /* Handle potential surrogate sequence across entries. */
\r
479 } /* ffconfigUNICODE_UTF8_SUPPORT */
\r
481 { /* use ASCII notation. */
\r
482 for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
484 *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_1 + xIndex ];
\r
486 for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
488 *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_2 + xIndex ];
\r
491 for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
493 *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_3 + xIndex ];
\r
496 #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && !( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */
\r
497 if( ( xLFNCount == xLFNTotal - 1 ) && ( pcCurPtr < pcLastPtr ) )
\r
499 *pcCurPtr = '\0'; /* Important when name len is multiple of 13. */
\r
501 } /* if( xLFNCount ) */
\r
503 #endif /* ffconfigLFN_SUPPORT */
\r
507 if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) == FF_FAT_ATTR_VOLID )
\r
509 #if( ffconfigLFN_SUPPORT != 0 )
\r
513 #endif /* ffconfigLFN_SUPPORT */
\r
517 #if( ffconfigLFN_SUPPORT != 0 )
\r
518 if( ( xLFNTotal == 0 ) || ( ucCheckSum != FF_CreateChkSum( src ) ) )
\r
519 #endif /* ffconfigLFN_SUPPORT */
\r
521 /* This entry has only a short name, or the checksum isn't correct
\r
522 * Use the short name for comparison */
\r
523 memcpy( pxDirEntry->pcFileName, src, 11 );
\r
524 FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName );
\r
525 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
527 /* FileName now contains a 8-bit short name
\r
528 Expand it to a FF_T_WCHAR string. */
\r
529 FF_ShortNameExpand( pxDirEntry->pcFileName );
\r
531 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
532 #if( ffconfigLFN_SUPPORT != 0)
\r
536 #endif /* ffconfigLFN_SUPPORT */
\r
539 /* This function FF_FindEntryInDir( ) is either called with
\r
540 * pa_Attrib==0 or with pa_Attrib==FF_FAT_ATTR_DIR
\r
541 * In the last case the caller is looking for a directory */
\r
542 if( ( pxDirEntry->ucAttrib & pa_Attrib ) == pa_Attrib )
\r
544 if( testShortname )
\r
546 /* Both strings are stored in the directory format
\r
547 * e.g. "README TXT", without a dot */
\r
548 if( memcmp( src, pxFindParams->pcEntryBuffer, 11 ) == 0 )
\r
550 pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED | FIND_FLAG_SHORTNAME_FOUND;
\r
554 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
555 if( wcsicmp( ( const char * )pcName, ( const char * )pxDirEntry->pcFileName ) == 0 )
\r
557 if( FF_stricmp( ( const char * )pcName, ( const char * )pxDirEntry->pcFileName ) == 0 )
\r
558 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
560 /* Finally get the complete information. */
\r
561 #if( ffconfigLFN_SUPPORT != 0 )
\r
564 xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, ( uint16_t ) lfnItem, &xFetchContext );
\r
565 if( FF_isERR( xError ) )
\r
571 #endif /* ffconfigLFN_SUPPORT */
\r
573 FF_PopulateShortDirent( pxIOManager, pxDirEntry, src );
\r
574 /* HT: usCurrentItem wasn't increased here. */
\r
575 pxDirEntry->usCurrentItem++;
\r
577 /* Object found, the cluster number will be returned. */
\r
578 xResult = pxDirEntry->ulObjectCluster;
\r
582 #if( ffconfigLFN_SUPPORT != 0 )
\r
587 } /* for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */
\r
590 FF_Error_t xTempError;
\r
591 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
592 if( FF_isERR( xError ) == pdFALSE )
\r
594 xError = xTempError;
\r
597 } /* if( FF_isERR( xError ) == pdFALSE ) */
\r
599 if( FF_isERR( xError ) == pdFALSE )
\r
601 /* If a free entry wasn't found yet, put it to the current (last) item */
\r
602 if( pxFindParams->lFreeEntry < 0 )
\r
604 pxFindParams->lFreeEntry = pxDirEntry->usCurrentItem;
\r
607 /* If we were checking the existence of the short-name
\r
608 set the Checked flag now */
\r
609 if( testShortname )
\r
611 pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED;
\r
615 if( pxError != NULL )
\r
621 } /* FF_FindEntryInDir() */
\r
622 /*-----------------------------------------------------------*/
\r
628 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
629 uint32_t FF_FindDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath, uint16_t pathLen, FF_Error_t *pxError )
\r
631 uint32_t FF_FindDir( FF_IOManager_t *pxIOManager, const char *pcPath, uint16_t pathLen, FF_Error_t *pxError )
\r
634 uint16_t it = 0; /* Re-entrancy Variables for FF_strtok( ). */
\r
635 BaseType_t last = pdFALSE;
\r
636 FF_DirEnt_t xMyDirectory;
\r
637 FF_FindParams_t xFindParams;
\r
641 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
642 FF_T_WCHAR mytoken[ ffconfigMAX_FILENAME ];
\r
645 char mytoken[ ffconfigMAX_FILENAME ];
\r
649 #if( ffconfigPATH_CACHE != 0 )
\r
653 memset( &xFindParams, '\0', sizeof( xFindParams ) );
\r
654 xFindParams.ulDirCluster = pxIOManager->xPartition.ulRootDirCluster;
\r
656 xError = FF_ERR_NONE;
\r
660 /* Must be the root directory.
\r
661 'xFindParams.ulDirCluster' will be returned containing 'pxIOManager->xPartition.ulRootDirCluster'. */
\r
666 /* Only the root directory '/' shall have a trailing slash in its name. */
\r
667 if( ( pcPath[ pathLen - 1 ] == '\\' ) || ( pcPath[ pathLen - 1 ] == '/' ) )
\r
673 #if( ffconfigPATH_CACHE != 0 ) /* Is the requested pcPath in the PATH CACHE? */
\r
675 FF_PendSemaphore( pxIOManager->pvSemaphore ); /* Thread safety on shared object! */
\r
677 for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ )
\r
679 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
680 if( wcslen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t )pathLen )
\r
682 if( strlen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t )pathLen )
\r
685 if( FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) )
\r
687 xFindParams.ulDirCluster = pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster;
\r
694 FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
\r
696 #endif /* ffconfigPATH_CACHE */
\r
698 if( xFound == pdFALSE )
\r
700 pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );
\r
704 xMyDirectory.usCurrentItem = 0;
\r
705 xFindParams.ulDirCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcToken, ( uint8_t ) FF_FAT_ATTR_DIR, &xMyDirectory, &xError );
\r
707 if( xFindParams.ulDirCluster == 0ul )
\r
712 pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );
\r
714 } while( pcToken != NULL );
\r
715 if( ( pcToken != NULL ) &&
\r
716 ( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) ) )
\r
718 xError = ( FF_Error_t ) ( FF_FINDDIR | FF_ERR_FILE_INVALID_PATH );
\r
720 #if( ffconfigPATH_CACHE != 0 ) /* Update the PATH CACHE with a new PATH. */
\r
722 /* Only cache if the dir was actually found! */
\r
723 if( ( FF_isERR( xError ) == pdFALSE ) && ( xFindParams.ulDirCluster != 0ul ) )
\r
725 FF_PendSemaphore( pxIOManager->pvSemaphore );
\r
727 if( pathLen < ffconfigMAX_FILENAME ) /* Ensure the PATH won't cause a buffer overrun. */
\r
729 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
731 memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen * sizeof( FF_T_WCHAR ) );
\r
735 memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen );
\r
739 pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath[ pathLen ] = '\0';
\r
740 pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].ulDirCluster = xFindParams.ulDirCluster;
\r
742 pxIOManager->xPartition.ulPCIndex += 1;
\r
743 if( pxIOManager->xPartition.ulPCIndex >= ffconfigPATH_CACHE_DEPTH )
\r
745 pxIOManager->xPartition.ulPCIndex = 0;
\r
749 FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
\r
752 #endif /* ffconfigPATH_CACHE */
\r
753 } /* if( pathLen > 1 ) */
\r
755 if( pxError != NULL )
\r
760 return xFindParams.ulDirCluster;
\r
761 } /* FF_FindDir() */
\r
762 /*-----------------------------------------------------------*/
\r
765 #if( ffconfigSHORTNAME_CASE != 0 )
\r
768 * For short-name entries, NT/XP etc store case information in byte 0x0c
\r
769 * Use this to show proper case of "README.txt" or "source.H"
\r
771 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
772 static void FF_CaseShortName( FF_T_WCHAR *pcName, uint8_t attrib )
\r
774 static void FF_CaseShortName( char *pcName, uint8_t attrib )
\r
777 uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;
\r
779 for ( ; *pcName != '\0'; pcName++ )
\r
781 if( *pcName == '.' )
\r
783 testAttrib = FF_FAT_CASE_ATTR_EXT;
\r
785 else if ( ( attrib & testAttrib ) != 0 )
\r
787 if( ( *pcName >= 'A' ) && ( *pcName <= 'Z' ) )
\r
792 else if( ( *pcName >= 'a' ) && ( *pcName <= 'z' ) )
\r
798 #endif /* ffconfigSHORTNAME_CASE */
\r
799 /*-----------------------------------------------------------*/
\r
801 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
804 * Expand a short-name, adding a zero after each character
\r
807 static void FF_ShortNameExpand( FF_T_WCHAR *pFileName )
\r
809 int8_t *pSource = ( ( int8_t * ) pFileName ) + 13;
\r
810 int8_t *pTarget = ( ( int8_t * ) pFileName ) + 26;
\r
811 /* "aaaaaaaa.eee": 12 characters plus a zero makes 13
\r
812 * Copy from the right to the left */
\r
813 while( pTarget > ( int8_t * ) pFileName )
\r
816 pTarget[ -2 ] = *( --pSource );
\r
820 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
821 /*-----------------------------------------------------------*/
\r
827 static void FF_ProcessShortName( char *pcName )
\r
829 char pcShortName[ 13 ];
\r
830 char *pcTarget = pcName;
\r
832 memcpy( pcShortName, pcName, 11 );
\r
834 for( iSource = 0; iSource < 11; iSource++ )
\r
836 if( pcShortName[ iSource ] == 0x20 )
\r
848 *( pcTarget++ ) = '.';
\r
850 *( pcTarget++ ) = pcShortName[ iSource ];
\r
855 /*-----------------------------------------------------------*/
\r
858 #if( ffconfigTIME_SUPPORT != 0 )
\r
859 static void FF_PlaceTime( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime )
\r
863 /* HT time changes:
\r
864 E.g. Unzip needs to use original time rather than
\r
865 the result of FF_GetSystemTime */
\r
868 myShort |= ( ( pxTime->Hour << 11 ) & 0xF800 );
\r
869 myShort |= ( ( pxTime->Minute << 5 ) & 0x07E0 );
\r
870 myShort |= ( ( pxTime->Second / 2 ) & 0x001F );
\r
871 FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
\r
873 #endif /* ffconfigTIME_SUPPORT */
\r
874 /*-----------------------------------------------------------*/
\r
877 #if( ffconfigTIME_SUPPORT != 0 )
\r
878 static void FF_PlaceDate( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime )
\r
882 /* HT time changes:
\r
883 Unzip needs to use original date rather than
\r
884 the current date, so make it a parameter. */
\r
886 myShort |= ( ( ( pxTime->Year- 1980 ) << 9 ) & 0xFE00 ) ;
\r
887 myShort |= ( ( pxTime->Month << 5 ) & 0x01E0 );
\r
888 myShort |= ( pxTime->Day & 0x001F );
\r
889 FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
\r
891 #endif /* ffconfigTIME_SUPPORT */
\r
892 /*-----------------------------------------------------------*/
\r
895 #if( ffconfigTIME_SUPPORT != 0 )
\r
896 static void FF_GetTime( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t Offset )
\r
899 myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
\r
900 pxTime->Hour = ( ( ( myShort & 0xF800 ) >> 11 ) & 0x001F );
\r
901 pxTime->Minute = ( ( ( myShort & 0x07E0 ) >> 5 ) & 0x003F );
\r
902 pxTime->Second = 2 * ( myShort & 0x01F );
\r
904 #endif /* ffconfigTIME_SUPPORT */
\r
905 /*-----------------------------------------------------------*/
\r
908 #if( ffconfigTIME_SUPPORT != 0 )
\r
909 static void FF_GetDate( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t Offset )
\r
912 myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
\r
913 pxTime->Year = 1980 + ( ( ( myShort & 0xFE00 ) >> 9 ) & 0x07F );
\r
914 pxTime->Month = ( ( ( myShort & 0x01E0 ) >> 5 ) & 0x000F );
\r
915 pxTime->Day = myShort & 0x01F;
\r
917 #endif /* ffconfigTIME_SUPPORT */
\r
918 /*-----------------------------------------------------------*/
\r
920 void FF_PopulateShortDirent( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const uint8_t *pucEntryBuffer )
\r
922 memcpy( pxDirEntry->pcFileName, pucEntryBuffer, 11 ); /* Copy the filename into the Dirent object. */
\r
923 #if( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
\r
924 memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
\r
925 pxDirEntry->pcShortName[ 11 ] = '\0';
\r
926 FF_ProcessShortName( pxDirEntry->pcShortName ); /* For debuggers only. */
\r
928 FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName ); /* Format the shortname, for pleasant viewing. */
\r
930 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
931 /* FileName contains a 8-bit short name
\r
932 * Expand it to a FF_T_WCHAR string */
\r
933 FF_ShortNameExpand( pxDirEntry->pcFileName );
\r
936 ( void )pxIOManager; /* Silence a compiler warning, about not referencing pxIOManager. */
\r
938 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
939 FF_tolower( pxDirEntry->pcFileName, ( uint32_t )wcslen( pxDirEntry->pcFileName ) );
\r
941 FF_tolower( pxDirEntry->pcFileName, ( uint32_t )strlen( pxDirEntry->pcFileName ) );
\r
944 /* Get the item's Cluster address. */
\r
945 pxDirEntry->ulObjectCluster =
\r
946 ( ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
\r
947 ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );
\r
948 #if( ffconfigTIME_SUPPORT != 0 )
\r
949 /* Get the creation Time & Date. */
\r
950 FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
\r
951 FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );
\r
952 /* Get the modified Time & Date
\r
953 HT Here xCreateTime became xModifiedTime: */
\r
954 FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
\r
955 FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
\r
956 /* Get the last accessed Date. */
\r
957 FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
\r
958 pxDirEntry->xAccessedTime.Hour = 0;
\r
959 pxDirEntry->xAccessedTime.Minute = 0;
\r
960 pxDirEntry->xAccessedTime.Second = 0;
\r
962 /* Get the filesize. */
\r
963 pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_FILESIZE ) );
\r
964 /* Get the attribute. */
\r
965 pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) );
\r
967 /*-----------------------------------------------------------*/
\r
970 /* Initialises a context object for FF_FetchEntryWithContext( */
\r
971 FF_Error_t FF_InitEntryFetch( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_FetchContext_t *pxContext )
\r
975 memset( pxContext, 0, sizeof( FF_FetchContext_t ) );
\r
977 /* Get the total length of the chain. */
\r
978 pxContext->ulChainLength = FF_GetChainLength( pxIOManager, ulDirCluster, NULL, &xError );
\r
980 if( FF_isERR( xError ) == pdFALSE )
\r
982 pxContext->ulDirCluster = ulDirCluster;
\r
983 pxContext->ulCurrentClusterLCN = ulDirCluster;
\r
985 if( pxIOManager->xPartition.ucType != FF_T_FAT32 )
\r
987 /* Handle Root Dirs that don't have cluster chains! */
\r
988 if( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
\r
990 /* This is a RootDIR, special consideration needs to be made, because it doesn't have a Cluster chain! */
\r
991 pxContext->ulChainLength = pxIOManager->xPartition.ulRootDirSectors / pxIOManager->xPartition.ulSectorsPerCluster;
\r
992 if( pxContext->ulChainLength == 0 ) /* Some media has ulRootDirSectors < ulSectorsPerCluster. This is wrong, as it should be atleast 1 cluster! */
\r
994 pxContext->ulChainLength = 1;
\r
1001 } /* FF_InitEntryFetch() */
\r
1002 /*-----------------------------------------------------------*/
\r
1004 FF_Error_t FF_CleanupEntryFetch( FF_IOManager_t *pxIOManager, FF_FetchContext_t *pxContext )
\r
1006 FF_Error_t xError = FF_ERR_NONE;
\r
1007 if( pxContext->pxBuffer )
\r
1009 xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
\r
1010 pxContext->pxBuffer = NULL;
\r
1014 } /* FF_CleanupEntryFetch() */
\r
1015 /*-----------------------------------------------------------*/
\r
1019 * @brief Find the cluster for a given Entry within a directory
\r
1020 * @brief Make an exception for the root directory ( non FAT32 only ):
\r
1021 * @brief Just calculate the cluster ( don't consult the actual FAT )
\r
1023 * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
\r
1024 * @param ulEntry The sequence number of the entry of interest
\r
1025 * @param pxContext Context of current search
\r
1027 * @Return FF_ERR_NONE on success
\r
1028 * @Return Possible error returned by FF_TraverseFAT( ) or END_OF_DIR
\r
1031 * - pxContext->ulCurrentClusterNum : relative cluster number ( 0 <= Num < ulChainLength )
\r
1032 * - pxContext->ulCurrentClusterLCN : fysical cluster on the partition
\r
1035 static FF_Error_t FF_Traverse( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext )
\r
1037 uint32_t ulClusterNum = FF_getClusterChainNumber( pxIOManager, ulEntry, ( uint16_t ) FF_SIZEOF_DIRECTORY_ENTRY );
\r
1038 FF_Error_t xError = FF_ERR_NONE;
\r
1040 /* Check if we're past the last cluster ( ulChainLength is also valid for root sectors ). */
\r
1041 if( ( ulClusterNum + 1 ) > pxContext->ulChainLength )
\r
1043 xError = FF_ERR_DIR_END_OF_DIR | FF_TRAVERSE; /* End of Dir was reached! */
\r
1045 else if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
\r
1046 ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
\r
1048 /* Double-check if the entry number isn't too high. */
\r
1049 if( ulEntry > ( ( pxIOManager->xPartition.ulRootDirSectors * pxIOManager->xPartition.usBlkSize ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
\r
1051 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FETCHENTRYWITHCONTEXT );
\r
1055 pxContext->ulCurrentClusterLCN = pxContext->ulDirCluster;
\r
1058 else if( ulClusterNum != pxContext->ulCurrentClusterNum )
\r
1060 /* Traverse the fat gently! */
\r
1061 if( ulClusterNum > pxContext->ulCurrentClusterNum )
\r
1063 /* Start traverse from the current entry. */
\r
1064 pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulCurrentClusterLCN, ( ulClusterNum - pxContext->ulCurrentClusterNum ), &xError );
\r
1068 /* Start traverse from the beginning. */
\r
1069 pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulDirCluster, ulClusterNum, &xError );
\r
1072 if( FF_isERR( xError ) == pdFALSE )
\r
1074 pxContext->ulCurrentClusterNum = ulClusterNum;
\r
1078 } /* FF_Traverse() */
\r
1079 /*-----------------------------------------------------------*/
\r
1081 FF_Error_t FF_FetchEntryWithContext( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext, uint8_t *pEntryBuffer )
\r
1083 uint32_t ulItemLBA;
\r
1084 uint32_t ulRelItem;
\r
1085 FF_Error_t xError;
\r
1087 xError = FF_Traverse( pxIOManager, ulEntry, pxContext );
\r
1089 if( FF_isERR( xError ) == pdFALSE )
\r
1091 ulRelItem = FF_getMinorBlockEntry ( pxIOManager, ulEntry, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
\r
1093 ulItemLBA = FF_Cluster2LBA ( pxIOManager, pxContext->ulCurrentClusterLCN ) +
\r
1094 FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
\r
1095 if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
\r
1096 ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
\r
1098 ulItemLBA += ( ulEntry / ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) /
\r
1099 FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
\r
1102 ulItemLBA = FF_getRealLBA ( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
\r
1104 if( ( pxContext->pxBuffer == NULL ) ||
\r
1105 ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
\r
1106 ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) != 0 ) )
\r
1108 if( pxContext->pxBuffer != NULL )
\r
1110 xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
\r
1111 pxContext->pxBuffer = NULL;
\r
1114 if( FF_isERR( xError ) == pdFALSE )
\r
1116 pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_READ );
\r
1117 if( pxContext->pxBuffer == NULL )
\r
1119 xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT );
\r
1124 if ( ( pEntryBuffer != NULL ) && ( pxContext->pxBuffer != NULL ) )
\r
1126 memcpy( pEntryBuffer, pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), FF_SIZEOF_DIRECTORY_ENTRY );
\r
1131 } /* FF_FetchEntryWithContext() */
\r
1132 /*-----------------------------------------------------------*/
\r
1135 FF_Error_t FF_PushEntryWithContext( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext, uint8_t *pEntryBuffer )
\r
1137 uint32_t ulItemLBA;
\r
1138 uint32_t ulRelItem;
\r
1139 FF_Error_t xError;
\r
1141 xError = FF_Traverse( pxIOManager, ulEntry, pxContext );
\r
1142 if( FF_isERR( xError ) == pdFALSE )
\r
1144 ulRelItem = FF_getMinorBlockEntry ( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
\r
1146 ulItemLBA = FF_Cluster2LBA ( pxIOManager, pxContext->ulCurrentClusterLCN ) + FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
\r
1147 if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
\r
1148 ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
\r
1150 ulItemLBA += ( ulEntry /
\r
1151 ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) / FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
\r
1154 ulItemLBA = FF_getRealLBA ( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
\r
1156 if( ( pxContext->pxBuffer == NULL ) ||
\r
1157 ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
\r
1158 ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) == 0 ) )
\r
1160 if( pxContext->pxBuffer != NULL )
\r
1162 xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
\r
1163 pxContext->pxBuffer = NULL;
\r
1165 if( FF_isERR( xError ) == pdFALSE )
\r
1167 pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_WRITE );
\r
1168 if( pxContext->pxBuffer == NULL )
\r
1170 xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT );
\r
1175 /* Now change the entry: */
\r
1176 if( pxContext->pxBuffer != NULL )
\r
1178 memcpy( pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), pEntryBuffer, FF_SIZEOF_DIRECTORY_ENTRY );
\r
1183 } /* FF_PushEntryWithContext() */
\r
1184 /*-----------------------------------------------------------*/
\r
1190 FF_Error_t FF_GetEntry( FF_IOManager_t *pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t *pxDirEntry )
\r
1192 /* A 32 byte directory entry. */
\r
1193 uint8_t ucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
1194 FF_FetchContext_t xFetchContext;
\r
1195 FF_Error_t xError;
\r
1197 #if( ffconfigLFN_SUPPORT == 0 )
\r
1198 BaseType_t xLFNCount;
\r
1200 xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
1202 if( FF_isERR( xError ) == pdFALSE )
\r
1204 xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, ucEntryBuffer );
\r
1206 if( ( FF_isERR( xError ) == pdFALSE ) &&
\r
1207 ( FF_isDeleted( ucEntryBuffer ) == pdFALSE ) )
\r
1209 if( FF_isEndOfDir( ucEntryBuffer ) != pdFALSE )
\r
1211 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_GETENTRY );
\r
1215 pxDirEntry->ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) );
\r
1217 if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
\r
1219 #if( ffconfigLFN_SUPPORT != 0 )
\r
1220 xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, usEntry, &xFetchContext );
\r
1222 /* LFN Processing. */
\r
1223 xLFNCount = ( BaseType_t )( ucEntryBuffer[0] & ~0x40 );
\r
1224 pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
\r
1227 else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
\r
1229 FF_PopulateShortDirent( pxIOManager, pxDirEntry, ucEntryBuffer );
\r
1230 pxDirEntry->usCurrentItem += 1;
\r
1235 FF_Error_t xTempError;
\r
1236 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
1237 if( FF_isERR( xError ) == pdFALSE )
\r
1239 xError = xTempError;
\r
1245 } /* FF_GetEntry() */
\r
1246 /*-----------------------------------------------------------*/
\r
1249 FF_Error_t FF_PopulateLongDirent( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, uint16_t usEntry, FF_FetchContext_t *pxFetchContext )
\r
1251 /* First get the entire name as UTF-16 from the LFN's.
\r
1252 Then transform into the API's native string format. */
\r
1254 FF_Error_t xError;
\r
1255 BaseType_t xNumLFNs;
\r
1256 uint8_t ucCheckSum;
\r
1257 /* A 32 byte directory entry. */
\r
1258 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
1259 char pcShortName[ 13 ];
\r
1261 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1262 UBaseType_t uiLfnLength = 0;
\r
1265 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
\r
1266 BaseType_t xIndex, y;
\r
1269 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 )
\r
1270 char *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
\r
1274 #if( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
1275 uint16_t nLfnBegin;
\r
1276 uint16_t usUtf8Len = 0;
\r
1277 #endif /* ffconfigUNICODE_UTF8_SUPPORT */
\r
1281 xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
\r
1282 if( FF_isERR( xError ) )
\r
1284 /* After breaking from this do {} while ( pdFALSE ) loop, xResult will be returned. */
\r
1288 xNumLFNs = ( BaseType_t )( pucEntryBuffer[0] & ~0x40 );
\r
1289 ucCheckSum = FF_getChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM );
\r
1291 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1293 /* UTF-16 Can simply get segments of the UTF-16 sequence
\r
1294 going forward in the directory entries ( but reversed order ). */
\r
1295 while( xNumLFNs > 0 )
\r
1297 /* Avoid stack intensive use of a UTF-16 buffer. Stream direct to FileName dirent field in correct format. */
\r
1298 /* memcpy direct! -UTF-16 support. */
\r
1299 memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 0, &( pucEntryBuffer[ FF_FAT_LFN_NAME_1] ), 10 );
\r
1300 memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 5, &( pucEntryBuffer[ FF_FAT_LFN_NAME_2] ), 12 );
\r
1301 memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 11, &( pucEntryBuffer[ FF_FAT_LFN_NAME_3] ), 4 );
\r
1302 uiLfnLength += 13;
\r
1304 xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
\r
1305 if( FF_isERR( xError ) )
\r
1311 if( FF_isERR( xError ) )
\r
1315 pxDirEntry->pcFileName[ uiLfnLength ] = '\0';
\r
1317 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
1319 #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
1321 /* UTF-8 Sequence, we can only convert this from the beginning, must receive entries in reverse. */
\r
1322 nLfnBegin = usEntry - 1;
\r
1324 for( xIndex = 0; xIndex < xNumLFNs; xIndex++ )
\r
1326 xError = FF_FetchEntryWithContext( pxIOManager, ( nLfnBegin + ( xNumLFNs - 1 ) - xIndex ), pxFetchContext, pucEntryBuffer );
\r
1327 if( FF_isERR( xError ) )
\r
1332 /* Now have the first part of the UTF-16 sequence. Stream into a UTF-8 sequence. */
\r
1333 for( y = 0; y < 5; y++ )
\r
1335 xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
\r
1336 ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_1 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
\r
1339 usUtf8Len += ( uint16_t )xError;
\r
1343 for( y = 0; y < 6; y++ )
\r
1345 xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
\r
1346 ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_2 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
\r
1349 usUtf8Len += ( uint16_t )xError;
\r
1353 for( y = 0; y < 2; y++ )
\r
1355 xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
\r
1356 ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_3 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
\r
1359 usUtf8Len += ( uint16_t ) xError;
\r
1364 if( FF_isERR( xError ) )
\r
1369 pxDirEntry->pcFileName[ usUtf8Len ] = '\0';
\r
1371 /* Put Entry context to correct position. */
\r
1372 xError = FF_FetchEntryWithContext( pxIOManager, usEntry-1, pxFetchContext, pucEntryBuffer );
\r
1373 if( FF_isERR( xError ) )
\r
1378 #endif /* ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */
\r
1380 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) /* No Unicode, simple ASCII. */
\r
1382 pcLastPtr[ -1 ] = '\0';
\r
1384 while( xNumLFNs-- )
\r
1386 pcCurPtr = pxDirEntry->pcFileName + ( xNumLFNs * 13 );
\r
1387 for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
1389 *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_1 + xIndex ];
\r
1392 for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
1394 *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_2 + xIndex ];
\r
1397 for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
\r
1399 *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_3 + xIndex ];
\r
1401 if( ( xNumLFNs == ( y - 1 ) ) && ( pcCurPtr < pcLastPtr ) )
\r
1405 xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
\r
1406 if( FF_isERR( xError ) )
\r
1411 if( FF_isERR( xError ) )
\r
1416 #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */
\r
1418 /* Process the Shortname. -- LFN Transformation is now complete.
\r
1419 Process the ShortName Entry. */
\r
1421 /* if SHORTNAMES must be included, simple byte copy into shortname buffer. */
\r
1422 #if( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
\r
1424 memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
\r
1425 pxDirEntry->pcShortName[ 11 ] = '\0';
\r
1426 FF_ProcessShortName( pxDirEntry->pcShortName );
\r
1428 #endif /* ( != 0 ffconfigLFN_SUPPORT ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) */
\r
1430 memcpy( pcShortName, pucEntryBuffer, 11 );
\r
1431 FF_ProcessShortName( pcShortName );
\r
1432 if( ucCheckSum != FF_CreateChkSum( pucEntryBuffer ) )
\r
1434 strcpy( pxDirEntry->pcFileName, pcShortName );
\r
1435 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1437 FF_ShortNameExpand( pxDirEntry->pcFileName );
\r
1439 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
1442 /* Finally fill in the other details. */
\r
1443 pxDirEntry->ulObjectCluster =
\r
1444 ( ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
\r
1445 ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );
\r
1447 #if( ffconfigTIME_SUPPORT != 0 )
\r
1449 /* Get the creation Time & Date. */
\r
1450 FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
\r
1451 FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );
\r
1452 /* Get the modified Time & Date. */
\r
1453 /* HT Here xCreateTime has become xModifiedTime, as it should: */
\r
1454 FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
\r
1455 FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
\r
1456 /* Get the last accessed Date. */
\r
1457 FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
\r
1458 /* HT Why should these times be zero'd ? */
\r
1459 pxDirEntry->xAccessedTime.Hour = 0;
\r
1460 pxDirEntry->xAccessedTime.Minute = 0;
\r
1461 pxDirEntry->xAccessedTime.Second = 0;
\r
1463 #endif /* ffconfigTIME_SUPPORT */
\r
1465 /* Get the filesize. */
\r
1466 pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) );
\r
1467 /* Get the attribute. */
\r
1468 pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
\r
1470 pxDirEntry->usCurrentItem = usEntry;
\r
1472 while ( pdFALSE );
\r
1475 } /* FF_PopulateLongDirent() */
\r
1476 /*-----------------------------------------------------------*/
\r
1480 * @brief Find's the first directory entry for the provided path.
\r
1482 * All values recorded in pxDirEntry must be preserved to and between calls to
\r
1485 * If ffconfigFINDAPI_ALLOW_WILDCARDS is defined, then path will have the following behaviour:
\r
1487 * path = "\" - Open the root dir, and iterate through all items.
\r
1488 * path = "\*.c" - Open the root dir, showing only files matching *.c wildcard.
\r
1489 * path = "\sub1\newdir" - Get the DIRENT for the newdir directory in /sub1/ if one exists.
\r
1490 * path = "\sub1\newdir\" - Open the directory /sub1/newdir/ and iterate through all items.
\r
1491 * path = "\sub1\newdir\*.c" - Open the directory /sub1/newdir/ and iterate through all items matching the *.c wildcard.
\r
1493 * It is important to distinguish the differences in behaviour between opening a Find operation
\r
1494 * on a path like /sub1 and /sub1/. ( /sub1 gets the sub1 dirent from the / dir, whereas /sub/ opens the sub1 dir ).
\r
1496 * Note, as compatible with other similar APIs, FreeRTOS+FAT also accepts \sub1\* for the same behaviour as
\r
1499 * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
\r
1500 * @param pxDirEntry FF_DirEnt_t object to store the entry information.
\r
1501 * @param path String to of the path to the Dir being listed.
\r
1503 * @Return 0 on success
\r
1504 * @Return FF_ERR_DEVICE_DRIVER_FAILED if device access failed.
\r
1505 * @Return -2 if Dir was not found.
\r
1508 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1509 FF_Error_t FF_FindFirst( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const FF_T_WCHAR *pcPath )
\r
1511 FF_Error_t FF_FindFirst( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const char *pcPath )
\r
1514 FF_Error_t xError;
\r
1515 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1516 uint16_t PathLen = ( uint16_t ) wcslen( pcPath );
\r
1518 uint16_t PathLen = ( uint16_t ) strlen( pcPath );
\r
1521 #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
\r
1522 BaseType_t xIndex = 0;
\r
1523 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1524 const FF_T_WCHAR *pcWildCard; /* Check for a Wild-card. */
\r
1526 const char *pcWildCard; /* Check for a Wild-card. */
\r
1530 memset( pxDirEntry, 0, sizeof( FF_DirEnt_t ) );
\r
1532 if( pxIOManager == NULL )
\r
1534 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST );
\r
1536 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
1537 else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
1539 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDFIRST );
\r
1541 #endif /* ffconfigREMOVABLE_MEDIA */
\r
1544 #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
\r
1546 pxDirEntry->pcWildCard[0] = '\0'; /* WildCard blank if its not a wildCard. */
\r
1548 pcWildCard = &pcPath[ PathLen - 1 ];
\r
1550 if( PathLen != 0 )
\r
1552 /* Open the dir of the last token. */
\r
1553 while( ( *pcWildCard != '\\' ) && ( *pcWildCard != '/' ) )
\r
1557 if( PathLen == xIndex )
\r
1564 pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen - xIndex, &xError );
\r
1565 if( FF_isERR( xError ) == pdFALSE )
\r
1567 if( pxDirEntry->ulDirCluster != 0 )
\r
1569 /* Valid Dir found, copy the wildCard to filename! */
\r
1570 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1571 wcsncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME );
\r
1573 strncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME );
\r
1575 if( pxDirEntry->pcWildCard[ xIndex - 1 ] == ':' )
\r
1577 pxDirEntry->xInvertWildCard = pdTRUE;
\r
1578 pxDirEntry->pcWildCard[ xIndex - 1 ] = '\0';
\r
1583 #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
\r
1585 /* Get the directory cluster, if it exists. */
\r
1586 pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen, &xError );
\r
1588 #endif /* ffconfigFINDAPI_ALLOW_WILDCARDS */
\r
1590 if( FF_isERR( xError ) == pdFALSE )
\r
1592 if( pxDirEntry->ulDirCluster == 0 )
\r
1594 xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_FINDFIRST );
\r
1598 /* Initialise the Fetch Context. */
\r
1599 xError = FF_InitEntryFetch( pxIOManager, pxDirEntry->ulDirCluster, &( pxDirEntry->xFetchContext ) );
\r
1600 if( FF_isERR( xError ) == pdFALSE )
\r
1602 pxDirEntry->usCurrentItem = 0;
\r
1603 xError = FF_FindNext( pxIOManager, pxDirEntry );
\r
1610 } /* FF_FindFirst() */
\r
1611 /*-----------------------------------------------------------*/
\r
1615 * @brief Get's the next Entry based on the data recorded in the FF_DirEnt_t object.
\r
1617 * All values recorded in pxDirEntry must be preserved to and between calls to
\r
1618 * FF_FindNext( ). Please see @see FF_FindFirst( ) for find initialisation.
\r
1620 * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
\r
1621 * @param pxDirEntry FF_DirEnt_t object to store the entry information. ( As initialised by FF_FindFirst( )).
\r
1623 * @Return FF_ERR_DEVICE_DRIVER_FAILED is device access failed.
\r
1626 FF_Error_t FF_FindNext( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry )
\r
1628 FF_Error_t xError;
\r
1629 BaseType_t xLFNCount;
\r
1630 const uint8_t *pucEntryBuffer = NULL;
\r
1631 #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
\r
1635 if( pxIOManager == NULL )
\r
1637 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDNEXT );
\r
1639 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
1640 else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
1642 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDNEXT );
\r
1644 #endif /* ffconfigREMOVABLE_MEDIA */
\r
1647 xError = FF_ERR_NONE;
\r
1648 for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
\r
1650 if( ( pucEntryBuffer == NULL ) ||
\r
1651 ( pucEntryBuffer >= ( pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) )
\r
1653 xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &( pxDirEntry->xFetchContext ), NULL );
\r
1655 if( FF_isERR( xError ) )
\r
1660 if( pucEntryBuffer == NULL )
\r
1662 pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
\r
1663 ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( FF_SIZEOF_SECTOR/FF_SIZEOF_DIRECTORY_ENTRY ) ) );
\r
1667 pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer;
\r
1672 pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
\r
1675 if( FF_isDeleted( pucEntryBuffer ) != pdFALSE )
\r
1677 /* The entry is not in use or deleted. */
\r
1681 if( FF_isEndOfDir( pucEntryBuffer ) )
\r
1683 /* End of directory, generate a pseudo error 'DIR_END_OF_DIR'. */
\r
1684 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
\r
1688 pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
\r
1690 if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
\r
1692 /* LFN Processing. */
\r
1693 xLFNCount = ( BaseType_t )( pucEntryBuffer[0] & ~0x40 );
\r
1694 /* Get the shortname and check if it is marked deleted. */
\r
1695 #if( ffconfigLFN_SUPPORT != 0 )
\r
1697 /* Reserve 32 bytes to hold one directory entry. */
\r
1698 uint8_t Buffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
1700 /* Fetch the shortname, and get it's checksum, or for a deleted item with
\r
1701 orphaned LFN entries. */
\r
1702 xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) ( pxDirEntry->usCurrentItem + xLFNCount ), &pxDirEntry->xFetchContext, Buffer );
\r
1703 if( FF_isERR( xError ) )
\r
1708 if( FF_isDeleted( Buffer ) == pdFALSE )
\r
1710 xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, pxDirEntry->usCurrentItem, &pxDirEntry->xFetchContext );
\r
1711 if( FF_isERR( xError ) )
\r
1715 #if( ffconfigINCLUDE_SHORT_NAME != 0 )
\r
1717 pxDirEntry->ucAttrib |= FF_FAT_ATTR_IS_LFN;
\r
1721 #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
\r
1723 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
1724 if( wcscmp( pxDirEntry->pcWildCard, L"" ) )
\r
1726 if( pxDirEntry->pcWildCard[0] )
\r
1729 b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );
\r
1730 if( pxDirEntry->xInvertWildCard != pdFALSE )
\r
1739 /* 'usCurrentItem' has already incremented by FF_PopulateLongDirent(),
\r
1740 this loop will incremente it again. */
\r
1741 pxDirEntry->usCurrentItem -= 1;
\r
1743 /* xFetchContext/usCurrentItem have changed. Update
\r
1744 'pucEntryBuffer' to point to the current buffer position. */
\r
1745 pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
\r
1746 ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( FF_SIZEOF_SECTOR/FF_SIZEOF_DIRECTORY_ENTRY ) ) );
\r
1753 #else /* ffconfigFINDAPI_ALLOW_WILDCARDS == 0 */
\r
1755 /* usCurrentItem has been incremented by FF_PopulateLongDirent().
\r
1756 Entry will be returned. */
\r
1762 #else /* ffconfigLFN_SUPPORT */
\r
1764 /* Increment 'usCurrentItem' with (xLFNCount-1),
\r
1765 the loop will do an extra increment. */
\r
1766 pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
\r
1768 #endif /* ffconfigLFN_SUPPORT */
\r
1769 } /* ( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) */
\r
1770 else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
\r
1772 /* If it's not a LFN entry, neither a Volume ID, it is a normal short name entry. */
\r
1773 FF_PopulateShortDirent( pxIOManager, pxDirEntry, pucEntryBuffer );
\r
1774 #if( ffconfigSHORTNAME_CASE != 0 )
\r
1776 /* Apply NT/XP+ bits to get correct case. */
\r
1777 FF_CaseShortName( pxDirEntry->pcFileName, FF_getChar( pucEntryBuffer, FF_FAT_CASE_OFFS ) );
\r
1781 #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
\r
1783 if( pxDirEntry->pcWildCard[ 0 ] )
\r
1785 b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );
\r
1786 if( pxDirEntry->xInvertWildCard != pdFALSE )
\r
1792 pxDirEntry->usCurrentItem += 1;
\r
1798 pxDirEntry->usCurrentItem += 1;
\r
1802 #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
\r
1804 pxDirEntry->usCurrentItem += 1;
\r
1809 } /* for ( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */
\r
1811 if( pxDirEntry->usCurrentItem == FF_MAX_ENTRIES_PER_DIRECTORY )
\r
1813 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
\r
1817 FF_Error_t xTempError;
\r
1818 xTempError = FF_CleanupEntryFetch( pxIOManager, &pxDirEntry->xFetchContext );
\r
1820 if( FF_isERR( xError ) == pdFALSE )
\r
1822 xError = xTempError;
\r
1828 } /* FF_FindNext() */
\r
1829 /*-----------------------------------------------------------*/
\r
1833 Returns >= 0 for a free dirent entry.
\r
1834 Returns < 0 with and xError code if anything goes wrong.
\r
1836 static int32_t FF_FindFreeDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, uint16_t usSequential )
\r
1838 const uint8_t *pucEntryBuffer = NULL;
\r
1839 uint16_t freeCount = 0;
\r
1840 UBaseType_t uxEntry = 0;
\r
1841 BaseType_t xEntryFound = pdFALSE;
\r
1842 FF_Error_t xError;
\r
1843 uint32_t DirLength;
\r
1844 FF_FetchContext_t xFetchContext;
\r
1845 uint32_t ulDirCluster = pxFindParams->ulDirCluster;
\r
1847 xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
1849 if( FF_isERR( xError ) == pdFALSE )
\r
1851 uxEntry = pxFindParams->lFreeEntry >= 0 ? pxFindParams->lFreeEntry : 0;
\r
1852 for ( ; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ )
\r
1854 if( ( pucEntryBuffer == NULL ) ||
\r
1855 ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
\r
1857 xError = FF_FetchEntryWithContext( pxIOManager, uxEntry, &xFetchContext, NULL );
\r
1858 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
\r
1861 xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
\r
1862 /* The value of xEntryFound will be ignored in case there was an error. */
\r
1863 xEntryFound = pdTRUE;
\r
1866 else if( FF_isERR( xError ) )
\r
1870 if( pucEntryBuffer == NULL )
\r
1872 pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer +
\r
1873 ( FF_SIZEOF_DIRECTORY_ENTRY * ( uxEntry % ( FF_SIZEOF_SECTOR / FF_SIZEOF_DIRECTORY_ENTRY ) ) );
\r
1877 pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
\r
1882 /* Advance 32 bytes to point to the next directory entry. */
\r
1883 pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
\r
1885 if( FF_isEndOfDir( pucEntryBuffer ) ) /* If its the end of the Dir, then FreeDirents from here. */
\r
1887 /* Check if the directory has enough space */
\r
1888 DirLength = xFetchContext.ulChainLength;
\r
1889 if( ( uxEntry + usSequential ) >
\r
1890 ( ( DirLength * ( ( UBaseType_t )pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->xPartition.usBlkSize ) ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
\r
1892 xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
\r
1894 xEntryFound = pdTRUE;
\r
1897 if( FF_isDeleted( pucEntryBuffer ) )
\r
1899 if( ++freeCount == usSequential )
\r
1901 xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
1902 xEntryFound = pdTRUE;
\r
1903 uxEntry = ( uxEntry - ( usSequential - 1 ) );/* Return the beginning entry in the sequential sequence. */
\r
1911 } /* for ( uxEntry = 0; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ ) */
\r
1914 FF_Error_t xTempError;
\r
1916 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
1917 if( FF_isERR( xError ) == pdFALSE )
\r
1919 xError = xTempError;
\r
1925 if( FF_isERR( xError ) == pdFALSE )
\r
1927 if( xEntryFound != pdFALSE )
\r
1929 /* No error has occurred and a free directory entry has been found. */
\r
1934 xError = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_FINDFREEDIRENT );
\r
1939 } /* FF_FindFreeDirent() */
\r
1940 /*-----------------------------------------------------------*/
\r
1942 /* _HT_ Now FF_PutEntry has a new optional parameter *pucContents */
\r
1943 /* _HT_ so it can be used FF_MkDir( ) to save some code when adding . and .. entries */
\r
1944 FF_Error_t FF_PutEntry( FF_IOManager_t *pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t *pxDirEntry, uint8_t *pucContents )
\r
1946 FF_Error_t xError;
\r
1947 /* Reserve 32 bytes to hold one directory entry. */
\r
1948 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
1949 FF_FetchContext_t xFetchContext;
\r
1951 /* HT: use the standard access routine to get the same logic for root dirs. */
\r
1952 xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
1953 if( FF_isERR( xError ) == pdFALSE )
\r
1955 xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );
\r
1956 if( FF_isERR( xError ) == pdFALSE )
\r
1958 /* Cleanup probably not necessary here?
\r
1959 FF_PushEntryWithContext checks for R/W flag. */
\r
1960 xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
1961 if( FF_isERR( xError ) == pdFALSE )
\r
1963 if ( pucContents != NULL )
\r
1965 memcpy ( pucEntryBuffer, pucContents, sizeof( pucEntryBuffer ) );
\r
1967 FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint32_t ) pxDirEntry->ucAttrib );
\r
1968 FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) ( pxDirEntry->ulObjectCluster >> 16 ) );
\r
1969 FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) ( pxDirEntry->ulObjectCluster ) );
\r
1970 FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );
\r
1971 #if( ffconfigTIME_SUPPORT != 0 )
\r
1973 FF_GetSystemTime( &pxDirEntry->xAccessedTime ); /*/< Date of Last Access. */
\r
1974 FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime );
\r
1975 FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime ); /* Last accessed date. */
\r
1976 FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
\r
1977 FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
\r
1978 FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
\r
1979 FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
\r
1981 #endif /* ffconfigTIME_SUPPORT */
\r
1982 xError = FF_PushEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );
\r
1987 FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
1990 } /* FF_PutEntry() */
\r
1991 /*-----------------------------------------------------------*/
\r
1993 static BaseType_t FF_ValidShortChar( char cChar )
\r
1995 return ( cChar >= 'A' && cChar <= 'Z' ) ||
\r
1996 ( cChar >= 'a' && cChar <= 'z' ) || /* lower-case can be stored using NT/XP attribute. */
\r
1997 ( cChar >= '0' && cChar <= '9' ) ||
\r
1998 strchr ( "$%-_@~`!(){}^#&", cChar ) != NULL;
\r
1999 } /* FF_ValidShortChar() */
\r
2000 /*-----------------------------------------------------------*/
\r
2002 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2003 void FF_CreateShortName( FF_FindParams_t *pxFindParams, const FF_T_WCHAR *pcLongName )
\r
2005 void FF_CreateShortName( FF_FindParams_t *pxFindParams, const char *pcLongName )
\r
2008 BaseType_t xIndex, xPosition, xLastDot;
\r
2011 #if( ffconfigSHORTNAME_CASE != 0 )
\r
2012 uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;
\r
2016 * "readme.TXT" will get the attribute FF_FAT_CASE_ATTR_BASE
\r
2017 * "README.txt" will get the attribute FF_FAT_CASE_ATTR_EXT
\r
2018 * "Readme.txt" can not be store as a short name */
\r
2020 pxFindParams->ucCaseAttrib = 0; /* May get the value FF_FAT_CASE_ATTR_BASE or FF_FAT_CASE_ATTR_EXT */
\r
2021 pxFindParams->ucFirstTilde = 6; /* The numerical position of the ~ */
\r
2022 pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_SET | FIND_FLAG_FITS_SHORT | FIND_FLAG_SIZE_OK;
\r
2024 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2026 NameLen = ( uint16_t ) wcslen( pcLongName );
\r
2030 NameLen = ( uint16_t ) strlen( pcLongName );
\r
2034 /* Does pcLongName fit a shortname? */
\r
2036 for( xIndex = 0, xPosition = 0, xLastDot = NameLen; xIndex < NameLen; xIndex++ )
\r
2038 if( pcLongName[ xIndex ] != '.' )
\r
2044 xLastDot = xIndex;
\r
2048 "FILENAME.EXT": NameLen = 12, xLastDot = 8, xPosition = 11
\r
2049 ".cproject" : NameLen = 9, xLastDot = 0, xPosition = 8
\r
2052 if( ( NameLen > 12 ) || /* If name is longer than 12 characters (8.3). */
\r
2053 ( NameLen - xPosition > 1 ) || /* If it contains more than 1 dot. */
\r
2054 ( NameLen - xLastDot > 4 ) || /* If the file name extension is longer than 3 characters. */
\r
2055 ( xLastDot > 8 ) ) /* If the file name base is too long. */
\r
2057 pxFindParams->ulFlags &= ~FIND_FLAG_SIZE_OK;
\r
2060 for( xIndex = 0, xPosition = 0; xIndex < 11; xPosition++ )
\r
2062 char ch = pcLongName[ xPosition ];
\r
2065 if( ( xIndex == 0 ) && ( ch == '.' ) )
\r
2067 pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
\r
2070 if( xPosition == xLastDot )
\r
2072 /* Remember where we put the first space. */
\r
2073 if ( pxFindParams->ucFirstTilde > xIndex )
\r
2075 pxFindParams->ucFirstTilde = xIndex;
\r
2077 while ( xIndex < 8 )
\r
2079 pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
\r
2081 #if( ffconfigSHORTNAME_CASE != 0 )
\r
2083 testAttrib = FF_FAT_CASE_ATTR_EXT;
\r
2091 if( xPosition <= xLastDot )
\r
2093 xPosition = xLastDot;
\r
2094 ch = ( int8_t ) pcLongName[ xPosition ];
\r
2099 ch = ( int8_t ) pcLongName[ ++xPosition ];
\r
2100 #if( ffconfigSHORTNAME_CASE != 0 )
\r
2102 testAttrib = FF_FAT_CASE_ATTR_EXT;
\r
2107 if( !FF_ValidShortChar ( ch ) )
\r
2109 pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
\r
2112 #if( ffconfigSHORTNAME_CASE != 0 )
\r
2114 if( ( ch >= 'a' ) && ( ch <= 'z' ) )
\r
2119 pxFindParams->ucCaseAttrib |= testAttrib;
\r
2123 pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had capital: does not fit. */
\r
2126 else if( ( ch >= 'A' ) && ( ch <= 'Z' ) )
\r
2128 if( ( pxFindParams->ucCaseAttrib & testAttrib ) != 0 )
\r
2130 pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had lower-case: does not fit. */
\r
2137 if( ( ch >= 'a' ) && ( ch <= 'z' ) )
\r
2142 #endif /* ffconfigSHORTNAME_CASE */
\r
2143 pxFindParams->pcEntryBuffer[ xIndex++ ] = ch;
\r
2147 if( ( xLastDot == 0 ) && ( xIndex < 6 ) )
\r
2149 /* This is a file name like ".info" or ".root" */
\r
2150 pxFindParams->ucFirstTilde = xIndex;
\r
2153 while ( xIndex < 11 )
\r
2155 pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
\r
2158 if( ( xLastDot < pxFindParams->ucFirstTilde ) && ( xLastDot > 0 ) )
\r
2160 pxFindParams->ucFirstTilde = xLastDot;
\r
2163 if( NameLen < pxFindParams->ucFirstTilde ) /* Names like "Abc" will become "~Abc". */
\r
2165 pxFindParams->ucFirstTilde = ( uint8_t ) NameLen;
\r
2167 } /* FF_CreateShortName() */
\r
2168 /*-----------------------------------------------------------*/
\r
2170 int32_t FF_FindShortName( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams )
\r
2172 char pcMyShortName[ 13 ];
\r
2173 FF_DirEnt_t xMyDirectory;
\r
2174 FF_Error_t xResult = 0;
\r
2175 BaseType_t xIndex, x, y;
\r
2177 char pcNumberBuf[ 12 ];
\r
2178 uint32_t ulCluster;
\r
2180 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2181 FF_T_WCHAR pcFileName[ 13 ];
\r
2183 char *pcFileName = pcMyShortName;
\r
2184 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
2186 #if( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
\r
2187 uint16_t usShortHash;
\r
2188 uint32_t ulRand = 0ul;
\r
2191 memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
\r
2192 FF_ProcessShortName( pcMyShortName );
\r
2193 if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
\r
2195 /* This entry will not get a LFN entry because it fits
\r
2196 * perfectly into a host name */
\r
2197 if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_CHECKED ) != 0 )
\r
2199 if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_FOUND ) != 0 )
\r
2201 xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME );
\r
2205 xResult = pxFindParams->ucCaseAttrib | 0x01;
\r
2210 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2212 memcpy( pcFileName, pcMyShortName, sizeof( pcMyShortName ) );
\r
2213 FF_ShortNameExpand( pcFileName );
\r
2216 ulCluster = FF_FindEntryInDir( pxIOManager, pxFindParams, pcFileName, 0x00, &xMyDirectory, &xResult );
\r
2218 /* END_OF_DIR is not a fatal error, it only means that the entry was not found. */
\r
2219 if( ( FF_isERR( xResult ) == pdFALSE ) || ( FF_GETERROR( xResult ) == FF_ERR_DIR_END_OF_DIR ) )
\r
2221 if( ulCluster == 0UL )
\r
2223 xResult = pxFindParams->ucCaseAttrib | 0x01;
\r
2227 xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME );
\r
2232 /* There was an error, which will be returned. */
\r
2238 for( xIndex = ( ( pxFindParams->ulFlags & FIND_FLAG_SIZE_OK ) ? 0 : 1 ); ; xIndex++ )
\r
2242 #if( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
\r
2244 /* In the first round, check if the original name can be used
\r
2245 Makefile will be stored as "makefile" and not as "makefi~1". */
\r
2247 /* This method saves a lot of time when creating directories with
\r
2248 many similar file names: when the short name version of a LFN already
\r
2249 exists, try at most 3 entries with a tilde:
\r
2253 After that create entries with pseudo-random 4-digit hex digits:
\r
2260 snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
\r
2264 if( ulRand == 0ul )
\r
2266 ulRand = pxIOManager->xPartition.ulLastFreeCluster;
\r
2267 usShortHash = FF_GetCRC16( ( uint8_t *)&ulRand, sizeof( ulRand ) );
\r
2271 usShortHash = FF_GetCRC16( ( uint8_t *)&usShortHash, sizeof( usShortHash ) );
\r
2273 snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%04X", ( int ) usShortHash );
\r
2278 snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
\r
2282 NameLen = ( uint16_t ) strlen( pcNumberBuf );
\r
2284 if ( x > pxFindParams->ucFirstTilde )
\r
2286 x = pxFindParams->ucFirstTilde;
\r
2288 pxFindParams->pcEntryBuffer[ x++ ] = '~';
\r
2289 for( y = 0; y < NameLen; y++ )
\r
2291 pxFindParams->pcEntryBuffer[ x + y ] = pcNumberBuf[ y ];
\r
2294 memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
\r
2295 FF_ProcessShortName( pcMyShortName );
\r
2296 if( FF_ShortNameExists( pxIOManager, pxFindParams->ulDirCluster, pcMyShortName, &xResult ) == pdFALSE )
\r
2300 if( xIndex >= FF_MAX_ENTRIES_PER_DIRECTORY )
\r
2302 xResult = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_CREATESHORTNAME );
\r
2306 /* Add a tail and special number until we're happy :D. */
\r
2310 } /* FF_FindShortName () */
\r
2311 /*-----------------------------------------------------------*/
\r
2314 #if( ffconfigLFN_SUPPORT != 0 )
\r
2315 static int8_t FF_CreateLFNEntry( uint8_t *pucEntryBuffer, uint8_t *pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum )
\r
2319 * Changed *pcName from 16- to of 8-bits
\r
2320 * The caller of this function doesn't need an expensive
\r
2321 * uint16_t usUtf16Name[ffconfigMAX_FILENAME + 1];
\r
2322 * in case UNICODE isn't used
\r
2323 * Also did quite a bit of optimisation here
\r
2326 UBaseType_t uxIndex, x;
\r
2328 memset( pucEntryBuffer, 0, FF_SIZEOF_DIRECTORY_ENTRY );
\r
2330 FF_putChar( pucEntryBuffer, FF_FAT_LFN_ORD, ( uint8_t )( ( uxLFN & ~0x40 ) ) );
\r
2331 FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint8_t ) FF_FAT_ATTR_LFN );
\r
2332 FF_putChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM, ( uint8_t ) ucCheckSum );
\r
2336 for( x = FF_FAT_LFN_NAME_1; uxIndex < 5u; uxIndex++, x += 2 )
\r
2338 if( uxIndex < uxNameLength )
\r
2340 pucEntryBuffer[ x ] = *( pcName++ );
\r
2341 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2343 pucEntryBuffer[ x + 1 ] = *( pcName++ );
\r
2347 else if ( uxIndex > uxNameLength )
\r
2349 pucEntryBuffer[ x] = 0xFF;
\r
2350 pucEntryBuffer[ x + 1 ] = 0xFF;
\r
2355 for( x = FF_FAT_LFN_NAME_2; uxIndex < 11u; uxIndex++, x += 2 )
\r
2357 if( uxIndex < uxNameLength )
\r
2359 pucEntryBuffer[ x ] = *( pcName++ );
\r
2360 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2362 pucEntryBuffer[ x + 1 ] = *( pcName++ );
\r
2366 else if( uxIndex > uxNameLength )
\r
2368 pucEntryBuffer[ x ] = 0xFF;
\r
2369 pucEntryBuffer[ x + 1 ] = 0xFF;
\r
2374 for( x = FF_FAT_LFN_NAME_3; uxIndex < 13u; uxIndex++, x += 2 )
\r
2376 if( uxIndex < uxNameLength )
\r
2378 pucEntryBuffer[ x ] = *( pcName++ );
\r
2379 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2381 pucEntryBuffer[ x + 1 ] = *( pcName++ );
\r
2385 else if( uxIndex > uxNameLength )
\r
2387 pucEntryBuffer[ x ] = 0xFF;
\r
2388 pucEntryBuffer[ x + 1 ] = 0xFF;
\r
2392 return FF_ERR_NONE;
\r
2393 } /* FF_CreateLFNEntry() */
\r
2394 #endif /* ffconfigLFN_SUPPORT */
\r
2395 /*-----------------------------------------------------------*/
\r
2397 #if( ffconfigLFN_SUPPORT != 0 )
\r
2398 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2399 static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR *pcName, uint8_t ucCheckSum, uint16_t usEntry )
\r
2401 static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcName, uint8_t ucCheckSum, uint16_t usEntry )
\r
2404 FF_Error_t xError = FF_ERR_NONE;
\r
2405 BaseType_t xNumLFNs;
\r
2406 BaseType_t xEndPos;
\r
2407 BaseType_t xIndex, y;
\r
2408 FF_FetchContext_t xFetchContext;
\r
2409 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
2411 #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2415 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2416 uint16_t usUtf16Name[ ffconfigMAX_FILENAME + 1 ];
\r
2419 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
\r
2426 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2428 #if WCHAR_MAX <= 0xFFFF
\r
2430 y = wcslen( pcName );
\r
2431 if( y > ffconfigMAX_FILENAME )
\r
2433 xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
\r
2437 wcsncpy( usUtf16Name, pcName, ffconfigMAX_FILENAME );
\r
2444 while( pcName[ xIndex ] )
\r
2446 FF_Utf32ctoUtf16c( &usUtf16Name[ y ], ( uint32_t ) pcName[xIndex], ffconfigMAX_FILENAME - xIndex );
\r
2447 y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );
\r
2449 if( y > ffconfigMAX_FILENAME )
\r
2451 xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
\r
2458 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
2460 /* Convert the name into UTF-16 format. */
\r
2461 #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2463 /* Simply convert the UTF8 to UTF16 and be done with it. */
\r
2466 while( pcName[ xIndex ] != 0 )
\r
2468 slRetVal = FF_Utf8ctoUtf16c( &( usUtf16Name[ y ] ), ( uint8_t * )&( pcName[ xIndex ] ), ffconfigMAX_FILENAME - xIndex );
\r
2469 if( slRetVal > 0 )
\r
2471 xIndex += slRetVal;
\r
2475 break; /* No more space in the UTF-16 buffer, simply truncate for safety. */
\r
2477 y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );
\r
2478 if( y > ffconfigMAX_FILENAME )
\r
2480 xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
\r
2485 #elif ( ffconfigUNICODE_UTF16_SUPPORT == 0 )
\r
2487 /* Just check the length. */
\r
2488 y = strlen( pcName );
\r
2489 if( y > ffconfigMAX_FILENAME )
\r
2491 xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
\r
2496 /* Whole name is now in a valid UTF-16 format. Lets go make thos LFN's.
\r
2497 At this point, it should a be the length of the name. */
\r
2498 if( FF_isERR( xError ) == pdFALSE )
\r
2500 xNumLFNs = y / 13; /* Number of LFNs is the total number of UTF-16 units, divided by 13 ( 13 units per LFN ). */
\r
2501 xEndPos = y % 13; /* The ending position in an LFN, of the last LFN UTF-16 character. */
\r
2512 xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
2513 if( FF_isERR( xError ) == pdFALSE )
\r
2515 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2517 NamePtr = ( int16_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
\r
2519 #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
2521 NamePtr = ( int8_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
\r
2525 NamePtr = pcName + 13 * ( xNumLFNs - 1 );
\r
2528 /* After this point, xIndex is no longer the length of the Filename in UTF-16 units. */
\r
2529 for( xIndex = xNumLFNs; xIndex > 0; xIndex-- )
\r
2531 if( xIndex == xNumLFNs )
\r
2533 FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) xEndPos, ( UBaseType_t ) xIndex, ucCheckSum );
\r
2534 pucEntryBuffer[0] |= 0x40;
\r
2538 FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) 13u, ( UBaseType_t ) xIndex, ucCheckSum );
\r
2542 xError = FF_PushEntryWithContext( pxIOManager, ( uint32_t ) ( usEntry + ( xNumLFNs - xIndex ) ), &xFetchContext, pucEntryBuffer );
\r
2543 if( FF_isERR( xError ) )
\r
2550 FF_Error_t xTempError;
\r
2552 /* Release any buffers that were used. */
\r
2553 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
2554 if( FF_isERR( xTempError ) == pdFALSE )
\r
2556 xError = xTempError;
\r
2563 } /* FF_CreateLFNs() */
\r
2564 #endif /* ffconfigLFN_SUPPORT */
\r
2565 /*-----------------------------------------------------------*/
\r
2567 FF_Error_t FF_ExtendDirectory( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
\r
2569 uint32_t xCurrentCluster;
\r
2570 uint32_t xNextCluster = 0UL;
\r
2571 FF_Error_t xError = FF_ERR_NONE;
\r
2572 FF_FATBuffers_t xFATBuffers;
\r
2574 if( ( ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) &&
\r
2575 ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) )
\r
2577 /* root directories on FAT12 and FAT16 can not be extended. */
\r
2578 xError = ( FF_Error_t ) ( FF_ERR_DIR_CANT_EXTEND_ROOT_DIR | FF_EXTENDDIRECTORY );
\r
2580 else if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
\r
2582 /* The number of free clusters was not yet calculated or equal to zero. */
\r
2583 pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError );
\r
2586 if( FF_isERR( xError ) == pdFALSE )
\r
2588 if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
\r
2590 xError = ( FF_Error_t ) ( FF_ERR_FAT_NO_FREE_CLUSTERS | FF_EXTENDDIRECTORY );
\r
2594 FF_LockFAT( pxIOManager );
\r
2596 xCurrentCluster = FF_FindEndOfChain( pxIOManager, ulDirCluster, &xError );
\r
2597 if( FF_isERR( xError ) == pdFALSE )
\r
2599 xNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE );
\r
2600 if( FF_isERR( xError ) == pdFALSE )
\r
2602 FF_InitFATBuffers ( &xFATBuffers, FF_MODE_WRITE );
\r
2603 /* xNextCluster already has been set to 0xFFFFFFFF,
\r
2604 now let xCurrentCluster point to xNextCluster. */
\r
2606 xError = FF_putFATEntry( pxIOManager, xCurrentCluster, xNextCluster, &xFATBuffers );
\r
2608 FF_Error_t xTempError;
\r
2610 xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );
\r
2611 if( FF_isERR( xError ) == pdFALSE )
\r
2613 xError = xTempError;
\r
2616 xTempError = FF_DecreaseFreeClusters( pxIOManager, 1 );
\r
2617 if( FF_isERR( xError ) == pdFALSE )
\r
2619 xError = xTempError;
\r
2625 FF_UnlockFAT( pxIOManager );
\r
2627 if( FF_isERR( xError ) == pdFALSE )
\r
2629 /* The entire cluster will be filled with zero's,
\r
2630 because it will contain directory data. */
\r
2631 xError = FF_ClearCluster( pxIOManager, xNextCluster );
\r
2637 } /* FF_ExtendDirectory() */
\r
2638 /*-----------------------------------------------------------*/
\r
2640 static const uint8_t forbiddenChrs[] =
\r
2642 /* Windows says: don't use these characters: '\/:*?"<>|'
\r
2643 " * / : < > ? '\' ? | */
\r
2644 0x22, 0x2A, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x5C, 0x7F, 0x7C
\r
2647 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2648 static void FF_MakeNameCompliant( FF_T_WCHAR *pcName )
\r
2650 static void FF_MakeNameCompliant( char *pcName )
\r
2654 if( ( uint8_t ) pcName[ 0 ] == FF_FAT_DELETED ) /* Support Japanese KANJI symbol0xE5. */
\r
2656 pcName[ 0 ] = 0x05;
\r
2658 for( ; *pcName; pcName++ )
\r
2660 for( index = 0; index < ( BaseType_t ) sizeof( forbiddenChrs ); index++ )
\r
2662 if( *pcName == forbiddenChrs[index] )
\r
2669 } /* FF_MakeNameCompliant() */
\r
2670 /*-----------------------------------------------------------*/
\r
2672 FF_Error_t FF_CreateDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, FF_DirEnt_t *pxDirEntry )
\r
2674 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
2676 BaseType_t xLFNCount;
\r
2677 int32_t lFreeEntry = 0L;
\r
2678 FF_Error_t xReturn = FF_ERR_NONE;
\r
2679 BaseType_t xEntryCount;
\r
2680 FF_FetchContext_t xFetchContext;
\r
2681 uint32_t ulDirCluster = pxFindParams->ulDirCluster;
\r
2682 int32_t lFitShort;
\r
2684 #if( ffconfigHASH_CACHE != 0 )
\r
2685 char pcShortName[ 13 ];
\r
2687 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2688 uint16_t NameLen = ( uint16_t ) wcslen( pxDirEntry->pcFileName );
\r
2690 uint16_t NameLen = ( uint16_t ) strlen( pxDirEntry->pcFileName );
\r
2693 #if( ffconfigLFN_SUPPORT != 0 )
\r
2694 uint8_t ucCheckSum;
\r
2697 /* Round-up the number of LFN's needed: */
\r
2698 xLFNCount = ( BaseType_t ) ( ( NameLen + 12 ) / 13 );
\r
2700 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2702 FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
\r
2706 FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
\r
2709 memset( pucEntryBuffer, 0, sizeof( pucEntryBuffer ) );
\r
2711 #if( ffconfigLFN_SUPPORT != 0 )
\r
2713 /* Create and push the LFN's. */
\r
2714 /* Find enough places for the LFNs and the ShortName. */
\r
2715 xEntryCount = xLFNCount + 1;
\r
2723 /* Create the ShortName. */
\r
2724 FF_LockDirectory( pxIOManager );
\r
2727 /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */
\r
2728 /* As FF_FindShortName( ) can fail, it should be called before finding a free directory entry. */
\r
2729 if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_SET ) == 0 )
\r
2731 FF_CreateShortName( pxFindParams, pxDirEntry->pcFileName );
\r
2733 lFitShort = FF_FindShortName ( pxIOManager, pxFindParams );
\r
2735 memcpy( pucEntryBuffer, pxFindParams->pcEntryBuffer, sizeof( pucEntryBuffer ) );
\r
2737 if( FF_isERR( lFitShort ) )
\r
2739 xReturn = lFitShort;
\r
2742 if( lFitShort != 0 )
\r
2744 /* There is no need to create a LFN entry because the file name
\r
2745 fits into a normal 32-byte entry.. */
\r
2749 lFreeEntry = FF_FindFreeDirent( pxIOManager, pxFindParams, ( uint16_t ) xEntryCount );
\r
2751 if( FF_isERR( lFreeEntry ) )
\r
2753 xReturn = lFreeEntry;
\r
2756 #if( ffconfigLFN_SUPPORT != 0 )
\r
2758 if( xLFNCount > 0 )
\r
2760 ucCheckSum = FF_CreateChkSum( pucEntryBuffer );
\r
2761 xReturn = FF_CreateLFNs( pxIOManager, ulDirCluster, pxDirEntry->pcFileName, ucCheckSum, ( uint16_t ) lFreeEntry );
\r
2768 #endif /* ffconfigLFN_SUPPORT */
\r
2769 if( FF_isERR( xReturn ) == pdFALSE )
\r
2771 #if( ffconfigTIME_SUPPORT != 0 )
\r
2773 FF_GetSystemTime( &pxDirEntry->xCreateTime ); /* Date and Time Created. */
\r
2774 pxDirEntry->xModifiedTime = pxDirEntry->xCreateTime; /* Date and Time Modified. */
\r
2775 pxDirEntry->xAccessedTime = pxDirEntry->xCreateTime; /* Date of Last Access. */
\r
2776 FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
\r
2777 FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
\r
2778 FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
\r
2779 FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
\r
2781 #endif /* ffconfigTIME_SUPPORT */
\r
2783 FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, pxDirEntry->ucAttrib );
\r
2784 #if( ffconfigSHORTNAME_CASE != 0 )
\r
2785 FF_putChar( pucEntryBuffer, FF_FAT_CASE_OFFS, ( uint32_t ) lFitShort & ( FF_FAT_CASE_ATTR_BASE | FF_FAT_CASE_ATTR_EXT ) );
\r
2787 FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint16_t )( pxDirEntry->ulObjectCluster >> 16 ) );
\r
2788 FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint16_t )( pxDirEntry->ulObjectCluster ) );
\r
2789 FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );
\r
2791 xReturn = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
2792 if( FF_isERR( xReturn ) )
\r
2796 xReturn = FF_PushEntryWithContext( pxIOManager, ( uint16_t ) ( lFreeEntry + xLFNCount ), &xFetchContext, pucEntryBuffer );
\r
2799 FF_Error_t xTempError;
\r
2801 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
2802 if( FF_isERR( xReturn ) == pdFALSE )
\r
2804 xReturn = xTempError;
\r
2807 if( FF_isERR( xReturn ) )
\r
2812 #if( ffconfigHASH_CACHE != 0 )
\r
2814 if( FF_DirHashed( pxIOManager, ulDirCluster ) == pdFALSE )
\r
2816 /* Hash the directory. */
\r
2817 FF_HashDir( pxIOManager, ulDirCluster );
\r
2819 memcpy( pcShortName, pucEntryBuffer, 11 );
\r
2820 FF_ProcessShortName( pcShortName ); /* Format the shortname to 8.3. */
\r
2821 #if( ffconfigHASH_FUNCTION == CRC16 )
\r
2823 FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t )FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) );
\r
2825 #elif( ffconfigHASH_FUNCTION == CRC8 )
\r
2827 FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t )FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) );
\r
2829 #endif /* ffconfigHASH_FUNCTION */
\r
2831 #endif /* ffconfigHASH_CACHE*/
\r
2836 FF_UnlockDirectory( pxIOManager );
\r
2838 if( FF_isERR( xReturn ) == pdFALSE )
\r
2840 if( pxDirEntry != NULL )
\r
2842 pxDirEntry->usCurrentItem = ( uint16_t )( lFreeEntry + xLFNCount );
\r
2847 } /* FF_CreateDirent() */
\r
2848 /*-----------------------------------------------------------*/
\r
2851 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2852 uint32_t FF_CreateFile( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, FF_T_WCHAR *pcFileName, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
\r
2854 uint32_t FF_CreateFile( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, char *pcFileName, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
\r
2857 FF_DirEnt_t xMyFile;
\r
2858 FF_Error_t xTempError, xError = FF_ERR_NONE;
\r
2859 uint32_t ulResult;
\r
2861 memset ( &xMyFile, '\0', sizeof( xMyFile ) );
\r
2863 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2865 wcsncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME );
\r
2869 strncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME );
\r
2873 xMyFile.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );
\r
2875 if( FF_isERR( xError ) == pdFALSE )
\r
2877 xError = FF_CreateDirent( pxIOManager, pxFindParams, &xMyFile );
\r
2878 if( FF_isERR( xError ) == pdFALSE )
\r
2880 /* The new file now has a cluster chain and it has an entry
\r
2881 in its directory. Copy data to a pointer provided by caller: */
\r
2882 if( pxDirEntry != NULL )
\r
2884 memcpy( pxDirEntry, &xMyFile, sizeof( FF_DirEnt_t ) );
\r
2889 /* An error occurred in FF_CreateDirent().
\r
2890 Unlink the file's cluster chain: */
\r
2891 FF_LockFAT( pxIOManager );
\r
2893 FF_UnlinkClusterChain( pxIOManager, xMyFile.ulObjectCluster, 0 );
\r
2894 xMyFile.ulObjectCluster = 0ul;
\r
2896 FF_UnlockFAT( pxIOManager );
\r
2898 /* Now flush all buffers to disk. */
\r
2899 xTempError = FF_FlushCache( pxIOManager );
\r
2900 if( FF_isERR( xError ) == pdFALSE )
\r
2902 xError = xTempError;
\r
2906 *pxError = xError;
\r
2908 if( FF_isERR( xError ) == pdFALSE )
\r
2910 ulResult = xMyFile.ulObjectCluster;
\r
2918 } /* FF_CreateFile() */
\r
2919 /*-----------------------------------------------------------*/
\r
2923 * @brief Creates a Directory of the specified path.
\r
2925 * @param pxIOManager Pointer to the FF_IOManager_t object.
\r
2926 * @param pcPath Path of the directory to create.
\r
2928 * @Return FF_ERR_NULL_POINTER if pxIOManager was NULL.
\r
2929 * @Return FF_ERR_DIR_OBJECT_EXISTS if the object specified by path already exists.
\r
2930 * @Return FF_ERR_DIR_INVALID_PATH
\r
2931 * @Return FF_ERR_NONE on success.
\r
2933 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2934 FF_Error_t FF_MkDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )
\r
2936 FF_Error_t FF_MkDir( FF_IOManager_t *pxIOManager, const char *pcPath )
\r
2939 FF_DirEnt_t xMyDirectory;
\r
2941 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2942 const FF_T_WCHAR *pcDirName;
\r
2944 const char *pcDirName;
\r
2946 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
2947 uint32_t ulObjectCluster;
\r
2948 BaseType_t xIndex;
\r
2949 FF_Error_t xError = FF_ERR_NONE;
\r
2951 FF_FindParams_t xFindParams;
\r
2953 memset ( &xFindParams, '\0', sizeof( xFindParams ) );
\r
2954 /* Inform the functions that the entry will be created if not found */
\r
2955 xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG;
\r
2957 /* Open a do {} while ( pdFALSE ) loop */
\r
2960 if( pxIOManager == NULL )
\r
2962 xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_MKDIR );
\r
2965 #if( ffconfigREMOVABLE_MEDIA != 0 )
\r
2966 if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
\r
2968 xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_MKDIR );
\r
2971 #endif /* ffconfigREMOVABLE_MEDIA */
\r
2973 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
2975 xIndex = ( BaseType_t ) wcslen( pcPath );
\r
2979 xIndex = ( BaseType_t ) strlen( pcPath );
\r
2983 /* Find the last slash in the path. */
\r
2984 while( xIndex != 0 )
\r
2986 if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) )
\r
2993 pcDirName = pcPath + xIndex + 1;
\r
3000 if( pcDirName[ 0 ] == '\0' )
\r
3002 xError = ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR );
\r
3006 xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );
\r
3008 if( FF_isERR( xError ) )
\r
3013 if( xFindParams.ulDirCluster == 0UL )
\r
3015 xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_MKDIR );
\r
3018 memset( &xMyDirectory, '\0', sizeof( xMyDirectory ) );
\r
3020 /* Will set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK */
\r
3021 FF_CreateShortName( &xFindParams, pcDirName );
\r
3023 if( FF_FindEntryInDir( pxIOManager, &xFindParams, pcDirName, 0x00, &xMyDirectory, &xError ) )
\r
3025 if( FF_isERR( xError ) == pdFALSE )
\r
3027 xError = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR );
\r
3032 if( ( FF_isERR( xError ) ) && ( FF_GETERROR( xError ) != FF_ERR_DIR_END_OF_DIR ) )
\r
3037 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
3039 wcsncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME );
\r
3043 strncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME );
\r
3047 xMyDirectory.ulFileSize = 0;
\r
3048 xMyDirectory.ucAttrib = FF_FAT_ATTR_DIR;
\r
3049 xMyDirectory.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );
\r
3051 /* Give all entries a proper time stamp, looks nicer than 1 Jan 1970 */
\r
3052 #if( ffconfigTIME_SUPPORT != 0 )
\r
3054 FF_GetSystemTime( &xMyDirectory.xCreateTime );
\r
3055 FF_GetSystemTime( &xMyDirectory.xModifiedTime );
\r
3059 if( FF_isERR( xError ) )
\r
3063 if( xMyDirectory.ulObjectCluster == 0UL )
\r
3065 /* Couldn't allocate any space for the dir! */
\r
3066 xError = ( FF_Error_t ) ( FF_ERR_DIR_EXTEND_FAILED | FF_MKDIR );
\r
3070 xError = FF_ClearCluster( pxIOManager, xMyDirectory.ulObjectCluster );
\r
3071 if( FF_isERR( xError ) == pdFALSE )
\r
3073 xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyDirectory );
\r
3076 if( FF_isERR( xError ) )
\r
3078 FF_LockFAT( pxIOManager );
\r
3080 FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
\r
3082 FF_UnlockFAT( pxIOManager );
\r
3083 FF_FlushCache( pxIOManager ); /* Don't override error. */
\r
3087 /* Write 8.3 entry "." */
\r
3088 pucEntryBuffer[ 0 ] = '.';
\r
3089 /* folowed by 10 spaces: */
\r
3090 memset( pucEntryBuffer + 1, ' ', 10 );
\r
3091 /* Clear the rest of the structure. */
\r
3092 memset( pucEntryBuffer + 11, 0, FF_SIZEOF_DIRECTORY_ENTRY - 11 );
\r
3094 ulObjectCluster = xMyDirectory.ulObjectCluster;
\r
3095 xError = FF_PutEntry( pxIOManager, ( uint16_t ) 0u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );
\r
3097 if( FF_isERR( xError ) == pdFALSE )
\r
3099 pucEntryBuffer[ 1 ] = '.';
\r
3101 if( xFindParams.ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
\r
3103 xMyDirectory.ulObjectCluster = 0;
\r
3107 xMyDirectory.ulObjectCluster = xFindParams.ulDirCluster;
\r
3109 xError = FF_PutEntry( pxIOManager, 1u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );
\r
3111 xMyDirectory.ulObjectCluster = ulObjectCluster;
\r
3114 if( FF_isERR( xError ) )
\r
3116 FF_LockFAT( pxIOManager );
\r
3118 FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
\r
3120 FF_UnlockFAT( pxIOManager );
\r
3122 FF_FlushCache( pxIOManager );
\r
3127 } /* FF_MkDir() */
\r
3128 /*-----------------------------------------------------------*/
\r
3131 FF_Error_t FF_RmLFNs( FF_IOManager_t *pxIOManager, uint16_t usDirEntry, FF_FetchContext_t *pxContext )
\r
3133 FF_Error_t xError = FF_ERR_NONE;
\r
3134 uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
\r
3136 if( usDirEntry != 0 )
\r
3142 xError = FF_FetchEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );
\r
3143 if( FF_isERR( xError ) )
\r
3148 if( FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN )
\r
3150 FF_putChar( pucEntryBuffer, ( uint16_t ) 0, ( uint8_t ) FF_FAT_DELETED );
\r
3151 xError = FF_PushEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );
\r
3152 if( FF_isERR( xError ) )
\r
3158 if( usDirEntry == 0 )
\r
3163 } while( FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN );
\r
3167 } /* FF_RmLFNs() */
\r
3168 /*-----------------------------------------------------------*/
\r
3170 #if( ffconfigHASH_CACHE != 0 )
\r
3171 FF_Error_t FF_HashDir( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
\r
3173 /* Find most suitable Hash Table to replace! */
\r
3174 BaseType_t xIndex;
\r
3175 FF_HashTable_t *pxHashCache = NULL;
\r
3176 FF_FetchContext_t xFetchContext;
\r
3177 const uint8_t *pucEntryBuffer = NULL;
\r
3180 FF_Error_t xError;
\r
3182 for( xIndex = 0; xIndex < ffconfigHASH_CACHE_DEPTH; xIndex++ )
\r
3184 if( pxIOManager->xHashCache[ xIndex ].ulNumHandles == 0 )
\r
3186 if( pxHashCache == NULL )
\r
3188 pxHashCache = &pxIOManager->xHashCache[ xIndex ];
\r
3192 if( ( pxIOManager->xHashCache[ xIndex ].ulMisses > pxHashCache->ulMisses ) )
\r
3194 pxHashCache = &pxIOManager->xHashCache[ xIndex ];
\r
3200 if( pxHashCache != NULL )
\r
3202 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
3203 FF_T_WCHAR pcMyShortName[ 13 ];
\r
3205 char pcMyShortName[ 13 ];
\r
3207 /* Clear the hash table! */
\r
3208 memset( pxHashCache, '\0', sizeof( *pxHashCache ) );
\r
3209 pxHashCache->ulDirCluster = ulDirCluster;
\r
3210 pxHashCache->ulMisses = 0;
\r
3212 /* Hash the directory! */
\r
3214 xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
\r
3216 if( FF_isERR( xError ) == pdFALSE )
\r
3218 for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
\r
3220 if( ( pucEntryBuffer == NULL ) ||
\r
3221 ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
\r
3223 xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );
\r
3224 if( FF_isERR( xError ) )
\r
3228 pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
\r
3232 /* Advance the pointer 32 bytes to the next directory entry. */
\r
3233 pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
\r
3235 if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
\r
3237 ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );
\r
3238 if( ( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) &&
\r
3239 ( ( ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) )
\r
3241 memcpy ( pcMyShortName, pucEntryBuffer, 11 );
\r
3242 FF_ProcessShortName( pcMyShortName );
\r
3243 if( FF_isEndOfDir( pucEntryBuffer ) )
\r
3248 /* Generate the Hash. */
\r
3249 #if( ffconfigHASH_FUNCTION == CRC16 )
\r
3251 ulHash = FF_GetCRC16( ( uint8_t * ) pcMyShortName, strlen( pcMyShortName ) );
\r
3253 #else /* ffconfigHASH_FUNCTION == CRC8 */
\r
3255 ulHash = FF_GetCRC8( pcMyShortName, strlen( pcMyShortName ) );
\r
3258 FF_SetHash( pxHashCache, ulHash );
\r
3261 } /* for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
\r
3263 FF_Error_t xTempError;
\r
3264 xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
\r
3265 if( FF_isERR( xError ) == pdFALSE )
\r
3267 xError = xTempError;
\r
3271 } /* if( pxHashCache != NULL ) */
\r
3278 } /* FF_HashDir() */
\r
3279 #endif /* ffconfigHASH_CACHE != 0 */
\r
3280 /*-----------------------------------------------------------*/
\r
3282 #if( ffconfigHASH_CACHE != 0 )
\r
3283 /* FF_UnHashDir() : invalidate the hash tables of a given directory.
\r
3284 It is called when a file or sub-directory is removed or when the
\r
3285 directory itself is removed. */
\r
3286 void FF_UnHashDir( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
\r
3288 FF_HashTable_t *pxHash = pxIOManager->xHashCache;
\r
3289 FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
\r
3291 for( ; pxHash < pxLast; pxHash++ )
\r
3293 if( pxHash->ulDirCluster == ulDirCluster )
\r
3295 pxHash->ulDirCluster = 0;
\r
3299 } /* FF_UnHashDir() */
\r
3300 #endif /* ffconfigHASH_CACHE */
\r
3301 /*-----------------------------------------------------------*/
\r
3303 #if( ffconfigHASH_CACHE != 0 )
\r
3308 void FF_SetHash( FF_HashTable_t *pxHash, uint32_t ulHash )
\r
3310 uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
\r
3311 uint32_t tblBit = ulHash % 32;
\r
3313 pxHash->ulBitTable[ tblIndex ] |= ( 0x80000000ul >> tblBit );
\r
3314 } /* FF_SetHash() */
\r
3315 #endif /* ffconfigHASH_CACHE */
\r
3316 /*-----------------------------------------------------------*/
\r
3318 #if( ffconfigHASH_CACHE != 0 )
\r
3319 void FF_ClearHash( FF_HashTable_t *pxHash, uint32_t ulHash )
\r
3321 if( pxHash != NULL )
\r
3323 uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
\r
3324 uint32_t tblBit = ulHash % 32;
\r
3326 pxHash->ulBitTable[ tblIndex ] &= ~( 0x80000000ul >> tblBit );
\r
3328 } /* FF_ClearHash() */
\r
3329 #endif /* ffconfigHASH_CACHE */
\r
3330 /*-----------------------------------------------------------*/
\r
3332 #if( ffconfigHASH_CACHE != 0 )
\r
3333 BaseType_t FF_isHashSet( FF_HashTable_t *pxHash, uint32_t ulHash )
\r
3335 FF_Error_t xResult;
\r
3337 xResult = pdFALSE;
\r
3339 if( pxHash != NULL )
\r
3341 uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
\r
3342 uint32_t tblBit = ulHash % 32;
\r
3344 if( pxHash->ulBitTable[ tblIndex ] & ( 0x80000000ul >> tblBit ) )
\r
3351 } /* FF_isHashSet() */
\r
3352 #endif /* ffconfigHASH_CACHE */
\r
3353 /*-----------------------------------------------------------*/
\r
3355 #if( ffconfigHASH_CACHE != 0 )
\r
3356 void FF_AddDirentHash( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, uint32_t ulHash )
\r
3358 FF_HashTable_t *pxHash = pxIOManager->xHashCache;
\r
3359 FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
\r
3361 for( ; pxHash < pxLast; pxHash++ )
\r
3363 if( pxHash->ulDirCluster == ulDirCluster )
\r
3365 FF_SetHash( pxHash, ulHash );
\r
3369 } /* FF_AddDirentHash() */
\r
3370 #endif /* ffconfigHASH_CACHE*/
\r
3371 /*-----------------------------------------------------------*/
\r
3373 #if( ffconfigHASH_CACHE != 0 )
\r
3374 BaseType_t FF_CheckDirentHash( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, uint32_t ulHash )
\r
3376 FF_HashTable_t *pxHash = pxIOManager->xHashCache;
\r
3377 FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
\r
3378 BaseType_t xResult;
\r
3382 if( pxHash->ulDirCluster == ulDirCluster )
\r
3384 xResult = FF_isHashSet( pxHash, ulHash );
\r
3388 if( pxHash >= pxLast )
\r
3396 } /* FF_CheckDirentHash() */
\r
3397 #endif /* ffconfigHASH_CACHE */
\r
3398 /*-----------------------------------------------------------*/
\r
3400 #if( ffconfigHASH_CACHE != 0 )
\r
3401 BaseType_t FF_DirHashed( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
\r
3403 FF_HashTable_t *pxHash = pxIOManager->xHashCache;
\r
3404 FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
\r
3405 BaseType_t xResult;
\r
3409 if( pxHash->ulDirCluster == ulDirCluster )
\r
3415 if( pxHash >= pxLast )
\r
3417 xResult = pdFALSE;
\r
3423 } /* FF_DirHashed() */
\r
3424 #endif /* ffconfigHASH_CACHE */
\r
3425 /*-----------------------------------------------------------*/
\r