blob: c8c768265004d5d3a89a361a5f67826a5e654a96 [file] [log] [blame]
/* ----> 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 cachde 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
(void)bTimeFields;
#endif
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 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
/** @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) || 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
/** @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
/** @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
/** @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;
}