/** @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 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; }