📄 buffer.c
字号:
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 + -