⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 buffer.c

📁 从大量的wince源代码中剥离出的fat文件系统源代码.移植性非常高. 里面带有source i.rar
💻 C
📖 第 1 页 / 共 4 页
字号:
 *      pos - file position of data being requested
 *      lenMod - length of data to be modified (0 if not modifying)
 *      *ppvStart - address of pvStart variable to set to start of data
 *      *ppvEnd - address of pvEnd variable to set to end of buffer (optional)
 *
 *  EXIT
 *      ERROR_SUCCESS (0) if successful, non-zero if not.
 *
 *      The buffer containing the data is left held.  It can be explicitly
 *      unheld (eg, via ReleaseStreamBuffer or CommitAndReleaseStreamBuffers),
 *      or implicitly by another ReadStreamBuffer;  if a second ReadStreamBuffer
 *      returns a pointer to the same buffer, the buffer remains held, but the
 *      hold count is not advanced.  The idea is to relieve callers from having
 *      to pair all (or any of) their reads with unholds.
 *
 *  NOTES
 *      Callers promise that the requested POSITION (pos) lies within
 *      the current RUN (pstm->s_run).  The current RUN starts at some
 *      BLOCK (s_run.r_blk).  If the data is buffered, it will be in
 *      a buffer containing block ((pos - s_run.r_start)/BLOCK_SIZE + r_blk).
 */

DWORD ReadStreamBuffer(PDSTREAM pstm, DWORD pos, int lenMod, PVOID *ppvStart, PVOID *ppvEnd)
{
    PBUF pbuf;
    DWORD dwError;
    DWORD blk, blkBuf, offset;
    PBYTE pdata, pdataEnd;
    PVOLUME pvol = pstm->s_pvol;

    if (pos < pstm->s_run.r_start || pos >= pstm->s_run.r_end) {
        DEBUGMSGBREAK(ZONE_INIT || ZONE_ERRORS,
                 (DBGTEXT("FATFS!ReadStreamBuffer: bad position (%d) for stream %.11hs\n"), pos, pstm->s_achOEM));
        return ERROR_INVALID_DATA;
    }

    // Compute the stream-relative offset
    // and the corresponding volume-relative block

    offset = pos - pstm->s_run.r_start;
    blk = pstm->s_run.r_blk + (offset >> BLOCK_LOG2);

    // Compute the offset into the volume-relative block

    offset &= BLOCK_OFFSET_MASK;


    // Convert the volume-relative block # into a buffer-granular block #

    blkBuf = blk & maskBufBlock;

    // Find (or create) a buffer that starts with that buffer-granular
    // block number.  If lenMod >= cbBuf and blkBuf == blk and offset == 0,
    // then FindBuffer does not need to actuall fill the buffer with data from
    // the volume, because the entire buffer is to be modified anyway.

    DEBUGMSG(ZONE_LOGIO && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!ReadStreamBuffer(DP): begin findbuffer\n")));
    dwError = FindBuffer(pvol, blkBuf, pstm,
                         (lenMod >= (int)cbBuf && blkBuf == blk && offset == 0),
                         &pbuf);
    DEBUGMSG(ZONE_LOGIO && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!ReadStreamBuffer(DP): end findbuffer\n")));

    if (!dwError) {

        pdata = pbuf->b_pdata + ((blk - blkBuf) << BLOCK_LOG2) + offset;
        *ppvStart = pdata;

        // There's data all the way up to (buf_pdata + cbBuf-1), but it's
        // not necessarily VALID data.  We need to limit it to the amount of data
        // specified in the RUN.

        if (ppvEnd) {
            pdataEnd = pdata + (pstm->s_run.r_end - pos);
            if (pdataEnd > pbuf->b_pdata + cbBuf)
                pdataEnd = pbuf->b_pdata + cbBuf;
            *ppvEnd = pdataEnd;
        }
    }
    return dwError;
}


/*  ModifyStreamBuffer
 *
 *  ENTRY
 *      pstm - pointer to stream
 *      pMod - pointer to modification
 *      cbMod - length of modification (ZERO to prevent logging)
 *
 *  EXIT
 *      ERROR_SUCCESS if the stream's buffer is allowed to be modified, otherwise
 *      some error code (eg, ERROR_WRITE_PROTECT).
 */

DWORD ModifyStreamBuffer(PDSTREAM pstm, PVOID pMod, int cbMod)
{
    PBYTE pbMod = pMod;
    PBUF pbuf = pstm->s_pbufCur;
    
    // A just-dirtied buffer should also be a held buffer

    DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!ModifyStreamBuffer(block %d, stream %.11hs)\n"), pbuf->b_blk, pstm->s_achOEM));

    ASSERT(pbuf);
    ASSERT(pstm->s_flags & STF_BUFCURHELD);

    // Make sure the specified address lies within the buffer's range,
    // and then adjust the length if it extends past the end of the buffer

    ASSERT(pbMod >= pbuf->b_pdata && pbMod <= pbuf->b_pdata+cbBuf);

    if (cbMod > 0 && pbMod+cbMod > pbuf->b_pdata+cbBuf)
        cbMod = (pbuf->b_pdata+cbBuf) - pbMod;

    // If this stream contains file system data structures (FAT or directory info),
    // then pass the region being modified to ModifyBuffer so that it can do appropriate
    // buffer logging;  otherwise, pass a zero length to indicate that no logging
    // is required.

    return ModifyBuffer(pbuf, pbMod, (pstm->s_flags & STF_VOLUME)? cbMod : 0);
}




/*  CommitBufferSet
 *
 *  ENTRY
 *      pstm - pointer to stream if iCommit >= 0;
 *             pointer to volume (or NULL) if iCommit < 0
 *
 *      if iCommit >= 0 (ie, pstm points to stream):
 *          1 means release stream buffer; 0 means retain
 *
 *      if iCommit < 0 (ie, pstm points to volume, or NULL):
 *          -1 means commit everything not held;
 *          -2 means commit everything not held older than msWriteDelayMin
 *
 *  EXIT
 *      ERROR_SUCCESS (0) if successful, non-zero if not.
 *
 *  NOTES
 *      The assumption we make here is that a dirty buffer that is
 *      currently held will be explicitly committed once it is unheld,
 *      so we leave it alone.  Besides, the buffer could be in a
 *      transitional/incomplete state.
 *
 *      This doesn't mean that UnholdBuffer will do the commit for you
 *      either (because it won't).  Callers are allowed to hold/unhold
 *      dirtied buffers repeatedly without incuring the overhead of a
 *      write until they choose to do so, but they must choose to do so
 *      EVENTUALLY, in order to minimize the periods of time where the
 *      file system is in an inconsistent state.
 */

DWORD CommitBufferSet(PDSTREAM pstm, int iCommit)
{
    DWORD dw, dwError;
    PBUF pbuf, pbufEnd;

    // If the buffer pool hasn't been initialized yet, feign success

    if (cBufVolumes == 0)
        return ERROR_SUCCESS;


    dwError = ERROR_SUCCESS;
    EnterCriticalSection(&csBuffers);

    // If the caller is done with the stream's current buffer, then release
    // it first, so that the logic below (to commit only those buffers that are
    // no longer held) does the right thing.
    
    if (iCommit > 0 && pstm)
        dwError = ReleaseStreamBuffer(pstm, TRUE);

    pbuf = dlBufMRU.pbufNext;
    pbufEnd = (PBUF)&dlBufMRU;

    while (pbuf != pbufEnd) {

        // If you didn't want to release the current buffer (ie, you want to
        // commit it even though you -- or someone else -- is still holding it)
        // then we'll commit ALL the stream's buffers (held or not), on the premise
        // that what you're *really* trying to do is get the entire stream into a
        // consistent state (eg, a directory stream that was both modified and
        // grown).

        if (!pstm ||
            iCommit >= 0 && pbuf->b_pstm == pstm ||
            iCommit <  0 && pbuf->b_pvol == (PVOLUME)pstm) {

            BOOL fCommit = (iCommit == 0 || !HeldBuffer(pbuf));

            if (fCommit) {
                dw = CommitBuffer(pbuf, TRUE);
                if (!dwError)
                    dwError = dw;
            }
        }
        pbuf = pbuf->b_dlink.pbufNext;
    }

    LeaveCriticalSection(&csBuffers);

    return dwError;
}


/*  InvalidateBufferSet - Invalidate all buffers for specified volume
 *
 *  ENTRY
 *      pvol -> VOLUME structure
 *      fAll == FALSE to invalidate only clean buffers, TRUE to also invalidate dirty ones
 *
 *  EXIT
 *      None
 */

void InvalidateBufferSet(PVOLUME pvol, BOOL fAll)
{
    BOOL fDirty;
    PBUF pbuf, pbufEnd;

    EnterCriticalSection(&csBuffers);

    fDirty = FALSE;
    pbuf = dlBufMRU.pbufNext;
    pbufEnd = (PBUF)&dlBufMRU;

    while (pbuf != pbufEnd) {

        // If this buffer contains data that belonged to the specified
        // volume, and no thread is using that data, invalidate the buffer.

        if (pbuf->b_pvol == pvol) {
            
            BOOL fInvalidate = !HeldBuffer(pbuf) && (fAll || !(pbuf->b_flags & BUF_DIRTY));

            DEBUGMSG(fInvalidate && (pbuf->b_flags & BUF_DIRTY), (DBGTEXT("FATFS!InvalidateBufferSet: dirty buffer for block %d being invalidated!\n"), pbuf->b_blk));

            // The only reason we hold the buffer is so that CleanBuffer
            // won't generate a bogus assertion.  We don't care about the
            // data anymore, so it is otherwise pointless.

            if (fInvalidate) {
                HoldBuffer(pbuf);
                CleanBuffer(pbuf);
                UnholdBuffer(pbuf);
                pbuf->b_pvol = NULL;    // invalidated!
            }
            else if (pbuf->b_flags & BUF_DIRTY)
                fDirty = TRUE;
        }
        pbuf = pbuf->b_dlink.pbufNext;
    }

    // If all dirty buffers were invalidated, the volume should no longer be considered dirty.

    if (!fDirty)
        pvol->v_flags &= ~VOLF_DIRTY;

    LeaveCriticalSection(&csBuffers);
}


/*  LockBlocks - Lock a range of blocks for direct (non-buffered) I/O
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      pos - file position of data being requested
 *      len - length of data being requested
 *      pblk - pointer to first unbuffered block (if successful)
 *      pcblk - pointer to count of unbuffered blocks (if successful)
 *      fWrite - FALSE if locking for read, TRUE if write
 *
 *  EXIT
 *      TRUE if successful (ie, the request starts at a buffer-granular
 *      block number and spans some multiple of blocks-per-buffer, none
 *      of which are currently buffered), FALSE if not.
 *
 *  NOTES
 *      If the call returns TRUE, then the caller can issue ReadVolume or
 *      WriteVolume directly.  The buffer critical section is left held on
 *      return for writes only -- to avoid races that might result in
 *      buffer pool inconsistency -- so it is critical that UnlockBlocks(TRUE)
 *      be called when done writing a range of blocks.
 *
 *      As with ReadStreamBuffer, callers promise that the requested POSITION
 *      (pos) lies within the current RUN (pstm->s_run).  The current RUN
 *      starts at some BLOCK (s_run.r_blk).  If the data is buffered, it will
 *      be in a buffer containing block ((pos - s_run.r_start)/BLOCK_SIZE + r_blk).
 */

BOOL LockBlocks(PDSTREAM pstm, DWORD pos, DWORD len, DWORD *pblk, DWORD *pcblk, BOOL fWrite)
{
    PBUF pbuf, pbufEnd;
    DWORD blk, blkBuf, blkBufEnd;
    DWORD offset, cblks;
    PVOLUME pvol = pstm->s_pvol;

    if (pos < pstm->s_run.r_start || pos >= pstm->s_run.r_end) {
        DEBUGMSGBREAK(ZONE_INIT || ZONE_ERRORS,
                 (DBGTEXT("FATFS!LockBlocks: bad position (%d) for stream %.11hs\n"), pos, pstm->s_achOEM));
        return FALSE;
    }

    // Compute the stream-relative offset
    // and the corresponding volume-relative block

    offset = pos - pstm->s_run.r_start;
    blk = pstm->s_run.r_blk + (offset >> BLOCK_LOG2);

    // Limit the size of the request to what exists in the current run

    if (len > pstm->s_run.r_end - pos)
        len = pstm->s_run.r_end - pos;

    // Convert the volume-relative block # into a buffer-granular block #

    blkBuf = blk & maskBufBlock;
    if (blkBuf != blk || (offset & BLOCK_OFFSET_MASK) || (cblks = len >> BLOCK_LOG2) < cblkBuf) {

        // The request does not start at the beginning of a buffer-granular
        // block, or does not start at the beginning of that block, or is
        // smaller the number of blocks in a buffer.

        
        return FALSE;
    }

    // Compute an ending buffer-granular block #, by adding cblks after
    // it has been truncated to the number of blocks in a buffer.

    blkBufEnd = blkBuf + (cblks & ~(cblkBuf-1));

    // Now we just need to find the largest cblkBuf chunk of blocks that
    // is not currently buffered.

    EnterCriticalSection(&csBuffers);

    pbuf = dlBufMRU.pbufNext;
    pbufEnd = (PBUF)&dlBufMRU;

    while (pbuf != pbufEnd) {

        if (pbuf->b_pvol && pbuf->b_pvol == pvol) {

            // If the first block is buffered, then fail the request.

            if (pbuf->b_blk == blkBuf) {
                blkBuf = blkBufEnd;
                break;
            }

            // If this buffer contains some information inside the range
            // being requested, then shrink the range down.

            if (pbuf->b_blk > blkBuf && pbuf->b_blk < blkBufEnd) {
                blkBufEnd = pbuf->b_blk;
            }
        }
        pbuf = pbuf->b_dlink.pbufNext;
    }

    if (blkBuf != blkBufEnd) {
        *pblk = blkBuf;
        *pcblk = blkBufEnd - blkBuf;
        if (!fWrite)
            LeaveCriticalSection(&csBuffers);
        return TRUE;
    }

    LeaveCriticalSection(&csBuffers);
    return FALSE;
}


void UnlockBlocks(DWORD blk, DWORD cblk, BOOL fWrite)
{
    if (fWrite)
        LeaveCriticalSection(&csBuffers);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -