/** @brief Determine whether the metaroot is valid. @param pMR The metaroot buffer. @param pfSectorCRCIsValid Populated with whether the first sector of the metaroot buffer is valid. @return Whether the metaroot is valid. @retval true The metaroot buffer is valid. @retval false The metaroot buffer is invalid. */ static bool MetarootIsValid( METAROOT *pMR, bool *pfSectorCRCIsValid) { bool fRet = false; if(pfSectorCRCIsValid == NULL) { REDERROR(); } else if(pMR == NULL) { REDERROR(); *pfSectorCRCIsValid = false; } #ifdef REDCONF_ENDIAN_SWAP else if(RedRev32(pMR->hdr.ulSignature) != META_SIG_METAROOT) #else else if(pMR->hdr.ulSignature != META_SIG_METAROOT) #endif { *pfSectorCRCIsValid = false; } else { const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMR); uint32_t ulSectorCRC = pMR->ulSectorCRC; uint32_t ulCRC; #ifdef REDCONF_ENDIAN_SWAP ulSectorCRC = RedRev32(ulSectorCRC); #endif /* The sector CRC was zero when the CRC was computed during the transaction, so it must be zero here. */ pMR->ulSectorCRC = 0U; ulCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U); fRet = ulCRC == ulSectorCRC; *pfSectorCRCIsValid = fRet; if(fRet) { if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE) { ulCRC = RedCrc32Update(ulCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize); } #ifdef REDCONF_ENDIAN_SWAP ulCRC = RedRev32(ulCRC); #endif fRet = ulCRC == pMR->hdr.ulCRC; } } return fRet; }
/** @brief Swap the byte order of a metadata node header @param pHeader Pointer to the metadata node header to swap */ static void BufferEndianSwapHeader( NODEHEADER *pHeader) { if(pHeader == NULL) { REDERROR(); } else { pHeader->ulSignature = RedRev32(pHeader->ulSignature); pHeader->ulCRC = RedRev32(pHeader->ulCRC); pHeader->ullSequence = RedRev64(pHeader->ullSequence); } }
static void MetaRootEndianSwap( METAROOT *pMetaRoot) { if(pMetaRoot == NULL) { REDERROR(); } else { pMetaRoot->ulSectorCRC = RedRev32(pMetaRoot->ulSectorCRC); pMetaRoot->ulFreeBlocks = RedRev32(pMetaRoot->ulFreeBlocks); #if REDCONF_API_POSIX == 1 pMetaRoot->ulFreeInodes = RedRev32(pMetaRoot->ulFreeInodes); #endif pMetaRoot->ulAllocNextBlock = RedRev32(pMetaRoot->ulAllocNextBlock); } }
/** @brief Swap the byte order of a master block @param pMaster Pointer to the master block to swap */ static void BufferEndianSwapMaster( MASTERBLOCK *pMaster) { if(pMaster == NULL) { REDERROR(); } else { pMaster->ulVersion = RedRev32(pMaster->ulVersion); pMaster->ulFormatTime = RedRev32(pMaster->ulFormatTime); pMaster->ulInodeCount = RedRev32(pMaster->ulInodeCount); pMaster->ulBlockCount = RedRev32(pMaster->ulBlockCount); pMaster->uMaxNameLen = RedRev16(pMaster->uMaxNameLen); pMaster->uDirectPointers = RedRev16(pMaster->uDirectPointers); pMaster->uIndirectPointers = RedRev16(pMaster->uIndirectPointers); } }
/** @brief Swap the byte order of an indirect or double indirect node @param pIndir Pointer to the node to swap */ static void BufferEndianSwapIndir( INDIR *pIndir) { if(pIndir == NULL) { REDERROR(); } else { uint32_t ulIdx; pIndir->ulInode = RedRev32(pIndir->ulInode); for(ulIdx = 0; ulIdx < INDIR_ENTRIES; ulIdx++) { pIndir->aulEntries[ulIdx] = RedRev32(pIndir->aulEntries[ulIdx]); } } }
/** @brief Swap the byte order of an inode @param pInode Pointer to the inode to swap */ static void BufferEndianSwapInode( INODE *pInode) { if(pInode == NULL) { REDERROR(); } else { uint32_t ulIdx; pInode->ullSize = RedRev64(pInode->ullSize); #if REDCONF_INODE_BLOCKS == 1 pInode->ulBlocks = RedRev32(pInode->ulBlocks); #endif #if REDCONF_INODE_TIMESTAMPS == 1 pInode->ulATime = RedRev32(pInode->ulATime); pInode->ulMTime = RedRev32(pInode->ulMTime); pInode->ulCTime = RedRev32(pInode->ulCTime); #endif pInode->uMode = RedRev16(pInode->uMode); #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1) pInode->uNLink = RedRev16(pInode->uNLink); #endif #if REDCONF_API_POSIX == 1 pInode->ulPInode = RedRev32(pInode->ulPInode); #endif for(ulIdx = 0; ulIdx < INODE_ENTRIES; ulIdx++) { pInode->aulEntries[ulIdx] = RedRev32(pInode->aulEntries[ulIdx]); } } }
/** @brief Finalize a metadata buffer. This updates the CRC and the sequence number. It also sets the signature, though this is only truly needed if the buffer is new. @param pbBuffer Pointer to the metadata buffer to finalize. @param bVolNum The volume number for the metadata buffer. @param uFlags The associated buffer flags. Used to determine the expected signature. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL Invalid parameter; or maximum sequence number reached. */ static REDSTATUS BufferFinalize( uint8_t *pbBuffer, uint8_t bVolNum, uint16_t uFlags) { REDSTATUS ret = 0; if((pbBuffer == NULL) || (bVolNum >= REDCONF_VOLUME_COUNT) || ((uFlags & BFLAG_MASK) != uFlags)) { REDERROR(); ret = -RED_EINVAL; } else { uint32_t ulSignature; switch(uFlags & BFLAG_META_MASK) { case BFLAG_META_MASTER: ulSignature = META_SIG_MASTER; break; #if REDCONF_IMAP_EXTERNAL == 1 case BFLAG_META_IMAP: ulSignature = META_SIG_IMAP; break; #endif case BFLAG_META_INODE: ulSignature = META_SIG_INODE; break; #if DINDIR_POINTERS > 0U case BFLAG_META_DINDIR: ulSignature = META_SIG_DINDIR; break; #endif #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES case BFLAG_META_INDIR: ulSignature = META_SIG_INDIR; break; #endif default: ulSignature = 0U; break; } if(ulSignature == 0U) { REDERROR(); ret = -RED_EINVAL; } else { uint64_t ullSeqNum = gaRedVolume[bVolNum].ullSequence; ret = RedVolSeqNumIncrement(bVolNum); if(ret == 0) { uint32_t ulCrc; RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SIG], &ulSignature, sizeof(ulSignature)); RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_SEQ], &ullSeqNum, sizeof(ullSeqNum)); #ifdef REDCONF_ENDIAN_SWAP BufferEndianSwap(pbBuffer, uFlags); #endif ulCrc = RedCrcNode(pbBuffer); #ifdef REDCONF_ENDIAN_SWAP ulCrc = RedRev32(ulCrc); #endif RedMemCpy(&pbBuffer[NODEHEADER_OFFSET_CRC], &ulCrc, sizeof(ulCrc)); } } } return ret; }
/** Determine whether a metadata buffer is valid. This includes checking its signature, CRC, and sequence number. @param pbBuffer Pointer to the metadata buffer to validate. @param uFlags The buffer flags provided by the caller. Used to determine the expected signature. @return Whether the metadata buffer is valid. @retval true The metadata buffer is valid. @retval false The metadata buffer is invalid. */ static bool BufferIsValid( const uint8_t *pbBuffer, uint16_t uFlags) { bool fValid; if((pbBuffer == NULL) || ((uFlags & BFLAG_MASK) != uFlags)) { REDERROR(); fValid = false; } else { NODEHEADER buf; uint16_t uMetaFlags = uFlags & BFLAG_META_MASK; /* Casting pbBuffer to (NODEHEADER *) would run afoul MISRA-C:2012 R11.3, so instead copy the fields out. */ RedMemCpy(&buf.ulSignature, &pbBuffer[NODEHEADER_OFFSET_SIG], sizeof(buf.ulSignature)); RedMemCpy(&buf.ulCRC, &pbBuffer[NODEHEADER_OFFSET_CRC], sizeof(buf.ulCRC)); RedMemCpy(&buf.ullSequence, &pbBuffer[NODEHEADER_OFFSET_SEQ], sizeof(buf.ullSequence)); #ifdef REDCONF_ENDIAN_SWAP buf.ulCRC = RedRev32(buf.ulCRC); buf.ulSignature = RedRev32(buf.ulSignature); buf.ullSequence = RedRev64(buf.ullSequence); #endif /* Make sure the signature is correct for the type of metadata block requested by the caller. */ switch(buf.ulSignature) { case META_SIG_MASTER: fValid = (uMetaFlags == BFLAG_META_MASTER); break; #if REDCONF_IMAP_EXTERNAL == 1 case META_SIG_IMAP: fValid = (uMetaFlags == BFLAG_META_IMAP); break; #endif case META_SIG_INODE: fValid = (uMetaFlags == BFLAG_META_INODE); break; #if DINDIR_POINTERS > 0U case META_SIG_DINDIR: fValid = (uMetaFlags == BFLAG_META_DINDIR); break; #endif #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES case META_SIG_INDIR: fValid = (uMetaFlags == BFLAG_META_INDIR); break; #endif default: fValid = false; break; } if(fValid) { uint32_t ulComputedCrc; /* Check for disk corruption by comparing the stored CRC with one computed from the data. Also check the sequence number: if it is greater than the current sequence number, the block is from a previous format or the disk is writing blocks out of order. During mount, before the metaroots have been read, the sequence number will be unknown, and the check is skipped. */ ulComputedCrc = RedCrcNode(pbBuffer); if(buf.ulCRC != ulComputedCrc) { fValid = false; } else if(gpRedVolume->fMounted && (buf.ullSequence >= gpRedVolume->ullSequence)) { fValid = false; } else { /* Buffer is valid. No action, fValid is already true. */ } } } return fValid; }
/** @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; }