/* ----> 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; | |
} | |