/** @brief Mount the master block. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO Master block missing, corrupt, or inconsistent with the compile-time driver settings. */ REDSTATUS RedVolMountMaster(void) { REDSTATUS ret; MASTERBLOCK *pMB; /* Read the master block, to ensure that the disk was formatted with Reliance Edge. */ ret = RedBufferGet(BLOCK_NUM_MASTER, BFLAG_META_MASTER, CAST_VOID_PTR_PTR(&pMB)); if(ret == 0) { /* Verify that the driver was compiled with the same settings that the disk was formatted with. If not, the user has made a mistake: either the driver settings are wrong, or the disk needs to be reformatted. */ if( (pMB->ulVersion != RED_DISK_LAYOUT_VERSION) || (pMB->ulInodeCount != gpRedVolConf->ulInodeCount) || (pMB->ulBlockCount != gpRedVolume->ulBlockCount) || (pMB->uMaxNameLen != REDCONF_NAME_MAX) || (pMB->uDirectPointers != REDCONF_DIRECT_POINTERS) || (pMB->uIndirectPointers != REDCONF_INDIRECT_POINTERS) || (pMB->bBlockSizeP2 != BLOCK_SIZE_P2) || (((pMB->bFlags & MBFLAG_API_POSIX) != 0U) != (REDCONF_API_POSIX == 1)) || (((pMB->bFlags & MBFLAG_INODE_TIMESTAMPS) != 0U) != (REDCONF_INODE_TIMESTAMPS == 1)) || (((pMB->bFlags & MBFLAG_INODE_BLOCKS) != 0U) != (REDCONF_INODE_BLOCKS == 1))) { ret = -RED_EIO; } #if REDCONF_API_POSIX == 1 else if(((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U) != (REDCONF_API_POSIX_LINK == 1)) { ret = -RED_EIO; } #else else if((pMB->bFlags & MBFLAG_INODE_NLINK) != 0U) { ret = -RED_EIO; } #endif else { /* Master block configuration is valid. Save the sequence number of the master block in the volume, since we need it later (see RedVolMountMetaroot()) and we do not want to re-buffer the master block. */ gpRedVolume->ullSequence = pMB->hdr.ullSequence; } RedBufferPut(pMB); } return ret; }
/** @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 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 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 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 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 Create an inode. @param pInode Pointer to the cached inode structure. If pInode->ulInode is #INODE_INVALID, a free inode will be found; otherwise, pInode->ulInode will be the inode number (an error will be returned if it is not free). @param ulPInode The parent inode number. @param uMode The inode mode. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EBADF pInode->ulInode is an invalid inode number other than #INODE_INVALID. @retval -RED_EINVAL Invalid parameters. @retval -RED_EEXIST Tried to create an inode with an inode number that is already in use. @retval -RED_ENFILE All inode slots are already in use. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedInodeCreate( CINODE *pInode, uint32_t ulPInode, uint16_t uMode) { REDSTATUS ret; #if REDCONF_API_POSIX == 1 /* ulPInode must be a valid inode number, unless we are creating the root directory, in which case ulPInode must be INODE_INVALID (the root directory has no parent). */ if( (pInode == NULL) || (!INODE_IS_VALID(ulPInode) && ((ulPInode != INODE_INVALID) || (pInode->ulInode != INODE_ROOTDIR)))) #else if(pInode == NULL) #endif { REDERROR(); ret = -RED_EINVAL; } else { uint32_t ulInode = pInode->ulInode; RedMemSet(pInode, 0U, sizeof(*pInode)); #if REDCONF_API_POSIX == 1 if(ulInode == INODE_INVALID) { /* Caller requested that an inode number be allocated. Search for an unused inode number, error if there isn't one. */ ret = InodeFindFree(&pInode->ulInode); } else #endif { /* Caller requested creation of a specific inode number. Make sure it's valid and doesn't already exist. */ if(INODE_IS_VALID(ulInode)) { bool fFree; ret = RedInodeIsFree(ulInode, &fFree); if(ret == 0) { if(fFree) { pInode->ulInode = ulInode; } else { ret = -RED_EEXIST; } } } else { ret = -RED_EBADF; } } if(ret == 0) { uint8_t bWriteableWhich; ret = InodeGetWriteableCopy(pInode->ulInode, &bWriteableWhich); if(ret == 0) { ret = RedBufferGet(InodeBlock(pInode->ulInode, bWriteableWhich), (uint16_t)((uint32_t)BFLAG_META_INODE | BFLAG_DIRTY | BFLAG_NEW), CAST_VOID_PTR_PTR(&pInode->pInodeBuf)); if(ret == 0) { /* Mark the inode block as allocated. */ ret = InodeBitSet(pInode->ulInode, bWriteableWhich, true); if(ret != 0) { RedBufferPut(pInode->pInodeBuf); } } } } if(ret == 0) { #if REDCONF_INODE_TIMESTAMPS == 1 uint32_t ulNow = RedOsClockGetTime(); pInode->pInodeBuf->ulATime = ulNow; pInode->pInodeBuf->ulMTime = ulNow; pInode->pInodeBuf->ulCTime = ulNow; #endif pInode->pInodeBuf->uMode = uMode; #if REDCONF_API_POSIX == 1 #if REDCONF_API_POSIX_LINK == 1 pInode->pInodeBuf->uNLink = 1U; #endif pInode->pInodeBuf->ulPInode = ulPInode; #else (void)ulPInode; #endif pInode->fBranched = true; pInode->fDirty = true; #if REDCONF_API_POSIX == 1 gpRedMR->ulFreeInodes--; #endif } } return ret; }
/** @brief Format a file system volume. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EBUSY Volume is mounted. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedVolFormat(void) { REDSTATUS ret; if(gpRedVolume->fMounted) { ret = -RED_EBUSY; } else { ret = RedOsBDevOpen(gbRedVolNum, BDEV_O_RDWR); } if(ret == 0) { MASTERBLOCK *pMB; REDSTATUS ret2; /* Overwrite the master block with zeroes, so that if formatting is interrupted, the volume will not be mountable. */ ret = RedBufferGet(BLOCK_NUM_MASTER, BFLAG_NEW | BFLAG_DIRTY, CAST_VOID_PTR_PTR(&pMB)); if(ret == 0) { ret = RedBufferFlush(BLOCK_NUM_MASTER, 1U); RedBufferDiscard(pMB); } if(ret == 0) { ret = RedIoFlush(gbRedVolNum); } #if REDCONF_IMAP_EXTERNAL == 1 if((ret == 0) && !gpRedCoreVol->fImapInline) { uint32_t ulImapBlock; uint32_t ulImapBlockLimit = gpRedCoreVol->ulImapStartBN + (gpRedCoreVol->ulImapNodeCount * 2U); uint16_t uImapFlags = (uint16_t)((uint32_t)BFLAG_META_IMAP | BFLAG_NEW | BFLAG_DIRTY); /* Technically it is only necessary to create one copy of each imap node (the copy the metaroot points at), but creating them both avoids headaches during disk image analysis from stale imaps left over from previous formats. */ for(ulImapBlock = gpRedCoreVol->ulImapStartBN; ulImapBlock < ulImapBlockLimit; ulImapBlock++) { IMAPNODE *pImap; ret = RedBufferGet(ulImapBlock, uImapFlags, CAST_VOID_PTR_PTR(&pImap)); if(ret != 0) { break; } RedBufferPut(pImap); } } #endif /* Write the first metaroot. */ if(ret == 0) { RedMemSet(gpRedMR, 0U, sizeof(*gpRedMR)); gpRedMR->ulFreeBlocks = gpRedVolume->ulBlocksAllocable; #if REDCONF_API_POSIX == 1 gpRedMR->ulFreeInodes = gpRedVolConf->ulInodeCount; #endif gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN; /* The branched flag is typically set automatically when bits in the imap change. It is set here explicitly because the imap has only been initialized, not changed. */ gpRedCoreVol->fBranched = true; ret = RedVolTransact(); } #if REDCONF_API_POSIX == 1 /* Create the root directory. */ if(ret == 0) { CINODE rootdir; rootdir.ulInode = INODE_ROOTDIR; ret = RedInodeCreate(&rootdir, INODE_INVALID, RED_S_IFDIR); if(ret == 0) { RedInodePut(&rootdir, 0U); } } #endif #if REDCONF_API_FSE == 1 /* The FSE API does not support creating or deletes files, so all the inodes are created during setup. */ if(ret == 0) { uint32_t ulInodeIdx; for(ulInodeIdx = 0U; ulInodeIdx < gpRedVolConf->ulInodeCount; ulInodeIdx++) { CINODE ino; ino.ulInode = INODE_FIRST_FREE + ulInodeIdx; ret = RedInodeCreate(&ino, INODE_INVALID, RED_S_IFREG); if(ret == 0) { RedInodePut(&ino, 0U); } } } #endif /* Write the second metaroot. */ if(ret == 0) { ret = RedVolTransact(); } /* Populate and write out the master block. */ if(ret == 0) { ret = RedBufferGet(BLOCK_NUM_MASTER, (uint16_t)((uint32_t)BFLAG_META_MASTER | BFLAG_NEW | BFLAG_DIRTY), CAST_VOID_PTR_PTR(&pMB)); } if(ret == 0) { pMB->ulVersion = RED_DISK_LAYOUT_VERSION; RedStrNCpy(pMB->acBuildNum, RED_BUILD_NUMBER, sizeof(pMB->acBuildNum)); pMB->ulFormatTime = RedOsClockGetTime(); pMB->ulInodeCount = gpRedVolConf->ulInodeCount; pMB->ulBlockCount = gpRedVolume->ulBlockCount; pMB->uMaxNameLen = REDCONF_NAME_MAX; pMB->uDirectPointers = REDCONF_DIRECT_POINTERS; pMB->uIndirectPointers = REDCONF_INDIRECT_POINTERS; pMB->bBlockSizeP2 = BLOCK_SIZE_P2; #if REDCONF_API_POSIX == 1 pMB->bFlags |= MBFLAG_API_POSIX; #endif #if REDCONF_INODE_TIMESTAMPS == 1 pMB->bFlags |= MBFLAG_INODE_TIMESTAMPS; #endif #if REDCONF_INODE_BLOCKS == 1 pMB->bFlags |= MBFLAG_INODE_BLOCKS; #endif #if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1) pMB->bFlags |= MBFLAG_INODE_NLINK; #endif ret = RedBufferFlush(BLOCK_NUM_MASTER, 1U); RedBufferPut(pMB); } if(ret == 0) { ret = RedIoFlush(gbRedVolNum); } ret2 = RedOsBDevClose(gbRedVolNum); if(ret == 0) { ret = ret2; } } /* Discard the buffers so a subsequent format will not run into blocks it does not expect. */ if(ret == 0) { ret = RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount); } return ret; }