📄 stream.c
字号:
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;
clusEnd = PositionStream(pstm, cbNew);
// 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 + BLOCK_LOG2));
}
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\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)\n"), dwError));
break; // can't allocate any more clusters
}
// 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;
}
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\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...
if (clusEnd != UNKNOWN_CLUSTER) {
// 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\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...\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;
}
}
// Commit and release FAT buffers now
if (dwResizeFlags & RESIZESTREAM_UPDATEFAT)
WriteAndReleaseStreamBuffers(pvol->v_pstmFAT);
UnlockFAT(pvol);
DEBUGMSG(ZONE_STREAMS || ZONE_ERRORS && dwError,(DBGTEXT("FATFS!ResizeStream(GROW) returned %d for '%.11hs'\n"), dwError, pstm->s_achOEM));
return dwError;
}
// If shrinking was not enabled, then we're done
if (!(dwResizeFlags & RESIZESTREAM_SHRINK))
return ERROR_SUCCESS;
// We enter the FAT's critical section even though we're just freeing
// clusters, because in the process, we could alter the FAT stream's
// current buffer, and that could mess up someone else accessing the FAT.
LockFAT(pvol);
// If clusEnd is NO_CLUSTER, then all the file's clusters can be freed;
// set pstm->s_clusFirst to UNKNOWN_CLUSTER, which will get propagated
// as ZERO to the DIRENTRY when the stream is committed (CommitStream will
// eventually do that, because we also set STF_DIRTY, below).
if (clusEnd == NO_CLUSTER) {
// clusEnd becomes the first cluster to free.
if (pstm->s_clusFirst != UNKNOWN_CLUSTER)
pstm->s_flags |= STF_DIRTY_CLUS;
clusEnd = pstm->s_clusFirst;
pstm->s_clusFirst = UNKNOWN_CLUSTER;
}
else
dwError = PACK(pvol, clusEnd, EOF_CLUSTER, &clusEnd);
// Now free all the remaining clusters in the chain.
while (!dwError && clusEnd >= DATA_CLUSTER && !ISEOF(pvol, clusEnd))
dwError = PACK(pvol, clusEnd, FREE_CLUSTER, &clusEnd);
// Commit and release FAT buffers now.
if (dwResizeFlags & RESIZESTREAM_UPDATEFAT)
WriteAndReleaseStreamBuffers(pvol->v_pstmFAT);
UnlockFAT(pvol);
pstm->s_size = cbNew;
pstm->s_flags |= STF_DIRTY;
// We need to update the stream's RUN info, too; the easiest
// way to do this is call RewindStream, but since cbNew should still
// be within the current run, we can save work by simply shrinking the run.
ASSERT(cbNew >= pstm->s_run.r_start && cbNew <= pstm->s_run.r_end);
// r_start and r_clusPrev are still valid, but r_end and r_clusNext
// need to be updated, and r_blk and r_clusThis *may* need to be updated
// if the current run was completely eliminated. r_clusNext simply
// becomes UNKNOWN_CLUSTER, since we just truncated the file, and r_end
// is the number of bytes from r_start to cbNew, converted to whole clusters.
pstm->s_run.r_clusNext = UNKNOWN_CLUSTER;
pstm->s_run.r_end = (cbNew + pvol->v_cbClus-1) & ~(pvol->v_cbClus-1);
// Now, if the current run is empty, we must also set r_clusThis to
// UNKNOWN_CLUSTER (and although no one ever pays attention to r_blk when
// r_clusThis is invalid, we will also set r_blk to INVALID_BLOCK in DEBUG).
if (pstm->s_run.r_start == pstm->s_run.r_end) {
pstm->s_run.r_clusThis = UNKNOWN_CLUSTER;
DEBUGONLY(pstm->s_run.r_blk = INVALID_BLOCK);
}
DEBUGMSG(ZONE_STREAMS || ZONE_ERRORS && dwError,(DBGTEXT("FATFS!ResizeStream(SHRINK) returned %d for '%.11hs'\n"), dwError, pstm->s_achOEM));
return dwError;
}
/* CheckStreamHandles - Check for stream with open handles
*
* ENTRY
* pvol - pointer to VOLUME
* psid - pointer to SID, or NULL to check all streams for open handles
*
* EXIT
* TRUE if stream exists and has open handles, FALSE if not
*
* NOTES
* This check excludes VOLUME-based handles, because it excludes VOLUME-
* based streams (ie, the FAT and root directory).
*/
BOOL CheckStreamHandles(PVOLUME pvol, PDSID psid)
{
PDSTREAM pstm;
BOOL fOpen = FALSE;
EnterCriticalSection(&pvol->v_csStms);
for (pstm = pvol->v_dlOpenStreams.pstmNext; pstm != (PDSTREAM)&pvol->v_dlOpenStreams; pstm = pstm->s_dlOpenStreams.pstmNext) {
if (pstm->s_flags & STF_VOLUME)
continue;
if (psid == NULL || pstm->s_sid.sid_clusDir == psid->sid_clusDir && pstm->s_sid.sid_ordDir == psid->sid_ordDir) {
fOpen = (pstm->s_dlOpenHandles.pfhNext != (PFHANDLE)&pstm->s_dlOpenHandles);
if (psid || fOpen)
break;
}
}
LeaveCriticalSection(&pvol->v_csStms);
return fOpen;
}
/* CheckStreamSharing - Check requested mode against stream
*
* ENTRY
* pstm - pointer to DSTREAM
* mode - requested mode
*
* EXIT
* TRUE if all handles for stream permit the specified mode, FALSE if not
*/
BOOL CheckStreamSharing(PDSTREAM pstm, int mode)
{
BYTE bRequired = 0;
PFHANDLE pfh, pfhEnd;
ASSERT(pstm);
ASSERT(OWNCRITICALSECTION(&pstm->s_cs));
if (mode & FH_MODE_READ)
bRequired |= FH_MODE_SHARE_READ;
if (mode & FH_MODE_WRITE)
bRequired |= FH_MODE_SHARE_WRITE;
pfh = pstm->s_dlOpenHandles.pfhNext;
pfhEnd = (PFHANDLE)&pstm->s_dlOpenHandles;
while (pfh != pfhEnd) {
if ((pfh->fh_mode & bRequired) != bRequired)
return FALSE;
pfh = pfh->fh_dlOpenHandles.pfhNext;
}
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -