mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-10-15 09:17:44 -04: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.
1936 lines
68 KiB
C
1936 lines
68 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 I/O functions.
|
|
*/
|
|
#include <redfs.h>
|
|
#include <redcore.h>
|
|
|
|
|
|
/* This value is used to initialize the uIndirEntry and uDindirEntry members of
|
|
* the CINODE structure. After seeking, a value of COORD_ENTRY_INVALID in
|
|
* uIndirEntry indicates that there is no indirect node in the path through the
|
|
* file metadata structure, and a value of COORD_ENTRY_INVALID in uDindirEntry
|
|
* indicates that there is no double indirect node.
|
|
*/
|
|
#define COORD_ENTRY_INVALID ( UINT16_MAX )
|
|
|
|
/* This enumeration is used by the BranchBlock() and BranchBlockCost()
|
|
* functions to determine which blocks of the file metadata structure need to
|
|
* be branched, and which to ignore. DINDIR requires requires branching the
|
|
* double indirect only, INDIR requires branching the double indirect
|
|
* (if present) and the indirect, and FILE_DATA requires branching the indirect
|
|
* and double indirect (if present) and the file data block.
|
|
*/
|
|
typedef enum
|
|
{
|
|
BRANCHDEPTH_DINDIR = 0U,
|
|
BRANCHDEPTH_INDIR = 1U,
|
|
BRANCHDEPTH_FILE_DATA = 2U,
|
|
BRANCHDEPTH_MAX = BRANCHDEPTH_FILE_DATA
|
|
} BRANCHDEPTH;
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
#if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
|
|
static REDSTATUS Shrink( CINODE * pInode,
|
|
uint64_t ullSize );
|
|
#if DINDIR_POINTERS > 0U
|
|
static REDSTATUS TruncDindir( CINODE * pInode,
|
|
bool * pfFreed );
|
|
#endif
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
static REDSTATUS TruncIndir( CINODE * pInode,
|
|
bool * pfFreed );
|
|
#endif
|
|
static REDSTATUS TruncDataBlock( const CINODE * pInode,
|
|
uint32_t * pulBlock,
|
|
bool fPropagate );
|
|
#endif /* if DELETE_SUPPORTED || TRUNCATE_SUPPORTED */
|
|
static REDSTATUS ExpandPrepare( CINODE * pInode );
|
|
#endif /* if REDCONF_READ_ONLY == 0 */
|
|
static void SeekCoord( CINODE * pInode,
|
|
uint32_t ulBlock );
|
|
static REDSTATUS ReadUnaligned( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t ulLen,
|
|
uint8_t * pbBuffer );
|
|
static REDSTATUS ReadAligned( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t ulBlockCount,
|
|
uint8_t * pbBuffer );
|
|
#if REDCONF_READ_ONLY == 0
|
|
static REDSTATUS WriteUnaligned( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t ulLen,
|
|
const uint8_t * pbBuffer );
|
|
static REDSTATUS WriteAligned( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t * pulBlockCount,
|
|
const uint8_t * pbBuffer );
|
|
#endif
|
|
static REDSTATUS GetExtent( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t * pulExtentStart,
|
|
uint32_t * pulExtentLen );
|
|
#if REDCONF_READ_ONLY == 0
|
|
static REDSTATUS BranchBlock( CINODE * pInode,
|
|
BRANCHDEPTH depth,
|
|
bool fBuffer );
|
|
static REDSTATUS BranchOneBlock( uint32_t * pulBlock,
|
|
void ** ppBuffer,
|
|
uint16_t uBFlag );
|
|
static REDSTATUS BranchBlockCost( const CINODE * pInode,
|
|
BRANCHDEPTH depth,
|
|
uint32_t * pulCost );
|
|
static uint32_t FreeBlockCount( void );
|
|
#endif /* if REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
/** @brief Read data from an inode.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure of the inode from
|
|
* which to read.
|
|
* @param ullStart The file offset at which to read.
|
|
* @param pulLen On input, the number of bytes to attempt to read. On
|
|
* successful return, populated with the number of bytes
|
|
* actually read.
|
|
* @param pBuffer The buffer to read into.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer; or
|
|
* @p pulLen is `NULL`; or @p pBuffer is `NULL`.
|
|
*/
|
|
REDSTATUS RedInodeDataRead( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t * pulLen,
|
|
void * pBuffer )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( pulLen == NULL ) || ( pBuffer == NULL ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( ullStart >= pInode->pInodeBuf->ullSize )
|
|
{
|
|
*pulLen = 0U;
|
|
}
|
|
else if( *pulLen == 0U )
|
|
{
|
|
/* Do nothing, just return success.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
|
|
uint32_t ulReadIndex = 0U;
|
|
uint32_t ulLen = *pulLen;
|
|
uint32_t ulRemaining;
|
|
|
|
/* Reading beyond the end of the file is not allowed. If the requested
|
|
* read extends beyond the end of the file, truncate the read length so
|
|
* that the read stops at the end of the file.
|
|
*/
|
|
if( ( pInode->pInodeBuf->ullSize - ullStart ) < ulLen )
|
|
{
|
|
ulLen = ( uint32_t ) ( pInode->pInodeBuf->ullSize - ullStart );
|
|
}
|
|
|
|
ulRemaining = ulLen;
|
|
|
|
/* Unaligned partial block at start.
|
|
*/
|
|
if( ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ) != 0U )
|
|
{
|
|
uint32_t ulBytesInFirstBlock = REDCONF_BLOCK_SIZE - ( uint32_t ) ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) );
|
|
uint32_t ulThisRead = REDMIN( ulRemaining, ulBytesInFirstBlock );
|
|
|
|
ret = ReadUnaligned( pInode, ullStart, ulThisRead, pbBuffer );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulReadIndex += ulThisRead;
|
|
ulRemaining -= ulThisRead;
|
|
}
|
|
}
|
|
|
|
/* Whole blocks.
|
|
*/
|
|
if( ( ret == 0 ) && ( ulRemaining >= REDCONF_BLOCK_SIZE ) )
|
|
{
|
|
uint32_t ulBlockOffset = ( uint32_t ) ( ( ullStart + ulReadIndex ) >> BLOCK_SIZE_P2 );
|
|
uint32_t ulBlockCount = ulRemaining >> BLOCK_SIZE_P2;
|
|
|
|
REDASSERT( ( ( ullStart + ulReadIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
|
|
|
|
ret = ReadAligned( pInode, ulBlockOffset, ulBlockCount, &pbBuffer[ ulReadIndex ] );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulReadIndex += ulBlockCount << BLOCK_SIZE_P2;
|
|
ulRemaining -= ulBlockCount << BLOCK_SIZE_P2;
|
|
}
|
|
}
|
|
|
|
/* Aligned partial block at end.
|
|
*/
|
|
if( ( ret == 0 ) && ( ulRemaining > 0U ) )
|
|
{
|
|
REDASSERT( ulRemaining < REDCONF_BLOCK_SIZE );
|
|
REDASSERT( ( ( ullStart + ulReadIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
|
|
|
|
ret = ReadUnaligned( pInode, ullStart + ulReadIndex, ulRemaining, &pbBuffer[ ulReadIndex ] );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pulLen = ulLen;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Write to an inode.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure of the inode into
|
|
* which to write.
|
|
* @param ullStart The file offset at which to write.
|
|
* @param pulLen On input, the number of bytes to attempt to write. On
|
|
* successful return, populated with the number of bytes
|
|
* actually written.
|
|
* @param pBuffer The buffer to write from.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EFBIG @p ullStart is greater than the maximum file size; or
|
|
* @p ullStart is equal to the maximum file size and the
|
|
* write length is non-zero.
|
|
* @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer; or
|
|
* @p pulLen is `NULL`; or @p pBuffer is `NULL`.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC No data can be written because there is insufficient
|
|
* free space.
|
|
*/
|
|
REDSTATUS RedInodeDataWrite( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t * pulLen,
|
|
const void * pBuffer )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_DIRTY( pInode ) || ( pulLen == NULL ) || ( pBuffer == NULL ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( ( ullStart > INODE_SIZE_MAX ) || ( ( ullStart == INODE_SIZE_MAX ) && ( *pulLen > 0U ) ) )
|
|
{
|
|
ret = -RED_EFBIG;
|
|
}
|
|
else if( *pulLen == 0U )
|
|
{
|
|
/* Do nothing, just return success.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
|
|
uint32_t ulWriteIndex = 0U;
|
|
uint32_t ulLen = *pulLen;
|
|
uint32_t ulRemaining;
|
|
|
|
if( ( INODE_SIZE_MAX - ullStart ) < ulLen )
|
|
{
|
|
ulLen = ( uint32_t ) ( INODE_SIZE_MAX - ullStart );
|
|
}
|
|
|
|
ulRemaining = ulLen;
|
|
|
|
/* If the write is beyond the current end of the file, and the current
|
|
* end of the file is not block-aligned, then there may be some data
|
|
* that needs to be zeroed in the last block.
|
|
*/
|
|
if( ullStart > pInode->pInodeBuf->ullSize )
|
|
{
|
|
ret = ExpandPrepare( pInode );
|
|
}
|
|
|
|
/* Partial block at start.
|
|
*/
|
|
if( ( ret == 0 ) && ( ( ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ) != 0U ) || ( ulRemaining < REDCONF_BLOCK_SIZE ) ) )
|
|
{
|
|
uint32_t ulBytesInFirstBlock = REDCONF_BLOCK_SIZE - ( uint32_t ) ( ullStart & ( REDCONF_BLOCK_SIZE - 1U ) );
|
|
uint32_t ulThisWrite = REDMIN( ulRemaining, ulBytesInFirstBlock );
|
|
|
|
ret = WriteUnaligned( pInode, ullStart, ulThisWrite, pbBuffer );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulWriteIndex += ulThisWrite;
|
|
ulRemaining -= ulThisWrite;
|
|
}
|
|
}
|
|
|
|
/* Whole blocks.
|
|
*/
|
|
if( ( ret == 0 ) && ( ulRemaining >= REDCONF_BLOCK_SIZE ) )
|
|
{
|
|
uint32_t ulBlockOffset = ( uint32_t ) ( ( ullStart + ulWriteIndex ) >> BLOCK_SIZE_P2 );
|
|
uint32_t ulBlockCount = ulRemaining >> BLOCK_SIZE_P2;
|
|
uint32_t ulBlocksWritten = ulBlockCount;
|
|
|
|
REDASSERT( ( ( ullStart + ulWriteIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
|
|
|
|
ret = WriteAligned( pInode, ulBlockOffset, &ulBlocksWritten, &pbBuffer[ ulWriteIndex ] );
|
|
|
|
if( ( ret == -RED_ENOSPC ) && ( ulWriteIndex > 0U ) )
|
|
{
|
|
ulBlocksWritten = 0U;
|
|
ret = 0;
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulWriteIndex += ulBlocksWritten << BLOCK_SIZE_P2;
|
|
ulRemaining -= ulBlocksWritten << BLOCK_SIZE_P2;
|
|
|
|
if( ulBlocksWritten < ulBlockCount )
|
|
{
|
|
ulRemaining = 0U;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Partial block at end.
|
|
*/
|
|
if( ( ret == 0 ) && ( ulRemaining > 0U ) )
|
|
{
|
|
REDASSERT( ulRemaining < REDCONF_BLOCK_SIZE );
|
|
REDASSERT( ( ( ullStart + ulWriteIndex ) & ( REDCONF_BLOCK_SIZE - 1U ) ) == 0U );
|
|
REDASSERT( ulWriteIndex > 0U );
|
|
|
|
ret = WriteUnaligned( pInode, ullStart + ulWriteIndex, ulRemaining, &pbBuffer[ ulWriteIndex ] );
|
|
|
|
if( ret == -RED_ENOSPC )
|
|
{
|
|
ret = 0;
|
|
}
|
|
else if( ret == 0 )
|
|
{
|
|
ulWriteIndex += ulRemaining;
|
|
|
|
REDASSERT( ulWriteIndex == ulLen );
|
|
}
|
|
else
|
|
{
|
|
/* Unexpected error, return it.
|
|
*/
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pulLen = ulWriteIndex;
|
|
|
|
if( ( ullStart + ulWriteIndex ) > pInode->pInodeBuf->ullSize )
|
|
{
|
|
pInode->pInodeBuf->ullSize = ullStart + ulWriteIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
|
|
|
|
/** @brief Change the size of an inode.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ullSize The new file size for the inode.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EFBIG @p ullSize is greater than the maximum file size.
|
|
* @retval -RED_EINVAL @p pInode is not a mounted cached inode pointer.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC Insufficient free space to perform the truncate.
|
|
*/
|
|
REDSTATUS RedInodeDataTruncate( CINODE * pInode,
|
|
uint64_t ullSize )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
/* The inode does not need to be dirtied when it is being deleted, because
|
|
* the inode buffer will be discarded without ever being written to disk.
|
|
* Thus, we only check to see if it's mounted here.
|
|
*/
|
|
if( !CINODE_IS_MOUNTED( pInode ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( ullSize > INODE_SIZE_MAX )
|
|
{
|
|
ret = -RED_EFBIG;
|
|
}
|
|
else
|
|
{
|
|
if( ullSize > pInode->pInodeBuf->ullSize )
|
|
{
|
|
ret = ExpandPrepare( pInode );
|
|
}
|
|
else if( ullSize < pInode->pInodeBuf->ullSize )
|
|
{
|
|
ret = Shrink( pInode, ullSize );
|
|
}
|
|
else
|
|
{
|
|
/* Size is staying the same, nothing to do.
|
|
*/
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
pInode->pInodeBuf->ullSize = ullSize;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Free all file data beyond a specified point.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ullSize The point beyond which to free all file data.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC Insufficient free space to perform the truncate.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS Shrink( CINODE * pInode,
|
|
uint64_t ullSize )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
/* pInode->fDirty is checked explicitly here, instead of using the
|
|
* CINODE_IS_DIRTY() macro, to avoid a duplicate mount check.
|
|
*/
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( ( ullSize > 0U ) && !pInode->fDirty ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulTruncBlock = ( uint32_t ) ( ( ullSize + REDCONF_BLOCK_SIZE - 1U ) >> BLOCK_SIZE_P2 );
|
|
|
|
RedInodePutData( pInode );
|
|
|
|
#if REDCONF_DIRECT_POINTERS > 0U
|
|
while( ulTruncBlock < REDCONF_DIRECT_POINTERS )
|
|
{
|
|
ret = TruncDataBlock( pInode, &pInode->pInodeBuf->aulEntries[ ulTruncBlock ], true );
|
|
|
|
if( ret != 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ulTruncBlock++;
|
|
}
|
|
#endif /* if REDCONF_DIRECT_POINTERS > 0U */
|
|
|
|
#if REDCONF_INDIRECT_POINTERS > 0U
|
|
while( ( ret == 0 ) && ( ulTruncBlock < ( REDCONF_DIRECT_POINTERS + INODE_INDIR_BLOCKS ) ) )
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ulTruncBlock );
|
|
|
|
if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
|
|
{
|
|
bool fFreed;
|
|
|
|
ret = TruncIndir( pInode, &fFreed );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fFreed )
|
|
{
|
|
pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = BLOCK_SPARSE;
|
|
}
|
|
|
|
/* The next seek will go to the beginning of the next
|
|
* indirect.
|
|
*/
|
|
ulTruncBlock += ( INDIR_ENTRIES - pInode->uIndirEntry );
|
|
}
|
|
}
|
|
}
|
|
#endif /* if REDCONF_INDIRECT_POINTERS > 0U */
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
while( ( ret == 0 ) && ( ulTruncBlock < INODE_DATA_BLOCKS ) )
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ulTruncBlock );
|
|
|
|
if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
|
|
{
|
|
bool fFreed;
|
|
|
|
/* TruncDindir() invokes seek as it goes along, which will
|
|
* update the entry values (possibly all three of these);
|
|
* make a copy so we can compute things correctly after.
|
|
*/
|
|
uint16_t uOrigInodeEntry = pInode->uInodeEntry;
|
|
uint16_t uOrigDindirEntry = pInode->uDindirEntry;
|
|
uint16_t uOrigIndirEntry = pInode->uIndirEntry;
|
|
|
|
ret = TruncDindir( pInode, &fFreed );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( fFreed )
|
|
{
|
|
pInode->pInodeBuf->aulEntries[ uOrigInodeEntry ] = BLOCK_SPARSE;
|
|
}
|
|
|
|
/* The next seek will go to the beginning of the next
|
|
* double indirect.
|
|
*/
|
|
ulTruncBlock += ( DINDIR_DATA_BLOCKS - ( uOrigDindirEntry * INDIR_ENTRIES ) ) - uOrigIndirEntry;
|
|
}
|
|
}
|
|
}
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
|
|
/** @brief Truncate a double indirect.
|
|
*
|
|
* @param pInode A pointer to the cached inode, whose coordinates indicate
|
|
* the truncation boundary.
|
|
* @param pfFreed On successful return, populated with whether the double
|
|
* indirect node was freed.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC Insufficient free space to perform the truncate.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS TruncDindir( CINODE * pInode,
|
|
bool * pfFreed )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( pfFreed == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( pInode->pDindir == NULL )
|
|
{
|
|
*pfFreed = false;
|
|
}
|
|
else
|
|
{
|
|
bool fBranch = false;
|
|
uint16_t uEntry;
|
|
|
|
/* The double indirect is definitely going to be branched (instead of
|
|
* deleted) if any of its indirect pointers which are entirely prior to
|
|
* the truncation boundary are non-sparse.
|
|
*/
|
|
for( uEntry = 0U; !fBranch && ( uEntry < pInode->uDindirEntry ); uEntry++ )
|
|
{
|
|
fBranch = pInode->pDindir->aulEntries[ uEntry ] != BLOCK_SPARSE;
|
|
}
|
|
|
|
/* Unless we already know for a fact that the double indirect is going
|
|
* to be branched, examine the contents of the indirect pointer which
|
|
* straddles the truncation boundary. If the indirect is going to be
|
|
* deleted, we know this indirect pointer is going away, and that might
|
|
* mean the double indirect is going to be deleted also.
|
|
*/
|
|
if( !fBranch && ( pInode->pDindir->aulEntries[ pInode->uDindirEntry ] != BLOCK_SPARSE ) )
|
|
{
|
|
for( uEntry = 0U; !fBranch && ( uEntry < pInode->uIndirEntry ); uEntry++ )
|
|
{
|
|
fBranch = pInode->pIndir->aulEntries[ uEntry ] != BLOCK_SPARSE;
|
|
}
|
|
}
|
|
|
|
if( fBranch )
|
|
{
|
|
ret = BranchBlock( pInode, BRANCHDEPTH_DINDIR, false );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
uint32_t ulBlock = pInode->ulLogicalBlock;
|
|
uint16_t uStart = pInode->uDindirEntry; /* pInode->uDindirEntry will change. */
|
|
|
|
for( uEntry = uStart; uEntry < INDIR_ENTRIES; uEntry++ )
|
|
{
|
|
/* Seek so that TruncIndir() has the correct indirect
|
|
* buffer and indirect entry.
|
|
*/
|
|
ret = RedInodeDataSeek( pInode, ulBlock );
|
|
|
|
if( ret == -RED_ENODATA )
|
|
{
|
|
ret = 0;
|
|
}
|
|
|
|
if( ( ret == 0 ) && ( pInode->ulIndirBlock != BLOCK_SPARSE ) )
|
|
{
|
|
bool fIndirFreed;
|
|
|
|
ret = TruncIndir( pInode, &fIndirFreed );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* All of the indirects after the one which straddles
|
|
* the truncation boundary should definitely end up
|
|
* deleted.
|
|
*/
|
|
REDASSERT( ( uEntry == uStart ) || fIndirFreed );
|
|
|
|
/* If the double indirect is being freed, all of the
|
|
* indirects should be freed too.
|
|
*/
|
|
REDASSERT( fIndirFreed || fBranch );
|
|
|
|
if( fBranch && fIndirFreed )
|
|
{
|
|
pInode->pDindir->aulEntries[ uEntry ] = BLOCK_SPARSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ret != 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ulBlock += ( INDIR_ENTRIES - pInode->uIndirEntry );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pfFreed = !fBranch;
|
|
|
|
if( !fBranch )
|
|
{
|
|
RedInodePutDindir( pInode );
|
|
|
|
ret = RedImapBlockSet( pInode->ulDindirBlock, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* DINDIR_POINTERS > 0U */
|
|
|
|
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
|
|
/** @brief Truncate a indirect.
|
|
*
|
|
* @param pInode A pointer to the cached inode, whose coordinates indicate
|
|
* the truncation boundary.
|
|
* @param pfFreed On successful return, populated with whether the indirect
|
|
* node was freed.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC Insufficient free space to perform the truncate.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS TruncIndir( CINODE * pInode,
|
|
bool * pfFreed )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( pfFreed == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( pInode->pIndir == NULL )
|
|
{
|
|
*pfFreed = false;
|
|
}
|
|
else
|
|
{
|
|
bool fBranch = false;
|
|
uint16_t uEntry;
|
|
|
|
/* Scan the range of entries which are not being truncated. If there
|
|
* is anything there, then the indirect will not be empty after the
|
|
* truncate, so it is branched and modified instead of deleted.
|
|
*/
|
|
for( uEntry = 0U; !fBranch && ( uEntry < pInode->uIndirEntry ); uEntry++ )
|
|
{
|
|
fBranch = pInode->pIndir->aulEntries[ uEntry ] != BLOCK_SPARSE;
|
|
}
|
|
|
|
if( fBranch )
|
|
{
|
|
ret = BranchBlock( pInode, BRANCHDEPTH_INDIR, false );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
for( uEntry = pInode->uIndirEntry; uEntry < INDIR_ENTRIES; uEntry++ )
|
|
{
|
|
ret = TruncDataBlock( pInode, &pInode->pIndir->aulEntries[ uEntry ], fBranch );
|
|
|
|
if( ret != 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pfFreed = !fBranch;
|
|
|
|
if( !fBranch )
|
|
{
|
|
RedInodePutIndir( pInode );
|
|
|
|
ret = RedImapBlockSet( pInode->ulIndirBlock, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
|
|
|
|
|
|
/** @brief Truncate a file data block.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param pulBlock On entry, contains the block to be truncated. On
|
|
* successful return, if @p fPropagate is true, populated
|
|
* with BLOCK_SPARSE, otherwise unmodified.
|
|
* @param fPropagate Whether the parent node is being branched.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS TruncDataBlock( const CINODE * pInode,
|
|
uint32_t * pulBlock,
|
|
bool fPropagate )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( pulBlock == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else if( *pulBlock != BLOCK_SPARSE )
|
|
{
|
|
ret = RedImapBlockSet( *pulBlock, false );
|
|
|
|
#if REDCONF_INODE_BLOCKS == 1
|
|
if( ret == 0 )
|
|
{
|
|
if( pInode->pInodeBuf->ulBlocks == 0U )
|
|
{
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
else
|
|
{
|
|
pInode->pInodeBuf->ulBlocks--;
|
|
}
|
|
}
|
|
#endif /* if REDCONF_INODE_BLOCKS == 1 */
|
|
|
|
if( ( ret == 0 ) && fPropagate )
|
|
{
|
|
*pulBlock = BLOCK_SPARSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Data block is sparse, nothing to truncate.
|
|
*/
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* DELETE_SUPPORTED || TRUNCATE_SUPPORTED */
|
|
|
|
|
|
/** @brief Prepare to increase the file size.
|
|
*
|
|
* When the inode size is increased, a sparse region is created. It is
|
|
* possible that a prior shrink operation to an unaligned size left stale data
|
|
* beyond the end of the file in the last data block. That data is not zeroed
|
|
* while shrinking the inode in order to transfer the disk full burden from the
|
|
* shrink operation to the expand operation.
|
|
*
|
|
* @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_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC Insufficient free space to perform the truncate.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS ExpandPrepare( CINODE * pInode )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_DIRTY( pInode ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulOldSizeByteInBlock = ( uint32_t ) ( pInode->pInodeBuf->ullSize & ( REDCONF_BLOCK_SIZE - 1U ) );
|
|
|
|
if( ulOldSizeByteInBlock != 0U )
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ( uint32_t ) ( pInode->pInodeBuf->ullSize >> BLOCK_SIZE_P2 ) );
|
|
|
|
if( ret == -RED_ENODATA )
|
|
{
|
|
ret = 0;
|
|
}
|
|
else if( ret == 0 )
|
|
{
|
|
ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, true );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
RedMemSet( &pInode->pbData[ ulOldSizeByteInBlock ], 0U, REDCONF_BLOCK_SIZE - ulOldSizeByteInBlock );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
REDERROR();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
/** @brief Seek to a given position within an inode, then buffer the data block.
|
|
*
|
|
* On successful return, pInode->pbData will be populated with a buffer
|
|
* corresponding to the @p ulBlock block offset.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlock The block offset to seek to and buffer.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_ENODATA The block offset is sparse.
|
|
* @retval -RED_EINVAL @p ulBlock is too large.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeDataSeekAndRead( CINODE * pInode,
|
|
uint32_t ulBlock )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
ret = RedInodeDataSeek( pInode, ulBlock );
|
|
|
|
if( ( ret == 0 ) && ( pInode->pbData == NULL ) )
|
|
{
|
|
REDASSERT( pInode->ulDataBlock != BLOCK_SPARSE );
|
|
|
|
ret = RedBufferGet( pInode->ulDataBlock, 0U, CAST_VOID_PTR_PTR( &pInode->pbData ) );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Seek to a given position within an inode.
|
|
*
|
|
* On successful return, pInode->ulDataBlock will be populated with the
|
|
* physical block number corresponding to the @p ulBlock block offset.
|
|
*
|
|
* Note: Callers of this function depend on its parameter checking.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlock The block offset to seek to.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_ENODATA The block offset is sparse.
|
|
* @retval -RED_EINVAL @p ulBlock is too large; or @p pInode is not a
|
|
* mounted cached inode pointer.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
*/
|
|
REDSTATUS RedInodeDataSeek( CINODE * pInode,
|
|
uint32_t ulBlock )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( ulBlock >= INODE_DATA_BLOCKS ) )
|
|
{
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
SeekCoord( pInode, ulBlock );
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
|
|
{
|
|
if( pInode->ulDindirBlock == BLOCK_SPARSE )
|
|
{
|
|
/* If the double indirect is unallocated, so is the indirect.
|
|
*/
|
|
pInode->ulIndirBlock = BLOCK_SPARSE;
|
|
}
|
|
else
|
|
{
|
|
if( pInode->pDindir == NULL )
|
|
{
|
|
ret = RedBufferGet( pInode->ulDindirBlock, BFLAG_META_DINDIR, CAST_VOID_PTR_PTR( &pInode->pDindir ) );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
pInode->ulIndirBlock = pInode->pDindir->aulEntries[ pInode->uDindirEntry ];
|
|
}
|
|
}
|
|
}
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
if( ( ret == 0 ) && ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) )
|
|
{
|
|
if( pInode->ulIndirBlock == BLOCK_SPARSE )
|
|
{
|
|
/* If the indirect is unallocated, so is the data block.
|
|
*/
|
|
pInode->ulDataBlock = BLOCK_SPARSE;
|
|
}
|
|
else
|
|
{
|
|
if( pInode->pIndir == NULL )
|
|
{
|
|
ret = RedBufferGet( pInode->ulIndirBlock, BFLAG_META_INDIR, CAST_VOID_PTR_PTR( &pInode->pIndir ) );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
pInode->ulDataBlock = pInode->pIndir->aulEntries[ pInode->uIndirEntry ];
|
|
}
|
|
}
|
|
}
|
|
#endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
|
|
|
|
if( ( ret == 0 ) && ( pInode->ulDataBlock == BLOCK_SPARSE ) )
|
|
{
|
|
ret = -RED_ENODATA;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Seek to the coordinates.
|
|
*
|
|
* Compute the new coordinates, and put any buffers which are not needed or are
|
|
* no longer appropriate.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlock The block offset to seek to.
|
|
*/
|
|
static void SeekCoord( CINODE * pInode,
|
|
uint32_t ulBlock )
|
|
{
|
|
if( !CINODE_IS_MOUNTED( pInode ) || ( ulBlock >= INODE_DATA_BLOCKS ) )
|
|
{
|
|
REDERROR();
|
|
}
|
|
else if( ( pInode->ulLogicalBlock != ulBlock ) || !pInode->fCoordInited )
|
|
{
|
|
RedInodePutData( pInode );
|
|
pInode->ulLogicalBlock = ulBlock;
|
|
|
|
#if REDCONF_DIRECT_POINTERS > 0U
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
if( ulBlock < REDCONF_DIRECT_POINTERS )
|
|
#endif
|
|
{
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
RedInodePutCoord( pInode );
|
|
#endif
|
|
|
|
pInode->uInodeEntry = ( uint16_t ) ulBlock;
|
|
pInode->ulDataBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
pInode->uDindirEntry = COORD_ENTRY_INVALID;
|
|
#endif
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
pInode->uIndirEntry = COORD_ENTRY_INVALID;
|
|
#endif
|
|
}
|
|
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
else
|
|
#endif
|
|
#endif /* if REDCONF_DIRECT_POINTERS > 0U */
|
|
#if REDCONF_INDIRECT_POINTERS > 0U
|
|
#if REDCONF_INDIRECT_POINTERS < INODE_ENTRIES
|
|
if( ulBlock < ( INODE_INDIR_BLOCKS + REDCONF_DIRECT_POINTERS ) )
|
|
#endif
|
|
{
|
|
uint32_t ulIndirRangeOffset = ulBlock - REDCONF_DIRECT_POINTERS;
|
|
uint16_t uInodeEntry = ( uint16_t ) ( ( ulIndirRangeOffset / INDIR_ENTRIES ) + REDCONF_DIRECT_POINTERS );
|
|
uint16_t uIndirEntry = ( uint16_t ) ( ulIndirRangeOffset % INDIR_ENTRIES );
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
RedInodePutDindir( pInode );
|
|
#endif
|
|
|
|
/* If the inode entry is not changing, then the previous indirect
|
|
* is still the correct one. Otherwise, the old indirect will be
|
|
* released and the new one will be read later.
|
|
*/
|
|
if( ( pInode->uInodeEntry != uInodeEntry ) || !pInode->fCoordInited )
|
|
{
|
|
RedInodePutIndir( pInode );
|
|
|
|
pInode->uInodeEntry = uInodeEntry;
|
|
|
|
pInode->ulIndirBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
|
|
}
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
pInode->uDindirEntry = COORD_ENTRY_INVALID;
|
|
#endif
|
|
pInode->uIndirEntry = uIndirEntry;
|
|
|
|
/* At this point, the following pInode members are needed but not
|
|
* yet populated:
|
|
*
|
|
* - pIndir
|
|
* - ulDataBlock
|
|
*/
|
|
}
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
else
|
|
#endif
|
|
#endif /* if REDCONF_INDIRECT_POINTERS > 0U */
|
|
#if DINDIR_POINTERS > 0U
|
|
{
|
|
uint32_t ulDindirRangeOffset = ( ulBlock - REDCONF_DIRECT_POINTERS ) - INODE_INDIR_BLOCKS;
|
|
uint16_t uInodeEntry = ( uint16_t ) ( ( ulDindirRangeOffset / DINDIR_DATA_BLOCKS ) + REDCONF_DIRECT_POINTERS + REDCONF_INDIRECT_POINTERS );
|
|
uint32_t ulDindirNodeOffset = ulDindirRangeOffset % DINDIR_DATA_BLOCKS;
|
|
uint16_t uDindirEntry = ( uint16_t ) ( ulDindirNodeOffset / INDIR_ENTRIES );
|
|
uint16_t uIndirEntry = ( uint16_t ) ( ulDindirNodeOffset % INDIR_ENTRIES );
|
|
|
|
/* If the inode entry is not changing, then the previous double
|
|
* indirect is still the correct one. Otherwise, the old double
|
|
* indirect will be released and the new one will be read later.
|
|
*/
|
|
if( ( pInode->uInodeEntry != uInodeEntry ) || !pInode->fCoordInited )
|
|
{
|
|
RedInodePutIndir( pInode );
|
|
RedInodePutDindir( pInode );
|
|
|
|
pInode->uInodeEntry = uInodeEntry;
|
|
|
|
pInode->ulDindirBlock = pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ];
|
|
}
|
|
|
|
/* If neither the inode entry nor double indirect entry are
|
|
* changing, then the previous indirect is still the correct one.
|
|
* Otherwise, it old indirect will be released and the new one will
|
|
* be read later.
|
|
*/
|
|
else if( pInode->uDindirEntry != uDindirEntry )
|
|
{
|
|
RedInodePutIndir( pInode );
|
|
}
|
|
else
|
|
{
|
|
/* Data buffer has already been put, nothing to do.
|
|
*/
|
|
}
|
|
|
|
pInode->uDindirEntry = uDindirEntry;
|
|
pInode->uIndirEntry = uIndirEntry;
|
|
|
|
/* At this point, the following pInode members are needed but not
|
|
* yet populated:
|
|
*
|
|
* - pDindir
|
|
* - pIndir
|
|
* - ulIndirBlock
|
|
* - ulDataBlock
|
|
*/
|
|
}
|
|
#elif ( REDCONF_DIRECT_POINTERS > 0U ) && ( REDCONF_INDIRECT_POINTERS > 0U )
|
|
else
|
|
{
|
|
/* There are no double indirects, so the block should have been in
|
|
* the direct or indirect range.
|
|
*/
|
|
REDERROR();
|
|
}
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
|
|
pInode->fCoordInited = true;
|
|
}
|
|
else
|
|
{
|
|
/* Seeking to the current position, nothing to do.
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
/** @brief Read an unaligned portion of a block.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ullStart The file offset at which to read.
|
|
* @param ulLen The number of bytes to read.
|
|
* @param pbBuffer The buffer to read into.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS ReadUnaligned( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t ulLen,
|
|
uint8_t * pbBuffer )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
/* This read should not cross a block boundary.
|
|
*/
|
|
if( ( ( ullStart >> BLOCK_SIZE_P2 ) != ( ( ( ullStart + ulLen ) - 1U ) >> BLOCK_SIZE_P2 ) ) ||
|
|
( pbBuffer == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedInodeDataSeekAndRead( pInode, ( uint32_t ) ( ullStart >> BLOCK_SIZE_P2 ) );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
RedMemCpy( pbBuffer, &pInode->pbData[ ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ], ulLen );
|
|
}
|
|
else if( ret == -RED_ENODATA )
|
|
{
|
|
/* Sparse block, return zeroed data.
|
|
*/
|
|
RedMemSet( pbBuffer, 0U, ulLen );
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
/* No action, just return the error.
|
|
*/
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Read one or more whole blocks.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlockStart The file block offset at which to read.
|
|
* @param ulBlockCount The number of blocks to read.
|
|
* @param pbBuffer The buffer to read into.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS ReadAligned( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t ulBlockCount,
|
|
uint8_t * pbBuffer )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( pbBuffer == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
uint32_t ulBlockIndex = 0U;
|
|
|
|
/* Read the data from disk one contiguous extent at a time.
|
|
*/
|
|
while( ( ret == 0 ) && ( ulBlockIndex < ulBlockCount ) )
|
|
{
|
|
uint32_t ulExtentStart;
|
|
uint32_t ulExtentLen = ulBlockCount - ulBlockIndex;
|
|
|
|
ret = GetExtent( pInode, ulBlockStart + ulBlockIndex, &ulExtentStart, &ulExtentLen );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/* Before reading directly from disk, flush any dirty file data
|
|
* buffers in the range to avoid reading stale data.
|
|
*/
|
|
ret = RedBufferFlush( ulExtentStart, ulExtentLen );
|
|
|
|
if( ret == 0 )
|
|
#endif
|
|
{
|
|
ret = RedIoRead( gbRedVolNum, ulExtentStart, ulExtentLen, &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ] );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulBlockIndex += ulExtentLen;
|
|
}
|
|
}
|
|
}
|
|
else if( ret == -RED_ENODATA )
|
|
{
|
|
/* Sparse block, return zeroed data.
|
|
*/
|
|
RedMemSet( &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ], 0U, REDCONF_BLOCK_SIZE );
|
|
ulBlockIndex++;
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
/* An unexpected error occurred; the loop will terminate.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Write an unaligned portion of a block.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ullStart The file offset at which to write.
|
|
* @param ulLen The number of bytes to write.
|
|
* @param pbBuffer The buffer to write from.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC No data can be written because there is insufficient
|
|
* free space.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS WriteUnaligned( CINODE * pInode,
|
|
uint64_t ullStart,
|
|
uint32_t ulLen,
|
|
const uint8_t * pbBuffer )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
/* This write should not cross a block boundary.
|
|
*/
|
|
if( ( ( ullStart >> BLOCK_SIZE_P2 ) != ( ( ( ullStart + ulLen ) - 1U ) >> BLOCK_SIZE_P2 ) ) ||
|
|
( pbBuffer == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ( uint32_t ) ( ullStart >> BLOCK_SIZE_P2 ) );
|
|
|
|
if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
|
|
{
|
|
ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, true );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
RedMemCpy( &pInode->pbData[ ullStart & ( REDCONF_BLOCK_SIZE - 1U ) ], pbBuffer, ulLen );
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Write one or more whole blocks.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlockStart The file block offset at which to write.
|
|
* @param pulBlockCount On entry, the number of blocks to attempt to write.
|
|
* On successful return, the number of blocks actually
|
|
* written.
|
|
* @param pbBuffer The buffer to write from.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC No data can be written because there is insufficient
|
|
* free space.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS WriteAligned( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t * pulBlockCount,
|
|
const uint8_t * pbBuffer )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( ( pulBlockCount == NULL ) || ( pbBuffer == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
bool fFull = false;
|
|
uint32_t ulBlockCount = *pulBlockCount;
|
|
uint32_t ulBlockIndex;
|
|
|
|
/* Branch all of the file data blocks in advance.
|
|
*/
|
|
for( ulBlockIndex = 0U; ( ulBlockIndex < ulBlockCount ) && !fFull; ulBlockIndex++ )
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ulBlockStart + ulBlockIndex );
|
|
|
|
if( ( ret == 0 ) || ( ret == -RED_ENODATA ) )
|
|
{
|
|
ret = BranchBlock( pInode, BRANCHDEPTH_FILE_DATA, false );
|
|
|
|
if( ret == -RED_ENOSPC )
|
|
{
|
|
if( ulBlockIndex > 0U )
|
|
{
|
|
ret = 0;
|
|
}
|
|
|
|
fFull = true;
|
|
}
|
|
}
|
|
|
|
if( ret != 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ulBlockCount = ulBlockIndex;
|
|
ulBlockIndex = 0U;
|
|
|
|
if( fFull )
|
|
{
|
|
ulBlockCount--;
|
|
}
|
|
|
|
/* Write the data to disk one contiguous extent at a time.
|
|
*/
|
|
while( ( ret == 0 ) && ( ulBlockIndex < ulBlockCount ) )
|
|
{
|
|
uint32_t ulExtentStart;
|
|
uint32_t ulExtentLen = ulBlockCount - ulBlockIndex;
|
|
|
|
ret = GetExtent( pInode, ulBlockStart + ulBlockIndex, &ulExtentStart, &ulExtentLen );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedIoWrite( gbRedVolNum, ulExtentStart, ulExtentLen, &pbBuffer[ ulBlockIndex << BLOCK_SIZE_P2 ] );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* If there is any buffered file data for the extent we
|
|
* just wrote, those buffers are now stale.
|
|
*/
|
|
ret = RedBufferDiscardRange( ulExtentStart, ulExtentLen );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ulBlockIndex += ulExtentLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pulBlockCount = ulBlockCount;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* REDCONF_READ_ONLY == 0 */
|
|
|
|
|
|
/** @brief Get the physical block number and count of contiguous blocks given a
|
|
* starting logical block number.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param ulBlockStart The file block offset for the start of the extent.
|
|
* @param pulExtentStart On successful return, the starting physical block
|
|
* number of the contiguous extent.
|
|
* @param pulExtentLen On entry, the maximum length of the extent; on
|
|
* successful return, the length of the contiguous
|
|
* extent.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENODATA The block offset is sparse.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS GetExtent( CINODE * pInode,
|
|
uint32_t ulBlockStart,
|
|
uint32_t * pulExtentStart,
|
|
uint32_t * pulExtentLen )
|
|
{
|
|
REDSTATUS ret;
|
|
|
|
if( ( pulExtentStart == NULL ) || ( pulExtentLen == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ulBlockStart );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
uint32_t ulExtentLen = *pulExtentLen;
|
|
uint32_t ulFirstBlock = pInode->ulDataBlock;
|
|
uint32_t ulRunLen = 1U;
|
|
|
|
while( ( ret == 0 ) && ( ulRunLen < ulExtentLen ) )
|
|
{
|
|
ret = RedInodeDataSeek( pInode, ulBlockStart + ulRunLen );
|
|
|
|
/* The extent ends when we find a sparse data block or when the
|
|
* data block is not contiguous with the preceding data block.
|
|
*/
|
|
if( ( ret == -RED_ENODATA ) || ( ( ret == 0 ) && ( pInode->ulDataBlock != ( ulFirstBlock + ulRunLen ) ) ) )
|
|
{
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
ulRunLen++;
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pulExtentStart = ulFirstBlock;
|
|
*pulExtentLen = ulRunLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if REDCONF_READ_ONLY == 0
|
|
|
|
/** @brief Allocate or branch the file metadata path and data block if necessary.
|
|
*
|
|
* Optionally, can stop allocating/branching at a certain depth.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure.
|
|
* @param depth A BRANCHDEPTH_ value indicating the lowest depth to branch.
|
|
* @param fBuffer Whether to buffer the data block.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_ENOSPC No data can be written because there is insufficient
|
|
* free space.
|
|
*/
|
|
static REDSTATUS BranchBlock( CINODE * pInode,
|
|
BRANCHDEPTH depth,
|
|
bool fBuffer )
|
|
{
|
|
REDSTATUS ret;
|
|
uint32_t ulCost = 0U; /* Init'd to quiet warnings. */
|
|
|
|
ret = BranchBlockCost( pInode, depth, &ulCost );
|
|
|
|
if( ( ret == 0 ) && ( ulCost > FreeBlockCount() ) )
|
|
{
|
|
ret = -RED_ENOSPC;
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
#if DINDIR_POINTERS > 0U
|
|
if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
|
|
{
|
|
ret = BranchOneBlock( &pInode->ulDindirBlock, CAST_VOID_PTR_PTR( &pInode->pDindir ), BFLAG_META_DINDIR );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* In case we just created the double indirect.
|
|
*/
|
|
pInode->pDindir->ulInode = pInode->ulInode;
|
|
|
|
pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulDindirBlock;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
{
|
|
if( ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) && ( depth >= BRANCHDEPTH_INDIR ) )
|
|
{
|
|
ret = BranchOneBlock( &pInode->ulIndirBlock, CAST_VOID_PTR_PTR( &pInode->pIndir ), BFLAG_META_INDIR );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
/* In case we just created the indirect.
|
|
*/
|
|
pInode->pIndir->ulInode = pInode->ulInode;
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
|
|
{
|
|
pInode->pDindir->aulEntries[ pInode->uDindirEntry ] = pInode->ulIndirBlock;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulIndirBlock;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
#endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
|
|
{
|
|
if( depth == BRANCHDEPTH_FILE_DATA )
|
|
{
|
|
#if REDCONF_INODE_BLOCKS == 1
|
|
bool fAllocedNew = ( pInode->ulDataBlock == BLOCK_SPARSE );
|
|
#endif
|
|
void ** ppBufPtr = ( fBuffer || ( pInode->pbData != NULL ) ) ? CAST_VOID_PTR_PTR( &pInode->pbData ) : NULL;
|
|
|
|
ret = BranchOneBlock( &pInode->ulDataBlock, ppBufPtr, 0U );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
if( pInode->uIndirEntry != COORD_ENTRY_INVALID )
|
|
{
|
|
pInode->pIndir->aulEntries[ pInode->uIndirEntry ] = pInode->ulDataBlock;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pInode->pInodeBuf->aulEntries[ pInode->uInodeEntry ] = pInode->ulDataBlock;
|
|
}
|
|
|
|
#if REDCONF_INODE_BLOCKS == 1
|
|
if( fAllocedNew )
|
|
{
|
|
if( pInode->pInodeBuf->ulBlocks < INODE_DATA_BLOCKS )
|
|
{
|
|
pInode->pInodeBuf->ulBlocks++;
|
|
}
|
|
else
|
|
{
|
|
CRITICAL_ERROR();
|
|
ret = -RED_EFUBAR;
|
|
}
|
|
}
|
|
#endif /* if REDCONF_INODE_BLOCKS == 1 */
|
|
}
|
|
}
|
|
}
|
|
|
|
CRITICAL_ASSERT( ret == 0 );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Branch a block.
|
|
*
|
|
* The block can be a double indirect, indirect, or file data block.
|
|
*
|
|
* The caller should have already handled the disk full implications of
|
|
* branching this block.
|
|
*
|
|
* @param pulBlock On entry, the current block number, which may be
|
|
* BLOCK_SPARSE if the block is to be newly allocated. On
|
|
* successful return, populated with the new block number,
|
|
* which may be the same as the original block number if it
|
|
* was not BLOCK_SPARSE and the block was already branched.
|
|
* @param ppBuffer If NULL, indicates that the caller does not want to buffer
|
|
* the branched block. If non-NULL, the caller does want the
|
|
* branched block buffered, and the following is true: On
|
|
* entry, the current buffer for the block, if there is one, or
|
|
* NULL if there is no buffer. On successful exit, populated
|
|
* with a buffer for the block, which will be dirty. If the
|
|
* block number is initially BLOCK_SPARSE, there should be no
|
|
* buffer for the block.
|
|
* @param uBFlag The buffer type flags: BFLAG_META_DINDIR, BFLAG_META_INDIR,
|
|
* or zero for file data.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS BranchOneBlock( uint32_t * pulBlock,
|
|
void ** ppBuffer,
|
|
uint16_t uBFlag )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( pulBlock == NULL )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ALLOCSTATE state = ALLOCSTATE_FREE;
|
|
uint32_t ulPrevBlock = *pulBlock;
|
|
|
|
if( ulPrevBlock != BLOCK_SPARSE )
|
|
{
|
|
ret = RedImapBlockState( ulPrevBlock, &state );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( state == ALLOCSTATE_NEW )
|
|
{
|
|
/* Block is already branched, so simply get it buffered dirty
|
|
* if requested.
|
|
*/
|
|
if( ppBuffer != NULL )
|
|
{
|
|
if( *ppBuffer != NULL )
|
|
{
|
|
RedBufferDirty( *ppBuffer );
|
|
}
|
|
else
|
|
{
|
|
ret = RedBufferGet( ulPrevBlock, uBFlag | BFLAG_DIRTY, ppBuffer );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Block does not exist or is committed state, so allocate a
|
|
* new block for the branch.
|
|
*/
|
|
ret = RedImapAllocBlock( pulBlock );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
if( ulPrevBlock == BLOCK_SPARSE )
|
|
{
|
|
/* Block did not exist previously, so just get it
|
|
* buffered if requested.
|
|
*/
|
|
if( ppBuffer != NULL )
|
|
{
|
|
if( *ppBuffer != NULL )
|
|
{
|
|
/* How could there be an existing buffer when
|
|
* the block did not exist?
|
|
*/
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ret = RedBufferGet( *pulBlock, ( uint16_t ) ( ( uint32_t ) uBFlag | BFLAG_NEW | BFLAG_DIRTY ), ppBuffer );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Branch the buffer for the committed state block to
|
|
* the newly allocated location.
|
|
*/
|
|
if( ppBuffer != NULL )
|
|
{
|
|
if( *ppBuffer == NULL )
|
|
{
|
|
ret = RedBufferGet( ulPrevBlock, uBFlag, ppBuffer );
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
RedBufferBranch( *ppBuffer, *pulBlock );
|
|
}
|
|
}
|
|
|
|
/* Mark the committed state block almost free.
|
|
*/
|
|
if( ret == 0 )
|
|
{
|
|
ret = RedImapBlockSet( ulPrevBlock, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Compute the free space cost of branching a block.
|
|
*
|
|
* The caller must first use RedInodeDataSeek() to the block to be branched.
|
|
*
|
|
* @param pInode A pointer to the cached inode structure, whose coordinates
|
|
* indicate the block to be branched.
|
|
* @param depth A BRANCHDEPTH_ value indicating how much of the file
|
|
* metadata structure needs to be branched.
|
|
* @param pulCost On successful return, populated with the number of blocks
|
|
* that must be allocated from free space in order to branch
|
|
* the given block.
|
|
*
|
|
* @return A negated ::REDSTATUS code indicating the operation result.
|
|
*
|
|
* @retval 0 Operation was successful.
|
|
* @retval -RED_EIO A disk I/O error occurred.
|
|
* @retval -RED_EINVAL Invalid parameters.
|
|
*/
|
|
static REDSTATUS BranchBlockCost( const CINODE * pInode,
|
|
BRANCHDEPTH depth,
|
|
uint32_t * pulCost )
|
|
{
|
|
REDSTATUS ret = 0;
|
|
|
|
if( !CINODE_IS_MOUNTED( pInode ) || !pInode->fCoordInited || ( depth > BRANCHDEPTH_MAX ) || ( pulCost == NULL ) )
|
|
{
|
|
REDERROR();
|
|
ret = -RED_EINVAL;
|
|
}
|
|
else
|
|
{
|
|
ALLOCSTATE state;
|
|
|
|
/* ulCost is initialized to the maximum number of blocks that could
|
|
* be branched, and decremented for every block we determine does not
|
|
* need to be branched.
|
|
*/
|
|
#if DINDIR_POINTERS > 0U
|
|
uint32_t ulCost = 3U;
|
|
#elif REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
uint32_t ulCost = 2U;
|
|
#else
|
|
uint32_t ulCost = 1U;
|
|
#endif
|
|
|
|
#if DINDIR_POINTERS > 0U
|
|
if( pInode->uDindirEntry != COORD_ENTRY_INVALID )
|
|
{
|
|
if( pInode->ulDindirBlock != BLOCK_SPARSE )
|
|
{
|
|
ret = RedImapBlockState( pInode->ulDindirBlock, &state );
|
|
|
|
if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
|
|
{
|
|
/* Double indirect already branched.
|
|
*/
|
|
ulCost--;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* At this inode offset there are no double indirects.
|
|
*/
|
|
ulCost--;
|
|
}
|
|
|
|
if( ret == 0 )
|
|
#endif /* if DINDIR_POINTERS > 0U */
|
|
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
|
|
{
|
|
if( ( pInode->uIndirEntry != COORD_ENTRY_INVALID ) && ( depth >= BRANCHDEPTH_INDIR ) )
|
|
{
|
|
if( pInode->ulIndirBlock != BLOCK_SPARSE )
|
|
{
|
|
ret = RedImapBlockState( pInode->ulIndirBlock, &state );
|
|
|
|
if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
|
|
{
|
|
/* Indirect already branched.
|
|
*/
|
|
ulCost--;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Either not branching this deep, or at this inode offset
|
|
* there are no indirects.
|
|
*/
|
|
ulCost--;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
#endif /* if REDCONF_DIRECT_POINTERS < INODE_ENTRIES */
|
|
{
|
|
if( depth == BRANCHDEPTH_FILE_DATA )
|
|
{
|
|
if( pInode->ulDataBlock != BLOCK_SPARSE )
|
|
{
|
|
ret = RedImapBlockState( pInode->ulDataBlock, &state );
|
|
|
|
if( ( ret == 0 ) && ( state == ALLOCSTATE_NEW ) )
|
|
{
|
|
/* File data block already branched.
|
|
*/
|
|
ulCost--;
|
|
|
|
/* If the file data block is branched, then its parent
|
|
* nodes should be branched as well.
|
|
*/
|
|
REDASSERT( ulCost == 0U );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Not branching this deep.
|
|
*/
|
|
ulCost--;
|
|
}
|
|
}
|
|
|
|
if( ret == 0 )
|
|
{
|
|
*pulCost = ulCost;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/** @brief Yields the number of currently available free blocks.
|
|
*
|
|
* Accounts for reserved blocks, subtracting the number of reserved blocks if
|
|
* they are unavailable.
|
|
*
|
|
* @return Number of currently available free blocks.
|
|
*/
|
|
static uint32_t FreeBlockCount( void )
|
|
{
|
|
uint32_t ulFreeBlocks = gpRedMR->ulFreeBlocks;
|
|
|
|
#if RESERVED_BLOCKS > 0U
|
|
if( !gpRedCoreVol->fUseReservedBlocks )
|
|
{
|
|
if( ulFreeBlocks >= RESERVED_BLOCKS )
|
|
{
|
|
ulFreeBlocks -= RESERVED_BLOCKS;
|
|
}
|
|
else
|
|
{
|
|
ulFreeBlocks = 0U;
|
|
}
|
|
}
|
|
#endif /* if RESERVED_BLOCKS > 0U */
|
|
|
|
return ulFreeBlocks;
|
|
}
|
|
#endif /* REDCONF_READ_ONLY == 0 */
|