/* ----> 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 routines for the external imap. * * The external imap is used on volumes that are too big for the imap bitmap * to be stored entirely in the metaroot, so instead the bitmap is stored in * imap nodes on disk, and the metaroot bitmap is used to toggle between imap * nodes. */ #include #if REDCONF_IMAP_EXTERNAL == 1 #include #if REDCONF_READ_ONLY == 0 static REDSTATUS ImapNodeBranch( uint32_t ulImapNode, IMAPNODE ** ppImap ); static bool ImapNodeIsBranched( uint32_t ulImapNode ); #endif /** @brief Get the allocation bit of a block from the imap as it exists in * either metaroot. * * @param bMR The metaroot index: either 0 or 1. * @param ulBlock The block number to query. * @param pfAllocated On successful exit, populated with the allocation bit * of the block. * * @return A negated ::REDSTATUS code indicating the operation result. * * @retval 0 Operation was successful. * @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range; * or @p pfAllocated is `NULL`. * @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedImapEBlockGet( uint8_t bMR, uint32_t ulBlock, bool * pfAllocated ) { REDSTATUS ret; if( gpRedCoreVol->fImapInline || ( bMR > 1U ) || ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) || ( ulBlock >= gpRedVolume->ulBlockCount ) || ( pfAllocated == NULL ) ) { REDERROR(); ret = -RED_EINVAL; } else { uint32_t ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN; uint32_t ulImapNode = ulOffset / IMAPNODE_ENTRIES; uint8_t bMRToRead = bMR; IMAPNODE * pImap; #if REDCONF_READ_ONLY == 0 /* If the imap node is not branched, then both copies of the imap are * identical. If the old metaroot copy is requested, use the current * copy instead, since it is more likely to be buffered. */ if( bMR == ( 1U - gpRedCoreVol->bCurMR ) ) { if( !ImapNodeIsBranched( ulImapNode ) ) { bMRToRead = 1U - bMR; } } #endif ret = RedBufferGet( RedImapNodeBlock( bMRToRead, ulImapNode ), BFLAG_META_IMAP, CAST_VOID_PTR_PTR( &pImap ) ); if( ret == 0 ) { *pfAllocated = RedBitGet( pImap->abEntries, ulOffset % IMAPNODE_ENTRIES ); RedBufferPut( pImap ); } } return ret; } #if REDCONF_READ_ONLY == 0 /** @brief Set the allocation bit of a block in the working-state imap. * * @param ulBlock The block number to allocate or free. * @param fAllocated Whether to allocate the block (true) or free it (false). * * @return A negated ::REDSTATUS code indicating the operation result. * * @retval 0 Operation was successful. * @retval -RED_EINVAL @p ulBlock is out of range. * @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedImapEBlockSet( uint32_t ulBlock, bool fAllocated ) { REDSTATUS ret; if( gpRedCoreVol->fImapInline || ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) || ( ulBlock >= gpRedVolume->ulBlockCount ) ) { REDERROR(); ret = -RED_EINVAL; } else { uint32_t ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN; uint32_t ulImapNode = ulOffset / IMAPNODE_ENTRIES; IMAPNODE * pImap; ret = ImapNodeBranch( ulImapNode, &pImap ); if( ret == 0 ) { uint32_t ulImapEntry = ulOffset % IMAPNODE_ENTRIES; if( RedBitGet( pImap->abEntries, ulImapEntry ) == fAllocated ) { /* The driver shouldn't ever set a bit in the imap to its * current value. That shouldn't ever be needed, and it * indicates that the driver is doing unnecessary I/O, or * that the imap is corrupt. */ CRITICAL_ERROR(); ret = -RED_EFUBAR; } else if( fAllocated ) { RedBitSet( pImap->abEntries, ulImapEntry ); } else { RedBitClear( pImap->abEntries, ulImapEntry ); } RedBufferPut( pImap ); } } return ret; } /** @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 Determine whether an imap node is branched. * * If the imap node is branched, it can be overwritten in its current location. * * @param ulImapNode The imap node to examine. * * @return Whether the imap node is branched. */ static bool ImapNodeIsBranched( uint32_t ulImapNode ) { bool fNodeBitSetInMetaroot0 = RedBitGet( gpRedCoreVol->aMR[ 0U ].abEntries, ulImapNode ); bool fNodeBitSetInMetaroot1 = RedBitGet( gpRedCoreVol->aMR[ 1U ].abEntries, ulImapNode ); /* If the imap node is not branched, both metaroots will point to the same * copy of the node. */ return fNodeBitSetInMetaroot0 != fNodeBitSetInMetaroot1; } #endif /* REDCONF_READ_ONLY == 0 */ /** @brief Calculate the block number of the imap node location indicated by the * given metaroot. * * An imap node has two locations on disk. A bit in the metaroot bitmap * indicates which location is the valid one, according to that metaroot. This * function returns the block number of the imap node which is valid in the * given metaroot. * * @param bMR Which metaroot to examine. * @param ulImapNode The imap node for which to calculate the block number. * * @return Block number of the imap node, as indicated by the given metaroot. */ uint32_t RedImapNodeBlock( uint8_t bMR, uint32_t ulImapNode ) { uint32_t ulBlock; REDASSERT( ulImapNode < gpRedCoreVol->ulImapNodeCount ); ulBlock = gpRedCoreVol->ulImapStartBN + ( ulImapNode * 2U ); if( bMR > 1U ) { REDERROR(); } else if( RedBitGet( gpRedCoreVol->aMR[ bMR ].abEntries, ulImapNode ) ) { /* Bit is set, so point ulBlock at the second copy of the node. */ ulBlock++; } else { /* ulBlock already points at the first copy of the node. */ } return ulBlock; } #endif /* REDCONF_IMAP_EXTERNAL == 1 */