/** @brief Branch an imap node and get a buffer for it. If the imap node is already branched, it can be overwritten in its current location, and this function just gets it buffered dirty. If the node is not already branched, the metaroot must be updated to toggle the imap node to its alternate location, thereby preserving the committed state copy of the imap node. @param ulImapNode The imap node to branch and buffer. @param ppImap On successful return, populated with the imap node buffer, which will be marked dirty. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p ulImapNode is out of range; or @p ppImap is `NULL`. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS ImapNodeBranch( uint32_t ulImapNode, IMAPNODE **ppImap) { REDSTATUS ret; if((ulImapNode >= gpRedCoreVol->ulImapNodeCount) || (ppImap == NULL)) { REDERROR(); ret = -RED_EINVAL; } else if(ImapNodeIsBranched(ulImapNode)) { /* Imap node is already branched, so just get it buffered dirty. */ ret = RedBufferGet(RedImapNodeBlock(gpRedCoreVol->bCurMR, ulImapNode), BFLAG_META_IMAP | BFLAG_DIRTY, CAST_VOID_PTR_PTR(ppImap)); } else { uint32_t ulBlockCurrent; uint32_t ulBlockOld; /* The metaroot currently points to the committed state imap node. Toggle the metaroot to point at the alternate, writeable location. */ if(RedBitGet(gpRedMR->abEntries, ulImapNode)) { RedBitClear(gpRedMR->abEntries, ulImapNode); } else { RedBitSet(gpRedMR->abEntries, ulImapNode); } ulBlockCurrent = RedImapNodeBlock(gpRedCoreVol->bCurMR, ulImapNode); ulBlockOld = RedImapNodeBlock(1U - gpRedCoreVol->bCurMR, ulImapNode); ret = RedBufferDiscardRange(ulBlockCurrent, 1U); /* Buffer the committed copy then reassign the block number to the writeable location. This also dirties the buffer. */ if(ret == 0) { ret = RedBufferGet(ulBlockOld, BFLAG_META_IMAP, CAST_VOID_PTR_PTR(ppImap)); if(ret == 0) { RedBufferBranch(*ppImap, ulBlockCurrent); } } } 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; }