mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-21 05:52:09 -04:00
537 lines
16 KiB
C
537 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)
|
|
{
|
|
(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
|
|
|
|
|
|
#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
|
|
|
|
|
|
/** @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_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;
|
|
}
|
|
|