MAX_CARLINK_A270S/MXC_A27-PCB4.5-270T/FreeRTOS-Plus/FreeRTOS-FAT/ff_file.c

3319 lines
110 KiB
C

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