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.
1128 lines
32 KiB
C
1128 lines
32 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 inode functions.
|
|
*/
|
|
#include <redfs.h>
|
|
#include <redcore.h>
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
static REDSTATUS InodeIsBranched( uint32_t ulInode,
|
|
bool * pfIsBranched );
|
|
#endif
|
|
#if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
|
|
static REDSTATUS InodeFindFree( uint32_t * pulInode );
|
|
#endif
|
|
#if REDCONF_READ_ONLY == 0
|
|
static REDSTATUS InodeGetWriteableCopy( uint32_t ulInode,
|
|
uint8_t * pbWhich );
|
|
#endif
|
|
static REDSTATUS InodeGetCurrentCopy( uint32_t ulInode,
|
|
uint8_t * pbWhich );
|
|
#if REDCONF_READ_ONLY == 0
|
|
static REDSTATUS InodeBitSet( uint32_t ulInode,
|
|
uint8_t bWhich,
|
|
bool fAllocated );
|
|
#endif
|
|
static uint32_t InodeBlock( uint32_t ulInode,
|
|
uint8_t bWhich );
|
|
|
|
|
|
/** @brief Mount an existing inode.
|
|
*
|
|
* Will populate all fields of the cached inode structure, except those which
|
|
* are populated during seek.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure. The
|
|
* pInode->ulInode field must already be initialized with the
|
|
* inode number to mount. All other fields will be discarded.
|
|
* @param type The expected inode type.
|
|
* @param fBranch Whether to branch the inode.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
* @retval -RED_EROFS @p fBranch is true but the driver is read-only.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EBADF The inode number is free; or the inode number is not
|
|
* valid.
|
|
* @retval -RED_EISDIR @p type is ::FTYPE_FILE and the inode is a directory.
|
|
* @retval -RED_ENOTDIR @p type is ::FTYPE_DIR and the inode is a file.
|
|
*/
|
|
REDSTATUS RedInodeMount( CINODE * pInode,
|
|
FTYPE type,
|
|
bool fBranch )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( pInode == NULL )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( !INODE_IS_VALID( pInode->ulInode ) )
|
|
{
|
|
ret = -RED_EBADF;
|
|
}
|
|
|
|
#if REDCONF_API_FSE == 1
|
|
else if( type == FTYPE_DIR )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
#endif
|
|
#if REDCONF_READ_ONLY == 1
|
|
else if( fBranch )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EROFS;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
uint32_t ulInode = pInode->ulInode;
|
|
uint8_t bWhich = 0U; /* Init'd to quiet warnings. */
|
|
|
|
RedMemSet( pInode, 0U, sizeof( *pInode ) );
|
|
pInode->ulInode = ulInode;
|
|
|
|
ret = InodeGetCurrentCopy( pInode->ulInode, &bWhich );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedBufferGet( InodeBlock( pInode->ulInode, bWhich ), BFLAG_META_INODE, CAST_VOID_PTR_PTR( &pInode->pInodeBuf ) );
|
|
}
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
if( ret == 0 )
|
|
{
|
|
ret = InodeIsBranched( pInode->ulInode, &pInode->fBranched );
|
|
}
|
|
#endif
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( RED_S_ISREG( pInode->pInodeBuf->uMode ) )
|
|
{
|
|
#if REDCONF_API_POSIX == 1
|
|
pInode->fDirectory = false;
|
|
|
|
if( type == FTYPE_DIR )
|
|
{
|
|
ret = -RED_ENOTDIR;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
else if( RED_S_ISDIR( pInode->pInodeBuf->uMode ) )
|
|
{
|
|
pInode->fDirectory = true;
|
|
|
|
if( type == FTYPE_FILE )
|
|
{
|
|
ret = -RED_EISDIR;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/* Missing or unsupported inode type.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
}
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
if( ( ret == 0 ) && fBranch )
|
|
{
|
|
ret = RedInodeBranch( pInode );
|
|
}
|
|
#endif
|
|
|
|
if( ret != 0 )
|
|
{
|
|
RedInodePut( pInode, 0U );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED )
|
|
|
|
/** @brief Create an inode.
|
|
*
|
|
* @param pInode Pointer to the cached inode structure. If pInode->ulInode
|
|
* is #INODE_INVALID, a free inode will be found; otherwise,
|
|
* pInode->ulInode will be the inode number (an error will be
|
|
* returned if it is not free).
|
|
* @param ulPInode The parent inode number.
|
|
* @param uMode The inode mode.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EBADF pInode->ulInode is an invalid inode number other than
|
|
#INODE_INVALID.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
* @retval -RED_EEXIST Tried to create an inode with an inode number that is
|
|
* already in use.
|
|
* @retval -RED_ENFILE All inode slots are already in use.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeCreate( CINODE * pInode,
|
|
uint32_t ulPInode,
|
|
uint16_t uMode )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
|
|
/* ulPInode must be a valid inode number, unless we are creating the root
|
|
* directory, in which case ulPInode must be INODE_INVALID (the root
|
|
* directory has no parent).
|
|
*/
|
|
if( ( pInode == NULL ) ||
|
|
( !INODE_IS_VALID( ulPInode ) && ( ( ulPInode != INODE_INVALID ) || ( pInode->ulInode != INODE_ROOTDIR ) ) ) )
|
|
#else
|
|
if( pInode == NULL )
|
|
#endif
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulInode = pInode->ulInode;
|
|
|
|
RedMemSet( pInode, 0U, sizeof( *pInode ) );
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
if( ulInode == INODE_INVALID )
|
|
{
|
|
/* Caller requested that an inode number be allocated. Search for
|
|
* an unused inode number, error if there isn't one.
|
|
*/
|
|
ret = InodeFindFree( &pInode->ulInode );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Caller requested creation of a specific inode number. Make sure
|
|
* it's valid and doesn't already exist.
|
|
*/
|
|
if( INODE_IS_VALID( ulInode ) )
|
|
{
|
|
bool fFree;
|
|
|
|
ret = RedInodeIsFree( ulInode, &fFree );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fFree )
|
|
{
|
|
pInode->ulInode = ulInode;
|
|
}
|
|
else
|
|
{
|
|
ret = -RED_EEXIST;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = -RED_EBADF;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
uint8_t bWriteableWhich;
|
|
|
|
ret = InodeGetWriteableCopy( pInode->ulInode, &bWriteableWhich );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedBufferGet( InodeBlock( pInode->ulInode, bWriteableWhich ),
|
|
( uint16_t ) ( ( uint32_t ) BFLAG_META_INODE | BFLAG_DIRTY | BFLAG_NEW ), CAST_VOID_PTR_PTR( &pInode->pInodeBuf ) );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* Mark the inode block as allocated.
|
|
*/
|
|
ret = InodeBitSet( pInode->ulInode, bWriteableWhich, true );
|
|
|
|
if( ret != 0 )
|
|
{
|
|
RedBufferPut( pInode->pInodeBuf );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
#if REDCONF_INODE_TIMESTAMPS == 1
|
|
uint32_t ulNow = RedOsClockGetTime();
|
|
|
|
pInode->pInodeBuf->ulATime = ulNow;
|
|
pInode->pInodeBuf->ulMTime = ulNow;
|
|
pInode->pInodeBuf->ulCTime = ulNow;
|
|
#endif
|
|
|
|
pInode->pInodeBuf->uMode = uMode;
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
#if REDCONF_API_POSIX_LINK == 1
|
|
pInode->pInodeBuf->uNLink = 1U;
|
|
#endif
|
|
pInode->pInodeBuf->ulPInode = ulPInode;
|
|
#else
|
|
( void ) ulPInode;
|
|
#endif
|
|
|
|
pInode->fBranched = true;
|
|
pInode->fDirty = true;
|
|
|
|
#if REDCONF_API_POSIX == 1
|
|
gpRedMR->ulFreeInodes--;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* (REDCONF_READ_ONLY == 0) && ((REDCONF_API_POSIX == 1) || FORMAT_SUPPORTED) */
|
|
|
|
|
|
#if DELETE_SUPPORTED
|
|
|
|
/** @brief Delete an inode.
|
|
*
|
|
* @param pInode Pointer to the cached inode structure.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EBADF The inode is free.
|
|
* @retval -RED_EINVAL @p pInode is `NULL`; or pInode->pBuffer is `NULL`.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeDelete( CINODE * pInode )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
if( pInode->pInodeBuf->ullSize != 0U )
|
|
{
|
|
ret = RedInodeDataTruncate( pInode, UINT64_SUFFIX( 0 ) );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedInodeFree( pInode );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Decrement an inode link count and delete the inode if the link count
|
|
* falls to zero.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p pInode is not a mounted cached inode.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeLinkDec( CINODE * pInode )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
|
|
#if REDCONF_API_POSIX_LINK == 1
|
|
else if( pInode->pInodeBuf->uNLink > 1U )
|
|
{
|
|
ret = RedInodeBranch( pInode );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
pInode->pInodeBuf->uNLink--;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
ret = RedInodeDelete( pInode );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* DELETE_SUPPORTED */
|
|
|
|
|
|
#if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
|
|
|
|
/** @brief Free an inode.
|
|
*
|
|
* @param pInode Pointer to the cached inode structure.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EBADF The inode is free.
|
|
* @retval -RED_EINVAL @p pInode is `NULL`; or pInode->pBuffer is `NULL`.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeFree( CINODE * pInode )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot0Allocated;
|
|
|
|
RedBufferDiscard( pInode->pInodeBuf );
|
|
pInode->pInodeBuf = NULL;
|
|
|
|
/* Determine which of the two slots for the inode is currently
|
|
* allocated, and free that slot.
|
|
*/
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, pInode->ulInode, 0U, &fSlot0Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
bool fSlot1Allocated;
|
|
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, pInode->ulInode, 1U, &fSlot1Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fSlot0Allocated )
|
|
{
|
|
if( fSlot1Allocated )
|
|
{
|
|
/* Both inode slots should never be allocated at
|
|
* the same time.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
else
|
|
{
|
|
ret = InodeBitSet( pInode->ulInode, 0U, false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !fSlot1Allocated )
|
|
{
|
|
/* The inode in unallocated, which should have been
|
|
* caught when it was mounted.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EBADF;
|
|
}
|
|
else
|
|
{
|
|
ret = InodeBitSet( pInode->ulInode, 1U, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pInode->ulInode = INODE_INVALID;
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( gpRedMR->ulFreeInodes >= gpRedVolConf->ulInodeCount )
|
|
{
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
else
|
|
{
|
|
gpRedMR->ulFreeInodes++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) */
|
|
|
|
|
|
/** @brief Put the cached inode structure.
|
|
*
|
|
* This puts all of the buffers in the ::CINODE structure. Also updates inode
|
|
* timestamp fields if requested.
|
|
*
|
|
* @param pInode The cached inode structure.
|
|
* @param bTimeFields The inode timestamp fields to update.
|
|
*/
|
|
void RedInodePut( CINODE * pInode,
|
|
uint8_t bTimeFields )
|
|
{
|
|
if( pInode == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else
|
|
{
|
|
RedInodePutCoord( pInode );
|
|
|
|
if( pInode->pInodeBuf != NULL )
|
|
{
|
|
#if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 )
|
|
if( ( bTimeFields & IPUT_UPDATE_MASK ) != 0U )
|
|
{
|
|
if( !pInode->fBranched || !pInode->fDirty )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulNow = RedOsClockGetTime();
|
|
|
|
#if REDCONF_ATIME == 1
|
|
if( ( bTimeFields & IPUT_UPDATE_ATIME ) != 0U )
|
|
{
|
|
pInode->pInodeBuf->ulATime = ulNow;
|
|
}
|
|
#endif
|
|
|
|
if( ( bTimeFields & IPUT_UPDATE_MTIME ) != 0U )
|
|
{
|
|
pInode->pInodeBuf->ulMTime = ulNow;
|
|
}
|
|
|
|
if( ( bTimeFields & IPUT_UPDATE_CTIME ) != 0U )
|
|
{
|
|
pInode->pInodeBuf->ulCTime = ulNow;
|
|
}
|
|
}
|
|
}
|
|
#else /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 ) */
|
|
( void ) bTimeFields;
|
|
#endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_INODE_TIMESTAMPS == 1 ) */
|
|
|
|
RedBufferPut( pInode->pInodeBuf );
|
|
pInode->pInodeBuf = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** @brief Put all buffers in the cached inode structure except for the inode
|
|
* node buffer.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
*/
|
|
void RedInodePutCoord( CINODE * pInode )
|
|
{
|
|
if( pInode == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else
|
|
{
|
|
RedInodePutData( pInode );
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
RedInodePutIndir( pInode );
|
|
#endif
|
|
#if DINDIR_POINTERS > 0U
|
|
RedInodePutDindir( pInode );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
|
|
/** @brief Put the double indirect buffer.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
*/
|
|
void RedInodePutDindir( CINODE * pInode )
|
|
{
|
|
if( pInode == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else if( pInode->pDindir != NULL )
|
|
{
|
|
RedBufferPut( pInode->pDindir );
|
|
pInode->pDindir = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* No double indirect buffer, nothing to put.
|
|
*/
|
|
}
|
|
}
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
|
|
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
|
|
/** @brief Put the indirect buffer.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
*/
|
|
void RedInodePutIndir( CINODE * pInode )
|
|
{
|
|
if( pInode == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else if( pInode->pIndir != NULL )
|
|
{
|
|
RedBufferPut( pInode->pIndir );
|
|
pInode->pIndir = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* No indirect buffer, nothing to put.
|
|
*/
|
|
}
|
|
}
|
|
#endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
|
|
|
|
|
|
/** @brief Put the inode data buffer.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
*/
|
|
void RedInodePutData( CINODE * pInode )
|
|
{
|
|
if( pInode == NULL )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else if( pInode->pbData != NULL )
|
|
{
|
|
RedBufferPut( pInode->pbData );
|
|
pInode->pbData = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* No data buffer, nothing to put.
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Determine if an inode is branched.
|
|
*
|
|
* @param ulInode The inode number to examine.
|
|
* @param pfIsBranched On successful return, populated with whether the inode
|
|
* is branched.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p pInode is `NULL`; or @p ulInode is not a valid inode
|
|
* number.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
static REDSTATUS InodeIsBranched( uint32_t ulInode,
|
|
bool * pfIsBranched )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !INODE_IS_VALID( ulInode ) || ( pfIsBranched == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ALLOCSTATE state;
|
|
|
|
ret = RedImapBlockState( InodeBlock( ulInode, 0U ), &state );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( state == ALLOCSTATE_NEW )
|
|
{
|
|
*pfIsBranched = true;
|
|
}
|
|
else
|
|
{
|
|
ret = RedImapBlockState( InodeBlock( ulInode, 1U ), &state );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( state == ALLOCSTATE_NEW )
|
|
{
|
|
*pfIsBranched = true;
|
|
}
|
|
else
|
|
{
|
|
*pfIsBranched = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Branch an inode.
|
|
*
|
|
* A branched inode is one in which the allocation state for one copy is free
|
|
* or almost free, and the other copy is in the new state. The copy which is
|
|
* in the new state is the writeable copy, which is also buffered and dirty.
|
|
*
|
|
* @param pInode Pointer to the cached inode structure which has already been
|
|
* mounted.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeBranch( CINODE * pInode )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( !pInode->fBranched )
|
|
{
|
|
uint8_t bWhich;
|
|
|
|
ret = InodeGetWriteableCopy( pInode->ulInode, &bWhich );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
RedBufferBranch( pInode->pInodeBuf, InodeBlock( pInode->ulInode, bWhich ) );
|
|
pInode->fBranched = true;
|
|
pInode->fDirty = true;
|
|
}
|
|
|
|
/* Toggle the inode slots: the old slot block becomes almost free
|
|
* (still used by the committed state) and the new slot block becomes
|
|
* new.
|
|
*/
|
|
if( ret == 0 )
|
|
{
|
|
ret = InodeBitSet( pInode->ulInode, 1U - bWhich, false );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = InodeBitSet( pInode->ulInode, bWhich, true );
|
|
}
|
|
|
|
CRITICAL_ASSERT( ret == 0 );
|
|
}
|
|
else
|
|
{
|
|
RedBufferDirty( pInode->pInodeBuf );
|
|
pInode->fDirty = true;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
#if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
|
|
|
|
/** @brief Find a free inode number.
|
|
*
|
|
* @param pulInode On successful return, populated with a free inode number.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p pulInode is `NULL`.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENFILE No available inode numbers.
|
|
*/
|
|
static REDSTATUS InodeFindFree( uint32_t * pulInode )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( pulInode == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( gpRedMR->ulFreeInodes == 0U )
|
|
{
|
|
ret = -RED_ENFILE;
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulInode;
|
|
|
|
ret = 0;
|
|
|
|
for( ulInode = INODE_FIRST_FREE; ulInode < ( INODE_FIRST_VALID + gpRedVolConf->ulInodeCount ); ulInode++ )
|
|
{
|
|
bool fFree;
|
|
|
|
ret = RedInodeIsFree( ulInode, &fFree );
|
|
|
|
if( ( ret != 0 ) || fFree )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( ulInode < ( INODE_FIRST_VALID + gpRedVolConf->ulInodeCount ) )
|
|
{
|
|
*pulInode = ulInode;
|
|
}
|
|
else
|
|
{
|
|
/* If gpRedMR->ulFreeInodes > 0, we should have found an inode.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_ENFILE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) */
|
|
|
|
|
|
#if ( ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED ) ) || ( REDCONF_CHECKER == 1 )
|
|
|
|
/** @brief Determine whether an inode number is available.
|
|
*
|
|
* @param ulInode The node number to examine.
|
|
* @param pfFree On successful return, populated with whether the inode
|
|
* number is available (true) or in use (false).
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p pfFree is `NULL`; or @p ulInode is not a valid inode
|
|
* number.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeIsFree( uint32_t ulInode,
|
|
bool * pfFree )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( pfFree == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot0Allocated;
|
|
|
|
*pfFree = false;
|
|
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
|
|
|
|
if( ( ret == 0 ) && !fSlot0Allocated )
|
|
{
|
|
bool fSlot1Allocated;
|
|
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
|
|
|
|
if( ( ret == 0 ) && !fSlot1Allocated )
|
|
{
|
|
*pfFree = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* if ( ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED ) ) || ( REDCONF_CHECKER == 1 ) */
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Determine which copy of the inode is currently writeable.
|
|
*
|
|
* @param ulInode The inode number to examine.
|
|
* @param pbWhich On successful return, populated with which copy of the inode
|
|
* (either 0 or 1) is writeable.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode
|
|
* number.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
static REDSTATUS InodeGetWriteableCopy( uint32_t ulInode,
|
|
uint8_t * pbWhich )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( pbWhich == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot0Allocated;
|
|
|
|
/* The writeable inode slot is the one which is free in the committed
|
|
* state, so query the committed state metaroot.
|
|
*/
|
|
ret = RedInodeBitGet( 1U - gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( !fSlot0Allocated )
|
|
{
|
|
*pbWhich = 0U;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot1Allocated;
|
|
|
|
ret = RedInodeBitGet( 1U - gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( !fSlot1Allocated )
|
|
{
|
|
*pbWhich = 1U;
|
|
}
|
|
else
|
|
{
|
|
/* Both inode slots were allocated, which should never
|
|
* happen.
|
|
*/
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* if REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
/** @brief Determine which copy of the inode is current.
|
|
*
|
|
* @param ulInode The inode number to examine.
|
|
* @param pbWhich On successful return, populated with which copy of the inode
|
|
* (either 0 or 1) is current.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EBADF @p ulInode is an unallocated inode number.
|
|
* @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode
|
|
* number.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
static REDSTATUS InodeGetCurrentCopy( uint32_t ulInode,
|
|
uint8_t * pbWhich )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( pbWhich == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot0Allocated;
|
|
|
|
/* The current inode slot is the one which is allocated in the working
|
|
* state metaroot.
|
|
*/
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fSlot0Allocated )
|
|
{
|
|
*pbWhich = 0U;
|
|
}
|
|
else
|
|
{
|
|
bool fSlot1Allocated;
|
|
|
|
ret = RedInodeBitGet( gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fSlot1Allocated )
|
|
{
|
|
*pbWhich = 1U;
|
|
}
|
|
else
|
|
{
|
|
/* Neither slot for this inode was allocated, so the
|
|
* inode is actually free.
|
|
*/
|
|
ret = -RED_EBADF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Get whether a copy of an inode is allocated.
|
|
*
|
|
* @param bMR The metaroot index: either 0 or 1.
|
|
* @param ulInode The inode number.
|
|
* @param bWhich Which copy of the inode to get.
|
|
* @param pfAllocated On successful return, populated with whether the given
|
|
* copy of the inode is allocated.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p bMR is not 1 or 0; @p ulInode is not a valid inode
|
|
* number; or @p bWhich is not 1 or 0; or @p pfAllocated is
|
|
* `NULL`.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeBitGet( uint8_t bMR,
|
|
uint32_t ulInode,
|
|
uint8_t bWhich,
|
|
bool * pfAllocated )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !INODE_IS_VALID( ulInode ) || ( bWhich > 1U ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedImapBlockGet( bMR, InodeBlock( ulInode, bWhich ), pfAllocated );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Set whether a copy of an inode is allocated.
|
|
*
|
|
* @param ulInode The inode number.
|
|
* @param bWhich Which copy of the inode to set.
|
|
* @param fAllocated If true, the inode is set to allocated; if false, the
|
|
* inode is set to free.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EINVAL @p ulInode is not a valid inode number; or @p bWhich is
|
|
* not 1 or 0.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
static REDSTATUS InodeBitSet( uint32_t ulInode,
|
|
uint8_t bWhich,
|
|
bool fAllocated )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( !INODE_IS_VALID( ulInode ) || ( bWhich > 1U ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedImapBlockSet( InodeBlock( ulInode, bWhich ), fAllocated );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* if REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
/** @brief Determine the block number of an inode.
|
|
*
|
|
* @param ulInode The inode number.
|
|
* @param bWhich Which copy of the inode.
|
|
*
|
|
* @return The block number of the inode.
|
|
*/
|
|
static uint32_t InodeBlock( uint32_t ulInode,
|
|
uint8_t bWhich )
|
|
{
|
|
REDASSERT( INODE_IS_VALID( ulInode ) );
|
|
REDASSERT( bWhich <= 1U );
|
|
|
|
return gpRedCoreVol->ulInodeTableStartBN + ( ( ulInode - INODE_FIRST_VALID ) * 2U ) + bWhich;
|
|
}
|