/** @brief Acquire a buffer. @param ulBlock Block number to acquire. @param uFlags BFLAG_ values for the operation. @param ppBuffer On success, populated with the acquired buffer. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO A disk I/O error occurred. @retval -RED_EINVAL Invalid parameters. @retval -RED_EBUSY All buffers are referenced. */ REDSTATUS RedBufferGet( uint32_t ulBlock, uint16_t uFlags, void **ppBuffer) { REDSTATUS ret = 0; uint8_t bIdx; if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL)) { REDERROR(); ret = -RED_EINVAL; } else { if(BufferFind(ulBlock, &bIdx)) { /* Error if the buffer exists and BFLAG_NEW was specified, since the new flag is used when a block is newly allocated/created, so the block was previously free and and there should never be an existing buffer for a free block. Error if the buffer exists but does not have the same type as was requested. */ if( ((uFlags & BFLAG_NEW) != 0U) || ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK))) { CRITICAL_ERROR(); ret = -RED_EFUBAR; } } else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT) { /* The MINIMUM_BUFFER_COUNT is supposed to ensure that no operation ever runs out of buffers, so this should never happen. */ CRITICAL_ERROR(); ret = -RED_EBUSY; } else { BUFFERHEAD *pHead; /* Search for the least recently used buffer which is not referenced. */ for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--) { if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U) { break; } } bIdx = gBufCtx.abMRU[bIdx]; pHead = &gBufCtx.aHead[bIdx]; if(pHead->bRefCount == 0U) { /* If the LRU buffer is valid and dirty, write it out before repurposing it. */ if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID)) { #if REDCONF_READ_ONLY == 1 CRITICAL_ERROR(); ret = -RED_EFUBAR; #else ret = BufferWrite(bIdx); #endif } } else { /* All the buffers are used, which should have been caught by checking gBufCtx.uNumUsed. */ CRITICAL_ERROR(); ret = -RED_EBUSY; } if(ret == 0) { if((uFlags & BFLAG_NEW) == 0U) { /* Invalidate the LRU buffer. If the read fails, we do not want the buffer head to continue to refer to the old block number, since the read, even if it fails, may have partially overwritten the buffer data (consider the case where block size exceeds sector size, and some but not all of the sectors are read successfully), and if the buffer were to be used subsequently with its partially erroneous contents, bad things could happen. */ pHead->ulBlock = BBLK_INVALID; ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]); if((ret == 0) && ((uFlags & BFLAG_META) != 0U)) { if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags)) { /* A corrupt metadata node is usually a critical error. The master block is an exception since it might be invalid because the volume is not mounted; that condition is expected and should not result in an assertion. */ CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) == BFLAG_META_MASTER); ret = -RED_EIO; } } #ifdef REDCONF_ENDIAN_SWAP if(ret == 0) { BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags); } #endif } else { RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE); } } if(ret == 0) { pHead->bVolNum = gbRedVolNum; pHead->ulBlock = ulBlock; pHead->uFlags = 0U; } } /* Reference the buffer, update its flags, and promote it to MRU. This happens both when BufferFind() found an existing buffer for the block and when the LRU buffer was repurposed to create a buffer for the block. */ if(ret == 0) { BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx]; pHead->bRefCount++; if(pHead->bRefCount == 1U) { gBufCtx.uNumUsed++; } /* BFLAG_NEW tells this function to zero the buffer instead of reading it from disk; it has no meaning later on, and thus is not saved. */ pHead->uFlags |= (uFlags & (~BFLAG_NEW)); BufferMakeMRU(bIdx); *ppBuffer = gBufCtx.b.aabBuffer[bIdx]; } } return ret; }
/** @brief Mount the latest metaroot. This function also populates the volume contexts. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO Both metaroots are missing or corrupt. */ REDSTATUS RedVolMountMetaroot(void) { REDSTATUS ret; ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT, 1U, &gpRedCoreVol->aMR[0U]); if(ret == 0) { ret = RedIoRead(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + 1U, 1U, &gpRedCoreVol->aMR[1U]); } /* Determine which metaroot is the most recent copy that was written completely. */ if(ret == 0) { uint8_t bMR = UINT8_MAX; bool fSectorCRCIsValid; if(MetarootIsValid(&gpRedCoreVol->aMR[0U], &fSectorCRCIsValid)) { bMR = 0U; #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(&gpRedCoreVol->aMR[0U]); #endif } else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid) { ret = -RED_EIO; } else { /* Metaroot is not valid, so it is ignored and there's nothing to do here. */ } if(ret == 0) { if(MetarootIsValid(&gpRedCoreVol->aMR[1U], &fSectorCRCIsValid)) { #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(&gpRedCoreVol->aMR[1U]); #endif if((bMR != 0U) || (gpRedCoreVol->aMR[1U].hdr.ullSequence > gpRedCoreVol->aMR[0U].hdr.ullSequence)) { bMR = 1U; } } else if(gpRedVolConf->fAtomicSectorWrite && !fSectorCRCIsValid) { ret = -RED_EIO; } else { /* Metaroot is not valid, so it is ignored and there's nothing to do here. */ } } if(ret == 0) { if(bMR == UINT8_MAX) { /* Neither metaroot was valid. */ ret = -RED_EIO; } else { gpRedCoreVol->bCurMR = bMR; gpRedMR = &gpRedCoreVol->aMR[bMR]; } } } if(ret == 0) { /* Normally the metaroot contains the highest sequence number, but the master block is the last block written during format, so on a freshly formatted volume the master block sequence number (stored in gpRedVolume->ullSequence) will be higher than that in the metaroot. */ if(gpRedMR->hdr.ullSequence > gpRedVolume->ullSequence) { gpRedVolume->ullSequence = gpRedMR->hdr.ullSequence; } /* gpRedVolume->ullSequence stores the *next* sequence number; to avoid giving the next node written to disk the same sequence number as the metaroot, increment it here. */ ret = RedVolSeqNumIncrement(); } if(ret == 0) { gpRedVolume->fMounted = true; #if REDCONF_READ_ONLY == 0 gpRedVolume->fReadOnly = false; #endif #if RESERVED_BLOCKS > 0U gpRedCoreVol->fUseReservedBlocks = false; #endif gpRedCoreVol->ulAlmostFreeBlocks = 0U; gpRedCoreVol->aMR[1U - gpRedCoreVol->bCurMR] = *gpRedMR; gpRedCoreVol->bCurMR = 1U - gpRedCoreVol->bCurMR; gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR]; } return ret; }