#ifndef _NFFMSTM_HXX_ #define _NFFMSTM_HXX_ #include class CNtfsStream; class CNtfsUpdateStreamForPropStg; //+---------------------------------------------------------------------------- // // Class: CNFFMappedStream // // This class is used by CNtfsStream, and implements the IMappedStream // interface for use on NFF (NTFS Flat-File) files. The mapping // exposed by this implementation is robust, in that updates to the // underlying stream are atomic (though not durable until flushed). // // This atomicity is accomplished by using stream renaming (which is only // supported in NTFS5). When this mapping is written to the underlying // NTFS files, it is written to a second stream (the "update" stream), // rather than the original stream. When Flush is called, // the update stream is renamed over the original stream. In order to // make NTFS stream renaming atomic, the overwritten stream must be // truncated first to have zero length. The the overall flush operation // is: truncate the original stream, rename the update stream over // the original stream, and optionally create a new update stream to be // ready for the next write. // // When the original stream is truncated, the update stream is then // considered master. So if there is a crash before the truncate, then // the original stream, with the original data, is master. If there is // a crash after the truncate but before the rename, then the update stream // is considered master. In this case, on the next open, the update stream // is either renamed over the zero-length original (if opening for write), // or the update stream is simply used as is (if opening for read). // // If the file isn't on an NTFS5 volume, so stream renaming isn't supported, // we just use the original stream unconditionally and there is no robustness. // // To accomplish all of this, this class works with two stream handles, // the original and the update. The original is the stream handle held // by the parent CNtfsStream. The update stream handle must behave like all other // stream handles, wrt USN, oplock, timestamps, etc (see CNtfsStream). // So the update stream is wrapped in a CNtfsStream too, but a special // one - the CNtfsUpdateStreamForPropStg derivation. An instance of this // CNtfsUpdateStreamForPropStg class is maintained by here (by CNFFMappedStream). // // // // +-------------+ +------------------+ // | |------->| | // | CNtfsStream | | CNFFMappedStream | // | |<-------| | // +-------------+ +------------------+ // | | +------------------------------+ // | | | | // | +----->| CNtfsUpdateStreamForPropStg | // V | : public CNtfsStream | // Original | | // NTFS +------------------------------+ // Stream | // | // V // Updated // NTFS // Stream // //+---------------------------------------------------------------------------- class CNFFMappedStream : public IMappedStream #if DBG , public IStorageTest // For testing only #endif { // Constructors public: CNFFMappedStream( CNtfsStream *pnffstm ); ~CNFFMappedStream(); // ------------- // IMappedStream // ------------- public: STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP_(VOID) Open(IN NTPROP np, OUT LONG *phr); STDMETHODIMP_(VOID) Close(OUT LONG *phr); STDMETHODIMP_(VOID) ReOpen(IN OUT VOID **ppv, OUT LONG *phr); STDMETHODIMP_(VOID) Quiesce(VOID); STDMETHODIMP_(VOID) Map(IN BOOLEAN fCreate, OUT VOID **ppv); STDMETHODIMP_(VOID) Unmap(IN BOOLEAN fFlush, IN OUT VOID **ppv); STDMETHODIMP_(VOID) Flush(OUT LONG *phr); STDMETHODIMP_(ULONG) GetSize(OUT LONG *phr); STDMETHODIMP_(VOID) SetSize(IN ULONG cb, IN BOOLEAN fPersistent, IN OUT VOID **ppv, OUT LONG *phr); STDMETHODIMP_(NTSTATUS) Lock(IN BOOLEAN fExclusive); STDMETHODIMP_(NTSTATUS) Unlock(VOID); STDMETHODIMP_(VOID) QueryTimeStamps(OUT STATPROPSETSTG *pspss, BOOLEAN fNonSimple) const; STDMETHODIMP_(BOOLEAN) QueryModifyTime(OUT LONGLONG *pll) const; STDMETHODIMP_(BOOLEAN) QuerySecurity(OUT ULONG *pul) const; STDMETHODIMP_(BOOLEAN) IsWriteable(VOID) const; STDMETHODIMP_(BOOLEAN) IsModified(VOID) const; STDMETHODIMP_(VOID) SetModified(OUT LONG *phr); STDMETHODIMP_(HANDLE) GetHandle(VOID) const; #if DBG STDMETHODIMP_(BOOLEAN) SetChangePending(BOOLEAN fChangePending); STDMETHODIMP_(BOOLEAN) IsNtMappedStream(VOID) const; #endif // ------------ // IStorageTest // ------------ public: #if DBG STDMETHOD(UseNTFS4Streams)( BOOL fUseNTFS4Streams ); STDMETHOD(GetFormatVersion)(WORD *pw); STDMETHOD(SimulateLowMemory)( BOOL fSimulate ); STDMETHOD_(LONG, GetLockCount)(); STDMETHOD(IsDirty)(); #endif // ----------------------------- // Public, non-interface methods // ----------------------------- public: inline BOOL IsMapped(); inline ULONG SizeOfMapping(); void Read( void *pv, ULONG ulOffset, ULONG *pcbCopy ); void Write( const void *pv, ULONG ulOffset, ULONG *pcbCopy ); HRESULT Init( HANDLE hStream ); HRESULT ShutDown(); // ---------------- // Internal Methods // ---------------- private: void InitMappedStreamMembers(); void BeginUsingUpdateStream(); void EndUsingUpdateStream(); void BeginUsingLatestStream(); void EndUsingLatestStream(); HRESULT WriteMappedStream(); enum enumCREATE_NEW_UPDATE_STREAM { CREATE_NEW_UPDATE_STREAM = 1, DONT_CREATE_NEW_UPDATE_STREAM = 2 }; inline HRESULT CreateUpdateStreamIfNecessary(); HRESULT ReplaceOriginalWithUpdate( enumCREATE_NEW_UPDATE_STREAM CreateNewUpdateStream ); HRESULT OpenUpdateStream( BOOL fCreate ); HRESULT RollForwardIfNecessary(); // -------------- // Internal state // -------------- private: // Containing stream (i.e., we're an embedded object in this CNtfsStream) CNtfsStream *_pnffstm; // Are we using global reserved memory? BOOL _fLowMem:1; // Does the mapped stream have unflushed changes? BOOL _fMappedStreamDirty:1; #if DBG // Should we pretent that CoTaskMemAlloc fails? BOOL _fSimulateLowMem:1; #endif // Is the latest data in _pstmUpdate? BOOL _fUpdateStreamHasLatest:1; // Have we already called the RollForwardIfNecessary method? BOOL _fCheckedForRollForward:1; // Is this an NTFS5 volume? BOOL _fStreamRenameSupported:1; // The current mapping BYTE *_pbMappedStream; // The size of the buffer referred to by _pbMappedStream ULONG _cbMappedStream; // The size of the underlying stream that _pbMappedStream represents ULONG _cbMappedStreamActual; // Cookie used in PrOnMappedStreamEvent VOID *_pMappedStreamOwner; // Count of calls to BeginUsingUpdateStream unmatched by a call to // EndUsingUpdateStream USHORT _cUpdateStreamInUse; // Count of calls to BeginUsingLatestStream unmatched by a call to // EndUsingLatestStream USHORT _cLatestStreamInUse; // The Update stream. CNtfsUpdateStreamForPropStg *_pstmUpdate; }; // class CNFFMappedStream inline CNFFMappedStream::CNFFMappedStream( CNtfsStream *pnffstm ) { _pnffstm = pnffstm; _pstmUpdate = NULL; _fUpdateStreamHasLatest = FALSE; IFDBG( _fSimulateLowMem = FALSE ); InitMappedStreamMembers(); } inline BOOL CNFFMappedStream::IsMapped() { return( NULL != _pbMappedStream ); } inline ULONG CNFFMappedStream::SizeOfMapping() { return( _cbMappedStream ); } #endif // #ifndef _NFFMSTM_HXX_