/* * FreeRTOS+FAT V2.3.3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /** * @file ff_dir.c * @ingroup DIR * * @defgroup DIR Handles Directory Traversal * @brief Handles DIR access and traversal. * * Provides FindFirst() and FindNext() Interfaces **/ #include "ff_headers.h" #include #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) #include #endif #if defined( WIN32 ) #define wcsicmp _wcsicmp #else #define wcsicmp wcscasecmp #include #endif /* Calculate a simple LFN checmsum. */ static uint8_t FF_CreateChkSum( const uint8_t * pa_pShortName ); static BaseType_t FF_ShortNameExists( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, char * pcShortName, FF_Error_t * pxError ); #if ( ffconfigSHORTNAME_CASE != 0 ) /* For short-name entries, NT/XP etc store case information in byte 0x0c * Use this to show proper case of "README.txt" or "source.H". */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static void FF_CaseShortName( FF_T_WCHAR * pcName, uint8_t attrib ); #else static void FF_CaseShortName( char * pcName, uint8_t attrib ); #endif #endif /* ffconfigSHORTNAME_CASE */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) /* For unicode, the short name can be expanded to wchar * by inserting zero's. */ static void FF_ShortNameExpand( FF_T_WCHAR * ); #endif /* * Transform a name as stored on disk "README__TXT" * to a nul-terminated string: "README.TXT", "FILE001". * A dot is only added if an extension is present. */ static void FF_ProcessShortName( char * pcName ); #if ( ffconfigTIME_SUPPORT != 0 ) static void FF_PlaceTime( uint8_t * pucEntryBuffer, uint32_t Offset, FF_SystemTime_t * pxTime ); static void FF_PlaceDate( uint8_t * pucEntryBuffer, uint32_t Offset, FF_SystemTime_t * pxTime ); static void FF_GetTime( FF_SystemTime_t * pxTime, const uint8_t * pucEntryBuffer, uint32_t ulOffset ); static void FF_GetDate( FF_SystemTime_t * pxTime, const uint8_t * pucEntryBuffer, uint32_t ulOffset ); #endif /* ffconfigTIME_SUPPORT */ static FF_Error_t FF_Traverse( FF_IOManager_t * pxIOManager, uint32_t ulEntry, FF_FetchContext_t * pxContext ); static int32_t FF_FindFreeDirent( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams, uint16_t usSequential ); #if ( ffconfigLFN_SUPPORT != 0 ) static int8_t FF_CreateLFNEntry( uint8_t * pucEntryBuffer, uint8_t * pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum ); #endif /* ffconfigLFN_SUPPORT */ static BaseType_t FF_ValidShortChar( char cChar ); #if ( ffconfigLFN_SUPPORT != 0 ) #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR * pcName, uint8_t ucCheckSum, uint16_t usEntry ); #else static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, char * pcName, uint8_t ucCheckSum, uint16_t usEntry ); #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ #endif /* if ( ffconfigLFN_SUPPORT != 0 ) */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static void FF_MakeNameCompliant( FF_T_WCHAR * pcName ); #else static void FF_MakeNameCompliant( char * pcName ); #endif #if ( FF_NOSTRCASECMP == 0 ) static portINLINE unsigned char prvToLower( unsigned char c ) { unsigned char cReturnChar; if( ( c >= 'A' ) && ( c <= 'Z' ) ) { cReturnChar = c + 0x20; } else { cReturnChar = c; } return cReturnChar; } int strcasecmp( const char * pcString1, const char * pcString2 ) { unsigned char c1, c2; do { c1 = *pcString1++; c2 = *pcString2++; c1 = ( unsigned char ) prvToLower( ( unsigned char ) c1 ); c2 = ( unsigned char ) prvToLower( ( unsigned char ) c2 ); } while( ( c1 == c2 ) && ( c1 != '\0' ) ); return ( int ) c1 - c2; } /* strcasecmp() */ #endif /* if ( FF_NOSTRCASECMP == 0 ) */ /*-----------------------------------------------------------*/ static uint8_t FF_CreateChkSum( const uint8_t * pa_pShortName ) { uint8_t cNameLen; uint8_t ChkSum = 0; for( cNameLen = 11; cNameLen != 0; cNameLen-- ) { ChkSum = ( uint8_t ) ( ( ( ChkSum & 1 ) ? 0x80 : 0 ) + ( ChkSum >> 1 ) + *( pa_pShortName++ ) ); } return ChkSum; } /* FF_CreateChkSum() */ /*-----------------------------------------------------------*/ /* _HT_ Does not need a wchar version because a short name is treated a normal string of bytes */ static BaseType_t FF_ShortNameExists( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, char * pcShortName, FF_Error_t * pxError ) { BaseType_t xIndex; const uint8_t * pucEntryBuffer = NULL; /* initialisation not necessary, just for the compiler */ uint8_t ucAttrib; FF_FetchContext_t xFetchContext; char pcMyShortName[ FF_SIZEOF_DIRECTORY_ENTRY ]; BaseType_t xResult = -1; #if ( ffconfigHASH_CACHE != 0 ) uint32_t ulHash; #endif *pxError = FF_ERR_NONE; #if ( ffconfigHASH_CACHE != 0 ) { if( !FF_DirHashed( pxIOManager, ulDirCluster ) ) { /* Hash the directory. */ FF_HashDir( pxIOManager, ulDirCluster ); } #if ffconfigHASH_FUNCTION == CRC16 { ulHash = ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) ); } #else /* ffconfigHASH_FUNCTION == CRC8 */ { ulHash = ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) ); } #endif { /* FF_CheckDirentHash result: 0 not found, 1 found, -1 directory not hashed */ xResult = FF_CheckDirentHash( pxIOManager, ulDirCluster, ulHash ); } } #endif /* if ( ffconfigHASH_CACHE != 0 ) */ if( xResult < 0 ) { xResult = pdFALSE; *pxError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( *pxError ) == pdFALSE ) { for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) { /* Call FF_FetchEntryWithContext only once for every block (usually 512 bytes) */ if( ( xIndex == 0 ) || ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) { *pxError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL ); if( FF_isERR( *pxError ) ) { break; } pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer; } else { /* Advance 32 bytes to get the next directory entry. */ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY; } if( FF_isEndOfDir( pucEntryBuffer ) ) { break; } if( FF_isDeleted( pucEntryBuffer ) == pdFALSE ) { ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB ); if( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) { memcpy( pcMyShortName, pucEntryBuffer, sizeof( pcMyShortName ) ); FF_ProcessShortName( pcMyShortName ); if( strcmp( ( const char * ) pcShortName, ( const char * ) pcMyShortName ) == 0 ) { xResult = pdTRUE; break; } } } } /* for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */ } *pxError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); } return xResult; } /* FF_ShortNameExists() */ /*-----------------------------------------------------------*/ /* _HT_ When adding many files to a single directory, FF_FindEntryInDir was sometimes */ /* _HT_ called 3 times before inserting a single file. With these changes it is called one time only */ /* _HT_ pxFindParams caches some information: */ /* _HT_ 1: the first free entry 2: whether the short-name version already exists */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) 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 ) #else 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 ) #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ { FF_FetchContext_t xFetchContext; /* const pointer to read from pBuffer */ const uint8_t * src = NULL; /* As we're walking through a directory, we might as well * find the first free entry to help FF_FindFreeDirent( ) * The result will be stored in 'pxFindParams->lFreeEntry' */ BaseType_t entriesNeeded; BaseType_t freeCount = 0; FF_Error_t xError; /* If the file name fits into a short file name * then the existence of that short file name will be checked as well. */ BaseType_t testShortname; uint32_t xResult = 0ul; #if ( ffconfigUNICODE_UTF8_SUPPORT == 1 ) int32_t utf8Error; #endif #if ( ffconfigLFN_SUPPORT != 0 ) #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_T_WCHAR * pcCurPtr; /* Pointer to store a LFN. */ FF_T_WCHAR * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName ); #else char * pcCurPtr; /* Pointer to store a LFN. */ char * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName ); #endif /* ffconfigUNICODE_UTF16_SUPPORT */ uint16_t lfnItem = 0; uint8_t ucCheckSum = 0; BaseType_t xLFNCount = 0; BaseType_t xLFNTotal = 0; uint8_t lastAttrib; BaseType_t xIndex; #endif /* ffconfigLFN_SUPPORT */ #if ( ffconfigLFN_SUPPORT != 0 ) { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) BaseType_t NameLen = ( BaseType_t ) wcslen( ( const char * ) pcName ); #else BaseType_t NameLen = ( BaseType_t ) strlen( ( const char * ) pcName ); #endif /* Find enough places for the LFNs and the ShortName. */ entriesNeeded = ( uint8_t ) ( ( NameLen + 12 ) / 13 ) + 1; } #else { entriesNeeded = 1; } #endif /* ffconfigLFN_SUPPORT */ if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK ) { testShortname = pdTRUE; } else { testShortname = pdFALSE; } pxDirEntry->ucAttrib = 0; if( ( pxFindParams->ulFlags & FIND_FLAG_CREATE_FLAG ) != 0 ) { /* A file is to be created: keep track of the first free entry big enough * to hold this file name. */ pxFindParams->lFreeEntry = -1; } else { pxFindParams->lFreeEntry = 0; } xError = FF_InitEntryFetch( pxIOManager, pxFindParams->ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { for( pxDirEntry->usCurrentItem = 0; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) { if( ( src == NULL ) || ( src >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) { xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &xFetchContext, NULL ); if( FF_isERR( xError ) != pdFALSE ) { break; } src = xFetchContext.pxBuffer->pucBuffer; } else { /* Advance 32 bytes. */ src += FF_SIZEOF_DIRECTORY_ENTRY; } if( FF_isEndOfDir( src ) ) { /* 0x00 end-of-dir. */ break; } if( FF_isDeleted( src ) ) { /* Entry not used or deleted. */ pxDirEntry->ucAttrib = 0; if( ( pxFindParams->lFreeEntry < 0 ) && ( ++freeCount == entriesNeeded ) ) { /* Remember the beginning entry in the sequential sequence. */ pxFindParams->lFreeEntry = ( pxDirEntry->usCurrentItem - ( entriesNeeded - 1 ) ); } continue; } /* The current entry is in use, so reset the free-entry-counter */ freeCount = 0; #if ( ffconfigLFN_SUPPORT != 0 ) { lastAttrib = pxDirEntry->ucAttrib; } #endif pxDirEntry->ucAttrib = FF_getChar( src, FF_FAT_DIRENT_ATTRIB ); if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) { /* LFN Processing. */ #if ( ffconfigLFN_SUPPORT != 0 ) { if( ( xLFNCount == 0 ) || ( ( lastAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) ) { xLFNTotal = xLFNCount = ( BaseType_t ) ( src[ 0 ] & ~0x40 ); lfnItem = pxDirEntry->usCurrentItem; ucCheckSum = FF_getChar( src, FF_FAT_LFN_CHECKSUM ); pcLastPtr[ -1 ] = '\0'; } if( xLFNCount != 0 ) { xLFNCount--; pcCurPtr = pxDirEntry->pcFileName + ( xLFNCount * 13 ); /* * This section needs to extract the name and do the comparison * dependent on UNICODE settings in the FreeRTOSFATConfig.h file. */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { /* Add UTF-16 Routine here. */ /* Copy first 5 UTF-16 chars ( 10 bytes ). */ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_1 ], 10 ); /* Increment Filename pointer 5 utf16 chars. */ pcCurPtr += 5; /* Copy next 6 chars ( 12 bytes ). */ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_2 ], 12 ); pcCurPtr += 6; /* You're getting the idea by now! */ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_3 ], 4 ); pcCurPtr += 2; } /* ffconfigUNICODE_UTF16_SUPPORT */ #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { /* UTF-8 Routine here. */ for( xIndex = 0; ( xIndex < 5 ) && ( pcCurPtr < pcLastPtr ); xIndex++ ) { /* Was there a surrogate sequence? -- Add handling here. */ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_1 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr ); if( utf8Error > 0 ) { pcCurPtr += utf8Error; } else { if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE ) { /* Handle potential surrogate sequence across entries. */ } } } for( xIndex = 0; xIndex < 6 && pcCurPtr < pcLastPtr; xIndex++ ) { /* Was there a surrogate sequence? -- To add handling here. */ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_2 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr ); if( utf8Error > 0 ) { pcCurPtr += utf8Error; } else { if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE ) { /* Handle potential surrogate sequence across entries. */ } } } for( xIndex = 0; xIndex < 2 && pcCurPtr < pcLastPtr; xIndex++ ) { /* Was there a surrogate sequence? -- To add handling here. */ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_3 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr ); if( utf8Error > 0 ) { pcCurPtr += utf8Error; } else { if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE ) { /* Handle potential surrogate sequence across entries. */ } } } } /* ffconfigUNICODE_UTF8_SUPPORT */ #else /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ { /* use ASCII notation. */ for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_1 + xIndex ]; } for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_2 + xIndex ]; } for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_3 + xIndex ]; } } #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && !( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */ if( ( xLFNCount == xLFNTotal - 1 ) && ( pcCurPtr < pcLastPtr ) ) { *pcCurPtr = '\0'; /* Important when name len is multiple of 13. */ } } /* if( xLFNCount ) */ } #endif /* ffconfigLFN_SUPPORT */ continue; } if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) == FF_FAT_ATTR_VOLID ) { #if ( ffconfigLFN_SUPPORT != 0 ) { xLFNTotal = 0; } #endif /* ffconfigLFN_SUPPORT */ continue; } #if ( ffconfigLFN_SUPPORT != 0 ) if( ( xLFNTotal == 0 ) || ( ucCheckSum != FF_CreateChkSum( src ) ) ) #endif /* ffconfigLFN_SUPPORT */ { /* This entry has only a short name, or the checksum isn't correct * Use the short name for comparison */ memcpy( pxDirEntry->pcFileName, src, 11 ); FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { /* FileName now contains a 8-bit short name * Expand it to a FF_T_WCHAR string. */ FF_ShortNameExpand( pxDirEntry->pcFileName ); } #endif /* ffconfigUNICODE_UTF16_SUPPORT */ #if ( ffconfigLFN_SUPPORT != 0 ) { xLFNTotal = 0; } #endif /* ffconfigLFN_SUPPORT */ } /* This function FF_FindEntryInDir( ) is either called with * pa_Attrib==0 or with pa_Attrib==FF_FAT_ATTR_DIR * In the last case the caller is looking for a directory */ if( ( pxDirEntry->ucAttrib & pa_Attrib ) == pa_Attrib ) { if( testShortname ) { /* Both strings are stored in the directory format * e.g. "README TXT", without a dot */ if( memcmp( src, pxFindParams->pcEntryBuffer, 11 ) == 0 ) { pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED | FIND_FLAG_SHORTNAME_FOUND; } } #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) if( wcsicmp( ( const char * ) pcName, ( const char * ) pxDirEntry->pcFileName ) == 0 ) #else if( FF_stricmp( ( const char * ) pcName, ( const char * ) pxDirEntry->pcFileName ) == 0 ) #endif /* ffconfigUNICODE_UTF16_SUPPORT */ { /* Finally get the complete information. */ #if ( ffconfigLFN_SUPPORT != 0 ) if( xLFNTotal ) { xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, ( uint16_t ) lfnItem, &xFetchContext ); if( FF_isERR( xError ) ) { break; } } else #endif /* ffconfigLFN_SUPPORT */ { FF_PopulateShortDirent( pxIOManager, pxDirEntry, src ); /* HT: usCurrentItem wasn't increased here. */ pxDirEntry->usCurrentItem++; } /* Object found, the cluster number will be returned. */ xResult = pxDirEntry->ulObjectCluster; break; } } #if ( ffconfigLFN_SUPPORT != 0 ) { xLFNTotal = 0; } #endif } /* for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */ { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } /* if( FF_isERR( xError ) == pdFALSE ) */ if( FF_isERR( xError ) == pdFALSE ) { /* If a free entry wasn't found yet, put it to the current (last) item */ if( pxFindParams->lFreeEntry < 0 ) { pxFindParams->lFreeEntry = pxDirEntry->usCurrentItem; } /* If we were checking the existence of the short-name * set the Checked flag now */ if( testShortname ) { pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED; } } if( pxError != NULL ) { *pxError = xError; } return xResult; } /* FF_FindEntryInDir() */ /*-----------------------------------------------------------*/ /** * @private **/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) uint32_t FF_FindDir( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath, uint16_t pathLen, FF_Error_t * pxError ) #else uint32_t FF_FindDir( FF_IOManager_t * pxIOManager, const char * pcPath, uint16_t pathLen, FF_Error_t * pxError ) #endif { uint16_t it = 0; /* Re-entrancy Variables for FF_strtok( ). */ BaseType_t last = pdFALSE; FF_DirEnt_t xMyDirectory; FF_FindParams_t xFindParams; FF_Error_t xError; BaseType_t xFound; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_T_WCHAR mytoken[ ffconfigMAX_FILENAME ]; FF_T_WCHAR * pcToken; #else char mytoken[ ffconfigMAX_FILENAME ]; char * pcToken; #endif #if ( ffconfigPATH_CACHE != 0 ) BaseType_t xIndex; #endif memset( &xFindParams, '\0', sizeof( xFindParams ) ); xFindParams.ulDirCluster = pxIOManager->xPartition.ulRootDirCluster; xError = FF_ERR_NONE; if( pathLen <= 1 ) { /* Must be the root directory. * 'xFindParams.ulDirCluster' will be returned containing 'pxIOManager->xPartition.ulRootDirCluster'. */ xFound = pdTRUE; } else { /* Only the root directory '/' shall have a trailing slash in its name. */ if( ( pcPath[ pathLen - 1 ] == '\\' ) || ( pcPath[ pathLen - 1 ] == '/' ) ) { pathLen--; } xFound = pdFALSE; #if ( ffconfigPATH_CACHE != 0 ) /* Is the requested pcPath in the PATH CACHE? */ { FF_PendSemaphore( pxIOManager->pvSemaphore ); /* Thread safety on shared object! */ { for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ ) { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) if( wcslen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t ) pathLen ) #else if( strlen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t ) pathLen ) #endif { if( FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) ) { xFindParams.ulDirCluster = pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster; xFound = pdTRUE; break; } } } } FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } #endif /* ffconfigPATH_CACHE */ } if( xFound == pdFALSE ) { pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen ); do { xMyDirectory.usCurrentItem = 0; xFindParams.ulDirCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcToken, ( uint8_t ) FF_FAT_ATTR_DIR, &xMyDirectory, &xError ); if( xFindParams.ulDirCluster == 0ul ) { break; } pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen ); } while( pcToken != NULL ); if( ( pcToken != NULL ) && ( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) ) ) { xError = ( FF_Error_t ) ( FF_FINDDIR | FF_ERR_FILE_INVALID_PATH ); } #if ( ffconfigPATH_CACHE != 0 ) /* Update the PATH CACHE with a new PATH. */ { /* Only cache if the dir was actually found! */ if( ( FF_isERR( xError ) == pdFALSE ) && ( xFindParams.ulDirCluster != 0ul ) ) { FF_PendSemaphore( pxIOManager->pvSemaphore ); { if( pathLen < ffconfigMAX_FILENAME ) /* Ensure the PATH won't cause a buffer overrun. */ { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen * sizeof( FF_T_WCHAR ) ); } #else { memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen ); } #endif pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath[ pathLen ] = '\0'; pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].ulDirCluster = xFindParams.ulDirCluster; pxIOManager->xPartition.ulPCIndex += 1; if( pxIOManager->xPartition.ulPCIndex >= ffconfigPATH_CACHE_DEPTH ) { pxIOManager->xPartition.ulPCIndex = 0; } } } FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } } #endif /* ffconfigPATH_CACHE */ } /* if( pathLen > 1 ) */ if( pxError != NULL ) { *pxError = xError; } return xFindParams.ulDirCluster; } /* FF_FindDir() */ /*-----------------------------------------------------------*/ #if ( ffconfigSHORTNAME_CASE != 0 ) /** * @private * For short-name entries, NT/XP etc store case information in byte 0x0c * Use this to show proper case of "README.txt" or "source.H" **/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static void FF_CaseShortName( FF_T_WCHAR * pcName, uint8_t attrib ) #else static void FF_CaseShortName( char * pcName, uint8_t attrib ) #endif { uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE; for( ; *pcName != '\0'; pcName++ ) { if( *pcName == '.' ) { testAttrib = FF_FAT_CASE_ATTR_EXT; } else if( ( attrib & testAttrib ) != 0 ) { if( ( *pcName >= 'A' ) && ( *pcName <= 'Z' ) ) { *pcName += 0x20; } } else if( ( *pcName >= 'a' ) && ( *pcName <= 'z' ) ) { *pcName -= 0x20; } } } #endif /* ffconfigSHORTNAME_CASE */ /*-----------------------------------------------------------*/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) /** * @private * Expand a short-name, adding a zero after each character **/ static void FF_ShortNameExpand( FF_T_WCHAR * pFileName ) { int8_t * pSource = ( ( int8_t * ) pFileName ) + 13; int8_t * pTarget = ( ( int8_t * ) pFileName ) + 26; /* "aaaaaaaa.eee": 12 characters plus a zero makes 13 * Copy from the right to the left */ while( pTarget > ( int8_t * ) pFileName ) { pTarget[ -1 ] = 0; pTarget[ -2 ] = *( --pSource ); pTarget -= 2; } } #endif /* ffconfigUNICODE_UTF16_SUPPORT */ /*-----------------------------------------------------------*/ /** * @private **/ static void FF_ProcessShortName( char * pcName ) { char pcShortName[ 13 ]; char * pcTarget = pcName; int iSource; memcpy( pcShortName, pcName, 11 ); for( iSource = 0; iSource < 11; iSource++ ) { if( pcShortName[ iSource ] == 0x20 ) { if( iSource >= 8 ) { break; } iSource = 7; } else { if( iSource == 8 ) { *( pcTarget++ ) = '.'; } *( pcTarget++ ) = pcShortName[ iSource ]; } } *pcTarget = '\0'; } /*-----------------------------------------------------------*/ #if ( ffconfigTIME_SUPPORT != 0 ) static void FF_PlaceTime( uint8_t * pucEntryBuffer, uint32_t Offset, FF_SystemTime_t * pxTime ) { uint16_t myShort; /* HT time changes: * E.g. Unzip needs to use original time rather than * the result of FF_GetSystemTime */ myShort = 0; myShort |= ( ( pxTime->Hour << 11 ) & 0xF800 ); myShort |= ( ( pxTime->Minute << 5 ) & 0x07E0 ); myShort |= ( ( pxTime->Second / 2 ) & 0x001F ); FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort ); } #endif /* ffconfigTIME_SUPPORT */ /*-----------------------------------------------------------*/ #if ( ffconfigTIME_SUPPORT != 0 ) static void FF_PlaceDate( uint8_t * pucEntryBuffer, uint32_t Offset, FF_SystemTime_t * pxTime ) { uint16_t myShort; /* HT time changes: * Unzip needs to use original date rather than * the current date, so make it a parameter. */ myShort = 0; myShort |= ( ( ( pxTime->Year - 1980 ) << 9 ) & 0xFE00 ); myShort |= ( ( pxTime->Month << 5 ) & 0x01E0 ); myShort |= ( pxTime->Day & 0x001F ); FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort ); } #endif /* ffconfigTIME_SUPPORT */ /*-----------------------------------------------------------*/ #if ( ffconfigTIME_SUPPORT != 0 ) static void FF_GetTime( FF_SystemTime_t * pxTime, const uint8_t * pucEntryBuffer, uint32_t Offset ) { uint16_t myShort; myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset ); pxTime->Hour = ( ( ( myShort & 0xF800 ) >> 11 ) & 0x001F ); pxTime->Minute = ( ( ( myShort & 0x07E0 ) >> 5 ) & 0x003F ); pxTime->Second = 2 * ( myShort & 0x01F ); } #endif /* ffconfigTIME_SUPPORT */ /*-----------------------------------------------------------*/ #if ( ffconfigTIME_SUPPORT != 0 ) static void FF_GetDate( FF_SystemTime_t * pxTime, const uint8_t * pucEntryBuffer, uint32_t Offset ) { uint16_t myShort; myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset ); pxTime->Year = 1980 + ( ( ( myShort & 0xFE00 ) >> 9 ) & 0x07F ); pxTime->Month = ( ( ( myShort & 0x01E0 ) >> 5 ) & 0x000F ); pxTime->Day = myShort & 0x01F; } #endif /* ffconfigTIME_SUPPORT */ /*-----------------------------------------------------------*/ void FF_PopulateShortDirent( FF_IOManager_t * pxIOManager, FF_DirEnt_t * pxDirEntry, const uint8_t * pucEntryBuffer ) { memcpy( pxDirEntry->pcFileName, pucEntryBuffer, 11 ); /* Copy the filename into the Dirent object. */ #if ( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 ); pxDirEntry->pcShortName[ 11 ] = '\0'; FF_ProcessShortName( pxDirEntry->pcShortName ); /* For debuggers only. */ #endif FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName ); /* Format the shortname, for pleasant viewing. */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) /* FileName contains a 8-bit short name * Expand it to a FF_T_WCHAR string */ FF_ShortNameExpand( pxDirEntry->pcFileName ); #endif ( void ) pxIOManager; /* Silence a compiler warning, about not referencing pxIOManager. */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_tolower( pxDirEntry->pcFileName, ( uint32_t ) wcslen( pxDirEntry->pcFileName ) ); #else FF_tolower( pxDirEntry->pcFileName, ( uint32_t ) strlen( pxDirEntry->pcFileName ) ); #endif /* Get the item's Cluster address. */ pxDirEntry->ulObjectCluster = ( ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) | ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW ); #if ( ffconfigTIME_SUPPORT != 0 ) /* Get the creation Time & Date. */ FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME ); FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE ); /* Get the modified Time & Date * HT Here xCreateTime became xModifiedTime: */ FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME ); FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE ); /* Get the last accessed Date. */ FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE ); pxDirEntry->xAccessedTime.Hour = 0; pxDirEntry->xAccessedTime.Minute = 0; pxDirEntry->xAccessedTime.Second = 0; #endif /* if ( ffconfigTIME_SUPPORT != 0 ) */ /* Get the filesize. */ pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) ); /* Get the attribute. */ pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ); } /*-----------------------------------------------------------*/ /* Initialises a context object for FF_FetchEntryWithContext( */ FF_Error_t FF_InitEntryFetch( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, FF_FetchContext_t * pxContext ) { FF_Error_t xError; memset( pxContext, 0, sizeof( FF_FetchContext_t ) ); /* Get the total length of the chain. */ pxContext->ulChainLength = FF_GetChainLength( pxIOManager, ulDirCluster, NULL, &xError ); if( FF_isERR( xError ) == pdFALSE ) { pxContext->ulDirCluster = ulDirCluster; pxContext->ulCurrentClusterLCN = ulDirCluster; if( pxIOManager->xPartition.ucType != FF_T_FAT32 ) { /* Handle Root Dirs that don't have cluster chains! */ if( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) { /* This is a RootDIR, special consideration needs to be made, because it doesn't have a Cluster chain! */ pxContext->ulChainLength = pxIOManager->xPartition.ulRootDirSectors / pxIOManager->xPartition.ulSectorsPerCluster; if( pxContext->ulChainLength == 0 ) /* Some media has ulRootDirSectors < ulSectorsPerCluster. This is wrong, as it should be atleast 1 cluster! */ { pxContext->ulChainLength = 1; } } } } return xError; } /* FF_InitEntryFetch() */ /*-----------------------------------------------------------*/ FF_Error_t FF_CleanupEntryFetch( FF_IOManager_t * pxIOManager, FF_FetchContext_t * pxContext ) { FF_Error_t xError = FF_ERR_NONE; if( pxContext->pxBuffer ) { xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer ); pxContext->pxBuffer = NULL; } return xError; } /* FF_CleanupEntryFetch() */ /*-----------------------------------------------------------*/ /** * @private * @brief Find the cluster for a given Entry within a directory * @brief Make an exception for the root directory ( non FAT32 only ): * @brief Just calculate the cluster ( don't consult the actual FAT ) * * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ). * @param ulEntry The sequence number of the entry of interest * @param pxContext Context of current search * * @Return FF_ERR_NONE on success * @Return Possible error returned by FF_TraverseFAT( ) or END_OF_DIR * * Side effects: * - pxContext->ulCurrentClusterNum : relative cluster number ( 0 <= Num < ulChainLength ) * - pxContext->ulCurrentClusterLCN : fysical cluster on the partition **/ static FF_Error_t FF_Traverse( FF_IOManager_t * pxIOManager, uint32_t ulEntry, FF_FetchContext_t * pxContext ) { uint32_t ulClusterNum = FF_getClusterChainNumber( pxIOManager, ulEntry, ( uint16_t ) FF_SIZEOF_DIRECTORY_ENTRY ); FF_Error_t xError = FF_ERR_NONE; /* Check if we're past the last cluster ( ulChainLength is also valid for root sectors ). */ if( ( ulClusterNum + 1 ) > pxContext->ulChainLength ) { xError = FF_ERR_DIR_END_OF_DIR | FF_TRAVERSE; /* End of Dir was reached! */ } else if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) && ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) ) { /* Double-check if the entry number isn't too high. */ if( ulEntry > ( ( pxIOManager->xPartition.ulRootDirSectors * pxIOManager->xPartition.usBlkSize ) / FF_SIZEOF_DIRECTORY_ENTRY ) ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FETCHENTRYWITHCONTEXT ); } else { pxContext->ulCurrentClusterLCN = pxContext->ulDirCluster; } } else if( ulClusterNum != pxContext->ulCurrentClusterNum ) { /* Traverse the fat gently! */ if( ulClusterNum > pxContext->ulCurrentClusterNum ) { /* Start traverse from the current entry. */ pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulCurrentClusterLCN, ( ulClusterNum - pxContext->ulCurrentClusterNum ), &xError ); } else { /* Start traverse from the beginning. */ pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulDirCluster, ulClusterNum, &xError ); } } if( FF_isERR( xError ) == pdFALSE ) { pxContext->ulCurrentClusterNum = ulClusterNum; } return xError; } /* FF_Traverse() */ /*-----------------------------------------------------------*/ FF_Error_t FF_FetchEntryWithContext( FF_IOManager_t * pxIOManager, uint32_t ulEntry, FF_FetchContext_t * pxContext, uint8_t * pEntryBuffer ) { uint32_t ulItemLBA; uint32_t ulRelItem; FF_Error_t xError; xError = FF_Traverse( pxIOManager, ulEntry, pxContext ); if( FF_isERR( xError ) == pdFALSE ) { ulRelItem = FF_getMinorBlockEntry( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); ulItemLBA = FF_Cluster2LBA( pxIOManager, pxContext->ulCurrentClusterLCN ) + FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) && ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) ) { ulItemLBA += ( ulEntry / ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) / FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster ); } ulItemLBA = FF_getRealLBA( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); if( ( pxContext->pxBuffer == NULL ) || ( pxContext->pxBuffer->ulSector != ulItemLBA ) || ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) != 0 ) ) { if( pxContext->pxBuffer != NULL ) { xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer ); pxContext->pxBuffer = NULL; } if( FF_isERR( xError ) == pdFALSE ) { pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_READ ); if( pxContext->pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT ); } } } if( ( pEntryBuffer != NULL ) && ( pxContext->pxBuffer != NULL ) ) { memcpy( pEntryBuffer, pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), FF_SIZEOF_DIRECTORY_ENTRY ); } } return xError; } /* FF_FetchEntryWithContext() */ /*-----------------------------------------------------------*/ FF_Error_t FF_PushEntryWithContext( FF_IOManager_t * pxIOManager, uint32_t ulEntry, FF_FetchContext_t * pxContext, uint8_t * pEntryBuffer ) { uint32_t ulItemLBA; uint32_t ulRelItem; FF_Error_t xError; xError = FF_Traverse( pxIOManager, ulEntry, pxContext ); if( FF_isERR( xError ) == pdFALSE ) { ulRelItem = FF_getMinorBlockEntry( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); ulItemLBA = FF_Cluster2LBA( pxIOManager, pxContext->ulCurrentClusterLCN ) + FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) && ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) ) { ulItemLBA += ( ulEntry / ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) / FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster ); } ulItemLBA = FF_getRealLBA( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY ); if( ( pxContext->pxBuffer == NULL ) || ( pxContext->pxBuffer->ulSector != ulItemLBA ) || ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) == 0 ) ) { if( pxContext->pxBuffer != NULL ) { xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer ); pxContext->pxBuffer = NULL; } if( FF_isERR( xError ) == pdFALSE ) { pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_WRITE ); if( pxContext->pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT ); } } } /* Now change the entry: */ if( pxContext->pxBuffer != NULL ) { memcpy( pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), pEntryBuffer, FF_SIZEOF_DIRECTORY_ENTRY ); } } return xError; } /* FF_PushEntryWithContext() */ /*-----------------------------------------------------------*/ /** * @private **/ FF_Error_t FF_GetEntry( FF_IOManager_t * pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t * pxDirEntry ) { /* A 32 byte directory entry. */ uint8_t ucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; FF_FetchContext_t xFetchContext; FF_Error_t xError; #if ( ffconfigLFN_SUPPORT == 0 ) BaseType_t xLFNCount; #endif xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, ucEntryBuffer ); if( ( FF_isERR( xError ) == pdFALSE ) && ( FF_isDeleted( ucEntryBuffer ) == pdFALSE ) ) { if( FF_isEndOfDir( ucEntryBuffer ) != pdFALSE ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_GETENTRY ); } else { pxDirEntry->ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ); if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) { #if ( ffconfigLFN_SUPPORT != 0 ) xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, usEntry, &xFetchContext ); #else /* LFN Processing. */ xLFNCount = ( BaseType_t ) ( ucEntryBuffer[ 0 ] & ~0x40 ); pxDirEntry->usCurrentItem += ( xLFNCount - 1 ); #endif } else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) { FF_PopulateShortDirent( pxIOManager, pxDirEntry, ucEntryBuffer ); pxDirEntry->usCurrentItem += 1; } } } { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } return xError; } /* FF_GetEntry() */ /*-----------------------------------------------------------*/ FF_Error_t FF_PopulateLongDirent( FF_IOManager_t * pxIOManager, FF_DirEnt_t * pxDirEntry, uint16_t usEntry, FF_FetchContext_t * pxFetchContext ) { /* First get the entire name as UTF-16 from the LFN's. * Then transform into the API's native string format. */ FF_Error_t xError; BaseType_t xNumLFNs; uint8_t ucCheckSum; /* A 32 byte directory entry. */ uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; char pcShortName[ 13 ]; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) UBaseType_t uiLfnLength = 0; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) BaseType_t xIndex, y; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) char * pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName ); char * pcCurPtr; #endif #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) uint16_t nLfnBegin; uint16_t usUtf8Len = 0; #endif /* ffconfigUNICODE_UTF8_SUPPORT */ do { xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { /* After breaking from this do {} while ( pdFALSE ) loop, xResult will be returned. */ break; } xNumLFNs = ( BaseType_t ) ( pucEntryBuffer[ 0 ] & ~0x40 ); ucCheckSum = FF_getChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { /* UTF-16 Can simply get segments of the UTF-16 sequence * going forward in the directory entries ( but reversed order ). */ while( xNumLFNs > 0 ) { /* Avoid stack intensive use of a UTF-16 buffer. Stream direct to FileName dirent field in correct format. */ /* memcpy direct! -UTF-16 support. */ memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 0, &( pucEntryBuffer[ FF_FAT_LFN_NAME_1 ] ), 10 ); memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 5, &( pucEntryBuffer[ FF_FAT_LFN_NAME_2 ] ), 12 ); memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 11, &( pucEntryBuffer[ FF_FAT_LFN_NAME_3 ] ), 4 ); uiLfnLength += 13; xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } xNumLFNs--; } if( FF_isERR( xError ) ) { break; } pxDirEntry->pcFileName[ uiLfnLength ] = '\0'; } #endif /* ffconfigUNICODE_UTF16_SUPPORT */ #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { /* UTF-8 Sequence, we can only convert this from the beginning, must receive entries in reverse. */ nLfnBegin = usEntry - 1; for( xIndex = 0; xIndex < xNumLFNs; xIndex++ ) { xError = FF_FetchEntryWithContext( pxIOManager, ( nLfnBegin + ( xNumLFNs - 1 ) - xIndex ), pxFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } /* Now have the first part of the UTF-16 sequence. Stream into a UTF-8 sequence. */ for( y = 0; y < 5; y++ ) { xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ], ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_1 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len ); if( xError > 0 ) { usUtf8Len += ( uint16_t ) xError; } } for( y = 0; y < 6; y++ ) { xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ], ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_2 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len ); if( xError > 0 ) { usUtf8Len += ( uint16_t ) xError; } } for( y = 0; y < 2; y++ ) { xError = FF_Utf16ctoUtf8c( ( uint8_t * ) &pxDirEntry->pcFileName[ usUtf8Len ], ( uint16_t * ) &pucEntryBuffer[ FF_FAT_LFN_NAME_3 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len ); if( xError > 0 ) { usUtf8Len += ( uint16_t ) xError; } } usEntry++; } if( FF_isERR( xError ) ) { break; } pxDirEntry->pcFileName[ usUtf8Len ] = '\0'; /* Put Entry context to correct position. */ xError = FF_FetchEntryWithContext( pxIOManager, usEntry - 1, pxFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } } #endif /* ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */ #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) /* No Unicode, simple ASCII. */ { pcLastPtr[ -1 ] = '\0'; y = xNumLFNs; while( xNumLFNs-- ) { pcCurPtr = pxDirEntry->pcFileName + ( xNumLFNs * 13 ); for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_1 + xIndex ]; } for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_2 + xIndex ]; } for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 ) { *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_3 + xIndex ]; } if( ( xNumLFNs == ( y - 1 ) ) && ( pcCurPtr < pcLastPtr ) ) { *pcCurPtr = '\0'; } xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } } if( FF_isERR( xError ) ) { break; } } #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */ /* Process the Shortname. -- LFN Transformation is now complete. * Process the ShortName Entry. */ /* if SHORTNAMES must be included, simple byte copy into shortname buffer. */ #if ( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) { memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 ); pxDirEntry->pcShortName[ 11 ] = '\0'; FF_ProcessShortName( pxDirEntry->pcShortName ); } #endif /* ( != 0 ffconfigLFN_SUPPORT ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) */ memcpy( pcShortName, pucEntryBuffer, 11 ); FF_ProcessShortName( pcShortName ); if( ucCheckSum != FF_CreateChkSum( pucEntryBuffer ) ) { strcpy( pxDirEntry->pcFileName, pcShortName ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { FF_ShortNameExpand( pxDirEntry->pcFileName ); } #endif /* ffconfigUNICODE_UTF16_SUPPORT */ } /* Finally fill in the other details. */ pxDirEntry->ulObjectCluster = ( ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) | ( uint32_t ) FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW ); #if ( ffconfigTIME_SUPPORT != 0 ) { /* Get the creation Time & Date. */ FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME ); FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE ); /* Get the modified Time & Date. */ /* HT Here xCreateTime has become xModifiedTime, as it should: */ FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME ); FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE ); /* Get the last accessed Date. */ FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE ); /* HT Why should these times be zero'd ? */ pxDirEntry->xAccessedTime.Hour = 0; pxDirEntry->xAccessedTime.Minute = 0; pxDirEntry->xAccessedTime.Second = 0; } #endif /* ffconfigTIME_SUPPORT */ /* Get the filesize. */ pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) ); /* Get the attribute. */ pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ); pxDirEntry->usCurrentItem = usEntry; } while( pdFALSE ); return xError; } /* FF_PopulateLongDirent() */ /*-----------------------------------------------------------*/ /** * @public * @brief Find's the first directory entry for the provided path. * * All values recorded in pxDirEntry must be preserved to and between calls to * FF_FindNext( ). * * If ffconfigFINDAPI_ALLOW_WILDCARDS is defined, then path will have the following behaviour: * * path = "\" - Open the root dir, and iterate through all items. * path = "\*.c" - Open the root dir, showing only files matching *.c wildcard. * path = "\sub1\newdir" - Get the DIRENT for the newdir directory in /sub1/ if one exists. * path = "\sub1\newdir\" - Open the directory /sub1/newdir/ and iterate through all items. * path = "\sub1\newdir\*.c" - Open the directory /sub1/newdir/ and iterate through all items matching the *.c wildcard. * * It is important to distinguish the differences in behaviour between opening a Find operation * on a path like /sub1 and /sub1/. ( /sub1 gets the sub1 dirent from the / dir, whereas /sub/ opens the sub1 dir ). * * Note, as compatible with other similar APIs, FreeRTOS+FAT also accepts \sub1\* for the same behaviour as * /sub1/. * * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ). * @param pxDirEntry FF_DirEnt_t object to store the entry information. * @param path String to of the path to the Dir being listed. * * @Return 0 on success * @Return FF_ERR_DEVICE_DRIVER_FAILED if device access failed. * @Return -2 if Dir was not found. * **/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_Error_t FF_FindFirst( FF_IOManager_t * pxIOManager, FF_DirEnt_t * pxDirEntry, const FF_T_WCHAR * pcPath ) #else FF_Error_t FF_FindFirst( FF_IOManager_t * pxIOManager, FF_DirEnt_t * pxDirEntry, const char * pcPath ) #endif { FF_Error_t xError; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) uint16_t PathLen = ( uint16_t ) wcslen( pcPath ); #else uint16_t PathLen = ( uint16_t ) strlen( pcPath ); #endif #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) BaseType_t xIndex = 0; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) const FF_T_WCHAR * pcWildCard; /* Check for a Wild-card. */ #else const char * pcWildCard; /* Check for a Wild-card. */ #endif #endif memset( pxDirEntry, 0, sizeof( FF_DirEnt_t ) ); if( pxIOManager == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST ); } #if ( ffconfigREMOVABLE_MEDIA != 0 ) else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 ) { xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDFIRST ); } #endif /* ffconfigREMOVABLE_MEDIA */ else { #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) { pxDirEntry->pcWildCard[ 0 ] = '\0'; /* WildCard blank if its not a wildCard. */ pcWildCard = &pcPath[ PathLen - 1 ]; if( PathLen != 0 ) { /* Open the dir of the last token. */ while( ( *pcWildCard != '\\' ) && ( *pcWildCard != '/' ) ) { xIndex++; pcWildCard--; if( PathLen == xIndex ) { break; } } } pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen - xIndex, &xError ); if( FF_isERR( xError ) == pdFALSE ) { if( pxDirEntry->ulDirCluster != 0 ) { /* Valid Dir found, copy the wildCard to filename! */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) wcsncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME ); #else strncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME ); #endif if( pxDirEntry->pcWildCard[ xIndex - 1 ] == ':' ) { pxDirEntry->xInvertWildCard = pdTRUE; pxDirEntry->pcWildCard[ xIndex - 1 ] = '\0'; } } } } #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */ { /* Get the directory cluster, if it exists. */ pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen, &xError ); } #endif /* ffconfigFINDAPI_ALLOW_WILDCARDS */ if( FF_isERR( xError ) == pdFALSE ) { if( pxDirEntry->ulDirCluster == 0 ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_FINDFIRST ); } else { /* Initialise the Fetch Context. */ xError = FF_InitEntryFetch( pxIOManager, pxDirEntry->ulDirCluster, &( pxDirEntry->xFetchContext ) ); if( FF_isERR( xError ) == pdFALSE ) { pxDirEntry->usCurrentItem = 0; xError = FF_FindNext( pxIOManager, pxDirEntry ); } } } } return xError; } /* FF_FindFirst() */ /*-----------------------------------------------------------*/ /** * @public * @brief Get's the next Entry based on the data recorded in the FF_DirEnt_t object. * * All values recorded in pxDirEntry must be preserved to and between calls to * FF_FindNext( ). Please see @see FF_FindFirst( ) for find initialisation. * * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ). * @param pxDirEntry FF_DirEnt_t object to store the entry information. ( As initialised by FF_FindFirst( )). * * @Return FF_ERR_DEVICE_DRIVER_FAILED is device access failed. * **/ FF_Error_t FF_FindNext( FF_IOManager_t * pxIOManager, FF_DirEnt_t * pxDirEntry ) { FF_Error_t xError; BaseType_t xLFNCount; const uint8_t * pucEntryBuffer = NULL; #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) BaseType_t b; #endif if( pxIOManager == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDNEXT ); } #if ( ffconfigREMOVABLE_MEDIA != 0 ) else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 ) { xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDNEXT ); } #endif /* ffconfigREMOVABLE_MEDIA */ else { xError = FF_ERR_NONE; for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) { if( ( pucEntryBuffer == NULL ) || ( pucEntryBuffer >= ( pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) ) { xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &( pxDirEntry->xFetchContext ), NULL ); if( FF_isERR( xError ) ) { break; } if( pucEntryBuffer == NULL ) { pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) ); } else { pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer; } } else { pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY; } if( FF_isDeleted( pucEntryBuffer ) != pdFALSE ) { /* The entry is not in use or deleted. */ continue; } if( FF_isEndOfDir( pucEntryBuffer ) ) { /* End of directory, generate a pseudo error 'DIR_END_OF_DIR'. */ xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT ); break; } pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ); if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) { /* LFN Processing. */ xLFNCount = ( BaseType_t ) ( pucEntryBuffer[ 0 ] & ~0x40 ); /* Get the shortname and check if it is marked deleted. */ #if ( ffconfigLFN_SUPPORT != 0 ) { /* Reserve 32 bytes to hold one directory entry. */ uint8_t Buffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; /* Fetch the shortname, and get it's checksum, or for a deleted item with * orphaned LFN entries. */ xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) ( pxDirEntry->usCurrentItem + xLFNCount ), &pxDirEntry->xFetchContext, Buffer ); if( FF_isERR( xError ) ) { break; } if( FF_isDeleted( Buffer ) == pdFALSE ) { xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, pxDirEntry->usCurrentItem, &pxDirEntry->xFetchContext ); if( FF_isERR( xError ) ) { break; } #if ( ffconfigINCLUDE_SHORT_NAME != 0 ) { pxDirEntry->ucAttrib |= FF_FAT_ATTR_IS_LFN; } #endif #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) if( wcscmp( pxDirEntry->pcWildCard, L"" ) ) #else if( pxDirEntry->pcWildCard[ 0 ] ) #endif { b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName ); if( pxDirEntry->xInvertWildCard != pdFALSE ) { b = !b; } if( b != 0 ) { break; } /* 'usCurrentItem' has already incremented by FF_PopulateLongDirent(), * this loop will incremente it again. */ pxDirEntry->usCurrentItem -= 1; /* xFetchContext/usCurrentItem have changed. Update * 'pucEntryBuffer' to point to the current buffer position. */ pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) ); } else { break; } } #else /* ffconfigFINDAPI_ALLOW_WILDCARDS == 0 */ { /* usCurrentItem has been incremented by FF_PopulateLongDirent(). * Entry will be returned. */ break; } #endif /* if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) */ } } #else /* ffconfigLFN_SUPPORT */ { /* Increment 'usCurrentItem' with (xLFNCount-1), * the loop will do an extra increment. */ pxDirEntry->usCurrentItem += ( xLFNCount - 1 ); } #endif /* ffconfigLFN_SUPPORT */ } /* ( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) */ else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) { /* If it's not a LFN entry, neither a Volume ID, it is a normal short name entry. */ FF_PopulateShortDirent( pxIOManager, pxDirEntry, pucEntryBuffer ); #if ( ffconfigSHORTNAME_CASE != 0 ) { /* Apply NT/XP+ bits to get correct case. */ FF_CaseShortName( pxDirEntry->pcFileName, FF_getChar( pucEntryBuffer, FF_FAT_CASE_OFFS ) ); } #endif #if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) { if( pxDirEntry->pcWildCard[ 0 ] ) { b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName ); if( pxDirEntry->xInvertWildCard != pdFALSE ) { b = !b; } if( b != 0 ) { pxDirEntry->usCurrentItem += 1; break; } } else { pxDirEntry->usCurrentItem += 1; break; } } #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */ { pxDirEntry->usCurrentItem += 1; break; } #endif /* if ( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 ) */ } } /* for ( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */ if( pxDirEntry->usCurrentItem == FF_MAX_ENTRIES_PER_DIRECTORY ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT ); } { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &pxDirEntry->xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } return xError; } /* FF_FindNext() */ /*-----------------------------------------------------------*/ /* * Returns >= 0 for a free dirent entry. * Returns < 0 with and xError code if anything goes wrong. */ static int32_t FF_FindFreeDirent( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams, uint16_t usSequential ) { const uint8_t * pucEntryBuffer = NULL; uint16_t freeCount = 0; UBaseType_t uxEntry = 0; BaseType_t xEntryFound = pdFALSE; FF_Error_t xError; uint32_t DirLength; FF_FetchContext_t xFetchContext; uint32_t ulDirCluster = pxFindParams->ulDirCluster; xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { uxEntry = pxFindParams->lFreeEntry >= 0 ? pxFindParams->lFreeEntry : 0; for( ; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ ) { if( ( pucEntryBuffer == NULL ) || ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) { xError = FF_FetchEntryWithContext( pxIOManager, uxEntry, &xFetchContext, NULL ); if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) { xError = FF_ExtendDirectory( pxIOManager, ulDirCluster ); /* The value of xEntryFound will be ignored in case there was an error. */ xEntryFound = pdTRUE; break; } else if( FF_isERR( xError ) ) { break; } if( pucEntryBuffer == NULL ) { pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_DIRECTORY_ENTRY * ( uxEntry % ( pxIOManager->usSectorSize / FF_SIZEOF_DIRECTORY_ENTRY ) ) ); } else { pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer; } } else { /* Advance 32 bytes to point to the next directory entry. */ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY; } if( FF_isEndOfDir( pucEntryBuffer ) ) /* If its the end of the Dir, then FreeDirents from here. */ { /* Check if the directory has enough space */ DirLength = xFetchContext.ulChainLength; if( ( uxEntry + usSequential ) > ( ( DirLength * ( ( UBaseType_t ) pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->xPartition.usBlkSize ) ) / FF_SIZEOF_DIRECTORY_ENTRY ) ) { xError = FF_ExtendDirectory( pxIOManager, ulDirCluster ); } xEntryFound = pdTRUE; break; } if( FF_isDeleted( pucEntryBuffer ) ) { if( ++freeCount == usSequential ) { xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); xEntryFound = pdTRUE; uxEntry = ( uxEntry - ( usSequential - 1 ) ); /* Return the beginning entry in the sequential sequence. */ break; } } else { freeCount = 0; } } /* for ( uxEntry = 0; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ ) */ { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } if( FF_isERR( xError ) == pdFALSE ) { if( xEntryFound != pdFALSE ) { /* No error has occurred and a free directory entry has been found. */ xError = uxEntry; } else { xError = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_FINDFREEDIRENT ); } } return xError; } /* FF_FindFreeDirent() */ /*-----------------------------------------------------------*/ /* _HT_ Now FF_PutEntry has a new optional parameter *pucContents */ /* _HT_ so it can be used FF_MkDir( ) to save some code when adding . and .. entries */ FF_Error_t FF_PutEntry( FF_IOManager_t * pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t * pxDirEntry, uint8_t * pucContents ) { FF_Error_t xError; /* Reserve 32 bytes to hold one directory entry. */ uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; FF_FetchContext_t xFetchContext; /* HT: use the standard access routine to get the same logic for root dirs. */ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) == pdFALSE ) { /* Cleanup probably not necessary here? * FF_PushEntryWithContext checks for R/W flag. */ xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { if( pucContents != NULL ) { memcpy( pucEntryBuffer, pucContents, sizeof( pucEntryBuffer ) ); } FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint32_t ) pxDirEntry->ucAttrib ); FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) ( pxDirEntry->ulObjectCluster >> 16 ) ); FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) ( pxDirEntry->ulObjectCluster ) ); FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize ); #if ( ffconfigTIME_SUPPORT != 0 ) { FF_GetSystemTime( &pxDirEntry->xAccessedTime ); /*/< Date of Last Access. */ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime ); FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime ); /* Last accessed date. */ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime ); FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime ); FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime ); FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime ); } #endif /* ffconfigTIME_SUPPORT */ xError = FF_PushEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer ); } } } FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); return xError; } /* FF_PutEntry() */ /*-----------------------------------------------------------*/ static BaseType_t FF_ValidShortChar( char cChar ) { return ( cChar >= 'A' && cChar <= 'Z' ) || ( cChar >= 'a' && cChar <= 'z' ) || /* lower-case can be stored using NT/XP attribute. */ ( cChar >= '0' && cChar <= '9' ) || strchr( "$%-_@~`!(){}^#&", cChar ) != NULL; } /* FF_ValidShortChar() */ /*-----------------------------------------------------------*/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) void FF_CreateShortName( FF_FindParams_t * pxFindParams, const FF_T_WCHAR * pcLongName ) #else void FF_CreateShortName( FF_FindParams_t * pxFindParams, const char * pcLongName ) #endif { BaseType_t xIndex, xPosition, xLastDot; uint16_t NameLen; #if ( ffconfigSHORTNAME_CASE != 0 ) uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE; #endif /* Examples: * "readme.TXT" will get the attribute FF_FAT_CASE_ATTR_BASE * "README.txt" will get the attribute FF_FAT_CASE_ATTR_EXT * "Readme.txt" can not be store as a short name */ pxFindParams->ucCaseAttrib = 0; /* May get the value FF_FAT_CASE_ATTR_BASE or FF_FAT_CASE_ATTR_EXT */ pxFindParams->ucFirstTilde = 6; /* The numerical position of the ~ */ pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_SET | FIND_FLAG_FITS_SHORT | FIND_FLAG_SIZE_OK; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { NameLen = ( uint16_t ) wcslen( pcLongName ); } #else { NameLen = ( uint16_t ) strlen( pcLongName ); } #endif /* Does pcLongName fit a shortname? */ for( xIndex = 0, xPosition = 0, xLastDot = NameLen; xIndex < NameLen; xIndex++ ) { if( pcLongName[ xIndex ] != '.' ) { xPosition++; } else { xLastDot = xIndex; } } /* For example: * "FILENAME.EXT": NameLen = 12, xLastDot = 8, xPosition = 11 * ".cproject" : NameLen = 9, xLastDot = 0, xPosition = 8 */ if( ( NameLen > 12 ) || /* If name is longer than 12 characters (8.3). */ ( NameLen - xPosition > 1 ) || /* If it contains more than 1 dot. */ ( NameLen - xLastDot > 4 ) || /* If the file name extension is longer than 3 characters. */ ( xLastDot > 8 ) ) /* If the file name base is too long. */ { pxFindParams->ulFlags &= ~FIND_FLAG_SIZE_OK; } for( xIndex = 0, xPosition = 0; xIndex < 11; xPosition++ ) { char ch = pcLongName[ xPosition ]; if( !ch ) { break; } if( ( xIndex == 0 ) && ( ch == '.' ) ) { pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; continue; } if( xPosition == xLastDot ) { /* Remember where we put the first space. */ if( pxFindParams->ucFirstTilde > xIndex ) { pxFindParams->ucFirstTilde = xIndex; } while( xIndex < 8 ) { pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20; } #if ( ffconfigSHORTNAME_CASE != 0 ) { testAttrib = FF_FAT_CASE_ATTR_EXT; } #endif } else { if( xIndex == 8 ) { if( xPosition <= xLastDot ) { xPosition = xLastDot; ch = ( int8_t ) pcLongName[ xPosition ]; if( ch == '\0' ) { break; } ch = ( int8_t ) pcLongName[ ++xPosition ]; #if ( ffconfigSHORTNAME_CASE != 0 ) { testAttrib = FF_FAT_CASE_ATTR_EXT; } #endif } } if( !FF_ValidShortChar( ch ) ) { pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; continue; } #if ( ffconfigSHORTNAME_CASE != 0 ) { if( ( ch >= 'a' ) && ( ch <= 'z' ) ) { ch -= 0x20; if( testAttrib ) { pxFindParams->ucCaseAttrib |= testAttrib; } else { pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had capital: does not fit. */ } } else if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) { if( ( pxFindParams->ucCaseAttrib & testAttrib ) != 0 ) { pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had lower-case: does not fit. */ } testAttrib = 0; } } #else /* if ( ffconfigSHORTNAME_CASE != 0 ) */ { if( ( ch >= 'a' ) && ( ch <= 'z' ) ) { ch -= 0x20; } } #endif /* ffconfigSHORTNAME_CASE */ pxFindParams->pcEntryBuffer[ xIndex++ ] = ch; } } if( ( xLastDot == 0 ) && ( xIndex < 6 ) ) { /* This is a file name like ".info" or ".root" */ pxFindParams->ucFirstTilde = xIndex; } while( xIndex < 11 ) { pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20; } if( ( xLastDot < pxFindParams->ucFirstTilde ) && ( xLastDot > 0 ) ) { pxFindParams->ucFirstTilde = xLastDot; } if( NameLen < pxFindParams->ucFirstTilde ) /* Names like "Abc" will become "~Abc". */ { pxFindParams->ucFirstTilde = ( uint8_t ) NameLen; } } /* FF_CreateShortName() */ /*-----------------------------------------------------------*/ int32_t FF_FindShortName( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams ) { char pcMyShortName[ 13 ]; FF_DirEnt_t xMyDirectory; FF_Error_t xResult = 0; BaseType_t xIndex, x, y; uint16_t NameLen; char pcNumberBuf[ 12 ]; uint32_t ulCluster; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_T_WCHAR pcFileName[ 13 ]; #else char * pcFileName = pcMyShortName; #endif /* ffconfigUNICODE_UTF16_SUPPORT */ #if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) uint16_t usShortHash; uint32_t ulRand = 0ul; #endif memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 ); FF_ProcessShortName( pcMyShortName ); if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK ) { /* This entry will not get a LFN entry because it fits * perfectly into a host name */ if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_CHECKED ) != 0 ) { if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_FOUND ) != 0 ) { xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME ); } else { xResult = pxFindParams->ucCaseAttrib | 0x01; } } else { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { memcpy( pcFileName, pcMyShortName, sizeof( pcMyShortName ) ); FF_ShortNameExpand( pcFileName ); } #endif ulCluster = FF_FindEntryInDir( pxIOManager, pxFindParams, pcFileName, 0x00, &xMyDirectory, &xResult ); /* END_OF_DIR is not a fatal error, it only means that the entry was not found. */ if( ( FF_isERR( xResult ) == pdFALSE ) || ( FF_GETERROR( xResult ) == FF_ERR_DIR_END_OF_DIR ) ) { if( ulCluster == 0UL ) { xResult = pxFindParams->ucCaseAttrib | 0x01; } else { xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME ); } } else { /* There was an error, which will be returned. */ } } } else { for( xIndex = ( ( pxFindParams->ulFlags & FIND_FLAG_SIZE_OK ) ? 0 : 1 ); ; xIndex++ ) { if( xIndex != 0 ) { #if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) { /* In the first round, check if the original name can be used * Makefile will be stored as "makefile" and not as "makefi~1". */ /* This method saves a lot of time when creating directories with * many similar file names: when the short name version of a LFN already * exists, try at most 3 entries with a tilde: * README~1.TXT * README~2.TXT * README~3.TXT * After that create entries with pseudo-random 4-digit hex digits: * REA~E7BB.TXT * REA~BA32.TXT * REA~D394.TXT */ if( xIndex <= 4 ) { snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex ); } else { if( ulRand == 0ul ) { ulRand = pxIOManager->xPartition.ulLastFreeCluster; usShortHash = FF_GetCRC16( ( uint8_t * ) &ulRand, sizeof( ulRand ) ); } else { usShortHash = FF_GetCRC16( ( uint8_t * ) &usShortHash, sizeof( usShortHash ) ); } snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%04X", ( int ) usShortHash ); } } #else /* if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) */ { snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex ); } #endif /* if ( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 ) */ NameLen = ( uint16_t ) strlen( pcNumberBuf ); x = 7 - NameLen; if( x > pxFindParams->ucFirstTilde ) { x = pxFindParams->ucFirstTilde; } pxFindParams->pcEntryBuffer[ x++ ] = '~'; for( y = 0; y < NameLen; y++ ) { pxFindParams->pcEntryBuffer[ x + y ] = pcNumberBuf[ y ]; } } memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 ); FF_ProcessShortName( pcMyShortName ); if( FF_ShortNameExists( pxIOManager, pxFindParams->ulDirCluster, pcMyShortName, &xResult ) == pdFALSE ) { break; } if( xIndex >= FF_MAX_ENTRIES_PER_DIRECTORY ) { xResult = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_CREATESHORTNAME ); break; } } /* Add a tail and special number until we're happy :D. */ } return xResult; } /* FF_FindShortName () */ /*-----------------------------------------------------------*/ #if ( ffconfigLFN_SUPPORT != 0 ) static int8_t FF_CreateLFNEntry( uint8_t * pucEntryBuffer, uint8_t * pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum ) { /* * HT for JW: * Changed *pcName from 16- to of 8-bits * The caller of this function doesn't need an expensive * uint16_t usUtf16Name[ffconfigMAX_FILENAME + 1]; * in case UNICODE isn't used * Also did quite a bit of optimisation here * and tested well */ UBaseType_t uxIndex, x; memset( pucEntryBuffer, 0, FF_SIZEOF_DIRECTORY_ENTRY ); FF_putChar( pucEntryBuffer, FF_FAT_LFN_ORD, ( uint8_t ) ( ( uxLFN & ~0x40 ) ) ); FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint8_t ) FF_FAT_ATTR_LFN ); FF_putChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM, ( uint8_t ) ucCheckSum ); /* Name_1. */ uxIndex = 0; for( x = FF_FAT_LFN_NAME_1; uxIndex < 5u; uxIndex++, x += 2 ) { if( uxIndex < uxNameLength ) { pucEntryBuffer[ x ] = *( pcName++ ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { pucEntryBuffer[ x + 1 ] = *( pcName++ ); } #endif } else if( uxIndex > uxNameLength ) { pucEntryBuffer[ x ] = 0xFF; pucEntryBuffer[ x + 1 ] = 0xFF; } } /* Name_2. */ for( x = FF_FAT_LFN_NAME_2; uxIndex < 11u; uxIndex++, x += 2 ) { if( uxIndex < uxNameLength ) { pucEntryBuffer[ x ] = *( pcName++ ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { pucEntryBuffer[ x + 1 ] = *( pcName++ ); } #endif } else if( uxIndex > uxNameLength ) { pucEntryBuffer[ x ] = 0xFF; pucEntryBuffer[ x + 1 ] = 0xFF; } } /* Name_3. */ for( x = FF_FAT_LFN_NAME_3; uxIndex < 13u; uxIndex++, x += 2 ) { if( uxIndex < uxNameLength ) { pucEntryBuffer[ x ] = *( pcName++ ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { pucEntryBuffer[ x + 1 ] = *( pcName++ ); } #endif } else if( uxIndex > uxNameLength ) { pucEntryBuffer[ x ] = 0xFF; pucEntryBuffer[ x + 1 ] = 0xFF; } } return FF_ERR_NONE; } /* FF_CreateLFNEntry() */ #endif /* ffconfigLFN_SUPPORT */ /*-----------------------------------------------------------*/ #if ( ffconfigLFN_SUPPORT != 0 ) #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR * pcName, uint8_t ucCheckSum, uint16_t usEntry ) #else static FF_Error_t FF_CreateLFNs( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, char * pcName, uint8_t ucCheckSum, uint16_t usEntry ) #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ { FF_Error_t xError = FF_ERR_NONE; BaseType_t xNumLFNs; BaseType_t xEndPos; BaseType_t xIndex, y; FF_FetchContext_t xFetchContext; uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) int32_t slRetVal; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) uint16_t usUtf16Name[ ffconfigMAX_FILENAME + 1 ]; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) char * NamePtr; #else int16_t * NamePtr; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { #if WCHAR_MAX <= 0xFFFF { y = wcslen( pcName ); if( y > ffconfigMAX_FILENAME ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS ); } else { wcsncpy( usUtf16Name, pcName, ffconfigMAX_FILENAME ); } } #else /* if WCHAR_MAX <= 0xFFFF */ { xIndex = 0; y = 0; while( pcName[ xIndex ] ) { FF_Utf32ctoUtf16c( &usUtf16Name[ y ], ( uint32_t ) pcName[ xIndex ], ffconfigMAX_FILENAME - xIndex ); y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] ); xIndex++; if( y > ffconfigMAX_FILENAME ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS ); break; } } } #endif /* if WCHAR_MAX <= 0xFFFF */ } #endif /* ffconfigUNICODE_UTF16_SUPPORT */ /* Convert the name into UTF-16 format. */ #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { /* Simply convert the UTF8 to UTF16 and be done with it. */ xIndex = 0; y = 0; while( pcName[ xIndex ] != 0 ) { slRetVal = FF_Utf8ctoUtf16c( &( usUtf16Name[ y ] ), ( uint8_t * ) &( pcName[ xIndex ] ), ffconfigMAX_FILENAME - xIndex ); if( slRetVal > 0 ) { xIndex += slRetVal; } else { break; /* No more space in the UTF-16 buffer, simply truncate for safety. */ } y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] ); if( y > ffconfigMAX_FILENAME ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS ); break; } } } #elif ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) { /* Just check the length. */ y = strlen( pcName ); if( y > ffconfigMAX_FILENAME ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS ); } } #endif /* if ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */ /* Whole name is now in a valid UTF-16 format. Lets go make thos LFN's. * At this point, it should a be the length of the name. */ if( FF_isERR( xError ) == pdFALSE ) { xNumLFNs = y / 13; /* Number of LFNs is the total number of UTF-16 units, divided by 13 ( 13 units per LFN ). */ xEndPos = y % 13; /* The ending position in an LFN, of the last LFN UTF-16 character. */ if( xEndPos ) { xNumLFNs++; } else { xEndPos = 13; } xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { NamePtr = ( int16_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) ); } #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) { NamePtr = ( char * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) ); } #else { NamePtr = pcName + 13 * ( xNumLFNs - 1 ); } #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ /* After this point, xIndex is no longer the length of the Filename in UTF-16 units. */ for( xIndex = xNumLFNs; xIndex > 0; xIndex-- ) { if( xIndex == xNumLFNs ) { FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) xEndPos, ( UBaseType_t ) xIndex, ucCheckSum ); pucEntryBuffer[ 0 ] |= 0x40; } else { FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) 13u, ( UBaseType_t ) xIndex, ucCheckSum ); } NamePtr -= 13; xError = FF_PushEntryWithContext( pxIOManager, ( uint32_t ) ( usEntry + ( xNumLFNs - xIndex ) ), &xFetchContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } } { FF_Error_t xTempError; /* Release any buffers that were used. */ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xTempError ) == pdFALSE ) { xError = xTempError; } } } } return xError; } /* FF_CreateLFNs() */ #endif /* ffconfigLFN_SUPPORT */ /*-----------------------------------------------------------*/ FF_Error_t FF_ExtendDirectory( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster ) { uint32_t xCurrentCluster; uint32_t xNextCluster = 0UL; FF_Error_t xError = FF_ERR_NONE; FF_FATBuffers_t xFATBuffers; if( ( ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) && ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) ) { /* root directories on FAT12 and FAT16 can not be extended. */ xError = ( FF_Error_t ) ( FF_ERR_DIR_CANT_EXTEND_ROOT_DIR | FF_EXTENDDIRECTORY ); } else if( pxIOManager->xPartition.ulFreeClusterCount == 0UL ) { /* The number of free clusters was not yet calculated or equal to zero. */ pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); } if( FF_isERR( xError ) == pdFALSE ) { if( pxIOManager->xPartition.ulFreeClusterCount == 0UL ) { xError = ( FF_Error_t ) ( FF_ERR_FAT_NO_FREE_CLUSTERS | FF_EXTENDDIRECTORY ); } else { FF_LockFAT( pxIOManager ); { xCurrentCluster = FF_FindEndOfChain( pxIOManager, ulDirCluster, &xError ); if( FF_isERR( xError ) == pdFALSE ) { xNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE ); if( FF_isERR( xError ) == pdFALSE ) { FF_InitFATBuffers( &xFATBuffers, FF_MODE_WRITE ); /* xNextCluster already has been set to 0xFFFFFFFF, * now let xCurrentCluster point to xNextCluster. */ xError = FF_putFATEntry( pxIOManager, xCurrentCluster, xNextCluster, &xFATBuffers ); { FF_Error_t xTempError; xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } xTempError = FF_DecreaseFreeClusters( pxIOManager, 1 ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } } } FF_UnlockFAT( pxIOManager ); if( FF_isERR( xError ) == pdFALSE ) { /* The entire cluster will be filled with zero's, * because it will contain directory data. */ xError = FF_ClearCluster( pxIOManager, xNextCluster ); } } } return xError; } /* FF_ExtendDirectory() */ /*-----------------------------------------------------------*/ static const uint8_t forbiddenChrs[] = { /* Windows says: don't use these characters: '\/:*?"<>|' * " * / : < > ? '\' ? | */ 0x22, 0x2A, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x5C, 0x7F, 0x7C }; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) static void FF_MakeNameCompliant( FF_T_WCHAR * pcName ) #else static void FF_MakeNameCompliant( char * pcName ) #endif { BaseType_t index; if( ( uint8_t ) pcName[ 0 ] == FF_FAT_DELETED ) /* Support Japanese KANJI symbol0xE5. */ { pcName[ 0 ] = 0x05; } for( ; *pcName; pcName++ ) { for( index = 0; index < ( BaseType_t ) sizeof( forbiddenChrs ); index++ ) { if( *pcName == forbiddenChrs[ index ] ) { *pcName = '_'; break; } } } } /* FF_MakeNameCompliant() */ /*-----------------------------------------------------------*/ FF_Error_t FF_CreateDirent( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams, FF_DirEnt_t * pxDirEntry ) { uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; BaseType_t xLFNCount; int32_t lFreeEntry = 0L; FF_Error_t xReturn = FF_ERR_NONE; BaseType_t xEntryCount; FF_FetchContext_t xFetchContext; uint32_t ulDirCluster = pxFindParams->ulDirCluster; int32_t lFitShort; #if ( ffconfigHASH_CACHE != 0 ) char pcShortName[ 13 ]; #endif #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) uint16_t NameLen = ( uint16_t ) wcslen( pxDirEntry->pcFileName ); #else uint16_t NameLen = ( uint16_t ) strlen( pxDirEntry->pcFileName ); #endif #if ( ffconfigLFN_SUPPORT != 0 ) uint8_t ucCheckSum; #endif /* Round-up the number of LFN's needed: */ xLFNCount = ( BaseType_t ) ( ( NameLen + 12 ) / 13 ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */ } #else { FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */ } #endif memset( pucEntryBuffer, 0, sizeof( pucEntryBuffer ) ); #if ( ffconfigLFN_SUPPORT != 0 ) { /* Create and push the LFN's. */ /* Find enough places for the LFNs and the ShortName. */ xEntryCount = xLFNCount + 1; } #else { xEntryCount = 1; } #endif /* Create the ShortName. */ FF_LockDirectory( pxIOManager ); do { /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */ /* As FF_FindShortName( ) can fail, it should be called before finding a free directory entry. */ if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_SET ) == 0 ) { FF_CreateShortName( pxFindParams, pxDirEntry->pcFileName ); } lFitShort = FF_FindShortName( pxIOManager, pxFindParams ); memcpy( pucEntryBuffer, pxFindParams->pcEntryBuffer, sizeof( pucEntryBuffer ) ); if( FF_isERR( lFitShort ) ) { xReturn = lFitShort; break; } if( lFitShort != 0 ) { /* There is no need to create a LFN entry because the file name * fits into a normal 32-byte entry.. */ xLFNCount = 0; xEntryCount = 1; } lFreeEntry = FF_FindFreeDirent( pxIOManager, pxFindParams, ( uint16_t ) xEntryCount ); if( FF_isERR( lFreeEntry ) ) { xReturn = lFreeEntry; break; } #if ( ffconfigLFN_SUPPORT != 0 ) { if( xLFNCount > 0 ) { ucCheckSum = FF_CreateChkSum( pucEntryBuffer ); xReturn = FF_CreateLFNs( pxIOManager, ulDirCluster, pxDirEntry->pcFileName, ucCheckSum, ( uint16_t ) lFreeEntry ); } } #else { xLFNCount = 0; } #endif /* ffconfigLFN_SUPPORT */ if( FF_isERR( xReturn ) == pdFALSE ) { #if ( ffconfigTIME_SUPPORT != 0 ) { FF_GetSystemTime( &pxDirEntry->xCreateTime ); /* Date and Time Created. */ pxDirEntry->xModifiedTime = pxDirEntry->xCreateTime; /* Date and Time Modified. */ pxDirEntry->xAccessedTime = pxDirEntry->xCreateTime; /* Date of Last Access. */ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime ); FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime ); FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime ); FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime ); } #endif /* ffconfigTIME_SUPPORT */ FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, pxDirEntry->ucAttrib ); #if ( ffconfigSHORTNAME_CASE != 0 ) FF_putChar( pucEntryBuffer, FF_FAT_CASE_OFFS, ( uint32_t ) lFitShort & ( FF_FAT_CASE_ATTR_BASE | FF_FAT_CASE_ATTR_EXT ) ); #endif FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint16_t ) ( pxDirEntry->ulObjectCluster >> 16 ) ); FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint16_t ) ( pxDirEntry->ulObjectCluster ) ); FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize ); xReturn = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xReturn ) ) { break; } xReturn = FF_PushEntryWithContext( pxIOManager, ( uint16_t ) ( lFreeEntry + xLFNCount ), &xFetchContext, pucEntryBuffer ); { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xReturn ) == pdFALSE ) { xReturn = xTempError; } } if( FF_isERR( xReturn ) ) { break; } #if ( ffconfigHASH_CACHE != 0 ) { if( FF_DirHashed( pxIOManager, ulDirCluster ) == pdFALSE ) { /* Hash the directory. */ FF_HashDir( pxIOManager, ulDirCluster ); } memcpy( pcShortName, pucEntryBuffer, 11 ); FF_ProcessShortName( pcShortName ); /* Format the shortname to 8.3. */ #if ( ffconfigHASH_FUNCTION == CRC16 ) { FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) ); } #elif ( ffconfigHASH_FUNCTION == CRC8 ) { FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) ); } #endif /* ffconfigHASH_FUNCTION */ } #endif /* ffconfigHASH_CACHE*/ } } while( pdFALSE ); FF_UnlockDirectory( pxIOManager ); if( FF_isERR( xReturn ) == pdFALSE ) { if( pxDirEntry != NULL ) { pxDirEntry->usCurrentItem = ( uint16_t ) ( lFreeEntry + xLFNCount ); } } return xReturn; } /* FF_CreateDirent() */ /*-----------------------------------------------------------*/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) uint32_t FF_CreateFile( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams, FF_T_WCHAR * pcFileName, FF_DirEnt_t * pxDirEntry, FF_Error_t * pxError ) #else uint32_t FF_CreateFile( FF_IOManager_t * pxIOManager, FF_FindParams_t * pxFindParams, char * pcFileName, FF_DirEnt_t * pxDirEntry, FF_Error_t * pxError ) #endif /* if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) */ { FF_DirEnt_t xMyFile; FF_Error_t xTempError, xError = FF_ERR_NONE; uint32_t ulResult; memset( &xMyFile, '\0', sizeof( xMyFile ) ); #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { wcsncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME ); } #else { strncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME ); } #endif xMyFile.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_CreateDirent( pxIOManager, pxFindParams, &xMyFile ); if( FF_isERR( xError ) == pdFALSE ) { /* The new file now has a cluster chain and it has an entry * in its directory. Copy data to a pointer provided by caller: */ if( pxDirEntry != NULL ) { memcpy( pxDirEntry, &xMyFile, sizeof( FF_DirEnt_t ) ); } } else { /* An error occurred in FF_CreateDirent(). * Unlink the file's cluster chain: */ FF_LockFAT( pxIOManager ); { FF_UnlinkClusterChain( pxIOManager, xMyFile.ulObjectCluster, 0 ); xMyFile.ulObjectCluster = 0ul; } FF_UnlockFAT( pxIOManager ); } /* Now flush all buffers to disk. */ xTempError = FF_FlushCache( pxIOManager ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } *pxError = xError; if( FF_isERR( xError ) == pdFALSE ) { ulResult = xMyFile.ulObjectCluster; } else { ulResult = 0; } return ulResult; } /* FF_CreateFile() */ /*-----------------------------------------------------------*/ /** * @brief Creates a Directory of the specified path. * * @param pxIOManager Pointer to the FF_IOManager_t object. * @param pcPath Path of the directory to create. * * @Return FF_ERR_NULL_POINTER if pxIOManager was NULL. * @Return FF_ERR_DIR_OBJECT_EXISTS if the object specified by path already exists. * @Return FF_ERR_DIR_INVALID_PATH * @Return FF_ERR_NONE on success. **/ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_Error_t FF_MkDir( FF_IOManager_t * pxIOManager, const FF_T_WCHAR * pcPath ) #else FF_Error_t FF_MkDir( FF_IOManager_t * pxIOManager, const char * pcPath ) #endif { FF_DirEnt_t xMyDirectory; #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) const FF_T_WCHAR * pcDirName; #else const char * pcDirName; #endif uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; uint32_t ulObjectCluster; BaseType_t xIndex; FF_Error_t xError = FF_ERR_NONE; FF_FindParams_t xFindParams; memset( &xFindParams, '\0', sizeof( xFindParams ) ); /* Inform the functions that the entry will be created if not found */ xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG; /* Open a do {} while ( pdFALSE ) loop */ do { if( pxIOManager == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_MKDIR ); break; } #if ( ffconfigREMOVABLE_MEDIA != 0 ) if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 ) { xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_MKDIR ); break; } #endif /* ffconfigREMOVABLE_MEDIA */ #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { xIndex = ( BaseType_t ) wcslen( pcPath ); } #else { xIndex = ( BaseType_t ) strlen( pcPath ); } #endif /* Find the last slash in the path. */ while( xIndex != 0 ) { if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) ) { break; } xIndex--; } pcDirName = pcPath + xIndex + 1; if( xIndex == 0 ) { xIndex = 1; } if( pcDirName[ 0 ] == '\0' ) { xError = ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR ); break; } xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError ); if( FF_isERR( xError ) ) { break; } if( xFindParams.ulDirCluster == 0UL ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_MKDIR ); break; } memset( &xMyDirectory, '\0', sizeof( xMyDirectory ) ); /* Will set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK */ FF_CreateShortName( &xFindParams, pcDirName ); if( FF_FindEntryInDir( pxIOManager, &xFindParams, pcDirName, 0x00, &xMyDirectory, &xError ) ) { if( FF_isERR( xError ) == pdFALSE ) { xError = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR ); } break; } if( ( FF_isERR( xError ) ) && ( FF_GETERROR( xError ) != FF_ERR_DIR_END_OF_DIR ) ) { break; } #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) { wcsncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME ); } #else { strncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME ); } #endif xMyDirectory.ulFileSize = 0; xMyDirectory.ucAttrib = FF_FAT_ATTR_DIR; xMyDirectory.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError ); /* Give all entries a proper time stamp, looks nicer than 1 Jan 1970 */ #if ( ffconfigTIME_SUPPORT != 0 ) { FF_GetSystemTime( &xMyDirectory.xCreateTime ); FF_GetSystemTime( &xMyDirectory.xModifiedTime ); } #endif if( FF_isERR( xError ) ) { break; } if( xMyDirectory.ulObjectCluster == 0UL ) { /* Couldn't allocate any space for the dir! */ xError = ( FF_Error_t ) ( FF_ERR_DIR_EXTEND_FAILED | FF_MKDIR ); break; } xError = FF_ClearCluster( pxIOManager, xMyDirectory.ulObjectCluster ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyDirectory ); } if( FF_isERR( xError ) ) { FF_LockFAT( pxIOManager ); { FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 ); } FF_UnlockFAT( pxIOManager ); FF_FlushCache( pxIOManager ); /* Don't override error. */ break; } /* Write 8.3 entry "." */ pucEntryBuffer[ 0 ] = '.'; /* folowed by 10 spaces: */ memset( pucEntryBuffer + 1, ' ', 10 ); /* Clear the rest of the structure. */ memset( pucEntryBuffer + 11, 0, FF_SIZEOF_DIRECTORY_ENTRY - 11 ); ulObjectCluster = xMyDirectory.ulObjectCluster; xError = FF_PutEntry( pxIOManager, ( uint16_t ) 0u, ulObjectCluster, &xMyDirectory, pucEntryBuffer ); if( FF_isERR( xError ) == pdFALSE ) { pucEntryBuffer[ 1 ] = '.'; if( xFindParams.ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) { xMyDirectory.ulObjectCluster = 0; } else { xMyDirectory.ulObjectCluster = xFindParams.ulDirCluster; } xError = FF_PutEntry( pxIOManager, 1u, ulObjectCluster, &xMyDirectory, pucEntryBuffer ); xMyDirectory.ulObjectCluster = ulObjectCluster; } if( FF_isERR( xError ) ) { FF_LockFAT( pxIOManager ); { FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 ); } FF_UnlockFAT( pxIOManager ); } FF_FlushCache( pxIOManager ); } while( pdFALSE ); return xError; } /* FF_MkDir() */ /*-----------------------------------------------------------*/ FF_Error_t FF_RmLFNs( FF_IOManager_t * pxIOManager, uint16_t usDirEntry, FF_FetchContext_t * pxContext ) { FF_Error_t xError = FF_ERR_NONE; uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ]; if( usDirEntry != 0 ) { usDirEntry--; do { xError = FF_FetchEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } if( FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN ) { FF_putChar( pucEntryBuffer, ( uint16_t ) 0, ( uint8_t ) FF_FAT_DELETED ); xError = FF_PushEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer ); if( FF_isERR( xError ) ) { break; } } if( usDirEntry == 0 ) { break; } usDirEntry--; } while( FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN ); } return xError; } /* FF_RmLFNs() */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) FF_Error_t FF_HashDir( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster ) { /* Find most suitable Hash Table to replace! */ BaseType_t xIndex; FF_HashTable_t * pxHashCache = NULL; FF_FetchContext_t xFetchContext; const uint8_t * pucEntryBuffer = NULL; uint8_t ucAttrib; uint32_t ulHash; FF_Error_t xError; for( xIndex = 0; xIndex < ffconfigHASH_CACHE_DEPTH; xIndex++ ) { if( pxIOManager->xHashCache[ xIndex ].ulNumHandles == 0 ) { if( pxHashCache == NULL ) { pxHashCache = &pxIOManager->xHashCache[ xIndex ]; } else { if( ( pxIOManager->xHashCache[ xIndex ].ulMisses > pxHashCache->ulMisses ) ) { pxHashCache = &pxIOManager->xHashCache[ xIndex ]; } } } } if( pxHashCache != NULL ) { #if ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) FF_T_WCHAR pcMyShortName[ 13 ]; #else char pcMyShortName[ 13 ]; #endif /* Clear the hash table! */ memset( pxHashCache, '\0', sizeof( *pxHashCache ) ); pxHashCache->ulDirCluster = ulDirCluster; pxHashCache->ulMisses = 0; /* Hash the directory! */ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) { if( ( pucEntryBuffer == NULL ) || ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( pxIOManager->usSectorSize - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) { xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL ); if( FF_isERR( xError ) ) { break; } pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer; } else { /* Advance the pointer 32 bytes to the next directory entry. */ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY; } if( FF_isDeleted( pucEntryBuffer ) == pdFALSE ) { ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB ); if( ( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) && ( ( ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) ) { memcpy( pcMyShortName, pucEntryBuffer, 11 ); FF_ProcessShortName( pcMyShortName ); if( FF_isEndOfDir( pucEntryBuffer ) ) { break; } /* Generate the Hash. */ #if ( ffconfigHASH_FUNCTION == CRC16 ) { ulHash = FF_GetCRC16( ( uint8_t * ) pcMyShortName, strlen( pcMyShortName ) ); } #else /* ffconfigHASH_FUNCTION == CRC8 */ { ulHash = FF_GetCRC8( pcMyShortName, strlen( pcMyShortName ) ); } #endif FF_SetHash( pxHashCache, ulHash ); } } } /* for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */ { FF_Error_t xTempError; xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } } } /* if( pxHashCache != NULL ) */ else { xError = -1; } return xError; } /* FF_HashDir() */ #endif /* ffconfigHASH_CACHE != 0 */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) /* FF_UnHashDir() : invalidate the hash tables of a given directory. * It is called when a file or sub-directory is removed or when the * directory itself is removed. */ void FF_UnHashDir( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster ) { FF_HashTable_t * pxHash = pxIOManager->xHashCache; FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH; for( ; pxHash < pxLast; pxHash++ ) { if( pxHash->ulDirCluster == ulDirCluster ) { pxHash->ulDirCluster = 0; break; } } } /* FF_UnHashDir() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) /** * * **/ void FF_SetHash( FF_HashTable_t * pxHash, uint32_t ulHash ) { uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT; uint32_t tblBit = ulHash % 32; pxHash->ulBitTable[ tblIndex ] |= ( 0x80000000ul >> tblBit ); } /* FF_SetHash() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) void FF_ClearHash( FF_HashTable_t * pxHash, uint32_t ulHash ) { if( pxHash != NULL ) { uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT; uint32_t tblBit = ulHash % 32; pxHash->ulBitTable[ tblIndex ] &= ~( 0x80000000ul >> tblBit ); } } /* FF_ClearHash() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) BaseType_t FF_isHashSet( FF_HashTable_t * pxHash, uint32_t ulHash ) { FF_Error_t xResult; xResult = pdFALSE; if( pxHash != NULL ) { uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT; uint32_t tblBit = ulHash % 32; if( pxHash->ulBitTable[ tblIndex ] & ( 0x80000000ul >> tblBit ) ) { xResult = pdTRUE; } } return xResult; } /* FF_isHashSet() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) void FF_AddDirentHash( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, uint32_t ulHash ) { FF_HashTable_t * pxHash = pxIOManager->xHashCache; FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH; for( ; pxHash < pxLast; pxHash++ ) { if( pxHash->ulDirCluster == ulDirCluster ) { FF_SetHash( pxHash, ulHash ); break; } } } /* FF_AddDirentHash() */ #endif /* ffconfigHASH_CACHE*/ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) BaseType_t FF_CheckDirentHash( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster, uint32_t ulHash ) { FF_HashTable_t * pxHash = pxIOManager->xHashCache; FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH; BaseType_t xResult; for( ; ; ) { if( pxHash->ulDirCluster == ulDirCluster ) { xResult = FF_isHashSet( pxHash, ulHash ); break; } pxHash++; if( pxHash >= pxLast ) { xResult = -1; break; } } return xResult; } /* FF_CheckDirentHash() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/ #if ( ffconfigHASH_CACHE != 0 ) BaseType_t FF_DirHashed( FF_IOManager_t * pxIOManager, uint32_t ulDirCluster ) { FF_HashTable_t * pxHash = pxIOManager->xHashCache; FF_HashTable_t * pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH; BaseType_t xResult; for( ; ; ) { if( pxHash->ulDirCluster == ulDirCluster ) { xResult = pdTRUE; break; } pxHash++; if( pxHash >= pxLast ) { xResult = pdFALSE; break; } } return xResult; } /* FF_DirHashed() */ #endif /* ffconfigHASH_CACHE */ /*-----------------------------------------------------------*/