/** @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;
}
Exemple #2
0
/** @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;
}