Example #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;
}
Example #2
0
/** @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;
}
Example #3
0
/** @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;
}
Example #4
0
/** @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;
}
Example #5
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;
}
Example #6
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;
}