windows-nt/Source/XPSP1/NT/com/ole32/stg/props/nffmstm.hxx
2020-09-26 16:20:57 +08:00

261 lines
8.8 KiB
C++

#ifndef _NFFMSTM_HXX_
#define _NFFMSTM_HXX_
#include <propstm.hxx>
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_