/** @brief Write out a dirty buffer. @param bIdx The index of the buffer to write. @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. */ static REDSTATUS BufferWrite( uint8_t bIdx) { REDSTATUS ret = 0; if(bIdx < REDCONF_BUFFER_COUNT) { const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx]; REDASSERT((pHead->uFlags & BFLAG_DIRTY) != 0U); if((pHead->uFlags & BFLAG_META) != 0U) { ret = BufferFinalize(gBufCtx.b.aabBuffer[bIdx], pHead->bVolNum, pHead->uFlags); } if(ret == 0) { ret = RedIoWrite(pHead->bVolNum, pHead->ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]); #ifdef REDCONF_ENDIAN_SWAP BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], pHead->uFlags); #endif } } else { REDERROR(); ret = -RED_EINVAL; } return ret; }
/** @brief Commit a transaction point. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedVolTransact(void) { REDSTATUS ret = 0; REDASSERT(!gpRedVolume->fReadOnly); /* Should be checked by caller. */ if(gpRedCoreVol->fBranched) { gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks; gpRedCoreVol->ulAlmostFreeBlocks = 0U; ret = RedBufferFlush(0U, gpRedVolume->ulBlockCount); if(ret == 0) { gpRedMR->hdr.ulSignature = META_SIG_METAROOT; gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence; ret = RedVolSeqNumIncrement(); } if(ret == 0) { const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(gpRedMR); uint32_t ulSectorCRC; #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(gpRedMR); #endif gpRedMR->ulSectorCRC = 0U; ulSectorCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U); if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE) { gpRedMR->hdr.ulCRC = RedCrc32Update(ulSectorCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize); } else { gpRedMR->hdr.ulCRC = ulSectorCRC; } gpRedMR->ulSectorCRC = ulSectorCRC; #ifdef REDCONF_ENDIAN_SWAP gpRedMR->hdr.ulCRC = RedRev32(gpRedMR->hdr.ulCRC); gpRedMR->ulSectorCRC = RedRev32(gpRedMR->ulSectorCRC); #endif /* Flush the block device before writing the metaroot, so that all previously written blocks are guaranteed to be on the media before the metaroot is written. Otherwise, if the block device reorders the writes, the metaroot could reach the media before metadata it points at, creating a window for disk corruption if power is lost. */ ret = RedIoFlush(gbRedVolNum); } if(ret == 0) { ret = RedIoWrite(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR); #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(gpRedMR); #endif } /* Flush the block device to force the metaroot write to the media. This guarantees the transaction point is really complete before we return. */ if(ret == 0) { ret = RedIoFlush(gbRedVolNum); } /* Toggle to the other metaroot buffer. The working state and committed state metaroot buffers exchange places. */ if(ret == 0) { uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR; gpRedCoreVol->aMR[bNextMR] = *gpRedMR; gpRedCoreVol->bCurMR = bNextMR; gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR]; gpRedCoreVol->fBranched = false; } CRITICAL_ASSERT(ret == 0); } return ret; }