//+------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1992. // // File: sstream.cxx // // Contents: Stream operations for Mstream project // // Classes: None. (defined in sstream.hxx) // //-------------------------------------------------------------------- #include "msfhead.cxx" #include "h/dirfunc.hxx" #include "h/sstream.hxx" #include #include "mread.hxx" #define DEB_STREAM (DEB_ITRACE | 0x00020000) //+-------------------------------------------------------------- // // Member: CDirectStream::CDirectStream, public // // Synopsis: Empty object constructor // // Arguments: [dl] - LUID // //--------------------------------------------------------------- CDirectStream::CDirectStream(DFLUID dl) : PEntry(dl) { _cReferences = 0; } //+-------------------------------------------------------------- // // Member: CDirectStream::InitSystem, public // // Synopsis: Initializes special system streams like the ministream // // Arguments: [pms] - Multistream // [sid] - SID // [cbSize] - size // //--------------------------------------------------------------- void CDirectStream::InitSystem(CMStream *pms, SID sid, ULONG cbSize) { _stmh.Init(pms, sid); _ulSize = _ulOldSize = cbSize; AddRef(); } //+------------------------------------------------------------------------- // // Method: CDirectStream::Init, public // // Synopsis: CDirectStream constructor // // Arguments: [pstgh] - Parent // [pdfn] - Name of entry // [fCreate] - Create or get // // Returns: Appropriate status code // //-------------------------------------------------------------------------- SCODE CDirectStream::Init( CStgHandle *pstgh, CDfName const *pdfn, BOOL const fCreate) { SCODE sc; if (fCreate) sc = pstgh->CreateEntry(pdfn, STGTY_STREAM, &_stmh); else sc = pstgh->GetEntry(pdfn, STGTY_STREAM, &_stmh); if (SUCCEEDED(sc)) { sc = _stmh.GetSize(&_ulSize); _ulOldSize = _ulSize; if (SUCCEEDED(sc)) AddRef(); } return sc; } //+------------------------------------------------------------------------- // // Method: CDirectStream::~CDirectStream, public // // Synopsis: CDirectStream destructor // // Notes: // //-------------------------------------------------------------------------- CDirectStream::~CDirectStream() { msfAssert(_cReferences == 0); } //+------------------------------------------------------------------------- // // Member: CDirectStream::ReadAt, public // // Synposis: Reads binary data from a linear single stream // // Arguments: [ulOffset] -- Position to be read from // // [pBuffer] -- Pointer to the area into which the data // will be read. // [ulCount] -- Indicates the number of bytes to be read // [pulRetval] -- Area into which return value will be stored // // Returns: Error Code of parent MStream call // // Algorithm: Calculate start and end sectors and offsets, then // pass call up to parent MStream. // // Notes: // //--------------------------------------------------------------------------- SCODE CDirectStream::ReadAt( ULONG ulOffset, VOID HUGEP *pBuffer, ULONG ulCount, ULONG STACKBASED *pulRetval) { msfDebugOut((DEB_TRACE,"In CDirectStream::ReadAt(%lu,%p,%lu)\n", ulOffset,pBuffer,ulCount)); SCODE sc = S_OK; CMStream *pms = _stmh.GetMS(); // Check for offset beyond stream size and zero count if ((ulOffset >= _ulSize) || (0 == ulCount)) { *pulRetval = 0; return S_OK; } if (ulOffset + ulCount > _ulSize) { msfDebugOut((DEB_ITRACE,"Truncating Read: ulOffset = %lu, ulCount = %lu, _ulSize = %lu\n", ulOffset,ulCount,_ulSize)); ulCount = _ulSize - ulOffset; } // Stream is stored in ministream if size < MINISTREAMSIZE // and this is not a scratch stream. SID sid = _stmh.GetSid(); CFat *pfat = pms->GetFat(); USHORT cbSector = pms->GetSectorSize(); USHORT uShift = pms->GetSectorShift(); USHORT uMask = pms->GetSectorMask(); if ((_ulSize < MINISTREAMSIZE) && (sid != SIDMINISTREAM)) { msfAssert(sid <= MAXREGSID); // This stream is stored in the ministream cbSector = MINISECTORSIZE; uShift = MINISECTORSHIFT; uMask = (USHORT) (cbSector - 1); pfat = pms->GetMiniFat(); } SECT start = (SECT)(ulOffset >> uShift); OFFSET oStart = (OFFSET)(ulOffset & uMask); SECT end = (SECT)((ulOffset + ulCount - 1) >> uShift); OFFSET oEnd = (OFFSET)((ulOffset + ulCount - 1) & uMask); ULONG total = 0; ULONG cSect = end - start + 1; SECT sectSidStart; USHORT offset; offset = oStart; while (TRUE) { SECT sect; if (start > _stmc.GetOffset()) { msfChk(pfat->GetSect( _stmc.GetSect(), start - _stmc.GetOffset(), §)); } else if (start == _stmc.GetOffset()) { sect = _stmc.GetSect(); } else { msfChk(pms->GetDir()->GetStart(sid, §SidStart)); msfChk(pfat->GetSect(sectSidStart, start, §)); } SSegment segtab[CSEG + 1]; msfChk(pfat->Contig( (SSegment STACKBASED *) segtab, sect, cSect)); USHORT oend = (USHORT) (cbSector - 1); for (USHORT iseg = 0; iseg < CSEG;) { msfDebugOut((DEB_ITRACE,"Segment: (%lu,%lu)\n",segtab[iseg].sectStart,segtab[iseg].cSect)); SECT sectStart = segtab[iseg].sectStart; ULONG i = segtab[iseg].cSect; cSect -= i; start += i; iseg++; if (segtab[iseg].sectStart == ENDOFCHAIN) oend = oEnd; ULONG ulSize = ((i - 1) << uShift) - offset + oend + 1; ULONG bytecount; SCODE sc; if (pms->GetMiniFat() == pfat) { sc = pms->GetMiniStream()->CDirectStream::ReadAt( (sectStart << uShift) + offset, pBuffer, ulSize, (ULONG STACKBASED *)&bytecount); } else { ULARGE_INTEGER ulOffset; ULISet32(ulOffset, ConvertSectOffset(sectStart,offset,uShift)); sc = DfGetScode(pms->GetILB()->ReadAt(ulOffset, (BYTE *)pBuffer, ulSize, &bytecount)); } total += bytecount; if ((0 == cSect) || (FAILED(sc))) { _stmc.SetCache(start - 1, sectStart + i - 1); *pulRetval = total; msfDebugOut((DEB_TRACE, "Leaving CDirectStream::ReadAt()=>%lu, ret is %lu\n", sc,*pulRetval)); return sc; } pBuffer = (BYTE HUGEP *)pBuffer + bytecount; offset = 0; } } msfDebugOut((DEB_ERROR,"In CDirectStream::ReadAt - reached end of function\n")); Err: return sc; } //+------------------------------------------------------------------------- // // Member: CDirectStream::Write, public // // Synposis: Writes binary data from a linear single stream // // Effects: Modifies _ulSeekPos. May cause modification in parent // MStream. // // Arguments: [pBuffer] -- Pointer to the area from which the data // will be written. // [ulCount] -- Indicates the number of bytes to be written // [pulRetval] -- Pointer to area in which number of bytes // will be returned // // Returns: Error code of MStream call. // // Algorithm: Calculate sector and offset for beginning and end of // write, then pass call up to MStream. // // // Notes: // //--------------------------------------------------------------------------- SCODE CDirectStream::WriteAt( ULONG ulOffset, VOID const HUGEP *pBuffer, ULONG ulCount, ULONG STACKBASED *pulRetval) { msfDebugOut((DEB_TRACE,"In CDirectStream::WriteAt(%lu,%p,%lu)\n",ulOffset,pBuffer,ulCount)); *pulRetval = 0; if (0 == ulCount) return S_OK; SCODE sc; if (ulOffset + ulCount > _ulSize) { if (_ulSize > MINISTREAMSIZE) { } else { msfChk(SetSize(ulOffset + ulCount)); } } CMStream *pms; pms = _stmh.GetMS(); msfAssert(pms != NULL); // This should be an inline call to MWrite msfChk(pms->MWrite( _stmh.GetSid(), (_ulSize < MINISTREAMSIZE), ulOffset, pBuffer, ulCount, &_stmc, pulRetval)); msfAssert(*pulRetval == ulCount); msfDebugOut((DEB_TRACE,"Leaving CDirectStream::WriteAt()==>%lu, ret is %lu\n",sc,*pulRetval)); Err: if (ulOffset + *pulRetval > _ulSize) { SCODE scSet; _ulSize = ulOffset + *pulRetval; scSet = pms->GetDir()->SetSize(_stmh.GetSid(), _ulSize); if (SUCCEEDED(sc) && FAILED(scSet)) { sc = scSet; } } return sc; } //+------------------------------------------------------------------------- // // Member: CDirectStream::SetSize, public // // Synposis: Set the size of a linear stream // // Effects: Modifies _ulSize. May cause change in parent MStream. // // Arguments: [ulNewSize] -- New size for stream // // Returns: Error code returned by MStream call. // // Algorithm: Pass call up to parent. // // Notes: When changing the size of a stream, we need to be concerned // with the cases where each stream is either zero length, // stored in the ministream, or stored in a regular stream. The following // grid shows the actions that we must perform in each case: // // New Sector Count (Cn) // // 0 S L // O ------------------------------------------------ // l | same size | allocate Cn | allocate Cn // d 0 | (fast out) | small sectors | large sectors // ------------------------------------------------ // S | small | Co > Cn: | cbCopy = cbOld // e S | setchain(Cn) | small | large allocate Cn // c | | setchain(Cn)| copy bytes // t | | Cn > Co: | small setchain(0) // o | | extend small | copy data // r ------------------------------------------------ // L | large | cbCopy = cbNew| Co > Cn: // C | setchain(Cn) | small | large setchain(Cn) // o | | allocate Cn | Cn > Co: // u | | copy bytes | extend large // n | | large | // t | | setchain(0) | // | | copy data | // (Co) ------------------------------------------------ // // where S indicates small sectors, L indicates large sectors, and Cx // represents count of sectors. For example, the middle box represents // doing a setsize on a stream which is currently stored in a small // stream in Co small sectors and which will end up in a large stream // with Cn sectors. // //--------------------------------------------------------------------------- SCODE CDirectStream::SetSize(ULONG cbNewSize) { msfDebugOut((DEB_TRACE,"In CDirectStream::SetSize(%lu)\n",cbNewSize)); SCODE sc = S_OK; BYTE *pBuf = NULL; SID sid = _stmh.GetSid(); CMStream *pms = _stmh.GetMS(); msfAssert(sid <= MAXREGSID); CDirectory *pdir = pms->GetDir(); SECT sectOldStart; if (_ulSize == cbNewSize) { return S_OK; } USHORT cbpsOld = pms->GetSectorSize(); // Count of Bytes Per Sector USHORT cbpsNew = cbpsOld; CFat *pfatOld = pms->GetFat(); CFat *pfatNew = pfatOld; if (SIDMINISTREAM != sid) { if (cbNewSize < MINISTREAMSIZE) { cbpsNew = MINISECTORSIZE; pfatNew = pms->GetMiniFat(); } if (_ulSize < MINISTREAMSIZE) { cbpsOld = MINISECTORSIZE; pfatOld = pms->GetMiniFat(); } } ULONG csectOld = (ULONG)(_ulSize + cbpsOld - 1) / (ULONG) cbpsOld; ULONG csectNew = (ULONG)(cbNewSize + cbpsNew - 1) / (ULONG) cbpsNew; msfAssert(sid <= MAXREGSID); SECT sectstart; msfChk(pdir->GetStart(sid, §start)); msfDebugOut((DEB_ITRACE,"pdbOld size is %lu\n\tSid is %lu\n\tStart is %lu\n", _ulSize,sid,sectstart)); msfDebugOut((DEB_ITRACE,"CMStream::SetSize() needs %lu %u byte sectors\n", csectNew, cbpsNew)); msfDebugOut((DEB_ITRACE,"SetSize() currently has %lu %u byte sectors\n", csectOld, cbpsOld)); USHORT cbCopy; cbCopy = 0; if (cbpsOld != cbpsNew) { // Sector sizes are different, so we'll copy the data msfAssert((cbNewSize > _ulSize ? _ulSize : cbNewSize) < 0x10000); cbCopy =(USHORT)(cbNewSize > _ulSize ? _ulSize : cbNewSize); } if (cbCopy > 0) { msfDebugOut((DEB_ITRACE,"Copying between fat and minifat\n")); GetSafeBuffer(cbCopy, cbCopy, &pBuf, &cbCopy); msfAssert((pBuf != NULL) && aMsg("Couldn't get scratch buffer")); ULONG ulRetVal; sc = ReadAt(0, pBuf, cbCopy, (ULONG STACKBASED *)&ulRetVal); if ((FAILED(sc)) || ((ulRetVal != cbCopy) ? (sc = STG_E_UNKNOWN) : 0)) { msfErr(Err, sc); } //The cache is no longer valid, so empty it. _stmc.SetCache(MAX_ULONG, ENDOFCHAIN); //Save start sector so we can free it later. sectOldStart = sectstart; msfChk(pfatNew->Allocate(csectNew, §start)); } else { SECT dummy; if ((csectOld > csectNew)) { msfChk(pfatOld->SetChainLength(sectstart, csectNew)); if (0 == csectNew) { sectstart = ENDOFCHAIN; } //If this turns out to be a common case, we can // sometimes keep the cache valid here. _stmc.SetCache(MAX_ULONG, ENDOFCHAIN); } else if (0 == csectOld) { msfAssert(_stmc.GetOffset() == MAX_ULONG); msfChk(pfatNew->Allocate(csectNew, §start)); } else if (csectNew > csectOld) { ULONG start = csectNew - 1; if (start > _stmc.GetOffset()) { msfChk(pfatNew->GetESect( _stmc.GetSect(), start - _stmc.GetOffset(), &dummy)); } else if (start != _stmc.GetOffset()) { msfChk(pfatNew->GetESect(sectstart, start, &dummy)); } } } // Resize the ministream, if necessary if (((MINISECTORSIZE == cbpsOld) && (csectOld > 0)) || ((MINISECTORSIZE == cbpsNew) && (csectNew > 0))) { msfChk(pms->SetMiniSize()); } msfChk(pms->SetSize()); //If we fail on either of these operations and cbCopy != 0, // we will have data loss. Ick. msfChk(pdir->SetStart(sid, sectstart)); //If we fail here, were in trouble. msfChk(pdir->SetSize(sid, cbNewSize)); _ulSize = cbNewSize; if (cbCopy > 0) { // now copy the data ULONG ulRetVal; msfAssert(cbCopy <= _ulSize); msfChk(WriteAt(0, pBuf, cbCopy, (ULONG STACKBASED *)&ulRetVal)); if (ulRetVal != cbCopy) { msfErr(Err, STG_E_UNKNOWN); } msfChk(pfatOld->SetChainLength(sectOldStart, 0)); msfChk(pms->SetMiniSize()); msfChk(pms->SetSize()); } if (((csectNew > csectOld) || (cbCopy > 0)) && ((cbNewSize & (cbpsNew - 1)) != 0)) { SECT sectLast; if (csectNew - 1 > _stmc.GetOffset()) { msfChk(pfatNew->GetSect( _stmc.GetSect(), (csectNew - 1) - _stmc.GetOffset(), §Last)); } else { msfChk(pfatNew->GetSect(sectstart, csectNew - 1, §Last)); } msfVerify(SUCCEEDED(pms->SecureSect( sectLast, cbNewSize, (cbNewSize < MINISTREAMSIZE) && (sid != SIDMINISTREAM)))); } Err: FreeBuffer(pBuf); return sc; } //+--------------------------------------------------------------------------- // // Member: CDirectStream::Release, public // // Synopsis: Decrements the ref count and frees if necessary // //---------------------------------------------------------------------------- void CDirectStream::Release(VOID) { msfDebugOut((DEB_TRACE,"In CDirectStream::Release()\n")); msfAssert(_cReferences > 0); AtomicDec(&_cReferences); if (_cReferences == 0) delete this; msfDebugOut((DEB_TRACE,"Out CDirectStream::Release()\n")); } //+-------------------------------------------------------------- // // Member: CDirectStream::AddRef, public // // Synopsis: Increments the ref count // //--------------------------------------------------------------- void CDirectStream::AddRef(void) { msfDebugOut((DEB_ITRACE, "In CDirectStream::AddRef()\n")); AtomicInc(&_cReferences); msfDebugOut((DEB_ITRACE, "Out CDirectStream::AddRef, %lu\n", _cReferences)); } //+--------------------------------------------------------------------------- // // Member: CDirectStream::GetSize, public // // Synopsis: Gets the size of the stream // // Arguments: [pulSize] - Size return // // Modifies: [pulSize] // //---------------------------------------------------------------------------- void CDirectStream::GetSize(ULONG *pulSize) { *pulSize = _ulSize; } //+-------------------------------------------------------------- // // Member: CDirectStream::GetTime, public // // Synopsis: Gets a time // // Arguments: [wt] - Which time // [ptm] - Time return // // Returns: Appropriate status code // // Modifies: [ptm] // //--------------------------------------------------------------- SCODE CDirectStream::GetTime(WHICHTIME wt, TIME_T *ptm) { return _stmh.GetTime(wt, ptm); } //+-------------------------------------------------------------- // // Member: CDirectStream::SetTime, public // // Synopsis: Sets a time // // Arguments: [wt] - Which time // [tm] - New time // // Returns: Appropriate status code // //--------------------------------------------------------------- SCODE CDirectStream::SetTime(WHICHTIME wt, TIME_T tm) { return _stmh.SetTime(wt, tm); }