?? stream.c
字號:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*++
Module Name:
stream.c
Abstract:
This file contains routines for manipulating streams.
Revision History:
--*/
#include "fatfs.h"
/* OpenStream - Open/create stream
*
* ENTRY
* pvol - pointer to VOLUME
* clusFirst - first cluster of stream (the search criteria)
* psid - pointer to SID, NULL if none
* pstmParent - pointer to parent DSTREAM (NULL if none/unknown)
* pdi - pointer to DIRINFO (NULL if none/unknown)
* dwFlags - one or more OPENSTREAM_* flags (eg, OPENSTREAM_CREATE, OPENSTREAM_REFRESH)
*
* EXIT
* pointer to DSTREAM if successful (and critical section left held),
* NULL if not (ie, out of memory)
*
* NOTES
* If clusFirst is UNKNOWN_CLUSTER, then psid becomes the search criteria.
*
* Streams for data that is not cluster-mapped (eg, FAT, root directory)
* must be completely contiguous; their RUN info must be set to match
* the size of the stream, so that ReadStream will always call directly
* to ReadStreamBuffer without attempting to call UnpackRun (which knows
* only how to deal with cluster-mapped data).
*/
PDSTREAM OpenStream(PVOLUME pvol, DWORD clusFirst, PDSID psid, PDSTREAM pstmParent, PDIRINFO pdi, DWORD dwFlags)
{
PDSTREAM pstm;
// Assert that at least one search criteria appears valid
ASSERT(clusFirst != UNKNOWN_CLUSTER? TRUE : (int)psid);
EnterCriticalSection(&pvol->v_csStms);
// If the caller wants to create a private stream, then skip the search
if ((dwFlags & (OPENSTREAM_CREATE|OPENSTREAM_PRIVATE)) != (OPENSTREAM_CREATE|OPENSTREAM_PRIVATE)) {
for (pstm = pvol->v_dlOpenStreams.pstmNext; pstm != (PDSTREAM)&pvol->v_dlOpenStreams; pstm = pstm->s_dlOpenStreams.pstmNext) {
if (!(pstm->s_flags & STF_PRIVATE) != !(dwFlags & OPENSTREAM_PRIVATE))
continue;
if (clusFirst != UNKNOWN_CLUSTER &&
pstm->s_clusFirst == clusFirst ||
clusFirst == UNKNOWN_CLUSTER &&
pstm->s_sid.sid_clusDir == psid->sid_clusDir && pstm->s_sid.sid_ordDir == psid->sid_ordDir) {
ASSERT(pstm->s_refs != 0);
goto init;
}
}
}
// The requested stream is not open. Bail if the volume is unmounted and
// has not yet been remounted or recycled, or if the volume is fine and we
// simply cannot allocate memory for a new stream.
if (!(dwFlags & OPENSTREAM_CREATE) ||
(pvol->v_flags & (VOLF_UNMOUNTED | VOLF_REMOUNTED | VOLF_RECYCLED)) == VOLF_UNMOUNTED ||
!(pstm = LocalAlloc(LPTR, sizeof(DSTREAM)))) {
LeaveCriticalSection(&pvol->v_csStms);
return NULL;
}
DEBUGALLOC(sizeof(DSTREAM));
InitializeCriticalSection(&pstm->s_cs);
DEBUGALLOC(DEBUGALLOC_CS);
pstm->s_pvol = pvol;
pstm->s_flags = STF_VOLUME;
pstm->s_clusFirst = clusFirst;
if (psid)
pstm->s_sid = *psid;
pstm->s_blkDir = INVALID_BLOCK;
RewindStream(pstm, INVALID_POS);
// If the caller wanted a PRIVATE root stream, then we have to clone
// the appropriate fields from the volume's root stream.
if (dwFlags & OPENSTREAM_PRIVATE) {
ASSERT(clusFirst);
pstm->s_flags |= STF_PRIVATE;
if (clusFirst == ROOT_PSEUDO_CLUSTER) {
pstm->s_run = pvol->v_pstmRoot->s_run;
pstm->s_size = pvol->v_pstmRoot->s_size;
}
}
InitList((PDLINK)&pstm->s_dlOpenHandles);
AddItem((PDLINK)&pvol->v_dlOpenStreams, (PDLINK)&pstm->s_dlOpenStreams);
// Common code for both cases (old stream, new stream). An old
// stream might have been created with minimal information by a thread
// for its own minimal purposes, but if another thread requests the same
// stream and provides more detailed information, we need to record/update
// that information, so that the stream will satisfy both threads'
// purposes.
init:
pstm->s_refs++;
LeaveCriticalSection(&pvol->v_csStms);
EnterCriticalSection(&pstm->s_cs);
if (!pstm->s_clusFirst || pstm->s_clusFirst == UNKNOWN_CLUSTER)
pstm->s_clusFirst = clusFirst;
if (psid) {
if (!pstm->s_sid.sid_clusDir || pstm->s_sid.sid_clusDir == UNKNOWN_CLUSTER) {
pstm->s_sid = *psid;
}
}
if (pdi && (!(pstm->s_flags & STF_INIT) || (dwFlags & OPENSTREAM_REFRESH))) {
// Whenever pdi is provided, pstmParent must be provided too.
ASSERT(pstmParent);
// We save the OEM name in the DIRENTRY for validation during handle
// regeneration.
memcpy(pstm->s_achOEM, pdi->di_pde->de_name, sizeof(pdi->di_pde->de_name));
pstm->s_attr = pdi->di_pde->de_attr;
pstm->s_size = (pdi->di_pde->de_attr & ATTR_DIR_MASK) == ATTR_DIRECTORY? MAX_DIRSIZE : pdi->di_pde->de_size;
pstm->s_sidParent = pstmParent->s_sid;
pstm->s_cwPath = pdi->di_cwName + pstmParent->s_cwPath + 1;
// We should never need to query or update a DIRECTORY stream's
// DIRENTRY however (or the date/time values inside it).
if (!(pstm->s_attr & ATTR_DIRECTORY)) {
pstm->s_flags &= ~STF_VOLUME;
// As an optimization, remember the physical block and offset
// within that block of the stream's DIRENTRY. This will save
// us from having to perform a OpenStream/FindNext combination
// in CommitStream whenever we need to update a stream's DIRENTRY.
// For that information to be available and valid, however, all
// callers must have a "lock" on the parent stream, and the stream
// in turn must have a "current buffer".
ASSERT(OWNCRITICALSECTION(&pstmParent->s_cs));
ASSERT(pstmParent->s_pbufCur);
if (pstmParent->s_pbufCur) {
pstm->s_blkDir = pstmParent->s_pbufCur->b_blk;
pstm->s_offDir = (PBYTE)pdi->di_pde - pstmParent->s_pbufCur->b_pdata;
}
// DOSTimeToFileTime can fail because it doesn't range-check any of
// the date/time values in the directory entry; it just pulls them apart
// and calls SystemTimeToFileTime, which won't initialize the FILETIME
// structure if there's an error. Fortunately, all the FILETIMEs have been
// zero-initialized, because that's what the Win32 API dictates when a time
// is unknown/not supported.
DOSTimeToFileTime(pdi->di_pde->de_createdDate,
pdi->di_pde->de_createdTime,
pdi->di_pde->de_createdTimeMsec, &pstm->s_ftCreate);
DOSTimeToFileTime(pdi->di_pde->de_date, pdi->di_pde->de_time, 0, &pstm->s_ftWrite);
DOSTimeToFileTime(pdi->di_pde->de_lastAccessDate, 0, 0, &pstm->s_ftAccess);
}
pstm->s_flags |= STF_INIT;
}
#ifdef DEBUG
else {
if (clusFirst == FAT_PSEUDO_CLUSTER)
memcpy(pstm->s_achOEM, "<FAT>", 5);
else if (clusFirst == ROOT_PSEUDO_CLUSTER)
memcpy(pstm->s_achOEM, "<ROOT>", 6);
}
#endif
DEBUGMSGW(ZONE_STREAMS,(DBGTEXTW("FATFS!OpenStream %s stream 0x%08x for '%.11hs'\n"), pstm->s_refs > 1? TEXTW("opened") : TEXTW("created"), pstm, pstm->s_achOEM));
return pstm;
}
DWORD CloseStream(PDSTREAM pstm)
{
DWORD dwError = ERROR_SUCCESS;
if (pstm) {
PVOLUME pvol = pstm->s_pvol;
ASSERT(OWNCRITICALSECTION(&pstm->s_cs));
// When there is no lazy-writer, CloseStream may be the last opportunity we
// have to visit buffers for this stream, so we need to call CommitStream(TRUE),
// lest any buffers languish dirty indefinitely -- which would in turn increase
// the chances of the media appearing inconsistent if it was removed unexpectedly.
// Of course, we warn the user to put the media back in when that happens, but
// it's best to prevent such warnings in the first place.
//
// When there IS a lazy-writer, we can let it take care of buffers for path-based
// and volume-based streams, and invoke CommitStream(TRUE) only for handle-based streams.
//
// This logic is somewhat arbitrary -- we could always call CommitStream(FALSE) -- but
// since files are often closed as a result of some explicit user action, and since such
// actions often precede unexpected media removal, I'm not exactly being paranoid for
// no reason here.... -JTP
dwError = CommitStream(pstm, TRUE);
ASSERT(!(pstm->s_flags & STF_BUFCURHELD) && pstm->s_pbufCur == NULL);
LeaveCriticalSection(&pstm->s_cs);
EnterCriticalSection(&pvol->v_csStms);
if (--pstm->s_refs == 0) {
DEBUGMSG(ZONE_STREAMS,(DBGTEXT("FATFS!CloseStream destroying stream 0x%08x for '%.11hs'\n"), pstm, pstm->s_achOEM));
RemoveItem((PDLINK)&pstm->s_dlOpenStreams);
DEBUGFREE(DEBUGALLOC_CS);
DeleteCriticalSection(&pstm->s_cs);
DEBUGFREE(sizeof(DSTREAM));
VERIFYNULL(LocalFree(pstm));
}
LeaveCriticalSection(&pvol->v_csStms);
}
return dwError;
}
/* CommitStream - flush buffers (and DIRENTRY) associated with this stream
*
* ENTRY
* pstm - pointer to DSTREAM
* fAll - TRUE to commit all stream buffers, FALSE to just update DIRENTRY
*
* EXIT
* ERROR_SUCCESS (0) if successful, non-zero if not
*
* NOTES
* As an optimization, OpenStream remembers the physical block and
* within that block of the stream's DIRENTRY. This will save
* us from having to perform a OpenStream/FindNext combination
* in CommitStream every time we need to update a stream's DIRENTRY.
*
* As an added optimization, OpenStream also records the parent's OID
* if a parent stream was specified when the stream was created. Thus,
* CommitStream has all the information it needs to post a CEOID_CHANGED
* message if the stream changed, again without having to open the
* parent stream.
*/
DWORD CommitStream(PDSTREAM pstm, BOOL fAll)
{
DWORD dwError = ERROR_SUCCESS;
ASSERT(OWNCRITICALSECTION(&pstm->s_cs));
// NOTE: CommitStream refuses to update an unmounted stream because
// the volume the stream was associated with may have been unmounted and
// RECYCLED, in which case this stream is dead and its directory entry
// can never be safely updated. The stream will eventually go away when
// all the apps with handles to the stream close their handles.
if (!(pstm->s_flags & STF_UNMOUNTED)) {
// Commit all dirty buffers associated with this stream. There
// is no return value from this function. Any held stream buffer will
// be released as well.
if (!fAll)
dwError = ReleaseStreamBuffer(pstm, FALSE);
else
dwError = CommitAndReleaseStreamBuffers(pstm);
// The rest of this is for non-FAT, non-root, non-directory
// streams only (ie, streams with DIRENTRY's that should be updated).
if (!(pstm->s_flags & STF_VOLUME)) {
SYSTEMTIME stLocal;
GetLocalTime(&stLocal);
// A stream gets a new access time if it's a new day, unless
// STF_ACCESS_TIME is set, in which case someone has already set a
// specific access time.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -