/** @brief Mount an existing inode. Will populate all fields of the cached inode structure, except those which are populated during seek. @param pInode A pointer to the cached inode structure. The pInode->ulInode field must already be initialized with the inode number to mount. All other fields will be discarded. @param type The expected inode type. @param fBranch Whether to branch the inode. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL Invalid parameters. @retval -RED_EROFS @p fBranch is true but the driver is read-only. @retval -RED_EIO A disk I/O error occurred. @retval -RED_EBADF The inode number is free; or the inode number is not valid. @retval -RED_EISDIR @p type is ::FTYPE_FILE and the inode is a directory. @retval -RED_ENOTDIR @p type is ::FTYPE_DIR and the inode is a file. */ REDSTATUS RedInodeMount( CINODE *pInode, FTYPE type, bool fBranch) { REDSTATUS ret = 0; if(pInode == NULL) { ret = -RED_EINVAL; } else if(!INODE_IS_VALID(pInode->ulInode)) { ret = -RED_EBADF; } #if REDCONF_API_FSE == 1 else if(type == FTYPE_DIR) { REDERROR(); ret = -RED_EINVAL; } #endif #if REDCONF_READ_ONLY == 1 else if(fBranch) { REDERROR(); ret = -RED_EROFS; } #endif else { uint32_t ulInode = pInode->ulInode; uint8_t bWhich = 0U; /* Init'd to quiet warnings. */ RedMemSet(pInode, 0U, sizeof(*pInode)); pInode->ulInode = ulInode; ret = InodeGetCurrentCopy(pInode->ulInode, &bWhich); if(ret == 0) { ret = RedBufferGet(InodeBlock(pInode->ulInode, bWhich), BFLAG_META_INODE, CAST_VOID_PTR_PTR(&pInode->pInodeBuf)); } #if REDCONF_READ_ONLY == 0 if(ret == 0) { ret = InodeIsBranched(pInode->ulInode, &pInode->fBranched); } #endif if(ret == 0) { if(RED_S_ISREG(pInode->pInodeBuf->uMode)) { #if REDCONF_API_POSIX == 1 pInode->fDirectory = false; if(type == FTYPE_DIR) { ret = -RED_ENOTDIR; } #endif } #if REDCONF_API_POSIX == 1 else if(RED_S_ISDIR(pInode->pInodeBuf->uMode)) { pInode->fDirectory = true; if(type == FTYPE_FILE) { ret = -RED_EISDIR; } } #endif else { /* Missing or unsupported inode type. */ CRITICAL_ERROR(); ret = -RED_EFUBAR; } } #if REDCONF_READ_ONLY == 0 if((ret == 0) && fBranch) { ret = RedInodeBranch(pInode); } #endif if(ret != 0) { RedInodePut(pInode, 0U); } } 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; }