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

📄 buffer.c

📁 Windows操作系统中文件系统过滤驱动和设备驱动之间的相似
💻 C
📖 第 1 页 / 共 4 页
字号:

DWORD FindBuffer(PVOLUME pvol, DWORD blk, PDSTREAM pstm, BOOL fNoRead, PBUF *ppbuf)
{
    PBUF pbuf, pbufOld, pbufEnd;
    DWORD dwError = ERROR_SUCCESS;

    DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): begin entercritsec\r\n")));
    EnterCriticalSection(&pvol->v_csBuffers);
    DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end entercritsec\r\n")));

    DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!FindBuffer(block %d, stream %.11hs)\r\n"), blk, pstm? pstm->s_achOEM : "<NONE>"));

    // Make sure the volume is allowed to use the buffer pool

    if (!(pvol->v_flags & VOLF_BUFFERED)) {
        DEBUGMSG(TRUE,(DBGTEXT("FATFS!FindBuffer: volume not allowed to use buffer pool yet!\r\n")));
        dwError = ERROR_GEN_FAILURE;
        goto error;
    }

    // If an associated stream is specified, the volume better match

    ASSERT(!pstm || pstm->s_pvol == pvol);

    // If the stream has a valid pbufCur, we'll check that buffer
    // first, but otherwise we'll walk the buffers in MRU order.  If we
    // don't find the desired block, then we'll read the data into the
    // oldest unheld buffer we saw.

    if (pstm && (pbuf = pstm->s_pbufCur)) {
        if (!(pbuf->b_flags & BUF_INVALID) && (pbuf->b_blk == blk))
            goto foundLastBuffer;
        ReleaseStreamBuffer(pstm, TRUE);        }

    // Search the buffer pool in MRU order, and also keep track of
    // a likely candidate to re-use (pbufOld), in case we don't find what 
    // we're looking for.

restart:
    pbuf = pvol->v_dlBufMRU.pbufNext;
    pbufEnd = (PBUF)&pvol->v_dlBufMRU;

    pbufOld = NULL;
    while (pbuf != pbufEnd) {

        if (!(pbuf->b_flags & (BUF_FREE|BUF_INVALID)) && (pbuf->b_blk == blk)) {

            // An existing buffer might be in the BUF_UNCERTAIN state (ie, if another
            // thread had wanted the same buffer, started reading it, but hadn't finished
            // by the time *we* came along).  In that case, we must block until the
            // BUF_UNCERTAIN flag is cleared.

            while (pbuf->b_flags & BUF_UNCERTAIN) {
                DEBUGMSG(TRUE,(DBGTEXT("FATFS!FindBuffer: waiting for block %d to become certain...\r\n"), pbuf->b_blk));
                Sleep(2000);
            }

            // Furthermore, if an UNCERTAIN buffer could not be read, then we will have
            // invalidated the buffer.  We need to check for that condition
            // now;  it needs to be checked outside the UNCERTAIN loop, since the timing might
            // have been such that we never even saw the UNCERTAIN bit get set.

            if (pbuf->b_flags & BUF_INVALID)
                goto restart;
            else
                goto foundBuffer;
        }

        // If this buffer isn't held, pick it.  The only exception is
        // if we already picked (a newer) one and this (older) one's dirty.
	 // also, if the picked one was dirty, we would want to pick older dirty one

        if ((pbuf->b_flags & BUF_FREE) || (!HeldBuffer(pbuf) && !(pbuf->b_flags & BUF_BUSY))) {
            if (!pbufOld || !(pbuf->b_flags & BUF_DIRTY) || (pbufOld->b_flags & BUF_DIRTY))
                pbufOld = pbuf;
        }

        pbuf = pbuf->b_dlink.pbufNext;
    }

    // There should always be an unheld buffer in the pool (which we
    // now assert).  If there isn't, then MIN_BUFFERS has been set too low
    // (ie, the current thread must already be holding onto at least that
    // many buffers, or some other thread is holding onto more than MIN_BUFFERS).

    if (!pbufOld) {
        DEBUGMSGBREAK(TRUE,(DBGTEXT("FATFS!FindBuffer: buffer pool unexpectedly exhausted!\r\n")));
        dwError = ERROR_GEN_FAILURE;
        goto error;
    }

    pbuf = pbufOld;

    // Make sure any buffer we're about to (re)use has been committed;  the
    // only time we should end up using a dirty buffer is when no clean ones
    // are available, and if we can't commit a particular buffer, we probably
    // can't commit *any* of them, which is why I just give up.

    DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): begin commitbuffer\n")));
    dwError = CommitBuffer(pbuf, TRUE);
    DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end commitbuffer\n")));
    if (dwError)
        goto error;

    // pbuf->b_pvol = pvol;
    pbuf->b_blk = blk;
    pbuf->b_flags &= ~(BUF_FREE|BUF_INVALID);
#ifdef DEBUG
    pbuf->b_refs = 0;
#endif

    // WARNING: The buffer critical section is not held across the actual
    // read, to insure that critical I/O (like demand-paging) can still occur
    // even if other threads start stacking up inside the driver.  Since
    // BUF_UNCERTAIN is set for the duration, no other threads entering FindBuffer
    // for the same block/buffer should continue until we're done here.

    if (!fNoRead) {
        pbuf->b_flags |= BUF_UNCERTAIN | BUF_BUSY;
        LeaveCriticalSection(&pvol->v_csBuffers);
        DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): begin readvolume\r\n")));
        dwError = ReadVolume(pvol, pbuf->b_blk, 1, pbuf->b_pdata);
        DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end readvolume\r\n")));
        if (dwError) {
            // Invalidate the buffer if the data could not be read, and do it *before*
            // clearing BUF_UNCERTAIN, so that anyone waiting on BUF_UNCERTAIN cannot miss
            // the fact that this buffer is now invalid.
            pbuf->b_flags |= BUF_INVALID;
            pbuf->b_flags &= ~(BUF_UNCERTAIN | BUF_BUSY);
            return dwError;
        }
        // We must clear BUF_UNCERTAIN *before* taking csBuffers, because otherwise we could
        // block forever due to another FindBuffer thread waiting for us to clear the same bit
        // on the same buffer.
        pbuf->b_flags &= ~BUF_UNCERTAIN;
        DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): begin entercritsec\r\n")));
        EnterCriticalSection(&pvol->v_csBuffers);
        DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end entercritsec\r\n")));
        // We must clear BUF_BUSY *after* taking csBuffers, because otherwise the buffer is
        // vulnerable to re-use by another thread while (A) we're outside csBuffers and (B) no
        // hold has been applied yet.
        pbuf->b_flags &= ~BUF_BUSY;
    }

    // Move the buffer to MRU position now

  foundBuffer:
    RemoveItem((PDLINK)&pbuf->b_dlink);
    AddItem((PDLINK)&pvol->v_dlBufMRU, (PDLINK)&pbuf->b_dlink);

    // Apply a conventional hold or a stream hold, as appropriate

  foundLastBuffer:
    if (!pstm) {
        HoldBuffer(pbuf);
    }
    else {
        AssignStreamBuffer(pstm, pbuf, TRUE);
    }

#ifdef DEBUG
    pbuf->b_refs++;
#endif

    // Last but not least, return a pointer to the buffer structure

    *ppbuf = pbuf;

  error:
    LeaveCriticalSection(&pvol->v_csBuffers);
    return dwError;
}


/*  AssignStreamBuffer
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      pbuf - pointer to buffer to assign to stream
 *      fCS - TRUE if csBuffers is held, FALSE if not
 *
 *  EXIT
 *      The buffer is held and assigned as the stream's current buffer.
 *
 *  NOTES
 *      Any caller messing with a stream's current buffer must also
 *      currently own the stream's critical section.
 */

void AssignStreamBuffer(PDSTREAM pstm, PBUF pbuf, BOOL fCS)
{
    ASSERT(OWNCRITICALSECTION(&pstm->s_cs));

    // If a buffer is changing hands, commit it first, because
    // CommitStreamBuffers for a given stream will only commit buffers
    // still associated with that stream, and we want to guarantee that
    // a stream is FULLY committed after calling CommitStreamBuffers.

    // We don't care if there's an error, because CommitBuffer no longer
    // invalidates a buffer on error;  it simply marks the buffer dirty again.

    if (pbuf->b_pstm != pstm) {
        if (pstm->s_flags & STF_WRITETHRU)
            CommitBuffer(pbuf, fCS);
    }

    pbuf->b_pstm = pstm;

#ifdef DEBUG
    memcpy(pbuf->b_achName, pstm->s_achOEM, min(sizeof(pbuf->b_achName),sizeof(pstm->s_achOEM)));
#endif

    if (!(pstm->s_flags & STF_BUFCURHELD)) {
        HoldBuffer(pbuf);
        pstm->s_pbufCur = pbuf;
        pstm->s_flags |= STF_BUFCURHELD;
    }
    else
        ASSERT(pstm->s_pbufCur == pbuf);
}


/*  ReleaseStreamBuffer
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      fCS - TRUE if csBuffers is held, FALSE if not
 *
 *  EXIT
 *      The stream's current buffer is unheld, if it was currently in use.
 *
 *  NOTES
 *      Any caller messing with a stream's current buffer must also
 *      currently own the stream's critical section.
 */

DWORD ReleaseStreamBuffer(PDSTREAM pstm, BOOL fCS)
{
    DWORD dwError = ERROR_SUCCESS;

    ASSERT(OWNCRITICALSECTION(&pstm->s_cs));

    if (pstm->s_flags & STF_BUFCURHELD) {

        if ((pstm->s_flags & STF_WRITETHRU) && (pstm->s_pbufCur))
            dwError = CommitBuffer(pstm->s_pbufCur, fCS);

        pstm->s_flags &= ~STF_BUFCURHELD;
        UnholdBuffer(pstm->s_pbufCur);

        // Zap the buffer pointer to prevent anyone from misusing it

        pstm->s_pbufCur = NULL;
    }
    return dwError;
}


/*  ReadStreamBuffer
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      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, offset;
    PBYTE pdata, pdataEnd;
    PVOLUME pvol = pstm->s_pvol;
    DWORD cbBuf = pvol->v_pdsk->d_diActive.di_bytes_per_sect;
    RunInfo RunInfo;

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

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

    dwError = GetRunInfo(&pstm->s_runList, pos, &RunInfo);
    if (dwError != ERROR_SUCCESS) {
        return dwError;
    }
    
    offset = pos - RunInfo.StartPosition;
    blk = RunInfo.StartBlock + (offset >> pvol->v_log2cbBlk);

    // Compute the offset into the volume-relative block

    offset &= (pvol->v_cbBlk - 1);//bytes per sector

    // 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, blk, pstm,
                         (lenMod >= (int)cbBuf && offset == 0),
                         &pbuf);
    DEBUGMSG(ZONE_LOGIO && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!ReadStreamBuffer(DP): end findbuffer\n")));

    if (!dwError) {

        pdata = pbuf->b_pdata + 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 = pbuf->b_pdata + cbBuf;
            if ((cbBuf - offset) > (RunInfo.EndPosition - pos)) {
                pdataEnd = pdata + (RunInfo.EndPosition - pos);
            }
            *ppvEnd = pdataEnd;

⌨️ 快捷键说明

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