📄 stream.c
字号:
__try {
memcpy(pvData, pbStart, cb);
(PBYTE)pvData += cb;
if (plenRead)
*plenRead += cb;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
dwError = ERROR_INVALID_PARAMETER;
}
ReleaseStreamBuffer(pstm, FALSE);
if (dwError)
break;
}
#ifdef DEBUG
if (ZONE_READVERIFY && ZONE_LOGIO) {
DWORD i, j;
for (i=0; i<cb; i++) {
DEBUGMSG(TRUE,(DBGTEXT("%02x "), pbStart[i]));
j = i+1;
if (j == cb || j % 16 == 0) {
j = j % 16;
if (j == 0)
j = i - 15;
else
j = i - j + 1;
while (j <= i) {
DEBUGMSG(TRUE,(DBGTEXT("%c"), pbStart[j]<' ' || pbStart[j]>=0x7F? '.' : pbStart[j]));
j++;
}
DEBUGMSG(TRUE,(DBGTEXT("\r\n")));
}
}
}
#endif
len -= cb;
pos += cb;
}
return dwError;
}
/* WriteStreamData
*
* Whereas ReadStream returns a pointer (to a buffer) and a length,
* and then the caller reads whatever data it wants up to that length
* (or modifies data up to that length and calls ModifyStreamBuffer), this
* function takes a pointer to data and its length, the position to write
* it, and simply writes it.
*
* Thus, unlike ReadStream, where the caller can never get more than a
* buffer's worth of data back at a time and probably needs to put a loop
* around its ReadStream call, WriteStreamData can write any amount of data
* with one call (no external loop).
*
* The same effect could be achieved with a ReadStream/ModifyStreamBuffer
* loop, but there are two important differences: WriteStreamData can avoid
* the overhead of reading data that it's simply going to overwrite anyway,
* and it takes care of extending the stream if needed.
*
* So, I've changed ReadStreamBuffer to accept an "advisory" lenMod parm,
* allowing us to advise it how many bytes we plan to modify. This
* information percolates down to FindBuffer, allowing FindBuffer to avoid
* filling a buffer with disk data if the entire buffer contents are going
* to be modified anyway. -JTP
*
* ENTRY
* pstm - pointer to DSTREAM
* pos - position within DSTREAM to write to
* pvData - pointer to data to write (or NULL to write zeros)
* len - length of data to write
* plenWritten - pointer to DWORD for length written (optional)
* fCommit - TRUE to commit buffers as they are written
*
* EXIT
* 0 if successful, error code if not (eg, disk full, or error reading
* the disk to fill the non-modified portion of a buffer)
*
* NOTES
* If fCommit is FALSE, note that the last buffer modified will still
* be held (ie, the stream's current buffer).
*/
#define MAX_UNPACK_RETRIES 20
DWORD WriteStreamData(PDSTREAM pstm, DWORD pos, PCVOID pvData, DWORD len, PDWORD plenWritten, BOOL fCommit)
{
DWORD dwError, dwUnpackError;
PBYTE pbStart, pbEnd;
DWORD dwRetries;
BOOL fSizeChanged = FALSE;
RETAILMSG(1,(L"[FSD] WriteStreamData\r\n"));
dwUnpackError = ERROR_SUCCESS;
dwRetries = 0;
ASSERT(OWNCRITICALSECTION(&pstm->s_cs));
if (pstm->s_pvol->v_flags & VOLF_READONLY)
return ERROR_WRITE_PROTECT;
dwError = ERROR_SUCCESS;
#ifdef TFAT
// If CloneStream fails (most likely because no room to copy), then fail the write
if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA)) {
LockFAT (pstm->s_pvol);
dwError = CloneStream(pstm, pos, len );
if (dwError != ERROR_SUCCESS)
goto exit;
}
#endif
// A write of 0 bytes always succeeds, and is not supposed to do
// anything other than cause the "last write" timestamp to eventually
// get updated.
if (len == 0) {
pstm->s_flags |= STF_DIRTY_DATA;
pstm->s_flags &= ~STF_WRITE_TIME; //set write time
goto exit;
}
do {
while (len) {
DWORD cb, blk, cblk;
// Locate the data run which contains the bytes requested.
if (dwError = SeekStream(pstm, pos)) {
break;
}
if (pvData && LockBlocks(pstm, pos, len, &blk, &cblk, TRUE)) {
RETAILMSG(1,(L"[FSD] WriteVolume\r\n"));
__try {
dwError = WriteVolume(pstm->s_pvol, blk, cblk, (PVOID)pvData, pstm->s_flags & STF_WRITETHRU);
if (!dwError) {
cb = cblk << pstm->s_pvol->v_log2cbBlk;
(PBYTE)pvData += cb;
if (plenWritten)
*plenWritten += cb;
}
else
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData: WriteVolume(block %d, %d blocks) failed (%d)\r\n"), blk, cblk, dwError));
}
__except (EXCEPTION_EXECUTE_HANDLER) {
dwError = ERROR_INVALID_PARAMETER;
}
UnlockBlocks(pstm, blk, cblk, TRUE);
if (dwError)
goto exit;
}
else {
dwError = ReadStreamBuffer(pstm,
pos,
pos+len < pstm->s_size? len : pstm->s_runList.r_EndPosition - pos,
&pbStart,
&pbEnd);
if (dwError) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData: ReadStreamBuffer(pos %d) failed (%d)\r\n"), pos, dwError));
goto exit;
}
cb = pbEnd - pbStart;
if (cb > len)
cb = len;
RETAILMSG(1,(L"[FSD] ModifyStreamBuffer\r\n"));
dwError = ModifyStreamBuffer(pstm, pbStart, cb);
if (dwError)
goto exit;
if (pvData) {
__try {
memcpy(pbStart, pvData, cb);
(PBYTE)pvData += cb;
if (plenWritten)
*plenWritten += cb;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
dwError = ERROR_INVALID_PARAMETER;
goto exit;
}
}
else
memset(pbStart, 0, cb);
}
len -= cb;
pos += cb;
// Once we get this far at least once through the loop,
// we need to make sure the stream data is flagged as dirty.
pstm->s_flags |= STF_DIRTY;
pstm->s_flags &= ~STF_WRITE_TIME;
// Furthermore, if we just wrote beyond the "official"
// size of the stream, then update the "official" size
if (pos > pstm->s_size) {
pstm->s_size = pos;
fSizeChanged = TRUE;
}
// This is where all successful WriteStreamData calls
// end up (except when len is ZERO to begin with, of course).
if (len == 0)
goto exit;
}
// We've run out of cluster runs, and we still have len bytes
// to write at pos. Resize the stream to encompass that boundary
// and try to continue.
} while ((dwError = ResizeStream(pstm, pos + len, RESIZESTREAM_SHRINK|RESIZESTREAM_UPDATEFAT)) == ERROR_SUCCESS);
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData failed (%d)\r\n"), dwError));
exit:
if (fCommit) {
DWORD dw;
if (pstm->s_flags & STF_DIRTY_CLUS) {
dw = CommitStream(pstm, FALSE);
if (!dwError)
dwError = dw;
}
#ifdef TFAT
else if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA) && !(pstm->s_attr & ATTR_DIRECTORY) && fSizeChanged) {
// Update the directory entry with the new size if we changed the file size
// without calling ResizeStream (didn't need a new cluster).
UpdateDirEntryCluster(pstm);
}
#endif
dw = ReleaseStreamBuffer(pstm, FALSE);
if (!dwError)
dwError = dw;
}
#ifdef TFAT
if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA)) {
UnlockFAT (pstm->s_pvol);
}
#endif
return dwError;
}
/* ResizeStream - truncate or extend a stream
*
* ENTRY
* pstm - pointer to DSTREAM
* cbNew - new size of stream
* dwResizeFlags - zero or more RESIZESTREAM flags; RESIZESTREAM_SHRINK
* forces shrinking (instead of simply insuring that the stream is at least
* as large as cbNew), and RESIZESTREAM_UPDATEFAT requests that changes to
* the FAT be written.
*
* EXIT
* ERROR_SUCCESS (0) if successful, non-zero if not
*
* Truncating streams should only fail due to "hard" error conditions
* (eg, write-protected media), whereas extending a stream can also fail
* due to "soft" error conditions (eg, disk is full, or non-cluster-mapped
* stream -- like the root directory -- is full).
*/
DWORD ResizeStream (PDSTREAM pstm, DWORD cbNew, DWORD dwResizeFlags)
{
PVOLUME pvol;
DWORD dwError = ERROR_SUCCESS;
DWORD posEnd, clus, clusEnd, clusPrev;
DWORD oldSize;
BOOL bFirstClusChanged = FALSE;
ASSERT(OWNCRITICALSECTION(&pstm->s_cs));
DEBUGMSG(ZONE_STREAMS,(DBGTEXT("FATFS!ResizeStream: changing size from 0x%x to 0x%x for '%.11hs'\r\n"), pstm->s_size, cbNew, pstm->s_achOEM));
// Early exit if stream size already matches desired size.
if (pstm->s_size == cbNew)
return ERROR_SUCCESS;
oldSize = pstm->s_size;
if (pstm->s_clusFirst < DATA_CLUSTER)
return cbNew <= pstm->s_runList.r_EndPosition ? ERROR_SUCCESS : ERROR_DISK_FULL;
pvol = pstm->s_pvol;
if (pvol->v_flags & VOLF_READONLY)
return ERROR_WRITE_PROTECT;
// Subtract 1 from the position, because in the case of a cluster boundary,
// we want the cluster preceding the position.
if (cbNew) {
dwError = PositionStream(pstm, cbNew - 1, &clusEnd);
}
else {
clusEnd = NO_CLUSTER;
}
if (dwError == ERROR_HANDLE_EOF) {
// This isn't an error in this case, just extending the stream
dwError = ERROR_SUCCESS;
}
if (dwError) {
ASSERT (0); // Something wrong with cluster chain or FAT
return dwError;
}
// If clusEnd is UNKNOWN_CLUSTER, then the new size was past EOF, so
// we need to add clusters until we have enough to reach the specified size.
if (clusEnd == UNKNOWN_CLUSTER) {
posEnd = pstm->s_runList.r_EndPosition;
// Assert that the reason we're here is that the stream is not
// large enough.
ASSERT(posEnd < cbNew);
// Get the ending cluster of the current stream.
if (posEnd > 0) {
dwError = GetClusterAtPosition (&pstm->s_runList, posEnd - 1, &clusEnd);
if (dwError != ERROR_SUCCESS) {
return dwError;
}
}
LockFAT(pvol);
clusPrev = NO_CLUSTER;
// If we have the number of free clusters cached, then let's calculate
// the minimum number of clusters we'll need, and if there are not enough
// free clusters to satisfy the request, then force an error.
if (pvol->v_cclusFree != UNKNOWN_CLUSTER) {
if (pvol->v_cclusFree == 0 || pvol->v_cclusFree < (cbNew - posEnd + pvol->v_cbClus - 1) / pvol->v_cbClus)
dwError = ERROR_DISK_FULL;
}
do {
if (dwError) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: ran out of free clusters\r\n")));
clus = UNKNOWN_CLUSTER;
break;
}
dwError = NewCluster(pvol, clusEnd, &clus);
if (dwError == ERROR_SUCCESS && clus == UNKNOWN_CLUSTER)
dwError = ERROR_DISK_FULL;
if (dwError != ERROR_SUCCESS) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: cannot allocate new cluster (%d)\r\n"), dwError));
break; // can't allocate any more clusters
}
DEBUGMSG(ZONE_STREAMS,(DBGTEXT("FATFS!ResizeStream: new cluster for %.11hs returned %d'\r\n"), pstm->s_achOEM, clus));
// Link clus onto the end of the stream's cluster chain. If
// the previous end was UNKNOWN_CLUSTER, then record clus in the
// stream's clusFirst field; otherwise, link clus onto clusEnd.
if (clusEnd == UNKNOWN_CLUSTER) {
pstm->s_clusFirst = clus;
pstm->s_flags |= STF_DIRTY | STF_DIRTY_CLUS;
bFirstClusChanged = TRUE;
}
else {
// If a PACK fails, we're having trouble writing to the FAT
if (PACK(pvol, clusEnd, clus, NULL) != ERROR_SUCCESS) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: trouble growing new FAT chain\r\n")));
clus = UNKNOWN_CLUSTER;
break;
}
}
dwError = AppendRunList(&pstm->s_runList, posEnd, clus);
if (dwError != ERROR_SUCCESS) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -