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