[1/2] ole32: Reorder storage32 functions to avoid forward declarations.
Francois Gouget
fgouget at free.fr
Sat May 2 16:52:44 CDT 2015
---
dlls/ole32/storage32.c | 12253 +++++++++++++++++++++++------------------------
dlls/ole32/storage32.h | 42 +-
2 files changed, 6107 insertions(+), 6188 deletions(-)
diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c
index 441b319..3ef1077 100644
--- a/dlls/ole32/storage32.c
+++ b/dlls/ole32/storage32.c
@@ -55,9 +55,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(storage);
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-#define OLESTREAM_ID 0x501
-#define OLESTREAM_MAX_STR_LEN 255
/*
* These are signatures to detect the type of Document file.
@@ -65,15 +62,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage);
static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
-static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
-{
- return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
-}
-
-static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
-{
- return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
-}
+extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
/****************************************************************************
* StorageInternalImpl definitions.
@@ -82,7 +71,7 @@ static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *i
* This one implements the IStorage interface for storage that are
* inside another storage.
*/
-struct StorageInternalImpl
+typedef struct StorageInternalImpl
{
struct StorageBaseImpl base;
@@ -92,45 +81,10 @@ struct StorageInternalImpl
struct list ParentListEntry;
StorageBaseImpl *parentStorage;
-};
-typedef struct StorageInternalImpl StorageInternalImpl;
+} StorageInternalImpl;
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
static const IStorageVtbl StorageInternalImpl_Vtbl;
-
-/* Method definitions for the StorageInternalImpl class. */
-static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
- DWORD openFlags, DirRef storageDirEntry);
-static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create);
-static void StorageImpl_Destroy(StorageBaseImpl* iface);
-static void StorageImpl_Invalidate(StorageBaseImpl* iface);
-static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
-static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
-static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
-static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
-static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
-static void StorageImpl_SaveFileHeader(StorageImpl* This);
-static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType);
-
-static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
-static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
-static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
-static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
-static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
-
-static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
-static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
-static ULONG BlockChainStream_GetCount(BlockChainStream* This);
-
-static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
-static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
-static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
- ULONG blockIndex, ULONG offset, DWORD value);
-static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
- ULONG blockIndex, ULONG offset, DWORD* value);
-
-static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
-static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
+static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
typedef struct TransactedDirEntry
{
@@ -197,6 +151,9 @@ typedef struct TransactedSnapshotImpl
ULONG lastTransactionSig;
} TransactedSnapshotImpl;
+static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
+static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
+
typedef struct TransactedSharedImpl
{
struct StorageBaseImpl base;
@@ -215,5411 +172,5183 @@ typedef struct TransactedSharedImpl
ULONG lastTransactionSig;
} TransactedSharedImpl;
-/* Generic function to create a transacted wrapper for a direct storage object. */
-static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, BOOL toplevel, StorageBaseImpl** result);
-
-/* OLESTREAM memory structure to use for Get and Put Routines */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
- DWORD dwOleID;
- DWORD dwTypeID;
- DWORD dwOleTypeNameLength;
- CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
- CHAR *pstrOleObjFileName;
- DWORD dwOleObjFileNameLength;
- DWORD dwMetaFileWidth;
- DWORD dwMetaFileHeight;
- CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
- DWORD dwDataLength;
- BYTE *pData;
-}OLECONVERT_OLESTREAM_DATA;
-
-/* CompObj Stream structure */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
- BYTE byUnknown1[12];
- CLSID clsid;
- DWORD dwCLSIDNameLength;
- CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
- DWORD dwOleTypeNameLength;
- CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
- DWORD dwProgIDNameLength;
- CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
- BYTE byUnknown2[16];
-}OLECONVERT_ISTORAGE_COMPOBJ;
+static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
+static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
-/* Ole Presentation Stream structure */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
- BYTE byUnknown1[28];
- DWORD dwExtentX;
- DWORD dwExtentY;
- DWORD dwSize;
- BYTE *pData;
-}OLECONVERT_ISTORAGE_OLEPRES;
+/************************************************************************
+ * STGM Functions
+ ***********************************************************************/
-/***********************************************************************
- * Forward declaration of internal functions used by the method DestroyElement
+/************************************************************************
+ * This method validates an STGM parameter that can contain the values below
+ *
+ * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
+ * The stgm values contained in 0xffff0000 are bitmasks.
+ *
+ * STGM_DIRECT 0x00000000
+ * STGM_TRANSACTED 0x00010000
+ * STGM_SIMPLE 0x08000000
+ *
+ * STGM_READ 0x00000000
+ * STGM_WRITE 0x00000001
+ * STGM_READWRITE 0x00000002
+ *
+ * STGM_SHARE_DENY_NONE 0x00000040
+ * STGM_SHARE_DENY_READ 0x00000030
+ * STGM_SHARE_DENY_WRITE 0x00000020
+ * STGM_SHARE_EXCLUSIVE 0x00000010
+ *
+ * STGM_PRIORITY 0x00040000
+ * STGM_DELETEONRELEASE 0x04000000
+ *
+ * STGM_CREATE 0x00001000
+ * STGM_CONVERT 0x00020000
+ * STGM_FAILIFTHERE 0x00000000
+ *
+ * STGM_NOSCRATCH 0x00100000
+ * STGM_NOSNAPSHOT 0x00200000
*/
-static HRESULT deleteStorageContents(
- StorageBaseImpl *parentStorage,
- DirRef indexToDelete,
- DirEntry entryDataToDelete);
+static HRESULT validateSTGM(DWORD stgm)
+{
+ DWORD access = STGM_ACCESS_MODE(stgm);
+ DWORD share = STGM_SHARE_MODE(stgm);
+ DWORD create = STGM_CREATE_MODE(stgm);
-static HRESULT deleteStreamContents(
- StorageBaseImpl *parentStorage,
- DirRef indexToDelete,
- DirEntry entryDataToDelete);
+ if (stgm&~STGM_KNOWN_FLAGS)
+ {
+ ERR("unknown flags %08x\n", stgm);
+ return E_FAIL;
+ }
-static HRESULT removeFromTree(
- StorageBaseImpl *This,
- DirRef parentStorageIndex,
- DirRef deletedIndex);
+ switch (access)
+ {
+ case STGM_READ:
+ case STGM_WRITE:
+ case STGM_READWRITE:
+ break;
+ default:
+ return E_FAIL;
+ }
-/***********************************************************************
- * Declaration of the functions used to manipulate DirEntry
- */
+ switch (share)
+ {
+ case STGM_SHARE_DENY_NONE:
+ case STGM_SHARE_DENY_READ:
+ case STGM_SHARE_DENY_WRITE:
+ case STGM_SHARE_EXCLUSIVE:
+ break;
+ case 0:
+ if (!(stgm & STGM_TRANSACTED))
+ return E_FAIL;
+ break;
+ default:
+ return E_FAIL;
+ }
-static HRESULT insertIntoTree(
- StorageBaseImpl *This,
- DirRef parentStorageIndex,
- DirRef newEntryIndex);
+ switch (create)
+ {
+ case STGM_CREATE:
+ case STGM_FAILIFTHERE:
+ break;
+ default:
+ return E_FAIL;
+ }
-static LONG entryNameCmp(
- const OLECHAR *name1,
- const OLECHAR *name2);
-
-static DirRef findElement(
- StorageBaseImpl *storage,
- DirRef storageEntry,
- const OLECHAR *name,
- DirEntry *data);
-
-static HRESULT findTreeParent(
- StorageBaseImpl *storage,
- DirRef storageEntry,
- const OLECHAR *childName,
- DirEntry *parentData,
- DirRef *parentEntry,
- ULONG *relation);
+ /*
+ * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
+ */
+ if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
+ return E_FAIL;
-/***********************************************************************
- * Declaration of miscellaneous functions...
- */
-static HRESULT validateSTGM(DWORD stgmValue);
+ /*
+ * STGM_CREATE | STGM_CONVERT
+ * if both are false, STGM_FAILIFTHERE is set to TRUE
+ */
+ if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
+ return E_FAIL;
-static DWORD GetShareModeFromSTGM(DWORD stgm);
-static DWORD GetAccessModeFromSTGM(DWORD stgm);
-static DWORD GetCreationModeFromSTGM(DWORD stgm);
+ /*
+ * STGM_NOSCRATCH requires STGM_TRANSACTED
+ */
+ if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
+ return E_FAIL;
-extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
+ /*
+ * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
+ * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
+ */
+ if ( (stgm & STGM_NOSNAPSHOT) &&
+ (!(stgm & STGM_TRANSACTED) ||
+ share == STGM_SHARE_EXCLUSIVE ||
+ share == STGM_SHARE_DENY_WRITE) )
+ return E_FAIL;
+ return S_OK;
+}
-/****************************************************************************
- * IEnumSTATSTGImpl definitions.
+/************************************************************************
+ * GetShareModeFromSTGM
*
- * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
- * This class allows iterating through the content of a storage and to find
- * specific items inside it.
+ * This method will return a share mode flag from a STGM value.
+ * The STGM value is assumed valid.
*/
-struct IEnumSTATSTGImpl
+static DWORD GetShareModeFromSTGM(DWORD stgm)
{
- IEnumSTATSTG IEnumSTATSTG_iface;
-
- LONG ref; /* Reference count */
- StorageBaseImpl* parentStorage; /* Reference to the parent storage */
- DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
-
- WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
-};
+ switch (STGM_SHARE_MODE(stgm))
+ {
+ case 0:
+ assert(stgm & STGM_TRANSACTED);
+ /* fall-through */
+ case STGM_SHARE_DENY_NONE:
+ return FILE_SHARE_READ | FILE_SHARE_WRITE;
+ case STGM_SHARE_DENY_READ:
+ return FILE_SHARE_WRITE;
+ case STGM_SHARE_DENY_WRITE:
+ case STGM_SHARE_EXCLUSIVE:
+ return FILE_SHARE_READ;
+ }
+ ERR("Invalid share mode!\n");
+ assert(0);
+ return 0;
+}
-static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
+/************************************************************************
+ * GetAccessModeFromSTGM
+ *
+ * This method will return an access mode flag from a STGM value.
+ * The STGM value is assumed valid.
+ */
+static DWORD GetAccessModeFromSTGM(DWORD stgm)
{
- return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
+ switch (STGM_ACCESS_MODE(stgm))
+ {
+ case STGM_READ:
+ return GENERIC_READ;
+ case STGM_WRITE:
+ case STGM_READWRITE:
+ return GENERIC_READ | GENERIC_WRITE;
+ }
+ ERR("Invalid access mode!\n");
+ assert(0);
+ return 0;
}
+/************************************************************************
+ * GetCreationModeFromSTGM
+ *
+ * This method will return a creation mode flag from a STGM value.
+ * The STGM value is assumed valid.
+ */
+static DWORD GetCreationModeFromSTGM(DWORD stgm)
+{
+ switch(STGM_CREATE_MODE(stgm))
+ {
+ case STGM_CREATE:
+ return CREATE_ALWAYS;
+ case STGM_CONVERT:
+ FIXME("STGM_CONVERT not implemented!\n");
+ return CREATE_NEW;
+ case STGM_FAILIFTHERE:
+ return CREATE_NEW;
+ }
+ ERR("Invalid create mode!\n");
+ assert(0);
+ return 0;
+}
-static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
-static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
/************************************************************************
-** Block Functions
-*/
+ * IDirectWriterLock implementation
+ ***********************************************************************/
-static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
+static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
{
- return (ULONGLONG)(index+1) * This->bigBlockSize;
+ return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
}
-/************************************************************************
-** StorageImpl implementation
-*/
-static HRESULT StorageImpl_ReadAt(StorageImpl* This,
- ULARGE_INTEGER offset,
- void* buffer,
- ULONG size,
- ULONG* bytesRead)
+static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
{
- return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
}
-static HRESULT StorageImpl_WriteAt(StorageImpl* This,
- ULARGE_INTEGER offset,
- const void* buffer,
- const ULONG size,
- ULONG* bytesWritten)
+static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
{
- return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ return IStorage_AddRef(&This->IStorage_iface);
}
-/************************************************************************
- * StorageBaseImpl_QueryInterface (IUnknown)
- *
- * This method implements the common QueryInterface for all IStorage
- * implementations contained in this file.
- *
- * See Windows documentation for more details on IUnknown methods.
- */
-static HRESULT WINAPI StorageBaseImpl_QueryInterface(
- IStorage* iface,
- REFIID riid,
- void** ppvObject)
+static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ return IStorage_Release(&This->IStorage_iface);
+}
- if (!ppvObject)
- return E_INVALIDARG;
+static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
+{
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ FIXME("(%p)->(%d): stub\n", This, timeout);
+ return E_NOTIMPL;
+}
- *ppvObject = 0;
+static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
+{
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ FIXME("(%p): stub\n", This);
+ return E_NOTIMPL;
+}
- if (IsEqualGUID(&IID_IUnknown, riid) ||
- IsEqualGUID(&IID_IStorage, riid))
- {
- *ppvObject = &This->IStorage_iface;
- }
- else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
- {
- *ppvObject = &This->IPropertySetStorage_iface;
- }
- /* locking interface is reported for writer only */
- else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
- {
- *ppvObject = &This->IDirectWriterLock_iface;
- }
- else
- return E_NOINTERFACE;
+static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
+{
+ StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+ FIXME("(%p): stub\n", This);
+ return E_NOTIMPL;
+}
- IStorage_AddRef(iface);
+static const IDirectWriterLockVtbl DirectWriterLockVtbl =
+{
+ directwriterlock_QueryInterface,
+ directwriterlock_AddRef,
+ directwriterlock_Release,
+ directwriterlock_WaitForWriteAccess,
+ directwriterlock_ReleaseWriteAccess,
+ directwriterlock_HaveWriteAccess
+};
- return S_OK;
-}
/************************************************************************
- * StorageBaseImpl_AddRef (IUnknown)
+ * StorageBaseImpl implementation : Tree helper functions
+ ***********************************************************************/
+
+/****************************************************************************
*
- * This method implements the common AddRef for all IStorage
- * implementations contained in this file.
+ * Internal Method
*
- * See Windows documentation for more details on IUnknown methods.
+ * Case insensitive comparison of DirEntry.name by first considering
+ * their size.
+ *
+ * Returns <0 when name1 < name2
+ * >0 when name1 > name2
+ * 0 when name1 == name2
*/
-static ULONG WINAPI StorageBaseImpl_AddRef(
- IStorage* iface)
+static LONG entryNameCmp(
+ const OLECHAR *name1,
+ const OLECHAR *name2)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- ULONG ref = InterlockedIncrement(&This->ref);
+ LONG diff = lstrlenW(name1) - lstrlenW(name2);
- TRACE("(%p) AddRef to %d\n", This, ref);
+ while (diff == 0 && *name1 != 0)
+ {
+ /*
+ * We compare the string themselves only when they are of the same length
+ */
+ diff = toupperW(*name1++) - toupperW(*name2++);
+ }
- return ref;
+ return diff;
}
-/************************************************************************
- * StorageBaseImpl_Release (IUnknown)
+/****************************************************************************
*
- * This method implements the common Release for all IStorage
- * implementations contained in this file.
+ * Internal Method
*
- * See Windows documentation for more details on IUnknown methods.
+ * Find and read the element of a storage with the given name.
*/
-static ULONG WINAPI StorageBaseImpl_Release(
- IStorage* iface)
+static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
+ const OLECHAR *name, DirEntry *data)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
+ DirRef currentEntry;
- ULONG ref = InterlockedDecrement(&This->ref);
+ /* Read the storage entry to find the root of the tree. */
+ StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
- TRACE("(%p) ReleaseRef to %d\n", This, ref);
+ currentEntry = data->dirRootEntry;
- if (ref == 0)
+ while (currentEntry != DIRENTRY_NULL)
{
- /*
- * Since we are using a system of base-classes, we want to call the
- * destructor of the appropriate derived class. To do this, we are
- * using virtual functions to implement the destructor.
- */
- StorageBaseImpl_Destroy(This);
+ LONG cmp;
+
+ StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
+
+ cmp = entryNameCmp(name, data->name);
+
+ if (cmp == 0)
+ /* found it */
+ break;
+
+ else if (cmp < 0)
+ currentEntry = data->leftChild;
+
+ else if (cmp > 0)
+ currentEntry = data->rightChild;
}
- return ref;
+ return currentEntry;
}
-/************************************************************************
- * StorageBaseImpl_OpenStream (IStorage)
+/****************************************************************************
*
- * This method will open the specified stream object from the current storage.
+ * Internal Method
*
- * See Windows documentation for more details on IStorage methods.
+ * Find and read the binary tree parent of the element with the given name.
+ *
+ * If there is no such element, find a place where it could be inserted and
+ * return STG_E_FILENOTFOUND.
*/
-static HRESULT WINAPI StorageBaseImpl_OpenStream(
- IStorage* iface,
- const OLECHAR* pwcsName, /* [string][in] */
- void* reserved1, /* [unique][in] */
- DWORD grfMode, /* [in] */
- DWORD reserved2, /* [in] */
- IStream** ppstm) /* [out] */
+static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
+ const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
+ ULONG *relation)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- StgStreamImpl* newStream;
- DirEntry currentEntry;
- DirRef streamEntryRef;
- HRESULT res = STG_E_UNKNOWN;
+ DirRef childEntry;
+ DirEntry childData;
- TRACE("(%p, %s, %p, %x, %d, %p)\n",
- iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
+ /* Read the storage entry to find the root of the tree. */
+ StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
- if ( (pwcsName==NULL) || (ppstm==0) )
- {
- res = E_INVALIDARG;
- goto end;
- }
+ *parentEntry = storageEntry;
+ *relation = DIRENTRY_RELATION_DIR;
- *ppstm = NULL;
+ childEntry = parentData->dirRootEntry;
- if ( FAILED( validateSTGM(grfMode) ) ||
- STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+ while (childEntry != DIRENTRY_NULL)
{
- res = STG_E_INVALIDFLAG;
- goto end;
- }
+ LONG cmp;
- /*
- * As documented.
- */
- if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
- {
- res = STG_E_INVALIDFUNCTION;
- goto end;
- }
+ StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
- if (This->reverted)
- {
- res = STG_E_REVERTED;
- goto end;
- }
+ cmp = entryNameCmp(childName, childData.name);
- /*
- * Check that we're compatible with the parent's storage mode, but
- * only if we are not in transacted mode
- */
- if(!(This->openFlags & STGM_TRANSACTED)) {
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+ if (cmp == 0)
+ /* found it */
+ break;
+
+ else if (cmp < 0)
{
- res = STG_E_INVALIDFLAG;
- goto end;
- }
- }
+ *parentData = childData;
+ *parentEntry = childEntry;
+ *relation = DIRENTRY_RELATION_PREVIOUS;
- /*
- * Search for the element with the given name
- */
- streamEntryRef = findElement(
- This,
- This->storageDirEntry,
- pwcsName,
- ¤tEntry);
+ childEntry = parentData->leftChild;
+ }
- /*
- * If it was found, construct the stream object and return a pointer to it.
- */
- if ( (streamEntryRef!=DIRENTRY_NULL) &&
- (currentEntry.stgType==STGTY_STREAM) )
- {
- if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
+ else if (cmp > 0)
{
- /* A single stream cannot be opened a second time. */
- res = STG_E_ACCESSDENIED;
- goto end;
- }
-
- newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
-
- if (newStream)
- {
- newStream->grfMode = grfMode;
- *ppstm = &newStream->IStream_iface;
-
- IStream_AddRef(*ppstm);
+ *parentData = childData;
+ *parentEntry = childEntry;
+ *relation = DIRENTRY_RELATION_NEXT;
- res = S_OK;
- goto end;
+ childEntry = parentData->rightChild;
}
-
- res = E_OUTOFMEMORY;
- goto end;
}
- res = STG_E_FILENOTFOUND;
+ if (childEntry == DIRENTRY_NULL)
+ return STG_E_FILENOTFOUND;
+ else
+ return S_OK;
+}
-end:
- if (res == S_OK)
- TRACE("<-- IStream %p\n", *ppstm);
- TRACE("<-- %08x\n", res);
- return res;
+static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
+{
+ switch (relation)
+ {
+ case DIRENTRY_RELATION_PREVIOUS:
+ entry->leftChild = new_target;
+ break;
+ case DIRENTRY_RELATION_NEXT:
+ entry->rightChild = new_target;
+ break;
+ case DIRENTRY_RELATION_DIR:
+ entry->dirRootEntry = new_target;
+ break;
+ default:
+ assert(0);
+ }
}
-/************************************************************************
- * StorageBaseImpl_OpenStorage (IStorage)
+/****************************************************************************
*
- * This method will open a new storage object from the current storage.
+ * Internal Method
*
- * See Windows documentation for more details on IStorage methods.
+ * Add a directory entry to a storage
*/
-static HRESULT WINAPI StorageBaseImpl_OpenStorage(
- IStorage* iface,
- const OLECHAR* pwcsName, /* [string][unique][in] */
- IStorage* pstgPriority, /* [unique][in] */
- DWORD grfMode, /* [in] */
- SNB snbExclude, /* [unique][in] */
- DWORD reserved, /* [in] */
- IStorage** ppstg) /* [out] */
+static HRESULT insertIntoTree(
+ StorageBaseImpl *This,
+ DirRef parentStorageIndex,
+ DirRef newEntryIndex)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- StorageInternalImpl* newStorage;
- StorageBaseImpl* newTransactedStorage;
- DirEntry currentEntry;
- DirRef storageEntryRef;
- HRESULT res = STG_E_UNKNOWN;
-
- TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
- iface, debugstr_w(pwcsName), pstgPriority,
- grfMode, snbExclude, reserved, ppstg);
-
- if ((pwcsName==NULL) || (ppstg==0) )
- {
- res = E_INVALIDARG;
- goto end;
- }
-
- if (This->openFlags & STGM_SIMPLE)
- {
- res = STG_E_INVALIDFUNCTION;
- goto end;
- }
-
- /* as documented */
- if (snbExclude != NULL)
- {
- res = STG_E_INVALIDPARAMETER;
- goto end;
- }
-
- if ( FAILED( validateSTGM(grfMode) ))
- {
- res = STG_E_INVALIDFLAG;
- goto end;
- }
+ DirEntry currentEntry;
+ DirEntry newEntry;
/*
- * As documented.
+ * Read the inserted entry
*/
- if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
- (grfMode & STGM_DELETEONRELEASE) ||
- (grfMode & STGM_PRIORITY) )
- {
- res = STG_E_INVALIDFUNCTION;
- goto end;
- }
-
- if (This->reverted)
- return STG_E_REVERTED;
+ StorageBaseImpl_ReadDirEntry(This,
+ newEntryIndex,
+ &newEntry);
/*
- * Check that we're compatible with the parent's storage mode,
- * but only if we are not transacted
+ * Read the storage entry
*/
- if(!(This->openFlags & STGM_TRANSACTED)) {
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
- {
- res = STG_E_ACCESSDENIED;
- goto end;
- }
- }
+ StorageBaseImpl_ReadDirEntry(This,
+ parentStorageIndex,
+ ¤tEntry);
- *ppstg = NULL;
+ if (currentEntry.dirRootEntry != DIRENTRY_NULL)
+ {
+ /*
+ * The root storage contains some element, therefore, start the research
+ * for the appropriate location.
+ */
+ BOOL found = FALSE;
+ DirRef current, next, previous, currentEntryId;
- storageEntryRef = findElement(
- This,
- This->storageDirEntry,
- pwcsName,
- ¤tEntry);
+ /*
+ * Keep a reference to the root of the storage's element tree
+ */
+ currentEntryId = currentEntry.dirRootEntry;
- if ( (storageEntryRef!=DIRENTRY_NULL) &&
- (currentEntry.stgType==STGTY_STORAGE) )
- {
- if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
- {
- /* A single storage cannot be opened a second time. */
- res = STG_E_ACCESSDENIED;
- goto end;
- }
+ /*
+ * Read
+ */
+ StorageBaseImpl_ReadDirEntry(This,
+ currentEntry.dirRootEntry,
+ ¤tEntry);
- newStorage = StorageInternalImpl_Construct(
- This,
- grfMode,
- storageEntryRef);
+ previous = currentEntry.leftChild;
+ next = currentEntry.rightChild;
+ current = currentEntryId;
- if (newStorage != 0)
+ while (!found)
{
- if (grfMode & STGM_TRANSACTED)
- {
- res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
+ LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
- if (FAILED(res))
+ if (diff < 0)
+ {
+ if (previous != DIRENTRY_NULL)
{
- HeapFree(GetProcessHeap(), 0, newStorage);
- goto end;
+ StorageBaseImpl_ReadDirEntry(This,
+ previous,
+ ¤tEntry);
+ current = previous;
+ }
+ else
+ {
+ currentEntry.leftChild = newEntryIndex;
+ StorageBaseImpl_WriteDirEntry(This,
+ current,
+ ¤tEntry);
+ found = TRUE;
+ }
+ }
+ else if (diff > 0)
+ {
+ if (next != DIRENTRY_NULL)
+ {
+ StorageBaseImpl_ReadDirEntry(This,
+ next,
+ ¤tEntry);
+ current = next;
+ }
+ else
+ {
+ currentEntry.rightChild = newEntryIndex;
+ StorageBaseImpl_WriteDirEntry(This,
+ current,
+ ¤tEntry);
+ found = TRUE;
}
-
- *ppstg = &newTransactedStorage->IStorage_iface;
}
else
{
- *ppstg = &newStorage->base.IStorage_iface;
+ /*
+ * Trying to insert an item with the same name in the
+ * subtree structure.
+ */
+ return STG_E_FILEALREADYEXISTS;
}
- list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
-
- res = S_OK;
- goto end;
+ previous = currentEntry.leftChild;
+ next = currentEntry.rightChild;
}
-
- res = STG_E_INSUFFICIENTMEMORY;
- goto end;
+ }
+ else
+ {
+ /*
+ * The storage is empty, make the new entry the root of its element tree
+ */
+ currentEntry.dirRootEntry = newEntryIndex;
+ StorageBaseImpl_WriteDirEntry(This,
+ parentStorageIndex,
+ ¤tEntry);
}
- res = STG_E_FILENOTFOUND;
-
-end:
- TRACE("<-- %08x\n", res);
- return res;
+ return S_OK;
}
-/************************************************************************
- * StorageBaseImpl_EnumElements (IStorage)
+/*************************************************************************
*
- * This method will create an enumerator object that can be used to
- * retrieve information about all the elements in the storage object.
+ * Internal Method
*
- * See Windows documentation for more details on IStorage methods.
+ * This method removes a directory entry from its parent storage tree without
+ * freeing any resources attached to it.
*/
-static HRESULT WINAPI StorageBaseImpl_EnumElements(
- IStorage* iface,
- DWORD reserved1, /* [in] */
- void* reserved2, /* [size_is][unique][in] */
- DWORD reserved3, /* [in] */
- IEnumSTATSTG** ppenum) /* [out] */
+static HRESULT removeFromTree(
+ StorageBaseImpl *This,
+ DirRef parentStorageIndex,
+ DirRef deletedIndex)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- IEnumSTATSTGImpl* newEnum;
+ DirEntry entryToDelete;
+ DirEntry parentEntry;
+ DirRef parentEntryRef;
+ ULONG typeOfRelation;
+ HRESULT hr;
- TRACE("(%p, %d, %p, %d, %p)\n",
- iface, reserved1, reserved2, reserved3, ppenum);
+ hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
- if (!ppenum)
- return E_INVALIDARG;
+ if (hr != S_OK)
+ return hr;
- if (This->reverted)
- return STG_E_REVERTED;
+ /*
+ * Find the element that links to the one we want to delete.
+ */
+ hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
+ &parentEntry, &parentEntryRef, &typeOfRelation);
- newEnum = IEnumSTATSTGImpl_Construct(
- This,
- This->storageDirEntry);
+ if (hr != S_OK)
+ return hr;
- if (newEnum)
+ if (entryToDelete.leftChild != DIRENTRY_NULL)
{
- *ppenum = &newEnum->IEnumSTATSTG_iface;
- return S_OK;
- }
-
- return E_OUTOFMEMORY;
-}
+ /*
+ * Replace the deleted entry with its left child
+ */
+ setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
-/************************************************************************
- * StorageBaseImpl_Stat (IStorage)
- *
- * This method will retrieve information about this storage object.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_Stat(
- IStorage* iface,
- STATSTG* pstatstg, /* [out] */
- DWORD grfStatFlag) /* [in] */
-{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- DirEntry currentEntry;
- HRESULT res = STG_E_UNKNOWN;
+ hr = StorageBaseImpl_WriteDirEntry(
+ This,
+ parentEntryRef,
+ &parentEntry);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
- TRACE("(%p, %p, %x)\n",
- iface, pstatstg, grfStatFlag);
+ if (entryToDelete.rightChild != DIRENTRY_NULL)
+ {
+ /*
+ * We need to reinsert the right child somewhere. We already know it and
+ * its children are greater than everything in the left tree, so we
+ * insert it at the rightmost point in the left tree.
+ */
+ DirRef newRightChildParent = entryToDelete.leftChild;
+ DirEntry newRightChildParentEntry;
- if (!pstatstg)
- {
- res = E_INVALIDARG;
- goto end;
- }
+ do
+ {
+ hr = StorageBaseImpl_ReadDirEntry(
+ This,
+ newRightChildParent,
+ &newRightChildParentEntry);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
- if (This->reverted)
- {
- res = STG_E_REVERTED;
- goto end;
- }
+ if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
+ newRightChildParent = newRightChildParentEntry.rightChild;
+ } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
- res = StorageBaseImpl_ReadDirEntry(
- This,
- This->storageDirEntry,
- ¤tEntry);
+ newRightChildParentEntry.rightChild = entryToDelete.rightChild;
- if (SUCCEEDED(res))
+ hr = StorageBaseImpl_WriteDirEntry(
+ This,
+ newRightChildParent,
+ &newRightChildParentEntry);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ }
+ else
{
- StorageUtl_CopyDirEntryToSTATSTG(
- This,
- pstatstg,
- ¤tEntry,
- grfStatFlag);
+ /*
+ * Replace the deleted entry with its right child
+ */
+ setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
- pstatstg->grfMode = This->openFlags;
- pstatstg->grfStateBits = This->stateBits;
+ hr = StorageBaseImpl_WriteDirEntry(
+ This,
+ parentEntryRef,
+ &parentEntry);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
}
-end:
- if (res == S_OK)
- {
- TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
- }
- TRACE("<-- %08x\n", res);
- return res;
+ return hr;
}
+
/************************************************************************
- * StorageBaseImpl_RenameElement (IStorage)
- *
- * This method will rename the specified element.
+ * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
+ ***********************************************************************/
+
+/*
+ * IEnumSTATSTGImpl definitions.
*
- * See Windows documentation for more details on IStorage methods.
+ * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
+ * This class allows iterating through the content of a storage and to find
+ * specific items inside it.
*/
-static HRESULT WINAPI StorageBaseImpl_RenameElement(
- IStorage* iface,
- const OLECHAR* pwcsOldName, /* [in] */
- const OLECHAR* pwcsNewName) /* [in] */
+struct IEnumSTATSTGImpl
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- DirEntry currentEntry;
- DirRef currentEntryRef;
-
- TRACE("(%p, %s, %s)\n",
- iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
-
- if (This->reverted)
- return STG_E_REVERTED;
-
- currentEntryRef = findElement(This,
- This->storageDirEntry,
- pwcsNewName,
- ¤tEntry);
+ IEnumSTATSTG IEnumSTATSTG_iface;
- if (currentEntryRef != DIRENTRY_NULL)
- {
- /*
- * There is already an element with the new name
- */
- return STG_E_FILEALREADYEXISTS;
- }
+ LONG ref; /* Reference count */
+ StorageBaseImpl* parentStorage; /* Reference to the parent storage */
+ DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
- /*
- * Search for the old element name
- */
- currentEntryRef = findElement(This,
- This->storageDirEntry,
- pwcsOldName,
- ¤tEntry);
+ WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
+};
- if (currentEntryRef != DIRENTRY_NULL)
- {
- if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
- StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
- {
- WARN("Element is already open; cannot rename.\n");
- return STG_E_ACCESSDENIED;
- }
+static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
+{
+ return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
+}
- /* Remove the element from its current position in the tree */
- removeFromTree(This, This->storageDirEntry,
- currentEntryRef);
+static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
+{
+ IStorage_Release(&This->parentStorage->IStorage_iface);
+ HeapFree(GetProcessHeap(), 0, This);
+}
- /* Change the name of the element */
- strcpyW(currentEntry.name, pwcsNewName);
+static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
+ IEnumSTATSTG* iface,
+ REFIID riid,
+ void** ppvObject)
+{
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- /* Delete any sibling links */
- currentEntry.leftChild = DIRENTRY_NULL;
- currentEntry.rightChild = DIRENTRY_NULL;
+ if (ppvObject==0)
+ return E_INVALIDARG;
- StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
- ¤tEntry);
+ *ppvObject = 0;
- /* Insert the element in a new position in the tree */
- insertIntoTree(This, This->storageDirEntry,
- currentEntryRef);
- }
- else
+ if (IsEqualGUID(&IID_IUnknown, riid) ||
+ IsEqualGUID(&IID_IEnumSTATSTG, riid))
{
- /*
- * There is no element with the old name
- */
- return STG_E_FILENOTFOUND;
+ *ppvObject = &This->IEnumSTATSTG_iface;
+ IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
+ return S_OK;
}
- return StorageBaseImpl_Flush(This);
+ return E_NOINTERFACE;
}
-/************************************************************************
- * StorageBaseImpl_CreateStream (IStorage)
- *
- * This method will create a stream object within this storage
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_CreateStream(
- IStorage* iface,
- const OLECHAR* pwcsName, /* [string][in] */
- DWORD grfMode, /* [in] */
- DWORD reserved1, /* [in] */
- DWORD reserved2, /* [in] */
- IStream** ppstm) /* [out] */
+static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
+ IEnumSTATSTG* iface)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- StgStreamImpl* newStream;
- DirEntry currentEntry, newStreamEntry;
- DirRef currentEntryRef, newStreamEntryRef;
- HRESULT hr;
-
- TRACE("(%p, %s, %x, %d, %d, %p)\n",
- iface, debugstr_w(pwcsName), grfMode,
- reserved1, reserved2, ppstm);
-
- if (ppstm == 0)
- return STG_E_INVALIDPOINTER;
-
- if (pwcsName == 0)
- return STG_E_INVALIDNAME;
-
- if (reserved1 || reserved2)
- return STG_E_INVALIDPARAMETER;
-
- if ( FAILED( validateSTGM(grfMode) ))
- return STG_E_INVALIDFLAG;
-
- if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
- return STG_E_INVALIDFLAG;
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ return InterlockedIncrement(&This->ref);
+}
- if (This->reverted)
- return STG_E_REVERTED;
+static ULONG WINAPI IEnumSTATSTGImpl_Release(
+ IEnumSTATSTG* iface)
+{
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- /*
- * As documented.
- */
- if ((grfMode & STGM_DELETEONRELEASE) ||
- (grfMode & STGM_TRANSACTED))
- return STG_E_INVALIDFUNCTION;
+ ULONG newRef;
- /*
- * Don't worry about permissions in transacted mode, as we can always write
- * changes; we just can't always commit them.
- */
- if(!(This->openFlags & STGM_TRANSACTED)) {
- /* Can't create a stream on read-only storage */
- if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
- return STG_E_ACCESSDENIED;
+ newRef = InterlockedDecrement(&This->ref);
- /* Can't create a stream with greater access than the parent. */
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
- return STG_E_ACCESSDENIED;
+ if (newRef==0)
+ {
+ IEnumSTATSTGImpl_Destroy(This);
}
- if(This->openFlags & STGM_SIMPLE)
- if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
+ return newRef;
+}
- *ppstm = 0;
+static HRESULT IEnumSTATSTGImpl_GetNextRef(
+ IEnumSTATSTGImpl* This,
+ DirRef *ref)
+{
+ DirRef result = DIRENTRY_NULL;
+ DirRef searchNode;
+ DirEntry entry;
+ HRESULT hr;
+ WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
- currentEntryRef = findElement(This,
- This->storageDirEntry,
- pwcsName,
- ¤tEntry);
+ hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
+ This->parentStorage->storageDirEntry, &entry);
+ searchNode = entry.dirRootEntry;
- if (currentEntryRef != DIRENTRY_NULL)
+ while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
{
- /*
- * An element with this name already exists
- */
- if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
+ hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
+
+ if (SUCCEEDED(hr))
{
- IStorage_DestroyElement(iface, pwcsName);
+ LONG diff = entryNameCmp( entry.name, This->name);
+
+ if (diff <= 0)
+ {
+ searchNode = entry.rightChild;
+ }
+ else
+ {
+ result = searchNode;
+ memcpy(result_name, entry.name, sizeof(result_name));
+ searchNode = entry.leftChild;
+ }
}
- else
- return STG_E_FILEALREADYEXISTS;
}
- /*
- * memset the empty entry
- */
- memset(&newStreamEntry, 0, sizeof(DirEntry));
-
- newStreamEntry.sizeOfNameString =
- ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
-
- if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
- return STG_E_INVALIDNAME;
+ if (SUCCEEDED(hr))
+ {
+ *ref = result;
+ if (result != DIRENTRY_NULL)
+ memcpy(This->name, result_name, sizeof(result_name));
+ }
- strcpyW(newStreamEntry.name, pwcsName);
+ return hr;
+}
- newStreamEntry.stgType = STGTY_STREAM;
- newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
- newStreamEntry.size.u.LowPart = 0;
- newStreamEntry.size.u.HighPart = 0;
+static HRESULT WINAPI IEnumSTATSTGImpl_Next(
+ IEnumSTATSTG* iface,
+ ULONG celt,
+ STATSTG* rgelt,
+ ULONG* pceltFetched)
+{
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- newStreamEntry.leftChild = DIRENTRY_NULL;
- newStreamEntry.rightChild = DIRENTRY_NULL;
- newStreamEntry.dirRootEntry = DIRENTRY_NULL;
+ DirEntry currentEntry;
+ STATSTG* currentReturnStruct = rgelt;
+ ULONG objectFetched = 0;
+ DirRef currentSearchNode;
+ HRESULT hr=S_OK;
- /* call CoFileTime to get the current time
- newStreamEntry.ctime
- newStreamEntry.mtime
- */
+ if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
+ return E_INVALIDARG;
- /* newStreamEntry.clsid */
+ if (This->parentStorage->reverted)
+ return STG_E_REVERTED;
/*
- * Create an entry with the new data
+ * To avoid the special case, get another pointer to a ULONG value if
+ * the caller didn't supply one.
*/
- hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
- if (FAILED(hr))
- return hr;
+ if (pceltFetched==0)
+ pceltFetched = &objectFetched;
/*
- * Insert the new entry in the parent storage's tree.
+ * Start the iteration, we will iterate until we hit the end of the
+ * linked list or until we hit the number of items to iterate through
*/
- hr = insertIntoTree(
- This,
- This->storageDirEntry,
- newStreamEntryRef);
- if (FAILED(hr))
+ *pceltFetched = 0;
+
+ while ( *pceltFetched < celt )
{
- StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
- return hr;
- }
+ hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
- /*
- * Open the stream to return it.
- */
- newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
+ if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
+ break;
- if (newStream)
- {
- *ppstm = &newStream->IStream_iface;
- IStream_AddRef(*ppstm);
- }
- else
- {
- return STG_E_INSUFFICIENTMEMORY;
+ /*
+ * Read the entry from the storage.
+ */
+ StorageBaseImpl_ReadDirEntry(This->parentStorage,
+ currentSearchNode,
+ ¤tEntry);
+
+ /*
+ * Copy the information to the return buffer.
+ */
+ StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
+ currentReturnStruct,
+ ¤tEntry,
+ STATFLAG_DEFAULT);
+
+ /*
+ * Step to the next item in the iteration
+ */
+ (*pceltFetched)++;
+ currentReturnStruct++;
}
- return StorageBaseImpl_Flush(This);
+ if (SUCCEEDED(hr) && *pceltFetched != celt)
+ hr = S_FALSE;
+
+ return hr;
}
-/************************************************************************
- * StorageBaseImpl_SetClass (IStorage)
- *
- * This method will write the specified CLSID in the directory entry of this
- * storage.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_SetClass(
- IStorage* iface,
- REFCLSID clsid) /* [in] */
+
+static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
+ IEnumSTATSTG* iface,
+ ULONG celt)
{
- StorageBaseImpl *This = impl_from_IStorage(iface);
- HRESULT hRes;
- DirEntry currentEntry;
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- TRACE("(%p, %p)\n", iface, clsid);
+ ULONG objectFetched = 0;
+ DirRef currentSearchNode;
+ HRESULT hr=S_OK;
- if (This->reverted)
+ if (This->parentStorage->reverted)
return STG_E_REVERTED;
- hRes = StorageBaseImpl_ReadDirEntry(This,
- This->storageDirEntry,
- ¤tEntry);
- if (SUCCEEDED(hRes))
+ while ( (objectFetched < celt) )
{
- currentEntry.clsid = *clsid;
-
- hRes = StorageBaseImpl_WriteDirEntry(This,
- This->storageDirEntry,
- ¤tEntry);
- }
+ hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
- if (SUCCEEDED(hRes))
- hRes = StorageBaseImpl_Flush(This);
+ if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
+ break;
- return hRes;
-}
+ objectFetched++;
+ }
-/************************************************************************
-** StorageBaseImpl implementation
-*/
+ if (SUCCEEDED(hr) && objectFetched != celt)
+ return S_FALSE;
-/************************************************************************
- * StorageBaseImpl_CreateStorage (IStorage)
- *
- * This method will create the storage object within the provided storage.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_CreateStorage(
- IStorage* iface,
- const OLECHAR *pwcsName, /* [string][in] */
- DWORD grfMode, /* [in] */
- DWORD reserved1, /* [in] */
- DWORD reserved2, /* [in] */
- IStorage **ppstg) /* [out] */
+ return hr;
+}
+
+static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
+ IEnumSTATSTG* iface)
{
- StorageBaseImpl* This = impl_from_IStorage(iface);
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- DirEntry currentEntry;
- DirEntry newEntry;
- DirRef currentEntryRef;
- DirRef newEntryRef;
- HRESULT hr;
+ if (This->parentStorage->reverted)
+ return STG_E_REVERTED;
- TRACE("(%p, %s, %x, %d, %d, %p)\n",
- iface, debugstr_w(pwcsName), grfMode,
- reserved1, reserved2, ppstg);
+ This->name[0] = 0;
- if (ppstg == 0)
- return STG_E_INVALIDPOINTER;
+ return S_OK;
+}
- if (This->openFlags & STGM_SIMPLE)
- {
- return STG_E_INVALIDFUNCTION;
- }
+static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
- if (pwcsName == 0)
- return STG_E_INVALIDNAME;
+static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
+ IEnumSTATSTG* iface,
+ IEnumSTATSTG** ppenum)
+{
+ IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ IEnumSTATSTGImpl* newClone;
- *ppstg = NULL;
+ if (This->parentStorage->reverted)
+ return STG_E_REVERTED;
- if ( FAILED( validateSTGM(grfMode) ) ||
- (grfMode & STGM_DELETEONRELEASE) )
+ if (ppenum==0)
+ return E_INVALIDARG;
+
+ newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
+ This->storageDirEntry);
+ if (!newClone)
{
- WARN("bad grfMode: 0x%x\n", grfMode);
- return STG_E_INVALIDFLAG;
+ *ppenum = NULL;
+ return E_OUTOFMEMORY;
}
- if (This->reverted)
- return STG_E_REVERTED;
-
/*
- * Check that we're compatible with the parent's storage mode
+ * The new clone enumeration must point to the same current node as
+ * the old one.
*/
- if ( !(This->openFlags & STGM_TRANSACTED) &&
- STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
- {
- WARN("access denied\n");
- return STG_E_ACCESSDENIED;
- }
+ memcpy(newClone->name, This->name, sizeof(newClone->name));
- currentEntryRef = findElement(This,
- This->storageDirEntry,
- pwcsName,
- ¤tEntry);
+ *ppenum = &newClone->IEnumSTATSTG_iface;
- if (currentEntryRef != DIRENTRY_NULL)
- {
- /*
- * An element with this name already exists
- */
- if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
- ((This->openFlags & STGM_TRANSACTED) ||
- STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
- {
- hr = IStorage_DestroyElement(iface, pwcsName);
- if (FAILED(hr))
- return hr;
- }
- else
- {
- WARN("file already exists\n");
- return STG_E_FILEALREADYEXISTS;
- }
- }
- else if (!(This->openFlags & STGM_TRANSACTED) &&
- STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
- {
- WARN("read-only storage\n");
- return STG_E_ACCESSDENIED;
- }
+ return S_OK;
+}
- memset(&newEntry, 0, sizeof(DirEntry));
+/*
+ * Virtual function table for the IEnumSTATSTGImpl class.
+ */
+static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
+{
+ IEnumSTATSTGImpl_QueryInterface,
+ IEnumSTATSTGImpl_AddRef,
+ IEnumSTATSTGImpl_Release,
+ IEnumSTATSTGImpl_Next,
+ IEnumSTATSTGImpl_Skip,
+ IEnumSTATSTGImpl_Reset,
+ IEnumSTATSTGImpl_Clone
+};
- newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
+static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
+ StorageBaseImpl* parentStorage,
+ DirRef storageDirEntry)
+{
+ IEnumSTATSTGImpl* newEnumeration;
- if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
+ newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
+
+ if (newEnumeration)
{
- FIXME("name too long\n");
- return STG_E_INVALIDNAME;
+ newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
+ newEnumeration->ref = 1;
+ newEnumeration->name[0] = 0;
+
+ /*
+ * We want to nail-down the reference to the storage in case the
+ * enumeration out-lives the storage in the client application.
+ */
+ newEnumeration->parentStorage = parentStorage;
+ IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
+
+ newEnumeration->storageDirEntry = storageDirEntry;
}
- strcpyW(newEntry.name, pwcsName);
+ return newEnumeration;
+}
- newEntry.stgType = STGTY_STORAGE;
- newEntry.startingBlock = BLOCK_END_OF_CHAIN;
- newEntry.size.u.LowPart = 0;
- newEntry.size.u.HighPart = 0;
- newEntry.leftChild = DIRENTRY_NULL;
- newEntry.rightChild = DIRENTRY_NULL;
- newEntry.dirRootEntry = DIRENTRY_NULL;
+/************************************************************************
+ * StorageBaseImpl implementation
+ ***********************************************************************/
- /* call CoFileTime to get the current time
- newEntry.ctime
- newEntry.mtime
- */
+static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
+{
+ return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
+}
- /* newEntry.clsid */
+/************************************************************************
+ * StorageBaseImpl_QueryInterface (IUnknown)
+ *
+ * This method implements the common QueryInterface for all IStorage
+ * implementations contained in this file.
+ *
+ * See Windows documentation for more details on IUnknown methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_QueryInterface(
+ IStorage* iface,
+ REFIID riid,
+ void** ppvObject)
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
- /*
- * Create a new directory entry for the storage
- */
- hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
- if (FAILED(hr))
- return hr;
+ if (!ppvObject)
+ return E_INVALIDARG;
- /*
- * Insert the new directory entry into the parent storage's tree
- */
- hr = insertIntoTree(
- This,
- This->storageDirEntry,
- newEntryRef);
- if (FAILED(hr))
+ *ppvObject = 0;
+
+ if (IsEqualGUID(&IID_IUnknown, riid) ||
+ IsEqualGUID(&IID_IStorage, riid))
{
- StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
- return hr;
+ *ppvObject = &This->IStorage_iface;
}
-
- /*
- * Open it to get a pointer to return.
- */
- hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
-
- if( (hr != S_OK) || (*ppstg == NULL))
+ else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
{
- return hr;
+ *ppvObject = &This->IPropertySetStorage_iface;
+ }
+ /* locking interface is reported for writer only */
+ else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
+ {
+ *ppvObject = &This->IDirectWriterLock_iface;
}
+ else
+ return E_NOINTERFACE;
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_Flush(This);
+ IStorage_AddRef(iface);
return S_OK;
}
-
-/***************************************************************************
+/************************************************************************
+ * StorageBaseImpl_AddRef (IUnknown)
*
- * Internal Method
+ * This method implements the common AddRef for all IStorage
+ * implementations contained in this file.
*
- * Reserve a directory entry in the file and initialize it.
+ * See Windows documentation for more details on IUnknown methods.
*/
-static HRESULT StorageImpl_CreateDirEntry(
- StorageBaseImpl *base,
- const DirEntry *newData,
- DirRef *index)
+static ULONG WINAPI StorageBaseImpl_AddRef(
+ IStorage* iface)
{
- StorageImpl *storage = (StorageImpl*)base;
- ULONG currentEntryIndex = 0;
- ULONG newEntryIndex = DIRENTRY_NULL;
- HRESULT hr = S_OK;
- BYTE currentData[RAW_DIRENTRY_SIZE];
- WORD sizeOfNameString;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ ULONG ref = InterlockedIncrement(&This->ref);
- do
+ TRACE("(%p) AddRef to %d\n", This, ref);
+
+ return ref;
+}
+
+/************************************************************************
+ * StorageBaseImpl_Release (IUnknown)
+ *
+ * This method implements the common Release for all IStorage
+ * implementations contained in this file.
+ *
+ * See Windows documentation for more details on IUnknown methods.
+ */
+static ULONG WINAPI StorageBaseImpl_Release(
+ IStorage* iface)
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+
+ ULONG ref = InterlockedDecrement(&This->ref);
+
+ TRACE("(%p) ReleaseRef to %d\n", This, ref);
+
+ if (ref == 0)
{
- hr = StorageImpl_ReadRawDirEntry(storage,
- currentEntryIndex,
- currentData);
+ /*
+ * Since we are using a system of base-classes, we want to call the
+ * destructor of the appropriate derived class. To do this, we are
+ * using virtual functions to implement the destructor.
+ */
+ StorageBaseImpl_Destroy(This);
+ }
- if (SUCCEEDED(hr))
+ return ref;
+}
+
+static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
+ DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+ SNB snbExclude, IStorage *pstgDest);
+
+static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
+ DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+ SNB snbExclude, IStorage *pstgDest)
+{
+ DirEntry data;
+ HRESULT hr;
+ BOOL skip = FALSE;
+ IStorage *pstgTmp;
+ IStream *pstrChild, *pstrTmp;
+ STATSTG strStat;
+
+ if (srcEntry == DIRENTRY_NULL)
+ return S_OK;
+
+ hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+
+ if (FAILED(hr))
+ return hr;
+
+ if ( snbExclude )
+ {
+ WCHAR **snb = snbExclude;
+
+ while ( *snb != NULL && !skip )
{
- StorageUtl_ReadWord(
- currentData,
- OFFSET_PS_NAMELENGTH,
- &sizeOfNameString);
+ if ( lstrcmpW(data.name, *snb) == 0 )
+ skip = TRUE;
+ ++snb;
+ }
+ }
- if (sizeOfNameString == 0)
+ if (!skip)
+ {
+ if (data.stgType == STGTY_STORAGE && !skip_storage)
+ {
+ /*
+ * create a new storage in destination storage
+ */
+ hr = IStorage_CreateStorage( pstgDest, data.name,
+ STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+ 0, 0,
+ &pstgTmp );
+
+ /*
+ * if it already exist, don't create a new one use this one
+ */
+ if (hr == STG_E_FILEALREADYEXISTS)
{
- /*
- * The entry exists and is available, we found it.
- */
- newEntryIndex = currentEntryIndex;
+ hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
+ STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+ NULL, 0, &pstgTmp );
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
+ skip_stream, NULL, pstgTmp );
+
+ IStorage_Release(pstgTmp);
}
}
- else
+ else if (data.stgType == STGTY_STREAM && !skip_stream)
{
/*
- * We exhausted the directory entries, we will create more space below
+ * create a new stream in destination storage. If the stream already
+ * exist, it will be deleted and a new one will be created.
*/
- newEntryIndex = currentEntryIndex;
- }
- currentEntryIndex++;
-
- } while (newEntryIndex == DIRENTRY_NULL);
+ hr = IStorage_CreateStream( pstgDest, data.name,
+ STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+ 0, 0, &pstrTmp );
- /*
- * grow the directory stream
- */
- if (FAILED(hr))
- {
- BYTE emptyData[RAW_DIRENTRY_SIZE];
- ULARGE_INTEGER newSize;
- ULONG entryIndex;
- ULONG lastEntry = 0;
- ULONG blockCount = 0;
+ /*
+ * open child stream storage. This operation must succeed even if the
+ * stream is already open, so we use internal functions to do it.
+ */
+ if (hr == S_OK)
+ {
+ StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
- /*
- * obtain the new count of blocks in the directory stream
- */
- blockCount = BlockChainStream_GetCount(
- storage->rootBlockChain)+1;
+ if (streamimpl)
+ {
+ pstrChild = &streamimpl->IStream_iface;
+ if (pstrChild)
+ IStream_AddRef(pstrChild);
+ }
+ else
+ {
+ pstrChild = NULL;
+ hr = E_OUTOFMEMORY;
+ }
+ }
- /*
- * initialize the size used by the directory stream
- */
- newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
+ if (hr == S_OK)
+ {
+ /*
+ * Get the size of the source stream
+ */
+ IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
- /*
- * add a block to the directory stream
- */
- BlockChainStream_SetSize(storage->rootBlockChain, newSize);
+ /*
+ * Set the size of the destination stream.
+ */
+ IStream_SetSize(pstrTmp, strStat.cbSize);
- /*
- * memset the empty entry in order to initialize the unused newly
- * created entries
- */
- memset(emptyData, 0, RAW_DIRENTRY_SIZE);
+ /*
+ * do the copy
+ */
+ hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
+ NULL, NULL );
- /*
- * initialize them
- */
- lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
+ IStream_Release( pstrChild );
+ }
- for(
- entryIndex = newEntryIndex + 1;
- entryIndex < lastEntry;
- entryIndex++)
- {
- StorageImpl_WriteRawDirEntry(
- storage,
- entryIndex,
- emptyData);
+ IStream_Release( pstrTmp );
}
-
- StorageImpl_SaveFileHeader(storage);
}
- UpdateRawDirEntry(currentData, newData);
-
- hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
+ /* copy siblings */
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
+ skip_stream, snbExclude, pstgDest );
if (SUCCEEDED(hr))
- *index = newEntryIndex;
+ hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
+ skip_stream, snbExclude, pstgDest );
return hr;
}
-/***************************************************************************
- *
- * Internal Method
- *
- * Mark a directory entry in the file as free.
- */
-static HRESULT StorageImpl_DestroyDirEntry(
- StorageBaseImpl *base,
- DirRef index)
+static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
{
- BYTE emptyData[RAW_DIRENTRY_SIZE];
- StorageImpl *storage = (StorageImpl*)base;
+ StgStreamImpl *strm;
- memset(emptyData, 0, RAW_DIRENTRY_SIZE);
+ LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
+ {
+ if (strm->dirEntry == streamEntry)
+ {
+ return TRUE;
+ }
+ }
- return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
+ return FALSE;
}
-
-/****************************************************************************
- *
- * Internal Method
- *
- * Case insensitive comparison of DirEntry.name by first considering
- * their size.
- *
- * Returns <0 when name1 < name2
- * >0 when name1 > name2
- * 0 when name1 == name2
- */
-static LONG entryNameCmp(
- const OLECHAR *name1,
- const OLECHAR *name2)
+static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
{
- LONG diff = lstrlenW(name1) - lstrlenW(name2);
+ StorageInternalImpl *childstg;
- while (diff == 0 && *name1 != 0)
+ LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
{
- /*
- * We compare the string themselves only when they are of the same length
- */
- diff = toupperW(*name1++) - toupperW(*name2++);
+ if (childstg->base.storageDirEntry == storageEntry)
+ {
+ return TRUE;
+ }
}
- return diff;
+ return FALSE;
}
-/****************************************************************************
+/************************************************************************
+ * StorageBaseImpl_OpenStream (IStorage)
*
- * Internal Method
+ * This method will open the specified stream object from the current storage.
*
- * Add a directory entry to a storage
+ * See Windows documentation for more details on IStorage methods.
*/
-static HRESULT insertIntoTree(
- StorageBaseImpl *This,
- DirRef parentStorageIndex,
- DirRef newEntryIndex)
+static HRESULT WINAPI StorageBaseImpl_OpenStream(
+ IStorage* iface,
+ const OLECHAR* pwcsName, /* [string][in] */
+ void* reserved1, /* [unique][in] */
+ DWORD grfMode, /* [in] */
+ DWORD reserved2, /* [in] */
+ IStream** ppstm) /* [out] */
{
- DirEntry currentEntry;
- DirEntry newEntry;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ StgStreamImpl* newStream;
+ DirEntry currentEntry;
+ DirRef streamEntryRef;
+ HRESULT res = STG_E_UNKNOWN;
- /*
- * Read the inserted entry
- */
- StorageBaseImpl_ReadDirEntry(This,
- newEntryIndex,
- &newEntry);
+ TRACE("(%p, %s, %p, %x, %d, %p)\n",
+ iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
+
+ if ( (pwcsName==NULL) || (ppstm==0) )
+ {
+ res = E_INVALIDARG;
+ goto end;
+ }
+
+ *ppstm = NULL;
+
+ if ( FAILED( validateSTGM(grfMode) ) ||
+ STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+ {
+ res = STG_E_INVALIDFLAG;
+ goto end;
+ }
/*
- * Read the storage entry
+ * As documented.
*/
- StorageBaseImpl_ReadDirEntry(This,
- parentStorageIndex,
- ¤tEntry);
+ if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
+ {
+ res = STG_E_INVALIDFUNCTION;
+ goto end;
+ }
- if (currentEntry.dirRootEntry != DIRENTRY_NULL)
+ if (This->reverted)
{
- /*
- * The root storage contains some element, therefore, start the research
- * for the appropriate location.
- */
- BOOL found = FALSE;
- DirRef current, next, previous, currentEntryId;
+ res = STG_E_REVERTED;
+ goto end;
+ }
- /*
- * Keep a reference to the root of the storage's element tree
- */
- currentEntryId = currentEntry.dirRootEntry;
+ /*
+ * Check that we're compatible with the parent's storage mode, but
+ * only if we are not in transacted mode
+ */
+ if(!(This->openFlags & STGM_TRANSACTED)) {
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+ {
+ res = STG_E_INVALIDFLAG;
+ goto end;
+ }
+ }
- /*
- * Read
- */
- StorageBaseImpl_ReadDirEntry(This,
- currentEntry.dirRootEntry,
- ¤tEntry);
+ /*
+ * Search for the element with the given name
+ */
+ streamEntryRef = findElement(
+ This,
+ This->storageDirEntry,
+ pwcsName,
+ ¤tEntry);
- previous = currentEntry.leftChild;
- next = currentEntry.rightChild;
- current = currentEntryId;
+ /*
+ * If it was found, construct the stream object and return a pointer to it.
+ */
+ if ( (streamEntryRef!=DIRENTRY_NULL) &&
+ (currentEntry.stgType==STGTY_STREAM) )
+ {
+ if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
+ {
+ /* A single stream cannot be opened a second time. */
+ res = STG_E_ACCESSDENIED;
+ goto end;
+ }
- while (!found)
+ newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
+
+ if (newStream)
{
- LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
+ newStream->grfMode = grfMode;
+ *ppstm = &newStream->IStream_iface;
- if (diff < 0)
- {
- if (previous != DIRENTRY_NULL)
- {
- StorageBaseImpl_ReadDirEntry(This,
- previous,
- ¤tEntry);
- current = previous;
- }
- else
- {
- currentEntry.leftChild = newEntryIndex;
- StorageBaseImpl_WriteDirEntry(This,
- current,
- ¤tEntry);
- found = TRUE;
- }
- }
- else if (diff > 0)
- {
- if (next != DIRENTRY_NULL)
- {
- StorageBaseImpl_ReadDirEntry(This,
- next,
- ¤tEntry);
- current = next;
- }
- else
- {
- currentEntry.rightChild = newEntryIndex;
- StorageBaseImpl_WriteDirEntry(This,
- current,
- ¤tEntry);
- found = TRUE;
- }
- }
- else
- {
- /*
- * Trying to insert an item with the same name in the
- * subtree structure.
- */
- return STG_E_FILEALREADYEXISTS;
- }
+ IStream_AddRef(*ppstm);
- previous = currentEntry.leftChild;
- next = currentEntry.rightChild;
+ res = S_OK;
+ goto end;
}
- }
- else
- {
- /*
- * The storage is empty, make the new entry the root of its element tree
- */
- currentEntry.dirRootEntry = newEntryIndex;
- StorageBaseImpl_WriteDirEntry(This,
- parentStorageIndex,
- ¤tEntry);
+
+ res = E_OUTOFMEMORY;
+ goto end;
}
- return S_OK;
+ res = STG_E_FILENOTFOUND;
+
+end:
+ if (res == S_OK)
+ TRACE("<-- IStream %p\n", *ppstm);
+ TRACE("<-- %08x\n", res);
+ return res;
}
-/****************************************************************************
+/************************************************************************
+ * StorageBaseImpl_OpenStorage (IStorage)
*
- * Internal Method
+ * This method will open a new storage object from the current storage.
*
- * Find and read the element of a storage with the given name.
+ * See Windows documentation for more details on IStorage methods.
*/
-static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
- const OLECHAR *name, DirEntry *data)
+static HRESULT WINAPI StorageBaseImpl_OpenStorage(
+ IStorage* iface,
+ const OLECHAR* pwcsName, /* [string][unique][in] */
+ IStorage* pstgPriority, /* [unique][in] */
+ DWORD grfMode, /* [in] */
+ SNB snbExclude, /* [unique][in] */
+ DWORD reserved, /* [in] */
+ IStorage** ppstg) /* [out] */
{
- DirRef currentEntry;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ StorageInternalImpl* newStorage;
+ StorageBaseImpl* newTransactedStorage;
+ DirEntry currentEntry;
+ DirRef storageEntryRef;
+ HRESULT res = STG_E_UNKNOWN;
- /* Read the storage entry to find the root of the tree. */
- StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
+ TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
+ iface, debugstr_w(pwcsName), pstgPriority,
+ grfMode, snbExclude, reserved, ppstg);
- currentEntry = data->dirRootEntry;
+ if ((pwcsName==NULL) || (ppstg==0) )
+ {
+ res = E_INVALIDARG;
+ goto end;
+ }
- while (currentEntry != DIRENTRY_NULL)
+ if (This->openFlags & STGM_SIMPLE)
{
- LONG cmp;
+ res = STG_E_INVALIDFUNCTION;
+ goto end;
+ }
- StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
+ /* as documented */
+ if (snbExclude != NULL)
+ {
+ res = STG_E_INVALIDPARAMETER;
+ goto end;
+ }
- cmp = entryNameCmp(name, data->name);
+ if ( FAILED( validateSTGM(grfMode) ))
+ {
+ res = STG_E_INVALIDFLAG;
+ goto end;
+ }
- if (cmp == 0)
- /* found it */
- break;
-
- else if (cmp < 0)
- currentEntry = data->leftChild;
-
- else if (cmp > 0)
- currentEntry = data->rightChild;
+ /*
+ * As documented.
+ */
+ if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
+ (grfMode & STGM_DELETEONRELEASE) ||
+ (grfMode & STGM_PRIORITY) )
+ {
+ res = STG_E_INVALIDFUNCTION;
+ goto end;
}
- return currentEntry;
-}
-
-/****************************************************************************
- *
- * Internal Method
- *
- * Find and read the binary tree parent of the element with the given name.
- *
- * If there is no such element, find a place where it could be inserted and
- * return STG_E_FILENOTFOUND.
- */
-static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
- const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
- ULONG *relation)
-{
- DirRef childEntry;
- DirEntry childData;
+ if (This->reverted)
+ return STG_E_REVERTED;
- /* Read the storage entry to find the root of the tree. */
- StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
+ /*
+ * Check that we're compatible with the parent's storage mode,
+ * but only if we are not transacted
+ */
+ if(!(This->openFlags & STGM_TRANSACTED)) {
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+ {
+ res = STG_E_ACCESSDENIED;
+ goto end;
+ }
+ }
- *parentEntry = storageEntry;
- *relation = DIRENTRY_RELATION_DIR;
+ *ppstg = NULL;
- childEntry = parentData->dirRootEntry;
+ storageEntryRef = findElement(
+ This,
+ This->storageDirEntry,
+ pwcsName,
+ ¤tEntry);
- while (childEntry != DIRENTRY_NULL)
+ if ( (storageEntryRef!=DIRENTRY_NULL) &&
+ (currentEntry.stgType==STGTY_STORAGE) )
{
- LONG cmp;
-
- StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
-
- cmp = entryNameCmp(childName, childData.name);
+ if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
+ {
+ /* A single storage cannot be opened a second time. */
+ res = STG_E_ACCESSDENIED;
+ goto end;
+ }
- if (cmp == 0)
- /* found it */
- break;
+ newStorage = StorageInternalImpl_Construct(
+ This,
+ grfMode,
+ storageEntryRef);
- else if (cmp < 0)
+ if (newStorage != 0)
{
- *parentData = childData;
- *parentEntry = childEntry;
- *relation = DIRENTRY_RELATION_PREVIOUS;
+ if (grfMode & STGM_TRANSACTED)
+ {
+ res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
- childEntry = parentData->leftChild;
- }
+ if (FAILED(res))
+ {
+ HeapFree(GetProcessHeap(), 0, newStorage);
+ goto end;
+ }
- else if (cmp > 0)
- {
- *parentData = childData;
- *parentEntry = childEntry;
- *relation = DIRENTRY_RELATION_NEXT;
+ *ppstg = &newTransactedStorage->IStorage_iface;
+ }
+ else
+ {
+ *ppstg = &newStorage->base.IStorage_iface;
+ }
- childEntry = parentData->rightChild;
+ list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
+
+ res = S_OK;
+ goto end;
}
+
+ res = STG_E_INSUFFICIENTMEMORY;
+ goto end;
}
- if (childEntry == DIRENTRY_NULL)
- return STG_E_FILENOTFOUND;
- else
- return S_OK;
+ res = STG_E_FILENOTFOUND;
+
+end:
+ TRACE("<-- %08x\n", res);
+ return res;
}
+/************************************************************************
+ * StorageBaseImpl_EnumElements (IStorage)
+ *
+ * This method will create an enumerator object that can be used to
+ * retrieve information about all the elements in the storage object.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_EnumElements(
+ IStorage* iface,
+ DWORD reserved1, /* [in] */
+ void* reserved2, /* [size_is][unique][in] */
+ DWORD reserved3, /* [in] */
+ IEnumSTATSTG** ppenum) /* [out] */
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ IEnumSTATSTGImpl* newEnum;
+
+ TRACE("(%p, %d, %p, %d, %p)\n",
+ iface, reserved1, reserved2, reserved3, ppenum);
-static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
- DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
- SNB snbExclude, IStorage *pstgDest);
+ if (!ppenum)
+ return E_INVALIDARG;
-static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
- DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
- SNB snbExclude, IStorage *pstgDest)
-{
- DirEntry data;
- HRESULT hr;
- BOOL skip = FALSE;
- IStorage *pstgTmp;
- IStream *pstrChild, *pstrTmp;
- STATSTG strStat;
+ if (This->reverted)
+ return STG_E_REVERTED;
- if (srcEntry == DIRENTRY_NULL)
+ newEnum = IEnumSTATSTGImpl_Construct(
+ This,
+ This->storageDirEntry);
+
+ if (newEnum)
+ {
+ *ppenum = &newEnum->IEnumSTATSTG_iface;
return S_OK;
+ }
- hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+ return E_OUTOFMEMORY;
+}
- if (FAILED(hr))
- return hr;
+/************************************************************************
+ * StorageBaseImpl_Stat (IStorage)
+ *
+ * This method will retrieve information about this storage object.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_Stat(
+ IStorage* iface,
+ STATSTG* pstatstg, /* [out] */
+ DWORD grfStatFlag) /* [in] */
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ DirEntry currentEntry;
+ HRESULT res = STG_E_UNKNOWN;
- if ( snbExclude )
- {
- WCHAR **snb = snbExclude;
+ TRACE("(%p, %p, %x)\n",
+ iface, pstatstg, grfStatFlag);
- while ( *snb != NULL && !skip )
- {
- if ( lstrcmpW(data.name, *snb) == 0 )
- skip = TRUE;
- ++snb;
- }
+ if (!pstatstg)
+ {
+ res = E_INVALIDARG;
+ goto end;
}
- if (!skip)
+ if (This->reverted)
{
- if (data.stgType == STGTY_STORAGE && !skip_storage)
- {
- /*
- * create a new storage in destination storage
- */
- hr = IStorage_CreateStorage( pstgDest, data.name,
- STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
- 0, 0,
- &pstgTmp );
+ res = STG_E_REVERTED;
+ goto end;
+ }
- /*
- * if it already exist, don't create a new one use this one
- */
- if (hr == STG_E_FILEALREADYEXISTS)
- {
- hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
- STGM_WRITE|STGM_SHARE_EXCLUSIVE,
- NULL, 0, &pstgTmp );
- }
+ res = StorageBaseImpl_ReadDirEntry(
+ This,
+ This->storageDirEntry,
+ ¤tEntry);
- if (SUCCEEDED(hr))
- {
- hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
- skip_stream, NULL, pstgTmp );
+ if (SUCCEEDED(res))
+ {
+ StorageUtl_CopyDirEntryToSTATSTG(
+ This,
+ pstatstg,
+ ¤tEntry,
+ grfStatFlag);
- IStorage_Release(pstgTmp);
- }
- }
- else if (data.stgType == STGTY_STREAM && !skip_stream)
- {
- /*
- * create a new stream in destination storage. If the stream already
- * exist, it will be deleted and a new one will be created.
- */
- hr = IStorage_CreateStream( pstgDest, data.name,
- STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
- 0, 0, &pstrTmp );
+ pstatstg->grfMode = This->openFlags;
+ pstatstg->grfStateBits = This->stateBits;
+ }
- /*
- * open child stream storage. This operation must succeed even if the
- * stream is already open, so we use internal functions to do it.
- */
- if (hr == S_OK)
- {
- StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
-
- if (streamimpl)
- {
- pstrChild = &streamimpl->IStream_iface;
- if (pstrChild)
- IStream_AddRef(pstrChild);
- }
- else
- {
- pstrChild = NULL;
- hr = E_OUTOFMEMORY;
- }
- }
+end:
+ if (res == S_OK)
+ {
+ TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
+ }
+ TRACE("<-- %08x\n", res);
+ return res;
+}
- if (hr == S_OK)
- {
- /*
- * Get the size of the source stream
- */
- IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
+/************************************************************************
+ * StorageBaseImpl_RenameElement (IStorage)
+ *
+ * This method will rename the specified element.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_RenameElement(
+ IStorage* iface,
+ const OLECHAR* pwcsOldName, /* [in] */
+ const OLECHAR* pwcsNewName) /* [in] */
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ DirEntry currentEntry;
+ DirRef currentEntryRef;
- /*
- * Set the size of the destination stream.
- */
- IStream_SetSize(pstrTmp, strStat.cbSize);
+ TRACE("(%p, %s, %s)\n",
+ iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
- /*
- * do the copy
- */
- hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
- NULL, NULL );
+ if (This->reverted)
+ return STG_E_REVERTED;
- IStream_Release( pstrChild );
- }
+ currentEntryRef = findElement(This,
+ This->storageDirEntry,
+ pwcsNewName,
+ ¤tEntry);
- IStream_Release( pstrTmp );
- }
+ if (currentEntryRef != DIRENTRY_NULL)
+ {
+ /*
+ * There is already an element with the new name
+ */
+ return STG_E_FILEALREADYEXISTS;
}
- /* copy siblings */
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
- skip_stream, snbExclude, pstgDest );
+ /*
+ * Search for the old element name
+ */
+ currentEntryRef = findElement(This,
+ This->storageDirEntry,
+ pwcsOldName,
+ ¤tEntry);
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
- skip_stream, snbExclude, pstgDest );
+ if (currentEntryRef != DIRENTRY_NULL)
+ {
+ if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
+ StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
+ {
+ WARN("Element is already open; cannot rename.\n");
+ return STG_E_ACCESSDENIED;
+ }
- return hr;
-}
+ /* Remove the element from its current position in the tree */
+ removeFromTree(This, This->storageDirEntry,
+ currentEntryRef);
-static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
- DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
- SNB snbExclude, IStorage *pstgDest)
-{
- DirEntry data;
- HRESULT hr;
+ /* Change the name of the element */
+ strcpyW(currentEntry.name, pwcsNewName);
- hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+ /* Delete any sibling links */
+ currentEntry.leftChild = DIRENTRY_NULL;
+ currentEntry.rightChild = DIRENTRY_NULL;
- if (SUCCEEDED(hr))
- hr = IStorage_SetClass( pstgDest, &data.clsid );
+ StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
+ ¤tEntry);
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
- skip_stream, snbExclude, pstgDest );
+ /* Insert the element in a new position in the tree */
+ insertIntoTree(This, This->storageDirEntry,
+ currentEntryRef);
+ }
+ else
+ {
+ /*
+ * There is no element with the old name
+ */
+ return STG_E_FILENOTFOUND;
+ }
- return hr;
+ return StorageBaseImpl_Flush(This);
}
-/*************************************************************************
- * CopyTo (IStorage)
+/************************************************************************
+ * StorageBaseImpl_CreateStream (IStorage)
+ *
+ * This method will create a stream object within this storage
+ *
+ * See Windows documentation for more details on IStorage methods.
*/
-static HRESULT WINAPI StorageBaseImpl_CopyTo(
- IStorage* iface,
- DWORD ciidExclude, /* [in] */
- const IID* rgiidExclude, /* [size_is][unique][in] */
- SNB snbExclude, /* [unique][in] */
- IStorage* pstgDest) /* [unique][in] */
+static HRESULT WINAPI StorageBaseImpl_CreateStream(
+ IStorage* iface,
+ const OLECHAR* pwcsName, /* [string][in] */
+ DWORD grfMode, /* [in] */
+ DWORD reserved1, /* [in] */
+ DWORD reserved2, /* [in] */
+ IStream** ppstm) /* [out] */
{
StorageBaseImpl *This = impl_from_IStorage(iface);
+ StgStreamImpl* newStream;
+ DirEntry currentEntry, newStreamEntry;
+ DirRef currentEntryRef, newStreamEntryRef;
+ HRESULT hr;
- BOOL skip_storage = FALSE, skip_stream = FALSE;
- DWORD i;
-
- TRACE("(%p, %d, %p, %p, %p)\n",
- iface, ciidExclude, rgiidExclude,
- snbExclude, pstgDest);
+ TRACE("(%p, %s, %x, %d, %d, %p)\n",
+ iface, debugstr_w(pwcsName), grfMode,
+ reserved1, reserved2, ppstm);
- if ( pstgDest == 0 )
+ if (ppstm == 0)
return STG_E_INVALIDPOINTER;
- for(i = 0; i < ciidExclude; ++i)
- {
- if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
- skip_storage = TRUE;
- else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
- skip_stream = TRUE;
- else
- WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
- }
+ if (pwcsName == 0)
+ return STG_E_INVALIDNAME;
- if (!skip_storage)
- {
- /* Give up early if it looks like this would be infinitely recursive.
- * Oddly enough, this includes some cases that aren't really recursive, like
- * copying to a transacted child. */
- IStorage *pstgDestAncestor = pstgDest;
- IStorage *pstgDestAncestorChild = NULL;
+ if (reserved1 || reserved2)
+ return STG_E_INVALIDPARAMETER;
- /* Go up the chain from the destination until we find the source storage. */
- while (pstgDestAncestor != iface) {
- pstgDestAncestorChild = pstgDest;
+ if ( FAILED( validateSTGM(grfMode) ))
+ return STG_E_INVALIDFLAG;
- if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
- {
- TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
+ if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+ return STG_E_INVALIDFLAG;
- pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
- }
- else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
- {
- StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
+ if (This->reverted)
+ return STG_E_REVERTED;
- pstgDestAncestor = &internal->parentStorage->IStorage_iface;
- }
- else
- break;
- }
+ /*
+ * As documented.
+ */
+ if ((grfMode & STGM_DELETEONRELEASE) ||
+ (grfMode & STGM_TRANSACTED))
+ return STG_E_INVALIDFUNCTION;
- if (pstgDestAncestor == iface)
- {
- BOOL fail = TRUE;
+ /*
+ * Don't worry about permissions in transacted mode, as we can always write
+ * changes; we just can't always commit them.
+ */
+ if(!(This->openFlags & STGM_TRANSACTED)) {
+ /* Can't create a stream on read-only storage */
+ if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
+ return STG_E_ACCESSDENIED;
- if (pstgDestAncestorChild && snbExclude)
- {
- StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
- DirEntry data;
- WCHAR **snb = snbExclude;
+ /* Can't create a stream with greater access than the parent. */
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+ return STG_E_ACCESSDENIED;
+ }
- StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
+ if(This->openFlags & STGM_SIMPLE)
+ if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
- while ( *snb != NULL && fail )
- {
- if ( lstrcmpW(data.name, *snb) == 0 )
- fail = FALSE;
- ++snb;
- }
- }
+ *ppstm = 0;
- if (fail)
- return STG_E_ACCESSDENIED;
- }
- }
+ currentEntryRef = findElement(This,
+ This->storageDirEntry,
+ pwcsName,
+ ¤tEntry);
- return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
- skip_storage, skip_stream, snbExclude, pstgDest );
-}
+ if (currentEntryRef != DIRENTRY_NULL)
+ {
+ /*
+ * An element with this name already exists
+ */
+ if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
+ {
+ IStorage_DestroyElement(iface, pwcsName);
+ }
+ else
+ return STG_E_FILEALREADYEXISTS;
+ }
-/*************************************************************************
- * MoveElementTo (IStorage)
- */
-static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
- IStorage* iface,
- const OLECHAR *pwcsName, /* [string][in] */
- IStorage *pstgDest, /* [unique][in] */
- const OLECHAR *pwcsNewName,/* [string][in] */
- DWORD grfFlags) /* [in] */
-{
- FIXME("(%p %s %p %s %u): stub\n", iface,
- debugstr_w(pwcsName), pstgDest,
- debugstr_w(pwcsNewName), grfFlags);
- return E_NOTIMPL;
-}
+ /*
+ * memset the empty entry
+ */
+ memset(&newStreamEntry, 0, sizeof(DirEntry));
-/*************************************************************************
- * Commit (IStorage)
- *
- * Ensures that any changes made to a storage object open in transacted mode
- * are reflected in the parent storage
- *
- * In a non-transacted mode, this ensures all cached writes are completed.
- */
-static HRESULT WINAPI StorageImpl_Commit(
- IStorage* iface,
- DWORD grfCommitFlags)/* [in] */
-{
- StorageBaseImpl* This = impl_from_IStorage(iface);
- TRACE("(%p %d)\n", iface, grfCommitFlags);
- return StorageBaseImpl_Flush(This);
-}
+ newStreamEntry.sizeOfNameString =
+ ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
-/*************************************************************************
- * Revert (IStorage)
- *
- * Discard all changes that have been made since the last commit operation
- */
-static HRESULT WINAPI StorageImpl_Revert(
- IStorage* iface)
-{
- TRACE("(%p)\n", iface);
- return S_OK;
-}
+ if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
+ return STG_E_INVALIDNAME;
-/*************************************************************************
- * DestroyElement (IStorage)
- *
- * Strategy: This implementation is built this way for simplicity not for speed.
- * I always delete the topmost element of the enumeration and adjust
- * the deleted element pointer all the time. This takes longer to
- * do but allow to reinvoke DestroyElement whenever we encounter a
- * storage object. The optimisation resides in the usage of another
- * enumeration strategy that would give all the leaves of a storage
- * first. (postfix order)
- */
-static HRESULT WINAPI StorageBaseImpl_DestroyElement(
- IStorage* iface,
- const OLECHAR *pwcsName)/* [string][in] */
-{
- StorageBaseImpl *This = impl_from_IStorage(iface);
+ strcpyW(newStreamEntry.name, pwcsName);
- HRESULT hr = S_OK;
- DirEntry entryToDelete;
- DirRef entryToDeleteRef;
+ newStreamEntry.stgType = STGTY_STREAM;
+ newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
+ newStreamEntry.size.u.LowPart = 0;
+ newStreamEntry.size.u.HighPart = 0;
- TRACE("(%p, %s)\n",
- iface, debugstr_w(pwcsName));
+ newStreamEntry.leftChild = DIRENTRY_NULL;
+ newStreamEntry.rightChild = DIRENTRY_NULL;
+ newStreamEntry.dirRootEntry = DIRENTRY_NULL;
- if (pwcsName==NULL)
- return STG_E_INVALIDPOINTER;
+ /* call CoFileTime to get the current time
+ newStreamEntry.ctime
+ newStreamEntry.mtime
+ */
- if (This->reverted)
- return STG_E_REVERTED;
+ /* newStreamEntry.clsid */
- if ( !(This->openFlags & STGM_TRANSACTED) &&
- STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
- return STG_E_ACCESSDENIED;
+ /*
+ * Create an entry with the new data
+ */
+ hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
+ if (FAILED(hr))
+ return hr;
- entryToDeleteRef = findElement(
+ /*
+ * Insert the new entry in the parent storage's tree.
+ */
+ hr = insertIntoTree(
This,
This->storageDirEntry,
- pwcsName,
- &entryToDelete);
-
- if ( entryToDeleteRef == DIRENTRY_NULL )
+ newStreamEntryRef);
+ if (FAILED(hr))
{
- return STG_E_FILENOTFOUND;
+ StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
+ return hr;
}
- if ( entryToDelete.stgType == STGTY_STORAGE )
+ /*
+ * Open the stream to return it.
+ */
+ newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
+
+ if (newStream)
{
- hr = deleteStorageContents(
- This,
- entryToDeleteRef,
- entryToDelete);
+ *ppstm = &newStream->IStream_iface;
+ IStream_AddRef(*ppstm);
}
- else if ( entryToDelete.stgType == STGTY_STREAM )
+ else
{
- hr = deleteStreamContents(
- This,
- entryToDeleteRef,
- entryToDelete);
+ return STG_E_INSUFFICIENTMEMORY;
}
- if (hr!=S_OK)
- return hr;
+ return StorageBaseImpl_Flush(This);
+}
- /*
- * Remove the entry from its parent storage
- */
- hr = removeFromTree(
- This,
- This->storageDirEntry,
- entryToDeleteRef);
+/************************************************************************
+ * StorageBaseImpl_SetClass (IStorage)
+ *
+ * This method will write the specified CLSID in the directory entry of this
+ * storage.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_SetClass(
+ IStorage* iface,
+ REFCLSID clsid) /* [in] */
+{
+ StorageBaseImpl *This = impl_from_IStorage(iface);
+ HRESULT hRes;
+ DirEntry currentEntry;
- /*
- * Invalidate the entry
- */
- if (SUCCEEDED(hr))
- StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
+ TRACE("(%p, %p)\n", iface, clsid);
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_Flush(This);
+ if (This->reverted)
+ return STG_E_REVERTED;
- return hr;
-}
+ hRes = StorageBaseImpl_ReadDirEntry(This,
+ This->storageDirEntry,
+ ¤tEntry);
+ if (SUCCEEDED(hRes))
+ {
+ currentEntry.clsid = *clsid;
+ hRes = StorageBaseImpl_WriteDirEntry(This,
+ This->storageDirEntry,
+ ¤tEntry);
+ }
-/******************************************************************************
- * Internal stream list handlers
- */
+ if (SUCCEEDED(hRes))
+ hRes = StorageBaseImpl_Flush(This);
-void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
-{
- TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
- list_add_tail(&stg->strmHead,&strm->StrmListEntry);
+ return hRes;
}
-void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+/************************************************************************
+ * StorageBaseImpl_CreateStorage (IStorage)
+ *
+ * This method will create the storage object within the provided storage.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_CreateStorage(
+ IStorage* iface,
+ const OLECHAR *pwcsName, /* [string][in] */
+ DWORD grfMode, /* [in] */
+ DWORD reserved1, /* [in] */
+ DWORD reserved2, /* [in] */
+ IStorage **ppstg) /* [out] */
{
- TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
- list_remove(&(strm->StrmListEntry));
-}
+ StorageBaseImpl* This = impl_from_IStorage(iface);
-static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
-{
- StgStreamImpl *strm;
+ DirEntry currentEntry;
+ DirEntry newEntry;
+ DirRef currentEntryRef;
+ DirRef newEntryRef;
+ HRESULT hr;
- LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
+ TRACE("(%p, %s, %x, %d, %d, %p)\n",
+ iface, debugstr_w(pwcsName), grfMode,
+ reserved1, reserved2, ppstg);
+
+ if (ppstg == 0)
+ return STG_E_INVALIDPOINTER;
+
+ if (This->openFlags & STGM_SIMPLE)
{
- if (strm->dirEntry == streamEntry)
- {
- return TRUE;
- }
+ return STG_E_INVALIDFUNCTION;
}
- return FALSE;
-}
+ if (pwcsName == 0)
+ return STG_E_INVALIDNAME;
-static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
-{
- StorageInternalImpl *childstg;
+ *ppstg = NULL;
- LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
+ if ( FAILED( validateSTGM(grfMode) ) ||
+ (grfMode & STGM_DELETEONRELEASE) )
{
- if (childstg->base.storageDirEntry == storageEntry)
- {
- return TRUE;
- }
+ WARN("bad grfMode: 0x%x\n", grfMode);
+ return STG_E_INVALIDFLAG;
}
- return FALSE;
-}
-
-static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
-{
- struct list *cur, *cur2;
- StgStreamImpl *strm=NULL;
- StorageInternalImpl *childstg=NULL;
+ if (This->reverted)
+ return STG_E_REVERTED;
- LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
- strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
- TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
- strm->parentStorage = NULL;
- list_remove(cur);
+ /*
+ * Check that we're compatible with the parent's storage mode
+ */
+ if ( !(This->openFlags & STGM_TRANSACTED) &&
+ STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+ {
+ WARN("access denied\n");
+ return STG_E_ACCESSDENIED;
}
- LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
- childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
- StorageBaseImpl_Invalidate( &childstg->base );
- }
+ currentEntryRef = findElement(This,
+ This->storageDirEntry,
+ pwcsName,
+ ¤tEntry);
- if (stg->transactedChild)
+ if (currentEntryRef != DIRENTRY_NULL)
{
- StorageBaseImpl_Invalidate(stg->transactedChild);
-
- stg->transactedChild = NULL;
+ /*
+ * An element with this name already exists
+ */
+ if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
+ ((This->openFlags & STGM_TRANSACTED) ||
+ STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
+ {
+ hr = IStorage_DestroyElement(iface, pwcsName);
+ if (FAILED(hr))
+ return hr;
+ }
+ else
+ {
+ WARN("file already exists\n");
+ return STG_E_FILEALREADYEXISTS;
+ }
+ }
+ else if (!(This->openFlags & STGM_TRANSACTED) &&
+ STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
+ {
+ WARN("read-only storage\n");
+ return STG_E_ACCESSDENIED;
}
-}
+ memset(&newEntry, 0, sizeof(DirEntry));
-/*********************************************************************
- *
- * Internal Method
- *
- * Delete the contents of a storage entry.
- *
- */
-static HRESULT deleteStorageContents(
- StorageBaseImpl *parentStorage,
- DirRef indexToDelete,
- DirEntry entryDataToDelete)
-{
- IEnumSTATSTG *elements = 0;
- IStorage *childStorage = 0;
- STATSTG currentElement;
- HRESULT hr;
- HRESULT destroyHr = S_OK;
- StorageInternalImpl *stg, *stg2;
+ newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
- /* Invalidate any open storage objects. */
- LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
+ if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
{
- if (stg->base.storageDirEntry == indexToDelete)
- {
- StorageBaseImpl_Invalidate(&stg->base);
- }
+ FIXME("name too long\n");
+ return STG_E_INVALIDNAME;
}
+ strcpyW(newEntry.name, pwcsName);
+
+ newEntry.stgType = STGTY_STORAGE;
+ newEntry.startingBlock = BLOCK_END_OF_CHAIN;
+ newEntry.size.u.LowPart = 0;
+ newEntry.size.u.HighPart = 0;
+
+ newEntry.leftChild = DIRENTRY_NULL;
+ newEntry.rightChild = DIRENTRY_NULL;
+ newEntry.dirRootEntry = DIRENTRY_NULL;
+
+ /* call CoFileTime to get the current time
+ newEntry.ctime
+ newEntry.mtime
+ */
+
+ /* newEntry.clsid */
+
/*
- * Open the storage and enumerate it
+ * Create a new directory entry for the storage
*/
- hr = IStorage_OpenStorage(
- &parentStorage->IStorage_iface,
- entryDataToDelete.name,
- 0,
- STGM_WRITE | STGM_SHARE_EXCLUSIVE,
- 0,
- 0,
- &childStorage);
+ hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
+ if (FAILED(hr))
+ return hr;
- if (hr != S_OK)
+ /*
+ * Insert the new directory entry into the parent storage's tree
+ */
+ hr = insertIntoTree(
+ This,
+ This->storageDirEntry,
+ newEntryRef);
+ if (FAILED(hr))
{
+ StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
return hr;
}
/*
- * Enumerate the elements
+ * Open it to get a pointer to return.
*/
- IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
+ hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
- do
+ if( (hr != S_OK) || (*ppstg == NULL))
{
- /*
- * Obtain the next element
- */
- hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
- if (hr==S_OK)
- {
- destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
+ return hr;
+ }
- CoTaskMemFree(currentElement.pwcsName);
- }
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_Flush(This);
- /*
- * We need to Reset the enumeration every time because we delete elements
- * and the enumeration could be invalid
- */
- IEnumSTATSTG_Reset(elements);
+ return S_OK;
+}
- } while ((hr == S_OK) && (destroyHr == S_OK));
+static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
+ DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+ SNB snbExclude, IStorage *pstgDest)
+{
+ DirEntry data;
+ HRESULT hr;
- IStorage_Release(childStorage);
- IEnumSTATSTG_Release(elements);
+ hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
- return destroyHr;
+ if (SUCCEEDED(hr))
+ hr = IStorage_SetClass( pstgDest, &data.clsid );
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
+ skip_stream, snbExclude, pstgDest );
+
+ return hr;
}
-/*********************************************************************
- *
- * Internal Method
- *
- * Perform the deletion of a stream's data
- *
+/*************************************************************************
+ * CopyTo (IStorage)
*/
-static HRESULT deleteStreamContents(
- StorageBaseImpl *parentStorage,
- DirRef indexToDelete,
- DirEntry entryDataToDelete)
+static HRESULT WINAPI StorageBaseImpl_CopyTo(
+ IStorage* iface,
+ DWORD ciidExclude, /* [in] */
+ const IID* rgiidExclude, /* [size_is][unique][in] */
+ SNB snbExclude, /* [unique][in] */
+ IStorage* pstgDest) /* [unique][in] */
{
- IStream *pis;
- HRESULT hr;
- ULARGE_INTEGER size;
- StgStreamImpl *strm, *strm2;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
- /* Invalidate any open stream objects. */
- LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
- {
- if (strm->dirEntry == indexToDelete)
- {
- TRACE("Stream deleted %p\n", strm);
- strm->parentStorage = NULL;
- list_remove(&strm->StrmListEntry);
- }
- }
+ BOOL skip_storage = FALSE, skip_stream = FALSE;
+ DWORD i;
- size.u.HighPart = 0;
- size.u.LowPart = 0;
+ TRACE("(%p, %d, %p, %p, %p)\n",
+ iface, ciidExclude, rgiidExclude,
+ snbExclude, pstgDest);
- hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
- entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
+ if ( pstgDest == 0 )
+ return STG_E_INVALIDPOINTER;
- if (hr!=S_OK)
+ for(i = 0; i < ciidExclude; ++i)
{
- return(hr);
+ if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
+ skip_storage = TRUE;
+ else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
+ skip_stream = TRUE;
+ else
+ WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
}
- /*
- * Zap the stream
- */
- hr = IStream_SetSize(pis, size);
-
- if(hr != S_OK)
+ if (!skip_storage)
{
- return hr;
- }
-
- /*
- * Release the stream object.
- */
- IStream_Release(pis);
+ /* Give up early if it looks like this would be infinitely recursive.
+ * Oddly enough, this includes some cases that aren't really recursive, like
+ * copying to a transacted child. */
+ IStorage *pstgDestAncestor = pstgDest;
+ IStorage *pstgDestAncestorChild = NULL;
- return S_OK;
-}
+ /* Go up the chain from the destination until we find the source storage. */
+ while (pstgDestAncestor != iface) {
+ pstgDestAncestorChild = pstgDest;
-static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
-{
- switch (relation)
- {
- case DIRENTRY_RELATION_PREVIOUS:
- entry->leftChild = new_target;
- break;
- case DIRENTRY_RELATION_NEXT:
- entry->rightChild = new_target;
- break;
- case DIRENTRY_RELATION_DIR:
- entry->dirRootEntry = new_target;
- break;
- default:
- assert(0);
- }
-}
-
-/*************************************************************************
- *
- * Internal Method
- *
- * This method removes a directory entry from its parent storage tree without
- * freeing any resources attached to it.
- */
-static HRESULT removeFromTree(
- StorageBaseImpl *This,
- DirRef parentStorageIndex,
- DirRef deletedIndex)
-{
- DirEntry entryToDelete;
- DirEntry parentEntry;
- DirRef parentEntryRef;
- ULONG typeOfRelation;
- HRESULT hr;
-
- hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
-
- if (hr != S_OK)
- return hr;
-
- /*
- * Find the element that links to the one we want to delete.
- */
- hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
- &parentEntry, &parentEntryRef, &typeOfRelation);
-
- if (hr != S_OK)
- return hr;
+ if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
+ {
+ TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
- if (entryToDelete.leftChild != DIRENTRY_NULL)
- {
- /*
- * Replace the deleted entry with its left child
- */
- setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
+ pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
+ }
+ else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
+ {
+ StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
- hr = StorageBaseImpl_WriteDirEntry(
- This,
- parentEntryRef,
- &parentEntry);
- if(FAILED(hr))
- {
- return hr;
+ pstgDestAncestor = &internal->parentStorage->IStorage_iface;
+ }
+ else
+ break;
}
- if (entryToDelete.rightChild != DIRENTRY_NULL)
+ if (pstgDestAncestor == iface)
{
- /*
- * We need to reinsert the right child somewhere. We already know it and
- * its children are greater than everything in the left tree, so we
- * insert it at the rightmost point in the left tree.
- */
- DirRef newRightChildParent = entryToDelete.leftChild;
- DirEntry newRightChildParentEntry;
+ BOOL fail = TRUE;
- do
+ if (pstgDestAncestorChild && snbExclude)
{
- hr = StorageBaseImpl_ReadDirEntry(
- This,
- newRightChildParent,
- &newRightChildParentEntry);
- if (FAILED(hr))
- {
- return hr;
- }
-
- if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
- newRightChildParent = newRightChildParentEntry.rightChild;
- } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
+ StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
+ DirEntry data;
+ WCHAR **snb = snbExclude;
- newRightChildParentEntry.rightChild = entryToDelete.rightChild;
+ StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
- hr = StorageBaseImpl_WriteDirEntry(
- This,
- newRightChildParent,
- &newRightChildParentEntry);
- if (FAILED(hr))
- {
- return hr;
+ while ( *snb != NULL && fail )
+ {
+ if ( lstrcmpW(data.name, *snb) == 0 )
+ fail = FALSE;
+ ++snb;
+ }
}
- }
- }
- else
- {
- /*
- * Replace the deleted entry with its right child
- */
- setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
- hr = StorageBaseImpl_WriteDirEntry(
- This,
- parentEntryRef,
- &parentEntry);
- if(FAILED(hr))
- {
- return hr;
+ if (fail)
+ return STG_E_ACCESSDENIED;
}
}
- return hr;
+ return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
+ skip_storage, skip_stream, snbExclude, pstgDest );
}
-
-/******************************************************************************
- * SetElementTimes (IStorage)
+/*************************************************************************
+ * MoveElementTo (IStorage)
*/
-static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
+static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
IStorage* iface,
- const OLECHAR *pwcsName,/* [string][in] */
- const FILETIME *pctime, /* [in] */
- const FILETIME *patime, /* [in] */
- const FILETIME *pmtime) /* [in] */
+ const OLECHAR *pwcsName, /* [string][in] */
+ IStorage *pstgDest, /* [unique][in] */
+ const OLECHAR *pwcsNewName,/* [string][in] */
+ DWORD grfFlags) /* [in] */
{
- FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
- return S_OK;
+ FIXME("(%p %s %p %s %u): stub\n", iface,
+ debugstr_w(pwcsName), pstgDest,
+ debugstr_w(pwcsNewName), grfFlags);
+ return E_NOTIMPL;
}
-/******************************************************************************
- * SetStateBits (IStorage)
+/*************************************************************************
+ * Commit (IStorage)
+ *
+ * Ensures that any changes made to a storage object open in transacted mode
+ * are reflected in the parent storage
+ *
+ * In a non-transacted mode, this ensures all cached writes are completed.
*/
-static HRESULT WINAPI StorageBaseImpl_SetStateBits(
+static HRESULT WINAPI StorageBaseImpl_Commit(
IStorage* iface,
- DWORD grfStateBits,/* [in] */
- DWORD grfMask) /* [in] */
-{
- StorageBaseImpl *This = impl_from_IStorage(iface);
-
- if (This->reverted)
- return STG_E_REVERTED;
-
- This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
- return S_OK;
-}
-
-static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
- DirRef index, const DirEntry *data)
+ DWORD grfCommitFlags)/* [in] */
{
- StorageImpl *This = (StorageImpl*)base;
- return StorageImpl_WriteDirEntry(This, index, data);
+ StorageBaseImpl* This = impl_from_IStorage(iface);
+ TRACE("(%p %d)\n", iface, grfCommitFlags);
+ return StorageBaseImpl_Flush(This);
}
-static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
- DirRef index, DirEntry *data)
+/*************************************************************************
+ * Revert (IStorage)
+ *
+ * Discard all changes that have been made since the last commit operation
+ */
+static HRESULT WINAPI StorageBaseImpl_Revert(
+ IStorage* iface)
{
- StorageImpl *This = (StorageImpl*)base;
- return StorageImpl_ReadDirEntry(This, index, data);
+ TRACE("(%p)\n", iface);
+ return S_OK;
}
-static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
+/*********************************************************************
+ *
+ * Internal helper function for StorageBaseImpl_DestroyElement()
+ *
+ * Delete the contents of a storage entry.
+ *
+ */
+static HRESULT deleteStorageContents(
+ StorageBaseImpl *parentStorage,
+ DirRef indexToDelete,
+ DirEntry entryDataToDelete)
{
- int i;
+ IEnumSTATSTG *elements = 0;
+ IStorage *childStorage = 0;
+ STATSTG currentElement;
+ HRESULT hr;
+ HRESULT destroyHr = S_OK;
+ StorageInternalImpl *stg, *stg2;
- for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ /* Invalidate any open storage objects. */
+ LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
{
- if (!This->blockChainCache[i])
+ if (stg->base.storageDirEntry == indexToDelete)
{
- return &This->blockChainCache[i];
+ StorageBaseImpl_Invalidate(&stg->base);
}
}
- i = This->blockChainToEvict;
-
- BlockChainStream_Destroy(This->blockChainCache[i]);
- This->blockChainCache[i] = NULL;
-
- This->blockChainToEvict++;
- if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
- This->blockChainToEvict = 0;
+ /*
+ * Open the storage and enumerate it
+ */
+ hr = IStorage_OpenStorage(
+ &parentStorage->IStorage_iface,
+ entryDataToDelete.name,
+ 0,
+ STGM_WRITE | STGM_SHARE_EXCLUSIVE,
+ 0,
+ 0,
+ &childStorage);
- return &This->blockChainCache[i];
-}
+ if (hr != S_OK)
+ {
+ return hr;
+ }
-static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
- DirRef index)
-{
- int i, free_index=-1;
+ /*
+ * Enumerate the elements
+ */
+ IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
- for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ do
{
- if (!This->blockChainCache[i])
- {
- if (free_index == -1) free_index = i;
- }
- else if (This->blockChainCache[i]->ownerDirEntry == index)
+ /*
+ * Obtain the next element
+ */
+ hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
+ if (hr==S_OK)
{
- return &This->blockChainCache[i];
+ destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
+
+ CoTaskMemFree(currentElement.pwcsName);
}
- }
- if (free_index == -1)
- {
- free_index = This->blockChainToEvict;
+ /*
+ * We need to Reset the enumeration every time because we delete elements
+ * and the enumeration could be invalid
+ */
+ IEnumSTATSTG_Reset(elements);
- BlockChainStream_Destroy(This->blockChainCache[free_index]);
- This->blockChainCache[free_index] = NULL;
+ } while ((hr == S_OK) && (destroyHr == S_OK));
- This->blockChainToEvict++;
- if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
- This->blockChainToEvict = 0;
- }
+ IStorage_Release(childStorage);
+ IEnumSTATSTG_Release(elements);
- This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
- return &This->blockChainCache[free_index];
+ return destroyHr;
}
-static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
+/*********************************************************************
+ *
+ * Internal helper function for StorageBaseImpl_DestroyElement()
+ *
+ * Perform the deletion of a stream's data
+ *
+ */
+static HRESULT deleteStreamContents(
+ StorageBaseImpl *parentStorage,
+ DirRef indexToDelete,
+ DirEntry entryDataToDelete)
{
- int i;
+ IStream *pis;
+ HRESULT hr;
+ ULARGE_INTEGER size;
+ StgStreamImpl *strm, *strm2;
- for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ /* Invalidate any open stream objects. */
+ LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
{
- if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
+ if (strm->dirEntry == indexToDelete)
{
- BlockChainStream_Destroy(This->blockChainCache[i]);
- This->blockChainCache[i] = NULL;
- return;
+ TRACE("Stream deleted %p\n", strm);
+ strm->parentStorage = NULL;
+ list_remove(&strm->StrmListEntry);
}
}
-}
-static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
- ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
- StorageImpl *This = (StorageImpl*)base;
- DirEntry data;
- HRESULT hr;
- ULONG bytesToRead;
+ size.u.HighPart = 0;
+ size.u.LowPart = 0;
- hr = StorageImpl_ReadDirEntry(This, index, &data);
- if (FAILED(hr)) return hr;
+ hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
+ entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
- if (data.size.QuadPart == 0)
+ if (hr!=S_OK)
{
- *bytesRead = 0;
- return S_OK;
+ return(hr);
}
- if (offset.QuadPart + size > data.size.QuadPart)
- {
- bytesToRead = data.size.QuadPart - offset.QuadPart;
- }
- else
- {
- bytesToRead = size;
- }
+ /*
+ * Zap the stream
+ */
+ hr = IStream_SetSize(pis, size);
- if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ if(hr != S_OK)
{
- SmallBlockChainStream *stream;
-
- stream = SmallBlockChainStream_Construct(This, NULL, index);
- if (!stream) return E_OUTOFMEMORY;
-
- hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
-
- SmallBlockChainStream_Destroy(stream);
-
return hr;
}
- else
- {
- BlockChainStream *stream = NULL;
-
- stream = *StorageImpl_GetCachedBlockChainStream(This, index);
- if (!stream) return E_OUTOFMEMORY;
- hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
+ /*
+ * Release the stream object.
+ */
+ IStream_Release(pis);
- return hr;
- }
+ return S_OK;
}
-static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
- ULARGE_INTEGER newsize)
+/*************************************************************************
+ * DestroyElement (IStorage)
+ *
+ * Strategy: This implementation is built this way for simplicity not for speed.
+ * I always delete the topmost element of the enumeration and adjust
+ * the deleted element pointer all the time. This takes longer to
+ * do but allow to reinvoke DestroyElement whenever we encounter a
+ * storage object. The optimisation resides in the usage of another
+ * enumeration strategy that would give all the leaves of a storage
+ * first. (postfix order)
+ */
+static HRESULT WINAPI StorageBaseImpl_DestroyElement(
+ IStorage* iface,
+ const OLECHAR *pwcsName)/* [string][in] */
{
- StorageImpl *This = (StorageImpl*)base;
- DirEntry data;
- HRESULT hr;
- SmallBlockChainStream *smallblock=NULL;
- BlockChainStream **pbigblock=NULL, *bigblock=NULL;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
- hr = StorageImpl_ReadDirEntry(This, index, &data);
- if (FAILED(hr)) return hr;
+ HRESULT hr = S_OK;
+ DirEntry entryToDelete;
+ DirRef entryToDeleteRef;
- /* In simple mode keep the stream size above the small block limit */
- if (This->base.openFlags & STGM_SIMPLE)
- newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
+ TRACE("(%p, %s)\n",
+ iface, debugstr_w(pwcsName));
- if (data.size.QuadPart == newsize.QuadPart)
- return S_OK;
+ if (pwcsName==NULL)
+ return STG_E_INVALIDPOINTER;
- /* Create a block chain object of the appropriate type */
- if (data.size.QuadPart == 0)
- {
- if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
- {
- smallblock = SmallBlockChainStream_Construct(This, NULL, index);
- if (!smallblock) return E_OUTOFMEMORY;
- }
- else
- {
- pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
- bigblock = *pbigblock;
- if (!bigblock) return E_OUTOFMEMORY;
- }
- }
- else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
- {
- smallblock = SmallBlockChainStream_Construct(This, NULL, index);
- if (!smallblock) return E_OUTOFMEMORY;
- }
- else
- {
- pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
- bigblock = *pbigblock;
- if (!bigblock) return E_OUTOFMEMORY;
- }
+ if (This->reverted)
+ return STG_E_REVERTED;
- /* Change the block chain type if necessary. */
- if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
- {
- bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
- if (!bigblock)
- {
- SmallBlockChainStream_Destroy(smallblock);
- return E_FAIL;
- }
+ if ( !(This->openFlags & STGM_TRANSACTED) &&
+ STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
+ return STG_E_ACCESSDENIED;
- pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
- *pbigblock = bigblock;
- }
- else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ entryToDeleteRef = findElement(
+ This,
+ This->storageDirEntry,
+ pwcsName,
+ &entryToDelete);
+
+ if ( entryToDeleteRef == DIRENTRY_NULL )
{
- smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
- if (!smallblock)
- return E_FAIL;
+ return STG_E_FILENOTFOUND;
}
- /* Set the size of the block chain. */
- if (smallblock)
+ if ( entryToDelete.stgType == STGTY_STORAGE )
{
- SmallBlockChainStream_SetSize(smallblock, newsize);
- SmallBlockChainStream_Destroy(smallblock);
+ hr = deleteStorageContents(
+ This,
+ entryToDeleteRef,
+ entryToDelete);
}
- else
+ else if ( entryToDelete.stgType == STGTY_STREAM )
{
- BlockChainStream_SetSize(bigblock, newsize);
+ hr = deleteStreamContents(
+ This,
+ entryToDeleteRef,
+ entryToDelete);
}
- /* Set the size in the directory entry. */
- hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (hr!=S_OK)
+ return hr;
+
+ /*
+ * Remove the entry from its parent storage
+ */
+ hr = removeFromTree(
+ This,
+ This->storageDirEntry,
+ entryToDeleteRef);
+
+ /*
+ * Invalidate the entry
+ */
if (SUCCEEDED(hr))
- {
- data.size = newsize;
+ StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_Flush(This);
- hr = StorageImpl_WriteDirEntry(This, index, &data);
- }
return hr;
}
-static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
- ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
{
- StorageImpl *This = (StorageImpl*)base;
- DirEntry data;
- HRESULT hr;
- ULARGE_INTEGER newSize;
-
- hr = StorageImpl_ReadDirEntry(This, index, &data);
- if (FAILED(hr)) return hr;
-
- /* Grow the stream if necessary */
- newSize.QuadPart = offset.QuadPart + size;
+ struct list *cur, *cur2;
+ StgStreamImpl *strm=NULL;
+ StorageInternalImpl *childstg=NULL;
- if (newSize.QuadPart > data.size.QuadPart)
- {
- hr = StorageImpl_StreamSetSize(base, index, newSize);
- if (FAILED(hr))
- return hr;
+ LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
+ strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
+ TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
+ strm->parentStorage = NULL;
+ list_remove(cur);
+ }
- hr = StorageImpl_ReadDirEntry(This, index, &data);
- if (FAILED(hr)) return hr;
+ LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
+ childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
+ StorageBaseImpl_Invalidate( &childstg->base );
}
- if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ if (stg->transactedChild)
{
- SmallBlockChainStream *stream;
-
- stream = SmallBlockChainStream_Construct(This, NULL, index);
- if (!stream) return E_OUTOFMEMORY;
-
- hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
-
- SmallBlockChainStream_Destroy(stream);
+ StorageBaseImpl_Invalidate(stg->transactedChild);
- return hr;
+ stg->transactedChild = NULL;
}
- else
- {
- BlockChainStream *stream;
-
- stream = *StorageImpl_GetCachedBlockChainStream(This, index);
- if (!stream) return E_OUTOFMEMORY;
+}
- return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
- }
+/******************************************************************************
+ * SetElementTimes (IStorage)
+ */
+static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
+ IStorage* iface,
+ const OLECHAR *pwcsName,/* [string][in] */
+ const FILETIME *pctime, /* [in] */
+ const FILETIME *patime, /* [in] */
+ const FILETIME *pmtime) /* [in] */
+{
+ FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
+ return S_OK;
}
-static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
- DirRef src)
+/******************************************************************************
+ * SetStateBits (IStorage)
+ */
+static HRESULT WINAPI StorageBaseImpl_SetStateBits(
+ IStorage* iface,
+ DWORD grfStateBits,/* [in] */
+ DWORD grfMask) /* [in] */
{
- StorageImpl *This = (StorageImpl*)base;
- DirEntry dst_data, src_data;
- HRESULT hr;
+ StorageBaseImpl *This = impl_from_IStorage(iface);
- hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
+ if (This->reverted)
+ return STG_E_REVERTED;
- if (SUCCEEDED(hr))
- hr = StorageImpl_ReadDirEntry(This, src, &src_data);
+ This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
+ return S_OK;
+}
- if (SUCCEEDED(hr))
- {
- StorageImpl_DeleteCachedBlockChainStream(This, src);
- dst_data.startingBlock = src_data.startingBlock;
- dst_data.size = src_data.size;
+/******************************************************************************
+ * Internal stream list handlers
+ */
- hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
- }
+void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+{
+ TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
+ list_add_tail(&stg->strmHead,&strm->StrmListEntry);
+}
- return hr;
+void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+{
+ TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
+ list_remove(&(strm->StrmListEntry));
}
-static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
- ULONG* result, BOOL refresh)
+static HRESULT StorageBaseImpl_CopyStream(
+ StorageBaseImpl *dst, DirRef dst_entry,
+ StorageBaseImpl *src, DirRef src_entry)
{
- StorageImpl *This = (StorageImpl*)base;
- HRESULT hr=S_OK;
- DWORD oldTransactionSig = This->transactionSig;
+ HRESULT hr;
+ BYTE data[4096];
+ DirEntry srcdata;
+ ULARGE_INTEGER bytes_copied;
+ ULONG bytestocopy, bytesread, byteswritten;
- if (refresh)
- {
- ULARGE_INTEGER offset;
- ULONG bytes_read;
- BYTE data[4];
+ hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
- offset.u.HighPart = 0;
- offset.u.LowPart = OFFSET_TRANSACTIONSIG;
- hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
+ if (SUCCEEDED(hr))
+ {
+ hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
- if (SUCCEEDED(hr))
+ bytes_copied.QuadPart = 0;
+ while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
{
- StorageUtl_ReadDWord(data, 0, &This->transactionSig);
+ bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
- if (oldTransactionSig != This->transactionSig)
- {
- /* Someone else wrote to this, so toss all cached information. */
- TRACE("signature changed\n");
+ hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
+ data, &bytesread);
+ if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
- hr = StorageImpl_Refresh(This, FALSE, FALSE);
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
+ data, &byteswritten);
+ if (SUCCEEDED(hr))
+ {
+ if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
+ bytes_copied.QuadPart += byteswritten;
}
-
- if (FAILED(hr))
- This->transactionSig = oldTransactionSig;
}
}
- *result = This->transactionSig;
-
return hr;
}
-static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
- ULONG value)
-{
- StorageImpl *This = (StorageImpl*)base;
-
- This->transactionSig = value;
- StorageImpl_SaveFileHeader(This);
-
- return S_OK;
-}
-
-static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT StorageBaseImpl_DupStorageTree(
+ StorageBaseImpl *dst, DirRef *dst_entry,
+ StorageBaseImpl *src, DirRef src_entry)
{
- StorageImpl *This = (StorageImpl*)base;
HRESULT hr;
- ULARGE_INTEGER offset, cb;
+ DirEntry data;
+ BOOL has_stream=FALSE;
- if (write)
+ if (src_entry == DIRENTRY_NULL)
{
- /* Synchronous grab of second priority range, the commit lock, and the
- * lock-checking lock. */
- offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
- cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+ *dst_entry = DIRENTRY_NULL;
+ return S_OK;
}
- else
+
+ hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
+ if (SUCCEEDED(hr))
{
- offset.QuadPart = RANGELOCK_COMMIT;
- cb.QuadPart = 1;
+ has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
+ data.startingBlock = BLOCK_END_OF_CHAIN;
+ data.size.QuadPart = 0;
+
+ hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
}
- hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
- if (hr == STG_E_INVALIDFUNCTION)
- hr = S_OK;
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
+
+ if (SUCCEEDED(hr) && has_stream)
+ hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
return hr;
}
-static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT StorageBaseImpl_CopyStorageTree(
+ StorageBaseImpl *dst, DirRef dst_entry,
+ StorageBaseImpl *src, DirRef src_entry)
{
- StorageImpl *This = (StorageImpl*)base;
HRESULT hr;
- ULARGE_INTEGER offset, cb;
+ DirEntry src_data, dst_data;
+ DirRef new_root_entry;
- if (write)
+ hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
+
+ if (SUCCEEDED(hr))
{
- offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
- cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+ hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
}
- else
+
+ if (SUCCEEDED(hr))
{
- offset.QuadPart = RANGELOCK_COMMIT;
- cb.QuadPart = 1;
+ hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
+ dst_data.clsid = src_data.clsid;
+ dst_data.ctime = src_data.ctime;
+ dst_data.mtime = src_data.mtime;
+ dst_data.dirRootEntry = new_root_entry;
}
- hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
-
- if (hr == STG_E_INVALIDFUNCTION)
- hr = S_OK;
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
return hr;
}
-static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
{
- StorageImpl *This = (StorageImpl*) iface;
- STATSTG statstg;
HRESULT hr;
+ DirEntry data;
+ ULARGE_INTEGER zero;
- hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
+ if (entry == DIRENTRY_NULL)
+ return S_OK;
- *result = statstg.pwcsName;
+ zero.QuadPart = 0;
- return hr;
-}
+ hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
-static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
-{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
-}
+ if (SUCCEEDED(hr) && include_siblings)
+ hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
-static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
-{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- return IStorage_AddRef(&This->IStorage_iface);
-}
+ if (SUCCEEDED(hr) && include_siblings)
+ hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
-static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
-{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- return IStorage_Release(&This->IStorage_iface);
-}
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
-static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
-{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- FIXME("(%p)->(%d): stub\n", This, timeout);
- return E_NOTIMPL;
-}
+ if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
+ hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
-static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
-{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- FIXME("(%p): stub\n", This);
- return E_NOTIMPL;
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_DestroyDirEntry(This, entry);
+
+ return hr;
}
-static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
+
+/************************************************************************
+ * StorageImpl implementation
+ ***********************************************************************/
+
+static HRESULT StorageImpl_ReadAt(StorageImpl* This,
+ ULARGE_INTEGER offset,
+ void* buffer,
+ ULONG size,
+ ULONG* bytesRead)
{
- StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
- FIXME("(%p): stub\n", This);
- return E_NOTIMPL;
+ return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
}
-static const IDirectWriterLockVtbl DirectWriterLockVtbl =
+static HRESULT StorageImpl_WriteAt(StorageImpl* This,
+ ULARGE_INTEGER offset,
+ const void* buffer,
+ const ULONG size,
+ ULONG* bytesWritten)
{
- directwriterlock_QueryInterface,
- directwriterlock_AddRef,
- directwriterlock_Release,
- directwriterlock_WaitForWriteAccess,
- directwriterlock_ReleaseWriteAccess,
- directwriterlock_HaveWriteAccess
-};
+ return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
+}
-/*
- * Virtual function table for the IStorageImpl class.
+/******************************************************************************
+ * StorageImpl_LoadFileHeader
+ *
+ * This method will read in the file header
*/
-static const IStorageVtbl StorageImpl_Vtbl =
-{
- StorageBaseImpl_QueryInterface,
- StorageBaseImpl_AddRef,
- StorageBaseImpl_Release,
- StorageBaseImpl_CreateStream,
- StorageBaseImpl_OpenStream,
- StorageBaseImpl_CreateStorage,
- StorageBaseImpl_OpenStorage,
- StorageBaseImpl_CopyTo,
- StorageBaseImpl_MoveElementTo,
- StorageImpl_Commit,
- StorageImpl_Revert,
- StorageBaseImpl_EnumElements,
- StorageBaseImpl_DestroyElement,
- StorageBaseImpl_RenameElement,
- StorageBaseImpl_SetElementTimes,
- StorageBaseImpl_SetClass,
- StorageBaseImpl_SetStateBits,
- StorageBaseImpl_Stat
-};
-
-static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
+static HRESULT StorageImpl_LoadFileHeader(
+ StorageImpl* This)
{
- StorageImpl_Destroy,
- StorageImpl_Invalidate,
- StorageImpl_Flush,
- StorageImpl_GetFilename,
- StorageImpl_CreateDirEntry,
- StorageImpl_BaseWriteDirEntry,
- StorageImpl_BaseReadDirEntry,
- StorageImpl_DestroyDirEntry,
- StorageImpl_StreamReadAt,
- StorageImpl_StreamWriteAt,
- StorageImpl_StreamSetSize,
- StorageImpl_StreamLink,
- StorageImpl_GetTransactionSig,
- StorageImpl_SetTransactionSig,
- StorageImpl_LockTransaction,
- StorageImpl_UnlockTransaction
-};
+ HRESULT hr;
+ BYTE headerBigBlock[HEADER_SIZE];
+ int index;
+ ULARGE_INTEGER offset;
+ DWORD bytes_read;
-static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
- ULARGE_INTEGER cb, DWORD dwLockType)
-{
- HRESULT hr;
- int delay = 0;
- DWORD start_time = GetTickCount();
- DWORD last_sanity_check = start_time;
- ULARGE_INTEGER sanity_offset, sanity_cb;
+ TRACE("\n");
+ /*
+ * Get a pointer to the big block of data containing the header.
+ */
+ offset.u.HighPart = 0;
+ offset.u.LowPart = 0;
+ hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
+ if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
+ hr = STG_E_FILENOTFOUND;
- sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
- sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
+ /*
+ * Extract the information from the header.
+ */
+ if (SUCCEEDED(hr))
+ {
+ /*
+ * Check for the "magic number" signature and return an error if it is not
+ * found.
+ */
+ if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
+ {
+ return STG_E_OLDFORMAT;
+ }
- do
+ if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
{
- hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
+ return STG_E_INVALIDHEADER;
+ }
- if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
- {
- DWORD current_time = GetTickCount();
- if (current_time - start_time >= 20000)
- {
- /* timeout */
- break;
- }
- if (current_time - last_sanity_check >= 500)
- {
- /* Any storage implementation with the file open in a
- * shared mode should not lock these bytes for writing. However,
- * some programs (LibreOffice Writer) will keep ALL bytes locked
- * when opening in exclusive mode. We can use a read lock to
- * detect this case early, and not hang a full 20 seconds.
- *
- * This can collide with another attempt to open the file in
- * exclusive mode, but it's unlikely, and someone would fail anyway. */
- hr = ILockBytes_LockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
- if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
- break;
- if (hr == STG_E_INVALIDFUNCTION)
- {
- /* ignore this, lockbytes might support dwLockType but not 0 */
- hr = STG_E_ACCESSDENIED;
- }
- if (SUCCEEDED(hr))
- {
- ILockBytes_UnlockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
- hr = STG_E_ACCESSDENIED;
- }
+ StorageUtl_ReadWord(
+ headerBigBlock,
+ OFFSET_BIGBLOCKSIZEBITS,
+ &This->bigBlockSizeBits);
- last_sanity_check = current_time;
- }
- Sleep(delay);
- if (delay < 150) delay++;
- }
- } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
+ StorageUtl_ReadWord(
+ headerBigBlock,
+ OFFSET_SMALLBLOCKSIZEBITS,
+ &This->smallBlockSizeBits);
- return hr;
-}
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTCOUNT,
+ &This->bigBlockDepotCount);
-static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
- ULONG end, HRESULT fail_hr)
-{
- HRESULT hr;
- ULARGE_INTEGER offset, cb;
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_ROOTSTARTBLOCK,
+ &This->rootStartBlock);
- offset.QuadPart = start;
- cb.QuadPart = 1 + end - start;
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_TRANSACTIONSIG,
+ &This->transactionSig);
- hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
- if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_SMALLBLOCKLIMIT,
+ &This->smallBlockLimit);
- if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
- return fail_hr;
- else
- return S_OK;
-}
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_SBDEPOTSTART,
+ &This->smallBlockDepotStart);
-static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
-{
- HRESULT hr=S_OK;
- int i, j;
- ULARGE_INTEGER offset, cb;
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTSTART,
+ &This->extBigBlockDepotStart);
- cb.QuadPart = 1;
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTCOUNT,
+ &This->extBigBlockDepotCount);
- for (i=start; i<=end; i++)
+ for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
{
- offset.QuadPart = i;
- hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
- if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
- break;
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
+ &(This->bigBlockDepotStart[index]));
}
- if (SUCCEEDED(hr))
- {
- for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
- {
- if (This->locked_bytes[j] == 0)
- {
- This->locked_bytes[j] = i;
- break;
- }
- }
+ /*
+ * Make the bitwise arithmetic to get the size of the blocks in bytes.
+ */
+ This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
+ This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
+
+ /*
+ * Right now, the code is making some assumptions about the size of the
+ * blocks, just make sure they are what we're expecting.
+ */
+ if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
+ This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
+ This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
+ This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
+ hr = STG_E_INVALIDHEADER;
}
+ else
+ hr = S_OK;
+ }
- return hr;
+ return hr;
}
-static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
+/******************************************************************************
+ * StorageImpl_SaveFileHeader
+ *
+ * This method will save to the file the header
+ */
+static void StorageImpl_SaveFileHeader(
+ StorageImpl* This)
{
- HRESULT hr;
- ULARGE_INTEGER offset;
- ULARGE_INTEGER cb;
- DWORD share_mode = STGM_SHARE_MODE(openFlags);
+ BYTE headerBigBlock[HEADER_SIZE];
+ int index;
+ HRESULT hr;
+ ULARGE_INTEGER offset;
+ DWORD bytes_read, bytes_written;
+ DWORD major_version, dirsectorcount;
- if (openFlags & STGM_NOSNAPSHOT)
- {
- /* STGM_NOSNAPSHOT implies deny write */
- if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
- else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
- }
+ /*
+ * Get a pointer to the big block of data containing the header.
+ */
+ offset.u.HighPart = 0;
+ offset.u.LowPart = 0;
+ hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
+ if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
+ hr = STG_E_FILENOTFOUND;
- /* Wrap all other locking inside a single lock so we can check ranges safely */
- offset.QuadPart = RANGELOCK_CHECKLOCKS;
- cb.QuadPart = 1;
- hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
+ if (This->bigBlockSizeBits == 0x9)
+ major_version = 3;
+ else if (This->bigBlockSizeBits == 0xc)
+ major_version = 4;
+ else
+ {
+ ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
+ major_version = 4;
+ }
- /* If the ILockBytes doesn't support locking that's ok. */
- if (hr == STG_E_INVALIDFUNCTION) return S_OK;
- else if (FAILED(hr)) return hr;
+ /*
+ * If the block read failed, the file is probably new.
+ */
+ if (FAILED(hr))
+ {
+ /*
+ * Initialize for all unknown fields.
+ */
+ memset(headerBigBlock, 0, HEADER_SIZE);
- hr = S_OK;
+ /*
+ * Initialize the magic number.
+ */
+ memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
+ }
- /* First check for any conflicting locks. */
- if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
- hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
+ /*
+ * Write the information to the header.
+ */
+ StorageUtl_WriteWord(
+ headerBigBlock,
+ OFFSET_MINORVERSION,
+ 0x3e);
- if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
- hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
+ StorageUtl_WriteWord(
+ headerBigBlock,
+ OFFSET_MAJORVERSION,
+ major_version);
- if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
- hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
+ StorageUtl_WriteWord(
+ headerBigBlock,
+ OFFSET_BYTEORDERMARKER,
+ (WORD)-2);
- if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
- hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
+ StorageUtl_WriteWord(
+ headerBigBlock,
+ OFFSET_BIGBLOCKSIZEBITS,
+ This->bigBlockSizeBits);
- if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
- hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
+ StorageUtl_WriteWord(
+ headerBigBlock,
+ OFFSET_SMALLBLOCKSIZEBITS,
+ This->smallBlockSizeBits);
- /* Then grab our locks. */
- if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
- {
- hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
- if (SUCCEEDED(hr))
- hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
- }
+ if (major_version >= 4)
+ {
+ if (This->rootBlockChain)
+ dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
+ else
+ /* This file is being created, and it will start out with one block. */
+ dirsectorcount = 1;
+ }
+ else
+ /* This field must be 0 in versions older than 4 */
+ dirsectorcount = 0;
- if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
- hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_DIRSECTORCOUNT,
+ dirsectorcount);
- if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
- hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTCOUNT,
+ This->bigBlockDepotCount);
- if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
- hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_ROOTSTARTBLOCK,
+ This->rootStartBlock);
- if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
- hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_TRANSACTIONSIG,
+ This->transactionSig);
- if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
- hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_SMALLBLOCKLIMIT,
+ This->smallBlockLimit);
- offset.QuadPart = RANGELOCK_CHECKLOCKS;
- cb.QuadPart = 1;
- ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_SBDEPOTSTART,
+ This->smallBlockDepotStart);
- return hr;
-}
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_SBDEPOTCOUNT,
+ This->smallBlockDepotChain ?
+ BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
-static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
-{
- HRESULT hr=S_OK;
- DirEntry currentEntry;
- DirRef currentEntryRef;
- BlockChainStream *blockChainStream;
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTSTART,
+ This->extBigBlockDepotStart);
- if (create)
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTCOUNT,
+ This->extBigBlockDepotCount);
+
+ for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
{
- ULARGE_INTEGER size;
- BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
+ StorageUtl_WriteDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
+ (This->bigBlockDepotStart[index]));
+ }
- /* Discard any existing data. */
- size.QuadPart = 0;
- ILockBytes_SetSize(This->lockBytes, size);
+ /*
+ * Write the big block back to the file.
+ */
+ StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
+}
- /*
- * Initialize all header variables:
- * - The big block depot consists of one block and it is at block 0
- * - The directory table starts at block 1
- * - There is no small block depot
- */
- memset( This->bigBlockDepotStart,
- BLOCK_UNUSED,
- sizeof(This->bigBlockDepotStart));
- This->bigBlockDepotCount = 1;
- This->bigBlockDepotStart[0] = 0;
- This->rootStartBlock = 1;
- This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
- This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
- if (This->bigBlockSize == 4096)
- This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
- else
- This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
- This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
- This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
- This->extBigBlockDepotCount = 0;
+/************************************************************************
+ * StorageImpl implementation : DirEntry methods
+ ***********************************************************************/
- StorageImpl_SaveFileHeader(This);
+/******************************************************************************
+ * StorageImpl_ReadRawDirEntry
+ *
+ * This method will read the raw data from a directory entry in the file.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
+{
+ ULARGE_INTEGER offset;
+ HRESULT hr;
+ ULONG bytesRead;
- /*
- * Add one block for the big block depot and one block for the directory table
- */
- size.u.HighPart = 0;
- size.u.LowPart = This->bigBlockSize * 3;
- ILockBytes_SetSize(This->lockBytes, size);
+ offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
- /*
- * Initialize the big block depot
- */
- memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
- StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
- StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
- StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
- }
- else
- {
- /*
- * Load the header for the file.
- */
- hr = StorageImpl_LoadFileHeader(This);
+ hr = BlockChainStream_ReadAt(
+ This->rootBlockChain,
+ offset,
+ RAW_DIRENTRY_SIZE,
+ buffer,
+ &bytesRead);
- if (FAILED(hr))
- {
- return hr;
- }
- }
+ if (bytesRead != RAW_DIRENTRY_SIZE)
+ return STG_E_READFAULT;
- /*
- * There is no block depot cached yet.
- */
- This->indexBlockDepotCached = 0xFFFFFFFF;
- This->indexExtBlockDepotCached = 0xFFFFFFFF;
+ return hr;
+}
- /*
- * Start searching for free blocks with block 0.
- */
- This->prevFreeBlock = 0;
+/******************************************************************************
+ * StorageImpl_WriteRawDirEntry
+ *
+ * This method will write the raw data from a directory entry in the file.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
+{
+ ULARGE_INTEGER offset;
+ ULONG bytesRead;
- This->firstFreeSmallBlock = 0;
+ offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
- /* Read the extended big block depot locations. */
- if (This->extBigBlockDepotCount != 0)
- {
- ULONG current_block = This->extBigBlockDepotStart;
- ULONG cache_size = This->extBigBlockDepotCount * 2;
- ULONG i;
+ return BlockChainStream_WriteAt(
+ This->rootBlockChain,
+ offset,
+ RAW_DIRENTRY_SIZE,
+ buffer,
+ &bytesRead);
+}
- This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
- if (!This->extBigBlockDepotLocations)
- {
- return E_OUTOFMEMORY;
- }
+/***************************************************************************
+ *
+ * Internal Method
+ *
+ * Mark a directory entry in the file as free.
+ */
+static HRESULT StorageImpl_DestroyDirEntry(
+ StorageBaseImpl *base,
+ DirRef index)
+{
+ BYTE emptyData[RAW_DIRENTRY_SIZE];
+ StorageImpl *storage = (StorageImpl*)base;
- This->extBigBlockDepotLocationsSize = cache_size;
+ memset(emptyData, 0, RAW_DIRENTRY_SIZE);
- for (i=0; i<This->extBigBlockDepotCount; i++)
- {
- if (current_block == BLOCK_END_OF_CHAIN)
- {
- WARN("File has too few extended big block depot blocks.\n");
- return STG_E_DOCFILECORRUPT;
- }
- This->extBigBlockDepotLocations[i] = current_block;
- current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
- }
- }
- else
- {
- This->extBigBlockDepotLocations = NULL;
- This->extBigBlockDepotLocationsSize = 0;
- }
+ return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
+}
- /*
- * Create the block chain abstractions.
- */
- if(!(blockChainStream =
- BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
- {
- return STG_E_READFAULT;
- }
- if (!new_object)
- BlockChainStream_Destroy(This->rootBlockChain);
- This->rootBlockChain = blockChainStream;
+/******************************************************************************
+ * UpdateRawDirEntry
+ *
+ * Update raw directory entry data from the fields in newData.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
+{
+ memset(buffer, 0, RAW_DIRENTRY_SIZE);
- if(!(blockChainStream =
- BlockChainStream_Construct(This, &This->smallBlockDepotStart,
- DIRENTRY_NULL)))
- {
- return STG_E_READFAULT;
- }
- if (!new_object)
- BlockChainStream_Destroy(This->smallBlockDepotChain);
- This->smallBlockDepotChain = blockChainStream;
+ memcpy(
+ buffer + OFFSET_PS_NAME,
+ newData->name,
+ DIRENTRY_NAME_BUFFER_LEN );
- /*
- * Write the root storage entry (memory only)
- */
- if (create)
- {
- static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
- DirEntry rootEntry;
- /*
- * Initialize the directory table
- */
- memset(&rootEntry, 0, sizeof(rootEntry));
- strcpyW(rootEntry.name, rootentryW);
- rootEntry.sizeOfNameString = sizeof(rootentryW);
- rootEntry.stgType = STGTY_ROOT;
- rootEntry.leftChild = DIRENTRY_NULL;
- rootEntry.rightChild = DIRENTRY_NULL;
- rootEntry.dirRootEntry = DIRENTRY_NULL;
- rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
- rootEntry.size.u.HighPart = 0;
- rootEntry.size.u.LowPart = 0;
+ memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
- StorageImpl_WriteDirEntry(This, 0, &rootEntry);
- }
+ StorageUtl_WriteWord(
+ buffer,
+ OFFSET_PS_NAMELENGTH,
+ newData->sizeOfNameString);
- /*
- * Find the ID of the root storage.
- */
- currentEntryRef = 0;
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_LEFTCHILD,
+ newData->leftChild);
- do
- {
- hr = StorageImpl_ReadDirEntry(
- This,
- currentEntryRef,
- ¤tEntry);
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_RIGHTCHILD,
+ newData->rightChild);
- if (SUCCEEDED(hr))
- {
- if ( (currentEntry.sizeOfNameString != 0 ) &&
- (currentEntry.stgType == STGTY_ROOT) )
- {
- This->base.storageDirEntry = currentEntryRef;
- }
- }
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_DIRROOT,
+ newData->dirRootEntry);
- currentEntryRef++;
+ StorageUtl_WriteGUID(
+ buffer,
+ OFFSET_PS_GUID,
+ &newData->clsid);
- } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_CTIMELOW,
+ newData->ctime.dwLowDateTime);
- if (FAILED(hr))
- {
- return STG_E_READFAULT;
- }
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_CTIMEHIGH,
+ newData->ctime.dwHighDateTime);
- /*
- * Create the block chain abstraction for the small block root chain.
- */
- if(!(blockChainStream =
- BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
- {
- return STG_E_READFAULT;
- }
- if (!new_object)
- BlockChainStream_Destroy(This->smallBlockRootChain);
- This->smallBlockRootChain = blockChainStream;
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_MTIMELOW,
+ newData->mtime.dwLowDateTime);
- if (!new_object)
- {
- int i;
- for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
- {
- BlockChainStream_Destroy(This->blockChainCache[i]);
- This->blockChainCache[i] = NULL;
- }
- }
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_MTIMEHIGH,
+ newData->ctime.dwHighDateTime);
- return hr;
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_STARTBLOCK,
+ newData->startingBlock);
+
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_SIZE,
+ newData->size.u.LowPart);
+
+ StorageUtl_WriteDWord(
+ buffer,
+ OFFSET_PS_SIZE_HIGH,
+ newData->size.u.HighPart);
}
-static HRESULT StorageImpl_Construct(
- HANDLE hFile,
- LPCOLESTR pwcsName,
- ILockBytes* pLkbyt,
- DWORD openFlags,
- BOOL fileBased,
- BOOL create,
- ULONG sector_size,
- StorageImpl** result)
+/***************************************************************************
+ *
+ * Internal Method
+ *
+ * Reserve a directory entry in the file and initialize it.
+ */
+static HRESULT StorageImpl_CreateDirEntry(
+ StorageBaseImpl *base,
+ const DirEntry *newData,
+ DirRef *index)
{
- StorageImpl* This;
- HRESULT hr = S_OK;
-
- if ( FAILED( validateSTGM(openFlags) ))
- return STG_E_INVALIDFLAG;
-
- This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
- if (!This)
- return E_OUTOFMEMORY;
-
- memset(This, 0, sizeof(StorageImpl));
-
- list_init(&This->base.strmHead);
+ StorageImpl *storage = (StorageImpl*)base;
+ ULONG currentEntryIndex = 0;
+ ULONG newEntryIndex = DIRENTRY_NULL;
+ HRESULT hr = S_OK;
+ BYTE currentData[RAW_DIRENTRY_SIZE];
+ WORD sizeOfNameString;
- list_init(&This->base.storageHead);
+ do
+ {
+ hr = StorageImpl_ReadRawDirEntry(storage,
+ currentEntryIndex,
+ currentData);
- This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
- This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
- This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
- This->base.baseVtbl = &StorageImpl_BaseVtbl;
- This->base.openFlags = (openFlags & ~STGM_CREATE);
- This->base.ref = 1;
- This->base.create = create;
+ if (SUCCEEDED(hr))
+ {
+ StorageUtl_ReadWord(
+ currentData,
+ OFFSET_PS_NAMELENGTH,
+ &sizeOfNameString);
- if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
- This->base.lockingrole = SWMR_Writer;
- else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
- This->base.lockingrole = SWMR_Reader;
- else
- This->base.lockingrole = SWMR_None;
+ if (sizeOfNameString == 0)
+ {
+ /*
+ * The entry exists and is available, we found it.
+ */
+ newEntryIndex = currentEntryIndex;
+ }
+ }
+ else
+ {
+ /*
+ * We exhausted the directory entries, we will create more space below
+ */
+ newEntryIndex = currentEntryIndex;
+ }
+ currentEntryIndex++;
- This->base.reverted = FALSE;
+ } while (newEntryIndex == DIRENTRY_NULL);
/*
- * Initialize the big block cache.
+ * grow the directory stream
*/
- This->bigBlockSize = sector_size;
- This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
- if (hFile)
- hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
- else
- {
- This->lockBytes = pLkbyt;
- ILockBytes_AddRef(pLkbyt);
- }
-
- if (SUCCEEDED(hr))
- hr = StorageImpl_GrabLocks(This, openFlags);
-
- if (SUCCEEDED(hr))
- hr = StorageImpl_Refresh(This, TRUE, create);
-
if (FAILED(hr))
{
- IStorage_Release(&This->base.IStorage_iface);
- *result = NULL;
- }
- else
- {
- StorageImpl_Flush(&This->base);
- *result = This;
- }
-
- return hr;
-}
-
-static void StorageImpl_Invalidate(StorageBaseImpl* iface)
-{
- StorageImpl *This = (StorageImpl*) iface;
-
- StorageBaseImpl_DeleteAll(&This->base);
-
- This->base.reverted = TRUE;
-}
-
-static void StorageImpl_Destroy(StorageBaseImpl* iface)
-{
- StorageImpl *This = (StorageImpl*) iface;
- int i;
- TRACE("(%p)\n", This);
+ BYTE emptyData[RAW_DIRENTRY_SIZE];
+ ULARGE_INTEGER newSize;
+ ULONG entryIndex;
+ ULONG lastEntry = 0;
+ ULONG blockCount = 0;
- StorageImpl_Flush(iface);
+ /*
+ * obtain the new count of blocks in the directory stream
+ */
+ blockCount = BlockChainStream_GetCount(
+ storage->rootBlockChain)+1;
- StorageImpl_Invalidate(iface);
+ /*
+ * initialize the size used by the directory stream
+ */
+ newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
- HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+ /*
+ * add a block to the directory stream
+ */
+ BlockChainStream_SetSize(storage->rootBlockChain, newSize);
- BlockChainStream_Destroy(This->smallBlockRootChain);
- BlockChainStream_Destroy(This->rootBlockChain);
- BlockChainStream_Destroy(This->smallBlockDepotChain);
+ /*
+ * memset the empty entry in order to initialize the unused newly
+ * created entries
+ */
+ memset(emptyData, 0, RAW_DIRENTRY_SIZE);
- for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
- BlockChainStream_Destroy(This->blockChainCache[i]);
+ /*
+ * initialize them
+ */
+ lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
- for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
- {
- ULARGE_INTEGER offset, cb;
- cb.QuadPart = 1;
- if (This->locked_bytes[i] != 0)
+ for(
+ entryIndex = newEntryIndex + 1;
+ entryIndex < lastEntry;
+ entryIndex++)
{
- offset.QuadPart = This->locked_bytes[i];
- ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ StorageImpl_WriteRawDirEntry(
+ storage,
+ entryIndex,
+ emptyData);
}
- }
-
- if (This->lockBytes)
- ILockBytes_Release(This->lockBytes);
- HeapFree(GetProcessHeap(), 0, This);
-}
-
-static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
-{
- StorageImpl *This = (StorageImpl*)storage;
- int i;
- HRESULT hr;
- TRACE("(%p)\n", This);
-
- hr = BlockChainStream_Flush(This->smallBlockRootChain);
- if (SUCCEEDED(hr))
- hr = BlockChainStream_Flush(This->rootBlockChain);
+ StorageImpl_SaveFileHeader(storage);
+ }
- if (SUCCEEDED(hr))
- hr = BlockChainStream_Flush(This->smallBlockDepotChain);
+ UpdateRawDirEntry(currentData, newData);
- for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
- if (This->blockChainCache[i])
- hr = BlockChainStream_Flush(This->blockChainCache[i]);
+ hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
if (SUCCEEDED(hr))
- hr = ILockBytes_Flush(This->lockBytes);
+ *index = newEntryIndex;
return hr;
}
/******************************************************************************
- * StorageImpl_GetNextFreeBigBlock
- *
- * Returns the index of the next free big block.
- * If the big block depot is filled, this method will enlarge it.
+ * StorageImpl_ReadDirEntry
*
+ * This method will read the specified directory entry.
*/
-static ULONG StorageImpl_GetNextFreeBigBlock(
- StorageImpl* This)
+HRESULT StorageImpl_ReadDirEntry(
+ StorageImpl* This,
+ DirRef index,
+ DirEntry* buffer)
{
- ULONG depotBlockIndexPos;
- BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
- ULONG depotBlockOffset;
- ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
- ULONG nextBlockIndex = BLOCK_SPECIAL;
- int depotIndex = 0;
- ULONG freeBlock = BLOCK_UNUSED;
- ULONG read;
- ULARGE_INTEGER neededSize;
- STATSTG statstg;
+ BYTE currentEntry[RAW_DIRENTRY_SIZE];
+ HRESULT readRes;
- depotIndex = This->prevFreeBlock / blocksPerDepot;
- depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
+ readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
- /*
- * Scan the entire big block depot until we find a block marked free
- */
- while (nextBlockIndex != BLOCK_UNUSED)
+ if (SUCCEEDED(readRes))
{
- if (depotIndex < COUNT_BBDEPOTINHEADER)
- {
- depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
-
- /*
- * Grow the primary depot.
- */
- if (depotBlockIndexPos == BLOCK_UNUSED)
- {
- depotBlockIndexPos = depotIndex*blocksPerDepot;
+ memset(buffer->name, 0, sizeof(buffer->name));
+ memcpy(
+ buffer->name,
+ (WCHAR *)currentEntry+OFFSET_PS_NAME,
+ DIRENTRY_NAME_BUFFER_LEN );
+ TRACE("storage name: %s\n", debugstr_w(buffer->name));
- /*
- * Add a block depot.
- */
- Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
- This->bigBlockDepotCount++;
- This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
+ memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
- /*
- * Flag it as a block depot.
- */
- StorageImpl_SetNextBlockInChain(This,
- depotBlockIndexPos,
- BLOCK_SPECIAL);
-
- /* Save new header information.
- */
- StorageImpl_SaveFileHeader(This);
- }
- }
- else
- {
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
+ StorageUtl_ReadWord(
+ currentEntry,
+ OFFSET_PS_NAMELENGTH,
+ &buffer->sizeOfNameString);
- if (depotBlockIndexPos == BLOCK_UNUSED)
- {
- /*
- * Grow the extended depot.
- */
- ULONG extIndex = BLOCK_UNUSED;
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
- ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_LEFTCHILD,
+ &buffer->leftChild);
- if (extBlockOffset == 0)
- {
- /* We need an extended block.
- */
- extIndex = Storage32Impl_AddExtBlockDepot(This);
- This->extBigBlockDepotCount++;
- depotBlockIndexPos = extIndex + 1;
- }
- else
- depotBlockIndexPos = depotIndex * blocksPerDepot;
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_RIGHTCHILD,
+ &buffer->rightChild);
- /*
- * Add a block depot and mark it in the extended block.
- */
- Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
- This->bigBlockDepotCount++;
- Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_DIRROOT,
+ &buffer->dirRootEntry);
- /* Flag the block depot.
- */
- StorageImpl_SetNextBlockInChain(This,
- depotBlockIndexPos,
- BLOCK_SPECIAL);
+ StorageUtl_ReadGUID(
+ currentEntry,
+ OFFSET_PS_GUID,
+ &buffer->clsid);
- /* If necessary, flag the extended depot block.
- */
- if (extIndex != BLOCK_UNUSED)
- StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_CTIMELOW,
+ &buffer->ctime.dwLowDateTime);
- /* Save header information.
- */
- StorageImpl_SaveFileHeader(This);
- }
- }
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_CTIMEHIGH,
+ &buffer->ctime.dwHighDateTime);
- StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_MTIMELOW,
+ &buffer->mtime.dwLowDateTime);
- if (read)
- {
- while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
- ( nextBlockIndex != BLOCK_UNUSED))
- {
- StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_MTIMEHIGH,
+ &buffer->mtime.dwHighDateTime);
- if (nextBlockIndex == BLOCK_UNUSED)
- {
- freeBlock = (depotIndex * blocksPerDepot) +
- (depotBlockOffset/sizeof(ULONG));
- }
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_STARTBLOCK,
+ &buffer->startingBlock);
- depotBlockOffset += sizeof(ULONG);
- }
- }
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_SIZE,
+ &buffer->size.u.LowPart);
- depotIndex++;
- depotBlockOffset = 0;
+ StorageUtl_ReadDWord(
+ currentEntry,
+ OFFSET_PS_SIZE_HIGH,
+ &buffer->size.u.HighPart);
}
- /*
- * make sure that the block physically exists before using it
- */
- neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
+ return readRes;
+}
- ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
+/*********************************************************************
+ * Write the specified directory entry to the file
+ */
+HRESULT StorageImpl_WriteDirEntry(
+ StorageImpl* This,
+ DirRef index,
+ const DirEntry* buffer)
+{
+ BYTE currentEntry[RAW_DIRENTRY_SIZE];
- if (neededSize.QuadPart > statstg.cbSize.QuadPart)
- ILockBytes_SetSize(This->lockBytes, neededSize);
+ UpdateRawDirEntry(currentEntry, buffer);
- This->prevFreeBlock = freeBlock;
+ return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
+}
- return freeBlock;
+
+/************************************************************************
+ * StorageImpl implementation : Block methods
+ ***********************************************************************/
+
+static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
+{
+ return (ULONGLONG)(index+1) * This->bigBlockSize;
}
-/******************************************************************************
- * Storage32Impl_AddBlockDepot
- *
- * This will create a depot block, essentially it is a block initialized
- * to BLOCK_UNUSEDs.
- */
-static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
+static HRESULT StorageImpl_ReadBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ void* buffer,
+ ULONG* out_read)
{
- BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
- ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
- ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
- ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
+ ULARGE_INTEGER ulOffset;
+ DWORD read=0;
+ HRESULT hr;
- /*
- * Initialize blocks as free
- */
- memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
- /* Reserve the range lock sector */
- if (depotIndex == rangeLockDepot)
+ hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
+
+ if (SUCCEEDED(hr) && read < This->bigBlockSize)
{
- ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
+ /* File ends during this block; fill the rest with 0's. */
+ memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
}
- StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
+ if (out_read) *out_read = read;
+
+ return hr;
}
-/******************************************************************************
- * Storage32Impl_GetExtDepotBlock
- *
- * Returns the index of the block that corresponds to the specified depot
- * index. This method is only for depot indexes equal or greater than
- * COUNT_BBDEPOTINHEADER.
- */
-static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
+static BOOL StorageImpl_ReadDWordFromBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ ULONG offset,
+ DWORD* value)
{
- ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
- ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
- ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
- ULONG blockIndex = BLOCK_UNUSED;
- ULONG extBlockIndex;
- BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
- int index, num_blocks;
-
- assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+ ULARGE_INTEGER ulOffset;
+ DWORD read;
+ DWORD tmp;
- if (extBlockCount >= This->extBigBlockDepotCount)
- return BLOCK_UNUSED;
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+ ulOffset.QuadPart += offset;
- if (This->indexExtBlockDepotCached != extBlockCount)
- {
- extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+ StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
+ *value = lendian32toh(tmp);
+ return (read == sizeof(DWORD));
+}
- StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
+static BOOL StorageImpl_WriteBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ const void* buffer)
+{
+ ULARGE_INTEGER ulOffset;
+ DWORD wrote;
- num_blocks = This->bigBlockSize / 4;
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
- for (index = 0; index < num_blocks; index++)
- {
- StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
- This->extBlockDepotCached[index] = blockIndex;
- }
+ StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
+ return (wrote == This->bigBlockSize);
+}
- This->indexExtBlockDepotCached = extBlockCount;
- }
+static BOOL StorageImpl_WriteDWordToBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ ULONG offset,
+ DWORD value)
+{
+ ULARGE_INTEGER ulOffset;
+ DWORD wrote;
- blockIndex = This->extBlockDepotCached[extBlockOffset];
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+ ulOffset.QuadPart += offset;
- return blockIndex;
+ value = htole32(value);
+ StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
+ return (wrote == sizeof(DWORD));
}
/******************************************************************************
- * Storage32Impl_SetExtDepotBlock
+ * Storage32Impl_SmallBlocksToBigBlocks
*
- * Associates the specified block index to the specified depot index.
- * This method is only for depot indexes equal or greater than
- * COUNT_BBDEPOTINHEADER.
+ * This method will convert a small block chain to a big block chain.
+ * The small block chain will be destroyed.
*/
-static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
+BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
+ StorageImpl* This,
+ SmallBlockChainStream** ppsbChain)
{
- ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
- ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
- ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
- ULONG extBlockIndex;
-
- assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+ ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
+ ULARGE_INTEGER size, offset;
+ ULONG cbRead, cbWritten;
+ ULARGE_INTEGER cbTotalRead;
+ DirRef streamEntryRef;
+ HRESULT resWrite = S_OK;
+ HRESULT resRead;
+ DirEntry streamEntry;
+ BYTE *buffer;
+ BlockChainStream *bbTempChain = NULL;
+ BlockChainStream *bigBlockChain = NULL;
- assert(extBlockCount < This->extBigBlockDepotCount);
+ /*
+ * Create a temporary big block chain that doesn't have
+ * an associated directory entry. This temporary chain will be
+ * used to copy data from small blocks to big blocks.
+ */
+ bbTempChain = BlockChainStream_Construct(This,
+ &bbHeadOfChain,
+ DIRENTRY_NULL);
+ if(!bbTempChain) return NULL;
+ /*
+ * Grow the big block chain.
+ */
+ size = SmallBlockChainStream_GetSize(*ppsbChain);
+ BlockChainStream_SetSize(bbTempChain, size);
- extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+ /*
+ * Copy the contents of the small block chain to the big block chain
+ * by small block size increments.
+ */
+ offset.u.LowPart = 0;
+ offset.u.HighPart = 0;
+ cbTotalRead.QuadPart = 0;
- if (extBlockIndex != BLOCK_UNUSED)
+ buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
+ do
{
- StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
- extBlockOffset * sizeof(ULONG),
- blockIndex);
- }
+ resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
+ offset,
+ min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
+ buffer,
+ &cbRead);
+ if (FAILED(resRead))
+ break;
- if (This->indexExtBlockDepotCached == extBlockCount)
- {
- This->extBlockDepotCached[extBlockOffset] = blockIndex;
- }
-}
+ if (cbRead > 0)
+ {
+ cbTotalRead.QuadPart += cbRead;
-/******************************************************************************
- * Storage32Impl_AddExtBlockDepot
- *
- * Creates an extended depot block.
- */
-static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
-{
- ULONG numExtBlocks = This->extBigBlockDepotCount;
- ULONG nextExtBlock = This->extBigBlockDepotStart;
- BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
- ULONG index = BLOCK_UNUSED;
- ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
- ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
- ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
+ resWrite = BlockChainStream_WriteAt(bbTempChain,
+ offset,
+ cbRead,
+ buffer,
+ &cbWritten);
- index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
- blocksPerDepotBlock;
+ if (FAILED(resWrite))
+ break;
- if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
+ offset.u.LowPart += cbRead;
+ }
+ else
+ {
+ resRead = STG_E_READFAULT;
+ break;
+ }
+ } while (cbTotalRead.QuadPart < size.QuadPart);
+ HeapFree(GetProcessHeap(),0,buffer);
+
+ size.u.HighPart = 0;
+ size.u.LowPart = 0;
+
+ if (FAILED(resRead) || FAILED(resWrite))
{
- /*
- * The first extended block.
- */
- This->extBigBlockDepotStart = index;
+ ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
+ BlockChainStream_SetSize(bbTempChain, size);
+ BlockChainStream_Destroy(bbTempChain);
+ return NULL;
}
- else
- {
- /*
- * Find the last existing extended block.
- */
- nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
- /*
- * Add the new extended block to the chain.
- */
- StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
- index);
- }
+ /*
+ * Destroy the small block chain.
+ */
+ streamEntryRef = (*ppsbChain)->ownerDirEntry;
+ SmallBlockChainStream_SetSize(*ppsbChain, size);
+ SmallBlockChainStream_Destroy(*ppsbChain);
+ *ppsbChain = 0;
/*
- * Initialize this block.
+ * Change the directory entry. This chain is now a big block chain
+ * and it doesn't reside in the small blocks chain anymore.
*/
- memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
- StorageImpl_WriteBigBlock(This, index, depotBuffer);
+ StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
- /* Add the block to our cache. */
- if (This->extBigBlockDepotLocationsSize == numExtBlocks)
- {
- ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
- ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
+ streamEntry.startingBlock = bbHeadOfChain;
- memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
- HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+ StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
- This->extBigBlockDepotLocations = new_cache;
- This->extBigBlockDepotLocationsSize = new_cache_size;
- }
- This->extBigBlockDepotLocations[numExtBlocks] = index;
+ /*
+ * Destroy the temporary entryless big block chain.
+ * Create a new big block chain associated with this entry.
+ */
+ BlockChainStream_Destroy(bbTempChain);
+ bigBlockChain = BlockChainStream_Construct(This,
+ NULL,
+ streamEntryRef);
- return index;
+ return bigBlockChain;
}
/******************************************************************************
- * StorageImpl_FreeBigBlock
+ * Storage32Impl_BigBlocksToSmallBlocks
*
- * This method will flag the specified block as free in the big block depot.
+ * This method will convert a big block chain to a small block chain.
+ * The big block chain will be destroyed on success.
*/
-static void StorageImpl_FreeBigBlock(
- StorageImpl* This,
- ULONG blockIndex)
+SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
+ StorageImpl* This,
+ BlockChainStream** ppbbChain,
+ ULARGE_INTEGER newSize)
{
- StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
-
- if (blockIndex < This->prevFreeBlock)
- This->prevFreeBlock = blockIndex;
-}
+ ULARGE_INTEGER size, offset, cbTotalRead;
+ ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
+ DirRef streamEntryRef;
+ HRESULT resWrite = S_OK, resRead = S_OK;
+ DirEntry streamEntry;
+ BYTE* buffer;
+ SmallBlockChainStream* sbTempChain;
-/************************************************************************
- * StorageImpl_GetNextBlockInChain
- *
- * This method will retrieve the block index of the next big block in
- * in the chain.
- *
- * Params: This - Pointer to the Storage object.
- * blockIndex - Index of the block to retrieve the chain
- * for.
- * nextBlockIndex - receives the return value.
- *
- * Returns: This method returns the index of the next block in the chain.
- * It will return the constants:
- * BLOCK_SPECIAL - If the block given was not part of a
- * chain.
- * BLOCK_END_OF_CHAIN - If the block given was the last in
- * a chain.
- * BLOCK_UNUSED - If the block given was not past of a chain
- * and is available.
- * BLOCK_EXTBBDEPOT - This block is part of the extended
- * big block depot.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT StorageImpl_GetNextBlockInChain(
- StorageImpl* This,
- ULONG blockIndex,
- ULONG* nextBlockIndex)
-{
- ULONG offsetInDepot = blockIndex * sizeof (ULONG);
- ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
- ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
- BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
- ULONG read;
- ULONG depotBlockIndexPos;
- int index, num_blocks;
+ TRACE("%p %p\n", This, ppbbChain);
- *nextBlockIndex = BLOCK_SPECIAL;
+ sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
+ DIRENTRY_NULL);
- if(depotBlockCount >= This->bigBlockDepotCount)
- {
- WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
- This->bigBlockDepotCount);
- return STG_E_READFAULT;
- }
+ if(!sbTempChain)
+ return NULL;
- /*
- * Cache the currently accessed depot block.
- */
- if (depotBlockCount != This->indexBlockDepotCached)
- {
- This->indexBlockDepotCached = depotBlockCount;
+ SmallBlockChainStream_SetSize(sbTempChain, newSize);
+ size = BlockChainStream_GetSize(*ppbbChain);
+ size.QuadPart = min(size.QuadPart, newSize.QuadPart);
- if (depotBlockCount < COUNT_BBDEPOTINHEADER)
+ offset.u.HighPart = 0;
+ offset.u.LowPart = 0;
+ cbTotalRead.QuadPart = 0;
+ buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
+ while(cbTotalRead.QuadPart < size.QuadPart)
{
- depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
- }
- else
- {
- /*
- * We have to look in the extended depot.
- */
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
- }
+ resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
+ min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
+ buffer, &cbRead);
- StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
+ if(FAILED(resRead))
+ break;
- if (!read)
- return STG_E_READFAULT;
+ if(cbRead > 0)
+ {
+ cbTotalRead.QuadPart += cbRead;
- num_blocks = This->bigBlockSize / 4;
+ resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
+ cbRead, buffer, &cbWritten);
- for (index = 0; index < num_blocks; index++)
+ if(FAILED(resWrite))
+ break;
+
+ offset.u.LowPart += cbRead;
+ }
+ else
+ {
+ resRead = STG_E_READFAULT;
+ break;
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, buffer);
+
+ size.u.HighPart = 0;
+ size.u.LowPart = 0;
+
+ if(FAILED(resRead) || FAILED(resWrite))
{
- StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
- This->blockDepotCached[index] = *nextBlockIndex;
+ ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
+ SmallBlockChainStream_SetSize(sbTempChain, size);
+ SmallBlockChainStream_Destroy(sbTempChain);
+ return NULL;
}
- }
- *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
+ /* destroy the original big block chain */
+ streamEntryRef = (*ppbbChain)->ownerDirEntry;
+ BlockChainStream_SetSize(*ppbbChain, size);
+ BlockChainStream_Destroy(*ppbbChain);
+ *ppbbChain = NULL;
- return S_OK;
+ StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
+ streamEntry.startingBlock = sbHeadOfChain;
+ StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
+
+ SmallBlockChainStream_Destroy(sbTempChain);
+ return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
}
/******************************************************************************
- * Storage32Impl_GetNextExtendedBlock
- *
- * Given an extended block this method will return the next extended block.
- *
- * NOTES:
- * The last ULONG of an extended block is the block index of the next
- * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
- * depot.
+ * Storage32Impl_AddBlockDepot
*
- * Return values:
- * - The index of the next extended block
- * - BLOCK_UNUSED: there is no next extended block.
- * - Any other return values denotes failure.
+ * This will create a depot block, essentially it is a block initialized
+ * to BLOCK_UNUSEDs.
*/
-static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
+static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
{
- ULONG nextBlockIndex = BLOCK_SPECIAL;
- ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
+ BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
+ ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
+ ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
+ ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
- StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
- &nextBlockIndex);
+ /*
+ * Initialize blocks as free
+ */
+ memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
- return nextBlockIndex;
+ /* Reserve the range lock sector */
+ if (depotIndex == rangeLockDepot)
+ {
+ ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
+ }
+
+ StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
}
/******************************************************************************
- * StorageImpl_SetNextBlockInChain
- *
- * This method will write the index of the specified block's next block
- * in the big block depot.
- *
- * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
- * do the following
- *
- * StorageImpl_SetNextBlockInChain(This, 3, 1);
- * StorageImpl_SetNextBlockInChain(This, 1, 7);
- * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
+ * Storage32Impl_GetExtDepotBlock
*
+ * Returns the index of the block that corresponds to the specified depot
+ * index. This method is only for depot indexes equal or greater than
+ * COUNT_BBDEPOTINHEADER.
*/
-static void StorageImpl_SetNextBlockInChain(
- StorageImpl* This,
- ULONG blockIndex,
- ULONG nextBlock)
+static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
{
- ULONG offsetInDepot = blockIndex * sizeof (ULONG);
- ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
- ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
- ULONG depotBlockIndexPos;
+ ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
+ ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
+ ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
+ ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
+ ULONG blockIndex = BLOCK_UNUSED;
+ ULONG extBlockIndex;
+ BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+ int index, num_blocks;
- assert(depotBlockCount < This->bigBlockDepotCount);
- assert(blockIndex != nextBlock);
+ assert(depotIndex >= COUNT_BBDEPOTINHEADER);
- if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
- /* This should never happen (storage file format spec forbids it), but
- * older versions of Wine may have generated broken files. We don't want to
- * assert and potentially lose data, but we do want to know if this ever
- * happens in a newly-created file. */
- ERR("Using range lock page\n");
+ if (extBlockCount >= This->extBigBlockDepotCount)
+ return BLOCK_UNUSED;
- if (depotBlockCount < COUNT_BBDEPOTINHEADER)
+ if (This->indexExtBlockDepotCached != extBlockCount)
{
- depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
+ extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+
+ StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
+
+ num_blocks = This->bigBlockSize / 4;
+
+ for (index = 0; index < num_blocks; index++)
+ {
+ StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
+ This->extBlockDepotCached[index] = blockIndex;
+ }
+
+ This->indexExtBlockDepotCached = extBlockCount;
}
- else
+
+ blockIndex = This->extBlockDepotCached[extBlockOffset];
+
+ return blockIndex;
+}
+
+/******************************************************************************
+ * Storage32Impl_SetExtDepotBlock
+ *
+ * Associates the specified block index to the specified depot index.
+ * This method is only for depot indexes equal or greater than
+ * COUNT_BBDEPOTINHEADER.
+ */
+static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
+{
+ ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
+ ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
+ ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
+ ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
+ ULONG extBlockIndex;
+
+ assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+
+ assert(extBlockCount < This->extBigBlockDepotCount);
+
+ extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+
+ if (extBlockIndex != BLOCK_UNUSED)
{
- /*
- * We have to look in the extended depot.
- */
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
+ StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
+ extBlockOffset * sizeof(ULONG),
+ blockIndex);
}
- StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
- nextBlock);
- /*
- * Update the cached block depot, if necessary.
- */
- if (depotBlockCount == This->indexBlockDepotCached)
+ if (This->indexExtBlockDepotCached == extBlockCount)
{
- This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
+ This->extBlockDepotCached[extBlockOffset] = blockIndex;
}
}
/******************************************************************************
- * StorageImpl_LoadFileHeader
+ * Storage32Impl_AddExtBlockDepot
*
- * This method will read in the file header
+ * Creates an extended depot block.
*/
-static HRESULT StorageImpl_LoadFileHeader(
- StorageImpl* This)
+static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
{
- HRESULT hr;
- BYTE headerBigBlock[HEADER_SIZE];
- int index;
- ULARGE_INTEGER offset;
- DWORD bytes_read;
+ ULONG numExtBlocks = This->extBigBlockDepotCount;
+ ULONG nextExtBlock = This->extBigBlockDepotStart;
+ BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+ ULONG index = BLOCK_UNUSED;
+ ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
+ ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
+ ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
- TRACE("\n");
- /*
- * Get a pointer to the big block of data containing the header.
- */
- offset.u.HighPart = 0;
- offset.u.LowPart = 0;
- hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
- if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
- hr = STG_E_FILENOTFOUND;
+ index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
+ blocksPerDepotBlock;
- /*
- * Extract the information from the header.
- */
- if (SUCCEEDED(hr))
+ if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
{
/*
- * Check for the "magic number" signature and return an error if it is not
- * found.
+ * The first extended block.
*/
- if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
- {
- return STG_E_OLDFORMAT;
- }
-
- if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
- {
- return STG_E_INVALIDHEADER;
- }
+ This->extBigBlockDepotStart = index;
+ }
+ else
+ {
+ /*
+ * Find the last existing extended block.
+ */
+ nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
- StorageUtl_ReadWord(
- headerBigBlock,
- OFFSET_BIGBLOCKSIZEBITS,
- &This->bigBlockSizeBits);
+ /*
+ * Add the new extended block to the chain.
+ */
+ StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
+ index);
+ }
- StorageUtl_ReadWord(
- headerBigBlock,
- OFFSET_SMALLBLOCKSIZEBITS,
- &This->smallBlockSizeBits);
+ /*
+ * Initialize this block.
+ */
+ memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
+ StorageImpl_WriteBigBlock(This, index, depotBuffer);
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_BBDEPOTCOUNT,
- &This->bigBlockDepotCount);
+ /* Add the block to our cache. */
+ if (This->extBigBlockDepotLocationsSize == numExtBlocks)
+ {
+ ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
+ ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_ROOTSTARTBLOCK,
- &This->rootStartBlock);
+ memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
+ HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_TRANSACTIONSIG,
- &This->transactionSig);
+ This->extBigBlockDepotLocations = new_cache;
+ This->extBigBlockDepotLocationsSize = new_cache_size;
+ }
+ This->extBigBlockDepotLocations[numExtBlocks] = index;
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_SMALLBLOCKLIMIT,
- &This->smallBlockLimit);
+ return index;
+}
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_SBDEPOTSTART,
- &This->smallBlockDepotStart);
+/************************************************************************
+ * StorageImpl_GetNextBlockInChain
+ *
+ * This method will retrieve the block index of the next big block in
+ * in the chain.
+ *
+ * Params: This - Pointer to the Storage object.
+ * blockIndex - Index of the block to retrieve the chain
+ * for.
+ * nextBlockIndex - receives the return value.
+ *
+ * Returns: This method returns the index of the next block in the chain.
+ * It will return the constants:
+ * BLOCK_SPECIAL - If the block given was not part of a
+ * chain.
+ * BLOCK_END_OF_CHAIN - If the block given was the last in
+ * a chain.
+ * BLOCK_UNUSED - If the block given was not past of a chain
+ * and is available.
+ * BLOCK_EXTBBDEPOT - This block is part of the extended
+ * big block depot.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT StorageImpl_GetNextBlockInChain(
+ StorageImpl* This,
+ ULONG blockIndex,
+ ULONG* nextBlockIndex)
+{
+ ULONG offsetInDepot = blockIndex * sizeof (ULONG);
+ ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
+ ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
+ BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+ ULONG read;
+ ULONG depotBlockIndexPos;
+ int index, num_blocks;
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_EXTBBDEPOTSTART,
- &This->extBigBlockDepotStart);
+ *nextBlockIndex = BLOCK_SPECIAL;
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_EXTBBDEPOTCOUNT,
- &This->extBigBlockDepotCount);
+ if(depotBlockCount >= This->bigBlockDepotCount)
+ {
+ WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
+ This->bigBlockDepotCount);
+ return STG_E_READFAULT;
+ }
- for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
+ /*
+ * Cache the currently accessed depot block.
+ */
+ if (depotBlockCount != This->indexBlockDepotCached)
+ {
+ This->indexBlockDepotCached = depotBlockCount;
+
+ if (depotBlockCount < COUNT_BBDEPOTINHEADER)
{
- StorageUtl_ReadDWord(
- headerBigBlock,
- OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
- &(This->bigBlockDepotStart[index]));
+ depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
+ }
+ else
+ {
+ /*
+ * We have to look in the extended depot.
+ */
+ depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
}
- /*
- * Make the bitwise arithmetic to get the size of the blocks in bytes.
- */
- This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
- This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
+ StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
- /*
- * Right now, the code is making some assumptions about the size of the
- * blocks, just make sure they are what we're expecting.
- */
- if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
- This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
- This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
+ if (!read)
+ return STG_E_READFAULT;
+
+ num_blocks = This->bigBlockSize / 4;
+
+ for (index = 0; index < num_blocks; index++)
{
- FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
- This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
- hr = STG_E_INVALIDHEADER;
+ StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
+ This->blockDepotCached[index] = *nextBlockIndex;
}
- else
- hr = S_OK;
}
- return hr;
+ *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
+
+ return S_OK;
}
/******************************************************************************
- * StorageImpl_SaveFileHeader
+ * Storage32Impl_GetNextExtendedBlock
*
- * This method will save to the file the header
+ * Given an extended block this method will return the next extended block.
+ *
+ * NOTES:
+ * The last ULONG of an extended block is the block index of the next
+ * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
+ * depot.
+ *
+ * Return values:
+ * - The index of the next extended block
+ * - BLOCK_UNUSED: there is no next extended block.
+ * - Any other return values denotes failure.
*/
-static void StorageImpl_SaveFileHeader(
- StorageImpl* This)
+static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
{
- BYTE headerBigBlock[HEADER_SIZE];
- int index;
- HRESULT hr;
- ULARGE_INTEGER offset;
- DWORD bytes_read, bytes_written;
- DWORD major_version, dirsectorcount;
+ ULONG nextBlockIndex = BLOCK_SPECIAL;
+ ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
- /*
- * Get a pointer to the big block of data containing the header.
- */
- offset.u.HighPart = 0;
- offset.u.LowPart = 0;
- hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
- if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
- hr = STG_E_FILENOTFOUND;
+ StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
+ &nextBlockIndex);
- if (This->bigBlockSizeBits == 0x9)
- major_version = 3;
- else if (This->bigBlockSizeBits == 0xc)
- major_version = 4;
- else
+ return nextBlockIndex;
+}
+
+/******************************************************************************
+ * StorageImpl_SetNextBlockInChain
+ *
+ * This method will write the index of the specified block's next block
+ * in the big block depot.
+ *
+ * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
+ * do the following
+ *
+ * StorageImpl_SetNextBlockInChain(This, 3, 1);
+ * StorageImpl_SetNextBlockInChain(This, 1, 7);
+ * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
+ *
+ */
+static void StorageImpl_SetNextBlockInChain(
+ StorageImpl* This,
+ ULONG blockIndex,
+ ULONG nextBlock)
+{
+ ULONG offsetInDepot = blockIndex * sizeof (ULONG);
+ ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
+ ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
+ ULONG depotBlockIndexPos;
+
+ assert(depotBlockCount < This->bigBlockDepotCount);
+ assert(blockIndex != nextBlock);
+
+ if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
+ /* This should never happen (storage file format spec forbids it), but
+ * older versions of Wine may have generated broken files. We don't want to
+ * assert and potentially lose data, but we do want to know if this ever
+ * happens in a newly-created file. */
+ ERR("Using range lock page\n");
+
+ if (depotBlockCount < COUNT_BBDEPOTINHEADER)
{
- ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
- major_version = 4;
+ depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
}
-
- /*
- * If the block read failed, the file is probably new.
- */
- if (FAILED(hr))
+ else
{
/*
- * Initialize for all unknown fields.
- */
- memset(headerBigBlock, 0, HEADER_SIZE);
-
- /*
- * Initialize the magic number.
+ * We have to look in the extended depot.
*/
- memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
+ depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
}
+ StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
+ nextBlock);
/*
- * Write the information to the header.
+ * Update the cached block depot, if necessary.
*/
- StorageUtl_WriteWord(
- headerBigBlock,
- OFFSET_MINORVERSION,
- 0x3e);
+ if (depotBlockCount == This->indexBlockDepotCached)
+ {
+ This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
+ }
+}
- StorageUtl_WriteWord(
- headerBigBlock,
- OFFSET_MAJORVERSION,
- major_version);
+/******************************************************************************
+ * StorageImpl_GetNextFreeBigBlock
+ *
+ * Returns the index of the next free big block.
+ * If the big block depot is filled, this method will enlarge it.
+ *
+ */
+static ULONG StorageImpl_GetNextFreeBigBlock(
+ StorageImpl* This)
+{
+ ULONG depotBlockIndexPos;
+ BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+ ULONG depotBlockOffset;
+ ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
+ ULONG nextBlockIndex = BLOCK_SPECIAL;
+ int depotIndex = 0;
+ ULONG freeBlock = BLOCK_UNUSED;
+ ULONG read;
+ ULARGE_INTEGER neededSize;
+ STATSTG statstg;
- StorageUtl_WriteWord(
- headerBigBlock,
- OFFSET_BYTEORDERMARKER,
- (WORD)-2);
+ depotIndex = This->prevFreeBlock / blocksPerDepot;
+ depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
- StorageUtl_WriteWord(
- headerBigBlock,
- OFFSET_BIGBLOCKSIZEBITS,
- This->bigBlockSizeBits);
+ /*
+ * Scan the entire big block depot until we find a block marked free
+ */
+ while (nextBlockIndex != BLOCK_UNUSED)
+ {
+ if (depotIndex < COUNT_BBDEPOTINHEADER)
+ {
+ depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
- StorageUtl_WriteWord(
- headerBigBlock,
- OFFSET_SMALLBLOCKSIZEBITS,
- This->smallBlockSizeBits);
+ /*
+ * Grow the primary depot.
+ */
+ if (depotBlockIndexPos == BLOCK_UNUSED)
+ {
+ depotBlockIndexPos = depotIndex*blocksPerDepot;
- if (major_version >= 4)
- {
- if (This->rootBlockChain)
- dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
+ /*
+ * Add a block depot.
+ */
+ Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
+ This->bigBlockDepotCount++;
+ This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
+
+ /*
+ * Flag it as a block depot.
+ */
+ StorageImpl_SetNextBlockInChain(This,
+ depotBlockIndexPos,
+ BLOCK_SPECIAL);
+
+ /* Save new header information.
+ */
+ StorageImpl_SaveFileHeader(This);
+ }
+ }
else
- /* This file is being created, and it will start out with one block. */
- dirsectorcount = 1;
- }
- else
- /* This field must be 0 in versions older than 4 */
- dirsectorcount = 0;
+ {
+ depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_DIRSECTORCOUNT,
- dirsectorcount);
+ if (depotBlockIndexPos == BLOCK_UNUSED)
+ {
+ /*
+ * Grow the extended depot.
+ */
+ ULONG extIndex = BLOCK_UNUSED;
+ ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
+ ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_BBDEPOTCOUNT,
- This->bigBlockDepotCount);
+ if (extBlockOffset == 0)
+ {
+ /* We need an extended block.
+ */
+ extIndex = Storage32Impl_AddExtBlockDepot(This);
+ This->extBigBlockDepotCount++;
+ depotBlockIndexPos = extIndex + 1;
+ }
+ else
+ depotBlockIndexPos = depotIndex * blocksPerDepot;
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_ROOTSTARTBLOCK,
- This->rootStartBlock);
+ /*
+ * Add a block depot and mark it in the extended block.
+ */
+ Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
+ This->bigBlockDepotCount++;
+ Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_TRANSACTIONSIG,
- This->transactionSig);
+ /* Flag the block depot.
+ */
+ StorageImpl_SetNextBlockInChain(This,
+ depotBlockIndexPos,
+ BLOCK_SPECIAL);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_SMALLBLOCKLIMIT,
- This->smallBlockLimit);
+ /* If necessary, flag the extended depot block.
+ */
+ if (extIndex != BLOCK_UNUSED)
+ StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_SBDEPOTSTART,
- This->smallBlockDepotStart);
+ /* Save header information.
+ */
+ StorageImpl_SaveFileHeader(This);
+ }
+ }
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_SBDEPOTCOUNT,
- This->smallBlockDepotChain ?
- BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
+ StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_EXTBBDEPOTSTART,
- This->extBigBlockDepotStart);
+ if (read)
+ {
+ while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
+ ( nextBlockIndex != BLOCK_UNUSED))
+ {
+ StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_EXTBBDEPOTCOUNT,
- This->extBigBlockDepotCount);
+ if (nextBlockIndex == BLOCK_UNUSED)
+ {
+ freeBlock = (depotIndex * blocksPerDepot) +
+ (depotBlockOffset/sizeof(ULONG));
+ }
- for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
- {
- StorageUtl_WriteDWord(
- headerBigBlock,
- OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
- (This->bigBlockDepotStart[index]));
+ depotBlockOffset += sizeof(ULONG);
+ }
+ }
+
+ depotIndex++;
+ depotBlockOffset = 0;
}
/*
- * Write the big block back to the file.
+ * make sure that the block physically exists before using it
*/
- StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
-}
-
-/******************************************************************************
- * StorageImpl_ReadRawDirEntry
- *
- * This method will read the raw data from a directory entry in the file.
- *
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
- */
-HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
-{
- ULARGE_INTEGER offset;
- HRESULT hr;
- ULONG bytesRead;
+ neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
- offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
+ ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
- hr = BlockChainStream_ReadAt(
- This->rootBlockChain,
- offset,
- RAW_DIRENTRY_SIZE,
- buffer,
- &bytesRead);
+ if (neededSize.QuadPart > statstg.cbSize.QuadPart)
+ ILockBytes_SetSize(This->lockBytes, neededSize);
- if (bytesRead != RAW_DIRENTRY_SIZE)
- return STG_E_READFAULT;
+ This->prevFreeBlock = freeBlock;
- return hr;
+ return freeBlock;
}
/******************************************************************************
- * StorageImpl_WriteRawDirEntry
- *
- * This method will write the raw data from a directory entry in the file.
+ * StorageImpl_FreeBigBlock
*
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ * This method will flag the specified block as free in the big block depot.
*/
-HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
+static void StorageImpl_FreeBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex)
{
- ULARGE_INTEGER offset;
- ULONG bytesRead;
-
- offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
+ StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
- return BlockChainStream_WriteAt(
- This->rootBlockChain,
- offset,
- RAW_DIRENTRY_SIZE,
- buffer,
- &bytesRead);
+ if (blockIndex < This->prevFreeBlock)
+ This->prevFreeBlock = blockIndex;
}
-/******************************************************************************
- * UpdateRawDirEntry
- *
- * Update raw directory entry data from the fields in newData.
- *
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
- */
-void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
-{
- memset(buffer, 0, RAW_DIRENTRY_SIZE);
- memcpy(
- buffer + OFFSET_PS_NAME,
- newData->name,
- DIRENTRY_NAME_BUFFER_LEN );
+static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
+ DirRef index, const DirEntry *data)
+{
+ StorageImpl *This = (StorageImpl*)base;
+ return StorageImpl_WriteDirEntry(This, index, data);
+}
- memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
+static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
+ DirRef index, DirEntry *data)
+{
+ StorageImpl *This = (StorageImpl*)base;
+ return StorageImpl_ReadDirEntry(This, index, data);
+}
- StorageUtl_WriteWord(
- buffer,
- OFFSET_PS_NAMELENGTH,
- newData->sizeOfNameString);
+static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
+{
+ int i;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_LEFTCHILD,
- newData->leftChild);
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ {
+ if (!This->blockChainCache[i])
+ {
+ return &This->blockChainCache[i];
+ }
+ }
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_RIGHTCHILD,
- newData->rightChild);
+ i = This->blockChainToEvict;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_DIRROOT,
- newData->dirRootEntry);
+ BlockChainStream_Destroy(This->blockChainCache[i]);
+ This->blockChainCache[i] = NULL;
- StorageUtl_WriteGUID(
- buffer,
- OFFSET_PS_GUID,
- &newData->clsid);
+ This->blockChainToEvict++;
+ if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
+ This->blockChainToEvict = 0;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_CTIMELOW,
- newData->ctime.dwLowDateTime);
+ return &This->blockChainCache[i];
+}
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_CTIMEHIGH,
- newData->ctime.dwHighDateTime);
+static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
+ DirRef index)
+{
+ int i, free_index=-1;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_MTIMELOW,
- newData->mtime.dwLowDateTime);
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ {
+ if (!This->blockChainCache[i])
+ {
+ if (free_index == -1) free_index = i;
+ }
+ else if (This->blockChainCache[i]->ownerDirEntry == index)
+ {
+ return &This->blockChainCache[i];
+ }
+ }
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_MTIMEHIGH,
- newData->ctime.dwHighDateTime);
+ if (free_index == -1)
+ {
+ free_index = This->blockChainToEvict;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_STARTBLOCK,
- newData->startingBlock);
+ BlockChainStream_Destroy(This->blockChainCache[free_index]);
+ This->blockChainCache[free_index] = NULL;
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_SIZE,
- newData->size.u.LowPart);
+ This->blockChainToEvict++;
+ if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
+ This->blockChainToEvict = 0;
+ }
- StorageUtl_WriteDWord(
- buffer,
- OFFSET_PS_SIZE_HIGH,
- newData->size.u.HighPart);
+ This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
+ return &This->blockChainCache[free_index];
}
-/******************************************************************************
- * StorageImpl_ReadDirEntry
- *
- * This method will read the specified directory entry.
- */
-HRESULT StorageImpl_ReadDirEntry(
- StorageImpl* This,
- DirRef index,
- DirEntry* buffer)
+static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
{
- BYTE currentEntry[RAW_DIRENTRY_SIZE];
- HRESULT readRes;
-
- readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
+ int i;
- if (SUCCEEDED(readRes))
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
{
- memset(buffer->name, 0, sizeof(buffer->name));
- memcpy(
- buffer->name,
- (WCHAR *)currentEntry+OFFSET_PS_NAME,
- DIRENTRY_NAME_BUFFER_LEN );
- TRACE("storage name: %s\n", debugstr_w(buffer->name));
-
- memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
+ if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
+ {
+ BlockChainStream_Destroy(This->blockChainCache[i]);
+ This->blockChainCache[i] = NULL;
+ return;
+ }
+ }
+}
- StorageUtl_ReadWord(
- currentEntry,
- OFFSET_PS_NAMELENGTH,
- &buffer->sizeOfNameString);
+static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
+ ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+ StorageImpl *This = (StorageImpl*)base;
+ DirEntry data;
+ HRESULT hr;
+ ULONG bytesToRead;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_LEFTCHILD,
- &buffer->leftChild);
+ hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (FAILED(hr)) return hr;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_RIGHTCHILD,
- &buffer->rightChild);
+ if (data.size.QuadPart == 0)
+ {
+ *bytesRead = 0;
+ return S_OK;
+ }
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_DIRROOT,
- &buffer->dirRootEntry);
+ if (offset.QuadPart + size > data.size.QuadPart)
+ {
+ bytesToRead = data.size.QuadPart - offset.QuadPart;
+ }
+ else
+ {
+ bytesToRead = size;
+ }
- StorageUtl_ReadGUID(
- currentEntry,
- OFFSET_PS_GUID,
- &buffer->clsid);
+ if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ SmallBlockChainStream *stream;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_CTIMELOW,
- &buffer->ctime.dwLowDateTime);
+ stream = SmallBlockChainStream_Construct(This, NULL, index);
+ if (!stream) return E_OUTOFMEMORY;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_CTIMEHIGH,
- &buffer->ctime.dwHighDateTime);
+ hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_MTIMELOW,
- &buffer->mtime.dwLowDateTime);
+ SmallBlockChainStream_Destroy(stream);
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_MTIMEHIGH,
- &buffer->mtime.dwHighDateTime);
+ return hr;
+ }
+ else
+ {
+ BlockChainStream *stream = NULL;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_STARTBLOCK,
- &buffer->startingBlock);
+ stream = *StorageImpl_GetCachedBlockChainStream(This, index);
+ if (!stream) return E_OUTOFMEMORY;
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_SIZE,
- &buffer->size.u.LowPart);
+ hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
- StorageUtl_ReadDWord(
- currentEntry,
- OFFSET_PS_SIZE_HIGH,
- &buffer->size.u.HighPart);
+ return hr;
}
-
- return readRes;
}
-/*********************************************************************
- * Write the specified directory entry to the file
- */
-HRESULT StorageImpl_WriteDirEntry(
- StorageImpl* This,
- DirRef index,
- const DirEntry* buffer)
+static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
+ ULARGE_INTEGER newsize)
{
- BYTE currentEntry[RAW_DIRENTRY_SIZE];
+ StorageImpl *This = (StorageImpl*)base;
+ DirEntry data;
+ HRESULT hr;
+ SmallBlockChainStream *smallblock=NULL;
+ BlockChainStream **pbigblock=NULL, *bigblock=NULL;
- UpdateRawDirEntry(currentEntry, buffer);
+ hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (FAILED(hr)) return hr;
- return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
-}
+ /* In simple mode keep the stream size above the small block limit */
+ if (This->base.openFlags & STGM_SIMPLE)
+ newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
-static HRESULT StorageImpl_ReadBigBlock(
- StorageImpl* This,
- ULONG blockIndex,
- void* buffer,
- ULONG* out_read)
-{
- ULARGE_INTEGER ulOffset;
- DWORD read=0;
- HRESULT hr;
+ if (data.size.QuadPart == newsize.QuadPart)
+ return S_OK;
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+ /* Create a block chain object of the appropriate type */
+ if (data.size.QuadPart == 0)
+ {
+ if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ smallblock = SmallBlockChainStream_Construct(This, NULL, index);
+ if (!smallblock) return E_OUTOFMEMORY;
+ }
+ else
+ {
+ pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
+ bigblock = *pbigblock;
+ if (!bigblock) return E_OUTOFMEMORY;
+ }
+ }
+ else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ smallblock = SmallBlockChainStream_Construct(This, NULL, index);
+ if (!smallblock) return E_OUTOFMEMORY;
+ }
+ else
+ {
+ pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
+ bigblock = *pbigblock;
+ if (!bigblock) return E_OUTOFMEMORY;
+ }
- hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
+ /* Change the block chain type if necessary. */
+ if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
+ if (!bigblock)
+ {
+ SmallBlockChainStream_Destroy(smallblock);
+ return E_FAIL;
+ }
- if (SUCCEEDED(hr) && read < This->bigBlockSize)
+ pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
+ *pbigblock = bigblock;
+ }
+ else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
{
- /* File ends during this block; fill the rest with 0's. */
- memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
+ smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
+ if (!smallblock)
+ return E_FAIL;
}
- if (out_read) *out_read = read;
+ /* Set the size of the block chain. */
+ if (smallblock)
+ {
+ SmallBlockChainStream_SetSize(smallblock, newsize);
+ SmallBlockChainStream_Destroy(smallblock);
+ }
+ else
+ {
+ BlockChainStream_SetSize(bigblock, newsize);
+ }
+ /* Set the size in the directory entry. */
+ hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (SUCCEEDED(hr))
+ {
+ data.size = newsize;
+
+ hr = StorageImpl_WriteDirEntry(This, index, &data);
+ }
return hr;
}
-static BOOL StorageImpl_ReadDWordFromBigBlock(
- StorageImpl* This,
- ULONG blockIndex,
- ULONG offset,
- DWORD* value)
+static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
+ ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
- ULARGE_INTEGER ulOffset;
- DWORD read;
- DWORD tmp;
+ StorageImpl *This = (StorageImpl*)base;
+ DirEntry data;
+ HRESULT hr;
+ ULARGE_INTEGER newSize;
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
- ulOffset.QuadPart += offset;
+ hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (FAILED(hr)) return hr;
- StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
- *value = lendian32toh(tmp);
- return (read == sizeof(DWORD));
-}
+ /* Grow the stream if necessary */
+ newSize.QuadPart = offset.QuadPart + size;
-static BOOL StorageImpl_WriteBigBlock(
- StorageImpl* This,
- ULONG blockIndex,
- const void* buffer)
-{
- ULARGE_INTEGER ulOffset;
- DWORD wrote;
+ if (newSize.QuadPart > data.size.QuadPart)
+ {
+ hr = StorageImpl_StreamSetSize(base, index, newSize);
+ if (FAILED(hr))
+ return hr;
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+ hr = StorageImpl_ReadDirEntry(This, index, &data);
+ if (FAILED(hr)) return hr;
+ }
- StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
- return (wrote == This->bigBlockSize);
-}
+ if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+ {
+ SmallBlockChainStream *stream;
-static BOOL StorageImpl_WriteDWordToBigBlock(
- StorageImpl* This,
- ULONG blockIndex,
- ULONG offset,
- DWORD value)
-{
- ULARGE_INTEGER ulOffset;
- DWORD wrote;
+ stream = SmallBlockChainStream_Construct(This, NULL, index);
+ if (!stream) return E_OUTOFMEMORY;
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
- ulOffset.QuadPart += offset;
+ hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
- value = htole32(value);
- StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
- return (wrote == sizeof(DWORD));
+ SmallBlockChainStream_Destroy(stream);
+
+ return hr;
+ }
+ else
+ {
+ BlockChainStream *stream;
+
+ stream = *StorageImpl_GetCachedBlockChainStream(This, index);
+ if (!stream) return E_OUTOFMEMORY;
+
+ return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
+ }
}
-/******************************************************************************
- * Storage32Impl_SmallBlocksToBigBlocks
- *
- * This method will convert a small block chain to a big block chain.
- * The small block chain will be destroyed.
- */
-BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
- StorageImpl* This,
- SmallBlockChainStream** ppsbChain)
+static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
+ DirRef src)
{
- ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
- ULARGE_INTEGER size, offset;
- ULONG cbRead, cbWritten;
- ULARGE_INTEGER cbTotalRead;
- DirRef streamEntryRef;
- HRESULT resWrite = S_OK;
- HRESULT resRead;
- DirEntry streamEntry;
- BYTE *buffer;
- BlockChainStream *bbTempChain = NULL;
- BlockChainStream *bigBlockChain = NULL;
+ StorageImpl *This = (StorageImpl*)base;
+ DirEntry dst_data, src_data;
+ HRESULT hr;
- /*
- * Create a temporary big block chain that doesn't have
- * an associated directory entry. This temporary chain will be
- * used to copy data from small blocks to big blocks.
- */
- bbTempChain = BlockChainStream_Construct(This,
- &bbHeadOfChain,
- DIRENTRY_NULL);
- if(!bbTempChain) return NULL;
- /*
- * Grow the big block chain.
- */
- size = SmallBlockChainStream_GetSize(*ppsbChain);
- BlockChainStream_SetSize(bbTempChain, size);
+ hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
- /*
- * Copy the contents of the small block chain to the big block chain
- * by small block size increments.
- */
- offset.u.LowPart = 0;
- offset.u.HighPart = 0;
- cbTotalRead.QuadPart = 0;
+ if (SUCCEEDED(hr))
+ hr = StorageImpl_ReadDirEntry(This, src, &src_data);
- buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
- do
+ if (SUCCEEDED(hr))
{
- resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
- offset,
- min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
- buffer,
- &cbRead);
- if (FAILED(resRead))
- break;
-
- if (cbRead > 0)
- {
- cbTotalRead.QuadPart += cbRead;
-
- resWrite = BlockChainStream_WriteAt(bbTempChain,
- offset,
- cbRead,
- buffer,
- &cbWritten);
+ StorageImpl_DeleteCachedBlockChainStream(This, src);
+ dst_data.startingBlock = src_data.startingBlock;
+ dst_data.size = src_data.size;
- if (FAILED(resWrite))
- break;
+ hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
+ }
- offset.u.LowPart += cbRead;
- }
- else
- {
- resRead = STG_E_READFAULT;
- break;
- }
- } while (cbTotalRead.QuadPart < size.QuadPart);
- HeapFree(GetProcessHeap(),0,buffer);
+ return hr;
+}
- size.u.HighPart = 0;
- size.u.LowPart = 0;
+static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
+{
+ HRESULT hr=S_OK;
+ DirEntry currentEntry;
+ DirRef currentEntryRef;
+ BlockChainStream *blockChainStream;
- if (FAILED(resRead) || FAILED(resWrite))
+ if (create)
{
- ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
- BlockChainStream_SetSize(bbTempChain, size);
- BlockChainStream_Destroy(bbTempChain);
- return NULL;
- }
+ ULARGE_INTEGER size;
+ BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
- /*
- * Destroy the small block chain.
- */
- streamEntryRef = (*ppsbChain)->ownerDirEntry;
- SmallBlockChainStream_SetSize(*ppsbChain, size);
- SmallBlockChainStream_Destroy(*ppsbChain);
- *ppsbChain = 0;
-
- /*
- * Change the directory entry. This chain is now a big block chain
- * and it doesn't reside in the small blocks chain anymore.
- */
- StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
-
- streamEntry.startingBlock = bbHeadOfChain;
-
- StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
-
- /*
- * Destroy the temporary entryless big block chain.
- * Create a new big block chain associated with this entry.
- */
- BlockChainStream_Destroy(bbTempChain);
- bigBlockChain = BlockChainStream_Construct(This,
- NULL,
- streamEntryRef);
-
- return bigBlockChain;
-}
+ /* Discard any existing data. */
+ size.QuadPart = 0;
+ ILockBytes_SetSize(This->lockBytes, size);
-/******************************************************************************
- * Storage32Impl_BigBlocksToSmallBlocks
- *
- * This method will convert a big block chain to a small block chain.
- * The big block chain will be destroyed on success.
- */
-SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
- StorageImpl* This,
- BlockChainStream** ppbbChain,
- ULARGE_INTEGER newSize)
-{
- ULARGE_INTEGER size, offset, cbTotalRead;
- ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
- DirRef streamEntryRef;
- HRESULT resWrite = S_OK, resRead = S_OK;
- DirEntry streamEntry;
- BYTE* buffer;
- SmallBlockChainStream* sbTempChain;
+ /*
+ * Initialize all header variables:
+ * - The big block depot consists of one block and it is at block 0
+ * - The directory table starts at block 1
+ * - There is no small block depot
+ */
+ memset( This->bigBlockDepotStart,
+ BLOCK_UNUSED,
+ sizeof(This->bigBlockDepotStart));
- TRACE("%p %p\n", This, ppbbChain);
+ This->bigBlockDepotCount = 1;
+ This->bigBlockDepotStart[0] = 0;
+ This->rootStartBlock = 1;
+ This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
+ This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
+ if (This->bigBlockSize == 4096)
+ This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
+ else
+ This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
+ This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
+ This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
+ This->extBigBlockDepotCount = 0;
- sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
- DIRENTRY_NULL);
+ StorageImpl_SaveFileHeader(This);
- if(!sbTempChain)
- return NULL;
+ /*
+ * Add one block for the big block depot and one block for the directory table
+ */
+ size.u.HighPart = 0;
+ size.u.LowPart = This->bigBlockSize * 3;
+ ILockBytes_SetSize(This->lockBytes, size);
- SmallBlockChainStream_SetSize(sbTempChain, newSize);
- size = BlockChainStream_GetSize(*ppbbChain);
- size.QuadPart = min(size.QuadPart, newSize.QuadPart);
+ /*
+ * Initialize the big block depot
+ */
+ memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+ StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
+ StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
+ StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
+ }
+ else
+ {
+ /*
+ * Load the header for the file.
+ */
+ hr = StorageImpl_LoadFileHeader(This);
- offset.u.HighPart = 0;
- offset.u.LowPart = 0;
- cbTotalRead.QuadPart = 0;
- buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
- while(cbTotalRead.QuadPart < size.QuadPart)
+ if (FAILED(hr))
{
- resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
- min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
- buffer, &cbRead);
+ return hr;
+ }
+ }
- if(FAILED(resRead))
- break;
+ /*
+ * There is no block depot cached yet.
+ */
+ This->indexBlockDepotCached = 0xFFFFFFFF;
+ This->indexExtBlockDepotCached = 0xFFFFFFFF;
- if(cbRead > 0)
- {
- cbTotalRead.QuadPart += cbRead;
+ /*
+ * Start searching for free blocks with block 0.
+ */
+ This->prevFreeBlock = 0;
- resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
- cbRead, buffer, &cbWritten);
+ This->firstFreeSmallBlock = 0;
- if(FAILED(resWrite))
- break;
+ /* Read the extended big block depot locations. */
+ if (This->extBigBlockDepotCount != 0)
+ {
+ ULONG current_block = This->extBigBlockDepotStart;
+ ULONG cache_size = This->extBigBlockDepotCount * 2;
+ ULONG i;
- offset.u.LowPart += cbRead;
- }
- else
- {
- resRead = STG_E_READFAULT;
- break;
- }
+ This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
+ if (!This->extBigBlockDepotLocations)
+ {
+ return E_OUTOFMEMORY;
}
- HeapFree(GetProcessHeap(), 0, buffer);
- size.u.HighPart = 0;
- size.u.LowPart = 0;
+ This->extBigBlockDepotLocationsSize = cache_size;
- if(FAILED(resRead) || FAILED(resWrite))
+ for (i=0; i<This->extBigBlockDepotCount; i++)
{
- ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
- SmallBlockChainStream_SetSize(sbTempChain, size);
- SmallBlockChainStream_Destroy(sbTempChain);
- return NULL;
+ if (current_block == BLOCK_END_OF_CHAIN)
+ {
+ WARN("File has too few extended big block depot blocks.\n");
+ return STG_E_DOCFILECORRUPT;
+ }
+ This->extBigBlockDepotLocations[i] = current_block;
+ current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
}
+ }
+ else
+ {
+ This->extBigBlockDepotLocations = NULL;
+ This->extBigBlockDepotLocationsSize = 0;
+ }
- /* destroy the original big block chain */
- streamEntryRef = (*ppbbChain)->ownerDirEntry;
- BlockChainStream_SetSize(*ppbbChain, size);
- BlockChainStream_Destroy(*ppbbChain);
- *ppbbChain = NULL;
+ /*
+ * Create the block chain abstractions.
+ */
+ if(!(blockChainStream =
+ BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
+ {
+ return STG_E_READFAULT;
+ }
+ if (!new_object)
+ BlockChainStream_Destroy(This->rootBlockChain);
+ This->rootBlockChain = blockChainStream;
- StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
- streamEntry.startingBlock = sbHeadOfChain;
- StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
+ if(!(blockChainStream =
+ BlockChainStream_Construct(This, &This->smallBlockDepotStart,
+ DIRENTRY_NULL)))
+ {
+ return STG_E_READFAULT;
+ }
+ if (!new_object)
+ BlockChainStream_Destroy(This->smallBlockDepotChain);
+ This->smallBlockDepotChain = blockChainStream;
- SmallBlockChainStream_Destroy(sbTempChain);
- return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
-}
+ /*
+ * Write the root storage entry (memory only)
+ */
+ if (create)
+ {
+ static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
+ DirEntry rootEntry;
+ /*
+ * Initialize the directory table
+ */
+ memset(&rootEntry, 0, sizeof(rootEntry));
+ strcpyW(rootEntry.name, rootentryW);
+ rootEntry.sizeOfNameString = sizeof(rootentryW);
+ rootEntry.stgType = STGTY_ROOT;
+ rootEntry.leftChild = DIRENTRY_NULL;
+ rootEntry.rightChild = DIRENTRY_NULL;
+ rootEntry.dirRootEntry = DIRENTRY_NULL;
+ rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
+ rootEntry.size.u.HighPart = 0;
+ rootEntry.size.u.LowPart = 0;
-static HRESULT StorageBaseImpl_CopyStream(
- StorageBaseImpl *dst, DirRef dst_entry,
- StorageBaseImpl *src, DirRef src_entry)
-{
- HRESULT hr;
- BYTE data[4096];
- DirEntry srcdata;
- ULARGE_INTEGER bytes_copied;
- ULONG bytestocopy, bytesread, byteswritten;
+ StorageImpl_WriteDirEntry(This, 0, &rootEntry);
+ }
- hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
+ /*
+ * Find the ID of the root storage.
+ */
+ currentEntryRef = 0;
- if (SUCCEEDED(hr))
+ do
{
- hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
+ hr = StorageImpl_ReadDirEntry(
+ This,
+ currentEntryRef,
+ ¤tEntry);
- bytes_copied.QuadPart = 0;
- while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
+ if (SUCCEEDED(hr))
{
- bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
-
- hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
- data, &bytesread);
- if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
- data, &byteswritten);
- if (SUCCEEDED(hr))
+ if ( (currentEntry.sizeOfNameString != 0 ) &&
+ (currentEntry.stgType == STGTY_ROOT) )
{
- if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
- bytes_copied.QuadPart += byteswritten;
+ This->base.storageDirEntry = currentEntryRef;
}
}
- }
- return hr;
-}
+ currentEntryRef++;
-static HRESULT StorageBaseImpl_DupStorageTree(
- StorageBaseImpl *dst, DirRef *dst_entry,
- StorageBaseImpl *src, DirRef src_entry)
-{
- HRESULT hr;
- DirEntry data;
- BOOL has_stream=FALSE;
+ } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
- if (src_entry == DIRENTRY_NULL)
+ if (FAILED(hr))
{
- *dst_entry = DIRENTRY_NULL;
- return S_OK;
+ return STG_E_READFAULT;
}
- hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
- if (SUCCEEDED(hr))
+ /*
+ * Create the block chain abstraction for the small block root chain.
+ */
+ if(!(blockChainStream =
+ BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
{
- has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
- data.startingBlock = BLOCK_END_OF_CHAIN;
- data.size.QuadPart = 0;
-
- hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
+ return STG_E_READFAULT;
}
+ if (!new_object)
+ BlockChainStream_Destroy(This->smallBlockRootChain);
+ This->smallBlockRootChain = blockChainStream;
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
-
- if (SUCCEEDED(hr) && has_stream)
- hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
+ if (!new_object)
+ {
+ int i;
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ {
+ BlockChainStream_Destroy(This->blockChainCache[i]);
+ This->blockChainCache[i] = NULL;
+ }
+ }
return hr;
}
-static HRESULT StorageBaseImpl_CopyStorageTree(
- StorageBaseImpl *dst, DirRef dst_entry,
- StorageBaseImpl *src, DirRef src_entry)
+static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
+ ULONG* result, BOOL refresh)
{
- HRESULT hr;
- DirEntry src_data, dst_data;
- DirRef new_root_entry;
-
- hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
+ StorageImpl *This = (StorageImpl*)base;
+ HRESULT hr=S_OK;
+ DWORD oldTransactionSig = This->transactionSig;
- if (SUCCEEDED(hr))
+ if (refresh)
{
- hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
- }
+ ULARGE_INTEGER offset;
+ ULONG bytes_read;
+ BYTE data[4];
- if (SUCCEEDED(hr))
- {
- hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
- dst_data.clsid = src_data.clsid;
- dst_data.ctime = src_data.ctime;
- dst_data.mtime = src_data.mtime;
- dst_data.dirRootEntry = new_root_entry;
+ offset.u.HighPart = 0;
+ offset.u.LowPart = OFFSET_TRANSACTIONSIG;
+ hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
+
+ if (SUCCEEDED(hr))
+ {
+ StorageUtl_ReadDWord(data, 0, &This->transactionSig);
+
+ if (oldTransactionSig != This->transactionSig)
+ {
+ /* Someone else wrote to this, so toss all cached information. */
+ TRACE("signature changed\n");
+
+ hr = StorageImpl_Refresh(This, FALSE, FALSE);
+ }
+
+ if (FAILED(hr))
+ This->transactionSig = oldTransactionSig;
+ }
}
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
+ *result = This->transactionSig;
return hr;
}
-static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
+static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
+ ULONG value)
{
- HRESULT hr;
- DirEntry data;
- ULARGE_INTEGER zero;
-
- if (entry == DIRENTRY_NULL)
- return S_OK;
+ StorageImpl *This = (StorageImpl*)base;
- zero.QuadPart = 0;
+ This->transactionSig = value;
+ StorageImpl_SaveFileHeader(This);
- hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
+ return S_OK;
+}
- if (SUCCEEDED(hr) && include_siblings)
- hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
+/* Internal function */
+static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
+ ULARGE_INTEGER cb, DWORD dwLockType)
+{
+ HRESULT hr;
+ int delay = 0;
+ DWORD start_time = GetTickCount();
+ DWORD last_sanity_check = start_time;
+ ULARGE_INTEGER sanity_offset, sanity_cb;
- if (SUCCEEDED(hr) && include_siblings)
- hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
+ sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
+ sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
+ do
+ {
+ hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
- if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
- hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
+ if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+ {
+ DWORD current_time = GetTickCount();
+ if (current_time - start_time >= 20000)
+ {
+ /* timeout */
+ break;
+ }
+ if (current_time - last_sanity_check >= 500)
+ {
+ /* Any storage implementation with the file open in a
+ * shared mode should not lock these bytes for writing. However,
+ * some programs (LibreOffice Writer) will keep ALL bytes locked
+ * when opening in exclusive mode. We can use a read lock to
+ * detect this case early, and not hang a full 20 seconds.
+ *
+ * This can collide with another attempt to open the file in
+ * exclusive mode, but it's unlikely, and someone would fail anyway. */
+ hr = ILockBytes_LockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
+ if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+ break;
+ if (hr == STG_E_INVALIDFUNCTION)
+ {
+ /* ignore this, lockbytes might support dwLockType but not 0 */
+ hr = STG_E_ACCESSDENIED;
+ }
+ if (SUCCEEDED(hr))
+ {
+ ILockBytes_UnlockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
+ hr = STG_E_ACCESSDENIED;
+ }
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_DestroyDirEntry(This, entry);
+ last_sanity_check = current_time;
+ }
+ Sleep(delay);
+ if (delay < 150) delay++;
+ }
+ } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
- return hr;
+ return hr;
}
-static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
+static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
{
- DirRef result=This->firstFreeEntry;
-
- while (result < This->entries_size && This->entries[result].inuse)
- result++;
+ StorageImpl *This = (StorageImpl*)base;
+ HRESULT hr;
+ ULARGE_INTEGER offset, cb;
- if (result == This->entries_size)
+ if (write)
{
- ULONG new_size = This->entries_size * 2;
- TransactedDirEntry *new_entries;
-
- new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
- if (!new_entries) return DIRENTRY_NULL;
-
- memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
- HeapFree(GetProcessHeap(), 0, This->entries);
-
- This->entries = new_entries;
- This->entries_size = new_size;
+ /* Synchronous grab of second priority range, the commit lock, and the
+ * lock-checking lock. */
+ offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
+ cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+ }
+ else
+ {
+ offset.QuadPart = RANGELOCK_COMMIT;
+ cb.QuadPart = 1;
}
- This->entries[result].inuse = TRUE;
+ hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
- This->firstFreeEntry = result+1;
+ if (hr == STG_E_INVALIDFUNCTION)
+ hr = S_OK;
- return result;
+ return hr;
}
-static DirRef TransactedSnapshotImpl_CreateStubEntry(
- TransactedSnapshotImpl *This, DirRef parentEntryRef)
+static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
{
- DirRef stubEntryRef;
- TransactedDirEntry *entry;
-
- stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
+ StorageImpl *This = (StorageImpl*)base;
+ HRESULT hr;
+ ULARGE_INTEGER offset, cb;
- if (stubEntryRef != DIRENTRY_NULL)
+ if (write)
{
- entry = &This->entries[stubEntryRef];
+ offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
+ cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+ }
+ else
+ {
+ offset.QuadPart = RANGELOCK_COMMIT;
+ cb.QuadPart = 1;
+ }
- entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
+ hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
- entry->read = FALSE;
- }
+ if (hr == STG_E_INVALIDFUNCTION)
+ hr = S_OK;
- return stubEntryRef;
+ return hr;
}
-static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
- TransactedSnapshotImpl *This, DirRef entry)
+static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
- HRESULT hr=S_OK;
- DirEntry data;
+ StorageImpl *This = (StorageImpl*) iface;
+ STATSTG statstg;
+ HRESULT hr;
- if (!This->entries[entry].read)
- {
- hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
- This->entries[entry].transactedParentEntry,
- &data);
+ hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
- if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
- {
- data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
+ *result = statstg.pwcsName;
- if (data.leftChild == DIRENTRY_NULL)
- hr = E_OUTOFMEMORY;
- }
+ return hr;
+}
- if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
- {
- data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
+static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
+ ULONG end, HRESULT fail_hr)
+{
+ HRESULT hr;
+ ULARGE_INTEGER offset, cb;
- if (data.rightChild == DIRENTRY_NULL)
- hr = E_OUTOFMEMORY;
- }
+ offset.QuadPart = start;
+ cb.QuadPart = 1 + end - start;
- if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
- {
- data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
+ hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
- if (data.dirRootEntry == DIRENTRY_NULL)
- hr = E_OUTOFMEMORY;
+ if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+ return fail_hr;
+ else
+ return S_OK;
+}
+
+static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
+{
+ HRESULT hr=S_OK;
+ int i, j;
+ ULARGE_INTEGER offset, cb;
+
+ cb.QuadPart = 1;
+
+ for (i=start; i<=end; i++)
+ {
+ offset.QuadPart = i;
+ hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
+ break;
}
if (SUCCEEDED(hr))
{
- memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
- This->entries[entry].read = TRUE;
+ for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
+ {
+ if (This->locked_bytes[j] == 0)
+ {
+ This->locked_bytes[j] = i;
+ break;
+ }
+ }
}
- }
- return hr;
+ return hr;
}
-static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
- TransactedSnapshotImpl *This, DirRef entry)
+static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
{
- HRESULT hr = S_OK;
-
- if (!This->entries[entry].stream_dirty)
- {
- DirEntry new_entrydata;
-
- memset(&new_entrydata, 0, sizeof(DirEntry));
- new_entrydata.name[0] = 'S';
- new_entrydata.sizeOfNameString = 1;
- new_entrydata.stgType = STGTY_STREAM;
- new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
- new_entrydata.leftChild = DIRENTRY_NULL;
- new_entrydata.rightChild = DIRENTRY_NULL;
- new_entrydata.dirRootEntry = DIRENTRY_NULL;
-
- hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
- &This->entries[entry].stream_entry);
+ HRESULT hr;
+ ULARGE_INTEGER offset;
+ ULARGE_INTEGER cb;
+ DWORD share_mode = STGM_SHARE_MODE(openFlags);
- if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+ if (openFlags & STGM_NOSNAPSHOT)
{
- hr = StorageBaseImpl_CopyStream(
- This->scratch, This->entries[entry].stream_entry,
- This->transactedParent, This->entries[entry].transactedParentEntry);
-
- if (FAILED(hr))
- StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
+ /* STGM_NOSNAPSHOT implies deny write */
+ if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
+ else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
}
- if (SUCCEEDED(hr))
- This->entries[entry].stream_dirty = TRUE;
+ /* Wrap all other locking inside a single lock so we can check ranges safely */
+ offset.QuadPart = RANGELOCK_CHECKLOCKS;
+ cb.QuadPart = 1;
+ hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
- if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
- {
- /* Since this entry is modified, and we aren't using its stream data, we
- * no longer care about the original entry. */
- DirRef delete_ref;
- delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
+ /* If the ILockBytes doesn't support locking that's ok. */
+ if (hr == STG_E_INVALIDFUNCTION) return S_OK;
+ else if (FAILED(hr)) return hr;
- if (delete_ref != DIRENTRY_NULL)
- This->entries[delete_ref].deleted = TRUE;
+ hr = S_OK;
- This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
- }
- }
+ /* First check for any conflicting locks. */
+ if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
+ hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
- return hr;
-}
+ if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+ hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
-/* Find the first entry in a depth-first traversal. */
-static DirRef TransactedSnapshotImpl_FindFirstChild(
- TransactedSnapshotImpl* This, DirRef parent)
-{
- DirRef cursor, prev;
- TransactedDirEntry *entry;
+ if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+ hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
- cursor = parent;
- entry = &This->entries[cursor];
- while (entry->read)
- {
- if (entry->data.leftChild != DIRENTRY_NULL)
- {
- prev = cursor;
- cursor = entry->data.leftChild;
- entry = &This->entries[cursor];
- entry->parent = prev;
- }
- else if (entry->data.rightChild != DIRENTRY_NULL)
- {
- prev = cursor;
- cursor = entry->data.rightChild;
- entry = &This->entries[cursor];
- entry->parent = prev;
- }
- else if (entry->data.dirRootEntry != DIRENTRY_NULL)
+ if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+ hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
+
+ if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+ hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
+
+ /* Then grab our locks. */
+ if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
{
- prev = cursor;
- cursor = entry->data.dirRootEntry;
- entry = &This->entries[cursor];
- entry->parent = prev;
+ hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
+ if (SUCCEEDED(hr))
+ hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
}
- else
- break;
- }
- return cursor;
-}
+ if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+ hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
-/* Find the next entry in a depth-first traversal. */
-static DirRef TransactedSnapshotImpl_FindNextChild(
- TransactedSnapshotImpl* This, DirRef current)
-{
- DirRef parent;
- TransactedDirEntry *parent_entry;
+ if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+ hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
- parent = This->entries[current].parent;
- parent_entry = &This->entries[parent];
+ if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+ hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
- if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
- {
- if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
- {
- This->entries[parent_entry->data.rightChild].parent = parent;
- return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
- }
+ if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+ hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
- if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
- {
- This->entries[parent_entry->data.dirRootEntry].parent = parent;
- return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
- }
- }
+ if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
+ hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
- return parent;
-}
+ offset.QuadPart = RANGELOCK_CHECKLOCKS;
+ cb.QuadPart = 1;
+ ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
-/* Return TRUE if we've made a copy of this entry for committing to the parent. */
-static inline BOOL TransactedSnapshotImpl_MadeCopy(
- TransactedSnapshotImpl* This, DirRef entry)
-{
- return entry != DIRENTRY_NULL &&
- This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
+ return hr;
}
-/* Destroy the entries created by CopyTree. */
-static void TransactedSnapshotImpl_DestroyTemporaryCopy(
- TransactedSnapshotImpl* This, DirRef stop)
+static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
{
- DirRef cursor;
- TransactedDirEntry *entry;
- ULARGE_INTEGER zero;
+ StorageImpl *This = (StorageImpl*)storage;
+ int i;
+ HRESULT hr;
+ TRACE("(%p)\n", This);
- zero.QuadPart = 0;
+ hr = BlockChainStream_Flush(This->smallBlockRootChain);
- if (!This->entries[This->base.storageDirEntry].read)
- return;
+ if (SUCCEEDED(hr))
+ hr = BlockChainStream_Flush(This->rootBlockChain);
- cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
+ if (SUCCEEDED(hr))
+ hr = BlockChainStream_Flush(This->smallBlockDepotChain);
- if (cursor == DIRENTRY_NULL)
- return;
+ for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
+ if (This->blockChainCache[i])
+ hr = BlockChainStream_Flush(This->blockChainCache[i]);
- cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
+ if (SUCCEEDED(hr))
+ hr = ILockBytes_Flush(This->lockBytes);
- while (cursor != DIRENTRY_NULL && cursor != stop)
- {
- if (TransactedSnapshotImpl_MadeCopy(This, cursor))
- {
- entry = &This->entries[cursor];
+ return hr;
+}
- if (entry->stream_dirty)
- StorageBaseImpl_StreamSetSize(This->transactedParent,
- entry->newTransactedParentEntry, zero);
-
- StorageBaseImpl_DestroyDirEntry(This->transactedParent,
- entry->newTransactedParentEntry);
+static void StorageImpl_Invalidate(StorageBaseImpl* iface)
+{
+ StorageImpl *This = (StorageImpl*) iface;
- entry->newTransactedParentEntry = entry->transactedParentEntry;
- }
+ StorageBaseImpl_DeleteAll(&This->base);
- cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
- }
+ This->base.reverted = TRUE;
}
-/* Make a copy of our edited tree that we can use in the parent. */
-static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
+static void StorageImpl_Destroy(StorageBaseImpl* iface)
{
- DirRef cursor;
- TransactedDirEntry *entry;
- HRESULT hr = S_OK;
+ StorageImpl *This = (StorageImpl*) iface;
+ int i;
+ TRACE("(%p)\n", This);
- cursor = This->base.storageDirEntry;
- entry = &This->entries[cursor];
- entry->parent = DIRENTRY_NULL;
- entry->newTransactedParentEntry = entry->transactedParentEntry;
+ StorageImpl_Flush(iface);
- if (entry->data.dirRootEntry == DIRENTRY_NULL)
- return S_OK;
+ StorageImpl_Invalidate(iface);
- This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
+ HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
- cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
- entry = &This->entries[cursor];
+ BlockChainStream_Destroy(This->smallBlockRootChain);
+ BlockChainStream_Destroy(This->rootBlockChain);
+ BlockChainStream_Destroy(This->smallBlockDepotChain);
- while (cursor != DIRENTRY_NULL)
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ BlockChainStream_Destroy(This->blockChainCache[i]);
+
+ for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
{
- /* Make a copy of this entry in the transacted parent. */
- if (!entry->read ||
- (!entry->dirty && !entry->stream_dirty &&
- !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
- !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
- !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
- entry->newTransactedParentEntry = entry->transactedParentEntry;
- else
+ ULARGE_INTEGER offset, cb;
+ cb.QuadPart = 1;
+ if (This->locked_bytes[i] != 0)
{
- DirEntry newData;
+ offset.QuadPart = This->locked_bytes[i];
+ ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+ }
+ }
- memcpy(&newData, &entry->data, sizeof(DirEntry));
+ if (This->lockBytes)
+ ILockBytes_Release(This->lockBytes);
+ HeapFree(GetProcessHeap(), 0, This);
+}
- newData.size.QuadPart = 0;
- newData.startingBlock = BLOCK_END_OF_CHAIN;
- if (newData.leftChild != DIRENTRY_NULL)
- newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
+static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
+{
+ StorageImpl_Destroy,
+ StorageImpl_Invalidate,
+ StorageImpl_Flush,
+ StorageImpl_GetFilename,
+ StorageImpl_CreateDirEntry,
+ StorageImpl_BaseWriteDirEntry,
+ StorageImpl_BaseReadDirEntry,
+ StorageImpl_DestroyDirEntry,
+ StorageImpl_StreamReadAt,
+ StorageImpl_StreamWriteAt,
+ StorageImpl_StreamSetSize,
+ StorageImpl_StreamLink,
+ StorageImpl_GetTransactionSig,
+ StorageImpl_SetTransactionSig,
+ StorageImpl_LockTransaction,
+ StorageImpl_UnlockTransaction
+};
- if (newData.rightChild != DIRENTRY_NULL)
- newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
- if (newData.dirRootEntry != DIRENTRY_NULL)
- newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
+/*
+ * Virtual function table for the IStorageBaseImpl class.
+ */
+static const IStorageVtbl StorageImpl_Vtbl =
+{
+ StorageBaseImpl_QueryInterface,
+ StorageBaseImpl_AddRef,
+ StorageBaseImpl_Release,
+ StorageBaseImpl_CreateStream,
+ StorageBaseImpl_OpenStream,
+ StorageBaseImpl_CreateStorage,
+ StorageBaseImpl_OpenStorage,
+ StorageBaseImpl_CopyTo,
+ StorageBaseImpl_MoveElementTo,
+ StorageBaseImpl_Commit,
+ StorageBaseImpl_Revert,
+ StorageBaseImpl_EnumElements,
+ StorageBaseImpl_DestroyElement,
+ StorageBaseImpl_RenameElement,
+ StorageBaseImpl_SetElementTimes,
+ StorageBaseImpl_SetClass,
+ StorageBaseImpl_SetStateBits,
+ StorageBaseImpl_Stat
+};
- hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
- &entry->newTransactedParentEntry);
- if (FAILED(hr))
- {
- TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
- return hr;
- }
+static HRESULT StorageImpl_Construct(
+ HANDLE hFile,
+ LPCOLESTR pwcsName,
+ ILockBytes* pLkbyt,
+ DWORD openFlags,
+ BOOL fileBased,
+ BOOL create,
+ ULONG sector_size,
+ StorageImpl** result)
+{
+ StorageImpl* This;
+ HRESULT hr = S_OK;
- if (entry->stream_dirty)
- {
- hr = StorageBaseImpl_CopyStream(
- This->transactedParent, entry->newTransactedParentEntry,
- This->scratch, entry->stream_entry);
- }
- else if (entry->data.size.QuadPart)
- {
- hr = StorageBaseImpl_StreamLink(
- This->transactedParent, entry->newTransactedParentEntry,
- entry->transactedParentEntry);
- }
+ if ( FAILED( validateSTGM(openFlags) ))
+ return STG_E_INVALIDFLAG;
- if (FAILED(hr))
- {
- cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
- TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
- return hr;
- }
- }
+ This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
+ if (!This)
+ return E_OUTOFMEMORY;
- cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
- entry = &This->entries[cursor];
- }
+ memset(This, 0, sizeof(StorageImpl));
- return hr;
-}
+ list_init(&This->base.strmHead);
-static HRESULT WINAPI TransactedSnapshotImpl_Commit(
- IStorage* iface,
- DWORD grfCommitFlags) /* [in] */
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
- TransactedDirEntry *root_entry;
- DirRef i, dir_root_ref;
- DirEntry data;
- ULARGE_INTEGER zero;
- HRESULT hr;
- ULONG transactionSig;
+ list_init(&This->base.storageHead);
- zero.QuadPart = 0;
+ This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
+ This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
+ This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
+ This->base.baseVtbl = &StorageImpl_BaseVtbl;
+ This->base.openFlags = (openFlags & ~STGM_CREATE);
+ This->base.ref = 1;
+ This->base.create = create;
- TRACE("(%p,%x)\n", iface, grfCommitFlags);
+ if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
+ This->base.lockingrole = SWMR_Writer;
+ else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
+ This->base.lockingrole = SWMR_Reader;
+ else
+ This->base.lockingrole = SWMR_None;
- /* Cannot commit a read-only transacted storage */
- if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
- return STG_E_ACCESSDENIED;
+ This->base.reverted = FALSE;
- hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
- if (hr == E_NOTIMPL) hr = S_OK;
- if (SUCCEEDED(hr))
+ /*
+ * Initialize the big block cache.
+ */
+ This->bigBlockSize = sector_size;
+ This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
+ if (hFile)
+ hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
+ else
{
- hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
- if (SUCCEEDED(hr))
- {
- if (transactionSig != This->lastTransactionSig)
- {
- ERR("file was externally modified\n");
- hr = STG_E_NOTCURRENT;
- }
-
- if (SUCCEEDED(hr))
- {
- This->lastTransactionSig = transactionSig+1;
- hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
- }
- }
- else if (hr == E_NOTIMPL)
- hr = S_OK;
+ This->lockBytes = pLkbyt;
+ ILockBytes_AddRef(pLkbyt);
+ }
- if (FAILED(hr)) goto end;
+ if (SUCCEEDED(hr))
+ hr = StorageImpl_GrabLocks(This, openFlags);
- /* To prevent data loss, we create the new structure in the file before we
- * delete the old one, so that in case of errors the old data is intact. We
- * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
- * needed in the rare situation where we have just enough free disk space to
- * overwrite the existing data. */
+ if (SUCCEEDED(hr))
+ hr = StorageImpl_Refresh(This, TRUE, create);
- root_entry = &This->entries[This->base.storageDirEntry];
+ if (FAILED(hr))
+ {
+ IStorage_Release(&This->base.IStorage_iface);
+ *result = NULL;
+ }
+ else
+ {
+ StorageImpl_Flush(&This->base);
+ *result = This;
+ }
- if (!root_entry->read)
- goto end;
+ return hr;
+}
- hr = TransactedSnapshotImpl_CopyTree(This);
- if (FAILED(hr)) goto end;
- if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
- dir_root_ref = DIRENTRY_NULL;
- else
- dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+/************************************************************************
+ * StorageInternalImpl implementation
+ ***********************************************************************/
- hr = StorageBaseImpl_Flush(This->transactedParent);
-
- /* Update the storage to use the new data in one step. */
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
- root_entry->transactedParentEntry, &data);
+static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
+{
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- if (SUCCEEDED(hr))
- {
- data.dirRootEntry = dir_root_ref;
- data.clsid = root_entry->data.clsid;
- data.ctime = root_entry->data.ctime;
- data.mtime = root_entry->data.mtime;
+ if (!This->base.reverted)
+ {
+ TRACE("Storage invalidated (stg=%p)\n", This);
- hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
- root_entry->transactedParentEntry, &data);
- }
+ This->base.reverted = TRUE;
- /* Try to flush after updating the root storage, but if the flush fails, keep
- * going, on the theory that it'll either succeed later or the subsequent
- * writes will fail. */
- StorageBaseImpl_Flush(This->transactedParent);
+ This->parentStorage = NULL;
- if (SUCCEEDED(hr))
- {
- /* Destroy the old now-orphaned data. */
- for (i=0; i<This->entries_size; i++)
- {
- TransactedDirEntry *entry = &This->entries[i];
- if (entry->inuse)
- {
- if (entry->deleted)
- {
- StorageBaseImpl_StreamSetSize(This->transactedParent,
- entry->transactedParentEntry, zero);
- StorageBaseImpl_DestroyDirEntry(This->transactedParent,
- entry->transactedParentEntry);
- memset(entry, 0, sizeof(TransactedDirEntry));
- This->firstFreeEntry = min(i, This->firstFreeEntry);
- }
- else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
- {
- if (entry->transactedParentEntry != DIRENTRY_NULL)
- StorageBaseImpl_DestroyDirEntry(This->transactedParent,
- entry->transactedParentEntry);
- if (entry->stream_dirty)
- {
- StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
- StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
- entry->stream_dirty = FALSE;
- }
- entry->dirty = FALSE;
- entry->transactedParentEntry = entry->newTransactedParentEntry;
- }
- }
- }
- }
- else
- {
- TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
- }
+ StorageBaseImpl_DeleteAll(&This->base);
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_Flush(This->transactedParent);
-end:
- StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+ list_remove(&This->ParentListEntry);
}
-
- return hr;
}
-static HRESULT WINAPI TransactedSnapshotImpl_Revert(
- IStorage* iface)
+static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
- ULARGE_INTEGER zero;
- ULONG i;
-
- TRACE("(%p)\n", iface);
-
- /* Destroy the open objects. */
- StorageBaseImpl_DeleteAll(&This->base);
-
- /* Clear out the scratch file. */
- zero.QuadPart = 0;
- for (i=0; i<This->entries_size; i++)
- {
- if (This->entries[i].stream_dirty)
- {
- StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
- zero);
-
- StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
- }
- }
-
- memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
+ StorageInternalImpl* This = (StorageInternalImpl*) iface;
- This->firstFreeEntry = 0;
- This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
+ StorageInternalImpl_Invalidate(&This->base);
- return S_OK;
+ HeapFree(GetProcessHeap(), 0, This);
}
-static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
+static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
{
- if (!This->reverted)
- {
- TRACE("Storage invalidated (stg=%p)\n", This);
-
- This->reverted = TRUE;
+ StorageInternalImpl* This = (StorageInternalImpl*) iface;
- StorageBaseImpl_DeleteAll(This);
- }
+ return StorageBaseImpl_Flush(This->parentStorage);
}
-static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
+static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+ StorageInternalImpl* This = (StorageInternalImpl*) iface;
- IStorage_Revert(&This->base.IStorage_iface);
- IStorage_Release(&This->transactedParent->IStorage_iface);
- IStorage_Release(&This->scratch->IStorage_iface);
- HeapFree(GetProcessHeap(), 0, This->entries);
- HeapFree(GetProcessHeap(), 0, This);
+ return StorageBaseImpl_GetFilename(This->parentStorage, result);
}
-static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
+static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
+ const DirEntry *newData, DirRef *index)
{
- /* We only need to flush when committing. */
- return S_OK;
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
+
+ return StorageBaseImpl_CreateDirEntry(This->parentStorage,
+ newData, index);
}
-static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
+ DirRef index, const DirEntry *data)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- return StorageBaseImpl_GetFilename(This->transactedParent, result);
+ return StorageBaseImpl_WriteDirEntry(This->parentStorage,
+ index, data);
}
-static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
- const DirEntry *newData, DirRef *index)
+static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
+ DirRef index, DirEntry *data)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- DirRef new_ref;
- TransactedDirEntry *new_entry;
-
- new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
- if (new_ref == DIRENTRY_NULL)
- return E_OUTOFMEMORY;
-
- new_entry = &This->entries[new_ref];
-
- new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
- new_entry->read = TRUE;
- new_entry->dirty = TRUE;
- memcpy(&new_entry->data, newData, sizeof(DirEntry));
-
- *index = new_ref;
-
- TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- return S_OK;
+ return StorageBaseImpl_ReadDirEntry(This->parentStorage,
+ index, data);
}
-static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
- DirRef index, const DirEntry *data)
+static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
+ DirRef index)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- HRESULT hr;
-
- TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
- if (FAILED(hr)) return hr;
+ return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
+ index);
+}
- memcpy(&This->entries[index].data, data, sizeof(DirEntry));
+static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- if (index != This->base.storageDirEntry)
- {
- This->entries[index].dirty = TRUE;
+ return StorageBaseImpl_StreamReadAt(This->parentStorage,
+ index, offset, size, buffer, bytesRead);
+}
- if (data->size.QuadPart == 0 &&
- This->entries[index].transactedParentEntry != DIRENTRY_NULL)
- {
- /* Since this entry is modified, and we aren't using its stream data, we
- * no longer care about the original entry. */
- DirRef delete_ref;
- delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+{
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- if (delete_ref != DIRENTRY_NULL)
- This->entries[delete_ref].deleted = TRUE;
+ return StorageBaseImpl_StreamWriteAt(This->parentStorage,
+ index, offset, size, buffer, bytesWritten);
+}
- This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
- }
- }
+static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER newsize)
+{
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- return S_OK;
+ return StorageBaseImpl_StreamSetSize(This->parentStorage,
+ index, newsize);
}
-static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
- DirRef index, DirEntry *data)
+static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
+ DirRef dst, DirRef src)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- HRESULT hr;
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
- if (FAILED(hr)) return hr;
+ return StorageBaseImpl_StreamLink(This->parentStorage,
+ dst, src);
+}
- memcpy(data, &This->entries[index].data, sizeof(DirEntry));
+static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
+ ULONG* result, BOOL refresh)
+{
+ return E_NOTIMPL;
+}
- TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
+ ULONG value)
+{
+ return E_NOTIMPL;
+}
- return S_OK;
+static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+ return E_NOTIMPL;
}
-static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
- DirRef index)
+static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ return E_NOTIMPL;
+}
- if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
- This->entries[index].data.size.QuadPart != 0)
- {
- /* If we deleted this entry while it has stream data. We must have left the
- * data because some other entry is using it, and we need to leave the
- * original entry alone. */
- memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
- This->firstFreeEntry = min(index, This->firstFreeEntry);
- }
- else
- {
- This->entries[index].deleted = TRUE;
- }
-
- return S_OK;
-}
-
-static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-
- if (This->entries[index].stream_dirty)
- {
- return StorageBaseImpl_StreamReadAt(This->scratch,
- This->entries[index].stream_entry, offset, size, buffer, bytesRead);
- }
- else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
- {
- /* This stream doesn't live in the parent, and we haven't allocated storage
- * for it yet */
- *bytesRead = 0;
- return S_OK;
- }
- else
- {
- return StorageBaseImpl_StreamReadAt(This->transactedParent,
- This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
- }
-}
-
-static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- HRESULT hr;
-
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
- if (FAILED(hr)) return hr;
-
- hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
- if (FAILED(hr)) return hr;
-
- hr = StorageBaseImpl_StreamWriteAt(This->scratch,
- This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
-
- if (SUCCEEDED(hr) && size != 0)
- This->entries[index].data.size.QuadPart = max(
- This->entries[index].data.size.QuadPart,
- offset.QuadPart + size);
-
- return hr;
-}
-
-static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER newsize)
+/******************************************************************************
+**
+** StorageInternalImpl_Commit
+**
+*/
+static HRESULT WINAPI StorageInternalImpl_Commit(
+ IStorage* iface,
+ DWORD grfCommitFlags) /* [in] */
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- HRESULT hr;
-
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
- if (FAILED(hr)) return hr;
-
- if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
- return S_OK;
-
- if (newsize.QuadPart == 0)
- {
- /* Destroy any parent references or entries in the scratch file. */
- if (This->entries[index].stream_dirty)
- {
- ULARGE_INTEGER zero;
- zero.QuadPart = 0;
- StorageBaseImpl_StreamSetSize(This->scratch,
- This->entries[index].stream_entry, zero);
- StorageBaseImpl_DestroyDirEntry(This->scratch,
- This->entries[index].stream_entry);
- This->entries[index].stream_dirty = FALSE;
- }
- else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
- {
- DirRef delete_ref;
- delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
-
- if (delete_ref != DIRENTRY_NULL)
- This->entries[delete_ref].deleted = TRUE;
-
- This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
- }
- }
- else
- {
- hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
- if (FAILED(hr)) return hr;
-
- hr = StorageBaseImpl_StreamSetSize(This->scratch,
- This->entries[index].stream_entry, newsize);
- }
-
- if (SUCCEEDED(hr))
- This->entries[index].data.size = newsize;
-
- return hr;
+ StorageBaseImpl* This = impl_from_IStorage(iface);
+ TRACE("(%p,%x)\n", iface, grfCommitFlags);
+ return StorageBaseImpl_Flush(This);
}
-static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
- DirRef dst, DirRef src)
+/******************************************************************************
+**
+** StorageInternalImpl_Revert
+**
+*/
+static HRESULT WINAPI StorageInternalImpl_Revert(
+ IStorage* iface)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- HRESULT hr;
- TransactedDirEntry *dst_entry, *src_entry;
-
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
- if (FAILED(hr)) return hr;
-
- hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
- if (FAILED(hr)) return hr;
-
- dst_entry = &This->entries[dst];
- src_entry = &This->entries[src];
-
- dst_entry->stream_dirty = src_entry->stream_dirty;
- dst_entry->stream_entry = src_entry->stream_entry;
- dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
- dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
- dst_entry->data.size = src_entry->data.size;
-
+ FIXME("(%p): stub\n", iface);
return S_OK;
}
-static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
- ULONG* result, BOOL refresh)
-{
- return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
- ULONG value)
-{
- return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
-{
- return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
-{
- return E_NOTIMPL;
-}
-
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
+/*
+ * Virtual function table for the StorageInternalImpl class.
+ */
+static const IStorageVtbl StorageInternalImpl_Vtbl =
{
StorageBaseImpl_QueryInterface,
StorageBaseImpl_AddRef,
@@ -5630,8 +5359,8 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
StorageBaseImpl_OpenStorage,
StorageBaseImpl_CopyTo,
StorageBaseImpl_MoveElementTo,
- TransactedSnapshotImpl_Commit,
- TransactedSnapshotImpl_Revert,
+ StorageInternalImpl_Commit,
+ StorageInternalImpl_Revert,
StorageBaseImpl_EnumElements,
StorageBaseImpl_DestroyElement,
StorageBaseImpl_RenameElement,
@@ -5641,934 +5370,1200 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
StorageBaseImpl_Stat
};
-static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
+static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
{
- TransactedSnapshotImpl_Destroy,
- TransactedSnapshotImpl_Invalidate,
- TransactedSnapshotImpl_Flush,
- TransactedSnapshotImpl_GetFilename,
- TransactedSnapshotImpl_CreateDirEntry,
- TransactedSnapshotImpl_WriteDirEntry,
- TransactedSnapshotImpl_ReadDirEntry,
- TransactedSnapshotImpl_DestroyDirEntry,
- TransactedSnapshotImpl_StreamReadAt,
- TransactedSnapshotImpl_StreamWriteAt,
- TransactedSnapshotImpl_StreamSetSize,
- TransactedSnapshotImpl_StreamLink,
- TransactedSnapshotImpl_GetTransactionSig,
- TransactedSnapshotImpl_SetTransactionSig,
- TransactedSnapshotImpl_LockTransaction,
- TransactedSnapshotImpl_UnlockTransaction
+ StorageInternalImpl_Destroy,
+ StorageInternalImpl_Invalidate,
+ StorageInternalImpl_Flush,
+ StorageInternalImpl_GetFilename,
+ StorageInternalImpl_CreateDirEntry,
+ StorageInternalImpl_WriteDirEntry,
+ StorageInternalImpl_ReadDirEntry,
+ StorageInternalImpl_DestroyDirEntry,
+ StorageInternalImpl_StreamReadAt,
+ StorageInternalImpl_StreamWriteAt,
+ StorageInternalImpl_StreamSetSize,
+ StorageInternalImpl_StreamLink,
+ StorageInternalImpl_GetTransactionSig,
+ StorageInternalImpl_SetTransactionSig,
+ StorageInternalImpl_LockTransaction,
+ StorageInternalImpl_UnlockTransaction
};
-static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
- TransactedSnapshotImpl** result)
+static StorageInternalImpl* StorageInternalImpl_Construct(
+ StorageBaseImpl* parentStorage,
+ DWORD openFlags,
+ DirRef storageDirEntry)
{
- HRESULT hr;
+ StorageInternalImpl* newStorage;
- *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
- if (*result)
+ newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
+
+ if (newStorage!=0)
{
- IStorage *scratch;
+ list_init(&newStorage->base.strmHead);
- (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
+ list_init(&newStorage->base.storageHead);
- /* This is OK because the property set storage functions use the IStorage functions. */
- (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
- (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
+ /*
+ * Initialize the virtual function table.
+ */
+ newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
+ newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
+ newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
+ newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
- list_init(&(*result)->base.strmHead);
+ newStorage->base.reverted = FALSE;
- list_init(&(*result)->base.storageHead);
+ newStorage->base.ref = 1;
- (*result)->base.ref = 1;
+ newStorage->parentStorage = parentStorage;
- (*result)->base.openFlags = parentStorage->openFlags;
+ /*
+ * Keep a reference to the directory entry of this storage
+ */
+ newStorage->base.storageDirEntry = storageDirEntry;
- /* This cannot fail, except with E_NOTIMPL in which case we don't care */
- StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
+ newStorage->base.create = FALSE;
- /* Create a new temporary storage to act as the scratch file. */
- hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
- 0, &scratch);
- (*result)->scratch = impl_from_IStorage(scratch);
+ return newStorage;
+ }
- if (SUCCEEDED(hr))
- {
- ULONG num_entries = 20;
+ return 0;
+}
- (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
- (*result)->entries_size = num_entries;
- (*result)->firstFreeEntry = 0;
- if ((*result)->entries)
- {
- /* parentStorage already has 1 reference, which we take over here. */
- (*result)->transactedParent = parentStorage;
+/************************************************************************
+ * TransactedSnapshotImpl implementation
+ ***********************************************************************/
- parentStorage->transactedChild = &(*result)->base;
+static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
+{
+ DirRef result=This->firstFreeEntry;
- (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
- }
- else
- {
- IStorage_Release(scratch);
+ while (result < This->entries_size && This->entries[result].inuse)
+ result++;
- hr = E_OUTOFMEMORY;
- }
- }
+ if (result == This->entries_size)
+ {
+ ULONG new_size = This->entries_size * 2;
+ TransactedDirEntry *new_entries;
- if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+ new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
+ if (!new_entries) return DIRENTRY_NULL;
- return hr;
+ memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
+ HeapFree(GetProcessHeap(), 0, This->entries);
+
+ This->entries = new_entries;
+ This->entries_size = new_size;
}
- else
- return E_OUTOFMEMORY;
-}
-static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
-{
- if (!This->reverted)
- {
- TRACE("Storage invalidated (stg=%p)\n", This);
+ This->entries[result].inuse = TRUE;
- This->reverted = TRUE;
+ This->firstFreeEntry = result+1;
- StorageBaseImpl_DeleteAll(This);
- }
+ return result;
}
-static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
+static DirRef TransactedSnapshotImpl_CreateStubEntry(
+ TransactedSnapshotImpl *This, DirRef parentEntryRef)
{
- TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+ DirRef stubEntryRef;
+ TransactedDirEntry *entry;
- TransactedSharedImpl_Invalidate(&This->base);
- IStorage_Release(&This->transactedParent->IStorage_iface);
- IStorage_Release(&This->scratch->base.IStorage_iface);
- HeapFree(GetProcessHeap(), 0, This);
-}
+ stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
-static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
-{
- /* We only need to flush when committing. */
- return S_OK;
-}
+ if (stubEntryRef != DIRENTRY_NULL)
+ {
+ entry = &This->entries[stubEntryRef];
-static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+ entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
- return StorageBaseImpl_GetFilename(This->transactedParent, result);
+ entry->read = FALSE;
+ }
+
+ return stubEntryRef;
}
-static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
- const DirEntry *newData, DirRef *index)
+static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
+ TransactedSnapshotImpl *This, DirRef entry)
{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ HRESULT hr=S_OK;
+ DirEntry data;
- return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
- newData, index);
-}
+ if (!This->entries[entry].read)
+ {
+ hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
+ This->entries[entry].transactedParentEntry,
+ &data);
-static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
- DirRef index, const DirEntry *data)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
+ {
+ data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
- return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
- index, data);
-}
+ if (data.leftChild == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
-static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
- DirRef index, DirEntry *data)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
+ {
+ data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
- return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
- index, data);
-}
+ if (data.rightChild == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
-static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
- DirRef index)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
+ {
+ data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
- return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
- index);
-}
+ if (data.dirRootEntry == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
-static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ if (SUCCEEDED(hr))
+ {
+ memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
+ This->entries[entry].read = TRUE;
+ }
+ }
- return StorageBaseImpl_StreamReadAt(&This->scratch->base,
- index, offset, size, buffer, bytesRead);
+ return hr;
}
-static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
+ TransactedSnapshotImpl *This, DirRef entry)
{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ HRESULT hr = S_OK;
- return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
- index, offset, size, buffer, bytesWritten);
-}
+ if (!This->entries[entry].stream_dirty)
+ {
+ DirEntry new_entrydata;
-static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER newsize)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ memset(&new_entrydata, 0, sizeof(DirEntry));
+ new_entrydata.name[0] = 'S';
+ new_entrydata.sizeOfNameString = 1;
+ new_entrydata.stgType = STGTY_STREAM;
+ new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
+ new_entrydata.leftChild = DIRENTRY_NULL;
+ new_entrydata.rightChild = DIRENTRY_NULL;
+ new_entrydata.dirRootEntry = DIRENTRY_NULL;
- return StorageBaseImpl_StreamSetSize(&This->scratch->base,
- index, newsize);
-}
+ hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
+ &This->entries[entry].stream_entry);
-static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
- DirRef dst, DirRef src)
-{
- TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+ if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+ {
+ hr = StorageBaseImpl_CopyStream(
+ This->scratch, This->entries[entry].stream_entry,
+ This->transactedParent, This->entries[entry].transactedParentEntry);
- return StorageBaseImpl_StreamLink(&This->scratch->base,
- dst, src);
-}
+ if (FAILED(hr))
+ StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
+ }
-static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
- ULONG* result, BOOL refresh)
-{
- return E_NOTIMPL;
-}
+ if (SUCCEEDED(hr))
+ This->entries[entry].stream_dirty = TRUE;
-static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
- ULONG value)
-{
- return E_NOTIMPL;
-}
+ if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+ {
+ /* Since this entry is modified, and we aren't using its stream data, we
+ * no longer care about the original entry. */
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
-static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
-{
- return E_NOTIMPL;
-}
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = TRUE;
-static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
-{
- return E_NOTIMPL;
+ This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+
+ return hr;
}
-static HRESULT WINAPI TransactedSharedImpl_Commit(
- IStorage* iface,
- DWORD grfCommitFlags) /* [in] */
+/* Find the first entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindFirstChild(
+ TransactedSnapshotImpl* This, DirRef parent)
{
- TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
- DirRef new_storage_ref, prev_storage_ref;
- DirEntry src_data, dst_data;
- HRESULT hr;
- ULONG transactionSig;
-
- TRACE("(%p,%x)\n", iface, grfCommitFlags);
-
- /* Cannot commit a read-only transacted storage */
- if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
- return STG_E_ACCESSDENIED;
+ DirRef cursor, prev;
+ TransactedDirEntry *entry;
- hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
- if (hr == E_NOTIMPL) hr = S_OK;
- if (SUCCEEDED(hr))
- {
- hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
- if (SUCCEEDED(hr))
+ cursor = parent;
+ entry = &This->entries[cursor];
+ while (entry->read)
+ {
+ if (entry->data.leftChild != DIRENTRY_NULL)
{
- if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
- hr = STG_E_NOTCURRENT;
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
+ prev = cursor;
+ cursor = entry->data.leftChild;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
}
- else if (hr == E_NOTIMPL)
- hr = S_OK;
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
-
- /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_Flush(This->transactedParent);
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
-
- if (SUCCEEDED(hr))
+ else if (entry->data.rightChild != DIRENTRY_NULL)
{
- prev_storage_ref = dst_data.dirRootEntry;
- dst_data.dirRootEntry = new_storage_ref;
- dst_data.clsid = src_data.clsid;
- dst_data.ctime = src_data.ctime;
- dst_data.mtime = src_data.mtime;
- hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+ prev = cursor;
+ cursor = entry->data.rightChild;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
}
-
- if (SUCCEEDED(hr))
+ else if (entry->data.dirRootEntry != DIRENTRY_NULL)
{
- /* Try to flush after updating the root storage, but if the flush fails, keep
- * going, on the theory that it'll either succeed later or the subsequent
- * writes will fail. */
- StorageBaseImpl_Flush(This->transactedParent);
-
- hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
+ prev = cursor;
+ cursor = entry->data.dirRootEntry;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
}
+ else
+ break;
+ }
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_Flush(This->transactedParent);
+ return cursor;
+}
- StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+/* Find the next entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindNextChild(
+ TransactedSnapshotImpl* This, DirRef current)
+{
+ DirRef parent;
+ TransactedDirEntry *parent_entry;
- if (SUCCEEDED(hr))
- hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
+ parent = This->entries[current].parent;
+ parent_entry = &This->entries[parent];
- if (SUCCEEDED(hr))
+ if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
+ {
+ if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
{
- This->lastTransactionSig = transactionSig+1;
+ This->entries[parent_entry->data.rightChild].parent = parent;
+ return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
+ }
+
+ if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
+ {
+ This->entries[parent_entry->data.dirRootEntry].parent = parent;
+ return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
}
}
- return hr;
+ return parent;
}
-static HRESULT WINAPI TransactedSharedImpl_Revert(
- IStorage* iface)
+/* Return TRUE if we've made a copy of this entry for committing to the parent. */
+static inline BOOL TransactedSnapshotImpl_MadeCopy(
+ TransactedSnapshotImpl* This, DirRef entry)
{
- TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+ return entry != DIRENTRY_NULL &&
+ This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
+}
- TRACE("(%p)\n", iface);
+/* Destroy the entries created by CopyTree. */
+static void TransactedSnapshotImpl_DestroyTemporaryCopy(
+ TransactedSnapshotImpl* This, DirRef stop)
+{
+ DirRef cursor;
+ TransactedDirEntry *entry;
+ ULARGE_INTEGER zero;
- /* Destroy the open objects. */
- StorageBaseImpl_DeleteAll(&This->base);
+ zero.QuadPart = 0;
- return IStorage_Revert(&This->scratch->base.IStorage_iface);
-}
+ if (!This->entries[This->base.storageDirEntry].read)
+ return;
-static const IStorageVtbl TransactedSharedImpl_Vtbl =
-{
- StorageBaseImpl_QueryInterface,
- StorageBaseImpl_AddRef,
- StorageBaseImpl_Release,
- StorageBaseImpl_CreateStream,
- StorageBaseImpl_OpenStream,
- StorageBaseImpl_CreateStorage,
- StorageBaseImpl_OpenStorage,
- StorageBaseImpl_CopyTo,
- StorageBaseImpl_MoveElementTo,
- TransactedSharedImpl_Commit,
- TransactedSharedImpl_Revert,
- StorageBaseImpl_EnumElements,
- StorageBaseImpl_DestroyElement,
- StorageBaseImpl_RenameElement,
- StorageBaseImpl_SetElementTimes,
- StorageBaseImpl_SetClass,
- StorageBaseImpl_SetStateBits,
- StorageBaseImpl_Stat
-};
+ cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
-static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
-{
- TransactedSharedImpl_Destroy,
- TransactedSharedImpl_Invalidate,
- TransactedSharedImpl_Flush,
- TransactedSharedImpl_GetFilename,
- TransactedSharedImpl_CreateDirEntry,
- TransactedSharedImpl_WriteDirEntry,
- TransactedSharedImpl_ReadDirEntry,
- TransactedSharedImpl_DestroyDirEntry,
- TransactedSharedImpl_StreamReadAt,
- TransactedSharedImpl_StreamWriteAt,
- TransactedSharedImpl_StreamSetSize,
- TransactedSharedImpl_StreamLink,
- TransactedSharedImpl_GetTransactionSig,
- TransactedSharedImpl_SetTransactionSig,
- TransactedSharedImpl_LockTransaction,
- TransactedSharedImpl_UnlockTransaction
-};
+ if (cursor == DIRENTRY_NULL)
+ return;
-static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
- TransactedSharedImpl** result)
-{
- HRESULT hr;
+ cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
- *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
- if (*result)
+ while (cursor != DIRENTRY_NULL && cursor != stop)
{
- IStorage *scratch;
+ if (TransactedSnapshotImpl_MadeCopy(This, cursor))
+ {
+ entry = &This->entries[cursor];
- (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
+ if (entry->stream_dirty)
+ StorageBaseImpl_StreamSetSize(This->transactedParent,
+ entry->newTransactedParentEntry, zero);
- /* This is OK because the property set storage functions use the IStorage functions. */
- (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
- (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->newTransactedParentEntry);
- list_init(&(*result)->base.strmHead);
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
+ }
- list_init(&(*result)->base.storageHead);
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ }
+}
- (*result)->base.ref = 1;
+/* Make a copy of our edited tree that we can use in the parent. */
+static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
+{
+ DirRef cursor;
+ TransactedDirEntry *entry;
+ HRESULT hr = S_OK;
- (*result)->base.openFlags = parentStorage->openFlags;
+ cursor = This->base.storageDirEntry;
+ entry = &This->entries[cursor];
+ entry->parent = DIRENTRY_NULL;
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
- hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
+ if (entry->data.dirRootEntry == DIRENTRY_NULL)
+ return S_OK;
- if (SUCCEEDED(hr))
- {
- STGOPTIONS stgo;
+ This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
- /* This cannot fail, except with E_NOTIMPL in which case we don't care */
- StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
+ cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
+ entry = &This->entries[cursor];
- stgo.usVersion = 1;
- stgo.reserved = 0;
- stgo.ulSectorSize = 4096;
- stgo.pwcsTemplateFile = NULL;
+ while (cursor != DIRENTRY_NULL)
+ {
+ /* Make a copy of this entry in the transacted parent. */
+ if (!entry->read ||
+ (!entry->dirty && !entry->stream_dirty &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
+ else
+ {
+ DirEntry newData;
- /* Create a new temporary storage to act as the scratch file. */
- hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
- STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
- (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
+ memcpy(&newData, &entry->data, sizeof(DirEntry));
- if (SUCCEEDED(hr))
- {
- hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
- parentStorage, parentStorage->storageDirEntry);
+ newData.size.QuadPart = 0;
+ newData.startingBlock = BLOCK_END_OF_CHAIN;
- if (SUCCEEDED(hr))
- {
- hr = IStorage_Commit(scratch, STGC_DEFAULT);
+ if (newData.leftChild != DIRENTRY_NULL)
+ newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
- (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
- (*result)->transactedParent = parentStorage;
- }
+ if (newData.rightChild != DIRENTRY_NULL)
+ newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
- if (FAILED(hr))
- IStorage_Release(scratch);
+ if (newData.dirRootEntry != DIRENTRY_NULL)
+ newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
+
+ hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
+ &entry->newTransactedParentEntry);
+ if (FAILED(hr))
+ {
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+ return hr;
}
- StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
- }
+ if (entry->stream_dirty)
+ {
+ hr = StorageBaseImpl_CopyStream(
+ This->transactedParent, entry->newTransactedParentEntry,
+ This->scratch, entry->stream_entry);
+ }
+ else if (entry->data.size.QuadPart)
+ {
+ hr = StorageBaseImpl_StreamLink(
+ This->transactedParent, entry->newTransactedParentEntry,
+ entry->transactedParentEntry);
+ }
- if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+ if (FAILED(hr))
+ {
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+ return hr;
+ }
+ }
- return hr;
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ entry = &This->entries[cursor];
}
- else
- return E_OUTOFMEMORY;
+
+ return hr;
}
-static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
- BOOL toplevel, StorageBaseImpl** result)
+static HRESULT WINAPI TransactedSnapshotImpl_Commit(
+ IStorage* iface,
+ DWORD grfCommitFlags) /* [in] */
{
- static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
+ TransactedDirEntry *root_entry;
+ DirRef i, dir_root_ref;
+ DirEntry data;
+ ULARGE_INTEGER zero;
+ HRESULT hr;
+ ULONG transactionSig;
- if (parentStorage->openFlags & fixme_flags)
- {
- fixme_flags &= ~parentStorage->openFlags;
- FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
- }
+ zero.QuadPart = 0;
- if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
- STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
- STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
+ TRACE("(%p,%x)\n", iface, grfCommitFlags);
+
+ /* Cannot commit a read-only transacted storage */
+ if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
+ return STG_E_ACCESSDENIED;
+
+ hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
+ if (hr == E_NOTIMPL) hr = S_OK;
+ if (SUCCEEDED(hr))
{
- /* Need to create a temp file for the snapshot */
- return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
- }
+ hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ if (transactionSig != This->lastTransactionSig)
+ {
+ ERR("file was externally modified\n");
+ hr = STG_E_NOTCURRENT;
+ }
- return TransactedSnapshotImpl_Construct(parentStorage,
- (TransactedSnapshotImpl**)result);
-}
+ if (SUCCEEDED(hr))
+ {
+ This->lastTransactionSig = transactionSig+1;
+ hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
+ }
+ }
+ else if (hr == E_NOTIMPL)
+ hr = S_OK;
-static HRESULT Storage_Construct(
- HANDLE hFile,
- LPCOLESTR pwcsName,
- ILockBytes* pLkbyt,
- DWORD openFlags,
- BOOL fileBased,
- BOOL create,
- ULONG sector_size,
- StorageBaseImpl** result)
-{
- StorageImpl *newStorage;
- StorageBaseImpl *newTransactedStorage;
- HRESULT hr;
+ if (FAILED(hr)) goto end;
- hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
- if (FAILED(hr)) goto end;
+ /* To prevent data loss, we create the new structure in the file before we
+ * delete the old one, so that in case of errors the old data is intact. We
+ * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
+ * needed in the rare situation where we have just enough free disk space to
+ * overwrite the existing data. */
- if (openFlags & STGM_TRANSACTED)
- {
- hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
- if (FAILED(hr))
- IStorage_Release(&newStorage->base.IStorage_iface);
+ root_entry = &This->entries[This->base.storageDirEntry];
+
+ if (!root_entry->read)
+ goto end;
+
+ hr = TransactedSnapshotImpl_CopyTree(This);
+ if (FAILED(hr)) goto end;
+
+ if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
+ dir_root_ref = DIRENTRY_NULL;
else
- *result = newTransactedStorage;
- }
- else
- *result = &newStorage->base;
+ dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+
+ hr = StorageBaseImpl_Flush(This->transactedParent);
+
+ /* Update the storage to use the new data in one step. */
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
+ root_entry->transactedParentEntry, &data);
+
+ if (SUCCEEDED(hr))
+ {
+ data.dirRootEntry = dir_root_ref;
+ data.clsid = root_entry->data.clsid;
+ data.ctime = root_entry->data.ctime;
+ data.mtime = root_entry->data.mtime;
+
+ hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
+ root_entry->transactedParentEntry, &data);
+ }
+
+ /* Try to flush after updating the root storage, but if the flush fails, keep
+ * going, on the theory that it'll either succeed later or the subsequent
+ * writes will fail. */
+ StorageBaseImpl_Flush(This->transactedParent);
+
+ if (SUCCEEDED(hr))
+ {
+ /* Destroy the old now-orphaned data. */
+ for (i=0; i<This->entries_size; i++)
+ {
+ TransactedDirEntry *entry = &This->entries[i];
+ if (entry->inuse)
+ {
+ if (entry->deleted)
+ {
+ StorageBaseImpl_StreamSetSize(This->transactedParent,
+ entry->transactedParentEntry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->transactedParentEntry);
+ memset(entry, 0, sizeof(TransactedDirEntry));
+ This->firstFreeEntry = min(i, This->firstFreeEntry);
+ }
+ else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
+ {
+ if (entry->transactedParentEntry != DIRENTRY_NULL)
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->transactedParentEntry);
+ if (entry->stream_dirty)
+ {
+ StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
+ entry->stream_dirty = FALSE;
+ }
+ entry->dirty = FALSE;
+ entry->transactedParentEntry = entry->newTransactedParentEntry;
+ }
+ }
+ }
+ }
+ else
+ {
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
+ }
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_Flush(This->transactedParent);
end:
+ StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+ }
+
return hr;
}
-static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
+static HRESULT WINAPI TransactedSnapshotImpl_Revert(
+ IStorage* iface)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
+ ULARGE_INTEGER zero;
+ ULONG i;
- if (!This->base.reverted)
+ TRACE("(%p)\n", iface);
+
+ /* Destroy the open objects. */
+ StorageBaseImpl_DeleteAll(&This->base);
+
+ /* Clear out the scratch file. */
+ zero.QuadPart = 0;
+ for (i=0; i<This->entries_size; i++)
{
- TRACE("Storage invalidated (stg=%p)\n", This);
+ if (This->entries[i].stream_dirty)
+ {
+ StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
+ zero);
- This->base.reverted = TRUE;
+ StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
+ }
+ }
- This->parentStorage = NULL;
+ memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
- StorageBaseImpl_DeleteAll(&This->base);
+ This->firstFreeEntry = 0;
+ This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
- list_remove(&This->ParentListEntry);
- }
+ return S_OK;
}
-static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
+static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
{
- StorageInternalImpl* This = (StorageInternalImpl*) iface;
+ if (!This->reverted)
+ {
+ TRACE("Storage invalidated (stg=%p)\n", This);
- StorageInternalImpl_Invalidate(&This->base);
+ This->reverted = TRUE;
- HeapFree(GetProcessHeap(), 0, This);
+ StorageBaseImpl_DeleteAll(This);
+ }
}
-static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
+static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
{
- StorageInternalImpl* This = (StorageInternalImpl*) iface;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
- return StorageBaseImpl_Flush(This->parentStorage);
+ IStorage_Revert(&This->base.IStorage_iface);
+ IStorage_Release(&This->transactedParent->IStorage_iface);
+ IStorage_Release(&This->scratch->IStorage_iface);
+ HeapFree(GetProcessHeap(), 0, This->entries);
+ HeapFree(GetProcessHeap(), 0, This);
}
-static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
{
- StorageInternalImpl* This = (StorageInternalImpl*) iface;
-
- return StorageBaseImpl_GetFilename(This->parentStorage, result);
+ /* We only need to flush when committing. */
+ return S_OK;
}
-static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
- const DirEntry *newData, DirRef *index)
+static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
- return StorageBaseImpl_CreateDirEntry(This->parentStorage,
- newData, index);
+ return StorageBaseImpl_GetFilename(This->transactedParent, result);
}
-static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
+ const DirEntry *newData, DirRef *index)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ DirRef new_ref;
+ TransactedDirEntry *new_entry;
+
+ new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
+ if (new_ref == DIRENTRY_NULL)
+ return E_OUTOFMEMORY;
+
+ new_entry = &This->entries[new_ref];
+
+ new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
+ new_entry->read = TRUE;
+ new_entry->dirty = TRUE;
+ memcpy(&new_entry->data, newData, sizeof(DirEntry));
+
+ *index = new_ref;
+
+ TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
+
+ return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
DirRef index, const DirEntry *data)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
- return StorageBaseImpl_WriteDirEntry(This->parentStorage,
- index, data);
+ TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ memcpy(&This->entries[index].data, data, sizeof(DirEntry));
+
+ if (index != This->base.storageDirEntry)
+ {
+ This->entries[index].dirty = TRUE;
+
+ if (data->size.QuadPart == 0 &&
+ This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+ {
+ /* Since this entry is modified, and we aren't using its stream data, we
+ * no longer care about the original entry. */
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = TRUE;
+
+ This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+
+ return S_OK;
}
-static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
DirRef index, DirEntry *data)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
- return StorageBaseImpl_ReadDirEntry(This->parentStorage,
- index, data);
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ memcpy(data, &This->entries[index].data, sizeof(DirEntry));
+
+ TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+ return S_OK;
}
-static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
DirRef index)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
- index);
+ if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
+ This->entries[index].data.size.QuadPart != 0)
+ {
+ /* If we deleted this entry while it has stream data. We must have left the
+ * data because some other entry is using it, and we need to leave the
+ * original entry alone. */
+ memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
+ This->firstFreeEntry = min(index, This->firstFreeEntry);
+ }
+ else
+ {
+ This->entries[index].deleted = TRUE;
+ }
+
+ return S_OK;
}
-static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
- return StorageBaseImpl_StreamReadAt(This->parentStorage,
- index, offset, size, buffer, bytesRead);
+ if (This->entries[index].stream_dirty)
+ {
+ return StorageBaseImpl_StreamReadAt(This->scratch,
+ This->entries[index].stream_entry, offset, size, buffer, bytesRead);
+ }
+ else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
+ {
+ /* This stream doesn't live in the parent, and we haven't allocated storage
+ * for it yet */
+ *bytesRead = 0;
+ return S_OK;
+ }
+ else
+ {
+ return StorageBaseImpl_StreamReadAt(This->transactedParent,
+ This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
+ }
}
-static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
- return StorageBaseImpl_StreamWriteAt(This->parentStorage,
- index, offset, size, buffer, bytesWritten);
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = StorageBaseImpl_StreamWriteAt(This->scratch,
+ This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
+
+ if (SUCCEEDED(hr) && size != 0)
+ This->entries[index].data.size.QuadPart = max(
+ This->entries[index].data.size.QuadPart,
+ offset.QuadPart + size);
+
+ return hr;
}
-static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
DirRef index, ULARGE_INTEGER newsize)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
- return StorageBaseImpl_StreamSetSize(This->parentStorage,
- index, newsize);
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
+ return S_OK;
+
+ if (newsize.QuadPart == 0)
+ {
+ /* Destroy any parent references or entries in the scratch file. */
+ if (This->entries[index].stream_dirty)
+ {
+ ULARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ StorageBaseImpl_StreamSetSize(This->scratch,
+ This->entries[index].stream_entry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->scratch,
+ This->entries[index].stream_entry);
+ This->entries[index].stream_dirty = FALSE;
+ }
+ else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+ {
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = TRUE;
+
+ This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+ else
+ {
+ hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = StorageBaseImpl_StreamSetSize(This->scratch,
+ This->entries[index].stream_entry, newsize);
+ }
+
+ if (SUCCEEDED(hr))
+ This->entries[index].data.size = newsize;
+
+ return hr;
}
-static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
DirRef dst, DirRef src)
{
- StorageInternalImpl* This = (StorageInternalImpl*) base;
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+ TransactedDirEntry *dst_entry, *src_entry;
- return StorageBaseImpl_StreamLink(This->parentStorage,
- dst, src);
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
+ if (FAILED(hr)) return hr;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
+ if (FAILED(hr)) return hr;
+
+ dst_entry = &This->entries[dst];
+ src_entry = &This->entries[src];
+
+ dst_entry->stream_dirty = src_entry->stream_dirty;
+ dst_entry->stream_entry = src_entry->stream_entry;
+ dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
+ dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
+ dst_entry->data.size = src_entry->data.size;
+
+ return S_OK;
}
-static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
ULONG* result, BOOL refresh)
{
return E_NOTIMPL;
}
-static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
ULONG value)
{
return E_NOTIMPL;
}
-static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
{
return E_NOTIMPL;
}
-static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
{
return E_NOTIMPL;
}
-/******************************************************************************
-**
-** StorageInternalImpl_Commit
-**
-*/
-static HRESULT WINAPI StorageInternalImpl_Commit(
- IStorage* iface,
- DWORD grfCommitFlags) /* [in] */
+static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
{
- StorageBaseImpl* This = impl_from_IStorage(iface);
- TRACE("(%p,%x)\n", iface, grfCommitFlags);
- return StorageBaseImpl_Flush(This);
-}
+ StorageBaseImpl_QueryInterface,
+ StorageBaseImpl_AddRef,
+ StorageBaseImpl_Release,
+ StorageBaseImpl_CreateStream,
+ StorageBaseImpl_OpenStream,
+ StorageBaseImpl_CreateStorage,
+ StorageBaseImpl_OpenStorage,
+ StorageBaseImpl_CopyTo,
+ StorageBaseImpl_MoveElementTo,
+ TransactedSnapshotImpl_Commit,
+ TransactedSnapshotImpl_Revert,
+ StorageBaseImpl_EnumElements,
+ StorageBaseImpl_DestroyElement,
+ StorageBaseImpl_RenameElement,
+ StorageBaseImpl_SetElementTimes,
+ StorageBaseImpl_SetClass,
+ StorageBaseImpl_SetStateBits,
+ StorageBaseImpl_Stat
+};
-/******************************************************************************
-**
-** StorageInternalImpl_Revert
-**
-*/
-static HRESULT WINAPI StorageInternalImpl_Revert(
- IStorage* iface)
-{
- FIXME("(%p): stub\n", iface);
- return S_OK;
-}
-
-static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
+static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
{
- IStorage_Release(&This->parentStorage->IStorage_iface);
- HeapFree(GetProcessHeap(), 0, This);
-}
+ TransactedSnapshotImpl_Destroy,
+ TransactedSnapshotImpl_Invalidate,
+ TransactedSnapshotImpl_Flush,
+ TransactedSnapshotImpl_GetFilename,
+ TransactedSnapshotImpl_CreateDirEntry,
+ TransactedSnapshotImpl_WriteDirEntry,
+ TransactedSnapshotImpl_ReadDirEntry,
+ TransactedSnapshotImpl_DestroyDirEntry,
+ TransactedSnapshotImpl_StreamReadAt,
+ TransactedSnapshotImpl_StreamWriteAt,
+ TransactedSnapshotImpl_StreamSetSize,
+ TransactedSnapshotImpl_StreamLink,
+ TransactedSnapshotImpl_GetTransactionSig,
+ TransactedSnapshotImpl_SetTransactionSig,
+ TransactedSnapshotImpl_LockTransaction,
+ TransactedSnapshotImpl_UnlockTransaction
+};
-static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
- IEnumSTATSTG* iface,
- REFIID riid,
- void** ppvObject)
+static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
+ TransactedSnapshotImpl** result)
{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
-
- if (ppvObject==0)
- return E_INVALIDARG;
-
- *ppvObject = 0;
+ HRESULT hr;
- if (IsEqualGUID(&IID_IUnknown, riid) ||
- IsEqualGUID(&IID_IEnumSTATSTG, riid))
+ *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
+ if (*result)
{
- *ppvObject = &This->IEnumSTATSTG_iface;
- IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
- return S_OK;
- }
-
- return E_NOINTERFACE;
-}
-
-static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
- IEnumSTATSTG* iface)
-{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- return InterlockedIncrement(&This->ref);
-}
+ IStorage *scratch;
-static ULONG WINAPI IEnumSTATSTGImpl_Release(
- IEnumSTATSTG* iface)
-{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
- ULONG newRef;
+ /* This is OK because the property set storage functions use the IStorage functions. */
+ (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
+ (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
- newRef = InterlockedDecrement(&This->ref);
+ list_init(&(*result)->base.strmHead);
- if (newRef==0)
- {
- IEnumSTATSTGImpl_Destroy(This);
- }
+ list_init(&(*result)->base.storageHead);
- return newRef;
-}
+ (*result)->base.ref = 1;
-static HRESULT IEnumSTATSTGImpl_GetNextRef(
- IEnumSTATSTGImpl* This,
- DirRef *ref)
-{
- DirRef result = DIRENTRY_NULL;
- DirRef searchNode;
- DirEntry entry;
- HRESULT hr;
- WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
+ (*result)->base.openFlags = parentStorage->openFlags;
- hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
- This->parentStorage->storageDirEntry, &entry);
- searchNode = entry.dirRootEntry;
+ /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+ StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
- while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
- {
- hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
+ /* Create a new temporary storage to act as the scratch file. */
+ hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
+ 0, &scratch);
+ (*result)->scratch = impl_from_IStorage(scratch);
if (SUCCEEDED(hr))
{
- LONG diff = entryNameCmp( entry.name, This->name);
+ ULONG num_entries = 20;
- if (diff <= 0)
- {
- searchNode = entry.rightChild;
- }
- else
- {
- result = searchNode;
- memcpy(result_name, entry.name, sizeof(result_name));
- searchNode = entry.leftChild;
- }
- }
- }
+ (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
+ (*result)->entries_size = num_entries;
+ (*result)->firstFreeEntry = 0;
- if (SUCCEEDED(hr))
- {
- *ref = result;
- if (result != DIRENTRY_NULL)
- memcpy(This->name, result_name, sizeof(result_name));
- }
+ if ((*result)->entries)
+ {
+ /* parentStorage already has 1 reference, which we take over here. */
+ (*result)->transactedParent = parentStorage;
- return hr;
-}
+ parentStorage->transactedChild = &(*result)->base;
-static HRESULT WINAPI IEnumSTATSTGImpl_Next(
- IEnumSTATSTG* iface,
- ULONG celt,
- STATSTG* rgelt,
- ULONG* pceltFetched)
-{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
+ }
+ else
+ {
+ IStorage_Release(scratch);
- DirEntry currentEntry;
- STATSTG* currentReturnStruct = rgelt;
- ULONG objectFetched = 0;
- DirRef currentSearchNode;
- HRESULT hr=S_OK;
+ hr = E_OUTOFMEMORY;
+ }
+ }
- if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
- return E_INVALIDARG;
+ if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
- if (This->parentStorage->reverted)
- return STG_E_REVERTED;
+ return hr;
+ }
+ else
+ return E_OUTOFMEMORY;
+}
- /*
- * To avoid the special case, get another pointer to a ULONG value if
- * the caller didn't supply one.
- */
- if (pceltFetched==0)
- pceltFetched = &objectFetched;
- /*
- * Start the iteration, we will iterate until we hit the end of the
- * linked list or until we hit the number of items to iterate through
- */
- *pceltFetched = 0;
+/************************************************************************
+ * TransactedSharedImpl implementation
+ ***********************************************************************/
- while ( *pceltFetched < celt )
+static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
+{
+ if (!This->reverted)
{
- hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
-
- if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
- break;
-
- /*
- * Read the entry from the storage.
- */
- StorageBaseImpl_ReadDirEntry(This->parentStorage,
- currentSearchNode,
- ¤tEntry);
+ TRACE("Storage invalidated (stg=%p)\n", This);
- /*
- * Copy the information to the return buffer.
- */
- StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
- currentReturnStruct,
- ¤tEntry,
- STATFLAG_DEFAULT);
+ This->reverted = TRUE;
- /*
- * Step to the next item in the iteration
- */
- (*pceltFetched)++;
- currentReturnStruct++;
+ StorageBaseImpl_DeleteAll(This);
}
+}
- if (SUCCEEDED(hr) && *pceltFetched != celt)
- hr = S_FALSE;
+static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
- return hr;
+ TransactedSharedImpl_Invalidate(&This->base);
+ IStorage_Release(&This->transactedParent->IStorage_iface);
+ IStorage_Release(&This->scratch->base.IStorage_iface);
+ HeapFree(GetProcessHeap(), 0, This);
}
+static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
+{
+ /* We only need to flush when committing. */
+ return S_OK;
+}
-static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
- IEnumSTATSTG* iface,
- ULONG celt)
+static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
- ULONG objectFetched = 0;
- DirRef currentSearchNode;
- HRESULT hr=S_OK;
+ return StorageBaseImpl_GetFilename(This->transactedParent, result);
+}
- if (This->parentStorage->reverted)
- return STG_E_REVERTED;
+static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
+ const DirEntry *newData, DirRef *index)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- while ( (objectFetched < celt) )
- {
- hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
+ return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
+ newData, index);
+}
- if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
- break;
+static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
+ DirRef index, const DirEntry *data)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- objectFetched++;
- }
+ return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
+ index, data);
+}
- if (SUCCEEDED(hr) && objectFetched != celt)
- return S_FALSE;
+static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
+ DirRef index, DirEntry *data)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- return hr;
+ return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
+ index, data);
}
-static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
- IEnumSTATSTG* iface)
+static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
+ DirRef index)
{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- if (This->parentStorage->reverted)
- return STG_E_REVERTED;
+ return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
+ index);
+}
- This->name[0] = 0;
+static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- return S_OK;
+ return StorageBaseImpl_StreamReadAt(&This->scratch->base,
+ index, offset, size, buffer, bytesRead);
}
-static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
- IEnumSTATSTG* iface,
- IEnumSTATSTG** ppenum)
+static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
- IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
- IEnumSTATSTGImpl* newClone;
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- if (This->parentStorage->reverted)
- return STG_E_REVERTED;
+ return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
+ index, offset, size, buffer, bytesWritten);
+}
- if (ppenum==0)
- return E_INVALIDARG;
+static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER newsize)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
- This->storageDirEntry);
- if (!newClone)
- {
- *ppenum = NULL;
- return E_OUTOFMEMORY;
- }
+ return StorageBaseImpl_StreamSetSize(&This->scratch->base,
+ index, newsize);
+}
- /*
- * The new clone enumeration must point to the same current node as
- * the old one.
- */
- memcpy(newClone->name, This->name, sizeof(newClone->name));
+static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
+ DirRef dst, DirRef src)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*) base;
- *ppenum = &newClone->IEnumSTATSTG_iface;
+ return StorageBaseImpl_StreamLink(&This->scratch->base,
+ dst, src);
+}
- return S_OK;
+static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
+ ULONG* result, BOOL refresh)
+{
+ return E_NOTIMPL;
}
-/*
- * Virtual function table for the IEnumSTATSTGImpl class.
- */
-static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
+static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
+ ULONG value)
{
- IEnumSTATSTGImpl_QueryInterface,
- IEnumSTATSTGImpl_AddRef,
- IEnumSTATSTGImpl_Release,
- IEnumSTATSTGImpl_Next,
- IEnumSTATSTGImpl_Skip,
- IEnumSTATSTGImpl_Reset,
- IEnumSTATSTGImpl_Clone
-};
+ return E_NOTIMPL;
+}
-/******************************************************************************
-** IEnumSTATSTGImpl implementation
-*/
+static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+ return E_NOTIMPL;
+}
-static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
- StorageBaseImpl* parentStorage,
- DirRef storageDirEntry)
+static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
{
- IEnumSTATSTGImpl* newEnumeration;
+ return E_NOTIMPL;
+}
- newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
+static HRESULT WINAPI TransactedSharedImpl_Commit(
+ IStorage* iface,
+ DWORD grfCommitFlags) /* [in] */
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+ DirRef new_storage_ref, prev_storage_ref;
+ DirEntry src_data, dst_data;
+ HRESULT hr;
+ ULONG transactionSig;
- if (newEnumeration)
+ TRACE("(%p,%x)\n", iface, grfCommitFlags);
+
+ /* Cannot commit a read-only transacted storage */
+ if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
+ return STG_E_ACCESSDENIED;
+
+ hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
+ if (hr == E_NOTIMPL) hr = S_OK;
+ if (SUCCEEDED(hr))
{
- newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
- newEnumeration->ref = 1;
- newEnumeration->name[0] = 0;
+ hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
+ hr = STG_E_NOTCURRENT;
- /*
- * We want to nail-down the reference to the storage in case the
- * enumeration out-lives the storage in the client application.
- */
- newEnumeration->parentStorage = parentStorage;
- IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
+ }
+ else if (hr == E_NOTIMPL)
+ hr = S_OK;
- newEnumeration->storageDirEntry = storageDirEntry;
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
+
+ /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_Flush(This->transactedParent);
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+
+ if (SUCCEEDED(hr))
+ {
+ prev_storage_ref = dst_data.dirRootEntry;
+ dst_data.dirRootEntry = new_storage_ref;
+ dst_data.clsid = src_data.clsid;
+ dst_data.ctime = src_data.ctime;
+ dst_data.mtime = src_data.mtime;
+ hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ /* Try to flush after updating the root storage, but if the flush fails, keep
+ * going, on the theory that it'll either succeed later or the subsequent
+ * writes will fail. */
+ StorageBaseImpl_Flush(This->transactedParent);
+
+ hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
+ }
+
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_Flush(This->transactedParent);
+
+ StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+
+ if (SUCCEEDED(hr))
+ hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
+
+ if (SUCCEEDED(hr))
+ {
+ This->lastTransactionSig = transactionSig+1;
+ }
}
- return newEnumeration;
+ return hr;
}
-/*
- * Virtual function table for the StorageInternalImpl class.
- */
-static const IStorageVtbl StorageInternalImpl_Vtbl =
+static HRESULT WINAPI TransactedSharedImpl_Revert(
+ IStorage* iface)
+{
+ TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+
+ TRACE("(%p)\n", iface);
+
+ /* Destroy the open objects. */
+ StorageBaseImpl_DeleteAll(&This->base);
+
+ return IStorage_Revert(&This->scratch->base.IStorage_iface);
+}
+
+static const IStorageVtbl TransactedSharedImpl_Vtbl =
{
StorageBaseImpl_QueryInterface,
StorageBaseImpl_AddRef,
@@ -6579,8 +6574,8 @@ static const IStorageVtbl StorageInternalImpl_Vtbl =
StorageBaseImpl_OpenStorage,
StorageBaseImpl_CopyTo,
StorageBaseImpl_MoveElementTo,
- StorageInternalImpl_Commit,
- StorageInternalImpl_Revert,
+ TransactedSharedImpl_Commit,
+ TransactedSharedImpl_Revert,
StorageBaseImpl_EnumElements,
StorageBaseImpl_DestroyElement,
StorageBaseImpl_RenameElement,
@@ -6590,75 +6585,156 @@ static const IStorageVtbl StorageInternalImpl_Vtbl =
StorageBaseImpl_Stat
};
-static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
+static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
{
- StorageInternalImpl_Destroy,
- StorageInternalImpl_Invalidate,
- StorageInternalImpl_Flush,
- StorageInternalImpl_GetFilename,
- StorageInternalImpl_CreateDirEntry,
- StorageInternalImpl_WriteDirEntry,
- StorageInternalImpl_ReadDirEntry,
- StorageInternalImpl_DestroyDirEntry,
- StorageInternalImpl_StreamReadAt,
- StorageInternalImpl_StreamWriteAt,
- StorageInternalImpl_StreamSetSize,
- StorageInternalImpl_StreamLink,
- StorageInternalImpl_GetTransactionSig,
- StorageInternalImpl_SetTransactionSig,
- StorageInternalImpl_LockTransaction,
- StorageInternalImpl_UnlockTransaction
+ TransactedSharedImpl_Destroy,
+ TransactedSharedImpl_Invalidate,
+ TransactedSharedImpl_Flush,
+ TransactedSharedImpl_GetFilename,
+ TransactedSharedImpl_CreateDirEntry,
+ TransactedSharedImpl_WriteDirEntry,
+ TransactedSharedImpl_ReadDirEntry,
+ TransactedSharedImpl_DestroyDirEntry,
+ TransactedSharedImpl_StreamReadAt,
+ TransactedSharedImpl_StreamWriteAt,
+ TransactedSharedImpl_StreamSetSize,
+ TransactedSharedImpl_StreamLink,
+ TransactedSharedImpl_GetTransactionSig,
+ TransactedSharedImpl_SetTransactionSig,
+ TransactedSharedImpl_LockTransaction,
+ TransactedSharedImpl_UnlockTransaction
};
-/******************************************************************************
-** StorageInternalImpl implementation
-*/
+static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
+ TransactedSharedImpl** result)
+{
+ HRESULT hr;
-static StorageInternalImpl* StorageInternalImpl_Construct(
- StorageBaseImpl* parentStorage,
- DWORD openFlags,
- DirRef storageDirEntry)
-{
- StorageInternalImpl* newStorage;
+ *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
+ if (*result)
+ {
+ IStorage *scratch;
- newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
+ (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
- if (newStorage!=0)
- {
- list_init(&newStorage->base.strmHead);
+ /* This is OK because the property set storage functions use the IStorage functions. */
+ (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
+ (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
- list_init(&newStorage->base.storageHead);
+ list_init(&(*result)->base.strmHead);
- /*
- * Initialize the virtual function table.
- */
- newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
- newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
- newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
- newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
+ list_init(&(*result)->base.storageHead);
- newStorage->base.reverted = FALSE;
+ (*result)->base.ref = 1;
- newStorage->base.ref = 1;
+ (*result)->base.openFlags = parentStorage->openFlags;
- newStorage->parentStorage = parentStorage;
+ hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
- /*
- * Keep a reference to the directory entry of this storage
- */
- newStorage->base.storageDirEntry = storageDirEntry;
+ if (SUCCEEDED(hr))
+ {
+ STGOPTIONS stgo;
- newStorage->base.create = FALSE;
+ /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+ StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
- return newStorage;
+ stgo.usVersion = 1;
+ stgo.reserved = 0;
+ stgo.ulSectorSize = 4096;
+ stgo.pwcsTemplateFile = NULL;
+
+ /* Create a new temporary storage to act as the scratch file. */
+ hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
+ STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
+ (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
+ parentStorage, parentStorage->storageDirEntry);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = IStorage_Commit(scratch, STGC_DEFAULT);
+
+ (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
+ (*result)->transactedParent = parentStorage;
+ }
+
+ if (FAILED(hr))
+ IStorage_Release(scratch);
+ }
+
+ StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
+ }
+
+ if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+
+ return hr;
+ }
+ else
+ return E_OUTOFMEMORY;
+}
+
+static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
+ BOOL toplevel, StorageBaseImpl** result)
+{
+ static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
+
+ if (parentStorage->openFlags & fixme_flags)
+ {
+ fixme_flags &= ~parentStorage->openFlags;
+ FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
}
- return 0;
+ if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
+ STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
+ STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
+ {
+ /* Need to create a temp file for the snapshot */
+ return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
+ }
+
+ return TransactedSnapshotImpl_Construct(parentStorage,
+ (TransactedSnapshotImpl**)result);
}
-/******************************************************************************
-** StorageUtl implementation
-*/
+static HRESULT Storage_Construct(
+ HANDLE hFile,
+ LPCOLESTR pwcsName,
+ ILockBytes* pLkbyt,
+ DWORD openFlags,
+ BOOL fileBased,
+ BOOL create,
+ ULONG sector_size,
+ StorageBaseImpl** result)
+{
+ StorageImpl *newStorage;
+ StorageBaseImpl *newTransactedStorage;
+ HRESULT hr;
+
+ hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
+ if (FAILED(hr)) goto end;
+
+ if (openFlags & STGM_TRANSACTED)
+ {
+ hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
+ if (FAILED(hr))
+ IStorage_Release(&newStorage->base.IStorage_iface);
+ else
+ *result = newTransactedStorage;
+ }
+ else
+ *result = &newStorage->base;
+
+end:
+ return hr;
+}
+
+
+/************************************************************************
+ * StorageUtl helper functions
+ ***********************************************************************/
void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
{
@@ -6789,9 +6865,40 @@ void StorageUtl_CopyDirEntryToSTATSTG(
destination->reserved = 0;
}
+
+/************************************************************************
+ * BlockChainStream implementation
+ ***********************************************************************/
+
/******************************************************************************
-** BlockChainStream implementation
-*/
+ * BlockChainStream_GetHeadOfChain
+ *
+ * Returns the head of this stream chain.
+ * Some special chains don't have directory entries, their heads are kept in
+ * This->headOfStreamPlaceHolder.
+ *
+ */
+static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
+{
+ DirEntry chainEntry;
+ HRESULT hr;
+
+ if (This->headOfStreamPlaceHolder != 0)
+ return *(This->headOfStreamPlaceHolder);
+
+ if (This->ownerDirEntry != DIRENTRY_NULL)
+ {
+ hr = StorageImpl_ReadDirEntry(
+ This->parentStorage,
+ This->ownerDirEntry,
+ &chainEntry);
+
+ if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
+ return chainEntry.startingBlock;
+ }
+
+ return BLOCK_END_OF_CHAIN;
+}
/* Read and save the index of all blocks in this stream. */
HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
@@ -7006,249 +7113,26 @@ void BlockChainStream_Destroy(BlockChainStream* This)
}
/******************************************************************************
- * BlockChainStream_GetHeadOfChain
- *
- * Returns the head of this stream chain.
- * Some special chains don't have directory entries, their heads are kept in
- * This->headOfStreamPlaceHolder.
- *
- */
-static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
-{
- DirEntry chainEntry;
- HRESULT hr;
-
- if (This->headOfStreamPlaceHolder != 0)
- return *(This->headOfStreamPlaceHolder);
-
- if (This->ownerDirEntry != DIRENTRY_NULL)
- {
- hr = StorageImpl_ReadDirEntry(
- This->parentStorage,
- This->ownerDirEntry,
- &chainEntry);
-
- if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
- return chainEntry.startingBlock;
- }
-
- return BLOCK_END_OF_CHAIN;
-}
-
-/******************************************************************************
- * BlockChainStream_GetCount
- *
- * Returns the number of blocks that comprises this chain.
- * This is not the size of the stream as the last block may not be full!
- */
-static ULONG BlockChainStream_GetCount(BlockChainStream* This)
-{
- return This->numBlocks;
-}
-
-/******************************************************************************
- * BlockChainStream_ReadAt
+ * BlockChainStream_Shrink
*
- * Reads a specified number of bytes from this chain at the specified offset.
- * bytesRead may be NULL.
- * Failure will be returned if the specified number of bytes has not been read.
+ * Shrinks this chain in the big block depot.
*/
-HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
- ULARGE_INTEGER offset,
- ULONG size,
- void* buffer,
- ULONG* bytesRead)
+static BOOL BlockChainStream_Shrink(BlockChainStream* This,
+ ULARGE_INTEGER newSize)
{
- ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
- ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
- ULONG bytesToReadInBuffer;
ULONG blockIndex;
- BYTE* bufferWalker;
- ULARGE_INTEGER stream_size;
- HRESULT hr;
- BlockChainBlock *cachedBlock;
-
- TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
+ ULONG numBlocks;
+ int i;
/*
- * Find the first block in the stream that contains part of the buffer.
+ * Figure out how many blocks are needed to contain the new size
*/
- blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
-
- *bytesRead = 0;
-
- stream_size = BlockChainStream_GetSize(This);
- if (stream_size.QuadPart > offset.QuadPart)
- size = min(stream_size.QuadPart - offset.QuadPart, size);
- else
- return S_OK;
+ numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
- /*
- * Start reading the buffer.
- */
- bufferWalker = buffer;
+ if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
+ numBlocks++;
- while (size > 0)
- {
- ULARGE_INTEGER ulOffset;
- DWORD bytesReadAt;
-
- /*
- * Calculate how many bytes we can copy from this big block.
- */
- bytesToReadInBuffer =
- min(This->parentStorage->bigBlockSize - offsetInBlock, size);
-
- hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
-
- if (FAILED(hr))
- return hr;
-
- if (!cachedBlock)
- {
- /* Not in cache, and we're going to read past the end of the block. */
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
- offsetInBlock;
-
- StorageImpl_ReadAt(This->parentStorage,
- ulOffset,
- bufferWalker,
- bytesToReadInBuffer,
- &bytesReadAt);
- }
- else
- {
- if (!cachedBlock->read)
- {
- ULONG read;
- if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
- return STG_E_READFAULT;
-
- cachedBlock->read = TRUE;
- }
-
- memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
- bytesReadAt = bytesToReadInBuffer;
- }
-
- blockNoInSequence++;
- bufferWalker += bytesReadAt;
- size -= bytesReadAt;
- *bytesRead += bytesReadAt;
- offsetInBlock = 0; /* There is no offset on the next block */
-
- if (bytesToReadInBuffer != bytesReadAt)
- break;
- }
-
- return S_OK;
-}
-
-/******************************************************************************
- * BlockChainStream_WriteAt
- *
- * Writes the specified number of bytes to this chain at the specified offset.
- * Will fail if not all specified number of bytes have been written.
- */
-HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
- ULARGE_INTEGER offset,
- ULONG size,
- const void* buffer,
- ULONG* bytesWritten)
-{
- ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
- ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
- ULONG bytesToWrite;
- ULONG blockIndex;
- const BYTE* bufferWalker;
- HRESULT hr;
- BlockChainBlock *cachedBlock;
-
- *bytesWritten = 0;
- bufferWalker = buffer;
-
- while (size > 0)
- {
- ULARGE_INTEGER ulOffset;
- DWORD bytesWrittenAt;
-
- /*
- * Calculate how many bytes we can copy to this big block.
- */
- bytesToWrite =
- min(This->parentStorage->bigBlockSize - offsetInBlock, size);
-
- hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
-
- /* BlockChainStream_SetSize should have already been called to ensure we have
- * enough blocks in the chain to write into */
- if (FAILED(hr))
- {
- ERR("not enough blocks in chain to write data\n");
- return hr;
- }
-
- if (!cachedBlock)
- {
- /* Not in cache, and we're going to write past the end of the block. */
- ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
- offsetInBlock;
-
- StorageImpl_WriteAt(This->parentStorage,
- ulOffset,
- bufferWalker,
- bytesToWrite,
- &bytesWrittenAt);
- }
- else
- {
- if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
- {
- ULONG read;
- if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
- return STG_E_READFAULT;
- }
-
- memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
- bytesWrittenAt = bytesToWrite;
- cachedBlock->read = TRUE;
- cachedBlock->dirty = TRUE;
- }
-
- blockNoInSequence++;
- bufferWalker += bytesWrittenAt;
- size -= bytesWrittenAt;
- *bytesWritten += bytesWrittenAt;
- offsetInBlock = 0; /* There is no offset on the next block */
-
- if (bytesWrittenAt != bytesToWrite)
- break;
- }
-
- return (size == 0) ? S_OK : STG_E_WRITEFAULT;
-}
-
-/******************************************************************************
- * BlockChainStream_Shrink
- *
- * Shrinks this chain in the big block depot.
- */
-static BOOL BlockChainStream_Shrink(BlockChainStream* This,
- ULARGE_INTEGER newSize)
-{
- ULONG blockIndex;
- ULONG numBlocks;
- int i;
-
- /*
- * Figure out how many blocks are needed to contain the new size
- */
- numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
-
- if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
- numBlocks++;
-
- if (numBlocks)
+ if (numBlocks)
{
/*
* Go to the new end of chain
@@ -7435,36 +7319,6 @@ static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
return TRUE;
}
-/******************************************************************************
- * BlockChainStream_SetSize
- *
- * Sets the size of this stream. The big block depot will be updated.
- * The file will grow if we grow the chain.
- *
- * TODO: Free the actual blocks in the file when we shrink the chain.
- * Currently, the blocks are still in the file. So the file size
- * doesn't shrink even if we shrink streams.
- */
-BOOL BlockChainStream_SetSize(
- BlockChainStream* This,
- ULARGE_INTEGER newSize)
-{
- ULARGE_INTEGER size = BlockChainStream_GetSize(This);
-
- if (newSize.QuadPart == size.QuadPart)
- return TRUE;
-
- if (newSize.QuadPart < size.QuadPart)
- {
- BlockChainStream_Shrink(This, newSize);
- }
- else
- {
- BlockChainStream_Enlarge(This, newSize);
- }
-
- return TRUE;
-}
/******************************************************************************
* BlockChainStream_GetSize
@@ -7505,68 +7359,282 @@ static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
}
/******************************************************************************
-** SmallBlockChainStream implementation
-*/
-
-SmallBlockChainStream* SmallBlockChainStream_Construct(
- StorageImpl* parentStorage,
- ULONG* headOfStreamPlaceHolder,
- DirRef dirEntry)
-{
- SmallBlockChainStream* newStream;
-
- newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
-
- newStream->parentStorage = parentStorage;
- newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
- newStream->ownerDirEntry = dirEntry;
-
- return newStream;
-}
-
-void SmallBlockChainStream_Destroy(
- SmallBlockChainStream* This)
-{
- HeapFree(GetProcessHeap(), 0, This);
-}
-
-/******************************************************************************
- * SmallBlockChainStream_GetHeadOfChain
+ * BlockChainStream_SetSize
*
- * Returns the head of this chain of small blocks.
+ * Sets the size of this stream. The big block depot will be updated.
+ * The file will grow if we grow the chain.
+ *
+ * TODO: Free the actual blocks in the file when we shrink the chain.
+ * Currently, the blocks are still in the file. So the file size
+ * doesn't shrink even if we shrink streams.
*/
-static ULONG SmallBlockChainStream_GetHeadOfChain(
- SmallBlockChainStream* This)
+BOOL BlockChainStream_SetSize(
+ BlockChainStream* This,
+ ULARGE_INTEGER newSize)
{
- DirEntry chainEntry;
- HRESULT hr;
+ ULARGE_INTEGER size = BlockChainStream_GetSize(This);
- if (This->headOfStreamPlaceHolder != NULL)
- return *(This->headOfStreamPlaceHolder);
+ if (newSize.QuadPart == size.QuadPart)
+ return TRUE;
- if (This->ownerDirEntry)
+ if (newSize.QuadPart < size.QuadPart)
{
- hr = StorageImpl_ReadDirEntry(
- This->parentStorage,
- This->ownerDirEntry,
- &chainEntry);
-
- if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
- return chainEntry.startingBlock;
+ BlockChainStream_Shrink(This, newSize);
+ }
+ else
+ {
+ BlockChainStream_Enlarge(This, newSize);
}
- return BLOCK_END_OF_CHAIN;
+ return TRUE;
}
/******************************************************************************
- * SmallBlockChainStream_GetNextBlockInChain
- *
- * Returns the index of the next small block in this chain.
+ * BlockChainStream_ReadAt
*
- * Return Values:
- * - BLOCK_END_OF_CHAIN: end of this chain
- * - BLOCK_UNUSED: small block 'blockIndex' is free
- */
+ * Reads a specified number of bytes from this chain at the specified offset.
+ * bytesRead may be NULL.
+ * Failure will be returned if the specified number of bytes has not been read.
+ */
+HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
+ ULARGE_INTEGER offset,
+ ULONG size,
+ void* buffer,
+ ULONG* bytesRead)
+{
+ ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+ ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
+ ULONG bytesToReadInBuffer;
+ ULONG blockIndex;
+ BYTE* bufferWalker;
+ ULARGE_INTEGER stream_size;
+ HRESULT hr;
+ BlockChainBlock *cachedBlock;
+
+ TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
+
+ /*
+ * Find the first block in the stream that contains part of the buffer.
+ */
+ blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
+
+ *bytesRead = 0;
+
+ stream_size = BlockChainStream_GetSize(This);
+ if (stream_size.QuadPart > offset.QuadPart)
+ size = min(stream_size.QuadPart - offset.QuadPart, size);
+ else
+ return S_OK;
+
+ /*
+ * Start reading the buffer.
+ */
+ bufferWalker = buffer;
+
+ while (size > 0)
+ {
+ ULARGE_INTEGER ulOffset;
+ DWORD bytesReadAt;
+
+ /*
+ * Calculate how many bytes we can copy from this big block.
+ */
+ bytesToReadInBuffer =
+ min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+ hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
+
+ if (FAILED(hr))
+ return hr;
+
+ if (!cachedBlock)
+ {
+ /* Not in cache, and we're going to read past the end of the block. */
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+ offsetInBlock;
+
+ StorageImpl_ReadAt(This->parentStorage,
+ ulOffset,
+ bufferWalker,
+ bytesToReadInBuffer,
+ &bytesReadAt);
+ }
+ else
+ {
+ if (!cachedBlock->read)
+ {
+ ULONG read;
+ if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
+ return STG_E_READFAULT;
+
+ cachedBlock->read = TRUE;
+ }
+
+ memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
+ bytesReadAt = bytesToReadInBuffer;
+ }
+
+ blockNoInSequence++;
+ bufferWalker += bytesReadAt;
+ size -= bytesReadAt;
+ *bytesRead += bytesReadAt;
+ offsetInBlock = 0; /* There is no offset on the next block */
+
+ if (bytesToReadInBuffer != bytesReadAt)
+ break;
+ }
+
+ return S_OK;
+}
+
+/******************************************************************************
+ * BlockChainStream_WriteAt
+ *
+ * Writes the specified number of bytes to this chain at the specified offset.
+ * Will fail if not all specified number of bytes have been written.
+ */
+HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
+ ULARGE_INTEGER offset,
+ ULONG size,
+ const void* buffer,
+ ULONG* bytesWritten)
+{
+ ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+ ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
+ ULONG bytesToWrite;
+ ULONG blockIndex;
+ const BYTE* bufferWalker;
+ HRESULT hr;
+ BlockChainBlock *cachedBlock;
+
+ *bytesWritten = 0;
+ bufferWalker = buffer;
+
+ while (size > 0)
+ {
+ ULARGE_INTEGER ulOffset;
+ DWORD bytesWrittenAt;
+
+ /*
+ * Calculate how many bytes we can copy to this big block.
+ */
+ bytesToWrite =
+ min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+ hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
+
+ /* BlockChainStream_SetSize should have already been called to ensure we have
+ * enough blocks in the chain to write into */
+ if (FAILED(hr))
+ {
+ ERR("not enough blocks in chain to write data\n");
+ return hr;
+ }
+
+ if (!cachedBlock)
+ {
+ /* Not in cache, and we're going to write past the end of the block. */
+ ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+ offsetInBlock;
+
+ StorageImpl_WriteAt(This->parentStorage,
+ ulOffset,
+ bufferWalker,
+ bytesToWrite,
+ &bytesWrittenAt);
+ }
+ else
+ {
+ if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
+ {
+ ULONG read;
+ if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
+ return STG_E_READFAULT;
+ }
+
+ memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
+ bytesWrittenAt = bytesToWrite;
+ cachedBlock->read = TRUE;
+ cachedBlock->dirty = TRUE;
+ }
+
+ blockNoInSequence++;
+ bufferWalker += bytesWrittenAt;
+ size -= bytesWrittenAt;
+ *bytesWritten += bytesWrittenAt;
+ offsetInBlock = 0; /* There is no offset on the next block */
+
+ if (bytesWrittenAt != bytesToWrite)
+ break;
+ }
+
+ return (size == 0) ? S_OK : STG_E_WRITEFAULT;
+}
+
+
+/************************************************************************
+ * SmallBlockChainStream implementation
+ ***********************************************************************/
+
+SmallBlockChainStream* SmallBlockChainStream_Construct(
+ StorageImpl* parentStorage,
+ ULONG* headOfStreamPlaceHolder,
+ DirRef dirEntry)
+{
+ SmallBlockChainStream* newStream;
+
+ newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
+
+ newStream->parentStorage = parentStorage;
+ newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
+ newStream->ownerDirEntry = dirEntry;
+
+ return newStream;
+}
+
+void SmallBlockChainStream_Destroy(
+ SmallBlockChainStream* This)
+{
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_GetHeadOfChain
+ *
+ * Returns the head of this chain of small blocks.
+ */
+static ULONG SmallBlockChainStream_GetHeadOfChain(
+ SmallBlockChainStream* This)
+{
+ DirEntry chainEntry;
+ HRESULT hr;
+
+ if (This->headOfStreamPlaceHolder != NULL)
+ return *(This->headOfStreamPlaceHolder);
+
+ if (This->ownerDirEntry)
+ {
+ hr = StorageImpl_ReadDirEntry(
+ This->parentStorage,
+ This->ownerDirEntry,
+ &chainEntry);
+
+ if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
+ return chainEntry.startingBlock;
+ }
+
+ return BLOCK_END_OF_CHAIN;
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_GetNextBlockInChain
+ *
+ * Returns the index of the next small block in this chain.
+ *
+ * Return Values:
+ * - BLOCK_END_OF_CHAIN: end of this chain
+ * - BLOCK_UNUSED: small block 'blockIndex' is free
+ */
static HRESULT SmallBlockChainStream_GetNextBlockInChain(
SmallBlockChainStream* This,
ULONG blockIndex,
@@ -8203,6 +8271,11 @@ static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
return chainEntry.size;
}
+
+/************************************************************************
+ * Miscellaneous storage functions
+ ***********************************************************************/
+
static HRESULT create_storagefile(
LPCOLESTR pwcsName,
DWORD grfMode,
@@ -8385,18 +8458,18 @@ HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD st
if (stgfmt != STGFMT_FILE && grfAttrs != 0)
{
ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
- return STG_E_INVALIDPARAMETER;
+ return STG_E_INVALIDPARAMETER;
}
if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
{
ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
- return STG_E_INVALIDPARAMETER;
+ return STG_E_INVALIDPARAMETER;
}
if (stgfmt == STGFMT_FILE)
{
- ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
+ ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
return STG_E_INVALIDPARAMETER;
}
@@ -8437,15 +8510,15 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf
if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
{
ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
- return STG_E_INVALIDPARAMETER;
+ return STG_E_INVALIDPARAMETER;
}
switch (stgfmt)
{
case STGFMT_FILE:
- ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
+ ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
return STG_E_INVALIDPARAMETER;
-
+
case STGFMT_STORAGE:
break;
@@ -8453,7 +8526,7 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf
if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
{
ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
- return STG_E_INVALIDPARAMETER;
+ return STG_E_INVALIDPARAMETER;
}
FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
break;
@@ -8694,393 +8767,647 @@ HRESULT WINAPI StgCreateDocfileOnILockBytes(
return hr;
}
- *ppstgOpen = &newStorage->IStorage_iface;
+ *ppstgOpen = &newStorage->IStorage_iface;
+
+ return hr;
+}
+
+/******************************************************************************
+ * StgOpenStorageOnILockBytes [OLE32.@]
+ */
+HRESULT WINAPI StgOpenStorageOnILockBytes(
+ ILockBytes *plkbyt,
+ IStorage *pstgPriority,
+ DWORD grfMode,
+ SNB snbExclude,
+ DWORD reserved,
+ IStorage **ppstgOpen)
+{
+ StorageBaseImpl* newStorage = 0;
+ HRESULT hr = S_OK;
+
+ if ((plkbyt == 0) || (ppstgOpen == 0))
+ return STG_E_INVALIDPOINTER;
+
+ if ( FAILED( validateSTGM(grfMode) ))
+ return STG_E_INVALIDFLAG;
+
+ *ppstgOpen = 0;
+
+ /*
+ * Allocate and initialize the new IStorage object.
+ */
+ hr = Storage_Construct(
+ 0,
+ 0,
+ plkbyt,
+ grfMode,
+ FALSE,
+ FALSE,
+ 512,
+ &newStorage);
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ *ppstgOpen = &newStorage->IStorage_iface;
+
+ return hr;
+}
+
+/******************************************************************************
+ * StgSetTimes [ole32.@]
+ * StgSetTimes [OLE32.@]
+ *
+ *
+ */
+HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
+ FILETIME const *patime, FILETIME const *pmtime)
+{
+ IStorage *stg = NULL;
+ HRESULT r;
+
+ TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
+
+ r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
+ 0, 0, &stg);
+ if( SUCCEEDED(r) )
+ {
+ r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
+ IStorage_Release(stg);
+ }
+
+ return r;
+}
+
+/******************************************************************************
+ * StgIsStorageILockBytes [OLE32.@]
+ *
+ * Determines if the ILockBytes contains a storage object.
+ */
+HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
+{
+ BYTE sig[sizeof(STORAGE_magic)];
+ ULARGE_INTEGER offset;
+ ULONG read = 0;
+
+ offset.u.HighPart = 0;
+ offset.u.LowPart = 0;
+
+ ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
+
+ if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+/******************************************************************************
+ * WriteClassStg [OLE32.@]
+ *
+ * This method will store the specified CLSID in the specified storage object
+ */
+HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
+{
+ if(!pStg)
+ return E_INVALIDARG;
+
+ if(!rclsid)
+ return STG_E_INVALIDPOINTER;
+
+ return IStorage_SetClass(pStg, rclsid);
+}
+
+/***********************************************************************
+ * ReadClassStg (OLE32.@)
+ *
+ * This method reads the CLSID previously written to a storage object with
+ * the WriteClassStg.
+ *
+ * PARAMS
+ * pstg [I] IStorage pointer
+ * pclsid [O] Pointer to where the CLSID is written
+ *
+ * RETURNS
+ * Success: S_OK.
+ * Failure: HRESULT code.
+ */
+HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
+
+ STATSTG pstatstg;
+ HRESULT hRes;
+
+ TRACE("(%p, %p)\n", pstg, pclsid);
+
+ if(!pstg || !pclsid)
+ return E_INVALIDARG;
+
+ /*
+ * read a STATSTG structure (contains the clsid) from the storage
+ */
+ hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
+
+ if(SUCCEEDED(hRes))
+ *pclsid=pstatstg.clsid;
+
+ return hRes;
+}
+
+/***********************************************************************
+ * OleLoadFromStream (OLE32.@)
+ *
+ * This function loads an object from stream
+ */
+HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
+{
+ CLSID clsid;
+ HRESULT res;
+ LPPERSISTSTREAM xstm;
+
+ TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
+
+ res=ReadClassStm(pStm,&clsid);
+ if (FAILED(res))
+ return res;
+ res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
+ if (FAILED(res))
+ return res;
+ res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
+ if (FAILED(res)) {
+ IUnknown_Release((IUnknown*)*ppvObj);
+ return res;
+ }
+ res=IPersistStream_Load(xstm,pStm);
+ IPersistStream_Release(xstm);
+ /* FIXME: all refcounts ok at this point? I think they should be:
+ * pStm : unchanged
+ * ppvObj : 1
+ * xstm : 0 (released)
+ */
+ return res;
+}
+
+/***********************************************************************
+ * OleSaveToStream (OLE32.@)
+ *
+ * This function saves an object with the IPersistStream interface on it
+ * to the specified stream.
+ */
+HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
+{
+
+ CLSID clsid;
+ HRESULT res;
+
+ TRACE("(%p,%p)\n",pPStm,pStm);
+
+ res=IPersistStream_GetClassID(pPStm,&clsid);
+
+ if (SUCCEEDED(res)){
+
+ res=WriteClassStm(pStm,&clsid);
+
+ if (SUCCEEDED(res))
+
+ res=IPersistStream_Save(pPStm,pStm,TRUE);
+ }
+
+ TRACE("Finished Save\n");
+ return res;
+}
+
+/*************************************************************************
+ * STORAGE_CreateOleStream [Internal]
+ *
+ * Creates the "\001OLE" stream in the IStorage if necessary.
+ *
+ * PARAMS
+ * storage [I] Dest storage to create the stream in
+ * flags [I] flags to be set for newly created stream
+ *
+ * RETURNS
+ * HRESULT return value
+ *
+ * NOTES
+ *
+ * This stream is still unknown, MS Word seems to have extra data
+ * but since the data is stored in the OLESTREAM there should be
+ * no need to recreate the stream. If the stream is manually
+ * deleted it will create it with this default data.
+ *
+ */
+HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
+{
+ static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
+ static const DWORD version_magic = 0x02000001;
+ IStream *stream;
+ HRESULT hr;
+
+ hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
+ if (hr == S_OK)
+ {
+ struct empty_1ole_stream {
+ DWORD version_magic;
+ DWORD flags;
+ DWORD update_options;
+ DWORD reserved;
+ DWORD mon_stream_size;
+ };
+ struct empty_1ole_stream stream_data;
+
+ stream_data.version_magic = version_magic;
+ stream_data.flags = flags;
+ stream_data.update_options = 0;
+ stream_data.reserved = 0;
+ stream_data.mon_stream_size = 0;
+
+ hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
+ IStream_Release(stream);
+ }
+
+ return hr;
+}
+
+/* write a string to a stream, preceded by its length */
+static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
+{
+ HRESULT r;
+ LPSTR str;
+ DWORD len = 0;
+
+ if( string )
+ len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
+ r = IStream_Write( stm, &len, sizeof(len), NULL);
+ if( FAILED( r ) )
+ return r;
+ if(len == 0)
+ return r;
+ str = CoTaskMemAlloc( len );
+ WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
+ r = IStream_Write( stm, str, len, NULL);
+ CoTaskMemFree( str );
+ return r;
+}
+
+/* read a string preceded by its length from a stream */
+static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
+{
+ HRESULT r;
+ DWORD len, count = 0;
+ LPSTR str;
+ LPWSTR wstr;
+
+ r = IStream_Read( stm, &len, sizeof(len), &count );
+ if( FAILED( r ) )
+ return r;
+ if( count != sizeof(len) )
+ return E_OUTOFMEMORY;
+
+ TRACE("%d bytes\n",len);
+
+ str = CoTaskMemAlloc( len );
+ if( !str )
+ return E_OUTOFMEMORY;
+ count = 0;
+ r = IStream_Read( stm, str, len, &count );
+ if( FAILED( r ) )
+ return r;
+ if( count != len )
+ {
+ CoTaskMemFree( str );
+ return E_OUTOFMEMORY;
+ }
+
+ TRACE("Read string %s\n",debugstr_an(str,len));
+
+ len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
+ wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
+ if( wstr )
+ {
+ MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
+ wstr[len] = 0;
+ }
+ CoTaskMemFree( str );
+
+ *string = wstr;
- return hr;
+ return r;
}
-/******************************************************************************
- * StgOpenStorageOnILockBytes [OLE32.@]
- */
-HRESULT WINAPI StgOpenStorageOnILockBytes(
- ILockBytes *plkbyt,
- IStorage *pstgPriority,
- DWORD grfMode,
- SNB snbExclude,
- DWORD reserved,
- IStorage **ppstgOpen)
+
+static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
+ LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
{
- StorageBaseImpl* newStorage = 0;
- HRESULT hr = S_OK;
+ IStream *pstm;
+ HRESULT r = S_OK;
+ static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
- if ((plkbyt == 0) || (ppstgOpen == 0))
- return STG_E_INVALIDPOINTER;
+ static const BYTE unknown1[12] =
+ { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF};
+ static const BYTE unknown2[16] =
+ { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- if ( FAILED( validateSTGM(grfMode) ))
- return STG_E_INVALIDFLAG;
+ TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
+ debugstr_w(lpszUserType), debugstr_w(szClipName),
+ debugstr_w(szProgIDName));
- *ppstgOpen = 0;
+ /* Create a CompObj stream */
+ r = IStorage_CreateStream(pstg, szwStreamName,
+ STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
+ if( FAILED (r) )
+ return r;
- /*
- * Allocate and initialize the new IStorage object.
- */
- hr = Storage_Construct(
- 0,
- 0,
- plkbyt,
- grfMode,
- FALSE,
- FALSE,
- 512,
- &newStorage);
+ /* Write CompObj Structure to stream */
+ r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
- if (FAILED(hr))
- {
- return hr;
- }
+ if( SUCCEEDED( r ) )
+ r = WriteClassStm( pstm, clsid );
- *ppstgOpen = &newStorage->IStorage_iface;
+ if( SUCCEEDED( r ) )
+ r = STREAM_WriteString( pstm, lpszUserType );
+ if( SUCCEEDED( r ) )
+ r = STREAM_WriteString( pstm, szClipName );
+ if( SUCCEEDED( r ) )
+ r = STREAM_WriteString( pstm, szProgIDName );
+ if( SUCCEEDED( r ) )
+ r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
- return hr;
+ IStream_Release( pstm );
+
+ return r;
}
-/******************************************************************************
- * StgSetTimes [ole32.@]
- * StgSetTimes [OLE32.@]
- *
- *
+/***********************************************************************
+ * WriteFmtUserTypeStg (OLE32.@)
*/
-HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
- FILETIME const *patime, FILETIME const *pmtime)
+HRESULT WINAPI WriteFmtUserTypeStg(
+ LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
{
- IStorage *stg = NULL;
- HRESULT r;
-
- TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
-
- r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
- 0, 0, &stg);
- if( SUCCEEDED(r) )
- {
- r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
- IStorage_Release(stg);
- }
+ STATSTG stat;
+ HRESULT r;
+ WCHAR szwClipName[0x40];
+ CLSID clsid;
+ LPWSTR wstrProgID = NULL;
+ DWORD n;
- return r;
-}
+ TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
-/******************************************************************************
- * StgIsStorageILockBytes [OLE32.@]
- *
- * Determines if the ILockBytes contains a storage object.
- */
-HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
-{
- BYTE sig[sizeof(STORAGE_magic)];
- ULARGE_INTEGER offset;
- ULONG read = 0;
+ /* get the clipboard format name */
+ if( cf )
+ {
+ n = GetClipboardFormatNameW( cf, szwClipName,
+ sizeof(szwClipName)/sizeof(szwClipName[0]) );
+ szwClipName[n]=0;
+ }
- offset.u.HighPart = 0;
- offset.u.LowPart = 0;
+ TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
- ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
+ r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
+ if(SUCCEEDED(r))
+ clsid = stat.clsid;
+ else
+ clsid = CLSID_NULL;
- if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
- return S_OK;
+ ProgIDFromCLSID(&clsid, &wstrProgID);
- return S_FALSE;
-}
+ TRACE("progid is %s\n",debugstr_w(wstrProgID));
-/******************************************************************************
- * WriteClassStg [OLE32.@]
- *
- * This method will store the specified CLSID in the specified storage object
- */
-HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
-{
- if(!pStg)
- return E_INVALIDARG;
+ r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
+ cf ? szwClipName : NULL, wstrProgID );
- if(!rclsid)
- return STG_E_INVALIDPOINTER;
+ CoTaskMemFree(wstrProgID);
- return IStorage_SetClass(pStg, rclsid);
+ return r;
}
-/***********************************************************************
- * ReadClassStg (OLE32.@)
- *
- * This method reads the CLSID previously written to a storage object with
- * the WriteClassStg.
- *
- * PARAMS
- * pstg [I] IStorage pointer
- * pclsid [O] Pointer to where the CLSID is written
- *
- * RETURNS
- * Success: S_OK.
- * Failure: HRESULT code.
+
+/******************************************************************************
+ * ReadFmtUserTypeStg [OLE32.@]
*/
-HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
+HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
+{
+ HRESULT r;
+ IStream *stm = 0;
+ static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
+ unsigned char unknown1[12];
+ unsigned char unknown2[16];
+ DWORD count;
+ LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
+ CLSID clsid;
- STATSTG pstatstg;
- HRESULT hRes;
+ TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
- TRACE("(%p, %p)\n", pstg, pclsid);
+ r = IStorage_OpenStream( pstg, szCompObj, NULL,
+ STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
+ if( FAILED ( r ) )
+ {
+ WARN("Failed to open stream r = %08x\n", r);
+ return r;
+ }
- if(!pstg || !pclsid)
- return E_INVALIDARG;
+ /* read the various parts of the structure */
+ r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
+ if( FAILED( r ) || ( count != sizeof(unknown1) ) )
+ goto end;
+ r = ReadClassStm( stm, &clsid );
+ if( FAILED( r ) )
+ goto end;
- /*
- * read a STATSTG structure (contains the clsid) from the storage
- */
- hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
+ r = STREAM_ReadString( stm, &szCLSIDName );
+ if( FAILED( r ) )
+ goto end;
- if(SUCCEEDED(hRes))
- *pclsid=pstatstg.clsid;
+ r = STREAM_ReadString( stm, &szOleTypeName );
+ if( FAILED( r ) )
+ goto end;
- return hRes;
-}
+ r = STREAM_ReadString( stm, &szProgIDName );
+ if( FAILED( r ) )
+ goto end;
-/***********************************************************************
- * OleLoadFromStream (OLE32.@)
- *
- * This function loads an object from stream
- */
-HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
-{
- CLSID clsid;
- HRESULT res;
- LPPERSISTSTREAM xstm;
+ r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
+ if( FAILED( r ) || ( count != sizeof(unknown2) ) )
+ goto end;
- TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
+ /* ok, success... now we just need to store what we found */
+ if( pcf )
+ *pcf = RegisterClipboardFormatW( szOleTypeName );
- res=ReadClassStm(pStm,&clsid);
- if (FAILED(res))
- return res;
- res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
- if (FAILED(res))
- return res;
- res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
- if (FAILED(res)) {
- IUnknown_Release((IUnknown*)*ppvObj);
- return res;
- }
- res=IPersistStream_Load(xstm,pStm);
- IPersistStream_Release(xstm);
- /* FIXME: all refcounts ok at this point? I think they should be:
- * pStm : unchanged
- * ppvObj : 1
- * xstm : 0 (released)
- */
- return res;
+ if( lplpszUserType )
+ {
+ *lplpszUserType = szCLSIDName;
+ szCLSIDName = NULL;
+ }
+
+end:
+ CoTaskMemFree( szCLSIDName );
+ CoTaskMemFree( szOleTypeName );
+ CoTaskMemFree( szProgIDName );
+ IStream_Release( stm );
+
+ return r;
}
-/***********************************************************************
- * OleSaveToStream (OLE32.@)
+/******************************************************************************
+ * StgIsStorageFile [OLE32.@]
+ * Verify if the file contains a storage object
*
- * This function saves an object with the IPersistStream interface on it
- * to the specified stream.
+ * PARAMS
+ * fn [ I] Filename
+ *
+ * RETURNS
+ * S_OK if file has magic bytes as a storage object
+ * S_FALSE if file is not storage
*/
-HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
+HRESULT WINAPI
+StgIsStorageFile(LPCOLESTR fn)
{
+ HANDLE hf;
+ BYTE magic[8];
+ DWORD bytes_read;
- CLSID clsid;
- HRESULT res;
-
- TRACE("(%p,%p)\n",pPStm,pStm);
+ TRACE("%s\n", debugstr_w(fn));
+ hf = CreateFileW(fn, GENERIC_READ,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- res=IPersistStream_GetClassID(pPStm,&clsid);
+ if (hf == INVALID_HANDLE_VALUE)
+ return STG_E_FILENOTFOUND;
- if (SUCCEEDED(res)){
+ if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
+ {
+ WARN(" unable to read file\n");
+ CloseHandle(hf);
+ return S_FALSE;
+ }
- res=WriteClassStm(pStm,&clsid);
+ CloseHandle(hf);
- if (SUCCEEDED(res))
+ if (bytes_read != 8) {
+ TRACE(" too short\n");
+ return S_FALSE;
+ }
- res=IPersistStream_Save(pPStm,pStm,TRUE);
- }
+ if (!memcmp(magic,STORAGE_magic,8)) {
+ TRACE(" -> YES\n");
+ return S_OK;
+ }
- TRACE("Finished Save\n");
- return res;
+ TRACE(" -> Invalid header.\n");
+ return S_FALSE;
}
-/****************************************************************************
- * This method validate a STGM parameter that can contain the values below
- *
- * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
- * The stgm values contained in 0xffff0000 are bitmasks.
+/***********************************************************************
+ * WriteClassStm (OLE32.@)
*
- * STGM_DIRECT 0x00000000
- * STGM_TRANSACTED 0x00010000
- * STGM_SIMPLE 0x08000000
+ * Writes a CLSID to a stream.
*
- * STGM_READ 0x00000000
- * STGM_WRITE 0x00000001
- * STGM_READWRITE 0x00000002
+ * PARAMS
+ * pStm [I] Stream to write to.
+ * rclsid [I] CLSID to write.
*
- * STGM_SHARE_DENY_NONE 0x00000040
- * STGM_SHARE_DENY_READ 0x00000030
- * STGM_SHARE_DENY_WRITE 0x00000020
- * STGM_SHARE_EXCLUSIVE 0x00000010
+ * RETURNS
+ * Success: S_OK.
+ * Failure: HRESULT code.
+ */
+HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
+{
+ TRACE("(%p,%p)\n",pStm,rclsid);
+
+ if (!pStm || !rclsid)
+ return E_INVALIDARG;
+
+ return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
+}
+
+/***********************************************************************
+ * ReadClassStm (OLE32.@)
*
- * STGM_PRIORITY 0x00040000
- * STGM_DELETEONRELEASE 0x04000000
+ * Reads a CLSID from a stream.
*
- * STGM_CREATE 0x00001000
- * STGM_CONVERT 0x00020000
- * STGM_FAILIFTHERE 0x00000000
+ * PARAMS
+ * pStm [I] Stream to read from.
+ * rclsid [O] CLSID to read.
*
- * STGM_NOSCRATCH 0x00100000
- * STGM_NOSNAPSHOT 0x00200000
+ * RETURNS
+ * Success: S_OK.
+ * Failure: HRESULT code.
*/
-static HRESULT validateSTGM(DWORD stgm)
+HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
{
- DWORD access = STGM_ACCESS_MODE(stgm);
- DWORD share = STGM_SHARE_MODE(stgm);
- DWORD create = STGM_CREATE_MODE(stgm);
-
- if (stgm&~STGM_KNOWN_FLAGS)
- {
- ERR("unknown flags %08x\n", stgm);
- return E_FAIL;
- }
+ ULONG nbByte;
+ HRESULT res;
- switch (access)
- {
- case STGM_READ:
- case STGM_WRITE:
- case STGM_READWRITE:
- break;
- default:
- return E_FAIL;
- }
+ TRACE("(%p,%p)\n",pStm,pclsid);
- switch (share)
- {
- case STGM_SHARE_DENY_NONE:
- case STGM_SHARE_DENY_READ:
- case STGM_SHARE_DENY_WRITE:
- case STGM_SHARE_EXCLUSIVE:
- break;
- case 0:
- if (!(stgm & STGM_TRANSACTED))
- return E_FAIL;
- break;
- default:
- return E_FAIL;
- }
+ if (!pStm || !pclsid)
+ return E_INVALIDARG;
- switch (create)
- {
- case STGM_CREATE:
- case STGM_FAILIFTHERE:
- break;
- default:
- return E_FAIL;
- }
+ /* clear the output args */
+ *pclsid = CLSID_NULL;
- /*
- * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
- */
- if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
- return E_FAIL;
+ res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
- /*
- * STGM_CREATE | STGM_CONVERT
- * if both are false, STGM_FAILIFTHERE is set to TRUE
- */
- if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
- return E_FAIL;
+ if (FAILED(res))
+ return res;
- /*
- * STGM_NOSCRATCH requires STGM_TRANSACTED
- */
- if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
- return E_FAIL;
+ if (nbByte != sizeof(CLSID))
+ return STG_E_READFAULT;
+ else
+ return S_OK;
+}
- /*
- * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
- * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
- */
- if ( (stgm & STGM_NOSNAPSHOT) &&
- (!(stgm & STGM_TRANSACTED) ||
- share == STGM_SHARE_EXCLUSIVE ||
- share == STGM_SHARE_DENY_WRITE) )
- return E_FAIL;
- return S_OK;
-}
+/************************************************************************
+ * OleConvert Functions
+ ***********************************************************************/
-/****************************************************************************
- * GetShareModeFromSTGM
- *
- * This method will return a share mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetShareModeFromSTGM(DWORD stgm)
-{
- switch (STGM_SHARE_MODE(stgm))
- {
- case 0:
- assert(stgm & STGM_TRANSACTED);
- /* fall-through */
- case STGM_SHARE_DENY_NONE:
- return FILE_SHARE_READ | FILE_SHARE_WRITE;
- case STGM_SHARE_DENY_READ:
- return FILE_SHARE_WRITE;
- case STGM_SHARE_DENY_WRITE:
- case STGM_SHARE_EXCLUSIVE:
- return FILE_SHARE_READ;
- }
- ERR("Invalid share mode!\n");
- assert(0);
- return 0;
-}
+#define OLESTREAM_ID 0x501
+#define OLESTREAM_MAX_STR_LEN 255
-/****************************************************************************
- * GetAccessModeFromSTGM
- *
- * This method will return an access mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetAccessModeFromSTGM(DWORD stgm)
+/* OLESTREAM memory structure to use for Get and Put Routines */
+typedef struct
{
- switch (STGM_ACCESS_MODE(stgm))
- {
- case STGM_READ:
- return GENERIC_READ;
- case STGM_WRITE:
- case STGM_READWRITE:
- return GENERIC_READ | GENERIC_WRITE;
- }
- ERR("Invalid access mode!\n");
- assert(0);
- return 0;
-}
+ DWORD dwOleID;
+ DWORD dwTypeID;
+ DWORD dwOleTypeNameLength;
+ CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
+ CHAR *pstrOleObjFileName;
+ DWORD dwOleObjFileNameLength;
+ DWORD dwMetaFileWidth;
+ DWORD dwMetaFileHeight;
+ CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
+ DWORD dwDataLength;
+ BYTE *pData;
+} OLECONVERT_OLESTREAM_DATA;
-/****************************************************************************
- * GetCreationModeFromSTGM
- *
- * This method will return a creation mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetCreationModeFromSTGM(DWORD stgm)
+/* CompObj Stream structure */
+typedef struct
{
- switch(STGM_CREATE_MODE(stgm))
- {
- case STGM_CREATE:
- return CREATE_ALWAYS;
- case STGM_CONVERT:
- FIXME("STGM_CONVERT not implemented!\n");
- return CREATE_NEW;
- case STGM_FAILIFTHERE:
- return CREATE_NEW;
- }
- ERR("Invalid create mode!\n");
- assert(0);
- return 0;
-}
+ BYTE byUnknown1[12];
+ CLSID clsid;
+ DWORD dwCLSIDNameLength;
+ CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
+ DWORD dwOleTypeNameLength;
+ CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
+ DWORD dwProgIDNameLength;
+ CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
+ BYTE byUnknown2[16];
+} OLECONVERT_ISTORAGE_COMPOBJ;
+
+/* Ole Presentation Stream structure */
+typedef struct
+{
+ BYTE byUnknown1[28];
+ DWORD dwExtentX;
+ DWORD dwExtentY;
+ DWORD dwSize;
+ BYTE *pData;
+} OLECONVERT_ISTORAGE_OLEPRES;
/*************************************************************************
@@ -9254,494 +9581,219 @@ static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM
}
/*************************************************************************
- * OLECONVERT_SaveOLE10 [Internal]
- *
- * Saves the OLE10 STREAM From memory
- *
- * PARAMS
- * pData [I] Data Structure for the OLESTREAM Data
- * pOleStream [I] The OLESTREAM to save
- *
- * RETURNS
- * Success: S_OK
- * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
- *
- * NOTES
- * This function is used by OleConvertIStorageToOLESTREAM only.
- *
- */
-static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
-{
- DWORD dwSize;
- HRESULT hRes = S_OK;
-
-
- /* Set the OleID */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
- if(dwSize != sizeof(pData->dwOleID))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
-
- if(hRes == S_OK)
- {
- /* Set the TypeID */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
- if(dwSize != sizeof(pData->dwTypeID))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
-
- if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
- {
- /* Set the Length of the OleTypeName */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
- if(dwSize != sizeof(pData->dwOleTypeNameLength))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
-
- if(hRes == S_OK)
- {
- if(pData->dwOleTypeNameLength > 0)
- {
- /* Set the OleTypeName */
- dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
- if(dwSize != pData->dwOleTypeNameLength)
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
- }
-
- if(hRes == S_OK)
- {
- /* Set the width of the Metafile */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
- if(dwSize != sizeof(pData->dwMetaFileWidth))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
-
- if(hRes == S_OK)
- {
- /* Set the height of the Metafile */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
- if(dwSize != sizeof(pData->dwMetaFileHeight))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
-
- if(hRes == S_OK)
- {
- /* Set the length of the Data */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
- if(dwSize != sizeof(pData->dwDataLength))
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
-
- if(hRes == S_OK)
- {
- if(pData->dwDataLength > 0)
- {
- /* Set the Data (eg. IStorage, Metafile, Bitmap) */
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
- if(dwSize != pData->dwDataLength)
- {
- hRes = CONVERT10_E_OLESTREAM_PUT;
- }
- }
- }
- }
- return hRes;
-}
-
-/*************************************************************************
- * OLECONVERT_GetOLE20FromOLE10[Internal]
- *
- * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
- * opens it, and copies the content to the dest IStorage for
- * OleConvertOLESTREAMToIStorage
- *
- *
- * PARAMS
- * pDestStorage [I] The IStorage to copy the data to
- * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
- * nBufferLength [I] The size of the buffer
- *
- * RETURNS
- * Nothing
- *
- * NOTES
- *
- *
- */
-static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
-{
- HRESULT hRes;
- HANDLE hFile;
- IStorage *pTempStorage;
- DWORD dwNumOfBytesWritten;
- WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
- static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
-
- /* Create a temp File */
- GetTempPathW(MAX_PATH, wstrTempDir);
- GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
- hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
-
- if(hFile != INVALID_HANDLE_VALUE)
- {
- /* Write IStorage Data to File */
- WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
- CloseHandle(hFile);
-
- /* Open and copy temp storage to the Dest Storage */
- hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
- if(hRes == S_OK)
- {
- hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
- IStorage_Release(pTempStorage);
- }
- DeleteFileW(wstrTempFile);
- }
-}
-
-
-/*************************************************************************
- * OLECONVERT_WriteOLE20ToBuffer [Internal]
- *
- * Saves the OLE10 STREAM From memory
- *
- * PARAMS
- * pStorage [I] The Src IStorage to copy
- * pData [I] The Dest Memory to write to.
- *
- * RETURNS
- * The size in bytes allocated for pData
- *
- * NOTES
- * Memory allocated for pData must be freed by the caller
- *
- * Used by OleConvertIStorageToOLESTREAM only.
- *
- */
-static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
-{
- HANDLE hFile;
- HRESULT hRes;
- DWORD nDataLength = 0;
- IStorage *pTempStorage;
- WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
- static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
-
- *pData = NULL;
-
- /* Create temp Storage */
- GetTempPathW(MAX_PATH, wstrTempDir);
- GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
- hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
-
- if(hRes == S_OK)
- {
- /* Copy Src Storage to the Temp Storage */
- IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
- IStorage_Release(pTempStorage);
-
- /* Open Temp Storage as a file and copy to memory */
- hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if(hFile != INVALID_HANDLE_VALUE)
- {
- nDataLength = GetFileSize(hFile, NULL);
- *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
- ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
- CloseHandle(hFile);
- }
- DeleteFileW(wstrTempFile);
- }
- return nDataLength;
-}
-
-/*************************************************************************
- * STORAGE_CreateOleStream [Internal]
+ * OLECONVERT_SaveOLE10 [Internal]
*
- * Creates the "\001OLE" stream in the IStorage if necessary.
+ * Saves the OLE10 STREAM From memory
*
* PARAMS
- * storage [I] Dest storage to create the stream in
- * flags [I] flags to be set for newly created stream
+ * pData [I] Data Structure for the OLESTREAM Data
+ * pOleStream [I] The OLESTREAM to save
*
* RETURNS
- * HRESULT return value
+ * Success: S_OK
+ * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
*
* NOTES
- *
- * This stream is still unknown, MS Word seems to have extra data
- * but since the data is stored in the OLESTREAM there should be
- * no need to recreate the stream. If the stream is manually
- * deleted it will create it with this default data.
+ * This function is used by OleConvertIStorageToOLESTREAM only.
*
*/
-HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
-{
- static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
- static const DWORD version_magic = 0x02000001;
- IStream *stream;
- HRESULT hr;
-
- hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
- if (hr == S_OK)
- {
- struct empty_1ole_stream {
- DWORD version_magic;
- DWORD flags;
- DWORD update_options;
- DWORD reserved;
- DWORD mon_stream_size;
- };
- struct empty_1ole_stream stream_data;
-
- stream_data.version_magic = version_magic;
- stream_data.flags = flags;
- stream_data.update_options = 0;
- stream_data.reserved = 0;
- stream_data.mon_stream_size = 0;
-
- hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
- IStream_Release(stream);
- }
-
- return hr;
-}
-
-/* write a string to a stream, preceded by its length */
-static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
-{
- HRESULT r;
- LPSTR str;
- DWORD len = 0;
-
- if( string )
- len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
- r = IStream_Write( stm, &len, sizeof(len), NULL);
- if( FAILED( r ) )
- return r;
- if(len == 0)
- return r;
- str = CoTaskMemAlloc( len );
- WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
- r = IStream_Write( stm, str, len, NULL);
- CoTaskMemFree( str );
- return r;
-}
-
-/* read a string preceded by its length from a stream */
-static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
+static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
{
- HRESULT r;
- DWORD len, count = 0;
- LPSTR str;
- LPWSTR wstr;
+ DWORD dwSize;
+ HRESULT hRes = S_OK;
- r = IStream_Read( stm, &len, sizeof(len), &count );
- if( FAILED( r ) )
- return r;
- if( count != sizeof(len) )
- return E_OUTOFMEMORY;
- TRACE("%d bytes\n",len);
-
- str = CoTaskMemAlloc( len );
- if( !str )
- return E_OUTOFMEMORY;
- count = 0;
- r = IStream_Read( stm, str, len, &count );
- if( FAILED( r ) )
- return r;
- if( count != len )
+ /* Set the OleID */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
+ if(dwSize != sizeof(pData->dwOleID))
{
- CoTaskMemFree( str );
- return E_OUTOFMEMORY;
+ hRes = CONVERT10_E_OLESTREAM_PUT;
}
- TRACE("Read string %s\n",debugstr_an(str,len));
-
- len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
- wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
- if( wstr )
+ if(hRes == S_OK)
{
- MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
- wstr[len] = 0;
+ /* Set the TypeID */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
+ if(dwSize != sizeof(pData->dwTypeID))
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
}
- CoTaskMemFree( str );
-
- *string = wstr;
-
- return r;
-}
-
-
-static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
- LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
-{
- IStream *pstm;
- HRESULT r = S_OK;
- static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
-
- static const BYTE unknown1[12] =
- { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
- 0xFF, 0xFF, 0xFF, 0xFF};
- static const BYTE unknown2[16] =
- { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
- TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
- debugstr_w(lpszUserType), debugstr_w(szClipName),
- debugstr_w(szProgIDName));
-
- /* Create a CompObj stream */
- r = IStorage_CreateStream(pstg, szwStreamName,
- STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
- if( FAILED (r) )
- return r;
-
- /* Write CompObj Structure to stream */
- r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
-
- if( SUCCEEDED( r ) )
- r = WriteClassStm( pstm, clsid );
-
- if( SUCCEEDED( r ) )
- r = STREAM_WriteString( pstm, lpszUserType );
- if( SUCCEEDED( r ) )
- r = STREAM_WriteString( pstm, szClipName );
- if( SUCCEEDED( r ) )
- r = STREAM_WriteString( pstm, szProgIDName );
- if( SUCCEEDED( r ) )
- r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
-
- IStream_Release( pstm );
-
- return r;
-}
-
-/***********************************************************************
- * WriteFmtUserTypeStg (OLE32.@)
- */
-HRESULT WINAPI WriteFmtUserTypeStg(
- LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
-{
- STATSTG stat;
- HRESULT r;
- WCHAR szwClipName[0x40];
- CLSID clsid;
- LPWSTR wstrProgID = NULL;
- DWORD n;
-
- TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
- /* get the clipboard format name */
- if( cf )
+ if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
{
- n = GetClipboardFormatNameW( cf, szwClipName,
- sizeof(szwClipName)/sizeof(szwClipName[0]) );
- szwClipName[n]=0;
- }
-
- TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
-
- r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
- if(SUCCEEDED(r))
- clsid = stat.clsid;
- else
- clsid = CLSID_NULL;
+ /* Set the Length of the OleTypeName */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
+ if(dwSize != sizeof(pData->dwOleTypeNameLength))
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
- ProgIDFromCLSID(&clsid, &wstrProgID);
+ if(hRes == S_OK)
+ {
+ if(pData->dwOleTypeNameLength > 0)
+ {
+ /* Set the OleTypeName */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
+ if(dwSize != pData->dwOleTypeNameLength)
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
+ }
+ }
- TRACE("progid is %s\n",debugstr_w(wstrProgID));
+ if(hRes == S_OK)
+ {
+ /* Set the width of the Metafile */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
+ if(dwSize != sizeof(pData->dwMetaFileWidth))
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
+ }
- r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
- cf ? szwClipName : NULL, wstrProgID );
+ if(hRes == S_OK)
+ {
+ /* Set the height of the Metafile */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
+ if(dwSize != sizeof(pData->dwMetaFileHeight))
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
+ }
- CoTaskMemFree(wstrProgID);
+ if(hRes == S_OK)
+ {
+ /* Set the length of the Data */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
+ if(dwSize != sizeof(pData->dwDataLength))
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
+ }
- return r;
+ if(hRes == S_OK)
+ {
+ if(pData->dwDataLength > 0)
+ {
+ /* Set the Data (eg. IStorage, Metafile, Bitmap) */
+ dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
+ if(dwSize != pData->dwDataLength)
+ {
+ hRes = CONVERT10_E_OLESTREAM_PUT;
+ }
+ }
+ }
+ }
+ return hRes;
}
-
-/******************************************************************************
- * ReadFmtUserTypeStg [OLE32.@]
+/*************************************************************************
+ * OLECONVERT_GetOLE20FromOLE10[Internal]
+ *
+ * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
+ * opens it, and copies the content to the dest IStorage for
+ * OleConvertOLESTREAMToIStorage
+ *
+ *
+ * PARAMS
+ * pDestStorage [I] The IStorage to copy the data to
+ * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
+ * nBufferLength [I] The size of the buffer
+ *
+ * RETURNS
+ * Nothing
+ *
+ * NOTES
+ *
+ *
*/
-HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
+static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
{
- HRESULT r;
- IStream *stm = 0;
- static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
- unsigned char unknown1[12];
- unsigned char unknown2[16];
- DWORD count;
- LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
- CLSID clsid;
+ HRESULT hRes;
+ HANDLE hFile;
+ IStorage *pTempStorage;
+ DWORD dwNumOfBytesWritten;
+ WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
+ static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
- TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
+ /* Create a temp File */
+ GetTempPathW(MAX_PATH, wstrTempDir);
+ GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
+ hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
- r = IStorage_OpenStream( pstg, szCompObj, NULL,
- STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
- if( FAILED ( r ) )
+ if(hFile != INVALID_HANDLE_VALUE)
{
- WARN("Failed to open stream r = %08x\n", r);
- return r;
- }
-
- /* read the various parts of the structure */
- r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
- if( FAILED( r ) || ( count != sizeof(unknown1) ) )
- goto end;
- r = ReadClassStm( stm, &clsid );
- if( FAILED( r ) )
- goto end;
+ /* Write IStorage Data to File */
+ WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
+ CloseHandle(hFile);
- r = STREAM_ReadString( stm, &szCLSIDName );
- if( FAILED( r ) )
- goto end;
+ /* Open and copy temp storage to the Dest Storage */
+ hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
+ if(hRes == S_OK)
+ {
+ hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
+ IStorage_Release(pTempStorage);
+ }
+ DeleteFileW(wstrTempFile);
+ }
+}
- r = STREAM_ReadString( stm, &szOleTypeName );
- if( FAILED( r ) )
- goto end;
- r = STREAM_ReadString( stm, &szProgIDName );
- if( FAILED( r ) )
- goto end;
+/*************************************************************************
+ * OLECONVERT_WriteOLE20ToBuffer [Internal]
+ *
+ * Saves the OLE10 STREAM From memory
+ *
+ * PARAMS
+ * pStorage [I] The Src IStorage to copy
+ * pData [I] The Dest Memory to write to.
+ *
+ * RETURNS
+ * The size in bytes allocated for pData
+ *
+ * NOTES
+ * Memory allocated for pData must be freed by the caller
+ *
+ * Used by OleConvertIStorageToOLESTREAM only.
+ *
+ */
+static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
+{
+ HANDLE hFile;
+ HRESULT hRes;
+ DWORD nDataLength = 0;
+ IStorage *pTempStorage;
+ WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
+ static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
- r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
- if( FAILED( r ) || ( count != sizeof(unknown2) ) )
- goto end;
+ *pData = NULL;
- /* ok, success... now we just need to store what we found */
- if( pcf )
- *pcf = RegisterClipboardFormatW( szOleTypeName );
+ /* Create temp Storage */
+ GetTempPathW(MAX_PATH, wstrTempDir);
+ GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
+ hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
- if( lplpszUserType )
+ if(hRes == S_OK)
{
- *lplpszUserType = szCLSIDName;
- szCLSIDName = NULL;
- }
-
-end:
- CoTaskMemFree( szCLSIDName );
- CoTaskMemFree( szOleTypeName );
- CoTaskMemFree( szProgIDName );
- IStream_Release( stm );
+ /* Copy Src Storage to the Temp Storage */
+ IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
+ IStorage_Release(pTempStorage);
- return r;
+ /* Open Temp Storage as a file and copy to memory */
+ hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ nDataLength = GetFileSize(hFile, NULL);
+ *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
+ ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
+ CloseHandle(hFile);
+ }
+ DeleteFileW(wstrTempFile);
+ }
+ return nDataLength;
}
-
/*************************************************************************
* OLECONVERT_CreateCompObjStream [Internal]
*
@@ -10435,112 +10487,3 @@ HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
IStream_Release(stream);
return hr;
}
-
-/******************************************************************************
- * StgIsStorageFile [OLE32.@]
- * Verify if the file contains a storage object
- *
- * PARAMS
- * fn [ I] Filename
- *
- * RETURNS
- * S_OK if file has magic bytes as a storage object
- * S_FALSE if file is not storage
- */
-HRESULT WINAPI
-StgIsStorageFile(LPCOLESTR fn)
-{
- HANDLE hf;
- BYTE magic[8];
- DWORD bytes_read;
-
- TRACE("%s\n", debugstr_w(fn));
- hf = CreateFileW(fn, GENERIC_READ,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
-
- if (hf == INVALID_HANDLE_VALUE)
- return STG_E_FILENOTFOUND;
-
- if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
- {
- WARN(" unable to read file\n");
- CloseHandle(hf);
- return S_FALSE;
- }
-
- CloseHandle(hf);
-
- if (bytes_read != 8) {
- TRACE(" too short\n");
- return S_FALSE;
- }
-
- if (!memcmp(magic,STORAGE_magic,8)) {
- TRACE(" -> YES\n");
- return S_OK;
- }
-
- TRACE(" -> Invalid header.\n");
- return S_FALSE;
-}
-
-/***********************************************************************
- * WriteClassStm (OLE32.@)
- *
- * Writes a CLSID to a stream.
- *
- * PARAMS
- * pStm [I] Stream to write to.
- * rclsid [I] CLSID to write.
- *
- * RETURNS
- * Success: S_OK.
- * Failure: HRESULT code.
- */
-HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
-{
- TRACE("(%p,%p)\n",pStm,rclsid);
-
- if (!pStm || !rclsid)
- return E_INVALIDARG;
-
- return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
-}
-
-/***********************************************************************
- * ReadClassStm (OLE32.@)
- *
- * Reads a CLSID from a stream.
- *
- * PARAMS
- * pStm [I] Stream to read from.
- * rclsid [O] CLSID to read.
- *
- * RETURNS
- * Success: S_OK.
- * Failure: HRESULT code.
- */
-HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
-{
- ULONG nbByte;
- HRESULT res;
-
- TRACE("(%p,%p)\n",pStm,pclsid);
-
- if (!pStm || !pclsid)
- return E_INVALIDARG;
-
- /* clear the output args */
- *pclsid = CLSID_NULL;
-
- res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
-
- if (FAILED(res))
- return res;
-
- if (nbByte != sizeof(CLSID))
- return STG_E_READFAULT;
- else
- return S_OK;
-}
diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h
index f894582..6c69655 100644
--- a/dlls/ole32/storage32.h
+++ b/dlls/ole32/storage32.h
@@ -416,39 +416,6 @@ struct StorageImpl
ULONG locked_bytes[8];
};
-HRESULT StorageImpl_ReadRawDirEntry(
- StorageImpl *This,
- ULONG index,
- BYTE *buffer) DECLSPEC_HIDDEN;
-
-void UpdateRawDirEntry(
- BYTE *buffer,
- const DirEntry *newData) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_WriteRawDirEntry(
- StorageImpl *This,
- ULONG index,
- const BYTE *buffer) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_ReadDirEntry(
- StorageImpl* This,
- DirRef index,
- DirEntry* buffer) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_WriteDirEntry(
- StorageImpl* This,
- DirRef index,
- const DirEntry* buffer) DECLSPEC_HIDDEN;
-
-BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
- StorageImpl* This,
- SmallBlockChainStream** ppsbChain) DECLSPEC_HIDDEN;
-
-SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
- StorageImpl* This,
- BlockChainStream** ppbbChain,
- ULARGE_INTEGER newSize) DECLSPEC_HIDDEN;
-
/****************************************************************************
* StgStreamImpl definitions.
*
@@ -628,6 +595,15 @@ struct BlockChainStream
/*
* Methods for the BlockChainStream class.
*/
+
+/* Returns the number of blocks that comprises this chain.
+ * This is not the size of the stream as the last block may not be full!
+ */
+static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
+{
+ return This->numBlocks;
+}
+
BlockChainStream* BlockChainStream_Construct(
StorageImpl* parentStorage,
ULONG* headOfStreamPlaceHolder,
--
2.1.4
More information about the wine-patches
mailing list