Exemple #1
0
/** @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;
}
Exemple #2
0
/** @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;
}
Exemple #5
0
/** @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.
        */
    }
}
Exemple #6
0
/** @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.
        */
    }
}
Exemple #7
0
/** @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;
}
Exemple #8
0
/** @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;
}