📄 stream.c
字号:
}
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;
}
dwError = UnpackRun(pstm);
if ((dwError == ERROR_INVALID_DATA) || ((dwUnpackError == ERROR_HANDLE_EOF) && (dwError == ERROR_HANDLE_EOF))){
if (dwRetries++ == MAX_UNPACK_RETRIES) {
dwError = ERROR_DISK_FULL;
goto exit;
}
}
dwUnpackError = dwError;
} while (dwUnpackError == ERROR_SUCCESS);
// 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_pvol->v_flFATFS & FATFS_TRANS_DATA) && !(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_pvol->v_flFATFS & FATFS_TRANS_DATA)) {
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 clusRun, clusRunEnd, cclusRun, cbRun;
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;
if (pstm->s_clusFirst < DATA_CLUSTER)
return cbNew <= pstm->s_run.r_end? ERROR_SUCCESS : ERROR_DISK_FULL;
pvol = pstm->s_pvol;
if (pvol->v_flags & VOLF_READONLY)
return ERROR_WRITE_PROTECT;
dwError = PositionStream(pstm, cbNew, &clusEnd);
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) {
// pstm->s_run.r_clusThis should contain the first cluster of the
// last run in a file's cluster chain. pstm->s_run.r_end is the
// position immediately past the last cluster (which we store in posEnd).
posEnd = pstm->s_run.r_end;
// Assert that the reason we're here is that the stream is not
// large enough.
ASSERT(posEnd < cbNew);
// The RUN only remembers a starting cluster (r_clusThis) and a
// length (pstm->s_run.r_end - pstm->s_run.r_start), which we must
// convert into an ending cluster (clusEnd). If clusEnd is
// UNKNOWN_CLUSTER, then the stream currently contains no clusters!
clusEnd = pstm->s_run.r_clusThis;
if (clusEnd != UNKNOWN_CLUSTER) {
ASSERT(pstm->s_run.r_clusThis != 0);
ASSERT(pstm->s_run.r_end > pstm->s_run.r_start);
clusEnd = pstm->s_run.r_clusThis +
((pstm->s_run.r_end - pstm->s_run.r_start-1) >>
(pvol->v_log2cblkClus + pvol->v_log2cbBlk));
}
LockFAT(pvol);
cclusRun = cbRun = 0;
clusRunEnd = UNKNOWN_CLUSTER;
clusPrev = clusRun = 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;
}
}
// posEnd advances, and clus becomes the new clusEnd
posEnd += pvol->v_cbClus;
// Remember the first new cluster we allocate, and keep
// track of the extent to which the new cluster(s) form a run.
if (clusRunEnd == UNKNOWN_CLUSTER) {
if (clusRun == NO_CLUSTER)
clusRun = clus;
if (clusRun + cclusRun == clus) {
cclusRun++;
cbRun += pvol->v_cbClus;
}
else
clusRunEnd = clus; // end of the RUN
}
if (clusEnd != UNKNOWN_CLUSTER)
clusPrev = clusEnd;
clusEnd = clus;
} while (posEnd < cbNew);
// If the stream has any new clusters at all...
#ifdef TFAT
if (!dwError && clusEnd != UNKNOWN_CLUSTER) {
#else
if (clusEnd != UNKNOWN_CLUSTER) {
#endif
// If a PACK fails, we're having trouble writing to the FAT
if (clus != UNKNOWN_CLUSTER) {
if (PACK(pvol, clusEnd, EOF_CLUSTER, NULL) != ERROR_SUCCESS) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: trouble terminating new FAT chain\r\n")));
clus = UNKNOWN_CLUSTER;
}
}
// If we couldn't allocate all the clusters we needed,
// then (try to) resize the stream to its original size.
if (clus == UNKNOWN_CLUSTER) {
DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: shrinking FAT chain back down...\r\n")));
ResizeStream(pstm, pstm->s_run.r_end, dwResizeFlags | RESIZESTREAM_SHRINK);
dwError = ERROR_DISK_FULL;
}
else {
// It's important that we record the cluster-granular
// size for directories, because when CreateName grows a
// directory, it uses this size to calculate how much data
// has to be zeroed. For all other streams, it's important
// to record only the exact number of bytes requested.
if (!(pstm->s_attr & ATTR_DIRECTORY))
posEnd = cbNew;
// If there's no net change in size, don't dirty the stream.
ASSERT(pstm->s_size != posEnd);
if (pstm->s_size != posEnd) {
pstm->s_size = posEnd;
pstm->s_flags |= STF_DIRTY;
}
// We need to update the stream's RUN info; the easiest
// way to do this is call RewindStream, but that can be quite
// expensive later, in terms of unnecessary calls to UnpackRun.
pstm->s_run.r_start = pstm->s_run.r_end;
pstm->s_run.r_end = pstm->s_run.r_end + cbRun;
pstm->s_run.r_blk = CLUSTERTOBLOCK(pvol, clusRun);
pstm->s_run.r_clusPrev = pstm->s_run.r_clusThis;
if (pstm->s_run.r_clusPrev == UNKNOWN_CLUSTER)
pstm->s_run.r_clusPrev = NO_CLUSTER;
pstm->s_run.r_clusThis = clusRun;
pstm->s_run.r_clusNext = clusRunEnd;
}
}
#ifdef TFAT
// Need to update the parent dir in all cases for a file and only if the
// first cluster changed for a directory.
if (pvol->v_fTfat && (!(pstm->s_attr & ATTR_DIRECTORY) || bFirstClusChanged))
UpdateDirEntryCluster(pstm);
#endif
// Commit and release FAT buffers now
if (dwResizeFlags & RESIZESTREAM_UPDATEFAT) {
BOOL fTempWriteThru = FALSE;
// If stream is write-through, but FAT stream is not, temporarily make it so that the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -