?? buffer.c
字號:
#endif
// If buffer is valid and dirty...
if (pbuf->b_pvol && (pbuf->b_flags & BUF_DIRTY)) {
if (pbuf->b_pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
}
DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!CommitBuffer(block %d)\n"), pbuf->b_blk));
// Hold the buffer to insure that FindBuffer won't try to steal
// it while the DIRTY bit is clear yet the buffer is still dirty.
HoldBuffer(pbuf);
// We clear the DIRTY bit before writing the buffer out, to
// insure that we don't inadvertently lose some other thread's
// modification to the buffer before our WriteVolume call is
// complete.
CleanBuffer(pbuf);
// If the caller tells us that csBuffers is currently held, release
// it across the WriteVolume call.
pbuf->b_flags |= BUF_BUSY;
// We used to release this critical section across a disk write but it would
// sometimes cause data corruption on multithreaded writes with odd buffer
// sizes. Beware if you try to "optimize" by adding the release back in.
//if (fCS)
// LeaveCriticalSection(&csBuffers);
dwError = WriteVolume(pbuf->b_pvol, pbuf->b_blk, cblkBuf, pbuf->b_pdata);
pbuf->b_flags &= ~BUF_BUSY;
//if (fCS)
// EnterCriticalSection(&csBuffers);
if (dwError) {
if (dwError == ERROR_WRITE_PROTECT) {
// Invalidate the buffer, it can't be written and is presumably
// now out of sync with the disk's actual contents.
pbuf->b_pvol = NULL;
}
else {
// If the cause of failure might simply be that the disk has been
// yanked, we can always hope that the user will put the disk back
// in, so let's just re-dirty the buffer.
DirtyBufferError(pbuf, pbMod, cbMod);
}
}
UnholdBuffer(pbuf);
}
return dwError;
}
/* CleanBuffer
*
* ENTRY
* pbuf - pointer to BUF
*
* EXIT
* The buffer is marked CLEAN, if it wasn't already.
*/
void CleanBuffer(PBUF pbuf)
{
ASSERTHELDBUFFER(pbuf);
pbuf->b_flags &= ~BUF_DIRTY;
if (pbuf->b_flags & BUF_ERROR) {
EnterCriticalSection(&csBuffers);
if (pbuf->b_flags & BUF_ERROR) {
cbufError--;
if (cbufError % MIN_BUFFERS == 0)
InterlockedIncrement(&cBufThreads);
pbuf->b_flags &= ~BUF_ERROR;
}
LeaveCriticalSection(&csBuffers);
}
}
/* ChecksumBuffer
*
* ENTRY
* pbuf - pointer to BUF
*
* EXIT
* 32-bit checksum of data in buffer
*/
DWORD ChecksumBuffer(PBUF pbuf)
{
DWORD dwSum = 0;
DWORD cdw = cbBuf / sizeof(DWORD);
PDWORD pdw = (PDWORD)pbuf->b_pdata;
ASSERT(cdw*sizeof(DWORD) == cbBuf);
while (cdw--) {
dwSum += *pdw++; }
return dwSum | 0x80000000; // insure that the checksum for a buffer is always non-zero
}
/* FindBuffer
*
* ENTRY
* pvol - pointer to VOLUME
* blk - desired block on VOLUME
* pstm - pointer to DSTREAM containing block
* fNoRead - TRUE to skip reading the coresponding disk data
* ppbuf - pointer to address of BUF (to be returned)
*
* EXIT
* ERROR_SUCCESS (0) if successful, non-zero if not.
*/
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\n")));
EnterCriticalSection(&csBuffers);
DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end entercritsec\n")));
DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!FindBuffer(block %d, stream %.11hs)\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!\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_pvol && pbuf->b_pvol == pvol && 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 = dlBufMRU.pbufNext;
pbufEnd = (PBUF)&dlBufMRU;
pbufOld = NULL;
while (pbuf != pbufEnd) {
if (pbuf->b_pvol && pbuf->b_pvol == pvol && 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...\n"), pbuf->b_blk));
Sleep(2000);
}
// Furthermore, if an UNCERTAIN buffer could not be read, then we will have
// set b_pvol to NULL to invalidate 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_pvol == NULL)
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.
if (!HeldBuffer(pbuf) && !(pbuf->b_flags & BUF_BUSY)) {
if (!pbufOld || !(pbuf->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!\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;
#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(&csBuffers);
DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): begin readvolume\n")));
dwError = ReadVolume(pvol, pbuf->b_blk, cblkBuf, pbuf->b_pdata);
DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end readvolume\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_pvol = NULL;
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\n")));
EnterCriticalSection(&csBuffers);
DEBUGMSG(ZONE_LOGIO && pstm && (pstm->s_flags & STF_DEMANDPAGED),(DBGTEXT("FATFS!FindBuffer(DP): end entercritsec\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)&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(&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)
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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -