📄 buffer.c
字号:
}
}
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;
DWORD cbBuf = pstm->s_pvol->v_pdsk->d_diActive.di_bytes_per_sect;
// A just-dirtied buffer should also be a held buffer
DEBUGMSG(ZONE_BUFFERS,(DBGTEXT("FATFS!ModifyStreamBuffer(block %d, stream %.11hs)\r\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):
* -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;
PVOLUME pvol = (iCommit < 0) ? (PVOLUME)pstm : pstm->s_pvol;
dwError = ERROR_SUCCESS;
EnterCriticalSection(&pvol->v_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 = pvol->v_dlBufMRU.pbufPrev;
pbufEnd = (PBUF)&pvol->v_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));
//TEST_BREAK
PWR_BREAK_NOTIFY(61);
if (fCommit) {
dw = CommitBuffer(pbuf, TRUE);
if (!dwError)
dwError = dw;
}
}
pbuf = pbuf->b_dlink.pbufPrev;
}
LeaveCriticalSection(&pvol->v_csBuffers);
return dwError;
}
// 通过查询pvol->v_dlBufMRU.pbufNext寻找出对应的pstm在哪个buffer中,然后将对应
// buffer的stream设置为NULL,即pbuf->b_pstm=NULL
DWORD InvalidateSteamBuffer(PDSTREAM pstm)
{
DWORD dwError = ERROR_SUCCESS;
PBUF pbuf, pbufEnd;
PVOLUME pvol = pstm->s_pvol;
dwError = ERROR_SUCCESS;
EnterCriticalSection(&pvol->v_csBuffers);
pbuf = pvol->v_dlBufMRU.pbufNext;
pbufEnd = (PBUF)&pvol->v_dlBufMRU;
while (pbuf != pbufEnd) {
if (pbuf->b_pstm == pstm) {
pbuf->b_pstm = NULL;
}
pbuf = pbuf->b_dlink.pbufNext;
}
LeaveCriticalSection(&pvol->v_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
*/
// 如果fAll为true,则将pvol->v_dlBufMRU上链接的所有的buffer,不管是否包含有
// 有效的数据都将该buffer标记的BUF_DIRTY清除;如果是false,则啥都干
void InvalidateBufferSet(PVOLUME pvol, BOOL fAll)
{
BOOL fDirty;
PBUF pbuf, pbufEnd;
EnterCriticalSection(&pvol->v_csBuffers);
fDirty = FALSE;
pbuf = pvol->v_dlBufMRU.pbufNext;
pbufEnd = (PBUF)&pvol->v_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.
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!\r\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_flags |= BUF_INVALID; // invalidated!
}
// pbuf中包含有有效的数据
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.
// 从下面的代码也可以看到,buffer的相关标记也在v_flags中实现
if (!fDirty)
pvol->v_flags &= ~VOLF_DIRTY;
LeaveCriticalSection(&pvol->v_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;
RunInfo RunInfo;
//首先判断pos在当前的stream的所有run中是否位于有效的位置
if (pos < pstm->s_runList.r_StartPosition || pos >= pstm->s_runList.r_EndPosition) {
DEBUGMSGBREAK(ZONE_INIT || ZONE_ERRORS,
(DBGTEXT("FATFS!LockBlocks: bad position (%d) for stream %.11hs\r\n"), pos, pstm->s_achOEM));
return FALSE;
}
//获取pos所在run中的信息
if (ERROR_SUCCESS != GetRunInfo(&pstm->s_runList, pos, &RunInfo)) {
return FALSE;
}
// Compute the stream-relative offset
// and the corresponding volume-relative block
offset = pos - RunInfo.StartPosition;
blk = RunInfo.StartBlock + (offset >> pvol->v_log2cbBlk);
// Limit the size of the request to what exists in the current run
// 对len的值进行判断,防止越界
if (len > (RunInfo.EndPosition - pos)) {
len = RunInfo.EndPosition - pos;
}
// Convert the volume-relative block # into a buffer-granular block #
// 如果len的长度甚至不足一个sector,那么LockBlocks返回错误,即不对底层进行实质性的写入
// 另外一种情况是,pos距离所在对应的某个run开始偏移不是sector的整数倍也不返回错误,
// 即不进行实质性的写入工作
blkBuf = blk;
if ((offset & (pvol->v_cbBlk - 1)) || (cblks = len >> pvol->v_log2cbBlk) < 1) {
// 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;
// Now we just need to find the largest cblkBuf chunk of blocks that
// is not currently buffered.
EnterCriticalSection(&pvol->v_csBuffers);
//获取buffer链表的相关信息
pbuf = pvol->v_dlBufMRU.pbufNext;
pbufEnd = (PBUF)&pvol->v_dlBufMRU;
while (pbuf != pbufEnd) {
//首先进行了一个buffer的有效性判断
if (!(pbuf->b_flags & BUF_INVALID)) {
// If the first block is buffered, then fail the request.
// 循环直到pos处,此处的pos肯定是sector对齐的
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(&pvol->v_csBuffers);
return TRUE;
}
LeaveCriticalSection(&pvol->v_csBuffers);
return FALSE;
}
void UnlockBlocks(PDSTREAM pstm, DWORD blk, DWORD cblk, BOOL fWrite)
{
if (fWrite)
LeaveCriticalSection(&pstm->s_pvol->v_csBuffers);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -