/** @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 Put the cached inode structure. This puts all of the buffers in the ::CINODE structure. Also updates inode timestamp fields if requested. @param pInode The cached inode structure. @param bTimeFields The inode timestamp fields to update. */ void RedInodePut( CINODE *pInode, uint8_t bTimeFields) { if(pInode == NULL) { REDERROR(); } else { RedInodePutCoord(pInode); if(pInode->pInodeBuf != NULL) { #if (REDCONF_READ_ONLY == 0) && (REDCONF_INODE_TIMESTAMPS == 1) if((bTimeFields & IPUT_UPDATE_MASK) != 0U) { if(!pInode->fBranched || !pInode->fDirty) { REDERROR(); } else { uint32_t ulNow = RedOsClockGetTime(); #if REDCONF_ATIME == 1 if((bTimeFields & IPUT_UPDATE_ATIME) != 0U) { pInode->pInodeBuf->ulATime = ulNow; } #endif if((bTimeFields & IPUT_UPDATE_MTIME) != 0U) { pInode->pInodeBuf->ulMTime = ulNow; } if((bTimeFields & IPUT_UPDATE_CTIME) != 0U) { pInode->pInodeBuf->ulCTime = ulNow; } } } #else (void)bTimeFields; #endif RedBufferPut(pInode->pInodeBuf); pInode->pInodeBuf = NULL; } } }
/** @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 Write a string to a user-visible output location. Write a null-terminated string to the serial port, console, terminal, or other display device, such that the text is visible to the user. @param pszString A null-terminated string. */ void RedOsOutputString( const char *pszString) { if(pszString == NULL) { REDERROR(); } else { /* The arm-atollic-eabi version of putchar has been observed not to end up calling __io_putchar() (as would have been expected), so we call it directly instead. */ uint32_t ulIdx = 0U; while(pszString[ulIdx] != '\0') { __io_putchar(pszString[ulIdx]); /* Serial output often requires a \r to print newlines correctly. */ if(pszString[ulIdx] == '\n') { __io_putchar('\r'); } ulIdx++; } } }
/** @brief Write a string to a user-visible output location. Write a null-terminated string to the serial port, console, terminal, or other display device, such that the text is visible to the user. @param pszString A null-terminated string. */ void RedOsOutputString( const char *pszString) { if(pszString == NULL) { REDERROR(); } else { uint32_t ulIdx = 0U; while(pszString[ulIdx] != '\0') { OUTPUT_CHARACTER(pszString[ulIdx]); /* Serial output often requires a \r to print newlines correctly. */ if(pszString[ulIdx] == '\n') { OUTPUT_CHARACTER('\r'); } ulIdx++; } } }
/** @brief Find a block in the buffers. @param ulBlock The block number to find. @param pbIdx If the block is buffered (true is returned), populated with the index of the buffer. @return Boolean indicating whether or not the block is buffered. @retval true @p ulBlock is buffered, and its index has been stored in @p pbIdx. @retval false @p ulBlock is not buffered. */ static bool BufferFind( uint32_t ulBlock, uint8_t *pbIdx) { bool ret = false; if((ulBlock >= gpRedVolume->ulBlockCount) || (pbIdx == NULL)) { REDERROR(); } else { uint8_t bIdx; for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++) { const BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx]; if((pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock == ulBlock)) { *pbIdx = bIdx; ret = true; break; } } } return ret; }
/** @brief Flush any caches beneath the file system. This function must synchronously flush all software and hardware caches beneath the file system, ensuring that all sectors written previously are committed to permanent storage. If the environment has no caching beneath the file system, the implementation of this function can do nothing and return success. The behavior of calling this function is undefined if the block device is closed or if it was opened with ::BDEV_O_RDONLY. @param bVolNum The volume number of the volume whose block device is being flushed. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p bVolNum is an invalid volume number. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedOsBDevFlush( uint8_t bVolNum) { REDSTATUS ret; if((bVolNum >= REDCONF_VOLUME_COUNT) || !gaDisk[bVolNum].fOpen || (gaDisk[bVolNum].mode == BDEV_O_RDONLY)) { ret = -RED_EINVAL; } else { switch(gaDisk[bVolNum].type) { case BDEVTYPE_RAM_DISK: ret = RamDiskFlush(bVolNum); break; case BDEVTYPE_FILE_DISK: ret = FileDiskFlush(bVolNum); break; case BDEVTYPE_RAW_DISK: ret = RawDiskFlush(bVolNum); break; default: REDERROR(); ret = -RED_EINVAL; break; } } return ret; }
/** @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; }
/** @brief Determine whether an inode number is available. @param ulInode The node number to examine. @param pfFree On successful return, populated with whether the inode number is available (true) or in use (false). @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p pfFree is `NULL`; or @p ulInode is not a valid inode number. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedInodeIsFree( uint32_t ulInode, bool *pfFree) { REDSTATUS ret; if(pfFree == NULL) { REDERROR(); ret = -RED_EINVAL; } else { bool fSlot0Allocated; *pfFree = false; ret = RedInodeBitGet(gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated); if((ret == 0) && !fSlot0Allocated) { bool fSlot1Allocated; ret = RedInodeBitGet(gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated); if((ret == 0) && !fSlot1Allocated) { *pfFree = true; } } } return ret; }
/** @brief Discard a range of buffers, marking them invalid. @param ulBlockStart The starting block number to discard @param ulBlockCount The number of blocks, starting at @p ulBlockStart, to discard. Must not be zero. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL Invalid parameters. @retval -RED_EBUSY A block in the desired range is referenced. */ REDSTATUS RedBufferDiscardRange( uint32_t ulBlockStart, uint32_t ulBlockCount) { REDSTATUS ret = 0; if( (ulBlockStart >= gpRedVolume->ulBlockCount) || ((gpRedVolume->ulBlockCount - ulBlockStart) < ulBlockCount) || (ulBlockCount == 0U)) { REDERROR(); ret = -RED_EINVAL; } else { uint8_t bIdx; for(bIdx = 0U; bIdx < REDCONF_BUFFER_COUNT; bIdx++) { BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx]; if( (pHead->bVolNum == gbRedVolNum) && (pHead->ulBlock != BBLK_INVALID) && (pHead->ulBlock >= ulBlockStart) && (pHead->ulBlock < (ulBlockStart + ulBlockCount))) { if(pHead->bRefCount == 0U) { pHead->ulBlock = BBLK_INVALID; BufferMakeLRU(bIdx); } else { /* This should never happen. There are three general cases when this function is used: 1) Discarding every block, as happens during unmount and at the end of format. There should no longer be any referenced buffers at those points. 2) Discarding a block which has become free. All buffers for such blocks should be put or branched beforehand. 3) Discarding of blocks that were just written straight to disk, leaving stale data in the buffer. The write code should never reference buffers for these blocks, since they would not be needed or used. */ CRITICAL_ERROR(); ret = -RED_EBUSY; break; } } } } return ret; }
/** @brief Uninitialize the mutex. The behavior of calling this function when the mutex is not initialized is undefined; likewise, the behavior of uninitializing the mutex when it is in the acquired state is undefined. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. */ REDSTATUS RedOsMutexUninit(void) { REDSTATUS ret; REDERROR(); ret = -RED_ENOSYS; 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 Mark a buffer as most recently used. @param bIdx The index of the buffer to make MRU. */ static void BufferMakeMRU( uint8_t bIdx) { if(bIdx >= REDCONF_BUFFER_COUNT) { REDERROR(); } else if(bIdx != gBufCtx.abMRU[0U]) { uint8_t bMruIdx; /* Find the current position of the buffer in the MRU array. We do not need to check the first slot, since we already know from the above check that the index is not there. */ for(bMruIdx = 1U; bMruIdx < REDCONF_BUFFER_COUNT; bMruIdx++) { if(bIdx == gBufCtx.abMRU[bMruIdx]) { break; } } if(bMruIdx < REDCONF_BUFFER_COUNT) { /* Move the buffer index to the front of the MRU array, making it the MRU buffer. */ RedMemMove(&gBufCtx.abMRU[1U], &gBufCtx.abMRU[0U], bMruIdx); gBufCtx.abMRU[0U] = bIdx; } else { REDERROR(); } } else { /* Buffer already MRU, nothing to do. */ } }
/** @brief Determine which copy of the inode is currently writeable. @param ulInode The inode number to examine. @param pbWhich On successful return, populated with which copy of the inode (either 0 or 1) is writeable. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode number. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS InodeGetWriteableCopy( uint32_t ulInode, uint8_t *pbWhich) { REDSTATUS ret; if(pbWhich == NULL) { REDERROR(); ret = -RED_EINVAL; } else { bool fSlot0Allocated; /* The writeable inode slot is the one which is free in the committed state, so query the committed state metaroot. */ ret = RedInodeBitGet(1U - gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated); if(ret == 0) { if(!fSlot0Allocated) { *pbWhich = 0U; } else { bool fSlot1Allocated; ret = RedInodeBitGet(1U - gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated); if(ret == 0) { if(!fSlot1Allocated) { *pbWhich = 1U; } else { /* Both inode slots were allocated, which should never happen. */ CRITICAL_ERROR(); ret = -RED_EFUBAR; } } } } } return ret; }
/** @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 Copy memory from one address to another. The source and destination memory buffers should not overlap. If the buffers overlap, use RedMemMove() instead. @param pDest The destination buffer. @param pSrc The source buffer. @param ulLen The number of bytes to copy. */ void RedMemCpy( void *pDest, const void *pSrc, uint32_t ulLen) { if((pDest == NULL) || (pSrc == NULL)) { REDERROR(); } else { RedMemCpyUnchecked(pDest, pSrc, ulLen); } }
/** @brief Find a free inode number. @param pulInode On successful return, populated with a free inode number. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p pulInode is `NULL`. @retval -RED_EIO A disk I/O error occurred. @retval -RED_ENFILE No available inode numbers. */ static REDSTATUS InodeFindFree( uint32_t *pulInode) { REDSTATUS ret; if(pulInode == NULL) { REDERROR(); ret = -RED_EINVAL; } else if(gpRedMR->ulFreeInodes == 0U) { ret = -RED_ENFILE; } else { uint32_t ulInode; ret = 0; for(ulInode = INODE_FIRST_FREE; ulInode < (INODE_FIRST_VALID + gpRedVolConf->ulInodeCount); ulInode++) { bool fFree; ret = RedInodeIsFree(ulInode, &fFree); if((ret != 0) || fFree) { break; } } if(ret == 0) { if(ulInode < (INODE_FIRST_VALID + gpRedVolConf->ulInodeCount)) { *pulInode = ulInode; } else { /* If gpRedMR->ulFreeInodes > 0, we should have found an inode. */ CRITICAL_ERROR(); ret = -RED_ENFILE; } } } return ret; }
/** @brief Determine which copy of the inode is current. @param ulInode The inode number to examine. @param pbWhich On successful return, populated with which copy of the inode (either 0 or 1) is current. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EBADF @p ulInode is an unallocated inode number. @retval -RED_EINVAL @p pbWhich is `NULL`; or ulInode is not a valid inode number. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS InodeGetCurrentCopy( uint32_t ulInode, uint8_t *pbWhich) { REDSTATUS ret; if(pbWhich == NULL) { REDERROR(); ret = -RED_EINVAL; } else { bool fSlot0Allocated; /* The current inode slot is the one which is allocated in the working state metaroot. */ ret = RedInodeBitGet(gpRedCoreVol->bCurMR, ulInode, 0U, &fSlot0Allocated); if(ret == 0) { if(fSlot0Allocated) { *pbWhich = 0U; } else { bool fSlot1Allocated; ret = RedInodeBitGet(gpRedCoreVol->bCurMR, ulInode, 1U, &fSlot1Allocated); if(ret == 0) { if(fSlot1Allocated) { *pbWhich = 1U; } else { /* Neither slot for this inode was allocated, so the inode is actually free. */ ret = -RED_EBADF; } } } } } return ret; }
/** @brief Initialize a buffer with the specified byte value. @param pDest The buffer to initialize. @param bVal The byte value with which to initialize @p pDest. @param ulLen The number of bytes to initialize. */ void RedMemSet( void *pDest, uint8_t bVal, uint32_t ulLen) { if(pDest == NULL) { REDERROR(); } else { RedMemSetUnchecked(pDest, bVal, ulLen); } }
/** @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); } }
/** @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; }
/** @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; }
/** @brief Mark a buffer dirty @param pBuffer The buffer to mark dirty. */ void RedBufferDirty( const void *pBuffer) { uint8_t bIdx; if(!BufferToIdx(pBuffer, &bIdx)) { REDERROR(); } else { REDASSERT(gBufCtx.aHead[bIdx].bRefCount > 0U); gBufCtx.aHead[bIdx].uFlags |= BFLAG_DIRTY; } }
/** @brief Uninitialize a block device. This function is called when the file system no longer needs access to a block device. If any resource were allocated by RedOsBDevOpen() to service block device requests, they should be freed at this time. Upon successful return, the block device must be in such a state that it can be opened again. The behavior of calling this function on a block device which is already closed is undefined. @param bVolNum The volume number of the volume whose block device is being uninitialized. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p bVolNum is an invalid volume number. */ REDSTATUS RedOsBDevClose( uint8_t bVolNum) { REDSTATUS ret; if(bVolNum >= REDCONF_VOLUME_COUNT) { ret = -RED_EINVAL; } else { REDERROR(); ret = -RED_ENOSYS; } return ret; }
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 Put all buffers in the cached inode structure except for the inode node buffer. @param pInode A pointer to the cached inode structure. */ void RedInodePutCoord( CINODE *pInode) { if(pInode == NULL) { REDERROR(); } else { RedInodePutData(pInode); #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES RedInodePutIndir(pInode); #endif #if DINDIR_POINTERS > 0U RedInodePutDindir(pInode); #endif } }
/** @brief Put the indirect buffer. @param pInode A pointer to the cached inode structure. */ void RedInodePutIndir( CINODE *pInode) { if(pInode == NULL) { REDERROR(); } else if(pInode->pIndir != NULL) { RedBufferPut(pInode->pIndir); pInode->pIndir = NULL; } else { /* No indirect buffer, nothing to put. */ } }
/** @brief Put the inode data buffer. @param pInode A pointer to the cached inode structure. */ void RedInodePutData( CINODE *pInode) { if(pInode == NULL) { REDERROR(); } else if(pInode->pbData != NULL) { RedBufferPut(pInode->pbData); pInode->pbData = NULL; } else { /* No data buffer, nothing to put. */ } }
/** @brief Determine if an inode is branched. @param ulInode The inode number to examine. @param pfIsBranched On successful return, populated with whether the inode is branched. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL @p pInode is `NULL`; or @p ulInode is not a valid inode number. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS InodeIsBranched( uint32_t ulInode, bool *pfIsBranched) { REDSTATUS ret; if(!INODE_IS_VALID(ulInode) || (pfIsBranched == NULL)) { REDERROR(); ret = -RED_EINVAL; } else { ALLOCSTATE state; ret = RedImapBlockState(InodeBlock(ulInode, 0U), &state); if(ret == 0) { if(state == ALLOCSTATE_NEW) { *pfIsBranched = true; } else { ret = RedImapBlockState(InodeBlock(ulInode, 1U), &state); if(ret == 0) { if(state == ALLOCSTATE_NEW) { *pfIsBranched = true; } else { *pfIsBranched = false; } } } } } return ret; }