mirror of
https://github.com/FreeRTOS/FreeRTOS.git
synced 2025-08-24 10:52:02 +08:00

* Use new version of CI-CD Actions, checkout@v3 instead of checkout@v2 on all jobs * Use cSpell spell check, and use ubuntu-20.04 for formatting check * Add in bot formatting action * Update freertos_demo.yml and freertos_plus_demo.yml files to increase github log readability * Add in a Qemu demo onto the workflows.
543 lines
16 KiB
C
543 lines
16 KiB
C
/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
|
|
*
|
|
* Copyright (c) 2014-2015 Datalight, Inc.
|
|
* All Rights Reserved Worldwide.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; use version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/* Businesses and individuals that for commercial or other reasons cannot
|
|
* comply with the terms of the GPLv2 license may obtain a commercial license
|
|
* before incorporating Reliance Edge into proprietary software for
|
|
* distribution in any form. Visit http://www.datalight.com/reliance-edge for
|
|
* more information.
|
|
*/
|
|
|
|
/** @file
|
|
* @brief Implements core volume operations.
|
|
*/
|
|
#include <redfs.h>
|
|
#include <redcore.h>
|
|
|
|
|
|
static bool MetarootIsValid( METAROOT * pMR,
|
|
bool * pfSectorCRCIsValid );
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
static void MetaRootEndianSwap( METAROOT * pMetaRoot );
|
|
#endif
|
|
|
|
|
|
/** @brief Mount a file system volume.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO Volume not formatted, improperly formatted, or corrupt.
|
|
*/
|
|
REDSTATUS RedVolMount( void )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
ret = RedOsBDevOpen( gbRedVolNum, BDEV_O_RDWR );
|
|
#else
|
|
ret = RedOsBDevOpen( gbRedVolNum, BDEV_O_RDONLY );
|
|
#endif
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedVolMountMaster();
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedVolMountMetaroot();
|
|
}
|
|
|
|
if( ret != 0 )
|
|
{
|
|
/* If we fail to mount, invalidate the buffers to prevent any
|
|
* confusion that could be caused by stale or corrupt metadata.
|
|
*/
|
|
( void ) RedBufferDiscardRange( 0U, gpRedVolume->ulBlockCount );
|
|
( void ) RedOsBDevClose( gbRedVolNum );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Mount the master block.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO Master block missing, corrupt, or inconsistent with the
|
|
* compile-time driver settings.
|
|
*/
|
|
REDSTATUS RedVolMountMaster( void )
|
|
{
|
|
REDSTATUS ret;
|
|
MASTERBLOCK * pMB;
|
|
|
|
/* Read the master block, to ensure that the disk was formatted with
|
|
* Reliance Edge.
|
|
*/
|
|
ret = RedBufferGet( BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR( &pMB ) );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* Verify that the driver was compiled with the same settings that
|
|
* the disk was formatted with. If not, the user has made a
|
|
* mistake: either the driver settings are wrong, or the disk needs
|
|
* to be reformatted.
|
|
*/
|
|
if( ( pMB->ulVersion != RED_DISK_LAYOUT_VERSION ) ||
|
|
( pMB->ulInodeCount != gpRedVolConf->ulInodeCount ) ||
|
|
( pMB->ulBlockCount != gpRedVolume->ulBlockCount ) ||
|
|
( pMB->uMaxNameLen != REDCONF_NAME_MAX ) ||
|
|
( pMB->uDirectPointers != REDCONF_DIRECT_POINTERS ) ||
|
|
( pMB->uIndirectPointers != REDCONF_INDIRECT_POINTERS ) ||
|
|
( pMB->bBlockSizeP2 != BLOCK_SIZE_P2 ) ||
|
|
( ( ( pMB->bFlags & MBFLAG_API_POSIX ) != 0U ) != ( REDCONF_API_POSIX == 1 ) ) ||
|
|
( ( ( pMB->bFlags & MBFLAG_INODE_TIMESTAMPS ) != 0U ) != ( REDCONF_INODE_TIMESTAMPS == 1 ) ) ||
|
|
( ( ( pMB->bFlags & MBFLAG_INODE_BLOCKS ) != 0U ) != ( REDCONF_INODE_BLOCKS == 1 ) ) )
|
|
{
|
|
ret = -RED_EIO;
|
|
}
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
else if( ( ( pMB->bFlags & MBFLAG_INODE_NLINK ) != 0U ) != ( REDCONF_API_POSIX_LINK == 1 ) )
|
|
{
|
|
ret = -RED_EIO;
|
|
}
|
|
#else
|
|
else if( ( pMB->bFlags & MBFLAG_INODE_NLINK ) != 0U )
|
|
{
|
|
ret = -RED_EIO;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/* Master block configuration is valid.
|
|
*
|
|
* Save the sequence number of the master block in the volume,
|
|
* since we need it later (see RedVolMountMetaroot()) and we do
|
|
* not want to re-buffer the master block.
|
|
*/
|
|
gpRedVolume->ullSequence = pMB->hdr.ullSequence;
|
|
}
|
|
|
|
RedBufferPut( pMB );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Mount the latest metaroot.
|
|
*
|
|
* This function also populates the volume contexts.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO Both metaroots are missing or corrupt.
|
|
*/
|
|
REDSTATUS RedVolMountMetaroot( void )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
ret = RedIoRead( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[ 0U ] );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedIoRead( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[ 1U ] );
|
|
}
|
|
|
|
/* Determine which metaroot is the most recent copy that was written
|
|
* completely.
|
|
*/
|
|
if( ret == 0 )
|
|
{
|
|
uint8_t bMR = UINT8_MAX;
|
|
bool fSectorCRCIsValid;
|
|
|
|
if( MetarootIsValid( &gpRedCoreVol->aMR[ 0U ], &fSectorCRCIsValid ) )
|
|
{
|
|
bMR = 0U;
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
MetaRootEndianSwap( &gpRedCoreVol->aMR[ 0U ] );
|
|
#endif
|
|
}
|
|
else if( gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid )
|
|
{
|
|
ret = -RED_EIO;
|
|
}
|
|
else
|
|
{
|
|
/* Metaroot is not valid, so it is ignored and there's nothing
|
|
* to do here.
|
|
*/
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( MetarootIsValid( &gpRedCoreVol->aMR[ 1U ], &fSectorCRCIsValid ) )
|
|
{
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
MetaRootEndianSwap( &gpRedCoreVol->aMR[ 1U ] );
|
|
#endif
|
|
|
|
if( ( bMR != 0U ) || ( gpRedCoreVol->aMR[ 1U ].hdr.ullSequence > gpRedCoreVol->aMR[ 0U ].hdr.ullSequence ) )
|
|
{
|
|
bMR = 1U;
|
|
}
|
|
}
|
|
else if( gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid )
|
|
{
|
|
ret = -RED_EIO;
|
|
}
|
|
else
|
|
{
|
|
/* Metaroot is not valid, so it is ignored and there's nothing
|
|
* to do here.
|
|
*/
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( bMR == UINT8_MAX )
|
|
{
|
|
/* Neither metaroot was valid.
|
|
*/
|
|
ret = -RED_EIO;
|
|
}
|
|
else
|
|
{
|
|
gpRedCoreVol->bCurMR = bMR;
|
|
gpRedMR = &gpRedCoreVol->aMR[ bMR ];
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* Normally the metaroot contains the highest sequence number, but the
|
|
* master block is the last block written during format, so on a
|
|
* freshly formatted volume the master block sequence number (stored in
|
|
* gpRedVolume->ullSequence) will be higher than that in the metaroot.
|
|
*/
|
|
if( gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence )
|
|
{
|
|
gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence;
|
|
}
|
|
|
|
/* gpRedVolume->ullSequence stores the *next* sequence number; to avoid
|
|
* giving the next node written to disk the same sequence number as the
|
|
* metaroot, increment it here.
|
|
*/
|
|
ret = RedVolSeqNumIncrement();
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
gpRedVolume->fMounted = true;
|
|
#if REDCONF_READ_ONLY == 0
|
|
gpRedVolume->fReadOnly = false;
|
|
#endif
|
|
|
|
#if RESERVED_BLOCKS > 0U
|
|
gpRedCoreVol->fUseReservedBlocks = false;
|
|
#endif
|
|
gpRedCoreVol->ulAlmostFreeBlocks = 0U;
|
|
|
|
gpRedCoreVol->aMR[ 1U - gpRedCoreVol->bCurMR ] = *gpRedMR;
|
|
gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR;
|
|
gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Determine whether the metaroot is valid.
|
|
*
|
|
* @param pMR The metaroot buffer.
|
|
* @param pfSectorCRCIsValid Populated with whether the first sector of the
|
|
* metaroot buffer is valid.
|
|
*
|
|
* @return Whether the metaroot is valid.
|
|
*
|
|
* @retval true The metaroot buffer is valid.
|
|
* @retval false The metaroot buffer is invalid.
|
|
*/
|
|
static bool MetarootIsValid( METAROOT * pMR,
|
|
bool * pfSectorCRCIsValid )
|
|
{
|
|
bool fRet = false;
|
|
|
|
if( pfSectorCRCIsValid == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else if( pMR == NULL )
|
|
{
|
|
REDERROR();
|
|
*pfSectorCRCIsValid = false;
|
|
}
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
else if( RedRev32( pMR->hdr.ulSignature ) != META_SIG_METAROOT )
|
|
#else
|
|
else if( pMR->hdr.ulSignature != META_SIG_METAROOT )
|
|
#endif
|
|
{
|
|
*pfSectorCRCIsValid = false;
|
|
}
|
|
else
|
|
{
|
|
const uint8_t * pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pMR );
|
|
uint32_t ulSectorCRC = pMR->ulSectorCRC;
|
|
uint32_t ulCRC;
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
ulSectorCRC = RedRev32( ulSectorCRC );
|
|
#endif
|
|
|
|
/* The sector CRC was zero when the CRC was computed during the
|
|
* transaction, so it must be zero here.
|
|
*/
|
|
pMR->ulSectorCRC = 0U;
|
|
|
|
ulCRC = RedCrc32Update( 0U, &pbMR[ 8U ], gpRedVolConf->ulSectorSize - 8U );
|
|
|
|
fRet = ulCRC == ulSectorCRC;
|
|
*pfSectorCRCIsValid = fRet;
|
|
|
|
if( fRet )
|
|
{
|
|
if( gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE )
|
|
{
|
|
ulCRC = RedCrc32Update( ulCRC, &pbMR[ gpRedVolConf->ulSectorSize ], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize );
|
|
}
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
ulCRC = RedRev32( ulCRC );
|
|
#endif
|
|
|
|
fRet = ulCRC == pMR->hdr.ulCRC;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Commit a transaction point.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedVolTransact( void )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
REDASSERT( !gpRedVolume->fReadOnly ); /* Should be checked by caller. */
|
|
|
|
if( gpRedCoreVol->fBranched )
|
|
{
|
|
gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks;
|
|
gpRedCoreVol->ulAlmostFreeBlocks = 0U;
|
|
|
|
ret = RedBufferFlush( 0U, gpRedVolume->ulBlockCount );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
gpRedMR->hdr.ulSignature = META_SIG_METAROOT;
|
|
gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence;
|
|
|
|
ret = RedVolSeqNumIncrement();
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
const uint8_t * pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR( gpRedMR );
|
|
uint32_t ulSectorCRC;
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
MetaRootEndianSwap( gpRedMR );
|
|
#endif
|
|
|
|
gpRedMR->ulSectorCRC = 0U;
|
|
|
|
ulSectorCRC = RedCrc32Update( 0U, &pbMR[ 8U ], gpRedVolConf->ulSectorSize - 8U );
|
|
|
|
if( gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE )
|
|
{
|
|
gpRedMR->hdr.ulCRC = RedCrc32Update( ulSectorCRC, &pbMR[ gpRedVolConf->ulSectorSize ], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize );
|
|
}
|
|
else
|
|
{
|
|
gpRedMR->hdr.ulCRC = ulSectorCRC;
|
|
}
|
|
|
|
gpRedMR->ulSectorCRC = ulSectorCRC;
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
gpRedMR->hdr.ulCRC = RedRev32( gpRedMR->hdr.ulCRC );
|
|
gpRedMR->ulSectorCRC = RedRev32( gpRedMR->ulSectorCRC );
|
|
#endif
|
|
|
|
/* Flush the block device before writing the metaroot, so that all
|
|
* previously written blocks are guaranteed to be on the media before
|
|
* the metaroot is written. Otherwise, if the block device reorders
|
|
* the writes, the metaroot could reach the media before metadata it
|
|
* points at, creating a window for disk corruption if power is lost.
|
|
*/
|
|
ret = RedIoFlush( gbRedVolNum );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedIoWrite( gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR );
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
MetaRootEndianSwap( gpRedMR );
|
|
#endif
|
|
}
|
|
|
|
/* Flush the block device to force the metaroot write to the media. This
|
|
* guarantees the transaction point is really complete before we return.
|
|
*/
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedIoFlush( gbRedVolNum );
|
|
}
|
|
|
|
/* Toggle to the other metaroot buffer. The working state and committed
|
|
* state metaroot buffers exchange places.
|
|
*/
|
|
if( ret == 0 )
|
|
{
|
|
uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR;
|
|
|
|
gpRedCoreVol->aMR[ bNextMR ] = *gpRedMR;
|
|
gpRedCoreVol->bCurMR = bNextMR;
|
|
|
|
gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
|
|
|
|
gpRedCoreVol->fBranched = false;
|
|
}
|
|
|
|
CRITICAL_ASSERT( ret == 0 );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* if REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
#ifdef REDCONF_ENDIAN_SWAP
|
|
static void MetaRootEndianSwap( METAROOT * pMetaRoot )
|
|
{
|
|
if( pMetaRoot == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else
|
|
{
|
|
pMetaRoot->ulSectorCRC = RedRev32( pMetaRoot->ulSectorCRC );
|
|
pMetaRoot->ulFreeBlocks = RedRev32( pMetaRoot->ulFreeBlocks );
|
|
#if REDCONF_API_POSIX == 1
|
|
pMetaRoot->ulFreeInodes = RedRev32( pMetaRoot->ulFreeInodes );
|
|
#endif
|
|
pMetaRoot->ulAllocNextBlock = RedRev32( pMetaRoot->ulAllocNextBlock );
|
|
}
|
|
}
|
|
#endif /* ifdef REDCONF_ENDIAN_SWAP */
|
|
|
|
|
|
/** @brief Process a critical file system error.
|
|
*
|
|
* @param pszFileName The file in which the error occurred.
|
|
* @param ulLineNum The line number at which the error occurred.
|
|
*/
|
|
void RedVolCriticalError( const char * pszFileName,
|
|
uint32_t ulLineNum )
|
|
{
|
|
#if REDCONF_OUTPUT == 1
|
|
#if REDCONF_READ_ONLY == 0
|
|
if( !gpRedVolume->fReadOnly )
|
|
{
|
|
RedOsOutputString( "Critical file system error in Reliance Edge, setting volume to READONLY\n" );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
RedOsOutputString( "Critical file system error in Reliance Edge (volume already READONLY)\n" );
|
|
}
|
|
#endif /* if REDCONF_OUTPUT == 1 */
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
gpRedVolume->fReadOnly = true;
|
|
#endif
|
|
|
|
#if REDCONF_ASSERTS == 1
|
|
RedOsAssertFail( pszFileName, ulLineNum );
|
|
#else
|
|
( void ) pszFileName;
|
|
( void ) ulLineNum;
|
|
#endif
|
|
}
|
|
|
|
|
|
/** @brief Increment the sequence number.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL Cannot increment sequence number: maximum value reached.
|
|
* This should not ever happen.
|
|
*/
|
|
REDSTATUS RedVolSeqNumIncrement( void )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( gpRedVolume->ullSequence == UINT64_MAX )
|
|
{
|
|
/* In practice this should never, ever happen; to get here, there would
|
|
* need to be UINT64_MAX disk writes, which would take eons: longer
|
|
* than the lifetime of any product or storage media. If this assert
|
|
* fires and the current year is still written with four digits,
|
|
* suspect memory corruption.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
else
|
|
{
|
|
gpRedVolume->ullSequence++;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|