261 lines
8.8 KiB
C++
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_
|