Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55075 Signed-off-by: Fabian Maurer dark.shadow4@web.de
-- v2: apisetschema: Add api-ms-win-core-com-l2-1-1 coml2: Move storage functions from ole32 coml2: Copy some files from ole32 coml2: Add dll and move code from ole32/memlockbytes.c
From: Fabian Maurer dark.shadow4@web.de
--- dlls/coml2/Makefile.in | 6 ++++++ dlls/coml2/coml2.spec | 2 ++ dlls/{ole32 => coml2}/memlockbytes.c | 0 dlls/ole32/Makefile.in | 3 +-- dlls/ole32/ole32.spec | 4 ++-- 5 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 dlls/coml2/Makefile.in create mode 100644 dlls/coml2/coml2.spec rename dlls/{ole32 => coml2}/memlockbytes.c (100%)
diff --git a/dlls/coml2/Makefile.in b/dlls/coml2/Makefile.in new file mode 100644 index 00000000000..8e4eba5f988 --- /dev/null +++ b/dlls/coml2/Makefile.in @@ -0,0 +1,6 @@ +MODULE = coml2.dll +IMPORTLIB = coml2 +IMPORTS = uuid + +SOURCES = \ + memlockbytes.c diff --git a/dlls/coml2/coml2.spec b/dlls/coml2/coml2.spec new file mode 100644 index 00000000000..7070e5ca3b2 --- /dev/null +++ b/dlls/coml2/coml2.spec @@ -0,0 +1,2 @@ +@ stdcall CreateILockBytesOnHGlobal(ptr long ptr) +@ stdcall GetHGlobalFromILockBytes(ptr ptr) diff --git a/dlls/ole32/memlockbytes.c b/dlls/coml2/memlockbytes.c similarity index 100% rename from dlls/ole32/memlockbytes.c rename to dlls/coml2/memlockbytes.c diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index d5da22c599a..84544a628a8 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -1,7 +1,7 @@ EXTRADEFS = -D_OLE32_ MODULE = ole32.dll IMPORTLIB = ole32 -IMPORTS = uuid advapi32 user32 gdi32 combase rpcrt4 kernelbase +IMPORTS = uuid advapi32 user32 gdi32 combase rpcrt4 kernelbase coml2 DELAYIMPORTS = oleaut32
SOURCES = \ @@ -26,7 +26,6 @@ SOURCES = \ irot.idl \ itemmoniker.c \ marshal.c \ - memlockbytes.c \ moniker.c \ nodrop.svg \ ole2.c \ diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index e9982036eeb..944ff81b1b3 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -101,7 +101,7 @@ @ stdcall CreateErrorInfo(ptr) combase.CreateErrorInfo @ stdcall CreateFileMoniker(wstr ptr) @ stdcall CreateGenericComposite(ptr ptr ptr) -@ stdcall CreateILockBytesOnHGlobal(ptr long ptr) +@ stdcall CreateILockBytesOnHGlobal(ptr long ptr) coml2.CreateILockBytesOnHGlobal @ stdcall CreateItemMoniker(wstr wstr ptr) @ stdcall CreateObjrefMoniker(ptr ptr) @ stdcall CreateOleAdviseHolder(ptr) @@ -121,7 +121,7 @@ @ stdcall GetConvertStg(ptr) @ stub GetDocumentBitStg @ stdcall GetErrorInfo(long ptr) combase.GetErrorInfo -@ stdcall GetHGlobalFromILockBytes(ptr ptr) +@ stdcall GetHGlobalFromILockBytes(ptr ptr) coml2.GetHGlobalFromILockBytes @ stdcall GetHGlobalFromStream(ptr ptr) combase.GetHGlobalFromStream @ stub GetHookInterface @ stdcall GetRunningObjectTable(long ptr)
From: Fabian Maurer dark.shadow4@web.de
--- dlls/coml2/dictionary.c | 191 + dlls/coml2/dictionary.h | 93 + dlls/coml2/filelockbytes.c | 401 ++ dlls/coml2/stg_prop.c | 3297 +++++++++++ dlls/coml2/stg_stream.c | 705 +++ dlls/coml2/storage32.c | 10649 +++++++++++++++++++++++++++++++++++ dlls/coml2/storage32.h | 571 ++ 7 files changed, 15907 insertions(+) create mode 100644 dlls/coml2/dictionary.c create mode 100644 dlls/coml2/dictionary.h create mode 100644 dlls/coml2/filelockbytes.c create mode 100644 dlls/coml2/stg_prop.c create mode 100644 dlls/coml2/stg_stream.c create mode 100644 dlls/coml2/storage32.c create mode 100644 dlls/coml2/storage32.h
diff --git a/dlls/coml2/dictionary.c b/dlls/coml2/dictionary.c new file mode 100644 index 00000000000..593a4ff1588 --- /dev/null +++ b/dlls/coml2/dictionary.c @@ -0,0 +1,191 @@ +/* Simple dictionary implementation using a linked list. + * FIXME: a skip list would be faster. + * + * Copyright 2005 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include <assert.h> +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "dictionary.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(storage); + +struct dictionary_entry +{ + void *key; + void *value; + struct dictionary_entry *next; +}; + +struct dictionary +{ + comparefunc comp; + destroyfunc destroy; + void *extra; + struct dictionary_entry *head; + UINT num_entries; +}; + +struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra) +{ + struct dictionary *ret; + + TRACE("(%p, %p, %p)\n", c, d, extra); + if (!c) + return NULL; + ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary)); + if (ret) + { + ret->comp = c; + ret->destroy = d; + ret->extra = extra; + ret->head = NULL; + ret->num_entries = 0; + } + TRACE("returning %p\n", ret); + return ret; +} + +void dictionary_destroy(struct dictionary *d) +{ + TRACE("(%p)\n", d); + if (d) + { + struct dictionary_entry *p; + + for (p = d->head; p; ) + { + struct dictionary_entry *next = p->next; + + if (d->destroy) + d->destroy(p->key, p->value, d->extra); + HeapFree(GetProcessHeap(), 0, p); + p = next; + } + HeapFree(GetProcessHeap(), 0, d); + } +} + +UINT dictionary_num_entries(struct dictionary *d) +{ + return d ? d->num_entries : 0; +} + +/* Returns the address of the pointer to the node containing k. (It returns + * the address of either h->head or the address of the next member of the + * prior node. It's useful when you want to delete.) + * Assumes h and prev are not NULL. + */ +static struct dictionary_entry **dictionary_find_internal(struct dictionary *d, + const void *k) +{ + struct dictionary_entry **ret = NULL; + struct dictionary_entry *p; + + assert(d); + /* special case for head containing the desired element */ + if (d->head && d->comp(k, d->head->key, d->extra) == 0) + ret = &d->head; + for (p = d->head; !ret && p && p->next; p = p->next) + { + if (d->comp(k, p->next->key, d->extra) == 0) + ret = &p->next; + } + return ret; +} + +void dictionary_insert(struct dictionary *d, const void *k, const void *v) +{ + struct dictionary_entry **prior; + + TRACE("(%p, %p, %p)\n", d, k, v); + if (!d) + return; + if ((prior = dictionary_find_internal(d, k))) + { + if (d->destroy) + d->destroy((*prior)->key, (*prior)->value, d->extra); + (*prior)->key = (void *)k; + (*prior)->value = (void *)v; + } + else + { + struct dictionary_entry *elem = HeapAlloc(GetProcessHeap(), 0, + sizeof(struct dictionary_entry)); + + if (!elem) + return; + elem->key = (void *)k; + elem->value = (void *)v; + elem->next = d->head; + d->head = elem; + d->num_entries++; + } +} + +BOOL dictionary_find(struct dictionary *d, const void *k, void **value) +{ + struct dictionary_entry **prior; + BOOL ret = FALSE; + + TRACE("(%p, %p, %p)\n", d, k, value); + if (!d) + return FALSE; + if (!value) + return FALSE; + if ((prior = dictionary_find_internal(d, k))) + { + *value = (*prior)->value; + ret = TRUE; + } + TRACE("returning %d (%p)\n", ret, *value); + return ret; +} + +void dictionary_remove(struct dictionary *d, const void *k) +{ + struct dictionary_entry **prior, *temp; + + TRACE("(%p, %p)\n", d, k); + if (!d) + return; + if ((prior = dictionary_find_internal(d, k))) + { + temp = *prior; + if (d->destroy) + d->destroy((*prior)->key, (*prior)->value, d->extra); + *prior = (*prior)->next; + HeapFree(GetProcessHeap(), 0, temp); + d->num_entries--; + } +} + +void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure) +{ + struct dictionary_entry *p; + + TRACE("(%p, %p, %p)\n", d, e, closure); + if (!d) + return; + if (!e) + return; + for (p = d->head; p; p = p->next) + if (!e(p->key, p->value, d->extra, closure)) + break; +} diff --git a/dlls/coml2/dictionary.h b/dlls/coml2/dictionary.h new file mode 100644 index 00000000000..231a6474723 --- /dev/null +++ b/dlls/coml2/dictionary.h @@ -0,0 +1,93 @@ +/* Simple dictionary + * + * Copyright 2005 Juan Lang + * + * This is a pretty basic dictionary, or map if you prefer. It's not + * thread-safe. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef __DICTIONARY_H__ +#define __DICTIONARY_H__ + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" + +struct dictionary; + +/* Returns whether key a is less than, equal to, or greater than key b, in + * the same way (a - b) would for integers or strcmp(a, b) would for ANSI + * strings. + */ +typedef int (*comparefunc)(const void *a, const void *b, void *extra); + +/* Called for every element removed from the dictionary. See + * dictionary_destroy, dictionary_insert, and dictionary_remove. + */ +typedef void (*destroyfunc)(void *k, void *v, void *extra); + +/* Called for each element in the dictionary. Return FALSE if you don't want + * to enumerate any more. + */ +typedef BOOL (*enumeratefunc)(const void *k, const void *v, void *extra, + void *closure); + +/* Constructs a dictionary, using c as a comparison function for keys. + * If d is not NULL, it will be called whenever an item is about to be removed + * from the table, for example when dictionary_remove is called for a key, or + * when dictionary_destroy is called. + * extra is passed to c (and d, if it's provided). + * Assumes c is not NULL. + */ +struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra); + +/* Assumes d is not NULL. */ +void dictionary_destroy(struct dictionary *d); + +/* Returns how many entries have been stored in the dictionary. If two values + * with the same key are inserted, only one is counted. + */ +UINT dictionary_num_entries(struct dictionary *d); + +/* Sets an element with key k and value v to the dictionary. If a value + * already exists with key k, its value is replaced, and the destroyfunc (if + * set) is called for the previous item. + * Assumes k and v can be bitwise-copied. + * Both k and v are allowed to be NULL, in case you want to use integer + * values for either the key or the value. + * Assumes d is not NULL. + */ +void dictionary_insert(struct dictionary *d, const void *k, const void *v); + +/* If a value with key k has been inserted into the dictionary, *v is set + * to its associated value. Returns FALSE if the key is not found, and TRUE + * if it is. *v is undefined if it returns FALSE. (It is not set to NULL, + * because this dictionary doesn't prevent you from using NULL as a value + * value; see dictionary_insert.) + * Assumes d and v are not NULL. + */ +BOOL dictionary_find(struct dictionary *d, const void *k, void **v); + +/* Removes the element with key k from the dictionary. Calls the destroyfunc + * for the dictionary with the element if found (so you may destroy it if it's + * dynamically allocated.) + * Assumes d is not NULL. + */ +void dictionary_remove(struct dictionary *d, const void *k); + +void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure); + +#endif /* ndef __DICTIONARY_H__ */ diff --git a/dlls/coml2/filelockbytes.c b/dlls/coml2/filelockbytes.c new file mode 100644 index 00000000000..d806916f1d5 --- /dev/null +++ b/dlls/coml2/filelockbytes.c @@ -0,0 +1,401 @@ +/****************************************************************************** + * + * File-based ILockBytes implementation + * + * Copyright 1999 Thuy Nguyen + * Copyright 2010 Vincent Povirk for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "objbase.h" +#include "ole2.h" + +#include "storage32.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(storage); + +typedef struct FileLockBytesImpl +{ + ILockBytes ILockBytes_iface; + LONG ref; + HANDLE hfile; + DWORD flProtect; + LPWSTR pwcsName; +} FileLockBytesImpl; + +static const ILockBytesVtbl FileLockBytesImpl_Vtbl; + +static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface) +{ + return CONTAINING_RECORD(iface, FileLockBytesImpl, ILockBytes_iface); +} + +/*********************************************************** + * Prototypes for private methods + */ + +/**************************************************************************** + * GetProtectMode + * + * This function will return a protection mode flag for a file-mapping object + * from the open flags of a file. + */ +static DWORD GetProtectMode(DWORD openFlags) +{ + switch(STGM_ACCESS_MODE(openFlags)) + { + case STGM_WRITE: + case STGM_READWRITE: + return PAGE_READWRITE; + } + return PAGE_READONLY; +} + +/****************************************************************************** + * FileLockBytesImpl_Construct + * + * Initialize a big block object supported by a file. + */ +HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes) +{ + FileLockBytesImpl *This; + WCHAR fullpath[MAX_PATH]; + + if (hFile == INVALID_HANDLE_VALUE) + return E_FAIL; + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(FileLockBytesImpl)); + + if (!This) + return E_OUTOFMEMORY; + + This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl; + This->ref = 1; + This->hfile = hFile; + This->flProtect = GetProtectMode(openFlags); + + if(pwcsName) { + if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL)) + { + lstrcpynW(fullpath, pwcsName, MAX_PATH); + } + This->pwcsName = HeapAlloc(GetProcessHeap(), 0, + (lstrlenW(fullpath)+1)*sizeof(WCHAR)); + if (!This->pwcsName) + { + HeapFree(GetProcessHeap(), 0, This); + return E_OUTOFMEMORY; + } + lstrcpyW(This->pwcsName, fullpath); + } + else + This->pwcsName = NULL; + + *pLockBytes = &This->ILockBytes_iface; + + return S_OK; +} + +/* ILockByte Interfaces */ + +static HRESULT WINAPI FileLockBytesImpl_QueryInterface(ILockBytes *iface, REFIID riid, + void **ppvObject) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ILockBytes)) + *ppvObject = iface; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppvObject); + + return S_OK; +} + +static ULONG WINAPI FileLockBytesImpl_AddRef(ILockBytes *iface) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI FileLockBytesImpl_Release(ILockBytes *iface) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + ULONG ref; + + ref = InterlockedDecrement(&This->ref); + + if (ref == 0) + { + CloseHandle(This->hfile); + HeapFree(GetProcessHeap(), 0, This->pwcsName); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +/****************************************************************************** + * This method is part of the ILockBytes interface. + * + * It reads a block of information from the byte array at the specified + * offset. + * + * See the documentation of ILockBytes for more info. + */ +static HRESULT WINAPI FileLockBytesImpl_ReadAt( + ILockBytes* iface, + ULARGE_INTEGER ulOffset, /* [in] */ + void* pv, /* [length_is][size_is][out] */ + ULONG cb, /* [in] */ + ULONG* pcbRead) /* [out] */ +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + ULONG bytes_left = cb; + LPBYTE readPtr = pv; + BOOL ret; + LARGE_INTEGER offset; + ULONG cbRead; + + TRACE("%p, %ld, %p, %lu, %p.\n", iface, ulOffset.LowPart, pv, cb, pcbRead); + + /* verify a sane environment */ + if (!This) return E_FAIL; + + if (pcbRead) + *pcbRead = 0; + + offset.QuadPart = ulOffset.QuadPart; + + ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN); + + if (!ret) + return STG_E_READFAULT; + + while (bytes_left) + { + ret = ReadFile(This->hfile, readPtr, bytes_left, &cbRead, NULL); + + if (!ret || cbRead == 0) + return STG_E_READFAULT; + + if (pcbRead) + *pcbRead += cbRead; + + bytes_left -= cbRead; + readPtr += cbRead; + } + + TRACE("finished\n"); + return S_OK; +} + +/****************************************************************************** + * This method is part of the ILockBytes interface. + * + * It writes the specified bytes at the specified offset. + * position. If the file is too small, it will be resized. + * + * See the documentation of ILockBytes for more info. + */ +static HRESULT WINAPI FileLockBytesImpl_WriteAt( + ILockBytes* iface, + ULARGE_INTEGER ulOffset, /* [in] */ + const void* pv, /* [size_is][in] */ + ULONG cb, /* [in] */ + ULONG* pcbWritten) /* [out] */ +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + ULONG bytes_left = cb; + const BYTE *writePtr = pv; + BOOL ret; + LARGE_INTEGER offset; + ULONG cbWritten; + + TRACE("%p, %ld, %p, %lu, %p.\n", iface, ulOffset.LowPart, pv, cb, pcbWritten); + + /* verify a sane environment */ + if (!This) return E_FAIL; + + if (This->flProtect != PAGE_READWRITE) + return STG_E_ACCESSDENIED; + + if (pcbWritten) + *pcbWritten = 0; + + offset.QuadPart = ulOffset.QuadPart; + + ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN); + + if (!ret) + return STG_E_WRITEFAULT; + + while (bytes_left) + { + ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL); + + if (!ret) + return STG_E_WRITEFAULT; + + if (pcbWritten) + *pcbWritten += cbWritten; + + bytes_left -= cbWritten; + writePtr += cbWritten; + } + + TRACE("finished\n"); + return S_OK; +} + +static HRESULT WINAPI FileLockBytesImpl_Flush(ILockBytes* iface) +{ + return S_OK; +} + +/****************************************************************************** + * ILockBytes_SetSize + * + * Sets the size of the file. + * + */ +static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGER newSize) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + HRESULT hr = S_OK; + LARGE_INTEGER newpos; + + TRACE("new size %lu\n", newSize.LowPart); + + newpos.QuadPart = newSize.QuadPart; + if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN)) + { + SetEndOfFile(This->hfile); + } + + return hr; +} + +static HRESULT get_lock_error(void) +{ + switch (GetLastError()) + { + case ERROR_LOCK_VIOLATION: return STG_E_LOCKVIOLATION; break; + case ERROR_ACCESS_DENIED: return STG_E_ACCESSDENIED; break; + case ERROR_NOT_SUPPORTED: return STG_E_INVALIDFUNCTION; break; + default: + FIXME("no mapping for error %ld\n", GetLastError()); + return STG_E_INVALIDFUNCTION; + } +} + +static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface, + ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + OVERLAPPED ol; + DWORD lock_flags = LOCKFILE_FAIL_IMMEDIATELY; + + TRACE("ofs %lu count %lu flags %lx\n", libOffset.LowPart, cb.LowPart, dwLockType); + + if (dwLockType & LOCK_WRITE) + return STG_E_INVALIDFUNCTION; + + if (dwLockType & (LOCK_EXCLUSIVE|LOCK_ONLYONCE)) + lock_flags |= LOCKFILE_EXCLUSIVE_LOCK; + + ol.hEvent = 0; + ol.Offset = libOffset.LowPart; + ol.OffsetHigh = libOffset.HighPart; + + if (LockFileEx(This->hfile, lock_flags, 0, cb.LowPart, cb.HighPart, &ol)) + return S_OK; + return get_lock_error(); +} + +static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface, + ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + OVERLAPPED ol; + + TRACE("ofs %lu count %lu flags %lx\n", libOffset.LowPart, cb.LowPart, dwLockType); + + if (dwLockType & LOCK_WRITE) + return STG_E_INVALIDFUNCTION; + + ol.hEvent = 0; + ol.Offset = libOffset.LowPart; + ol.OffsetHigh = libOffset.HighPart; + + if (UnlockFileEx(This->hfile, 0, cb.LowPart, cb.HighPart, &ol)) + return S_OK; + return get_lock_error(); +} + +static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface, + STATSTG *pstatstg, DWORD grfStatFlag) +{ + FileLockBytesImpl* This = impl_from_ILockBytes(iface); + + if (!(STATFLAG_NONAME & grfStatFlag) && This->pwcsName) + { + pstatstg->pwcsName = + CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR)); + + lstrcpyW(pstatstg->pwcsName, This->pwcsName); + } + else + pstatstg->pwcsName = NULL; + + pstatstg->type = STGTY_LOCKBYTES; + + pstatstg->cbSize.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.HighPart); + /* FIXME: If the implementation is exported, we'll need to set other fields. */ + + pstatstg->grfLocksSupported = LOCK_EXCLUSIVE|LOCK_ONLYONCE|WINE_LOCK_READ; + + return S_OK; +} + +static const ILockBytesVtbl FileLockBytesImpl_Vtbl = { + FileLockBytesImpl_QueryInterface, + FileLockBytesImpl_AddRef, + FileLockBytesImpl_Release, + FileLockBytesImpl_ReadAt, + FileLockBytesImpl_WriteAt, + FileLockBytesImpl_Flush, + FileLockBytesImpl_SetSize, + FileLockBytesImpl_LockRegion, + FileLockBytesImpl_UnlockRegion, + FileLockBytesImpl_Stat +}; diff --git a/dlls/coml2/stg_prop.c b/dlls/coml2/stg_prop.c new file mode 100644 index 00000000000..8bc159c7dd5 --- /dev/null +++ b/dlls/coml2/stg_prop.c @@ -0,0 +1,3297 @@ +/* + * Compound Storage (32 bit version) + * Storage implementation + * + * This file contains the compound file implementation + * of the storage interface. + * + * Copyright 1999 Francis Beaudet + * Copyright 1999 Sylvain St-Germain + * Copyright 1999 Thuy Nguyen + * Copyright 2005 Mike McCormack + * Copyright 2005 Juan Lang + * Copyright 2006 Mike McCormack + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * TODO: + * - I don't honor the maximum property set size. + * - Certain bogus files could result in reading past the end of a buffer. + * - Mac-generated files won't be read correctly, even if they're little + * endian, because I disregard whether the generator was a Mac. This means + * strings will probably be munged (as I don't understand Mac scripts.) + * - Not all PROPVARIANT types are supported. + * - User defined properties are not supported, see comment in + * PropertyStorage_ReadFromStream + */ + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winuser.h" +#include "wine/asm.h" +#include "wine/debug.h" +#include "wine/heap.h" +#include "dictionary.h" +#include "storage32.h" +#include "oleauto.h" + +WINE_DEFAULT_DEBUG_CHANNEL(storage); + +static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface ) +{ + return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface); +} + +/* These are documented in MSDN, + * but they don't seem to be in any header file. + */ +#define PROPSETHDR_BYTEORDER_MAGIC 0xfffe +#define PROPSETHDR_OSVER_KIND_WIN16 0 +#define PROPSETHDR_OSVER_KIND_MAC 1 +#define PROPSETHDR_OSVER_KIND_WIN32 2 + +#define CP_UNICODE 1200 + +#define MAX_VERSION_0_PROP_NAME_LENGTH 256 + +#define CFTAG_WINDOWS (-1L) +#define CFTAG_MACINTOSH (-2L) +#define CFTAG_FMTID (-3L) +#define CFTAG_NODATA 0L + +#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align)) + +typedef struct tagPROPERTYSETHEADER +{ + WORD wByteOrder; /* always 0xfffe */ + WORD wFormat; /* can be zero or one */ + DWORD dwOSVer; /* OS version of originating system */ + CLSID clsid; /* application CLSID */ + DWORD reserved; /* always 1 */ +} PROPERTYSETHEADER; + +typedef struct tagFORMATIDOFFSET +{ + FMTID fmtid; + DWORD dwOffset; /* from beginning of stream */ +} FORMATIDOFFSET; + +typedef struct tagPROPERTYSECTIONHEADER +{ + DWORD cbSection; + DWORD cProperties; +} PROPERTYSECTIONHEADER; + +typedef struct tagPROPERTYIDOFFSET +{ + DWORD propid; + DWORD dwOffset; /* from beginning of section */ +} PROPERTYIDOFFSET; + +typedef struct tagPropertyStorage_impl PropertyStorage_impl; + +/* Initializes the property storage from the stream (and undoes any uncommitted + * changes in the process.) Returns an error if there is an error reading or + * if the stream format doesn't match what's expected. + */ +static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *); + +static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *); + +/* Creates the dictionaries used by the property storage. If successful, all + * the dictionaries have been created. If failed, none has been. (This makes + * it a bit easier to deal with destroying them.) + */ +static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *); + +static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *); + +/* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the + * string using PropertyStorage_StringCopy. + */ +static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, + const PROPVARIANT *propvar, UINT targetCP, UINT srcCP); + +/* Copies the string src, which is encoded using code page srcCP, and returns + * it in *dst, in the code page specified by targetCP. The returned string is + * allocated using CoTaskMemAlloc. + * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP + * is CP_UNICODE, the returned string is in fact an LPWSTR. + * Returns S_OK on success, something else on failure. + */ +static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, + UINT targetCP); + +static const IPropertyStorageVtbl IPropertyStorage_Vtbl; + +/*********************************************************************** + * Implementation of IPropertyStorage + */ +struct tagPropertyStorage_impl +{ + IPropertyStorage IPropertyStorage_iface; + LONG ref; + CRITICAL_SECTION cs; + IStream *stm; + BOOL dirty; + FMTID fmtid; + CLSID clsid; + WORD format; + DWORD originatorOS; + DWORD grfFlags; + DWORD grfMode; + UINT codePage; + LCID locale; + PROPID highestProp; + struct dictionary *name_to_propid; + struct dictionary *propid_to_name; + struct dictionary *propid_to_prop; +}; + +static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface) +{ + return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface); +} + +struct enum_stat_prop_stg +{ + IEnumSTATPROPSTG IEnumSTATPROPSTG_iface; + LONG refcount; + PropertyStorage_impl *storage; + STATPROPSTG *stats; + size_t current; + size_t count; +}; + +static struct enum_stat_prop_stg *impl_from_IEnumSTATPROPSTG(IEnumSTATPROPSTG *iface) +{ + return CONTAINING_RECORD(iface, struct enum_stat_prop_stg, IEnumSTATPROPSTG_iface); +} + +static HRESULT WINAPI enum_stat_prop_stg_QueryInterface(IEnumSTATPROPSTG *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IEnumSTATPROPSTG) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IEnumSTATPROPSTG_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI enum_stat_prop_stg_AddRef(IEnumSTATPROPSTG *iface) +{ + struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); + LONG refcount = InterlockedIncrement(&penum->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI enum_stat_prop_stg_Release(IEnumSTATPROPSTG *iface) +{ + struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); + LONG refcount = InterlockedDecrement(&penum->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IPropertyStorage_Release(&penum->storage->IPropertyStorage_iface); + heap_free(penum->stats); + heap_free(penum); + } + + return refcount; +} + +static HRESULT WINAPI enum_stat_prop_stg_Next(IEnumSTATPROPSTG *iface, ULONG celt, STATPROPSTG *ret, ULONG *fetched) +{ + struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); + ULONG count = 0; + WCHAR *name; + + TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched); + + if (penum->current == ~0u) + penum->current = 0; + + while (count < celt && penum->current < penum->count) + { + *ret = penum->stats[penum->current++]; + + if (dictionary_find(penum->storage->propid_to_name, UlongToPtr(ret->propid), (void **)&name)) + { + SIZE_T size = (lstrlenW(name) + 1) * sizeof(WCHAR); + ret->lpwstrName = CoTaskMemAlloc(size); + if (ret->lpwstrName) + memcpy(ret->lpwstrName, name, size); + } + ret++; + count++; + } + + if (fetched) + *fetched = count; + + return count < celt ? S_FALSE : S_OK; +} + +static HRESULT WINAPI enum_stat_prop_stg_Skip(IEnumSTATPROPSTG *iface, ULONG celt) +{ + FIXME("%p, %lu.\n", iface, celt); + + return S_OK; +} + +static HRESULT WINAPI enum_stat_prop_stg_Reset(IEnumSTATPROPSTG *iface) +{ + struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); + + TRACE("%p.\n", iface); + + penum->current = ~0u; + + return S_OK; +} + +static HRESULT WINAPI enum_stat_prop_stg_Clone(IEnumSTATPROPSTG *iface, IEnumSTATPROPSTG **ppenum) +{ + FIXME("%p, %p.\n", iface, ppenum); + + return E_NOTIMPL; +} + +static const IEnumSTATPROPSTGVtbl enum_stat_prop_stg_vtbl = +{ + enum_stat_prop_stg_QueryInterface, + enum_stat_prop_stg_AddRef, + enum_stat_prop_stg_Release, + enum_stat_prop_stg_Next, + enum_stat_prop_stg_Skip, + enum_stat_prop_stg_Reset, + enum_stat_prop_stg_Clone, +}; + +static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg) +{ + struct enum_stat_prop_stg *stg = arg; + PROPID propid = PtrToUlong(k); + const PROPVARIANT *prop = v; + STATPROPSTG *dest; + + dest = &stg->stats[stg->count]; + + dest->lpwstrName = NULL; + dest->propid = propid; + dest->vt = prop->vt; + stg->count++; + + return TRUE; +} + +static BOOL prop_enum_stat_count(const void *k, const void *v, void *extra, void *arg) +{ + DWORD *count = arg; + + *count += 1; + + return TRUE; +} + +static HRESULT create_enum_stat_prop_stg(PropertyStorage_impl *storage, IEnumSTATPROPSTG **ret) +{ + struct enum_stat_prop_stg *enum_obj; + DWORD count; + + enum_obj = heap_alloc_zero(sizeof(*enum_obj)); + if (!enum_obj) + return E_OUTOFMEMORY; + + enum_obj->IEnumSTATPROPSTG_iface.lpVtbl = &enum_stat_prop_stg_vtbl; + enum_obj->refcount = 1; + enum_obj->storage = storage; + IPropertyStorage_AddRef(&storage->IPropertyStorage_iface); + + count = 0; + dictionary_enumerate(storage->propid_to_prop, prop_enum_stat_count, &count); + + if (count) + { + if (!(enum_obj->stats = heap_alloc(sizeof(*enum_obj->stats) * count))) + { + IEnumSTATPROPSTG_Release(&enum_obj->IEnumSTATPROPSTG_iface); + return E_OUTOFMEMORY; + } + + dictionary_enumerate(storage->propid_to_prop, prop_enum_stat, enum_obj); + } + + *ret = &enum_obj->IEnumSTATPROPSTG_iface; + + return S_OK; +} + +/************************************************************************ + * IPropertyStorage_fnQueryInterface (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnQueryInterface( + IPropertyStorage *iface, + REFIID riid, + void** ppvObject) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + + TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject); + + if (!ppvObject) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualGUID(&IID_IUnknown, riid) || + IsEqualGUID(&IID_IPropertyStorage, riid)) + { + IPropertyStorage_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + + return E_NOINTERFACE; +} + +/************************************************************************ + * IPropertyStorage_fnAddRef (IPropertyStorage) + */ +static ULONG WINAPI IPropertyStorage_fnAddRef( + IPropertyStorage *iface) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + return InterlockedIncrement(&This->ref); +} + +/************************************************************************ + * IPropertyStorage_fnRelease (IPropertyStorage) + */ +static ULONG WINAPI IPropertyStorage_fnRelease( + IPropertyStorage *iface) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + ULONG ref; + + ref = InterlockedDecrement(&This->ref); + if (ref == 0) + { + TRACE("Destroying %p\n", This); + if (This->dirty) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + IStream_Release(This->stm); + This->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->cs); + PropertyStorage_DestroyDictionaries(This); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This, + DWORD propid) +{ + PROPVARIANT *ret = NULL; + + dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret); + TRACE("returning %p\n", ret); + return ret; +} + +/* Returns NULL if name is NULL. */ +static PROPVARIANT *PropertyStorage_FindPropertyByName( + PropertyStorage_impl *This, LPCWSTR name) +{ + PROPVARIANT *ret = NULL; + void *propid; + + if (!name) + return NULL; + if (This->codePage == CP_UNICODE) + { + if (dictionary_find(This->name_to_propid, name, &propid)) + ret = PropertyStorage_FindProperty(This, PtrToUlong(propid)); + } + else + { + LPSTR ansiName; + HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE, + &ansiName, This->codePage); + + if (SUCCEEDED(hr)) + { + if (dictionary_find(This->name_to_propid, ansiName, &propid)) + ret = PropertyStorage_FindProperty(This, PtrToUlong(propid)); + CoTaskMemFree(ansiName); + } + } + TRACE("returning %p\n", ret); + return ret; +} + +static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This, + DWORD propid) +{ + LPWSTR ret = NULL; + + dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret); + TRACE("returning %p\n", ret); + return ret; +} + +/************************************************************************ + * IPropertyStorage_fnReadMultiple (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnReadMultiple( + IPropertyStorage* iface, + ULONG cpspec, + const PROPSPEC rgpspec[], + PROPVARIANT rgpropvar[]) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + HRESULT hr = S_OK; + ULONG i; + + TRACE("%p, %lu, %p, %p\n", iface, cpspec, rgpspec, rgpropvar); + + if (!cpspec) + return S_FALSE; + if (!rgpspec || !rgpropvar) + return E_INVALIDARG; + EnterCriticalSection(&This->cs); + for (i = 0; i < cpspec; i++) + { + PropVariantInit(&rgpropvar[i]); + if (rgpspec[i].ulKind == PRSPEC_LPWSTR) + { + PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This, + rgpspec[i].lpwstr); + + if (prop) + PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(), + This->codePage); + } + else + { + switch (rgpspec[i].propid) + { + case PID_CODEPAGE: + rgpropvar[i].vt = VT_I2; + rgpropvar[i].iVal = This->codePage; + break; + case PID_LOCALE: + rgpropvar[i].vt = VT_I4; + rgpropvar[i].lVal = This->locale; + break; + default: + { + PROPVARIANT *prop = PropertyStorage_FindProperty(This, + rgpspec[i].propid); + + if (prop) + PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, + GetACP(), This->codePage); + else + hr = S_FALSE; + } + } + } + } + LeaveCriticalSection(&This->cs); + return hr; +} + +static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, UINT dstCP) +{ + HRESULT hr = S_OK; + int len; + + TRACE("%s, %p, %d, %d\n", + srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst, + dstCP, srcCP); + assert(src); + assert(dst); + *dst = NULL; + if (dstCP == srcCP) + { + size_t len; + + if (dstCP == CP_UNICODE) + len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR); + else + len = strlen(src) + 1; + *dst = CoTaskMemAlloc(len); + if (!*dst) + hr = STG_E_INSUFFICIENTMEMORY; + else + memcpy(*dst, src, len); + } + else + { + if (dstCP == CP_UNICODE) + { + len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0); + *dst = CoTaskMemAlloc(len * sizeof(WCHAR)); + if (!*dst) + hr = STG_E_INSUFFICIENTMEMORY; + else + MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len); + } + else + { + LPCWSTR wideStr = NULL; + LPWSTR wideStr_tmp = NULL; + + if (srcCP == CP_UNICODE) + wideStr = (LPCWSTR)src; + else + { + len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0); + wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (wideStr_tmp) + { + MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len); + wideStr = wideStr_tmp; + } + else + hr = STG_E_INSUFFICIENTMEMORY; + } + if (SUCCEEDED(hr)) + { + len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0, + NULL, NULL); + *dst = CoTaskMemAlloc(len); + if (!*dst) + hr = STG_E_INSUFFICIENTMEMORY; + else + { + BOOL defCharUsed = FALSE; + + if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len, + NULL, &defCharUsed) == 0 || defCharUsed) + { + CoTaskMemFree(*dst); + *dst = NULL; + hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION); + } + } + } + HeapFree(GetProcessHeap(), 0, wideStr_tmp); + } + } + TRACE("returning %#lx (%s)\n", hr, + dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst)); + return hr; +} + +static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, const PROPVARIANT *propvar, + UINT targetCP, UINT srcCP) +{ + HRESULT hr = S_OK; + + assert(prop); + assert(propvar); + + switch (propvar->vt) + { + case VT_LPSTR: + hr = PropertyStorage_StringCopy(propvar->pszVal, srcCP, &prop->pszVal, targetCP); + if (SUCCEEDED(hr)) + prop->vt = VT_LPSTR; + break; + case VT_BSTR: + if ((prop->bstrVal = SysAllocStringLen(propvar->bstrVal, SysStringLen(propvar->bstrVal)))) + prop->vt = VT_BSTR; + else + hr = E_OUTOFMEMORY; + break; + default: + hr = PropVariantCopy(prop, propvar); + } + + return hr; +} + +/* Stores the property with id propid and value propvar into this property + * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's + * type is VT_LPSTR, converts the string using lcid as the source code page + * and This->codePage as the target code page before storing. + * As a side effect, may change This->format to 1 if the type of propvar is + * a version 1-only property. + */ +static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This, + PROPID propid, const PROPVARIANT *propvar, UINT cp) +{ + HRESULT hr = S_OK; + PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid); + + assert(propvar); + if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY) + This->format = 1; + switch (propvar->vt) + { + case VT_DECIMAL: + case VT_I1: + case VT_INT: + case VT_UINT: + case VT_VECTOR|VT_I1: + This->format = 1; + } + TRACE("Setting %#lx to type %d\n", propid, propvar->vt); + if (prop) + { + PropVariantClear(prop); + hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp); + } + else + { + prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(PROPVARIANT)); + if (prop) + { + hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp); + if (SUCCEEDED(hr)) + { + dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop); + if (propid > This->highestProp) + This->highestProp = propid; + } + else + HeapFree(GetProcessHeap(), 0, prop); + } + else + hr = STG_E_INSUFFICIENTMEMORY; + } + return hr; +} + +/* Adds the name srcName to the name dictionaries, mapped to property ID id. + * srcName is encoded in code page cp, and is converted to This->codePage. + * If cp is CP_UNICODE, srcName is actually a unicode string. + * As a side effect, may change This->format to 1 if srcName is too long for + * a version 0 property storage. + * Doesn't validate id. + */ +static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This, + LPCSTR srcName, UINT cp, PROPID id) +{ + LPSTR name; + HRESULT hr; + + assert(srcName); + + hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage); + if (SUCCEEDED(hr)) + { + if (This->codePage == CP_UNICODE) + { + if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH) + This->format = 1; + } + else + { + if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH) + This->format = 1; + } + TRACE("Adding prop name %s, propid %ld\n", + This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) : + debugstr_a(name), id); + dictionary_insert(This->name_to_propid, name, UlongToPtr(id)); + dictionary_insert(This->propid_to_name, UlongToPtr(id), name); + } + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnWriteMultiple (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnWriteMultiple( + IPropertyStorage* iface, + ULONG cpspec, + const PROPSPEC rgpspec[], + const PROPVARIANT rgpropvar[], + PROPID propidNameFirst) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + HRESULT hr = S_OK; + ULONG i; + + TRACE("%p, %lu, %p, %p.\n", iface, cpspec, rgpspec, rgpropvar); + + if (cpspec && (!rgpspec || !rgpropvar)) + return E_INVALIDARG; + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + EnterCriticalSection(&This->cs); + This->dirty = TRUE; + This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()), + PROPSETHDR_OSVER_KIND_WIN32) ; + for (i = 0; i < cpspec; i++) + { + if (rgpspec[i].ulKind == PRSPEC_LPWSTR) + { + PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This, + rgpspec[i].lpwstr); + + if (prop) + PropVariantCopy(prop, &rgpropvar[i]); + else + { + /* Note that I don't do the special cases here that I do below, + * because naming the special PIDs isn't supported. + */ + if (propidNameFirst < PID_FIRST_USABLE || + propidNameFirst >= PID_MIN_READONLY) + hr = STG_E_INVALIDPARAMETER; + else + { + PROPID nextId = max(propidNameFirst, This->highestProp + 1); + + hr = PropertyStorage_StoreNameWithId(This, + (LPCSTR)rgpspec[i].lpwstr, CP_UNICODE, nextId); + if (SUCCEEDED(hr)) + hr = PropertyStorage_StorePropWithId(This, nextId, + &rgpropvar[i], GetACP()); + } + } + } + else + { + switch (rgpspec[i].propid) + { + case PID_DICTIONARY: + /* Can't set the dictionary */ + hr = STG_E_INVALIDPARAMETER; + break; + case PID_CODEPAGE: + /* Can only set the code page if nothing else has been set */ + if (dictionary_num_entries(This->propid_to_prop) == 0 && + rgpropvar[i].vt == VT_I2) + { + This->codePage = (USHORT)rgpropvar[i].iVal; + if (This->codePage == CP_UNICODE) + This->grfFlags &= ~PROPSETFLAG_ANSI; + else + This->grfFlags |= PROPSETFLAG_ANSI; + } + else + hr = STG_E_INVALIDPARAMETER; + break; + case PID_LOCALE: + /* Can only set the locale if nothing else has been set */ + if (dictionary_num_entries(This->propid_to_prop) == 0 && + rgpropvar[i].vt == VT_I4) + This->locale = rgpropvar[i].lVal; + else + hr = STG_E_INVALIDPARAMETER; + break; + case PID_ILLEGAL: + /* silently ignore like MSDN says */ + break; + default: + if (rgpspec[i].propid >= PID_MIN_READONLY) + hr = STG_E_INVALIDPARAMETER; + else + hr = PropertyStorage_StorePropWithId(This, + rgpspec[i].propid, &rgpropvar[i], GetACP()); + } + } + } + if (This->grfFlags & PROPSETFLAG_UNBUFFERED) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnDeleteMultiple (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple( + IPropertyStorage* iface, + ULONG cpspec, + const PROPSPEC rgpspec[]) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + ULONG i; + HRESULT hr; + + TRACE("%p, %ld, %p.\n", iface, cpspec, rgpspec); + + if (cpspec && !rgpspec) + return E_INVALIDARG; + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + hr = S_OK; + EnterCriticalSection(&This->cs); + This->dirty = TRUE; + for (i = 0; i < cpspec; i++) + { + if (rgpspec[i].ulKind == PRSPEC_LPWSTR) + { + void *propid; + + if (dictionary_find(This->name_to_propid, rgpspec[i].lpwstr, &propid)) + dictionary_remove(This->propid_to_prop, propid); + } + else + { + if (rgpspec[i].propid >= PID_FIRST_USABLE && + rgpspec[i].propid < PID_MIN_READONLY) + dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].propid)); + else + hr = STG_E_INVALIDPARAMETER; + } + } + if (This->grfFlags & PROPSETFLAG_UNBUFFERED) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnReadPropertyNames (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames( + IPropertyStorage* iface, + ULONG cpropid, + const PROPID rgpropid[], + LPOLESTR rglpwstrName[]) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + ULONG i; + HRESULT hr = S_FALSE; + + TRACE("%p, %ld, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName); + + if (cpropid && (!rgpropid || !rglpwstrName)) + return E_INVALIDARG; + EnterCriticalSection(&This->cs); + for (i = 0; i < cpropid && SUCCEEDED(hr); i++) + { + LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]); + + if (name) + { + size_t len = lstrlenW(name); + + hr = S_OK; + rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR)); + if (rglpwstrName[i]) + memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR)); + else + hr = STG_E_INSUFFICIENTMEMORY; + } + else + rglpwstrName[i] = NULL; + } + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnWritePropertyNames (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames( + IPropertyStorage* iface, + ULONG cpropid, + const PROPID rgpropid[], + const LPOLESTR rglpwstrName[]) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + ULONG i; + HRESULT hr; + + TRACE("%p, %lu, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName); + + if (cpropid && (!rgpropid || !rglpwstrName)) + return E_INVALIDARG; + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + hr = S_OK; + EnterCriticalSection(&This->cs); + This->dirty = TRUE; + for (i = 0; SUCCEEDED(hr) && i < cpropid; i++) + { + if (rgpropid[i] != PID_ILLEGAL) + hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i], + CP_UNICODE, rgpropid[i]); + } + if (This->grfFlags & PROPSETFLAG_UNBUFFERED) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames( + IPropertyStorage* iface, + ULONG cpropid, + const PROPID rgpropid[]) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + ULONG i; + HRESULT hr; + + TRACE("%p, %ld, %p.\n", iface, cpropid, rgpropid); + + if (cpropid && !rgpropid) + return E_INVALIDARG; + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + hr = S_OK; + EnterCriticalSection(&This->cs); + This->dirty = TRUE; + for (i = 0; i < cpropid; i++) + { + LPWSTR name = NULL; + + if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name)) + { + dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i])); + dictionary_remove(This->name_to_propid, name); + } + } + if (This->grfFlags & PROPSETFLAG_UNBUFFERED) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnCommit (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnCommit( + IPropertyStorage* iface, + DWORD grfCommitFlags) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + HRESULT hr; + + TRACE("%p, %#lx.\n", iface, grfCommitFlags); + + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + EnterCriticalSection(&This->cs); + if (This->dirty) + hr = PropertyStorage_WriteToStream(This); + else + hr = S_OK; + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnRevert (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnRevert( + IPropertyStorage* iface) +{ + HRESULT hr; + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + + TRACE("%p\n", iface); + + EnterCriticalSection(&This->cs); + if (This->dirty) + { + PropertyStorage_DestroyDictionaries(This); + hr = PropertyStorage_CreateDictionaries(This); + if (SUCCEEDED(hr)) + hr = PropertyStorage_ReadFromStream(This); + } + else + hr = S_OK; + LeaveCriticalSection(&This->cs); + return hr; +} + +/************************************************************************ + * IPropertyStorage_fnEnum (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnEnum(IPropertyStorage *iface, IEnumSTATPROPSTG **ppenum) +{ + PropertyStorage_impl *storage = impl_from_IPropertyStorage(iface); + + TRACE("%p, %p.\n", iface, ppenum); + + return create_enum_stat_prop_stg(storage, ppenum); +} + +/************************************************************************ + * IPropertyStorage_fnSetTimes (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnSetTimes( + IPropertyStorage* iface, + const FILETIME* pctime, + const FILETIME* patime, + const FILETIME* pmtime) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +/************************************************************************ + * IPropertyStorage_fnSetClass (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnSetClass( + IPropertyStorage* iface, + REFCLSID clsid) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + + TRACE("%p, %s\n", iface, debugstr_guid(clsid)); + + if (!clsid) + return E_INVALIDARG; + if (!(This->grfMode & STGM_READWRITE)) + return STG_E_ACCESSDENIED; + This->clsid = *clsid; + This->dirty = TRUE; + if (This->grfFlags & PROPSETFLAG_UNBUFFERED) + IPropertyStorage_Commit(iface, STGC_DEFAULT); + return S_OK; +} + +/************************************************************************ + * IPropertyStorage_fnStat (IPropertyStorage) + */ +static HRESULT WINAPI IPropertyStorage_fnStat( + IPropertyStorage* iface, + STATPROPSETSTG* statpsstg) +{ + PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); + STATSTG stat; + HRESULT hr; + + TRACE("%p, %p\n", iface, statpsstg); + + if (!statpsstg) + return E_INVALIDARG; + + hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME); + if (SUCCEEDED(hr)) + { + statpsstg->fmtid = This->fmtid; + statpsstg->clsid = This->clsid; + statpsstg->grfFlags = This->grfFlags; + statpsstg->mtime = stat.mtime; + statpsstg->ctime = stat.ctime; + statpsstg->atime = stat.atime; + statpsstg->dwOSVersion = This->originatorOS; + } + return hr; +} + +static int PropertyStorage_PropNameCompare(const void *a, const void *b, + void *extra) +{ + PropertyStorage_impl *This = extra; + + if (This->codePage == CP_UNICODE) + { + TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b)); + if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) + return wcscmp(a, b); + else + return lstrcmpiW(a, b); + } + else + { + TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b)); + if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) + return lstrcmpA(a, b); + else + return lstrcmpiA(a, b); + } +} + +static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra) +{ + CoTaskMemFree(k); +} + +static int PropertyStorage_PropCompare(const void *a, const void *b, + void *extra) +{ + TRACE("%lu, %lu.\n", PtrToUlong(a), PtrToUlong(b)); + return PtrToUlong(a) - PtrToUlong(b); +} + +static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra) +{ + PropVariantClear(d); + HeapFree(GetProcessHeap(), 0, d); +} + +#ifdef WORDS_BIGENDIAN +/* Swaps each character in str to or from little endian; assumes the conversion + * is symmetric, that is, that lendian16toh is equivalent to htole16. + */ +static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len) +{ + DWORD i; + + /* Swap characters to host order. + * FIXME: alignment? + */ + for (i = 0; i < len; i++) + str[i] = lendian16toh(str[i]); +} +#else +#define PropertyStorage_ByteSwapString(s, l) +#endif + +static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size) +{ + return CoTaskMemAlloc(size); +} + +struct read_buffer +{ + BYTE *data; + size_t size; +}; + +static HRESULT buffer_test_offset(const struct read_buffer *buffer, size_t offset, size_t len) +{ + return len > buffer->size || offset > buffer->size - len ? STG_E_READFAULT : S_OK; +} + +static HRESULT buffer_read_uint64(const struct read_buffer *buffer, size_t offset, ULARGE_INTEGER *data) +{ + HRESULT hr; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) + StorageUtl_ReadULargeInteger(buffer->data, offset, data); + + return hr; +} + +static HRESULT buffer_read_dword(const struct read_buffer *buffer, size_t offset, DWORD *data) +{ + HRESULT hr; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) + StorageUtl_ReadDWord(buffer->data, offset, data); + + return hr; +} + +static HRESULT buffer_read_word(const struct read_buffer *buffer, size_t offset, WORD *data) +{ + HRESULT hr; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) + StorageUtl_ReadWord(buffer->data, offset, data); + + return hr; +} + +static HRESULT buffer_read_byte(const struct read_buffer *buffer, size_t offset, BYTE *data) +{ + HRESULT hr; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) + *data = *(buffer->data + offset); + + return hr; +} + +static HRESULT buffer_read_len(const struct read_buffer *buffer, size_t offset, void *dest, size_t len) +{ + HRESULT hr; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, len))) + memcpy(dest, buffer->data + offset, len); + + return hr; +} + +static HRESULT propertystorage_read_scalar(PROPVARIANT *prop, const struct read_buffer *buffer, size_t offset, + UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data) +{ + HRESULT hr; + + assert(!(prop->vt & (VT_ARRAY | VT_VECTOR))); + + switch (prop->vt) + { + case VT_EMPTY: + case VT_NULL: + hr = S_OK; + break; + case VT_I1: + hr = buffer_read_byte(buffer, offset, (BYTE *)&prop->cVal); + TRACE("Read char 0x%x ('%c')\n", prop->cVal, prop->cVal); + break; + case VT_UI1: + hr = buffer_read_byte(buffer, offset, &prop->bVal); + TRACE("Read byte 0x%x\n", prop->bVal); + break; + case VT_BOOL: + hr = buffer_read_word(buffer, offset, (WORD *)&prop->boolVal); + TRACE("Read bool %d\n", prop->boolVal); + break; + case VT_I2: + hr = buffer_read_word(buffer, offset, (WORD *)&prop->iVal); + TRACE("Read short %d\n", prop->iVal); + break; + case VT_UI2: + hr = buffer_read_word(buffer, offset, &prop->uiVal); + TRACE("Read ushort %d\n", prop->uiVal); + break; + case VT_INT: + case VT_I4: + hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->lVal); + TRACE("Read long %ld\n", prop->lVal); + break; + case VT_UINT: + case VT_UI4: + hr = buffer_read_dword(buffer, offset, &prop->ulVal); + TRACE("Read ulong %ld\n", prop->ulVal); + break; + case VT_I8: + hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->hVal); + TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->hVal.QuadPart)); + break; + case VT_UI8: + hr = buffer_read_uint64(buffer, offset, &prop->uhVal); + TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->uhVal.QuadPart)); + break; + case VT_R8: + hr = buffer_read_len(buffer, offset, &prop->dblVal, sizeof(prop->dblVal)); + TRACE("Read double %f\n", prop->dblVal); + break; + case VT_LPSTR: + { + DWORD count; + + if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) + break; + + offset += sizeof(DWORD); + + if (codepage == CP_UNICODE && count % sizeof(WCHAR)) + { + WARN("Unicode string has odd number of bytes\n"); + hr = STG_E_INVALIDHEADER; + } + else + { + prop->pszVal = allocate(allocate_data, count); + if (prop->pszVal) + { + if (FAILED(hr = buffer_read_len(buffer, offset, prop->pszVal, count))) + break; + + /* This is stored in the code page specified in codepage. + * Don't convert it, the caller will just store it as-is. + */ + if (codepage == CP_UNICODE) + { + /* Make sure it's NULL-terminated */ + prop->pszVal[count / sizeof(WCHAR) - 1] = '\0'; + TRACE("Read string value %s\n", + debugstr_w(prop->pwszVal)); + } + else + { + /* Make sure it's NULL-terminated */ + prop->pszVal[count - 1] = '\0'; + TRACE("Read string value %s\n", debugstr_a(prop->pszVal)); + } + } + else + hr = STG_E_INSUFFICIENTMEMORY; + } + break; + } + case VT_BSTR: + { + DWORD count, wcount; + + if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) + break; + + offset += sizeof(DWORD); + + if (codepage == CP_UNICODE && count % sizeof(WCHAR)) + { + WARN("Unicode string has odd number of bytes\n"); + hr = STG_E_INVALIDHEADER; + } + else + { + if (codepage == CP_UNICODE) + wcount = count / sizeof(WCHAR); + else + { + if (FAILED(hr = buffer_test_offset(buffer, offset, count))) + break; + wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, NULL, 0); + } + + prop->bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */ + + if (prop->bstrVal) + { + if (codepage == CP_UNICODE) + hr = buffer_read_len(buffer, offset, prop->bstrVal, count); + else + MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->bstrVal, wcount); + + prop->bstrVal[wcount - 1] = '\0'; + TRACE("Read string value %s\n", debugstr_w(prop->bstrVal)); + } + else + hr = STG_E_INSUFFICIENTMEMORY; + } + break; + } + case VT_BLOB: + { + DWORD count; + + if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) + break; + + offset += sizeof(DWORD); + + prop->blob.cbSize = count; + prop->blob.pBlobData = allocate(allocate_data, count); + if (prop->blob.pBlobData) + { + hr = buffer_read_len(buffer, offset, prop->blob.pBlobData, count); + TRACE("Read blob value of size %ld\n", count); + } + else + hr = STG_E_INSUFFICIENTMEMORY; + break; + } + case VT_LPWSTR: + { + DWORD count; + + if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) + break; + + offset += sizeof(DWORD); + + prop->pwszVal = allocate(allocate_data, count * sizeof(WCHAR)); + if (prop->pwszVal) + { + if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->pwszVal, count * sizeof(WCHAR)))) + { + /* make sure string is NULL-terminated */ + prop->pwszVal[count - 1] = '\0'; + PropertyStorage_ByteSwapString(prop->pwszVal, count); + TRACE("Read string value %s\n", debugstr_w(prop->pwszVal)); + } + } + else + hr = STG_E_INSUFFICIENTMEMORY; + break; + } + case VT_FILETIME: + hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->filetime); + break; + case VT_CF: + { + DWORD len = 0, tag = 0; + + if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &len))) + hr = buffer_read_dword(buffer, offset + sizeof(DWORD), &tag); + if (FAILED(hr)) + break; + + offset += 2 * sizeof(DWORD); + + if (len > 8) + { + len -= 8; + prop->pclipdata = allocate(allocate_data, sizeof (CLIPDATA)); + prop->pclipdata->cbSize = len; + prop->pclipdata->ulClipFmt = tag; + prop->pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->pclipdata->ulClipFmt)); + hr = buffer_read_len(buffer, offset, prop->pclipdata->pClipData, len - sizeof(prop->pclipdata->ulClipFmt)); + } + else + hr = STG_E_INVALIDPARAMETER; + } + break; + case VT_CLSID: + if (!(prop->puuid = allocate(allocate_data, sizeof (*prop->puuid)))) + return STG_E_INSUFFICIENTMEMORY; + + if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->puuid)))) + StorageUtl_ReadGUID(buffer->data, offset, prop->puuid); + + break; + default: + FIXME("unsupported type %d\n", prop->vt); + hr = STG_E_INVALIDPARAMETER; + } + + return hr; +} + +static size_t propertystorage_get_elemsize(const PROPVARIANT *prop) +{ + if (!(prop->vt & VT_VECTOR)) + return 0; + + switch (prop->vt & ~VT_VECTOR) + { + case VT_I1: return sizeof(*prop->cac.pElems); + case VT_UI1: return sizeof(*prop->caub.pElems); + case VT_I2: return sizeof(*prop->cai.pElems); + case VT_UI2: return sizeof(*prop->caui.pElems); + case VT_BOOL: return sizeof(*prop->cabool.pElems); + case VT_I4: return sizeof(*prop->cal.pElems); + case VT_UI4: return sizeof(*prop->caul.pElems); + case VT_R4: return sizeof(*prop->caflt.pElems); + case VT_ERROR: return sizeof(*prop->cascode.pElems); + case VT_I8: return sizeof(*prop->cah.pElems); + case VT_UI8: return sizeof(*prop->cauh.pElems); + case VT_R8: return sizeof(*prop->cadbl.pElems); + case VT_CY: return sizeof(*prop->cacy.pElems); + case VT_DATE: return sizeof(*prop->cadate.pElems); + case VT_FILETIME: return sizeof(*prop->cafiletime.pElems); + case VT_CLSID: return sizeof(*prop->cauuid.pElems); + case VT_VARIANT: return sizeof(*prop->capropvar.pElems); + default: + FIXME("Unhandled type %#x.\n", prop->vt); + return 0; + } +} + +static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const struct read_buffer *buffer, + size_t offset, UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data) +{ + HRESULT hr; + DWORD vt; + + assert(prop); + assert(buffer->data); + + if (FAILED(hr = buffer_read_dword(buffer, offset, &vt))) + return hr; + + offset += sizeof(DWORD); + prop->vt = vt; + + if (prop->vt & VT_VECTOR) + { + DWORD count, i; + + switch (prop->vt & VT_VECTOR) + { + case VT_BSTR: + case VT_VARIANT: + case VT_LPSTR: + case VT_LPWSTR: + case VT_CF: + FIXME("Vector with variable length elements are not supported.\n"); + return STG_E_INVALIDPARAMETER; + default: + ; + } + + if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &count))) + { + size_t elemsize = propertystorage_get_elemsize(prop); + PROPVARIANT elem; + + offset += sizeof(DWORD); + + if ((prop->capropvar.pElems = allocate(allocate_data, elemsize * count))) + { + prop->capropvar.cElems = count; + elem.vt = prop->vt & ~VT_VECTOR; + + for (i = 0; i < count; ++i) + { + if (SUCCEEDED(hr = propertystorage_read_scalar(&elem, buffer, offset + i * elemsize, codepage, + allocate, allocate_data))) + { + memcpy(&prop->capropvar.pElems[i], &elem.lVal, elemsize); + } + } + } + else + hr = STG_E_INSUFFICIENTMEMORY; + } + } + else if (prop->vt & VT_ARRAY) + { + FIXME("VT_ARRAY properties are not supported.\n"); + hr = STG_E_INVALIDPARAMETER; + } + else + hr = propertystorage_read_scalar(prop, buffer, offset, codepage, allocate, allocate_data); + + return hr; +} + +static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm, + PROPERTYSETHEADER *hdr) +{ + BYTE buf[sizeof(PROPERTYSETHEADER)]; + ULONG count = 0; + HRESULT hr; + + assert(stm); + assert(hdr); + hr = IStream_Read(stm, buf, sizeof(buf), &count); + if (SUCCEEDED(hr)) + { + if (count != sizeof(buf)) + { + WARN("read only %ld\n", count); + hr = STG_E_INVALIDHEADER; + } + else + { + StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder), + &hdr->wByteOrder); + StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat), + &hdr->wFormat); + StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer), + &hdr->dwOSVer); + StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid), + &hdr->clsid); + StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved), + &hdr->reserved); + } + } + TRACE("returning %#lx\n", hr); + return hr; +} + +static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm, + FORMATIDOFFSET *fmt) +{ + BYTE buf[sizeof(FORMATIDOFFSET)]; + ULONG count = 0; + HRESULT hr; + + assert(stm); + assert(fmt); + hr = IStream_Read(stm, buf, sizeof(buf), &count); + if (SUCCEEDED(hr)) + { + if (count != sizeof(buf)) + { + WARN("read only %ld\n", count); + hr = STG_E_INVALIDHEADER; + } + else + { + StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid), + &fmt->fmtid); + StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset), + &fmt->dwOffset); + } + } + TRACE("returning %#lx\n", hr); + return hr; +} + +static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm, + PROPERTYSECTIONHEADER *hdr) +{ + BYTE buf[sizeof(PROPERTYSECTIONHEADER)]; + ULONG count = 0; + HRESULT hr; + + assert(stm); + assert(hdr); + hr = IStream_Read(stm, buf, sizeof(buf), &count); + if (SUCCEEDED(hr)) + { + if (count != sizeof(buf)) + { + WARN("read only %ld\n", count); + hr = STG_E_INVALIDHEADER; + } + else + { + StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, + cbSection), &hdr->cbSection); + StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, + cProperties), &hdr->cProperties); + } + } + TRACE("returning %#lx\n", hr); + return hr; +} + +/* Reads the dictionary from the memory buffer beginning at ptr. Interprets + * the entries according to the values of This->codePage and This->locale. + */ +static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This, const struct read_buffer *buffer, + size_t offset) +{ + DWORD numEntries, i; + HRESULT hr; + + assert(This->name_to_propid); + assert(This->propid_to_name); + + if (FAILED(hr = buffer_read_dword(buffer, offset, &numEntries))) + return hr; + + TRACE("Reading %ld entries:\n", numEntries); + + offset += sizeof(DWORD); + + for (i = 0; SUCCEEDED(hr) && i < numEntries; i++) + { + PROPID propid; + DWORD cbEntry; + WCHAR ch = 0; + + if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &propid))) + hr = buffer_read_dword(buffer, offset + sizeof(PROPID), &cbEntry); + if (FAILED(hr)) + break; + + offset += sizeof(PROPID) + sizeof(DWORD); + + if (FAILED(hr = buffer_test_offset(buffer, offset, This->codePage == CP_UNICODE ? + ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3) : cbEntry))) + { + WARN("Broken name length for entry %ld.\n", i); + return hr; + } + + /* Make sure the source string is NULL-terminated */ + if (This->codePage != CP_UNICODE) + buffer_read_byte(buffer, offset + cbEntry - 1, (BYTE *)&ch); + else + buffer_read_word(buffer, offset + (cbEntry - 1) * sizeof(WCHAR), &ch); + + if (ch) + { + WARN("Dictionary entry name %ld is not null-terminated.\n", i); + return E_FAIL; + } + + TRACE("Reading entry with ID %#lx, %ld chars, name %s.\n", propid, cbEntry, This->codePage == CP_UNICODE ? + debugstr_wn((WCHAR *)buffer->data, cbEntry) : debugstr_an((char *)buffer->data, cbEntry)); + + hr = PropertyStorage_StoreNameWithId(This, (char *)buffer->data + offset, This->codePage, propid); + /* Unicode entries are padded to DWORD boundaries */ + if (This->codePage == CP_UNICODE) + cbEntry = ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3); + + offset += cbEntry; + } + + return hr; +} + +static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This) +{ + struct read_buffer read_buffer; + PROPERTYSETHEADER hdr; + FORMATIDOFFSET fmtOffset; + PROPERTYSECTIONHEADER sectionHdr; + LARGE_INTEGER seek; + ULONG i; + STATSTG stat; + HRESULT hr; + BYTE *buf = NULL; + ULONG count = 0; + DWORD dictOffset = 0; + + This->dirty = FALSE; + This->highestProp = 0; + hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME); + if (FAILED(hr)) + goto end; + if (stat.cbSize.HighPart) + { + WARN("stream too big\n"); + /* maximum size varies, but it can't be this big */ + hr = STG_E_INVALIDHEADER; + goto end; + } + if (stat.cbSize.LowPart == 0) + { + /* empty stream is okay */ + hr = S_OK; + goto end; + } + else if (stat.cbSize.LowPart < sizeof(PROPERTYSETHEADER) + + sizeof(FORMATIDOFFSET)) + { + WARN("stream too small\n"); + hr = STG_E_INVALIDHEADER; + goto end; + } + seek.QuadPart = 0; + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr); + /* I've only seen reserved == 1, but the article says I shouldn't disallow + * higher values. + */ + if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1) + { + WARN("bad magic in prop set header\n"); + hr = STG_E_INVALIDHEADER; + goto end; + } + if (hdr.wFormat != 0 && hdr.wFormat != 1) + { + WARN("bad format version %d\n", hdr.wFormat); + hr = STG_E_INVALIDHEADER; + goto end; + } + This->format = hdr.wFormat; + This->clsid = hdr.clsid; + This->originatorOS = hdr.dwOSVer; + if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC) + WARN("File comes from a Mac, strings will probably be screwed up\n"); + hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset); + if (FAILED(hr)) + goto end; + if (fmtOffset.dwOffset > stat.cbSize.LowPart) + { + WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset, stat.cbSize.LowPart); + hr = STG_E_INVALIDHEADER; + goto end; + } + /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there + * follows not one, but two sections. The first contains the standard properties + * for the document summary information, and the second consists of user-defined + * properties. This is the only case in which multiple sections are + * allowed. + * Reading the second stream isn't implemented yet. + */ + hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr); + if (FAILED(hr)) + goto end; + /* The section size includes the section header, so check it */ + if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER)) + { + WARN("section header too small, got %ld\n", sectionHdr.cbSection); + hr = STG_E_INVALIDHEADER; + goto end; + } + buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection - + sizeof(PROPERTYSECTIONHEADER)); + if (!buf) + { + hr = STG_E_INSUFFICIENTMEMORY; + goto end; + } + read_buffer.data = buf; + read_buffer.size = sectionHdr.cbSection - sizeof(sectionHdr); + + hr = IStream_Read(This->stm, read_buffer.data, read_buffer.size, &count); + if (FAILED(hr)) + goto end; + TRACE("Reading %ld properties:\n", sectionHdr.cProperties); + for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++) + { + PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(read_buffer.data + + i * sizeof(PROPERTYIDOFFSET)); + + if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) || + idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD)) + hr = STG_E_INVALIDPOINTER; + else + { + if (idOffset->propid >= PID_FIRST_USABLE && + idOffset->propid < PID_MIN_READONLY && idOffset->propid > + This->highestProp) + This->highestProp = idOffset->propid; + if (idOffset->propid == PID_DICTIONARY) + { + /* Don't read the dictionary yet, its entries depend on the + * code page. Just store the offset so we know to read it + * later. + */ + dictOffset = idOffset->dwOffset; + TRACE("Dictionary offset is %ld\n", dictOffset); + } + else + { + PROPVARIANT prop; + + PropVariantInit(&prop); + if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, &read_buffer, + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), This->codePage, + Allocate_CoTaskMemAlloc, NULL))) + { + TRACE("Read property with ID %#lx, type %d\n", idOffset->propid, prop.vt); + switch(idOffset->propid) + { + case PID_CODEPAGE: + if (prop.vt == VT_I2) + This->codePage = (USHORT)prop.iVal; + break; + case PID_LOCALE: + if (prop.vt == VT_I4) + This->locale = (LCID)prop.lVal; + break; + case PID_BEHAVIOR: + if (prop.vt == VT_I4 && prop.lVal) + This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE; + /* The format should already be 1, but just in case */ + This->format = 1; + break; + default: + hr = PropertyStorage_StorePropWithId(This, + idOffset->propid, &prop, This->codePage); + } + } + PropVariantClear(&prop); + } + } + } + if (!This->codePage) + { + /* default to Unicode unless told not to, as specified on msdn */ + if (This->grfFlags & PROPSETFLAG_ANSI) + This->codePage = GetACP(); + else + This->codePage = CP_UNICODE; + } + if (!This->locale) + This->locale = LOCALE_SYSTEM_DEFAULT; + TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale); + if (dictOffset) + hr = PropertyStorage_ReadDictionary(This, &read_buffer, dictOffset - sizeof(PROPERTYSECTIONHEADER)); + +end: + HeapFree(GetProcessHeap(), 0, buf); + if (FAILED(hr)) + { + dictionary_destroy(This->name_to_propid); + This->name_to_propid = NULL; + dictionary_destroy(This->propid_to_name); + This->propid_to_name = NULL; + dictionary_destroy(This->propid_to_prop); + This->propid_to_prop = NULL; + } + return hr; +} + +static void PropertyStorage_MakeHeader(PropertyStorage_impl *This, + PROPERTYSETHEADER *hdr) +{ + assert(hdr); + StorageUtl_WriteWord(&hdr->wByteOrder, 0, PROPSETHDR_BYTEORDER_MAGIC); + StorageUtl_WriteWord(&hdr->wFormat, 0, This->format); + StorageUtl_WriteDWord(&hdr->dwOSVer, 0, This->originatorOS); + StorageUtl_WriteGUID(&hdr->clsid, 0, &This->clsid); + StorageUtl_WriteDWord(&hdr->reserved, 0, 1); +} + +static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This, + FORMATIDOFFSET *fmtOffset) +{ + assert(fmtOffset); + StorageUtl_WriteGUID(fmtOffset, 0, &This->fmtid); + StorageUtl_WriteDWord(fmtOffset, offsetof(FORMATIDOFFSET, dwOffset), + sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)); +} + +static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps, + PROPERTYSECTIONHEADER *hdr) +{ + assert(hdr); + StorageUtl_WriteDWord(hdr, 0, cbSection); + StorageUtl_WriteDWord(hdr, offsetof(PROPERTYSECTIONHEADER, cProperties), numProps); +} + +static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset, + PROPERTYIDOFFSET *propIdOffset) +{ + assert(propIdOffset); + StorageUtl_WriteDWord(propIdOffset, 0, propid); + StorageUtl_WriteDWord(propIdOffset, offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset); +} + +static inline HRESULT PropertyStorage_WriteWStringToStream(IStream *stm, + LPCWSTR str, DWORD len, DWORD *written) +{ +#ifdef WORDS_BIGENDIAN + WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + HRESULT hr; + + if (!leStr) + return E_OUTOFMEMORY; + memcpy(leStr, str, len * sizeof(WCHAR)); + PropertyStorage_ByteSwapString(leStr, len); + hr = IStream_Write(stm, leStr, len, written); + HeapFree(GetProcessHeap(), 0, leStr); + return hr; +#else + return IStream_Write(stm, str, len * sizeof(WCHAR), written); +#endif +} + +struct DictionaryClosure +{ + HRESULT hr; + DWORD bytesWritten; +}; + +static BOOL PropertyStorage_DictionaryWriter(const void *key, + const void *value, void *extra, void *closure) +{ + PropertyStorage_impl *This = extra; + struct DictionaryClosure *c = closure; + DWORD propid, keyLen; + ULONG count; + + assert(key); + assert(closure); + StorageUtl_WriteDWord(&propid, 0, PtrToUlong(value)); + c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count); + if (FAILED(c->hr)) + goto end; + c->bytesWritten += sizeof(DWORD); + if (This->codePage == CP_UNICODE) + { + DWORD pad = 0, pad_len; + + StorageUtl_WriteDWord(&keyLen, 0, lstrlenW((LPCWSTR)key) + 1); + c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); + if (FAILED(c->hr)) + goto end; + c->bytesWritten += sizeof(DWORD); + c->hr = PropertyStorage_WriteWStringToStream(This->stm, key, keyLen, + &count); + if (FAILED(c->hr)) + goto end; + keyLen *= sizeof(WCHAR); + c->bytesWritten += keyLen; + + /* Align to 4 bytes. */ + pad_len = sizeof(DWORD) - keyLen % sizeof(DWORD); + if (pad_len) + { + c->hr = IStream_Write(This->stm, &pad, pad_len, &count); + if (FAILED(c->hr)) + goto end; + c->bytesWritten += pad_len; + } + } + else + { + StorageUtl_WriteDWord(&keyLen, 0, strlen((LPCSTR)key) + 1); + c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); + if (FAILED(c->hr)) + goto end; + c->bytesWritten += sizeof(DWORD); + c->hr = IStream_Write(This->stm, key, keyLen, &count); + if (FAILED(c->hr)) + goto end; + c->bytesWritten += keyLen; + } +end: + return SUCCEEDED(c->hr); +} + +#define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET) + +/* Writes the dictionary to the stream. Assumes without checking that the + * dictionary isn't empty. + */ +static HRESULT PropertyStorage_WriteDictionaryToStream( + PropertyStorage_impl *This, DWORD *sectionOffset) +{ + HRESULT hr; + LARGE_INTEGER seek; + PROPERTYIDOFFSET propIdOffset; + ULONG count; + DWORD dwTemp; + struct DictionaryClosure closure; + + assert(sectionOffset); + + /* The dictionary's always the first property written, so seek to its + * spot. + */ + seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER); + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset, + &propIdOffset); + hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); + if (FAILED(hr)) + goto end; + + seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + StorageUtl_WriteDWord(&dwTemp, 0, dictionary_num_entries(This->name_to_propid)); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (FAILED(hr)) + goto end; + *sectionOffset += sizeof(dwTemp); + + closure.hr = S_OK; + closure.bytesWritten = 0; + dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter, + &closure); + hr = closure.hr; + if (FAILED(hr)) + goto end; + *sectionOffset += closure.bytesWritten; + if (closure.bytesWritten % sizeof(DWORD)) + { + DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD); + TRACE("adding %ld bytes of padding\n", padding); + *sectionOffset += padding; + } + +end: + return hr; +} + +static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This, + DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset) +{ + DWORD len, dwType, dwTemp, bytesWritten; + HRESULT hr; + LARGE_INTEGER seek; + PROPERTYIDOFFSET propIdOffset; + ULARGE_INTEGER ularge; + ULONG count; + + assert(var); + assert(sectionOffset); + + TRACE("%p, %ld, %#lx, %d, %ld.\n", This, propNum, propid, var->vt, + *sectionOffset); + + seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) + + propNum * sizeof(PROPERTYIDOFFSET); + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset); + hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); + if (FAILED(hr)) + goto end; + + seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + StorageUtl_WriteDWord(&dwType, 0, var->vt); + hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count); + if (FAILED(hr)) + goto end; + *sectionOffset += sizeof(dwType); + + switch (var->vt) + { + case VT_EMPTY: + case VT_NULL: + bytesWritten = 0; + break; + case VT_I1: + case VT_UI1: + hr = IStream_Write(This->stm, &var->cVal, sizeof(var->cVal), + &count); + bytesWritten = count; + break; + case VT_I2: + case VT_UI2: + { + WORD wTemp; + + StorageUtl_WriteWord(&wTemp, 0, var->iVal); + hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count); + bytesWritten = count; + break; + } + case VT_I4: + case VT_UI4: + { + StorageUtl_WriteDWord(&dwTemp, 0, var->lVal); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + bytesWritten = count; + break; + } + case VT_I8: + case VT_UI8: + { + StorageUtl_WriteULargeInteger(&ularge, 0, &var->uhVal); + hr = IStream_Write(This->stm, &ularge, sizeof(ularge), &bytesWritten); + break; + } + case VT_LPSTR: + { + if (This->codePage == CP_UNICODE) + len = (lstrlenW(var->pwszVal) + 1) * sizeof(WCHAR); + else + len = lstrlenA(var->pszVal) + 1; + StorageUtl_WriteDWord(&dwTemp, 0, len); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (FAILED(hr)) + goto end; + hr = IStream_Write(This->stm, var->pszVal, len, &count); + bytesWritten = count + sizeof(DWORD); + break; + } + case VT_BSTR: + { + if (This->codePage == CP_UNICODE) + { + len = SysStringByteLen(var->bstrVal) + sizeof(WCHAR); + StorageUtl_WriteDWord(&dwTemp, 0, len); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (SUCCEEDED(hr)) + hr = IStream_Write(This->stm, var->bstrVal, len, &count); + } + else + { + char *str; + + len = WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal) + 1, + NULL, 0, NULL, NULL); + + str = heap_alloc(len); + if (!str) + { + hr = E_OUTOFMEMORY; + goto end; + } + + WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal), + str, len, NULL, NULL); + StorageUtl_WriteDWord(&dwTemp, 0, len); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (SUCCEEDED(hr)) + hr = IStream_Write(This->stm, str, len, &count); + heap_free(str); + } + + bytesWritten = count + sizeof(DWORD); + break; + } + case VT_LPWSTR: + { + len = lstrlenW(var->pwszVal) + 1; + + StorageUtl_WriteDWord(&dwTemp, 0, len); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (FAILED(hr)) + goto end; + hr = IStream_Write(This->stm, var->pwszVal, len * sizeof(WCHAR), + &count); + bytesWritten = count + sizeof(DWORD); + break; + } + case VT_FILETIME: + { + FILETIME temp; + + StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->filetime); + hr = IStream_Write(This->stm, &temp, sizeof(temp), &count); + bytesWritten = count; + break; + } + case VT_CF: + { + DWORD cf_hdr[2]; + + len = var->pclipdata->cbSize; + StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8); + StorageUtl_WriteDWord(&cf_hdr[1], 0, var->pclipdata->ulClipFmt); + hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count); + if (FAILED(hr)) + goto end; + hr = IStream_Write(This->stm, var->pclipdata->pClipData, + len - sizeof(var->pclipdata->ulClipFmt), &count); + if (FAILED(hr)) + goto end; + bytesWritten = count + sizeof cf_hdr; + break; + } + case VT_CLSID: + { + CLSID temp; + + StorageUtl_WriteGUID(&temp, 0, var->puuid); + hr = IStream_Write(This->stm, &temp, sizeof(temp), &count); + bytesWritten = count; + break; + } + case VT_BLOB: + { + StorageUtl_WriteDWord(&dwTemp, 0, var->blob.cbSize); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + if (FAILED(hr)) + goto end; + hr = IStream_Write(This->stm, var->blob.pBlobData, var->blob.cbSize, &count); + bytesWritten = count + sizeof(DWORD); + break; + } + default: + FIXME("unsupported type: %d\n", var->vt); + return STG_E_INVALIDPARAMETER; + } + + if (SUCCEEDED(hr)) + { + *sectionOffset += bytesWritten; + if (bytesWritten % sizeof(DWORD)) + { + DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD); + TRACE("adding %ld bytes of padding\n", padding); + *sectionOffset += padding; + } + } + +end: + return hr; +} + +struct PropertyClosure +{ + HRESULT hr; + DWORD propNum; + DWORD *sectionOffset; +}; + +static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value, + void *extra, void *closure) +{ + PropertyStorage_impl *This = extra; + struct PropertyClosure *c = closure; + + assert(key); + assert(value); + assert(extra); + assert(closure); + c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++, + PtrToUlong(key), value, c->sectionOffset); + return SUCCEEDED(c->hr); +} + +static HRESULT PropertyStorage_WritePropertiesToStream( + PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset) +{ + struct PropertyClosure closure; + + assert(sectionOffset); + closure.hr = S_OK; + closure.propNum = startingPropNum; + closure.sectionOffset = sectionOffset; + dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter, + &closure); + return closure.hr; +} + +static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This) +{ + HRESULT hr; + ULONG count = 0; + LARGE_INTEGER seek = { {0} }; + PROPERTYSETHEADER hdr; + FORMATIDOFFSET fmtOffset; + + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + PropertyStorage_MakeHeader(This, &hdr); + hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count); + if (FAILED(hr)) + goto end; + if (count != sizeof(hdr)) + { + hr = STG_E_WRITEFAULT; + goto end; + } + + PropertyStorage_MakeFmtIdOffset(This, &fmtOffset); + hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count); + if (FAILED(hr)) + goto end; + if (count != sizeof(fmtOffset)) + { + hr = STG_E_WRITEFAULT; + goto end; + } + hr = S_OK; + +end: + return hr; +} + +static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This) +{ + PROPERTYSECTIONHEADER sectionHdr; + HRESULT hr; + ULONG count; + LARGE_INTEGER seek; + DWORD numProps, prop, sectionOffset, dwTemp; + PROPVARIANT var; + + PropertyStorage_WriteHeadersToStream(This); + + /* Count properties. Always at least one property, the code page */ + numProps = 1; + if (dictionary_num_entries(This->name_to_propid)) + numProps++; + if (This->locale != LOCALE_SYSTEM_DEFAULT) + numProps++; + if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) + numProps++; + numProps += dictionary_num_entries(This->propid_to_prop); + + /* Write section header with 0 bytes right now, I'll adjust it after + * writing properties. + */ + PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr); + seek.QuadPart = SECTIONHEADER_OFFSET; + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count); + if (FAILED(hr)) + goto end; + + prop = 0; + sectionOffset = sizeof(PROPERTYSECTIONHEADER) + + numProps * sizeof(PROPERTYIDOFFSET); + + if (dictionary_num_entries(This->name_to_propid)) + { + prop++; + hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset); + if (FAILED(hr)) + goto end; + } + + PropVariantInit(&var); + + var.vt = VT_I2; + var.iVal = This->codePage; + hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE, + &var, §ionOffset); + if (FAILED(hr)) + goto end; + + if (This->locale != LOCALE_SYSTEM_DEFAULT) + { + var.vt = VT_I4; + var.lVal = This->locale; + hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE, + &var, §ionOffset); + if (FAILED(hr)) + goto end; + } + + if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) + { + var.vt = VT_I4; + var.lVal = 1; + hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR, + &var, §ionOffset); + if (FAILED(hr)) + goto end; + } + + hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset); + if (FAILED(hr)) + goto end; + + /* Now write the byte count of the section */ + seek.QuadPart = SECTIONHEADER_OFFSET; + hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + goto end; + StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset); + hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); + +end: + return hr; +} + +/*********************************************************************** + * PropertyStorage_Construct + */ +static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This) +{ + dictionary_destroy(This->name_to_propid); + This->name_to_propid = NULL; + dictionary_destroy(This->propid_to_name); + This->propid_to_name = NULL; + dictionary_destroy(This->propid_to_prop); + This->propid_to_prop = NULL; +} + +static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This) +{ + HRESULT hr = S_OK; + + This->name_to_propid = dictionary_create( + PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy, + This); + if (!This->name_to_propid) + { + hr = STG_E_INSUFFICIENTMEMORY; + goto end; + } + This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, + NULL, This); + if (!This->propid_to_name) + { + hr = STG_E_INSUFFICIENTMEMORY; + goto end; + } + This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare, + PropertyStorage_PropertyDestroy, This); + if (!This->propid_to_prop) + { + hr = STG_E_INSUFFICIENTMEMORY; + goto end; + } +end: + if (FAILED(hr)) + PropertyStorage_DestroyDictionaries(This); + return hr; +} + +static HRESULT PropertyStorage_BaseConstruct(IStream *stm, + REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps) +{ + HRESULT hr = S_OK; + + assert(pps); + assert(rfmtid); + *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps); + if (!*pps) + return E_OUTOFMEMORY; + + (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl; + (*pps)->ref = 1; + InitializeCriticalSection(&(*pps)->cs); + (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs"); + (*pps)->stm = stm; + (*pps)->fmtid = *rfmtid; + (*pps)->grfMode = grfMode; + + hr = PropertyStorage_CreateDictionaries(*pps); + if (FAILED(hr)) + { + (*pps)->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&(*pps)->cs); + HeapFree(GetProcessHeap(), 0, *pps); + *pps = NULL; + } + else IStream_AddRef( stm ); + + return hr; +} + +static HRESULT PropertyStorage_ConstructFromStream(IStream *stm, + REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps) +{ + PropertyStorage_impl *ps; + HRESULT hr; + + assert(pps); + hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); + if (SUCCEEDED(hr)) + { + hr = PropertyStorage_ReadFromStream(ps); + if (SUCCEEDED(hr)) + { + *pps = &ps->IPropertyStorage_iface; + TRACE("PropertyStorage %p constructed\n", ps); + hr = S_OK; + } + else IPropertyStorage_Release( &ps->IPropertyStorage_iface ); + } + return hr; +} + +static HRESULT PropertyStorage_ConstructEmpty(IStream *stm, + REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps) +{ + PropertyStorage_impl *ps; + HRESULT hr; + + assert(pps); + hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); + if (SUCCEEDED(hr)) + { + ps->format = 0; + ps->grfFlags = grfFlags; + if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE) + ps->format = 1; + /* default to Unicode unless told not to, as specified on msdn */ + if (ps->grfFlags & PROPSETFLAG_ANSI) + ps->codePage = GetACP(); + else + ps->codePage = CP_UNICODE; + ps->locale = LOCALE_SYSTEM_DEFAULT; + TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale); + *pps = &ps->IPropertyStorage_iface; + TRACE("PropertyStorage %p constructed\n", ps); + hr = S_OK; + } + return hr; +} + + +/*********************************************************************** + * Implementation of IPropertySetStorage + */ + +struct enum_stat_propset_stg +{ + IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface; + LONG refcount; + STATPROPSETSTG *stats; + size_t current; + size_t count; +}; + +static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface) +{ + return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface); +} + +static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IEnumSTATPROPSETSTG_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface) +{ + struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); + LONG refcount = InterlockedIncrement(&psenum->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface) +{ + struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); + LONG refcount = InterlockedDecrement(&psenum->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + heap_free(psenum->stats); + heap_free(psenum); + } + + return refcount; +} + +static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt, + STATPROPSETSTG *ret, ULONG *fetched) +{ + struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); + ULONG count = 0; + + TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched); + + if (psenum->current == ~0u) + psenum->current = 0; + + while (count < celt && psenum->current < psenum->count) + ret[count++] = psenum->stats[psenum->current++]; + + if (fetched) + *fetched = count; + + return count < celt ? S_FALSE : S_OK; +} + +static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt) +{ + FIXME("%p, %lu.\n", iface, celt); + + return S_OK; +} + +static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface) +{ + struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); + + TRACE("%p.\n", iface); + + psenum->current = ~0u; + + return S_OK; +} + +static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum) +{ + FIXME("%p, %p.\n", iface, ppenum); + + return E_NOTIMPL; +} + +static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl = +{ + enum_stat_propset_stg_QueryInterface, + enum_stat_propset_stg_AddRef, + enum_stat_propset_stg_Release, + enum_stat_propset_stg_Next, + enum_stat_propset_stg_Skip, + enum_stat_propset_stg_Reset, + enum_stat_propset_stg_Clone, +}; + +static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret) +{ + IStorage *stg = &storage->base.IStorage_iface; + IEnumSTATSTG *penum = NULL; + STATSTG stat; + ULONG count; + HRESULT hr; + + struct enum_stat_propset_stg *enum_obj; + + enum_obj = heap_alloc_zero(sizeof(*enum_obj)); + if (!enum_obj) + return E_OUTOFMEMORY; + + enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl; + enum_obj->refcount = 1; + + /* add all the property set elements into a list */ + hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum); + if (FAILED(hr)) + goto done; + + /* Allocate stats array and fill it. */ + while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK) + { + enum_obj->count++; + CoTaskMemFree(stat.pwcsName); + } + + if (FAILED(hr)) + goto done; + + enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats)); + if (!enum_obj->stats) + { + hr = E_OUTOFMEMORY; + goto done; + } + enum_obj->count = 0; + + if (FAILED(hr = IEnumSTATSTG_Reset(penum))) + goto done; + + while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK) + { + if (!stat.pwcsName) + continue; + + if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM) + { + STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++]; + + PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid); + + TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid)); + + ptr->mtime = stat.mtime; + ptr->atime = stat.atime; + ptr->ctime = stat.ctime; + ptr->grfFlags = stat.grfMode; + ptr->clsid = stat.clsid; + } + CoTaskMemFree(stat.pwcsName); + } + +done: + + if (penum) + IEnumSTATSTG_Release(penum); + + if (SUCCEEDED(hr)) + { + *ret = &enum_obj->IEnumSTATPROPSETSTG_iface; + } + else + { + *ret = NULL; + IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface); + } + + return hr; +} + +/************************************************************************ + * IPropertySetStorage_fnQueryInterface (IUnknown) + * + * This method forwards to the common QueryInterface implementation + */ +static HRESULT WINAPI IPropertySetStorage_fnQueryInterface( + IPropertySetStorage *ppstg, + REFIID riid, + void** ppvObject) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject ); +} + +/************************************************************************ + * IPropertySetStorage_fnAddRef (IUnknown) + * + * This method forwards to the common AddRef implementation + */ +static ULONG WINAPI IPropertySetStorage_fnAddRef( + IPropertySetStorage *ppstg) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + return IStorage_AddRef( &This->base.IStorage_iface ); +} + +/************************************************************************ + * IPropertySetStorage_fnRelease (IUnknown) + * + * This method forwards to the common Release implementation + */ +static ULONG WINAPI IPropertySetStorage_fnRelease( + IPropertySetStorage *ppstg) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + return IStorage_Release( &This->base.IStorage_iface ); +} + +/************************************************************************ + * IPropertySetStorage_fnCreate (IPropertySetStorage) + */ +static HRESULT WINAPI IPropertySetStorage_fnCreate( + IPropertySetStorage *ppstg, + REFFMTID rfmtid, + const CLSID* pclsid, + DWORD grfFlags, + DWORD grfMode, + IPropertyStorage** ppprstg) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; + IStream *stm = NULL; + HRESULT r; + + TRACE("%p, %s %#lx, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfFlags, + grfMode, ppprstg); + + /* be picky */ + if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE)) + { + r = STG_E_INVALIDFLAG; + goto end; + } + + if (!rfmtid) + { + r = E_INVALIDARG; + goto end; + } + + /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a + * storage, not a stream. For now, disallow it. + */ + if (grfFlags & PROPSETFLAG_NONSIMPLE) + { + FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); + r = STG_E_INVALIDFLAG; + goto end; + } + + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + goto end; + + r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm ); + if (FAILED(r)) + goto end; + + r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg); + + IStream_Release( stm ); + +end: + TRACE("returning %#lx\n", r); + return r; +} + +/************************************************************************ + * IPropertySetStorage_fnOpen (IPropertySetStorage) + */ +static HRESULT WINAPI IPropertySetStorage_fnOpen( + IPropertySetStorage *ppstg, + REFFMTID rfmtid, + DWORD grfMode, + IPropertyStorage** ppprstg) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + IStream *stm = NULL; + WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; + HRESULT r; + + TRACE("%p, %s, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfMode, ppprstg); + + /* be picky */ + if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) && + grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE)) + { + r = STG_E_INVALIDFLAG; + goto end; + } + + if (!rfmtid) + { + r = E_INVALIDARG; + goto end; + } + + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + goto end; + + r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm ); + if (FAILED(r)) + goto end; + + r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg); + + IStream_Release( stm ); + +end: + TRACE("returning %#lx\n", r); + return r; +} + +/************************************************************************ + * IPropertySetStorage_fnDelete (IPropertySetStorage) + */ +static HRESULT WINAPI IPropertySetStorage_fnDelete( + IPropertySetStorage *ppstg, + REFFMTID rfmtid) +{ + StorageImpl *This = impl_from_IPropertySetStorage(ppstg); + WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; + HRESULT r; + + TRACE("%p %s\n", This, debugstr_guid(rfmtid)); + + if (!rfmtid) + return E_INVALIDARG; + + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + return r; + + return IStorage_DestroyElement(&This->base.IStorage_iface, name); +} + +static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj) +{ + StorageImpl *storage = impl_from_IPropertySetStorage(iface); + + TRACE("%p, %p.\n", iface, enum_obj); + + if (!enum_obj) + return E_INVALIDARG; + + return create_enum_stat_propset_stg(storage, enum_obj); +} + +/*********************************************************************** + * vtables + */ +const IPropertySetStorageVtbl IPropertySetStorage_Vtbl = +{ + IPropertySetStorage_fnQueryInterface, + IPropertySetStorage_fnAddRef, + IPropertySetStorage_fnRelease, + IPropertySetStorage_fnCreate, + IPropertySetStorage_fnOpen, + IPropertySetStorage_fnDelete, + IPropertySetStorage_fnEnum +}; + +static const IPropertyStorageVtbl IPropertyStorage_Vtbl = +{ + IPropertyStorage_fnQueryInterface, + IPropertyStorage_fnAddRef, + IPropertyStorage_fnRelease, + IPropertyStorage_fnReadMultiple, + IPropertyStorage_fnWriteMultiple, + IPropertyStorage_fnDeleteMultiple, + IPropertyStorage_fnReadPropertyNames, + IPropertyStorage_fnWritePropertyNames, + IPropertyStorage_fnDeletePropertyNames, + IPropertyStorage_fnCommit, + IPropertyStorage_fnRevert, + IPropertyStorage_fnEnum, + IPropertyStorage_fnSetTimes, + IPropertyStorage_fnSetClass, + IPropertyStorage_fnStat, +}; + +/*********************************************************************** + * Format ID <-> name conversion + */ +static const WCHAR szSummaryInfo[] = L"\5SummaryInformation"; +static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation"; + +#define BITS_PER_BYTE 8 +#define CHARMASK 0x1f +#define BITS_IN_CHARMASK 5 +#define NUM_ALPHA_CHARS 26 + +/*********************************************************************** + * FmtIdToPropStgName [ole32.@] + * Returns the storage name of the format ID rfmtid. + * PARAMS + * rfmtid [I] Format ID for which to return a storage name + * str [O] Storage name associated with rfmtid. + * + * RETURNS + * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise. + * + * NOTES + * str must be at least CCH_MAX_PROPSTG_NAME characters in length. + */ +HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str) +{ + static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345"; + + TRACE("%s, %p\n", debugstr_guid(rfmtid), str); + + if (!rfmtid) return E_INVALIDARG; + if (!str) return E_INVALIDARG; + + if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid)) + lstrcpyW(str, szSummaryInfo); + else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid)) + lstrcpyW(str, szDocSummaryInfo); + else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid)) + lstrcpyW(str, szDocSummaryInfo); + else + { + const BYTE *fmtptr; + WCHAR *pstr = str; + ULONG bitsRemaining = BITS_PER_BYTE; + + *pstr++ = 5; + for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); ) + { + ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining); + + if (bitsRemaining >= BITS_IN_CHARMASK) + { + *pstr = (WCHAR)(fmtMap[i & CHARMASK]); + if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' && + *pstr <= 'z') + *pstr += 'A' - 'a'; + pstr++; + bitsRemaining -= BITS_IN_CHARMASK; + if (bitsRemaining == 0) + { + fmtptr++; + bitsRemaining = BITS_PER_BYTE; + } + } + else + { + if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID)) + i |= *fmtptr << bitsRemaining; + *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]); + bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK; + } + } + *pstr = 0; + } + TRACE("returning %s\n", debugstr_w(str)); + return S_OK; +} + +/*********************************************************************** + * PropStgNameToFmtId [ole32.@] + * Returns the format ID corresponding to the given name. + * PARAMS + * str [I] Storage name to convert to a format ID. + * rfmtid [O] Format ID corresponding to str. + * + * RETURNS + * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to + * a format ID, S_OK otherwise. + */ +HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid) +{ + HRESULT hr = STG_E_INVALIDNAME; + + TRACE("%s, %p\n", debugstr_w(str), rfmtid); + + if (!rfmtid) return E_INVALIDARG; + if (!str) return STG_E_INVALIDNAME; + + if (!lstrcmpiW(str, szDocSummaryInfo)) + { + *rfmtid = FMTID_DocSummaryInformation; + hr = S_OK; + } + else if (!lstrcmpiW(str, szSummaryInfo)) + { + *rfmtid = FMTID_SummaryInformation; + hr = S_OK; + } + else + { + ULONG bits; + BYTE *fmtptr = (BYTE *)rfmtid - 1; + const WCHAR *pstr = str; + + memset(rfmtid, 0, sizeof(*rfmtid)); + for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE; + bits += BITS_IN_CHARMASK) + { + ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored; + WCHAR wc; + + if (bitsUsed == 0) + fmtptr++; + wc = *++pstr - 'A'; + if (wc > NUM_ALPHA_CHARS) + { + wc += 'A' - 'a'; + if (wc > NUM_ALPHA_CHARS) + { + wc += 'a' - '0' + NUM_ALPHA_CHARS; + if (wc > CHARMASK) + { + WARN("invalid character (%d)\n", *pstr); + goto end; + } + } + } + *fmtptr |= wc << bitsUsed; + bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK); + if (bitsStored < BITS_IN_CHARMASK) + { + wc >>= BITS_PER_BYTE - bitsUsed; + if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE) + { + if (wc != 0) + { + WARN("extra bits\n"); + goto end; + } + break; + } + fmtptr++; + *fmtptr |= (BYTE)wc; + } + } + hr = S_OK; + } +end: + return hr; +} + +#ifdef __i386__ /* thiscall functions are i386-specific */ + +#define DEFINE_STDCALL_WRAPPER(num,func,args) \ + __ASM_STDCALL_FUNC(func, args, \ + "popl %eax\n\t" \ + "popl %ecx\n\t" \ + "pushl %eax\n\t" \ + "movl (%ecx), %eax\n\t" \ + "jmp *(4*(" #num "))(%eax)" ) + +DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8) +extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize); + +#else + +static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize) +{ + void* (WINAPI *fn)(void*,ULONG) = **(void***)this; + return fn(this, cbSize); +} + +#endif + +BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, + USHORT CodePage, PROPVARIANT* pvar, void* pma) +{ + struct read_buffer read_buffer; + HRESULT hr; + + read_buffer.data = (BYTE *)prop; + read_buffer.size = ~(size_t)0; + hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma); + + if (FAILED(hr)) + { + FIXME("should raise C++ exception on failure\n"); + PropVariantInit(pvar); + } + + return FALSE; +} + +SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar, + USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid, + BOOLEAN fReserved, ULONG *pcIndirect) +{ + FIXME("%p, %d, %p, %p, %ld, %d, %p.\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect); + + return NULL; +} + +HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid, + DWORD flags, DWORD reserved, IPropertyStorage **prop_stg) +{ + IStorage *stg; + IStream *stm; + HRESULT r; + + TRACE("%p, %s, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg); + + if (!fmt || reserved) + { + r = E_INVALIDARG; + goto end; + } + + if (flags & PROPSETFLAG_NONSIMPLE) + { + r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); + if (FAILED(r)) + goto end; + + /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a + * storage, not a stream. For now, disallow it. + */ + FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); + IStorage_Release(stg); + r = STG_E_INVALIDFLAG; + } + else + { + r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); + if (FAILED(r)) + goto end; + + r = PropertyStorage_ConstructEmpty(stm, fmt, flags, + STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); + + IStream_Release( stm ); + } + +end: + TRACE("returning %#lx\n", r); + return r; +} + +HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags, + DWORD reserved, IPropertyStorage **prop_stg) +{ + IStorage *stg; + IStream *stm; + HRESULT r; + + TRACE("%p, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg); + + if (!fmt || reserved) + { + r = E_INVALIDARG; + goto end; + } + + if (flags & PROPSETFLAG_NONSIMPLE) + { + r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); + if (FAILED(r)) + goto end; + + /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a + * storage, not a stream. For now, disallow it. + */ + FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); + IStorage_Release(stg); + r = STG_E_INVALIDFLAG; + } + else + { + r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); + if (FAILED(r)) + goto end; + + r = PropertyStorage_ConstructFromStream(stm, fmt, + STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); + + IStream_Release( stm ); + } + +end: + TRACE("returning %#lx\n", r); + return r; +} diff --git a/dlls/coml2/stg_stream.c b/dlls/coml2/stg_stream.c new file mode 100644 index 00000000000..25ead2c7c49 --- /dev/null +++ b/dlls/coml2/stg_stream.c @@ -0,0 +1,705 @@ +/* + * Compound Storage (32 bit version) + * Stream implementation + * + * This file contains the implementation of the stream interface + * for streams contained in a compound storage. + * + * Copyright 1999 Francis Beaudet + * Copyright 1999 Thuy Nguyen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winternl.h" +#include "wine/debug.h" + +#include "storage32.h" + +WINE_DEFAULT_DEBUG_CHANNEL(storage); + +/*** + * This implements the IUnknown method QueryInterface for this + * class + */ +static HRESULT WINAPI StgStreamImpl_QueryInterface( + IStream* iface, + REFIID riid, /* [in] */ + void** ppvObject) /* [iid_is][out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + if (ppvObject==0) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualIID(&IID_IUnknown, riid) || + IsEqualIID(&IID_ISequentialStream, riid) || + IsEqualIID(&IID_IStream, riid)) + { + *ppvObject = &This->IStream_iface; + } + else + return E_NOINTERFACE; + + IStream_AddRef(iface); + + return S_OK; +} + +/*** + * This implements the IUnknown method AddRef for this + * class + */ +static ULONG WINAPI StgStreamImpl_AddRef( + IStream* iface) +{ + StgStreamImpl* This = impl_from_IStream(iface); + return InterlockedIncrement(&This->ref); +} + +/*** + * This implements the IUnknown method Release for this + * class + */ +static ULONG WINAPI StgStreamImpl_Release( + IStream* iface) +{ + StgStreamImpl* This = impl_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + if (!ref) + { + TRACE("(%p)\n", This); + + /* + * Release the reference we are holding on the parent storage. + * IStorage_Release(&This->parentStorage->IStorage_iface); + * + * No, don't do this. Some apps call IStorage_Release without + * calling IStream_Release first. If we grab a reference the + * file is not closed, and the app fails when it tries to + * reopen the file (Easy-PC, for example). Just inform the + * storage that we have closed the stream + */ + + if (This->parentStorage) + StorageBaseImpl_RemoveStream(This->parentStorage, This); + This->parentStorage = 0; + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +/*** + * This method is part of the ISequentialStream interface. + * + * It reads a block of information from the stream at the current + * position. It then moves the current position at the end of the + * read block + * + * See the documentation of ISequentialStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Read( + IStream* iface, + void* pv, /* [length_is][size_is][out] */ + ULONG cb, /* [in] */ + ULONG* pcbRead) /* [out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + ULONG bytesReadBuffer; + HRESULT res; + + TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbRead); + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + /* + * If the caller is not interested in the number of bytes read, + * we use another buffer to avoid "if" statements in the code. + */ + if (pcbRead==0) + pcbRead = &bytesReadBuffer; + + res = StorageBaseImpl_StreamReadAt(This->parentStorage, + This->dirEntry, + This->currentPosition, + cb, + pv, + pcbRead); + + if (SUCCEEDED(res)) + { + /* + * Advance the pointer for the number of positions read. + */ + This->currentPosition.QuadPart += *pcbRead; + } + + TRACE("<-- %#lx\n", res); + return res; +} + +/*** + * This method is part of the ISequentialStream interface. + * + * It writes a block of information to the stream at the current + * position. It then moves the current position at the end of the + * written block. If the stream is too small to fit the block, + * the stream is grown to fit. + * + * See the documentation of ISequentialStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Write( + IStream* iface, + const void* pv, /* [size_is][in] */ + ULONG cb, /* [in] */ + ULONG* pcbWritten) /* [out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + ULONG bytesWritten = 0; + HRESULT res; + + TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbWritten); + + /* + * Do we have permission to write to this stream? + */ + switch(STGM_ACCESS_MODE(This->grfMode)) + { + case STGM_WRITE: + case STGM_READWRITE: + break; + default: + WARN("access denied by flags: %#lx\n", STGM_ACCESS_MODE(This->grfMode)); + return STG_E_ACCESSDENIED; + } + + if (!pv) + return STG_E_INVALIDPOINTER; + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + /* + * If the caller is not interested in the number of bytes written, + * we use another buffer to avoid "if" statements in the code. + */ + if (pcbWritten == 0) + pcbWritten = &bytesWritten; + + /* + * Initialize the out parameter + */ + *pcbWritten = 0; + + if (cb == 0) + { + TRACE("<-- S_OK, written 0\n"); + return S_OK; + } + + res = StorageBaseImpl_StreamWriteAt(This->parentStorage, + This->dirEntry, + This->currentPosition, + cb, + pv, + pcbWritten); + + /* + * Advance the position pointer for the number of positions written. + */ + This->currentPosition.QuadPart += *pcbWritten; + + if (SUCCEEDED(res)) + res = StorageBaseImpl_Flush(This->parentStorage); + + TRACE("<-- %#lx, written %lu\n", res, *pcbWritten); + return res; +} + +/*** + * This method is part of the IStream interface. + * + * It will move the current stream pointer according to the parameters + * given. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Seek( + IStream* iface, + LARGE_INTEGER dlibMove, /* [in] */ + DWORD dwOrigin, /* [in] */ + ULARGE_INTEGER* plibNewPosition) /* [out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + ULARGE_INTEGER newPosition; + DirEntry currentEntry; + HRESULT hr; + + TRACE("%p, %ld, %ld, %p.\n", iface, dlibMove.LowPart, dwOrigin, plibNewPosition); + + /* + * fail if the stream has no parent (as does windows) + */ + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + /* + * The caller is allowed to pass in NULL as the new position return value. + * If it happens, we assign it to a dynamic variable to avoid special cases + * in the code below. + */ + if (plibNewPosition == 0) + { + plibNewPosition = &newPosition; + } + + /* + * The file pointer is moved depending on the given "function" + * parameter. + */ + switch (dwOrigin) + { + case STREAM_SEEK_SET: + plibNewPosition->u.HighPart = 0; + plibNewPosition->u.LowPart = 0; + break; + case STREAM_SEEK_CUR: + *plibNewPosition = This->currentPosition; + break; + case STREAM_SEEK_END: + hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, ¤tEntry); + if (FAILED(hr)) return hr; + *plibNewPosition = currentEntry.size; + break; + default: + WARN("invalid dwOrigin %ld\n", dwOrigin); + return STG_E_INVALIDFUNCTION; + } + + plibNewPosition->QuadPart += dlibMove.QuadPart; + + /* + * tell the caller what we calculated + */ + This->currentPosition = *plibNewPosition; + + return S_OK; +} + +/*** + * This method is part of the IStream interface. + * + * It will change the size of a stream. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_SetSize( + IStream* iface, + ULARGE_INTEGER libNewSize) /* [in] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + HRESULT hr; + + TRACE("%p, %ld.\n", iface, libNewSize.LowPart); + + if(!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + /* + * As documented. + */ + if (libNewSize.HighPart != 0) + { + WARN("invalid value for libNewSize.HighPart %ld\n", libNewSize.HighPart); + return STG_E_INVALIDFUNCTION; + } + + /* + * Do we have permission? + */ + if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) + { + WARN("access denied\n"); + return STG_E_ACCESSDENIED; + } + + hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_Flush(This->parentStorage); + + return hr; +} + +/*** + * This method is part of the IStream interface. + * + * It will copy the 'cb' Bytes to 'pstm' IStream. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_CopyTo( + IStream* iface, + IStream* pstm, /* [unique][in] */ + ULARGE_INTEGER cb, /* [in] */ + ULARGE_INTEGER* pcbRead, /* [out] */ + ULARGE_INTEGER* pcbWritten) /* [out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + HRESULT hr = S_OK; + BYTE tmpBuffer[128]; + ULONG bytesRead, bytesWritten, copySize; + ULARGE_INTEGER totalBytesRead; + ULARGE_INTEGER totalBytesWritten; + + TRACE("%p, %p, %ld, %p, %p.\n", iface, pstm, cb.LowPart, pcbRead, pcbWritten); + + /* + * Sanity check + */ + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + if ( pstm == 0 ) + return STG_E_INVALIDPOINTER; + + totalBytesRead.QuadPart = 0; + totalBytesWritten.QuadPart = 0; + + while ( cb.QuadPart > 0 ) + { + if ( cb.QuadPart >= sizeof(tmpBuffer) ) + copySize = sizeof(tmpBuffer); + else + copySize = cb.LowPart; + + IStream_Read(iface, tmpBuffer, copySize, &bytesRead); + + totalBytesRead.QuadPart += bytesRead; + + IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); + + totalBytesWritten.QuadPart += bytesWritten; + + /* + * Check that read & write operations were successful + */ + if (bytesRead != bytesWritten) + { + hr = STG_E_MEDIUMFULL; + WARN("medium full\n"); + break; + } + + if (bytesRead!=copySize) + cb.QuadPart = 0; + else + cb.QuadPart -= bytesRead; + } + + if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; + if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; + + return hr; +} + +/*** + * This method is part of the IStream interface. + * + * For streams contained in structured storages, this method + * does nothing. This is what the documentation tells us. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Commit( + IStream* iface, + DWORD grfCommitFlags) /* [in] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + return StorageBaseImpl_Flush(This->parentStorage); +} + +/*** + * This method is part of the IStream interface. + * + * For streams contained in structured storages, this method + * does nothing. This is what the documentation tells us. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Revert( + IStream* iface) +{ + return S_OK; +} + +static HRESULT WINAPI StgStreamImpl_LockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType) /* [in] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + FIXME("not implemented!\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI StgStreamImpl_UnlockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType) /* [in] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + FIXME("not implemented!\n"); + return E_NOTIMPL; +} + +/*** + * This method is part of the IStream interface. + * + * This method returns information about the current + * stream. + * + * See the documentation of IStream for more info. + */ +static HRESULT WINAPI StgStreamImpl_Stat( + IStream* iface, + STATSTG* pstatstg, /* [out] */ + DWORD grfStatFlag) /* [in] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + + DirEntry currentEntry; + HRESULT hr; + + TRACE("%p, %p, %#lx.\n", This, pstatstg, grfStatFlag); + + /* + * if stream has no parent, return STG_E_REVERTED + */ + + if (!This->parentStorage) + { + WARN("storage reverted\n"); + return STG_E_REVERTED; + } + + /* + * Read the information from the directory entry. + */ + hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, + This->dirEntry, + ¤tEntry); + + if (SUCCEEDED(hr)) + { + StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, + pstatstg, + ¤tEntry, + grfStatFlag); + + pstatstg->grfMode = This->grfMode; + + /* In simple create mode cbSize is the current pos */ + if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create) + pstatstg->cbSize = This->currentPosition; + + return S_OK; + } + + WARN("failed to read entry\n"); + return hr; +} + +/*** + * This method is part of the IStream interface. + * + * This method returns a clone of the interface that allows for + * another seek pointer + * + * See the documentation of IStream for more info. + * + * I am not totally sure what I am doing here but I presume that this + * should be basically as simple as creating a new stream with the same + * parent etc and positioning its seek cursor. + */ +static HRESULT WINAPI StgStreamImpl_Clone( + IStream* iface, + IStream** ppstm) /* [out] */ +{ + StgStreamImpl* This = impl_from_IStream(iface); + StgStreamImpl* new_stream; + LARGE_INTEGER seek_pos; + + TRACE("%p %p\n", This, ppstm); + + /* + * Sanity check + */ + + if (!This->parentStorage) + return STG_E_REVERTED; + + if ( ppstm == 0 ) + return STG_E_INVALIDPOINTER; + + new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry); + + if (!new_stream) + return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */ + + *ppstm = &new_stream->IStream_iface; + IStream_AddRef(*ppstm); + + seek_pos.QuadPart = This->currentPosition.QuadPart; + + return IStream_Seek(*ppstm, seek_pos, STREAM_SEEK_SET, NULL); +} + +/* + * Virtual function table for the StgStreamImpl class. + */ +static const IStreamVtbl StgStreamVtbl = +{ + StgStreamImpl_QueryInterface, + StgStreamImpl_AddRef, + StgStreamImpl_Release, + StgStreamImpl_Read, + StgStreamImpl_Write, + StgStreamImpl_Seek, + StgStreamImpl_SetSize, + StgStreamImpl_CopyTo, + StgStreamImpl_Commit, + StgStreamImpl_Revert, + StgStreamImpl_LockRegion, + StgStreamImpl_UnlockRegion, + StgStreamImpl_Stat, + StgStreamImpl_Clone +}; + +/****************************************************************************** +** StgStreamImpl implementation +*/ + +/*** + * This is the constructor for the StgStreamImpl class. + * + * Params: + * parentStorage - Pointer to the storage that contains the stream to open + * dirEntry - Index of the directory entry that points to this stream. + */ +StgStreamImpl* StgStreamImpl_Construct( + StorageBaseImpl* parentStorage, + DWORD grfMode, + DirRef dirEntry) +{ + StgStreamImpl* newStream; + + newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl)); + + if (newStream) + { + /* + * Set-up the virtual function table and reference count. + */ + newStream->IStream_iface.lpVtbl = &StgStreamVtbl; + newStream->ref = 0; + + newStream->parentStorage = parentStorage; + + /* + * We want to nail-down the reference to the storage in case the + * stream out-lives the storage in the client application. + * + * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface); + * + * No, don't do this. Some apps call IStorage_Release without + * calling IStream_Release first. If we grab a reference the + * file is not closed, and the app fails when it tries to + * reopen the file (Easy-PC, for example) + */ + + newStream->grfMode = grfMode; + newStream->dirEntry = dirEntry; + + /* + * Start the stream at the beginning. + */ + newStream->currentPosition.HighPart = 0; + newStream->currentPosition.LowPart = 0; + + /* add us to the storage's list of active streams */ + StorageBaseImpl_AddStream(parentStorage, newStream); + } + + return newStream; +} diff --git a/dlls/coml2/storage32.c b/dlls/coml2/storage32.c new file mode 100644 index 00000000000..0d3f9922d78 --- /dev/null +++ b/dlls/coml2/storage32.c @@ -0,0 +1,10649 @@ +/* + * Compound Storage (32 bit version) + * Storage implementation + * + * This file contains the compound file implementation + * of the storage interface. + * + * Copyright 1999 Francis Beaudet + * Copyright 1999 Sylvain St-Germain + * Copyright 1999 Thuy Nguyen + * Copyright 2005 Mike McCormack + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * NOTES + * The compound file implementation of IStorage used for create + * and manage substorages and streams within a storage object + * residing in a compound file object. + */ + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winuser.h" +#include "wine/debug.h" + +#include "storage32.h" +#include "ole2.h" /* For Write/ReadClassStm */ + +#include "winreg.h" +#include "wine/wingdi16.h" +#include "compobj_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(storage); + + +/* + * These are signatures to detect the type of Document file. + */ +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}; + +extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; + + +/**************************************************************************** + * StorageInternalImpl definitions. + * + * Definition of the implementation structure for the IStorage interface. + * This one implements the IStorage interface for storage that are + * inside another storage. + */ +typedef struct StorageInternalImpl +{ + struct StorageBaseImpl base; + + /* + * Entry in the parent's stream tracking list + */ + struct list ParentListEntry; + + StorageBaseImpl *parentStorage; +} StorageInternalImpl; + +static const IStorageVtbl StorageInternalImpl_Vtbl; +static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef); + +typedef struct TransactedDirEntry +{ + /* If applicable, a reference to the original DirEntry in the transacted + * parent. If this is a newly-created entry, DIRENTRY_NULL. */ + DirRef transactedParentEntry; + + /* True if this entry is being used. */ + BOOL inuse; + + /* True if data is up to date. */ + BOOL read; + + /* True if this entry has been modified. */ + BOOL dirty; + + /* True if this entry's stream has been modified. */ + BOOL stream_dirty; + + /* True if this entry has been deleted in the transacted storage, but the + * delete has not yet been committed. */ + BOOL deleted; + + /* If this entry's stream has been modified, a reference to where the stream + * is stored in the snapshot file. */ + DirRef stream_entry; + + /* This directory entry's data, including any changes that have been made. */ + DirEntry data; + + /* A reference to the parent of this node. This is only valid while we are + * committing changes. */ + DirRef parent; + + /* A reference to a newly-created entry in the transacted parent. This is + * always equal to transactedParentEntry except when committing changes. */ + DirRef newTransactedParentEntry; +} TransactedDirEntry; + + +/**************************************************************************** + * Transacted storage object. + */ +typedef struct TransactedSnapshotImpl +{ + struct StorageBaseImpl base; + + /* + * Modified streams are temporarily saved to the scratch file. + */ + StorageBaseImpl *scratch; + + /* The directory structure is kept here, so that we can track how these + * entries relate to those in the parent storage. */ + TransactedDirEntry *entries; + ULONG entries_size; + ULONG firstFreeEntry; + + /* + * Changes are committed to the transacted parent. + */ + StorageBaseImpl *transactedParent; + + /* The transaction signature from when we last committed */ + ULONG lastTransactionSig; +} TransactedSnapshotImpl; + +static const IStorageVtbl TransactedSnapshotImpl_Vtbl; +static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**); + +typedef struct TransactedSharedImpl +{ + struct StorageBaseImpl base; + + /* + * Snapshot and uncommitted changes go here. + */ + TransactedSnapshotImpl *scratch; + + /* + * Changes are committed to the transacted parent. + */ + StorageBaseImpl *transactedParent; + + /* The transaction signature from when we last committed */ + ULONG lastTransactionSig; +} TransactedSharedImpl; + + +/**************************************************************************** + * BlockChainStream definitions. + * + * The BlockChainStream class is a utility class that is used to create an + * abstraction of the big block chains in the storage file. + */ + +struct BlockChainRun +{ + /* This represents a range of blocks that happen reside in consecutive sectors. */ + ULONG firstSector; + ULONG firstOffset; + ULONG lastOffset; +}; + +typedef struct BlockChainBlock +{ + ULONG index; + ULONG sector; + BOOL read; + BOOL dirty; + BYTE data[MAX_BIG_BLOCK_SIZE]; +} BlockChainBlock; + +struct BlockChainStream +{ + StorageImpl* parentStorage; + ULONG* headOfStreamPlaceHolder; + DirRef ownerDirEntry; + struct BlockChainRun* indexCache; + ULONG indexCacheLen; + ULONG indexCacheSize; + BlockChainBlock cachedBlocks[2]; + ULONG blockToEvict; + ULONG tailIndex; + ULONG numBlocks; +}; + +/* 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; +} + +static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); +static void BlockChainStream_Destroy(BlockChainStream*); +static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); +static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); +static HRESULT BlockChainStream_Flush(BlockChainStream*); +static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*); +static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER); + + +/**************************************************************************** + * SmallBlockChainStream definitions. + * + * The SmallBlockChainStream class is a utility class that is used to create an + * abstraction of the small block chains in the storage file. + */ + +struct SmallBlockChainStream +{ + StorageImpl* parentStorage; + DirRef ownerDirEntry; + ULONG* headOfStreamPlaceHolder; +}; + +static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); +static void SmallBlockChainStream_Destroy(SmallBlockChainStream*); +static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); +static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); +static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*); +static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER); + + +/************************************************************************ + * STGM Functions + ***********************************************************************/ + +/************************************************************************ + * 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 validateSTGM(DWORD stgm) +{ + 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 %#lx\n", stgm); + return E_FAIL; + } + + switch (access) + { + case STGM_READ: + case STGM_WRITE: + case STGM_READWRITE: + break; + default: + return E_FAIL; + } + + 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; + } + + switch (create) + { + case STGM_CREATE: + case STGM_FAILIFTHERE: + break; + default: + return E_FAIL; + } + + /* + * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE + */ + if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) ) + return E_FAIL; + + /* + * STGM_CREATE | STGM_CONVERT + * if both are false, STGM_FAILIFTHERE is set to TRUE + */ + if ( create == STGM_CREATE && (stgm & STGM_CONVERT) ) + return E_FAIL; + + /* + * STGM_NOSCRATCH requires STGM_TRANSACTED + */ + if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) ) + return E_FAIL; + + /* + * 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; +} + +/************************************************************************ + * 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; +} + +/************************************************************************ + * GetAccessModeFromSTGM + * + * This method will return an access mode flag from a STGM value. + * The STGM value is assumed valid. + */ +static DWORD GetAccessModeFromSTGM(DWORD stgm) +{ + 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; +} + + +/************************************************************************ + * IDirectWriterLock implementation + ***********************************************************************/ + +static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface ) +{ + return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface); +} + +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); +} + +static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface) +{ + StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); + return IStorage_AddRef(&This->IStorage_iface); +} + +static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface) +{ + StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); + return IStorage_Release(&This->IStorage_iface); +} + +static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout) +{ + StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); + FIXME("%p, %ld: stub\n", This, timeout); + return E_NOTIMPL; +} + +static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface) +{ + StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface) +{ + StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static const IDirectWriterLockVtbl DirectWriterLockVtbl = +{ + directwriterlock_QueryInterface, + directwriterlock_AddRef, + directwriterlock_Release, + directwriterlock_WaitForWriteAccess, + directwriterlock_ReleaseWriteAccess, + directwriterlock_HaveWriteAccess +}; + + +/************************************************************************ + * StorageBaseImpl implementation : Tree helper functions + ***********************************************************************/ + +/**************************************************************************** + * + * 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) +{ + LONG diff = lstrlenW(name1) - lstrlenW(name2); + + while (diff == 0 && *name1 != 0) + { + /* + * We compare the string themselves only when they are of the same length + */ + diff = towupper(*name1++) - towupper(*name2++); + } + + return diff; +} + +/**************************************************************************** + * + * Internal Method + * + * Find and read the element of a storage with the given name. + */ +static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry, + const OLECHAR *name, DirEntry *data) +{ + DirRef currentEntry; + + /* Read the storage entry to find the root of the tree. */ + StorageBaseImpl_ReadDirEntry(storage, storageEntry, data); + + currentEntry = data->dirRootEntry; + + while (currentEntry != DIRENTRY_NULL) + { + 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 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; + + /* Read the storage entry to find the root of the tree. */ + StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData); + + *parentEntry = storageEntry; + *relation = DIRENTRY_RELATION_DIR; + + childEntry = parentData->dirRootEntry; + + while (childEntry != DIRENTRY_NULL) + { + LONG cmp; + + StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData); + + cmp = entryNameCmp(childName, childData.name); + + if (cmp == 0) + /* found it */ + break; + + else if (cmp < 0) + { + *parentData = childData; + *parentEntry = childEntry; + *relation = DIRENTRY_RELATION_PREVIOUS; + + childEntry = parentData->leftChild; + } + + else if (cmp > 0) + { + *parentData = childData; + *parentEntry = childEntry; + *relation = DIRENTRY_RELATION_NEXT; + + childEntry = parentData->rightChild; + } + } + + if (childEntry == DIRENTRY_NULL) + return STG_E_FILENOTFOUND; + else + return S_OK; +} + +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 + * + * Add a directory entry to a storage + */ +static HRESULT insertIntoTree( + StorageBaseImpl *This, + DirRef parentStorageIndex, + DirRef newEntryIndex) +{ + DirEntry currentEntry; + DirEntry newEntry; + + /* + * Read the inserted entry + */ + StorageBaseImpl_ReadDirEntry(This, + newEntryIndex, + &newEntry); + + /* + * Read the storage entry + */ + StorageBaseImpl_ReadDirEntry(This, + parentStorageIndex, + ¤tEntry); + + 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; + + /* + * Keep a reference to the root of the storage's element tree + */ + currentEntryId = currentEntry.dirRootEntry; + + /* + * Read + */ + StorageBaseImpl_ReadDirEntry(This, + currentEntry.dirRootEntry, + ¤tEntry); + + previous = currentEntry.leftChild; + next = currentEntry.rightChild; + current = currentEntryId; + + while (!found) + { + LONG diff = entryNameCmp( newEntry.name, currentEntry.name); + + 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; + } + + previous = currentEntry.leftChild; + next = currentEntry.rightChild; + } + } + else + { + /* + * The storage is empty, make the new entry the root of its element tree + */ + currentEntry.dirRootEntry = newEntryIndex; + StorageBaseImpl_WriteDirEntry(This, + parentStorageIndex, + ¤tEntry); + } + + return S_OK; +} + +/************************************************************************* + * + * 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 (entryToDelete.leftChild != DIRENTRY_NULL) + { + /* + * Replace the deleted entry with its left child + */ + setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild); + + hr = StorageBaseImpl_WriteDirEntry( + This, + parentEntryRef, + &parentEntry); + if(FAILED(hr)) + { + return hr; + } + + 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; + + do + { + hr = StorageBaseImpl_ReadDirEntry( + This, + newRightChildParent, + &newRightChildParentEntry); + if (FAILED(hr)) + { + return hr; + } + + if (newRightChildParentEntry.rightChild != DIRENTRY_NULL) + newRightChildParent = newRightChildParentEntry.rightChild; + } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL); + + newRightChildParentEntry.rightChild = entryToDelete.rightChild; + + hr = StorageBaseImpl_WriteDirEntry( + This, + newRightChildParent, + &newRightChildParentEntry); + if (FAILED(hr)) + { + return hr; + } + } + } + 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; + } + } + + return hr; +} + + +/************************************************************************ + * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements + ***********************************************************************/ + +/* + * IEnumSTATSTGImpl definitions. + * + * Definition of the implementation structure for the IEnumSTATSTGImpl interface. + * This class allows iterating through the content of a storage and finding + * specific items inside it. + */ +struct IEnumSTATSTGImpl +{ + 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 */ +}; + +static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface) +{ + return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface); +} + +static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This) +{ + IStorage_Release(&This->parentStorage->IStorage_iface); + HeapFree(GetProcessHeap(), 0, This); +} + +static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface( + IEnumSTATSTG* iface, + REFIID riid, + void** ppvObject) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); + + if (ppvObject==0) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualGUID(&IID_IUnknown, riid) || + IsEqualGUID(&IID_IEnumSTATSTG, riid)) + { + *ppvObject = &This->IEnumSTATSTG_iface; + IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface); + TRACE("<-- %p\n", *ppvObject); + return S_OK; + } + + TRACE("<-- E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI IEnumSTATSTGImpl_AddRef( + IEnumSTATSTG* iface) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI IEnumSTATSTGImpl_Release( + IEnumSTATSTG* iface) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + ULONG newRef; + + newRef = InterlockedDecrement(&This->ref); + + if (newRef==0) + { + IEnumSTATSTGImpl_Destroy(This); + } + + return newRef; +} + +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]; + + TRACE("%p,%p\n", This, ref); + + hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, + This->parentStorage->storageDirEntry, &entry); + searchNode = entry.dirRootEntry; + + while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL) + { + hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry); + + if (SUCCEEDED(hr)) + { + 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; + } + } + } + + if (SUCCEEDED(hr)) + { + *ref = result; + if (result != DIRENTRY_NULL) + memcpy(This->name, result_name, sizeof(result_name)); + } + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static HRESULT WINAPI IEnumSTATSTGImpl_Next( + IEnumSTATSTG* iface, + ULONG celt, + STATSTG* rgelt, + ULONG* pceltFetched) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + DirEntry currentEntry; + STATSTG* currentReturnStruct = rgelt; + ULONG objectFetched = 0; + DirRef currentSearchNode; + HRESULT hr=S_OK; + + TRACE("%p, %lu, %p, %p.\n", iface, celt, rgelt, pceltFetched); + + if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) + return E_INVALIDARG; + + if (This->parentStorage->reverted) + { + TRACE("<-- STG_E_REVERTED\n"); + return STG_E_REVERTED; + } + + /* + * 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; + + while ( *pceltFetched < celt ) + { + hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); + + if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) + { + memset(currentReturnStruct, 0, sizeof(*currentReturnStruct)); + break; + } + + /* + * Read the entry from the storage. + */ + hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, + currentSearchNode, + ¤tEntry); + if (FAILED(hr)) break; + + /* + * 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++; + } + + if (SUCCEEDED(hr) && *pceltFetched != celt) + hr = S_FALSE; + + TRACE("<-- %#lx (asked %lu, got %lu)\n", hr, celt, *pceltFetched); + return hr; +} + + +static HRESULT WINAPI IEnumSTATSTGImpl_Skip( + IEnumSTATSTG* iface, + ULONG celt) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + ULONG objectFetched = 0; + DirRef currentSearchNode; + HRESULT hr=S_OK; + + TRACE("%p, %lu.\n", iface, celt); + + if (This->parentStorage->reverted) + { + TRACE("<-- STG_E_REVERTED\n"); + return STG_E_REVERTED; + } + + while ( (objectFetched < celt) ) + { + hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); + + if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) + break; + + objectFetched++; + } + + if (SUCCEEDED(hr) && objectFetched != celt) + return S_FALSE; + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static HRESULT WINAPI IEnumSTATSTGImpl_Reset( + IEnumSTATSTG* iface) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + TRACE("%p\n", iface); + + if (This->parentStorage->reverted) + { + TRACE("<-- STG_E_REVERTED\n"); + return STG_E_REVERTED; + } + + This->name[0] = 0; + + return S_OK; +} + +static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef); + +static HRESULT WINAPI IEnumSTATSTGImpl_Clone( + IEnumSTATSTG* iface, + IEnumSTATSTG** ppenum) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + IEnumSTATSTGImpl* newClone; + + TRACE("%p,%p\n", iface, ppenum); + + if (This->parentStorage->reverted) + { + TRACE("<-- STG_E_REVERTED\n"); + return STG_E_REVERTED; + } + + if (ppenum==0) + return E_INVALIDARG; + + newClone = IEnumSTATSTGImpl_Construct(This->parentStorage, + This->storageDirEntry); + if (!newClone) + { + *ppenum = NULL; + return E_OUTOFMEMORY; + } + + /* + * The new clone enumeration must point to the same current node as + * the old one. + */ + memcpy(newClone->name, This->name, sizeof(newClone->name)); + + *ppenum = &newClone->IEnumSTATSTG_iface; + + return S_OK; +} + +/* + * 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 +}; + +static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct( + StorageBaseImpl* parentStorage, + DirRef storageDirEntry) +{ + IEnumSTATSTGImpl* newEnumeration; + + newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl)); + + if (newEnumeration) + { + 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; + } + + return newEnumeration; +} + + +/************************************************************************ + * StorageBaseImpl implementation + ***********************************************************************/ + +static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface ) +{ + return CONTAINING_RECORD(iface, StorageBaseImpl, 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) +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + + TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); + + if (!ppvObject) + return E_INVALIDARG; + + *ppvObject = 0; + + 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 + { + TRACE("<-- E_NOINTERFACE\n"); + return E_NOINTERFACE; + } + + IStorage_AddRef(iface); + TRACE("<-- %p\n", *ppvObject); + return S_OK; +} + +/************************************************************************ + * StorageBaseImpl_AddRef (IUnknown) + * + * This method implements the common AddRef for all IStorage + * implementations contained in this file. + * + * See Windows documentation for more details on IUnknown methods. + */ +static ULONG WINAPI StorageBaseImpl_AddRef( + IStorage* iface) +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("%p, refcount %lu.\n", iface, 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, refcount %lu.\n", iface, ref); + + if (ref == 0) + { + /* + * 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); + } + + 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 ) + { + if ( wcscmp(data.name, *snb) == 0 ) + skip = TRUE; + ++snb; + } + } + + 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) + { + 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 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 ); + + /* + * 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; + } + } + + if (hr == S_OK) + { + /* + * Get the size of the source stream + */ + IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME ); + + /* + * Set the size of the destination stream. + */ + IStream_SetSize(pstrTmp, strStat.cbSize); + + /* + * do the copy + */ + hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize, + NULL, NULL ); + + IStream_Release( pstrChild ); + } + + IStream_Release( pstrTmp ); + } + } + + /* copy siblings */ + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage, + skip_stream, snbExclude, pstgDest ); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage, + skip_stream, snbExclude, pstgDest ); + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) +{ + StgStreamImpl *strm; + + TRACE("%p, %ld.\n", stg, streamEntry); + + LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry) + { + if (strm->dirEntry == streamEntry) + { + return TRUE; + } + } + + return FALSE; +} + +static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry) +{ + StorageInternalImpl *childstg; + + TRACE("%p, %ld.\n", stg, storageEntry); + + LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) + { + if (childstg->base.storageDirEntry == storageEntry) + { + return TRUE; + } + } + + return FALSE; +} + +/************************************************************************ + * StorageBaseImpl_OpenStream (IStorage) + * + * This method will open the specified stream object from the current storage. + * + * See Windows documentation for more details on IStorage methods. + */ +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] */ +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + StgStreamImpl* newStream; + DirEntry currentEntry; + DirRef streamEntryRef; + HRESULT res = STG_E_UNKNOWN; + + TRACE("%p, %s, %p, %#lx, %ld, %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; + } + + /* + * As documented. + */ + if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) ) + { + res = STG_E_INVALIDFUNCTION; + goto end; + } + + if (This->reverted) + { + res = STG_E_REVERTED; + goto end; + } + + /* + * 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; + } + } + + /* + * Search for the element with the given name + */ + streamEntryRef = findElement( + This, + This->storageDirEntry, + pwcsName, + ¤tEntry); + + /* + * 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; + } + + newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef); + + if (newStream) + { + newStream->grfMode = grfMode; + *ppstm = &newStream->IStream_iface; + + IStream_AddRef(*ppstm); + + res = S_OK; + goto end; + } + + res = E_OUTOFMEMORY; + goto end; + } + + res = STG_E_FILENOTFOUND; + +end: + if (res == S_OK) + TRACE("<-- IStream %p\n", *ppstm); + TRACE("<-- %#lx\n", res); + return res; +} + +/************************************************************************ + * StorageBaseImpl_OpenStorage (IStorage) + * + * This method will open a new storage object from the current storage. + * + * See Windows documentation for more details on IStorage methods. + */ +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] */ +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + StorageInternalImpl* newStorage; + StorageBaseImpl* newTransactedStorage; + DirEntry currentEntry; + DirRef storageEntryRef; + HRESULT res = STG_E_UNKNOWN; + + TRACE("%p, %s, %p, %#lx, %p, %ld, %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; + } + + /* + * As documented. + */ + 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; + + /* + * 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; + } + } + + *ppstg = NULL; + + storageEntryRef = findElement( + This, + This->storageDirEntry, + pwcsName, + ¤tEntry); + + 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; + } + + newStorage = StorageInternalImpl_Construct( + This, + grfMode, + storageEntryRef); + + if (newStorage != 0) + { + if (grfMode & STGM_TRANSACTED) + { + res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage); + + if (FAILED(res)) + { + HeapFree(GetProcessHeap(), 0, newStorage); + goto end; + } + + *ppstg = &newTransactedStorage->IStorage_iface; + } + else + { + *ppstg = &newStorage->base.IStorage_iface; + } + + list_add_tail(&This->storageHead, &newStorage->ParentListEntry); + + res = S_OK; + goto end; + } + + res = STG_E_INSUFFICIENTMEMORY; + goto end; + } + + res = STG_E_FILENOTFOUND; + +end: + TRACE("<-- %#lx\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, %ld, %p, %ld, %p.\n", iface, reserved1, reserved2, reserved3, ppenum); + + if (!ppenum) + return E_INVALIDARG; + + if (This->reverted) + return STG_E_REVERTED; + + newEnum = IEnumSTATSTGImpl_Construct( + This, + This->storageDirEntry); + + if (newEnum) + { + *ppenum = &newEnum->IEnumSTATSTG_iface; + return S_OK; + } + + return E_OUTOFMEMORY; +} + +/************************************************************************ + * 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; + + TRACE("%p, %p, %#lx.\n", iface, pstatstg, grfStatFlag); + + if (!pstatstg) + { + res = E_INVALIDARG; + goto end; + } + + if (This->reverted) + { + res = STG_E_REVERTED; + goto end; + } + + res = StorageBaseImpl_ReadDirEntry( + This, + This->storageDirEntry, + ¤tEntry); + + if (SUCCEEDED(res)) + { + StorageUtl_CopyDirEntryToSTATSTG( + This, + pstatstg, + ¤tEntry, + grfStatFlag); + + pstatstg->grfMode = This->openFlags; + pstatstg->grfStateBits = This->stateBits; + } + +end: + if (res == S_OK) + { + TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.LowPart, pstatstg->cbSize.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits); + } + TRACE("<-- %#lx\n", res); + return res; +} + +/************************************************************************ + * 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; + + 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); + + if (currentEntryRef != DIRENTRY_NULL) + { + /* + * There is already an element with the new name + */ + return STG_E_FILEALREADYEXISTS; + } + + /* + * Search for the old element name + */ + currentEntryRef = findElement(This, + This->storageDirEntry, + pwcsOldName, + ¤tEntry); + + 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; + } + + /* Remove the element from its current position in the tree */ + removeFromTree(This, This->storageDirEntry, + currentEntryRef); + + /* Change the name of the element */ + lstrcpyW(currentEntry.name, pwcsNewName); + + /* Delete any sibling links */ + currentEntry.leftChild = DIRENTRY_NULL; + currentEntry.rightChild = DIRENTRY_NULL; + + StorageBaseImpl_WriteDirEntry(This, currentEntryRef, + ¤tEntry); + + /* 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 StorageBaseImpl_Flush(This); +} + +/************************************************************************ + * 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] */ +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + StgStreamImpl* newStream; + DirEntry currentEntry, newStreamEntry; + DirRef currentEntryRef, newStreamEntryRef; + HRESULT hr; + + TRACE("%p, %s, %#lx, %ld, %ld, %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; + + if (This->reverted) + return STG_E_REVERTED; + + /* + * As documented. + */ + if ((grfMode & STGM_DELETEONRELEASE) || + (grfMode & STGM_TRANSACTED)) + return STG_E_INVALIDFUNCTION; + + /* + * 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; + + /* 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(This->openFlags & STGM_SIMPLE) + if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG; + + *ppstm = 0; + + currentEntryRef = findElement(This, + This->storageDirEntry, + pwcsName, + ¤tEntry); + + 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; + } + + /* + * 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; + + lstrcpyW(newStreamEntry.name, pwcsName); + + newStreamEntry.stgType = STGTY_STREAM; + newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN; + newStreamEntry.size.LowPart = 0; + newStreamEntry.size.HighPart = 0; + + newStreamEntry.leftChild = DIRENTRY_NULL; + newStreamEntry.rightChild = DIRENTRY_NULL; + newStreamEntry.dirRootEntry = DIRENTRY_NULL; + + /* call CoFileTime to get the current time + newStreamEntry.ctime + newStreamEntry.mtime + */ + + /* newStreamEntry.clsid */ + + /* + * Create an entry with the new data + */ + hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef); + if (FAILED(hr)) + return hr; + + /* + * Insert the new entry in the parent storage's tree. + */ + hr = insertIntoTree( + This, + This->storageDirEntry, + newStreamEntryRef); + if (FAILED(hr)) + { + StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef); + return hr; + } + + /* + * Open the stream to return it. + */ + newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef); + + if (newStream) + { + *ppstm = &newStream->IStream_iface; + IStream_AddRef(*ppstm); + } + else + { + return STG_E_INSUFFICIENTMEMORY; + } + + return StorageBaseImpl_Flush(This); +} + +/************************************************************************ + * 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; + + TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid)); + + if (This->reverted) + return STG_E_REVERTED; + + hRes = StorageBaseImpl_ReadDirEntry(This, + This->storageDirEntry, + ¤tEntry); + if (SUCCEEDED(hRes)) + { + currentEntry.clsid = *clsid; + + hRes = StorageBaseImpl_WriteDirEntry(This, + This->storageDirEntry, + ¤tEntry); + } + + if (SUCCEEDED(hRes)) + hRes = StorageBaseImpl_Flush(This); + + return hRes; +} + +/************************************************************************ + * 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] */ +{ + StorageBaseImpl* This = impl_from_IStorage(iface); + + DirEntry currentEntry; + DirEntry newEntry; + DirRef currentEntryRef; + DirRef newEntryRef; + HRESULT hr; + + TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode, + reserved1, reserved2, ppstg); + + if (ppstg == 0) + return STG_E_INVALIDPOINTER; + + if (This->openFlags & STGM_SIMPLE) + { + return STG_E_INVALIDFUNCTION; + } + + if (pwcsName == 0) + return STG_E_INVALIDNAME; + + *ppstg = NULL; + + if ( FAILED( validateSTGM(grfMode) ) || + (grfMode & STGM_DELETEONRELEASE) ) + { + WARN("bad grfMode: %#lx\n", grfMode); + return STG_E_INVALIDFLAG; + } + + if (This->reverted) + return STG_E_REVERTED; + + /* + * 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; + } + + currentEntryRef = findElement(This, + This->storageDirEntry, + pwcsName, + ¤tEntry); + + 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; + } + + memset(&newEntry, 0, sizeof(DirEntry)); + + newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR); + + if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) + { + FIXME("name too long\n"); + return STG_E_INVALIDNAME; + } + + lstrcpyW(newEntry.name, pwcsName); + + newEntry.stgType = STGTY_STORAGE; + newEntry.startingBlock = BLOCK_END_OF_CHAIN; + newEntry.size.LowPart = 0; + newEntry.size.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 */ + + /* + * Create a new directory entry for the storage + */ + hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef); + if (FAILED(hr)) + return hr; + + /* + * 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; + } + + /* + * Open it to get a pointer to return. + */ + hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg); + + if( (hr != S_OK) || (*ppstg == NULL)) + { + return hr; + } + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_Flush(This); + + return S_OK; +} + +static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, + DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, + SNB snbExclude, IStorage *pstgDest) +{ + DirEntry data; + HRESULT hr; + + hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); + + if (SUCCEEDED(hr)) + hr = IStorage_SetClass( pstgDest, &data.clsid ); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage, + skip_stream, snbExclude, pstgDest ); + + TRACE("<-- %#lx\n", hr); + return hr; +} + +/************************************************************************* + * CopyTo (IStorage) + */ +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] */ +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + + BOOL skip_storage = FALSE, skip_stream = FALSE; + DWORD i; + + TRACE("%p, %ld, %p, %p, %p.\n", iface, ciidExclude, rgiidExclude, snbExclude, pstgDest); + + if ( pstgDest == 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 (!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; + + /* Go up the chain from the destination until we find the source storage. */ + while (pstgDestAncestor != iface) { + pstgDestAncestorChild = pstgDest; + + if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl) + { + TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor; + + pstgDestAncestor = &snapshot->transactedParent->IStorage_iface; + } + else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl) + { + StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor; + + pstgDestAncestor = &internal->parentStorage->IStorage_iface; + } + else + break; + } + + if (pstgDestAncestor == iface) + { + BOOL fail = TRUE; + + if (pstgDestAncestorChild && snbExclude) + { + StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild; + DirEntry data; + WCHAR **snb = snbExclude; + + StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data); + + while ( *snb != NULL && fail ) + { + if ( wcscmp(data.name, *snb) == 0 ) + fail = FALSE; + ++snb; + } + } + + if (fail) + return STG_E_ACCESSDENIED; + } + } + + return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry, + skip_storage, skip_stream, snbExclude, pstgDest ); +} + +/************************************************************************* + * 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, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest, + debugstr_w(pwcsNewName), grfFlags); + return E_NOTIMPL; +} + +/************************************************************************* + * 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_Commit( + IStorage* iface, + DWORD grfCommitFlags)/* [in] */ +{ + StorageBaseImpl* This = impl_from_IStorage(iface); + TRACE("%p, %#lx.\n", iface, grfCommitFlags); + return StorageBaseImpl_Flush(This); +} + +/************************************************************************* + * Revert (IStorage) + * + * Discard all changes that have been made since the last commit operation + */ +static HRESULT WINAPI StorageBaseImpl_Revert( + IStorage* iface) +{ + TRACE("(%p)\n", iface); + return S_OK; +} + +/********************************************************************* + * + * Internal helper function for StorageBaseImpl_DestroyElement() + * + * 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; + + TRACE("%p, %ld.\n", parentStorage, indexToDelete); + + /* Invalidate any open storage objects. */ + LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry) + { + if (stg->base.storageDirEntry == indexToDelete) + { + StorageBaseImpl_Invalidate(&stg->base); + } + } + + /* + * Open the storage and enumerate it + */ + hr = IStorage_OpenStorage( + &parentStorage->IStorage_iface, + entryDataToDelete.name, + 0, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, + 0, + 0, + &childStorage); + + if (hr != S_OK) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + /* + * Enumerate the elements + */ + hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements); + if (FAILED(hr)) + { + IStorage_Release(childStorage); + TRACE("<-- %#lx\n", hr); + return hr; + } + + do + { + /* + * Obtain the next element + */ + hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL); + if (hr==S_OK) + { + destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName); + + CoTaskMemFree(currentElement.pwcsName); + } + + /* + * We need to Reset the enumeration every time because we delete elements + * and the enumeration could be invalid + */ + IEnumSTATSTG_Reset(elements); + + } while ((hr == S_OK) && (destroyHr == S_OK)); + + IStorage_Release(childStorage); + IEnumSTATSTG_Release(elements); + + TRACE("%#lx\n", hr); + return destroyHr; +} + +/********************************************************************* + * + * Internal helper function for StorageBaseImpl_DestroyElement() + * + * Perform the deletion of a stream's data + * + */ +static HRESULT deleteStreamContents( + StorageBaseImpl *parentStorage, + DirRef indexToDelete, + DirEntry entryDataToDelete) +{ + IStream *pis; + HRESULT hr; + ULARGE_INTEGER size; + StgStreamImpl *strm, *strm2; + + /* 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); + } + } + + size.HighPart = 0; + size.LowPart = 0; + + hr = IStorage_OpenStream(&parentStorage->IStorage_iface, + entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); + + if (hr!=S_OK) + { + TRACE("<-- %#lx\n", hr); + return(hr); + } + + /* + * Zap the stream + */ + hr = IStream_SetSize(pis, size); + + if(hr != S_OK) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + /* + * Release the stream object. + */ + IStream_Release(pis); + TRACE("<-- %#lx\n", hr); + return S_OK; +} + +/************************************************************************* + * 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 allows reinvoking 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); + + HRESULT hr = S_OK; + DirEntry entryToDelete; + DirRef entryToDeleteRef; + + TRACE("(%p, %s)\n", + iface, debugstr_w(pwcsName)); + + if (pwcsName==NULL) + return STG_E_INVALIDPOINTER; + + if (This->reverted) + return STG_E_REVERTED; + + if ( !(This->openFlags & STGM_TRANSACTED) && + STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) + return STG_E_ACCESSDENIED; + + entryToDeleteRef = findElement( + This, + This->storageDirEntry, + pwcsName, + &entryToDelete); + + if ( entryToDeleteRef == DIRENTRY_NULL ) + { + TRACE("<-- STG_E_FILENOTFOUND\n"); + return STG_E_FILENOTFOUND; + } + + if ( entryToDelete.stgType == STGTY_STORAGE ) + { + hr = deleteStorageContents( + This, + entryToDeleteRef, + entryToDelete); + } + else if ( entryToDelete.stgType == STGTY_STREAM ) + { + hr = deleteStreamContents( + This, + entryToDeleteRef, + entryToDelete); + } + + if (hr!=S_OK) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + /* + * Remove the entry from its parent storage + */ + hr = removeFromTree( + This, + This->storageDirEntry, + entryToDeleteRef); + + /* + * Invalidate the entry + */ + if (SUCCEEDED(hr)) + StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_Flush(This); + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg) +{ + struct list *cur, *cur2; + StgStreamImpl *strm=NULL; + StorageInternalImpl *childstg=NULL; + + 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); + } + + LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) { + childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry); + StorageBaseImpl_Invalidate( &childstg->base ); + } + + if (stg->transactedChild) + { + StorageBaseImpl_Invalidate(stg->transactedChild); + + stg->transactedChild = NULL; + } +} + +/****************************************************************************** + * 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; +} + +/****************************************************************************** + * SetStateBits (IStorage) + */ +static HRESULT WINAPI StorageBaseImpl_SetStateBits( + 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; +} + +/****************************************************************************** + * Internal stream list handlers + */ + +void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) +{ + TRACE("Stream added (stg=%p strm=%p)\n", stg, strm); + list_add_tail(&stg->strmHead,&strm->StrmListEntry); +} + +void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) +{ + TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm); + list_remove(&(strm->StrmListEntry)); +} + +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; + + hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata); + + if (SUCCEEDED(hr)) + { + hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size); + + bytes_copied.QuadPart = 0; + while (bytes_copied.QuadPart < srcdata.size.QuadPart && 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 (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT; + bytes_copied.QuadPart += byteswritten; + } + } + } + + return hr; +} + +static HRESULT StorageBaseImpl_DupStorageTree( + StorageBaseImpl *dst, DirRef *dst_entry, + StorageBaseImpl *src, DirRef src_entry) +{ + HRESULT hr; + DirEntry data; + BOOL has_stream=FALSE; + + if (src_entry == DIRENTRY_NULL) + { + *dst_entry = DIRENTRY_NULL; + return S_OK; + } + + hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data); + if (SUCCEEDED(hr)) + { + 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); + } + + 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); + + return hr; +} + +static HRESULT StorageBaseImpl_CopyStorageTree( + StorageBaseImpl *dst, DirRef dst_entry, + StorageBaseImpl *src, DirRef src_entry) +{ + HRESULT hr; + DirEntry src_data, dst_data; + DirRef new_root_entry; + + hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data); + + if (SUCCEEDED(hr)) + { + hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry); + } + + 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; + } + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data); + + return hr; +} + +static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings) +{ + HRESULT hr; + DirEntry data; + ULARGE_INTEGER zero; + + if (entry == DIRENTRY_NULL) + return S_OK; + + zero.QuadPart = 0; + + hr = StorageBaseImpl_ReadDirEntry(This, entry, &data); + + if (SUCCEEDED(hr) && include_siblings) + hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE); + + if (SUCCEEDED(hr) && include_siblings) + hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE); + + if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM) + hr = StorageBaseImpl_StreamSetSize(This, entry, zero); + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_DestroyDirEntry(This, entry); + + return hr; +} + + +/************************************************************************ + * StorageImpl implementation + ***********************************************************************/ + +static HRESULT StorageImpl_ReadAt(StorageImpl* This, + ULARGE_INTEGER offset, + void* buffer, + ULONG size, + ULONG* bytesRead) +{ + return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead); +} + +static HRESULT StorageImpl_WriteAt(StorageImpl* This, + ULARGE_INTEGER offset, + const void* buffer, + const ULONG size, + ULONG* bytesWritten) +{ + return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten); +} + +/****************************************************************************** + * StorageImpl_LoadFileHeader + * + * This method will read in the file header + */ +static HRESULT StorageImpl_LoadFileHeader( + StorageImpl* This) +{ + HRESULT hr; + BYTE headerBigBlock[HEADER_SIZE]; + int index; + ULARGE_INTEGER offset; + DWORD bytes_read; + + TRACE("\n"); + /* + * Get a pointer to the big block of data containing the header. + */ + offset.HighPart = 0; + offset.LowPart = 0; + hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); + if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) + hr = STG_E_FILENOTFOUND; + + /* + * 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; + } + + if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0) + { + return STG_E_INVALIDHEADER; + } + + StorageUtl_ReadWord( + headerBigBlock, + OFFSET_BIGBLOCKSIZEBITS, + &This->bigBlockSizeBits); + + StorageUtl_ReadWord( + headerBigBlock, + OFFSET_SMALLBLOCKSIZEBITS, + &This->smallBlockSizeBits); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_BBDEPOTCOUNT, + &This->bigBlockDepotCount); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_ROOTSTARTBLOCK, + &This->rootStartBlock); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_TRANSACTIONSIG, + &This->transactionSig); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_SMALLBLOCKLIMIT, + &This->smallBlockLimit); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_SBDEPOTSTART, + &This->smallBlockDepotStart); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_EXTBBDEPOTSTART, + &This->extBigBlockDepotStart); + + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_EXTBBDEPOTCOUNT, + &This->extBigBlockDepotCount); + + for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) + { + StorageUtl_ReadDWord( + headerBigBlock, + OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), + &(This->bigBlockDepotStart[index])); + } + + /* + * 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=%#lx, smallblock=%#lx, sblimit=%#lx\n", + This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); + hr = STG_E_INVALIDHEADER; + } + else + hr = S_OK; + } + + return hr; +} + +/****************************************************************************** + * StorageImpl_SaveFileHeader + * + * This method will save to the file the header + */ +static void StorageImpl_SaveFileHeader( + StorageImpl* This) +{ + BYTE headerBigBlock[HEADER_SIZE]; + int index; + ULARGE_INTEGER offset; + DWORD bytes_written; + DWORD major_version, dirsectorcount; + + 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; + } + + memset(headerBigBlock, 0, HEADER_SIZE); + memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic)); + + /* + * Write the information to the header. + */ + StorageUtl_WriteWord( + headerBigBlock, + OFFSET_MINORVERSION, + 0x3e); + + StorageUtl_WriteWord( + headerBigBlock, + OFFSET_MAJORVERSION, + major_version); + + StorageUtl_WriteWord( + headerBigBlock, + OFFSET_BYTEORDERMARKER, + (WORD)-2); + + StorageUtl_WriteWord( + headerBigBlock, + OFFSET_BIGBLOCKSIZEBITS, + This->bigBlockSizeBits); + + StorageUtl_WriteWord( + headerBigBlock, + OFFSET_SMALLBLOCKSIZEBITS, + This->smallBlockSizeBits); + + 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; + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_DIRSECTORCOUNT, + dirsectorcount); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_BBDEPOTCOUNT, + This->bigBlockDepotCount); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_ROOTSTARTBLOCK, + This->rootStartBlock); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_TRANSACTIONSIG, + This->transactionSig); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_SMALLBLOCKLIMIT, + This->smallBlockLimit); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_SBDEPOTSTART, + This->smallBlockDepotStart); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_SBDEPOTCOUNT, + This->smallBlockDepotChain ? + BlockChainStream_GetCount(This->smallBlockDepotChain) : 0); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_EXTBBDEPOTSTART, + This->extBigBlockDepotStart); + + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_EXTBBDEPOTCOUNT, + This->extBigBlockDepotCount); + + for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) + { + StorageUtl_WriteDWord( + headerBigBlock, + OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), + (This->bigBlockDepotStart[index])); + } + + offset.QuadPart = 0; + StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written); +} + + +/************************************************************************ + * StorageImpl implementation : DirEntry methods + ***********************************************************************/ + +/****************************************************************************** + * StorageImpl_ReadRawDirEntry + * + * This method will read the raw data from a directory entry in the file. + * + * buffer must be RAW_DIRENTRY_SIZE bytes long. + */ +static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer) +{ + ULARGE_INTEGER offset; + HRESULT hr; + ULONG bytesRead; + + offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; + + hr = BlockChainStream_ReadAt( + This->rootBlockChain, + offset, + RAW_DIRENTRY_SIZE, + buffer, + &bytesRead); + + if (bytesRead != RAW_DIRENTRY_SIZE) + return STG_E_READFAULT; + + return hr; +} + +/****************************************************************************** + * StorageImpl_WriteRawDirEntry + * + * This method will write the raw data from a directory entry in the file. + * + * buffer must be RAW_DIRENTRY_SIZE bytes long. + */ +static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer) +{ + ULARGE_INTEGER offset; + ULONG bytesRead; + + offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; + + return BlockChainStream_WriteAt( + This->rootBlockChain, + offset, + RAW_DIRENTRY_SIZE, + buffer, + &bytesRead); +} + +/*************************************************************************** + * + * 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; + + memset(emptyData, 0, RAW_DIRENTRY_SIZE); + + return StorageImpl_WriteRawDirEntry(storage, index, emptyData); +} + +/****************************************************************************** + * UpdateRawDirEntry + * + * Update raw directory entry data from the fields in newData. + * + * buffer must be RAW_DIRENTRY_SIZE bytes long. + */ +static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData) +{ + memset(buffer, 0, RAW_DIRENTRY_SIZE); + + memcpy( + buffer + OFFSET_PS_NAME, + newData->name, + DIRENTRY_NAME_BUFFER_LEN ); + + memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1); + + StorageUtl_WriteWord( + buffer, + OFFSET_PS_NAMELENGTH, + newData->sizeOfNameString); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_LEFTCHILD, + newData->leftChild); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_RIGHTCHILD, + newData->rightChild); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_DIRROOT, + newData->dirRootEntry); + + StorageUtl_WriteGUID( + buffer, + OFFSET_PS_GUID, + &newData->clsid); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_CTIMELOW, + newData->ctime.dwLowDateTime); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_CTIMEHIGH, + newData->ctime.dwHighDateTime); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_MTIMELOW, + newData->mtime.dwLowDateTime); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_MTIMEHIGH, + newData->ctime.dwHighDateTime); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_STARTBLOCK, + newData->startingBlock); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_SIZE, + newData->size.LowPart); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_SIZE_HIGH, + newData->size.HighPart); +} + +/*************************************************************************** + * + * Internal Method + * + * Reserve a directory entry in the file and initialize it. + */ +static HRESULT StorageImpl_CreateDirEntry( + StorageBaseImpl *base, + const DirEntry *newData, + DirRef *index) +{ + StorageImpl *storage = (StorageImpl*)base; + ULONG currentEntryIndex = 0; + ULONG newEntryIndex = DIRENTRY_NULL; + HRESULT hr = S_OK; + BYTE currentData[RAW_DIRENTRY_SIZE]; + WORD sizeOfNameString; + + do + { + hr = StorageImpl_ReadRawDirEntry(storage, + currentEntryIndex, + currentData); + + if (SUCCEEDED(hr)) + { + StorageUtl_ReadWord( + currentData, + OFFSET_PS_NAMELENGTH, + &sizeOfNameString); + + 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++; + + } while (newEntryIndex == DIRENTRY_NULL); + + /* + * grow the directory stream + */ + if (FAILED(hr)) + { + BYTE emptyData[RAW_DIRENTRY_SIZE]; + ULARGE_INTEGER newSize; + ULONG entryIndex; + ULONG lastEntry = 0; + ULONG blockCount = 0; + + /* + * obtain the new count of blocks in the directory stream + */ + blockCount = BlockChainStream_GetCount( + storage->rootBlockChain)+1; + + /* + * initialize the size used by the directory stream + */ + newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount; + + /* + * add a block to the directory stream + */ + BlockChainStream_SetSize(storage->rootBlockChain, newSize); + + /* + * memset the empty entry in order to initialize the unused newly + * created entries + */ + memset(emptyData, 0, RAW_DIRENTRY_SIZE); + + /* + * initialize them + */ + lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount; + + for( + entryIndex = newEntryIndex + 1; + entryIndex < lastEntry; + entryIndex++) + { + StorageImpl_WriteRawDirEntry( + storage, + entryIndex, + emptyData); + } + + StorageImpl_SaveFileHeader(storage); + } + + UpdateRawDirEntry(currentData, newData); + + hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData); + + if (SUCCEEDED(hr)) + *index = newEntryIndex; + + return hr; +} + +/****************************************************************************** + * StorageImpl_ReadDirEntry + * + * This method will read the specified directory entry. + */ +static HRESULT StorageImpl_ReadDirEntry( + StorageImpl* This, + DirRef index, + DirEntry* buffer) +{ + BYTE currentEntry[RAW_DIRENTRY_SIZE]; + HRESULT readRes; + + readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry); + + if (SUCCEEDED(readRes)) + { + 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); + + StorageUtl_ReadWord( + currentEntry, + OFFSET_PS_NAMELENGTH, + &buffer->sizeOfNameString); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_LEFTCHILD, + &buffer->leftChild); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_RIGHTCHILD, + &buffer->rightChild); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_DIRROOT, + &buffer->dirRootEntry); + + StorageUtl_ReadGUID( + currentEntry, + OFFSET_PS_GUID, + &buffer->clsid); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_CTIMELOW, + &buffer->ctime.dwLowDateTime); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_CTIMEHIGH, + &buffer->ctime.dwHighDateTime); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_MTIMELOW, + &buffer->mtime.dwLowDateTime); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_MTIMEHIGH, + &buffer->mtime.dwHighDateTime); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_STARTBLOCK, + &buffer->startingBlock); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_SIZE, + &buffer->size.LowPart); + + if (This->bigBlockSize < 4096) + { + /* Version 3 files may have junk in the high part of size. */ + buffer->size.HighPart = 0; + } + else + { + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_SIZE_HIGH, + &buffer->size.HighPart); + } + } + + return readRes; +} + +/********************************************************************* + * Write the specified directory entry to the file + */ +static HRESULT StorageImpl_WriteDirEntry( + StorageImpl* This, + DirRef index, + const DirEntry* buffer) +{ + BYTE currentEntry[RAW_DIRENTRY_SIZE]; + + UpdateRawDirEntry(currentEntry, buffer); + + return StorageImpl_WriteRawDirEntry(This, index, currentEntry); +} + + +/************************************************************************ + * StorageImpl implementation : Block methods + ***********************************************************************/ + +static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index) +{ + return (ULONGLONG)(index+1) * This->bigBlockSize; +} + +static HRESULT StorageImpl_ReadBigBlock( + StorageImpl* This, + ULONG blockIndex, + void* buffer, + ULONG* out_read) +{ + ULARGE_INTEGER ulOffset; + DWORD read=0; + HRESULT hr; + + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); + + hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read); + + if (SUCCEEDED(hr) && read < This->bigBlockSize) + { + /* File ends during this block; fill the rest with 0's. */ + memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read); + } + + if (out_read) *out_read = read; + + return hr; +} + +static BOOL StorageImpl_ReadDWordFromBigBlock( + StorageImpl* This, + ULONG blockIndex, + ULONG offset, + DWORD* value) +{ + ULARGE_INTEGER ulOffset; + DWORD read; + DWORD tmp; + + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); + ulOffset.QuadPart += offset; + + StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read); + *value = lendian32toh(tmp); + return (read == sizeof(DWORD)); +} + +static BOOL StorageImpl_WriteBigBlock( + StorageImpl* This, + ULONG blockIndex, + const void* buffer) +{ + ULARGE_INTEGER ulOffset; + DWORD wrote; + + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); + + StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote); + return (wrote == This->bigBlockSize); +} + +static BOOL StorageImpl_WriteDWordToBigBlock( + StorageImpl* This, + ULONG blockIndex, + ULONG offset, + DWORD value) +{ + ULARGE_INTEGER ulOffset; + DWORD wrote; + + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); + ulOffset.QuadPart += offset; + + value = htole32(value); + StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote); + return (wrote == sizeof(DWORD)); +} + +/****************************************************************************** + * Storage32Impl_SmallBlocksToBigBlocks + * + * This method will convert a small block chain to a big block chain. + * The small block chain will be destroyed. + */ +static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( + StorageImpl* This, + SmallBlockChainStream** ppsbChain) +{ + 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; + + /* + * 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); + + /* + * Copy the contents of the small block chain to the big block chain + * by small block size increments. + */ + offset.LowPart = 0; + offset.HighPart = 0; + cbTotalRead.QuadPart = 0; + + buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); + do + { + resRead = SmallBlockChainStream_ReadAt(*ppsbChain, + offset, + min(This->smallBlockSize, size.LowPart - offset.LowPart), + buffer, + &cbRead); + if (FAILED(resRead)) + break; + + if (cbRead > 0) + { + cbTotalRead.QuadPart += cbRead; + + resWrite = BlockChainStream_WriteAt(bbTempChain, + offset, + cbRead, + buffer, + &cbWritten); + + if (FAILED(resWrite)) + break; + + offset.LowPart += cbRead; + } + else + { + resRead = STG_E_READFAULT; + break; + } + } while (cbTotalRead.QuadPart < size.QuadPart); + HeapFree(GetProcessHeap(),0,buffer); + + size.HighPart = 0; + size.LowPart = 0; + + if (FAILED(resRead) || FAILED(resWrite)) + { + ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite); + BlockChainStream_SetSize(bbTempChain, size); + BlockChainStream_Destroy(bbTempChain); + return NULL; + } + + /* + * 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; +} + +/****************************************************************************** + * Storage32Impl_BigBlocksToSmallBlocks + * + * This method will convert a big block chain to a small block chain. + * The big block chain will be destroyed on success. + */ +static 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; + + TRACE("%p %p\n", This, ppbbChain); + + sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain, + DIRENTRY_NULL); + + if(!sbTempChain) + return NULL; + + SmallBlockChainStream_SetSize(sbTempChain, newSize); + size = BlockChainStream_GetSize(*ppbbChain); + size.QuadPart = min(size.QuadPart, newSize.QuadPart); + + offset.HighPart = 0; + offset.LowPart = 0; + cbTotalRead.QuadPart = 0; + buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize); + while(cbTotalRead.QuadPart < size.QuadPart) + { + resRead = BlockChainStream_ReadAt(*ppbbChain, offset, + min(This->bigBlockSize, size.LowPart - offset.LowPart), + buffer, &cbRead); + + if(FAILED(resRead)) + break; + + if(cbRead > 0) + { + cbTotalRead.QuadPart += cbRead; + + resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset, + cbRead, buffer, &cbWritten); + + if(FAILED(resWrite)) + break; + + offset.LowPart += cbRead; + } + else + { + resRead = STG_E_READFAULT; + break; + } + } + HeapFree(GetProcessHeap(), 0, buffer); + + size.HighPart = 0; + size.LowPart = 0; + + if(FAILED(resRead) || FAILED(resWrite)) + { + ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite); + SmallBlockChainStream_SetSize(sbTempChain, size); + SmallBlockChainStream_Destroy(sbTempChain); + return NULL; + } + + /* destroy the original big block chain */ + streamEntryRef = (*ppbbChain)->ownerDirEntry; + BlockChainStream_SetSize(*ppbbChain, size); + BlockChainStream_Destroy(*ppbbChain); + *ppbbChain = NULL; + + StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); + streamEntry.startingBlock = sbHeadOfChain; + StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); + + SmallBlockChainStream_Destroy(sbTempChain); + return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); +} + +/****************************************************************************** + * 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) +{ + BYTE blockBuffer[MAX_BIG_BLOCK_SIZE]; + ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1; + ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); + ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot; + + /* + * Initialize blocks as free + */ + memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize); + + /* Reserve the range lock sector */ + if (depotIndex == rangeLockDepot) + { + ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN; + } + + StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer); +} + +/****************************************************************************** + * 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) +{ + 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); + + if (extBlockCount >= This->extBigBlockDepotCount) + return BLOCK_UNUSED; + + if (This->indexExtBlockDepotCached != extBlockCount) + { + 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; + } + + 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) + { + StorageImpl_WriteDWordToBigBlock(This, extBlockIndex, + extBlockOffset * sizeof(ULONG), + blockIndex); + } + + if (This->indexExtBlockDepotCached == extBlockCount) + { + This->extBlockDepotCached[extBlockOffset] = blockIndex; + } +} + +/****************************************************************************** + * 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; + + index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) * + blocksPerDepotBlock; + + if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN)) + { + /* + * The first extended block. + */ + This->extBigBlockDepotStart = index; + } + 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); + } + + /* + * Initialize this block. + */ + memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize); + StorageImpl_WriteBigBlock(This, index, depotBuffer); + + /* 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); + + memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize); + HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); + + This->extBigBlockDepotLocations = new_cache; + This->extBigBlockDepotLocationsSize = new_cache_size; + } + This->extBigBlockDepotLocations[numExtBlocks] = index; + + return index; +} + +/************************************************************************ + * 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; + + *nextBlockIndex = BLOCK_SPECIAL; + + if(depotBlockCount >= This->bigBlockDepotCount) + { + WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount, This->bigBlockDepotCount); + return STG_E_READFAULT; + } + + /* + * Cache the currently accessed depot block. + */ + if (depotBlockCount != This->indexBlockDepotCached) + { + This->indexBlockDepotCached = depotBlockCount; + + if (depotBlockCount < COUNT_BBDEPOTINHEADER) + { + depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; + } + else + { + /* + * We have to look in the extended depot. + */ + depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); + } + + StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); + + if (!read) + return STG_E_READFAULT; + + num_blocks = This->bigBlockSize / 4; + + for (index = 0; index < num_blocks; index++) + { + StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex); + This->blockDepotCached[index] = *nextBlockIndex; + } + } + + *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; + + return S_OK; +} + +/****************************************************************************** + * 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. + * + * Return values: + * - The index of the next extended block + * - BLOCK_UNUSED: there is no next extended block. + * - Any other return values denotes failure. + */ +static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) +{ + ULONG nextBlockIndex = BLOCK_SPECIAL; + ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); + + StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, + &nextBlockIndex); + + 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) + { + depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; + } + else + { + /* + * We have to look in the extended depot. + */ + depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); + } + + StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset, + nextBlock); + /* + * Update the cached block depot, if necessary. + */ + if (depotBlockCount == This->indexBlockDepotCached) + { + This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock; + } +} + +/****************************************************************************** + * 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 neededAddNumBlocks) +{ + 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; + + depotIndex = This->prevFreeBlock / blocksPerDepot; + depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG); + + /* + * 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]; + + /* + * Grow the primary depot. + */ + if (depotBlockIndexPos == BLOCK_UNUSED) + { + depotBlockIndexPos = depotIndex*blocksPerDepot; + + /* + * 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 + { + depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex); + + if (depotBlockIndexPos == BLOCK_UNUSED) + { + /* + * Grow the extended depot. + */ + ULONG extIndex = BLOCK_UNUSED; + ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; + ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1); + + if (extBlockOffset == 0) + { + /* We need an extended block. + */ + extIndex = Storage32Impl_AddExtBlockDepot(This); + This->extBigBlockDepotCount++; + depotBlockIndexPos = extIndex + 1; + } + else + depotBlockIndexPos = depotIndex * blocksPerDepot; + + /* + * Add a block depot and mark it in the extended block. + */ + Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex); + This->bigBlockDepotCount++; + Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos); + + /* Flag the block depot. + */ + StorageImpl_SetNextBlockInChain(This, + depotBlockIndexPos, + BLOCK_SPECIAL); + + /* If necessary, flag the extended depot block. + */ + if (extIndex != BLOCK_UNUSED) + StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT); + + /* Save header information. + */ + StorageImpl_SaveFileHeader(This); + } + } + + StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); + + if (read) + { + while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) && + ( nextBlockIndex != BLOCK_UNUSED)) + { + StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex); + + if (nextBlockIndex == BLOCK_UNUSED) + { + freeBlock = (depotIndex * blocksPerDepot) + + (depotBlockOffset/sizeof(ULONG)); + } + + depotBlockOffset += sizeof(ULONG); + } + } + + depotIndex++; + depotBlockOffset = 0; + } + + /* + * make sure that the block physically exists before using it + */ + neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize * neededAddNumBlocks; + + ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME); + + if (neededSize.QuadPart > statstg.cbSize.QuadPart) + ILockBytes_SetSize(This->lockBytes, neededSize); + + This->prevFreeBlock = freeBlock; + + return freeBlock; +} + +/****************************************************************************** + * StorageImpl_FreeBigBlock + * + * This method will flag the specified block as free in the big block depot. + */ +static void StorageImpl_FreeBigBlock( + StorageImpl* This, + ULONG blockIndex) +{ + StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); + + if (blockIndex < This->prevFreeBlock) + This->prevFreeBlock = blockIndex; +} + + +static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base, + DirRef index, const DirEntry *data) +{ + StorageImpl *This = (StorageImpl*)base; + return StorageImpl_WriteDirEntry(This, index, data); +} + +static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base, + DirRef index, DirEntry *data) +{ + StorageImpl *This = (StorageImpl*)base; + return StorageImpl_ReadDirEntry(This, index, data); +} + +static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This) +{ + int i; + + for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) + { + if (!This->blockChainCache[i]) + { + return &This->blockChainCache[i]; + } + } + + i = This->blockChainToEvict; + + BlockChainStream_Destroy(This->blockChainCache[i]); + This->blockChainCache[i] = NULL; + + This->blockChainToEvict++; + if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) + This->blockChainToEvict = 0; + + return &This->blockChainCache[i]; +} + +static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This, + DirRef index) +{ + int i, free_index=-1; + + 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]; + } + } + + if (free_index == -1) + { + free_index = This->blockChainToEvict; + + BlockChainStream_Destroy(This->blockChainCache[free_index]); + This->blockChainCache[free_index] = NULL; + + This->blockChainToEvict++; + if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) + This->blockChainToEvict = 0; + } + + This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index); + return &This->blockChainCache[free_index]; +} + +static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index) +{ + int i; + + for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) + { + if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index) + { + BlockChainStream_Destroy(This->blockChainCache[i]); + This->blockChainCache[i] = NULL; + return; + } + } +} + +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; + + hr = StorageImpl_ReadDirEntry(This, index, &data); + if (FAILED(hr)) return hr; + + if (data.size.QuadPart == 0) + { + *bytesRead = 0; + return S_OK; + } + + if (offset.QuadPart + size > data.size.QuadPart) + { + bytesToRead = data.size.QuadPart - offset.QuadPart; + } + else + { + bytesToRead = size; + } + + if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) + { + 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); + + return hr; + } +} + +static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index, + ULARGE_INTEGER newsize) +{ + StorageImpl *This = (StorageImpl*)base; + DirEntry data; + HRESULT hr; + SmallBlockChainStream *smallblock=NULL; + BlockChainStream **pbigblock=NULL, *bigblock=NULL; + + hr = StorageImpl_ReadDirEntry(This, index, &data); + if (FAILED(hr)) return hr; + + /* 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); + + if (data.size.QuadPart == newsize.QuadPart) + return S_OK; + + /* 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; + } + + /* 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; + } + + pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This); + *pbigblock = bigblock; + } + else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) + { + smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize); + if (!smallblock) + return E_FAIL; + } + + /* 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 HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, + ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) +{ + 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; + + if (newSize.QuadPart > data.size.QuadPart) + { + hr = StorageImpl_StreamSetSize(base, index, newSize); + if (FAILED(hr)) + return hr; + + hr = StorageImpl_ReadDirEntry(This, index, &data); + if (FAILED(hr)) return hr; + } + + if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) + { + SmallBlockChainStream *stream; + + stream = SmallBlockChainStream_Construct(This, NULL, index); + if (!stream) return E_OUTOFMEMORY; + + hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); + + 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); + } +} + +static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst, + DirRef src) +{ + StorageImpl *This = (StorageImpl*)base; + DirEntry dst_data, src_data; + HRESULT hr; + + hr = StorageImpl_ReadDirEntry(This, dst, &dst_data); + + if (SUCCEEDED(hr)) + hr = StorageImpl_ReadDirEntry(This, src, &src_data); + + if (SUCCEEDED(hr)) + { + StorageImpl_DeleteCachedBlockChainStream(This, src); + dst_data.startingBlock = src_data.startingBlock; + dst_data.size = src_data.size; + + hr = StorageImpl_WriteDirEntry(This, dst, &dst_data); + } + + return hr; +} + +static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create) +{ + HRESULT hr=S_OK; + DirEntry currentEntry; + DirRef currentEntryRef; + BlockChainStream *blockChainStream; + + if (create) + { + ULARGE_INTEGER size; + BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE]; + + /* Discard any existing data. */ + size.QuadPart = 0; + ILockBytes_SetSize(This->lockBytes, size); + + /* + * 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_SaveFileHeader(This); + + /* + * Add one block for the big block depot and one block for the directory table + */ + size.HighPart = 0; + size.LowPart = This->bigBlockSize * 3; + ILockBytes_SetSize(This->lockBytes, 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); + + if (FAILED(hr)) + { + return hr; + } + } + + /* + * There is no block depot cached yet. + */ + This->indexBlockDepotCached = 0xFFFFFFFF; + This->indexExtBlockDepotCached = 0xFFFFFFFF; + + /* + * Start searching for free blocks with block 0. + */ + This->prevFreeBlock = 0; + + This->firstFreeSmallBlock = 0; + + /* Read the extended big block depot locations. */ + if (This->extBigBlockDepotCount != 0) + { + ULONG current_block = This->extBigBlockDepotStart; + ULONG cache_size = This->extBigBlockDepotCount * 2; + ULONG i; + + This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size); + if (!This->extBigBlockDepotLocations) + { + return E_OUTOFMEMORY; + } + + This->extBigBlockDepotLocationsSize = cache_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; + } + + /* + * 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; + + if(!(blockChainStream = + BlockChainStream_Construct(This, &This->smallBlockDepotStart, + DIRENTRY_NULL))) + { + return STG_E_READFAULT; + } + if (!new_object) + BlockChainStream_Destroy(This->smallBlockDepotChain); + This->smallBlockDepotChain = blockChainStream; + + /* + * Write the root storage entry (memory only) + */ + if (create) + { + DirEntry rootEntry; + /* + * Initialize the directory table + */ + memset(&rootEntry, 0, sizeof(rootEntry)); + lstrcpyW(rootEntry.name, L"Root Entry"); + rootEntry.sizeOfNameString = sizeof(L"Root Entry"); + rootEntry.stgType = STGTY_ROOT; + rootEntry.leftChild = DIRENTRY_NULL; + rootEntry.rightChild = DIRENTRY_NULL; + rootEntry.dirRootEntry = DIRENTRY_NULL; + rootEntry.startingBlock = BLOCK_END_OF_CHAIN; + rootEntry.size.HighPart = 0; + rootEntry.size.LowPart = 0; + + StorageImpl_WriteDirEntry(This, 0, &rootEntry); + } + + /* + * Find the ID of the root storage. + */ + currentEntryRef = 0; + + do + { + hr = StorageImpl_ReadDirEntry( + This, + currentEntryRef, + ¤tEntry); + + if (SUCCEEDED(hr)) + { + if ( (currentEntry.sizeOfNameString != 0 ) && + (currentEntry.stgType == STGTY_ROOT) ) + { + This->base.storageDirEntry = currentEntryRef; + } + } + + currentEntryRef++; + + } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) ); + + if (FAILED(hr)) + { + return STG_E_READFAULT; + } + + /* + * 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; + + 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 StorageImpl_GetTransactionSig(StorageBaseImpl *base, + ULONG* result, BOOL refresh) +{ + StorageImpl *This = (StorageImpl*)base; + HRESULT hr=S_OK; + DWORD oldTransactionSig = This->transactionSig; + + if (refresh) + { + ULARGE_INTEGER offset; + ULONG bytes_read; + BYTE data[4]; + + offset.HighPart = 0; + offset.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; + } + } + + *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_LockRegion(StorageImpl *This, ULARGE_INTEGER offset, + ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) +{ + if ((dwLockType & This->locks_supported) == 0) + { + if (supported) *supported = FALSE; + return S_OK; + } + + if (supported) *supported = TRUE; + return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); +} + +static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + if ((dwLockType & This->locks_supported) == 0) + return S_OK; + + return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType); +} + +/* Internal function */ +static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, + ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) +{ + HRESULT hr; + int delay = 0; + DWORD start_time = GetTickCount(); + DWORD last_sanity_check = start_time; + ULARGE_INTEGER sanity_offset, sanity_cb; + + sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST; + sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1; + + do + { + hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported); + + 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 = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL); + if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) + break; + if (SUCCEEDED(hr)) + { + StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ); + hr = STG_E_ACCESSDENIED; + } + + last_sanity_check = current_time; + } + Sleep(delay); + if (delay < 150) delay++; + } + } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION); + + return hr; +} + +static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write) +{ + StorageImpl *This = (StorageImpl*)base; + HRESULT hr; + ULARGE_INTEGER offset, cb; + + if (write) + { + /* 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; + } + + hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL); + + return hr; +} + +static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) +{ + StorageImpl *This = (StorageImpl*)base; + HRESULT hr; + ULARGE_INTEGER offset, cb; + + if (write) + { + offset.QuadPart = RANGELOCK_TRANSACTION_FIRST; + cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1; + } + else + { + offset.QuadPart = RANGELOCK_COMMIT; + cb.QuadPart = 1; + } + + hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); + + return hr; +} + +static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) +{ + StorageImpl *This = (StorageImpl*) iface; + STATSTG statstg; + HRESULT hr; + + hr = ILockBytes_Stat(This->lockBytes, &statstg, 0); + + *result = statstg.pwcsName; + + return hr; +} + +static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start, + ULONG end, HRESULT fail_hr) +{ + HRESULT hr; + ULARGE_INTEGER offset, cb; + + offset.QuadPart = start; + cb.QuadPart = 1 + end - start; + + hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); + if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); + + if (FAILED(hr)) + 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 = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); + if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION) + break; + } + + if (SUCCEEDED(hr)) + { + for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++) + { + if (This->locked_bytes[j] == 0) + { + This->locked_bytes[j] = i; + break; + } + } + } + + return hr; +} + +static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) +{ + HRESULT hr; + ULARGE_INTEGER offset; + ULARGE_INTEGER cb; + DWORD share_mode = STGM_SHARE_MODE(openFlags); + BOOL supported; + + 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; + } + + /* 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, &supported); + + /* If the ILockBytes doesn't support locking that's ok. */ + if (!supported) return S_OK; + else if (FAILED(hr)) return hr; + + hr = S_OK; + + /* First check for any conflicting locks. */ + if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY) + hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION); + + if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) + hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION); + + if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) + hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION); + + 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); + + if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE) + { + hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION); + + if (SUCCEEDED(hr)) + hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION); + } + + /* 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 (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) + hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST); + + if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) + hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST); + + 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 (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 (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT) + hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST); + + offset.QuadPart = RANGELOCK_CHECKLOCKS; + cb.QuadPart = 1; + StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); + + return hr; +} + +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); + + if (SUCCEEDED(hr)) + hr = BlockChainStream_Flush(This->smallBlockDepotChain); + + for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++) + if (This->blockChainCache[i]) + hr = BlockChainStream_Flush(This->blockChainCache[i]); + + if (SUCCEEDED(hr)) + hr = ILockBytes_Flush(This->lockBytes); + + 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); + + StorageImpl_Flush(iface); + + StorageImpl_Invalidate(iface); + + HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); + + BlockChainStream_Destroy(This->smallBlockRootChain); + BlockChainStream_Destroy(This->rootBlockChain); + BlockChainStream_Destroy(This->smallBlockDepotChain); + + for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++) + BlockChainStream_Destroy(This->blockChainCache[i]); + + for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++) + { + ULARGE_INTEGER offset, cb; + cb.QuadPart = 1; + if (This->locked_bytes[i] != 0) + { + offset.QuadPart = This->locked_bytes[i]; + StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); + } + } + + if (This->lockBytes) + ILockBytes_Release(This->lockBytes); + HeapFree(GetProcessHeap(), 0, This); +} + + +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 +}; + + +/* + * 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 +}; + +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; + STATSTG stat; + + 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); + + list_init(&This->base.storageHead); + + 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 (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; + + This->base.reverted = FALSE; + + /* + * 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 + { + This->lockBytes = pLkbyt; + ILockBytes_AddRef(pLkbyt); + } + + if (SUCCEEDED(hr)) + hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME); + + if (SUCCEEDED(hr)) + { + This->locks_supported = stat.grfLocksSupported; + if (!hFile) + /* Don't try to use wine-internal locking flag with custom ILockBytes */ + This->locks_supported &= ~WINE_LOCK_READ; + + 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; +} + + +/************************************************************************ + * StorageInternalImpl implementation + ***********************************************************************/ + +static void StorageInternalImpl_Invalidate( StorageBaseImpl *base ) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + if (!This->base.reverted) + { + TRACE("Storage invalidated (stg=%p)\n", This); + + This->base.reverted = TRUE; + + This->parentStorage = NULL; + + StorageBaseImpl_DeleteAll(&This->base); + + list_remove(&This->ParentListEntry); + } +} + +static void StorageInternalImpl_Destroy( StorageBaseImpl *iface) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; + + StorageInternalImpl_Invalidate(&This->base); + + HeapFree(GetProcessHeap(), 0, This); +} + +static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; + + return StorageBaseImpl_Flush(This->parentStorage); +} + +static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; + + return StorageBaseImpl_GetFilename(This->parentStorage, result); +} + +static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base, + const DirEntry *newData, DirRef *index) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_CreateDirEntry(This->parentStorage, + newData, index); +} + +static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base, + DirRef index, const DirEntry *data) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_WriteDirEntry(This->parentStorage, + index, data); +} + +static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base, + DirRef index, DirEntry *data) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_ReadDirEntry(This->parentStorage, + index, data); +} + +static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base, + DirRef index) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_DestroyDirEntry(This->parentStorage, + index); +} + +static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_StreamReadAt(This->parentStorage, + index, offset, size, buffer, bytesRead); +} + +static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_StreamWriteAt(This->parentStorage, + index, offset, size, buffer, bytesWritten); +} + +static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER newsize) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_StreamSetSize(This->parentStorage, + index, newsize); +} + +static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base, + DirRef dst, DirRef src) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_StreamLink(This->parentStorage, + dst, src); +} + +static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base, + ULONG* result, BOOL refresh) +{ + return E_NOTIMPL; +} + +static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base, + ULONG value) +{ + return E_NOTIMPL; +} + +static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write) +{ + return E_NOTIMPL; +} + +static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) +{ + return E_NOTIMPL; +} + +/****************************************************************************** +** +** StorageInternalImpl_Commit +** +*/ +static HRESULT WINAPI StorageInternalImpl_Commit( + IStorage* iface, + DWORD grfCommitFlags) /* [in] */ +{ + StorageBaseImpl* This = impl_from_IStorage(iface); + TRACE("%p, %#lx.\n", iface, grfCommitFlags); + return StorageBaseImpl_Flush(This); +} + +/****************************************************************************** +** +** StorageInternalImpl_Revert +** +*/ +static HRESULT WINAPI StorageInternalImpl_Revert( + IStorage* iface) +{ + FIXME("(%p): stub\n", iface); + return S_OK; +} + +/* + * Virtual function table for the StorageInternalImpl class. + */ +static const IStorageVtbl StorageInternalImpl_Vtbl = +{ + StorageBaseImpl_QueryInterface, + StorageBaseImpl_AddRef, + StorageBaseImpl_Release, + StorageBaseImpl_CreateStream, + StorageBaseImpl_OpenStream, + StorageBaseImpl_CreateStorage, + StorageBaseImpl_OpenStorage, + StorageBaseImpl_CopyTo, + StorageBaseImpl_MoveElementTo, + StorageInternalImpl_Commit, + StorageInternalImpl_Revert, + StorageBaseImpl_EnumElements, + StorageBaseImpl_DestroyElement, + StorageBaseImpl_RenameElement, + StorageBaseImpl_SetElementTimes, + StorageBaseImpl_SetClass, + StorageBaseImpl_SetStateBits, + StorageBaseImpl_Stat +}; + +static const StorageBaseImplVtbl StorageInternalImpl_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 +}; + +static StorageInternalImpl* StorageInternalImpl_Construct( + StorageBaseImpl* parentStorage, + DWORD openFlags, + DirRef storageDirEntry) +{ + StorageInternalImpl* newStorage; + + newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl)); + + if (newStorage!=0) + { + list_init(&newStorage->base.strmHead); + + list_init(&newStorage->base.storageHead); + + /* + * 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); + + newStorage->base.reverted = FALSE; + + newStorage->base.ref = 1; + + newStorage->parentStorage = parentStorage; + + /* + * Keep a reference to the directory entry of this storage + */ + newStorage->base.storageDirEntry = storageDirEntry; + + newStorage->base.create = FALSE; + + return newStorage; + } + + return 0; +} + + +/************************************************************************ + * TransactedSnapshotImpl implementation + ***********************************************************************/ + +static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This) +{ + DirRef result=This->firstFreeEntry; + + while (result < This->entries_size && This->entries[result].inuse) + result++; + + if (result == This->entries_size) + { + 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; + } + + This->entries[result].inuse = TRUE; + + This->firstFreeEntry = result+1; + + return result; +} + +static DirRef TransactedSnapshotImpl_CreateStubEntry( + TransactedSnapshotImpl *This, DirRef parentEntryRef) +{ + DirRef stubEntryRef; + TransactedDirEntry *entry; + + stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This); + + if (stubEntryRef != DIRENTRY_NULL) + { + entry = &This->entries[stubEntryRef]; + + entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef; + + entry->read = FALSE; + } + + return stubEntryRef; +} + +static HRESULT TransactedSnapshotImpl_EnsureReadEntry( + TransactedSnapshotImpl *This, DirRef entry) +{ + HRESULT hr=S_OK; + DirEntry data; + + if (!This->entries[entry].read) + { + hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, + This->entries[entry].transactedParentEntry, + &data); + + if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL) + { + data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild); + + if (data.leftChild == DIRENTRY_NULL) + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL) + { + data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild); + + if (data.rightChild == DIRENTRY_NULL) + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL) + { + data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry); + + if (data.dirRootEntry == DIRENTRY_NULL) + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + memcpy(&This->entries[entry].data, &data, sizeof(DirEntry)); + This->entries[entry].read = TRUE; + } + } + + return hr; +} + +static HRESULT TransactedSnapshotImpl_MakeStreamDirty( + TransactedSnapshotImpl *This, DirRef entry) +{ + 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); + + 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); + + if (FAILED(hr)) + StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry); + } + + if (SUCCEEDED(hr)) + This->entries[entry].stream_dirty = TRUE; + + 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 (delete_ref != DIRENTRY_NULL) + This->entries[delete_ref].deleted = TRUE; + + This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL; + } + } + + return hr; +} + +/* Find the first entry in a depth-first traversal. */ +static DirRef TransactedSnapshotImpl_FindFirstChild( + TransactedSnapshotImpl* This, DirRef parent) +{ + DirRef cursor, prev; + TransactedDirEntry *entry; + + 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) + { + prev = cursor; + cursor = entry->data.dirRootEntry; + entry = &This->entries[cursor]; + entry->parent = prev; + } + else + break; + } + + return cursor; +} + +/* Find the next entry in a depth-first traversal. */ +static DirRef TransactedSnapshotImpl_FindNextChild( + TransactedSnapshotImpl* This, DirRef current) +{ + DirRef parent; + TransactedDirEntry *parent_entry; + + parent = This->entries[current].parent; + parent_entry = &This->entries[parent]; + + 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 (parent_entry->data.dirRootEntry != DIRENTRY_NULL) + { + This->entries[parent_entry->data.dirRootEntry].parent = parent; + return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry); + } + } + + return parent; +} + +/* 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; +} + +/* Destroy the entries created by CopyTree. */ +static void TransactedSnapshotImpl_DestroyTemporaryCopy( + TransactedSnapshotImpl* This, DirRef stop) +{ + DirRef cursor; + TransactedDirEntry *entry; + ULARGE_INTEGER zero; + + zero.QuadPart = 0; + + if (!This->entries[This->base.storageDirEntry].read) + return; + + cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry; + + if (cursor == DIRENTRY_NULL) + return; + + cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor); + + while (cursor != DIRENTRY_NULL && cursor != stop) + { + if (TransactedSnapshotImpl_MadeCopy(This, cursor)) + { + entry = &This->entries[cursor]; + + if (entry->stream_dirty) + StorageBaseImpl_StreamSetSize(This->transactedParent, + entry->newTransactedParentEntry, zero); + + StorageBaseImpl_DestroyDirEntry(This->transactedParent, + entry->newTransactedParentEntry); + + entry->newTransactedParentEntry = entry->transactedParentEntry; + } + + cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); + } +} + +/* 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; + + cursor = This->base.storageDirEntry; + entry = &This->entries[cursor]; + entry->parent = DIRENTRY_NULL; + entry->newTransactedParentEntry = entry->transactedParentEntry; + + if (entry->data.dirRootEntry == DIRENTRY_NULL) + return S_OK; + + This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL; + + cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry); + entry = &This->entries[cursor]; + + 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; + + memcpy(&newData, &entry->data, sizeof(DirEntry)); + + newData.size.QuadPart = 0; + newData.startingBlock = BLOCK_END_OF_CHAIN; + + if (newData.leftChild != DIRENTRY_NULL) + newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry; + + if (newData.rightChild != DIRENTRY_NULL) + newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry; + + 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; + } + + 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)) + { + cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); + TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); + return hr; + } + } + + cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); + entry = &This->entries[cursor]; + } + + return hr; +} + +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; + + zero.QuadPart = 0; + + TRACE("%p, %#lx.\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)) + { + 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; + + 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. */ + + 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 + 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); + } + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static HRESULT WINAPI TransactedSnapshotImpl_Revert( + IStorage* 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); + + This->firstFreeEntry = 0; + This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry); + + return S_OK; +} + +static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This) +{ + if (!This->reverted) + { + TRACE("Storage invalidated (stg=%p)\n", This); + + This->reverted = TRUE; + + StorageBaseImpl_DeleteAll(This); + } +} + +static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) 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); +} + +static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface) +{ + /* We only need to flush when committing. */ + return S_OK; +} + +static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; + + return StorageBaseImpl_GetFilename(This->transactedParent, result); +} + +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=%lx r=%lx d=%lx <-- %lx\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) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; + + TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", 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; + } + } + TRACE("<-- S_OK\n"); + return S_OK; +} + +static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, + DirRef index, DirEntry *data) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + memcpy(data, &This->entries[index].data, sizeof(DirEntry)); + + TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); + + return S_OK; +} + +static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, + DirRef index) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + + 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)) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", 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); + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER newsize) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", 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; + + TRACE("<-- %#lx\n", hr); + return hr; +} + +static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base, + DirRef dst, DirRef src) +{ + TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; + TransactedDirEntry *dst_entry, *src_entry; + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, src); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", hr); + return hr; + } + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst); + if (FAILED(hr)) + { + TRACE("<-- %#lx\n", 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 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 = +{ + 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 +}; + +static const StorageBaseImplVtbl TransactedSnapshotImpl_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 +}; + +static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, + TransactedSnapshotImpl** result) +{ + HRESULT hr; + + *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl)); + if (*result) + { + IStorage *scratch; + + (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl; + + /* 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; + + list_init(&(*result)->base.strmHead); + + list_init(&(*result)->base.storageHead); + + (*result)->base.ref = 1; + + (*result)->base.openFlags = parentStorage->openFlags; + + /* This cannot fail, except with E_NOTIMPL in which case we don't care */ + StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, 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); + + if (SUCCEEDED(hr)) + { + ULONG num_entries = 20; + + (*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; + + parentStorage->transactedChild = &(*result)->base; + + (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry); + } + else + { + IStorage_Release(scratch); + + hr = E_OUTOFMEMORY; + } + } + + if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result); + + return hr; + } + else + return E_OUTOFMEMORY; +} + + +/************************************************************************ + * TransactedSharedImpl implementation + ***********************************************************************/ + +static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This) +{ + if (!This->reverted) + { + TRACE("Storage invalidated (stg=%p)\n", This); + + This->reverted = TRUE; + + StorageBaseImpl_DeleteAll(This); + } +} + +static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) iface; + + 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 TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) iface; + + return StorageBaseImpl_GetFilename(This->transactedParent, result); +} + +static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base, + const DirEntry *newData, DirRef *index) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_CreateDirEntry(&This->scratch->base, + newData, index); +} + +static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base, + DirRef index, const DirEntry *data) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_WriteDirEntry(&This->scratch->base, + index, data); +} + +static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base, + DirRef index, DirEntry *data) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_ReadDirEntry(&This->scratch->base, + index, data); +} + +static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base, + DirRef index) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_DestroyDirEntry(&This->scratch->base, + index); +} + +static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_StreamReadAt(&This->scratch->base, + index, offset, size, buffer, bytesRead); +} + +static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_StreamWriteAt(&This->scratch->base, + index, offset, size, buffer, bytesWritten); +} + +static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base, + DirRef index, ULARGE_INTEGER newsize) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_StreamSetSize(&This->scratch->base, + index, newsize); +} + +static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base, + DirRef dst, DirRef src) +{ + TransactedSharedImpl* This = (TransactedSharedImpl*) base; + + return StorageBaseImpl_StreamLink(&This->scratch->base, + dst, src); +} + +static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base, + ULONG* result, BOOL refresh) +{ + return E_NOTIMPL; +} + +static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base, + ULONG value) +{ + return E_NOTIMPL; +} + +static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write) +{ + return E_NOTIMPL; +} + +static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) +{ + return E_NOTIMPL; +} + +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; + + TRACE("%p, %#lx\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)) + { + hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE); + if (SUCCEEDED(hr)) + { + if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig) + hr = STG_E_NOTCURRENT; + + if (SUCCEEDED(hr)) + hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1); + } + 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)) + { + 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; + } + } + TRACE("<-- %#lx\n", hr); + return hr; +} + +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, + 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 +}; + +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 +}; + +static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage, + TransactedSharedImpl** result) +{ + HRESULT hr; + + *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl)); + if (*result) + { + IStorage *scratch; + + (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl; + + /* 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(&(*result)->base.strmHead); + + list_init(&(*result)->base.storageHead); + + (*result)->base.ref = 1; + + (*result)->base.openFlags = parentStorage->openFlags; + + hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE); + + if (SUCCEEDED(hr)) + { + STGOPTIONS stgo; + + /* This cannot fail, except with E_NOTIMPL in which case we don't care */ + StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE); + + 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 %lx\n", parentStorage->openFlags); + } + + 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); +} + +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) +{ + WORD tmp; + + memcpy(&tmp, buffer+offset, sizeof(WORD)); + *value = lendian16toh(tmp); +} + +void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value) +{ + value = htole16(value); + memcpy((BYTE *)buffer + offset, &value, sizeof(WORD)); +} + +void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value) +{ + DWORD tmp; + + memcpy(&tmp, buffer+offset, sizeof(DWORD)); + *value = lendian32toh(tmp); +} + +void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value) +{ + value = htole32(value); + memcpy((BYTE *)buffer + offset, &value, sizeof(DWORD)); +} + +void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, + ULARGE_INTEGER* value) +{ +#ifdef WORDS_BIGENDIAN + ULARGE_INTEGER tmp; + + memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER)); + value->u.LowPart = htole32(tmp.HighPart); + value->u.HighPart = htole32(tmp.LowPart); +#else + memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER)); +#endif +} + +void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value) +{ +#ifdef WORDS_BIGENDIAN + ULARGE_INTEGER tmp; + + tmp.LowPart = htole32(value->u.HighPart); + tmp.HighPart = htole32(value->u.LowPart); + memcpy((BYTE *)buffer + offset, &tmp, sizeof(ULARGE_INTEGER)); +#else + memcpy((BYTE *)buffer + offset, value, sizeof(ULARGE_INTEGER)); +#endif +} + +void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value) +{ + StorageUtl_ReadDWord(buffer, offset, (DWORD *)&value->Data1); + StorageUtl_ReadWord(buffer, offset+4, &(value->Data2)); + StorageUtl_ReadWord(buffer, offset+6, &(value->Data3)); + + memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4)); +} + +void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value) +{ + StorageUtl_WriteDWord(buffer, offset, value->Data1); + StorageUtl_WriteWord(buffer, offset+4, value->Data2); + StorageUtl_WriteWord(buffer, offset+6, value->Data3); + + memcpy((BYTE *)buffer + offset + 8, value->Data4, sizeof(value->Data4)); +} + +void StorageUtl_CopyDirEntryToSTATSTG( + StorageBaseImpl* storage, + STATSTG* destination, + const DirEntry* source, + int statFlags) +{ + /* + * The copy of the string occurs only when the flag is not set + */ + if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT) + { + /* Use the filename for the root storage. */ + destination->pwcsName = 0; + StorageBaseImpl_GetFilename(storage, &destination->pwcsName); + } + else if( ((statFlags & STATFLAG_NONAME) != 0) || + (source->name[0] == 0) ) + { + destination->pwcsName = 0; + } + else + { + destination->pwcsName = + CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR)); + + lstrcpyW(destination->pwcsName, source->name); + } + + switch (source->stgType) + { + case STGTY_STORAGE: + case STGTY_ROOT: + destination->type = STGTY_STORAGE; + break; + case STGTY_STREAM: + destination->type = STGTY_STREAM; + break; + default: + destination->type = STGTY_STREAM; + break; + } + + destination->cbSize = source->size; +/* + currentReturnStruct->mtime = {0}; TODO + currentReturnStruct->ctime = {0}; + currentReturnStruct->atime = {0}; +*/ + destination->grfMode = 0; + destination->grfLocksSupported = 0; + destination->clsid = source->clsid; + destination->grfStateBits = 0; + destination->reserved = 0; +} + + +/************************************************************************ + * 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. */ +static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) +{ + ULONG next_sector, next_offset; + HRESULT hr; + struct BlockChainRun *last_run; + + if (This->indexCacheLen == 0) + { + last_run = NULL; + next_offset = 0; + next_sector = BlockChainStream_GetHeadOfChain(This); + } + else + { + last_run = &This->indexCache[This->indexCacheLen-1]; + next_offset = last_run->lastOffset+1; + hr = StorageImpl_GetNextBlockInChain(This->parentStorage, + last_run->firstSector + last_run->lastOffset - last_run->firstOffset, + &next_sector); + if (FAILED(hr)) return hr; + } + + while (next_sector != BLOCK_END_OF_CHAIN) + { + if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset) + { + /* Add the current block to the cache. */ + if (This->indexCacheSize == 0) + { + This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16); + if (!This->indexCache) return E_OUTOFMEMORY; + This->indexCacheSize = 16; + } + else if (This->indexCacheSize == This->indexCacheLen) + { + struct BlockChainRun *new_cache; + ULONG new_size; + + new_size = This->indexCacheSize * 2; + new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size); + if (!new_cache) return E_OUTOFMEMORY; + memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen); + + HeapFree(GetProcessHeap(), 0, This->indexCache); + This->indexCache = new_cache; + This->indexCacheSize = new_size; + } + + This->indexCacheLen++; + last_run = &This->indexCache[This->indexCacheLen-1]; + last_run->firstSector = next_sector; + last_run->firstOffset = next_offset; + } + + last_run->lastOffset = next_offset; + + /* Find the next block. */ + next_offset++; + hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector); + if (FAILED(hr)) return hr; + } + + if (This->indexCacheLen) + { + This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset; + This->numBlocks = last_run->lastOffset+1; + } + else + { + This->tailIndex = BLOCK_END_OF_CHAIN; + This->numBlocks = 0; + } + + return S_OK; +} + +/* Locate the nth block in this stream. */ +static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) +{ + ULONG min_offset = 0, max_offset = This->numBlocks-1; + ULONG min_run = 0, max_run = This->indexCacheLen-1; + + if (offset >= This->numBlocks) + return BLOCK_END_OF_CHAIN; + + while (min_run < max_run) + { + ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset); + if (offset < This->indexCache[run_to_check].firstOffset) + { + max_offset = This->indexCache[run_to_check].firstOffset-1; + max_run = run_to_check-1; + } + else if (offset > This->indexCache[run_to_check].lastOffset) + { + min_offset = This->indexCache[run_to_check].lastOffset+1; + min_run = run_to_check+1; + } + else + /* Block is in this run. */ + min_run = max_run = run_to_check; + } + + return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; +} + +static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, + ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) +{ + BlockChainBlock *result=NULL; + int i; + + for (i=0; i<2; i++) + if (This->cachedBlocks[i].index == index) + { + *sector = This->cachedBlocks[i].sector; + *block = &This->cachedBlocks[i]; + return S_OK; + } + + *sector = BlockChainStream_GetSectorOfOffset(This, index); + if (*sector == BLOCK_END_OF_CHAIN) + return STG_E_DOCFILECORRUPT; + + if (create) + { + if (This->cachedBlocks[0].index == 0xffffffff) + result = &This->cachedBlocks[0]; + else if (This->cachedBlocks[1].index == 0xffffffff) + result = &This->cachedBlocks[1]; + else + { + result = &This->cachedBlocks[This->blockToEvict++]; + if (This->blockToEvict == 2) + This->blockToEvict = 0; + } + + if (result->dirty) + { + if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data)) + return STG_E_WRITEFAULT; + result->dirty = FALSE; + } + + result->read = FALSE; + result->index = index; + result->sector = *sector; + } + + *block = result; + return S_OK; +} + +BlockChainStream* BlockChainStream_Construct( + StorageImpl* parentStorage, + ULONG* headOfStreamPlaceHolder, + DirRef dirEntry) +{ + BlockChainStream* newStream; + + newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream)); + if(!newStream) + return NULL; + + newStream->parentStorage = parentStorage; + newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; + newStream->ownerDirEntry = dirEntry; + newStream->indexCache = NULL; + newStream->indexCacheLen = 0; + newStream->indexCacheSize = 0; + newStream->cachedBlocks[0].index = 0xffffffff; + newStream->cachedBlocks[0].dirty = FALSE; + newStream->cachedBlocks[1].index = 0xffffffff; + newStream->cachedBlocks[1].dirty = FALSE; + newStream->blockToEvict = 0; + + if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) + { + HeapFree(GetProcessHeap(), 0, newStream->indexCache); + HeapFree(GetProcessHeap(), 0, newStream); + return NULL; + } + + return newStream; +} + +HRESULT BlockChainStream_Flush(BlockChainStream* This) +{ + int i; + if (!This) return S_OK; + for (i=0; i<2; i++) + { + if (This->cachedBlocks[i].dirty) + { + if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data)) + This->cachedBlocks[i].dirty = FALSE; + else + return STG_E_WRITEFAULT; + } + } + return S_OK; +} + +void BlockChainStream_Destroy(BlockChainStream* This) +{ + if (This) + { + BlockChainStream_Flush(This); + HeapFree(GetProcessHeap(), 0, This->indexCache); + } + HeapFree(GetProcessHeap(), 0, This); +} + +/****************************************************************************** + * 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) + { + /* + * Go to the new end of chain + */ + blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1); + + /* Mark the new end of chain */ + StorageImpl_SetNextBlockInChain( + This->parentStorage, + blockIndex, + BLOCK_END_OF_CHAIN); + + This->tailIndex = blockIndex; + } + else + { + if (This->headOfStreamPlaceHolder != 0) + { + *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN; + } + else + { + DirEntry chainEntry; + assert(This->ownerDirEntry != DIRENTRY_NULL); + + StorageImpl_ReadDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + chainEntry.startingBlock = BLOCK_END_OF_CHAIN; + + StorageImpl_WriteDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + } + + This->tailIndex = BLOCK_END_OF_CHAIN; + } + + This->numBlocks = numBlocks; + + /* + * Mark the extra blocks as free + */ + while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks) + { + struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1]; + StorageImpl_FreeBigBlock(This->parentStorage, + last_run->firstSector + last_run->lastOffset - last_run->firstOffset); + if (last_run->lastOffset == last_run->firstOffset) + This->indexCacheLen--; + else + last_run->lastOffset--; + } + + /* + * Reset the last accessed block cache. + */ + for (i=0; i<2; i++) + { + if (This->cachedBlocks[i].index >= numBlocks) + { + This->cachedBlocks[i].index = 0xffffffff; + This->cachedBlocks[i].dirty = FALSE; + } + } + + return TRUE; +} + +/****************************************************************************** + * BlockChainStream_Enlarge + * + * Grows this chain in the big block depot. + */ +static BOOL BlockChainStream_Enlarge(BlockChainStream* This, + ULARGE_INTEGER newSize) +{ + ULONG blockIndex, currentBlock; + ULONG newNumBlocks; + ULONG oldNumBlocks = 0; + + blockIndex = BlockChainStream_GetHeadOfChain(This); + + /* + * Empty chain. Create the head. + */ + if (blockIndex == BLOCK_END_OF_CHAIN) + { + blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, 1); + StorageImpl_SetNextBlockInChain(This->parentStorage, + blockIndex, + BLOCK_END_OF_CHAIN); + + if (This->headOfStreamPlaceHolder != 0) + { + *(This->headOfStreamPlaceHolder) = blockIndex; + } + else + { + DirEntry chainEntry; + assert(This->ownerDirEntry != DIRENTRY_NULL); + + StorageImpl_ReadDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + chainEntry.startingBlock = blockIndex; + + StorageImpl_WriteDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + } + + This->tailIndex = blockIndex; + This->numBlocks = 1; + } + + /* + * Figure out how many blocks are needed to contain this stream + */ + newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize; + + if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0) + newNumBlocks++; + + /* + * Go to the current end of chain + */ + if (This->tailIndex == BLOCK_END_OF_CHAIN) + { + currentBlock = blockIndex; + + while (blockIndex != BLOCK_END_OF_CHAIN) + { + This->numBlocks++; + currentBlock = blockIndex; + + if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock, + &blockIndex))) + return FALSE; + } + + This->tailIndex = currentBlock; + } + + currentBlock = This->tailIndex; + oldNumBlocks = This->numBlocks; + + /* + * Add new blocks to the chain + */ + if (oldNumBlocks < newNumBlocks) + { + while (oldNumBlocks < newNumBlocks) + { + blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, newNumBlocks - oldNumBlocks); + + StorageImpl_SetNextBlockInChain( + This->parentStorage, + currentBlock, + blockIndex); + + StorageImpl_SetNextBlockInChain( + This->parentStorage, + blockIndex, + BLOCK_END_OF_CHAIN); + + currentBlock = blockIndex; + oldNumBlocks++; + } + + This->tailIndex = blockIndex; + This->numBlocks = newNumBlocks; + } + + if (FAILED(BlockChainStream_UpdateIndexCache(This))) + return FALSE; + + return TRUE; +} + + +/****************************************************************************** + * BlockChainStream_GetSize + * + * Returns the size of this chain. + * Will return the block count if this chain doesn't have a directory entry. + */ +static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This) +{ + DirEntry chainEntry; + + if(This->headOfStreamPlaceHolder == NULL) + { + /* + * This chain has a directory entry so use the size value from there. + */ + StorageImpl_ReadDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + return chainEntry.size; + } + else + { + /* + * this chain is a chain that does not have a directory entry, figure out the + * size by making the product number of used blocks times the + * size of them + */ + ULARGE_INTEGER result; + result.QuadPart = + (ULONGLONG)BlockChainStream_GetCount(This) * + This->parentStorage->bigBlockSize; + + return result; + } +} + +/****************************************************************************** + * 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_ReadAt + * + * 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, %li, %p, %lu, %p.\n",This, offset.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, + ULONG* nextBlockInChain) +{ + ULARGE_INTEGER offsetOfBlockInDepot; + DWORD buffer; + ULONG bytesRead; + HRESULT res; + + *nextBlockInChain = BLOCK_END_OF_CHAIN; + + offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); + + /* + * Read those bytes in the buffer from the small block file. + */ + res = BlockChainStream_ReadAt( + This->parentStorage->smallBlockDepotChain, + offsetOfBlockInDepot, + sizeof(DWORD), + &buffer, + &bytesRead); + + if (SUCCEEDED(res) && bytesRead != sizeof(DWORD)) + res = STG_E_READFAULT; + + if (SUCCEEDED(res)) + { + StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain); + return S_OK; + } + + return res; +} + +/****************************************************************************** + * SmallBlockChainStream_SetNextBlockInChain + * + * Writes the index of the next block of the specified block in the small + * block depot. + * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock. + * To flag a block as free use BLOCK_UNUSED as nextBlock. + */ +static void SmallBlockChainStream_SetNextBlockInChain( + SmallBlockChainStream* This, + ULONG blockIndex, + ULONG nextBlock) +{ + ULARGE_INTEGER offsetOfBlockInDepot; + DWORD buffer; + ULONG bytesWritten; + + offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); + + StorageUtl_WriteDWord(&buffer, 0, nextBlock); + + /* + * Read those bytes in the buffer from the small block file. + */ + BlockChainStream_WriteAt( + This->parentStorage->smallBlockDepotChain, + offsetOfBlockInDepot, + sizeof(DWORD), + &buffer, + &bytesWritten); +} + +/****************************************************************************** + * SmallBlockChainStream_FreeBlock + * + * Flag small block 'blockIndex' as free in the small block depot. + */ +static void SmallBlockChainStream_FreeBlock( + SmallBlockChainStream* This, + ULONG blockIndex) +{ + SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); +} + +/****************************************************************************** + * SmallBlockChainStream_GetNextFreeBlock + * + * Returns the index of a free small block. The small block depot will be + * enlarged if necessary. The small block chain will also be enlarged if + * necessary. + */ +static ULONG SmallBlockChainStream_GetNextFreeBlock( + SmallBlockChainStream* This) +{ + ULARGE_INTEGER offsetOfBlockInDepot; + DWORD buffer; + ULONG bytesRead; + ULONG blockIndex = This->parentStorage->firstFreeSmallBlock; + ULONG nextBlockIndex = BLOCK_END_OF_CHAIN; + HRESULT res = S_OK; + ULONG smallBlocksPerBigBlock; + DirEntry rootEntry; + ULONG blocksRequired; + ULARGE_INTEGER old_size, size_required; + + offsetOfBlockInDepot.HighPart = 0; + + /* + * Scan the small block depot for a free block + */ + while (nextBlockIndex != BLOCK_UNUSED) + { + offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); + + res = BlockChainStream_ReadAt( + This->parentStorage->smallBlockDepotChain, + offsetOfBlockInDepot, + sizeof(DWORD), + &buffer, + &bytesRead); + + /* + * If we run out of space for the small block depot, enlarge it + */ + if (SUCCEEDED(res) && bytesRead == sizeof(DWORD)) + { + StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex); + + if (nextBlockIndex != BLOCK_UNUSED) + blockIndex++; + } + else + { + ULONG count = + BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain); + + BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE]; + ULARGE_INTEGER newSize, offset; + ULONG bytesWritten; + + newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize; + BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize); + + /* + * Initialize all the small blocks to free + */ + memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize); + offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize; + BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain, + offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten); + + StorageImpl_SaveFileHeader(This->parentStorage); + } + } + + This->parentStorage->firstFreeSmallBlock = blockIndex+1; + + smallBlocksPerBigBlock = + This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize; + + /* + * Verify if we have to allocate big blocks to contain small blocks + */ + blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; + + size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize; + + old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain); + + if (size_required.QuadPart > old_size.QuadPart) + { + BlockChainStream_SetSize( + This->parentStorage->smallBlockRootChain, + size_required); + + StorageImpl_ReadDirEntry( + This->parentStorage, + This->parentStorage->base.storageDirEntry, + &rootEntry); + + rootEntry.size = size_required; + + StorageImpl_WriteDirEntry( + This->parentStorage, + This->parentStorage->base.storageDirEntry, + &rootEntry); + } + + return blockIndex; +} + +/****************************************************************************** + * SmallBlockChainStream_ReadAt + * + * 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 SmallBlockChainStream_ReadAt( + SmallBlockChainStream* This, + ULARGE_INTEGER offset, + ULONG size, + void* buffer, + ULONG* bytesRead) +{ + HRESULT rc = S_OK; + ULARGE_INTEGER offsetInBigBlockFile; + ULONG blockNoInSequence = + offset.LowPart / This->parentStorage->smallBlockSize; + + ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize; + ULONG bytesToReadInBuffer; + ULONG blockIndex; + ULONG bytesReadFromBigBlockFile; + BYTE* bufferWalker; + ULARGE_INTEGER stream_size; + + /* + * This should never happen on a small block file. + */ + assert(offset.HighPart==0); + + *bytesRead = 0; + + stream_size = SmallBlockChainStream_GetSize(This); + if (stream_size.QuadPart > offset.QuadPart) + size = min(stream_size.QuadPart - offset.QuadPart, size); + else + return S_OK; + + /* + * Find the first block in the stream that contains part of the buffer. + */ + blockIndex = SmallBlockChainStream_GetHeadOfChain(This); + + while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) + { + rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); + if(FAILED(rc)) + return rc; + blockNoInSequence--; + } + + /* + * Start reading the buffer. + */ + bufferWalker = buffer; + + while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) + { + /* + * Calculate how many bytes we can copy from this small block. + */ + bytesToReadInBuffer = + min(This->parentStorage->smallBlockSize - offsetInBlock, size); + + /* + * Calculate the offset of the small block in the small block file. + */ + offsetInBigBlockFile.QuadPart = + (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; + + offsetInBigBlockFile.QuadPart += offsetInBlock; + + /* + * Read those bytes in the buffer from the small block file. + * The small block has already been identified so it shouldn't fail + * unless the file is corrupt. + */ + rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain, + offsetInBigBlockFile, + bytesToReadInBuffer, + bufferWalker, + &bytesReadFromBigBlockFile); + + if (FAILED(rc)) + return rc; + + if (!bytesReadFromBigBlockFile) + return STG_E_DOCFILECORRUPT; + + /* + * Step to the next big block. + */ + rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); + if(FAILED(rc)) + return STG_E_DOCFILECORRUPT; + + bufferWalker += bytesReadFromBigBlockFile; + size -= bytesReadFromBigBlockFile; + *bytesRead += bytesReadFromBigBlockFile; + offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize; + } + + return S_OK; +} + +/****************************************************************************** + * SmallBlockChainStream_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 SmallBlockChainStream_WriteAt( + SmallBlockChainStream* This, + ULARGE_INTEGER offset, + ULONG size, + const void* buffer, + ULONG* bytesWritten) +{ + ULARGE_INTEGER offsetInBigBlockFile; + ULONG blockNoInSequence = + offset.LowPart / This->parentStorage->smallBlockSize; + + ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize; + ULONG bytesToWriteInBuffer; + ULONG blockIndex; + ULONG bytesWrittenToBigBlockFile; + const BYTE* bufferWalker; + HRESULT res; + + /* + * This should never happen on a small block file. + */ + assert(offset.HighPart==0); + + /* + * Find the first block in the stream that contains part of the buffer. + */ + blockIndex = SmallBlockChainStream_GetHeadOfChain(This); + + while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) + { + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex))) + return STG_E_DOCFILECORRUPT; + blockNoInSequence--; + } + + /* + * Start writing the buffer. + */ + *bytesWritten = 0; + bufferWalker = buffer; + while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) + { + /* + * Calculate how many bytes we can copy to this small block. + */ + bytesToWriteInBuffer = + min(This->parentStorage->smallBlockSize - offsetInBlock, size); + + /* + * Calculate the offset of the small block in the small block file. + */ + offsetInBigBlockFile.QuadPart = + (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; + + offsetInBigBlockFile.QuadPart += offsetInBlock; + + /* + * Write those bytes in the buffer to the small block file. + */ + res = BlockChainStream_WriteAt( + This->parentStorage->smallBlockRootChain, + offsetInBigBlockFile, + bytesToWriteInBuffer, + bufferWalker, + &bytesWrittenToBigBlockFile); + if (FAILED(res)) + return res; + + /* + * Step to the next big block. + */ + res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); + if (FAILED(res)) + return res; + bufferWalker += bytesWrittenToBigBlockFile; + size -= bytesWrittenToBigBlockFile; + *bytesWritten += bytesWrittenToBigBlockFile; + offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize; + } + + return (size == 0) ? S_OK : STG_E_WRITEFAULT; +} + +/****************************************************************************** + * SmallBlockChainStream_Shrink + * + * Shrinks this chain in the small block depot. + */ +static BOOL SmallBlockChainStream_Shrink( + SmallBlockChainStream* This, + ULARGE_INTEGER newSize) +{ + ULONG blockIndex, extraBlock; + ULONG numBlocks; + ULONG count = 0; + + numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize; + + if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0) + numBlocks++; + + blockIndex = SmallBlockChainStream_GetHeadOfChain(This); + + /* + * Go to the new end of chain + */ + while (count < numBlocks) + { + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, + &blockIndex))) + return FALSE; + count++; + } + + /* + * If the count is 0, we have a special case, the head of the chain was + * just freed. + */ + if (count == 0) + { + DirEntry chainEntry; + + StorageImpl_ReadDirEntry(This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + chainEntry.startingBlock = BLOCK_END_OF_CHAIN; + + StorageImpl_WriteDirEntry(This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + /* + * We start freeing the chain at the head block. + */ + extraBlock = blockIndex; + } + else + { + /* Get the next block before marking the new end */ + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, + &extraBlock))) + return FALSE; + + /* Mark the new end of chain */ + SmallBlockChainStream_SetNextBlockInChain( + This, + blockIndex, + BLOCK_END_OF_CHAIN); + } + + /* + * Mark the extra blocks as free + */ + while (extraBlock != BLOCK_END_OF_CHAIN) + { + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock, + &blockIndex))) + return FALSE; + SmallBlockChainStream_FreeBlock(This, extraBlock); + This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock); + extraBlock = blockIndex; + } + + return TRUE; +} + +/****************************************************************************** + * SmallBlockChainStream_Enlarge + * + * Grows this chain in the small block depot. + */ +static BOOL SmallBlockChainStream_Enlarge( + SmallBlockChainStream* This, + ULARGE_INTEGER newSize) +{ + ULONG blockIndex, currentBlock; + ULONG newNumBlocks; + ULONG oldNumBlocks = 0; + + blockIndex = SmallBlockChainStream_GetHeadOfChain(This); + + /* + * Empty chain. Create the head. + */ + if (blockIndex == BLOCK_END_OF_CHAIN) + { + blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); + SmallBlockChainStream_SetNextBlockInChain( + This, + blockIndex, + BLOCK_END_OF_CHAIN); + + if (This->headOfStreamPlaceHolder != NULL) + { + *(This->headOfStreamPlaceHolder) = blockIndex; + } + else + { + DirEntry chainEntry; + + StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry, + &chainEntry); + + chainEntry.startingBlock = blockIndex; + + StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry, + &chainEntry); + } + } + + currentBlock = blockIndex; + + /* + * Figure out how many blocks are needed to contain this stream + */ + newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize; + + if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0) + newNumBlocks++; + + /* + * Go to the current end of chain + */ + while (blockIndex != BLOCK_END_OF_CHAIN) + { + oldNumBlocks++; + currentBlock = blockIndex; + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex))) + return FALSE; + } + + /* + * Add new blocks to the chain + */ + while (oldNumBlocks < newNumBlocks) + { + blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); + SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex); + + SmallBlockChainStream_SetNextBlockInChain( + This, + blockIndex, + BLOCK_END_OF_CHAIN); + + currentBlock = blockIndex; + oldNumBlocks++; + } + + return TRUE; +} + +/****************************************************************************** + * SmallBlockChainStream_SetSize + * + * Sets the size of this stream. + * 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 SmallBlockChainStream_SetSize( + SmallBlockChainStream* This, + ULARGE_INTEGER newSize) +{ + ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This); + + if (newSize.LowPart == size.LowPart) + return TRUE; + + if (newSize.LowPart < size.LowPart) + { + SmallBlockChainStream_Shrink(This, newSize); + } + else + { + SmallBlockChainStream_Enlarge(This, newSize); + } + + return TRUE; +} + +/****************************************************************************** + * SmallBlockChainStream_GetCount + * + * Returns the number of small blocks that comprises this chain. + * This is not the size of the stream as the last block may not be full! + * + */ +static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This) +{ + ULONG blockIndex; + ULONG count = 0; + + blockIndex = SmallBlockChainStream_GetHeadOfChain(This); + + while(blockIndex != BLOCK_END_OF_CHAIN) + { + count++; + + if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, + blockIndex, &blockIndex))) + return 0; + } + + return count; +} + +/****************************************************************************** + * SmallBlockChainStream_GetSize + * + * Returns the size of this chain. + */ +static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This) +{ + DirEntry chainEntry; + + if(This->headOfStreamPlaceHolder != NULL) + { + ULARGE_INTEGER result; + result.HighPart = 0; + + result.LowPart = SmallBlockChainStream_GetCount(This) * + This->parentStorage->smallBlockSize; + + return result; + } + + StorageImpl_ReadDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + return chainEntry.size; +} + + +/************************************************************************ + * Miscellaneous storage functions + ***********************************************************************/ + +static HRESULT create_storagefile( + LPCOLESTR pwcsName, + DWORD grfMode, + DWORD grfAttrs, + STGOPTIONS* pStgOptions, + REFIID riid, + void** ppstgOpen) +{ + StorageBaseImpl* newStorage = 0; + HANDLE hFile = INVALID_HANDLE_VALUE; + HRESULT hr = STG_E_INVALIDFLAG; + DWORD shareMode; + DWORD accessMode; + DWORD creationMode; + DWORD fileAttributes; + WCHAR tempFileName[MAX_PATH]; + + if (ppstgOpen == 0) + return STG_E_INVALIDPOINTER; + + if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE) + return STG_E_INVALIDPARAMETER; + + /* if no share mode given then DENY_NONE is the default */ + if (STGM_SHARE_MODE(grfMode) == 0) + grfMode |= STGM_SHARE_DENY_NONE; + + if ( FAILED( validateSTGM(grfMode) )) + goto end; + + /* StgCreateDocFile seems to refuse readonly access, despite MSDN */ + switch(STGM_ACCESS_MODE(grfMode)) + { + case STGM_WRITE: + case STGM_READWRITE: + break; + default: + goto end; + } + + /* in direct mode, can only use SHARE_EXCLUSIVE */ + if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)) + goto end; + + /* but in transacted mode, any share mode is valid */ + + /* + * Generate a unique name. + */ + if (pwcsName == 0) + { + WCHAR tempPath[MAX_PATH]; + + memset(tempPath, 0, sizeof(tempPath)); + memset(tempFileName, 0, sizeof(tempFileName)); + + if ((GetTempPathW(MAX_PATH, tempPath)) == 0 ) + tempPath[0] = '.'; + + if (GetTempFileNameW(tempPath, L"STO", 0, tempFileName) != 0) + pwcsName = tempFileName; + else + { + hr = STG_E_INSUFFICIENTMEMORY; + goto end; + } + + creationMode = TRUNCATE_EXISTING; + } + else + { + creationMode = GetCreationModeFromSTGM(grfMode); + } + + /* + * Interpret the STGM value grfMode + */ + shareMode = GetShareModeFromSTGM(grfMode); + accessMode = GetAccessModeFromSTGM(grfMode); + + if (grfMode & STGM_DELETEONRELEASE) + fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE; + else + fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + + *ppstgOpen = 0; + + hFile = CreateFileW(pwcsName, + accessMode, + shareMode, + NULL, + creationMode, + fileAttributes, + 0); + + if (hFile == INVALID_HANDLE_VALUE) + { + if(GetLastError() == ERROR_FILE_EXISTS) + hr = STG_E_FILEALREADYEXISTS; + else + hr = E_FAIL; + goto end; + } + + /* + * Allocate and initialize the new IStorage object. + */ + hr = Storage_Construct( + hFile, + pwcsName, + NULL, + grfMode, + TRUE, + TRUE, + pStgOptions->ulSectorSize, + &newStorage); + + if (FAILED(hr)) + { + goto end; + } + + hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen); + IStorage_Release(&newStorage->IStorage_iface); + +end: + TRACE("<-- %p r = %#lx\n", *ppstgOpen, hr); + + return hr; +} + +/****************************************************************************** + * StgCreateDocfile [OLE32.@] + * Creates a new compound file storage object + * + * PARAMS + * pwcsName [ I] Unicode string with filename (can be relative or NULL) + * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants) + * reserved [ ?] unused?, usually 0 + * ppstgOpen [IO] A pointer to IStorage pointer to the new object + * + * RETURNS + * S_OK if the file was successfully created + * some STG_E_ value if error + * NOTES + * if pwcsName is NULL, create file with new unique name + * the function can returns + * STG_S_CONVERTED if the specified file was successfully converted to storage format + * (unrealized now) + */ +HRESULT WINAPI StgCreateDocfile( + LPCOLESTR pwcsName, + DWORD grfMode, + DWORD reserved, + IStorage **ppstgOpen) +{ + STGOPTIONS stgoptions = {1, 0, 512}; + + TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName), grfMode, reserved, ppstgOpen); + + if (ppstgOpen == 0) + return STG_E_INVALIDPOINTER; + if (reserved != 0) + return STG_E_INVALIDPARAMETER; + + return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen); +} + +/****************************************************************************** + * StgCreateStorageEx [OLE32.@] + */ +HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) +{ + TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName), + grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); + + if (stgfmt != STGFMT_FILE && grfAttrs != 0) + { + ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n"); + 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; + } + + if (stgfmt == STGFMT_FILE) + { + ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); + return STG_E_INVALIDPARAMETER; + } + + if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE) + { + STGOPTIONS defaultOptions = {1, 0, 512}; + + if (!pStgOptions) pStgOptions = &defaultOptions; + return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen); + } + + + ERR("Invalid stgfmt argument\n"); + return STG_E_INVALIDPARAMETER; +} + +/****************************************************************************** + * StgCreatePropSetStg [OLE32.@] + */ +HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved, + IPropertySetStorage **propset) +{ + TRACE("%p, %#lx, %p.\n", pstg, reserved, propset); + if (reserved) + return STG_E_INVALIDPARAMETER; + + return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset); +} + +/****************************************************************************** + * StgOpenStorageEx [OLE32.@] + */ +HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) +{ + TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName), + grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); + + if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0) + { + ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n"); + return STG_E_INVALIDPARAMETER; + } + + switch (stgfmt) + { + case STGFMT_FILE: + ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); + return STG_E_INVALIDPARAMETER; + + case STGFMT_STORAGE: + break; + + case STGFMT_DOCFILE: + 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; + } + FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n"); + break; + + case STGFMT_ANY: + WARN("STGFMT_ANY assuming storage\n"); + break; + + default: + return STG_E_INVALIDPARAMETER; + } + + return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen); +} + + +/****************************************************************************** + * StgOpenStorage [OLE32.@] + */ +HRESULT WINAPI StgOpenStorage( + const OLECHAR *pwcsName, + IStorage *pstgPriority, + DWORD grfMode, + SNB snbExclude, + DWORD reserved, + IStorage **ppstgOpen) +{ + StorageBaseImpl* newStorage = 0; + HRESULT hr = S_OK; + HANDLE hFile = 0; + DWORD shareMode; + DWORD accessMode; + LPWSTR temp_name = NULL; + + TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName), pstgPriority, grfMode, + snbExclude, reserved, ppstgOpen); + + if (pstgPriority) + { + /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */ + hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name); + if (FAILED(hr)) goto end; + pwcsName = temp_name; + TRACE("using filename %s\n", debugstr_w(temp_name)); + } + + if (pwcsName == 0) + { + hr = STG_E_INVALIDNAME; + goto end; + } + + if (ppstgOpen == 0) + { + hr = STG_E_INVALIDPOINTER; + goto end; + } + + if (reserved) + { + hr = STG_E_INVALIDPARAMETER; + goto end; + } + + if (grfMode & STGM_PRIORITY) + { + if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT)) + return STG_E_INVALIDFLAG; + if (grfMode & STGM_DELETEONRELEASE) + return STG_E_INVALIDFUNCTION; + if(STGM_ACCESS_MODE(grfMode) != STGM_READ) + return STG_E_INVALIDFLAG; + grfMode &= ~0xf0; /* remove the existing sharing mode */ + grfMode |= STGM_SHARE_DENY_NONE; + } + + /* + * Validate the sharing mode + */ + if (grfMode & STGM_DIRECT_SWMR) + { + if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) && + (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE)) + { + hr = STG_E_INVALIDFLAG; + goto end; + } + } + else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY))) + switch(STGM_SHARE_MODE(grfMode)) + { + case STGM_SHARE_EXCLUSIVE: + case STGM_SHARE_DENY_WRITE: + break; + default: + hr = STG_E_INVALIDFLAG; + goto end; + } + + if ( FAILED( validateSTGM(grfMode) ) || + (grfMode&STGM_CREATE)) + { + hr = STG_E_INVALIDFLAG; + goto end; + } + + /* shared reading requires transacted or single writer mode */ + if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE && + STGM_ACCESS_MODE(grfMode) == STGM_READWRITE && + !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR)) + { + hr = STG_E_INVALIDFLAG; + goto end; + } + + /* + * Interpret the STGM value grfMode + */ + shareMode = GetShareModeFromSTGM(grfMode); + accessMode = GetAccessModeFromSTGM(grfMode); + + *ppstgOpen = 0; + + hFile = CreateFileW( pwcsName, + accessMode, + shareMode, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + 0); + + if (hFile==INVALID_HANDLE_VALUE) + { + DWORD last_error = GetLastError(); + + hr = E_FAIL; + + switch (last_error) + { + case ERROR_FILE_NOT_FOUND: + hr = STG_E_FILENOTFOUND; + break; + + case ERROR_PATH_NOT_FOUND: + hr = STG_E_PATHNOTFOUND; + break; + + case ERROR_ACCESS_DENIED: + case ERROR_WRITE_PROTECT: + hr = STG_E_ACCESSDENIED; + break; + + case ERROR_SHARING_VIOLATION: + hr = STG_E_SHAREVIOLATION; + break; + + default: + hr = E_FAIL; + } + + goto end; + } + + /* + * Refuse to open the file if it's too small to be a structured storage file + * FIXME: verify the file when reading instead of here + */ + if (GetFileSize(hFile, NULL) < HEADER_SIZE) + { + CloseHandle(hFile); + hr = STG_E_FILEALREADYEXISTS; + goto end; + } + + /* + * Allocate and initialize the new IStorage object. + */ + hr = Storage_Construct( + hFile, + pwcsName, + NULL, + grfMode, + TRUE, + FALSE, + 512, + &newStorage); + + if (FAILED(hr)) + { + /* + * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS + */ + if(hr == STG_E_INVALIDHEADER) + hr = STG_E_FILEALREADYEXISTS; + goto end; + } + + *ppstgOpen = &newStorage->IStorage_iface; + +end: + CoTaskMemFree(temp_name); + if (pstgPriority) IStorage_Release(pstgPriority); + TRACE("<-- %#lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL); + return hr; +} + +/****************************************************************************** + * StgCreateDocfileOnILockBytes [OLE32.@] + */ +HRESULT WINAPI StgCreateDocfileOnILockBytes( + ILockBytes *plkbyt, + DWORD grfMode, + DWORD reserved, + IStorage** ppstgOpen) +{ + StorageBaseImpl* newStorage = 0; + HRESULT hr = S_OK; + + if ((ppstgOpen == 0) || (plkbyt == 0)) + return STG_E_INVALIDPOINTER; + + /* + * Allocate and initialize the new IStorage object. + */ + hr = Storage_Construct( + 0, + 0, + plkbyt, + grfMode, + FALSE, + TRUE, + 512, + &newStorage); + + if (FAILED(hr)) + { + return hr; + } + + *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.HighPart = 0; + offset.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 DWORD version_magic = 0x02000001; + IStream *stream; + HRESULT hr; + + hr = IStorage_CreateStream(storage, L"\1Ole", 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("%ld bytes\n",len); + + str = CoTaskMemAlloc( len ); + if( !str ) + return E_OUTOFMEMORY; + count = 0; + r = IStream_Read( stm, str, len, &count ); + if( FAILED( r ) ) + { + CoTaskMemFree( str ); + 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 r; +} + + +static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, + LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) +{ + IStream *pstm; + HRESULT r = S_OK; + + 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, L"\1CompObj", + 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 ) + { + n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName)); + 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; + + ProgIDFromCLSID(&clsid, &wstrProgID); + + TRACE("progid is %s\n",debugstr_w(wstrProgID)); + + r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType, + cf ? szwClipName : NULL, wstrProgID ); + + CoTaskMemFree(wstrProgID); + + return r; +} + + +/****************************************************************************** + * ReadFmtUserTypeStg [OLE32.@] + */ +HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) +{ + HRESULT r; + IStream *stm = 0; + unsigned char unknown1[12]; + unsigned char unknown2[16]; + DWORD count; + LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; + CLSID clsid; + + TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); + + r = IStorage_OpenStream( pstg, L"\1CompObj", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); + if( FAILED ( r ) ) + { + WARN("Failed to open stream r = %#lx\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; + + r = STREAM_ReadString( stm, &szCLSIDName ); + if( FAILED( r ) ) + goto end; + + r = STREAM_ReadString( stm, &szOleTypeName ); + if( FAILED( r ) ) + goto end; + + r = STREAM_ReadString( stm, &szProgIDName ); + if( FAILED( r ) ) + goto end; + + r = IStream_Read( stm, unknown2, sizeof(unknown2), &count ); + if( FAILED( r ) || ( count != sizeof(unknown2) ) ) + goto end; + + /* ok, success... now we just need to store what we found */ + if( pcf ) + *pcf = RegisterClipboardFormatW( szOleTypeName ); + + if( lplpszUserType ) + { + *lplpszUserType = szCLSIDName; + szCLSIDName = NULL; + } + +end: + CoTaskMemFree( szCLSIDName ); + CoTaskMemFree( szOleTypeName ); + CoTaskMemFree( szProgIDName ); + IStream_Release( stm ); + + return r; +} + +/****************************************************************************** + * 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; +} + + +/************************************************************************ + * OleConvert Functions + ***********************************************************************/ + +#define OLESTREAM_ID 0x501 +#define OLESTREAM_MAX_STR_LEN 255 + +/* OLESTREAM memory structure to use for Get and Put Routines */ +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 */ +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; + +/* Ole Presentation Stream structure */ +typedef struct +{ + BYTE byUnknown1[28]; + DWORD dwExtentX; + DWORD dwExtentY; + DWORD dwSize; + BYTE *pData; +} OLECONVERT_ISTORAGE_OLEPRES; + + +/************************************************************************* + * OLECONVERT_LoadOLE10 [Internal] + * + * Loads the OLE10 STREAM to memory + * + * PARAMS + * pOleStream [I] The OLESTREAM + * pData [I] Data Structure for the OLESTREAM Data + * + * RETURNS + * Success: S_OK + * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get + * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid + * + * NOTES + * This function is used by OleConvertOLESTREAMToIStorage only. + * + * Memory allocated for pData must be freed by the caller + */ +static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStream1) +{ + DWORD dwSize; + HRESULT hRes = S_OK; + int nTryCnt=0; + int max_try = 6; + + pData->pData = NULL; + pData->pstrOleObjFileName = NULL; + + for( nTryCnt=0;nTryCnt < max_try; nTryCnt++) + { + /* Get the OleID */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); + if(dwSize != sizeof(pData->dwOleID)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + else if(pData->dwOleID != OLESTREAM_ID) + { + hRes = CONVERT10_E_OLESTREAM_FMT; + } + else + { + hRes = S_OK; + break; + } + } + + if(hRes == S_OK) + { + /* Get the TypeID... more info needed for this field */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); + if(dwSize != sizeof(pData->dwTypeID)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + if(hRes == S_OK) + { + if(pData->dwTypeID != 0) + { + /* Get the length of the OleTypeName */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); + if(dwSize != sizeof(pData->dwOleTypeNameLength)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + + if(hRes == S_OK) + { + if(pData->dwOleTypeNameLength > 0) + { + /* Get the OleTypeName */ + dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); + if(dwSize != pData->dwOleTypeNameLength) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + } + if(bStream1) + { + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength)); + if(dwSize != sizeof(pData->dwOleObjFileNameLength)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + if(hRes == S_OK) + { + if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */ + pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength); + pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength); + if(pData->pstrOleObjFileName) + { + dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength); + if(dwSize != pData->dwOleObjFileNameLength) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + else + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + else + { + /* Get the Width of the Metafile */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); + if(dwSize != sizeof(pData->dwMetaFileWidth)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + if(hRes == S_OK) + { + /* Get the Height of the Metafile */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); + if(dwSize != sizeof(pData->dwMetaFileHeight)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + } + if(hRes == S_OK) + { + /* Get the Length of the Data */ + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); + if(dwSize != sizeof(pData->dwDataLength)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + + if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */ + { + if(!bStream1) /* if it is a second OLE stream data */ + { + pData->dwDataLength -= 8; + dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown)); + if(dwSize != sizeof(pData->strUnknown)) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + } + if(hRes == S_OK) + { + if(pData->dwDataLength > 0) + { + pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength); + + /* Get Data (ex. IStorage, Metafile, or BMP) */ + if(pData->pData) + { + dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength); + if(dwSize != pData->dwDataLength) + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + else + { + hRes = CONVERT10_E_OLESTREAM_GET; + } + } + } + } + } + return hRes; +} + +/************************************************************************* + * 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]; + + /* Create a temp File */ + GetTempPathW(MAX_PATH, wstrTempDir); + GetTempFileNameW(wstrTempDir, L"sis", 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]; + + *pData = NULL; + + /* Create temp Storage */ + GetTempPathW(MAX_PATH, wstrTempDir); + GetTempFileNameW(wstrTempDir, L"sis", 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; +} + +/************************************************************************* + * OLECONVERT_CreateCompObjStream [Internal] + * + * Creates a "\001CompObj" is the destination IStorage if necessary. + * + * PARAMS + * pStorage [I] The dest IStorage to create the CompObj Stream + * if necessary. + * strOleTypeName [I] The ProgID + * + * RETURNS + * Success: S_OK + * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream + * + * NOTES + * This function is used by OleConvertOLESTREAMToIStorage only. + * + * The stream data is stored in the OLESTREAM and there should be + * no need to recreate the stream. If the stream is manually + * deleted it will attempt to create it by querying the registry. + * + * + */ +HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName) +{ + IStream *pStream; + HRESULT hStorageRes, hRes = S_OK; + OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj; + WCHAR bufferW[OLESTREAM_MAX_STR_LEN]; + + static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; + static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71}; + + /* Initialize the CompObj structure */ + memset(&IStorageCompObj, 0, sizeof(IStorageCompObj)); + memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1)); + memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2)); + + + /* Create a CompObj stream if it doesn't exist */ + hStorageRes = IStorage_CreateStream(pStorage, L"\1CompObj", + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); + if(hStorageRes == S_OK) + { + /* copy the OleTypeName to the compobj struct */ + IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1; + strcpy(IStorageCompObj.strOleTypeName, strOleTypeName); + + /* copy the OleTypeName to the compobj struct */ + /* Note: in the test made, these were Identical */ + IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1; + strcpy(IStorageCompObj.strProgIDName, strOleTypeName); + + /* Get the CLSID */ + MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1, + bufferW, OLESTREAM_MAX_STR_LEN ); + hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid)); + + if(hRes == S_OK) + { + HKEY hKey; + LONG hErr; + /* Get the CLSID Default Name from the Registry */ + hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey); + if(hErr == ERROR_SUCCESS) + { + char strTemp[OLESTREAM_MAX_STR_LEN]; + IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN; + hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength)); + if(hErr == ERROR_SUCCESS) + { + strcpy(IStorageCompObj.strCLSIDName, strTemp); + } + RegCloseKey(hKey); + } + } + + /* Write CompObj Structure to stream */ + hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL); + + WriteClassStm(pStream,&(IStorageCompObj.clsid)); + + hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL); + if(IStorageCompObj.dwCLSIDNameLength > 0) + { + hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL); + } + hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL); + if(IStorageCompObj.dwOleTypeNameLength > 0) + { + hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL); + } + hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL); + if(IStorageCompObj.dwProgIDNameLength > 0) + { + hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL); + } + hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL); + IStream_Release(pStream); + } + return hRes; +} + + +/************************************************************************* + * OLECONVERT_CreateOlePresStream[Internal] + * + * Creates the "\002OlePres000" Stream with the Metafile data + * + * PARAMS + * pStorage [I] The dest IStorage to create \002OLEPres000 stream in. + * dwExtentX [I] Width of the Metafile + * dwExtentY [I] Height of the Metafile + * pData [I] Metafile data + * dwDataLength [I] Size of the Metafile data + * + * RETURNS + * Success: S_OK + * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put + * + * NOTES + * This function is used by OleConvertOLESTREAMToIStorage only. + * + */ +static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength) +{ + HRESULT hRes; + IStream *pStream; + static const BYTE pOlePresStreamHeader[] = + { + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + static const BYTE pOlePresStreamHeaderEmpty[] = + { + 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + /* Create the OlePres000 Stream */ + hRes = IStorage_CreateStream(pStorage, L"\2OlePres000", + STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); + + if(hRes == S_OK) + { + DWORD nHeaderSize; + OLECONVERT_ISTORAGE_OLEPRES OlePres; + + memset(&OlePres, 0, sizeof(OlePres)); + /* Do we have any metafile data to save */ + if(dwDataLength > 0) + { + memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader)); + nHeaderSize = sizeof(pOlePresStreamHeader); + } + else + { + memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty)); + nHeaderSize = sizeof(pOlePresStreamHeaderEmpty); + } + /* Set width and height of the metafile */ + OlePres.dwExtentX = dwExtentX; + OlePres.dwExtentY = -dwExtentY; + + /* Set Data and Length */ + if(dwDataLength > sizeof(METAFILEPICT16)) + { + OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16); + OlePres.pData = &(pData[8]); + } + /* Save OlePres000 Data to Stream */ + hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL); + hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL); + hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL); + hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL); + if(OlePres.dwSize > 0) + { + hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL); + } + IStream_Release(pStream); + } +} + +/************************************************************************* + * OLECONVERT_CreateOle10NativeStream [Internal] + * + * Creates the "\001Ole10Native" Stream (should contain a BMP) + * + * PARAMS + * pStorage [I] Dest storage to create the stream in + * pData [I] Ole10 Native Data (ex. bmp) + * dwDataLength [I] Size of the Ole10 Native Data + * + * RETURNS + * Nothing + * + * NOTES + * This function is used by OleConvertOLESTREAMToIStorage only. + * + * Might need to verify the data and return appropriate error message + * + */ +static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength) +{ + HRESULT hRes; + IStream *pStream; + + /* Create the Ole10Native Stream */ + hRes = IStorage_CreateStream(pStorage, L"\1Ole10Native", + STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); + + if(hRes == S_OK) + { + /* Write info to stream */ + hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL); + hRes = IStream_Write(pStream, pData, dwDataLength, NULL); + IStream_Release(pStream); + } + +} + +/************************************************************************* + * OLECONVERT_GetOLE10ProgID [Internal] + * + * Finds the ProgID (or OleTypeID) from the IStorage + * + * PARAMS + * pStorage [I] The Src IStorage to get the ProgID + * strProgID [I] the ProgID string to get + * dwSize [I] the size of the string + * + * RETURNS + * Success: S_OK + * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream + * + * NOTES + * This function is used by OleConvertIStorageToOLESTREAM only. + * + * + */ +static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize) +{ + HRESULT hRes; + IStream *pStream; + LARGE_INTEGER iSeekPos; + OLECONVERT_ISTORAGE_COMPOBJ CompObj; + + /* Open the CompObj Stream */ + hRes = IStorage_OpenStream(pStorage, L"\1CompObj", NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); + if(hRes == S_OK) + { + + /*Get the OleType from the CompObj Stream */ + iSeekPos.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid); + iSeekPos.HighPart = 0; + + IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); + IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL); + iSeekPos.LowPart = CompObj.dwCLSIDNameLength; + IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); + IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL); + iSeekPos.LowPart = CompObj.dwOleTypeNameLength; + IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); + + IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL); + if(*dwSize > 0) + { + IStream_Read(pStream, strProgID, *dwSize, NULL); + } + IStream_Release(pStream); + } + else + { + STATSTG stat; + LPOLESTR wstrProgID; + + /* Get the OleType from the registry */ + REFCLSID clsid = &(stat.clsid); + IStorage_Stat(pStorage, &stat, STATFLAG_NONAME); + hRes = ProgIDFromCLSID(clsid, &wstrProgID); + if(hRes == S_OK) + { + *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE); + CoTaskMemFree(wstrProgID); + } + + } + return hRes; +} + +/************************************************************************* + * OLECONVERT_GetOle10PresData [Internal] + * + * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream + * + * PARAMS + * pStorage [I] Src IStroage + * pOleStream [I] Dest OleStream Mem Struct + * + * RETURNS + * Nothing + * + * NOTES + * This function is used by OleConvertIStorageToOLESTREAM only. + * + * Memory allocated for pData must be freed by the caller + * + * + */ +static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) +{ + + HRESULT hRes; + IStream *pStream; + + /* Initialize Default data for OLESTREAM */ + pOleStreamData[0].dwOleID = OLESTREAM_ID; + pOleStreamData[0].dwTypeID = 2; + pOleStreamData[1].dwOleID = OLESTREAM_ID; + pOleStreamData[1].dwTypeID = 0; + pOleStreamData[0].dwMetaFileWidth = 0; + pOleStreamData[0].dwMetaFileHeight = 0; + pOleStreamData[0].pData = NULL; + pOleStreamData[1].pData = NULL; + + /* Open Ole10Native Stream */ + hRes = IStorage_OpenStream(pStorage, L"\1Ole10Native", NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); + if(hRes == S_OK) + { + + /* Read Size and Data */ + IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL); + if(pOleStreamData->dwDataLength > 0) + { + pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength); + IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL); + } + IStream_Release(pStream); + } + +} + + +/************************************************************************* + * OLECONVERT_GetOle20PresData[Internal] + * + * Converts IStorage "/002OlePres000" stream to a OLE10 Stream + * + * PARAMS + * pStorage [I] Src IStroage + * pOleStreamData [I] Dest OleStream Mem Struct + * + * RETURNS + * Nothing + * + * NOTES + * This function is used by OleConvertIStorageToOLESTREAM only. + * + * Memory allocated for pData must be freed by the caller + */ +static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) +{ + HRESULT hRes; + IStream *pStream; + OLECONVERT_ISTORAGE_OLEPRES olePress; + + /* Initialize Default data for OLESTREAM */ + pOleStreamData[0].dwOleID = OLESTREAM_ID; + pOleStreamData[0].dwTypeID = 2; + pOleStreamData[0].dwMetaFileWidth = 0; + pOleStreamData[0].dwMetaFileHeight = 0; + pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData)); + pOleStreamData[1].dwOleID = OLESTREAM_ID; + pOleStreamData[1].dwTypeID = 0; + pOleStreamData[1].dwOleTypeNameLength = 0; + pOleStreamData[1].strOleTypeName[0] = 0; + pOleStreamData[1].dwMetaFileWidth = 0; + pOleStreamData[1].dwMetaFileHeight = 0; + pOleStreamData[1].pData = NULL; + pOleStreamData[1].dwDataLength = 0; + + + /* Open OlePress000 stream */ + hRes = IStorage_OpenStream(pStorage, L"\2OlePres000", NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); + if(hRes == S_OK) + { + LARGE_INTEGER iSeekPos; + METAFILEPICT16 MetaFilePict; + static const char strMetafilePictName[] = "METAFILEPICT"; + + /* Set the TypeID for a Metafile */ + pOleStreamData[1].dwTypeID = 5; + + /* Set the OleTypeName to Metafile */ + pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1; + strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName); + + iSeekPos.HighPart = 0; + iSeekPos.LowPart = sizeof(olePress.byUnknown1); + + /* Get Presentation Data */ + IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); + IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL); + IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL); + IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL); + + /*Set width and Height */ + pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX; + pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY; + if(olePress.dwSize > 0) + { + /* Set Length */ + pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16); + + /* Set MetaFilePict struct */ + MetaFilePict.mm = 8; + MetaFilePict.xExt = olePress.dwExtentX; + MetaFilePict.yExt = olePress.dwExtentY; + MetaFilePict.hMF = 0; + + /* Get Metafile Data */ + pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength); + memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict)); + IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL); + } + IStream_Release(pStream); + } +} + +/************************************************************************* + * OleConvertOLESTREAMToIStorage [OLE32.@] + * + * Read info on MSDN + * + * TODO + * DVTARGETDEVICE parameter is not handled + * Still unsure of some mem fields for OLE 10 Stream + * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", + * and "\001OLE" streams + * + */ +HRESULT WINAPI OleConvertOLESTREAMToIStorage ( + LPOLESTREAM pOleStream, + LPSTORAGE pstg, + const DVTARGETDEVICE* ptd) +{ + int i; + HRESULT hRes=S_OK; + OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; + + TRACE("%p %p %p\n", pOleStream, pstg, ptd); + + memset(pOleStreamData, 0, sizeof(pOleStreamData)); + + if(ptd != NULL) + { + FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n"); + } + + if(pstg == NULL || pOleStream == NULL) + { + hRes = E_INVALIDARG; + } + + if(hRes == S_OK) + { + /* Load the OLESTREAM to Memory */ + hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE); + } + + if(hRes == S_OK) + { + /* Load the OLESTREAM to Memory (part 2)*/ + hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE); + } + + if(hRes == S_OK) + { + + if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic)) + { + /* Do we have the IStorage Data in the OLESTREAM */ + if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0) + { + OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); + OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength); + } + else + { + /* It must be an original OLE 1.0 source */ + OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); + } + } + else + { + /* It must be an original OLE 1.0 source */ + OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); + } + + /* Create CompObj Stream if necessary */ + hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName); + if(hRes == S_OK) + { + /*Create the Ole Stream if necessary */ + STORAGE_CreateOleStream(pstg, 0); + } + } + + + /* Free allocated memory */ + for(i=0; i < 2; i++) + { + HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); + HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName); + pOleStreamData[i].pstrOleObjFileName = NULL; + } + return hRes; +} + +/************************************************************************* + * OleConvertIStorageToOLESTREAM [OLE32.@] + * + * Read info on MSDN + * + * Read info on MSDN + * + * TODO + * Still unsure of some mem fields for OLE 10 Stream + * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", + * and "\001OLE" streams. + * + */ +HRESULT WINAPI OleConvertIStorageToOLESTREAM ( + LPSTORAGE pstg, + LPOLESTREAM pOleStream) +{ + int i; + HRESULT hRes = S_OK; + IStream *pStream; + OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; + + TRACE("%p %p\n", pstg, pOleStream); + + memset(pOleStreamData, 0, sizeof(pOleStreamData)); + + if(pstg == NULL || pOleStream == NULL) + { + hRes = E_INVALIDARG; + } + if(hRes == S_OK) + { + /* Get the ProgID */ + pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN; + hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength)); + } + if(hRes == S_OK) + { + /* Was it originally Ole10 */ + hRes = IStorage_OpenStream(pstg, L"\1Ole10Native", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream); + if(hRes == S_OK) + { + IStream_Release(pStream); + /* Get Presentation Data for Ole10Native */ + OLECONVERT_GetOle10PresData(pstg, pOleStreamData); + } + else + { + /* Get Presentation Data (OLE20) */ + OLECONVERT_GetOle20PresData(pstg, pOleStreamData); + } + + /* Save OLESTREAM */ + hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream); + if(hRes == S_OK) + { + hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream); + } + + } + + /* Free allocated memory */ + for(i=0; i < 2; i++) + { + HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); + } + + return hRes; +} + +enum stream_1ole_flags { + OleStream_LinkedObject = 0x00000001, + OleStream_Convert = 0x00000004 +}; + +/************************************************************************* + * OleConvertIStorageToOLESTREAMEx [OLE32.@] + */ +HRESULT WINAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg, CLIPFORMAT cf, LONG width, LONG height, + DWORD size, LPSTGMEDIUM medium, LPOLESTREAM olestream ) +{ + FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg, cf, width, height, size, medium, olestream); + + return E_NOTIMPL; +} + +/*********************************************************************** + * GetConvertStg (OLE32.@) + */ +HRESULT WINAPI GetConvertStg(IStorage *stg) +{ + static const DWORD version_magic = 0x02000001; + DWORD header[2]; + IStream *stream; + HRESULT hr; + + TRACE("%p\n", stg); + + if (!stg) return E_INVALIDARG; + + hr = IStorage_OpenStream(stg, L"\1Ole", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream); + if (FAILED(hr)) return hr; + + hr = IStream_Read(stream, header, sizeof(header), NULL); + IStream_Release(stream); + if (FAILED(hr)) return hr; + + if (header[0] != version_magic) + { + ERR("got wrong version magic for 1Ole stream, %#lx.\n", header[0]); + return E_FAIL; + } + + return header[1] & OleStream_Convert ? S_OK : S_FALSE; +} + +/*********************************************************************** + * SetConvertStg (OLE32.@) + */ +HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert) +{ + DWORD flags = convert ? OleStream_Convert : 0; + IStream *stream; + DWORD header[2]; + HRESULT hr; + + TRACE("(%p, %d)\n", storage, convert); + + hr = IStorage_OpenStream(storage, L"\1Ole", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream); + if (FAILED(hr)) + { + if (hr != STG_E_FILENOTFOUND) + return hr; + + return STORAGE_CreateOleStream(storage, flags); + } + + hr = IStream_Read(stream, header, sizeof(header), NULL); + if (FAILED(hr)) + { + IStream_Release(stream); + return hr; + } + + /* update flag if differs */ + if ((header[1] ^ flags) & OleStream_Convert) + { + LARGE_INTEGER pos = {{0}}; + + if (header[1] & OleStream_Convert) + flags = header[1] & ~OleStream_Convert; + else + flags = header[1] | OleStream_Convert; + + pos.QuadPart = sizeof(DWORD); + hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + { + IStream_Release(stream); + return hr; + } + + hr = IStream_Write(stream, &flags, sizeof(flags), NULL); + } + + IStream_Release(stream); + return hr; +} diff --git a/dlls/coml2/storage32.h b/dlls/coml2/storage32.h new file mode 100644 index 00000000000..dd4b89aceec --- /dev/null +++ b/dlls/coml2/storage32.h @@ -0,0 +1,571 @@ +/* + * Compound Storage (32 bit version) + * + * Implemented using the documentation of the LAOLA project at + * URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html + * (Thanks to Martin Schwartz schwartz@cs.tu-berlin.de) + * + * This include file contains definitions of types and function + * prototypes that are used in the many files implementing the + * storage functionality + * + * Copyright 1998,1999 Francis Beaudet + * Copyright 1998,1999 Thuy Nguyen + * Copyright 2010 Vincent Povirk for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef __STORAGE32_H__ +#define __STORAGE32_H__ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "objbase.h" +#include "winreg.h" +#include "winternl.h" +#include "wine/list.h" + +/* + * Definitions for the file format offsets. + */ +static const ULONG OFFSET_MINORVERSION = 0x00000018; +static const ULONG OFFSET_MAJORVERSION = 0x0000001a; +static const ULONG OFFSET_BYTEORDERMARKER = 0x0000001c; +static const ULONG OFFSET_BIGBLOCKSIZEBITS = 0x0000001e; +static const ULONG OFFSET_SMALLBLOCKSIZEBITS = 0x00000020; +static const ULONG OFFSET_DIRSECTORCOUNT = 0x00000028; +static const ULONG OFFSET_BBDEPOTCOUNT = 0x0000002C; +static const ULONG OFFSET_ROOTSTARTBLOCK = 0x00000030; +static const ULONG OFFSET_TRANSACTIONSIG = 0x00000034; +static const ULONG OFFSET_SMALLBLOCKLIMIT = 0x00000038; +static const ULONG OFFSET_SBDEPOTSTART = 0x0000003C; +static const ULONG OFFSET_SBDEPOTCOUNT = 0x00000040; +static const ULONG OFFSET_EXTBBDEPOTSTART = 0x00000044; +static const ULONG OFFSET_EXTBBDEPOTCOUNT = 0x00000048; +static const ULONG OFFSET_BBDEPOTSTART = 0x0000004C; +static const ULONG OFFSET_PS_NAME = 0x00000000; +static const ULONG OFFSET_PS_NAMELENGTH = 0x00000040; +static const ULONG OFFSET_PS_STGTYPE = 0x00000042; +static const ULONG OFFSET_PS_LEFTCHILD = 0x00000044; +static const ULONG OFFSET_PS_RIGHTCHILD = 0x00000048; +static const ULONG OFFSET_PS_DIRROOT = 0x0000004C; +static const ULONG OFFSET_PS_GUID = 0x00000050; +static const ULONG OFFSET_PS_CTIMELOW = 0x00000064; +static const ULONG OFFSET_PS_CTIMEHIGH = 0x00000068; +static const ULONG OFFSET_PS_MTIMELOW = 0x0000006C; +static const ULONG OFFSET_PS_MTIMEHIGH = 0x00000070; +static const ULONG OFFSET_PS_STARTBLOCK = 0x00000074; +static const ULONG OFFSET_PS_SIZE = 0x00000078; +static const ULONG OFFSET_PS_SIZE_HIGH = 0x0000007C; +static const WORD DEF_BIG_BLOCK_SIZE_BITS = 0x0009; +static const WORD MIN_BIG_BLOCK_SIZE_BITS = 0x0009; +static const WORD MAX_BIG_BLOCK_SIZE_BITS = 0x000c; +static const WORD DEF_SMALL_BLOCK_SIZE_BITS = 0x0006; +static const WORD DEF_BIG_BLOCK_SIZE = 0x0200; +static const WORD DEF_SMALL_BLOCK_SIZE = 0x0040; +static const ULONG BLOCK_FIRST_SPECIAL = 0xFFFFFFFB; +static const ULONG BLOCK_EXTBBDEPOT = 0xFFFFFFFC; +static const ULONG BLOCK_SPECIAL = 0xFFFFFFFD; +static const ULONG BLOCK_END_OF_CHAIN = 0xFFFFFFFE; +static const ULONG BLOCK_UNUSED = 0xFFFFFFFF; +static const ULONG DIRENTRY_NULL = 0xFFFFFFFF; + +#define DIRENTRY_NAME_MAX_LEN 0x20 +#define DIRENTRY_NAME_BUFFER_LEN 0x40 + +#define RAW_DIRENTRY_SIZE 0x00000080 + +#define HEADER_SIZE 512 + +#define MIN_BIG_BLOCK_SIZE 0x200 +#define MAX_BIG_BLOCK_SIZE 0x1000 + +/* + * Type of child entry link + */ +#define DIRENTRY_RELATION_PREVIOUS 0 +#define DIRENTRY_RELATION_NEXT 1 +#define DIRENTRY_RELATION_DIR 2 + +/* + * type constant used in files for the root storage + */ +#define STGTY_ROOT 0x05 + +#define COUNT_BBDEPOTINHEADER 109 + +/* FIXME: This value is stored in the header, but we hard-code it to 0x1000. */ +#define LIMIT_TO_USE_SMALL_BLOCK 0x1000 + +#define STGM_ACCESS_MODE(stgm) ((stgm)&0x0000f) +#define STGM_SHARE_MODE(stgm) ((stgm)&0x000f0) +#define STGM_CREATE_MODE(stgm) ((stgm)&0x0f000) + +#define STGM_KNOWN_FLAGS (0xf0ff | \ + STGM_TRANSACTED | STGM_CONVERT | STGM_PRIORITY | STGM_NOSCRATCH | \ + STGM_NOSNAPSHOT | STGM_DIRECT_SWMR | STGM_DELETEONRELEASE | STGM_SIMPLE) + +/* + * Forward declarations of all the structures used by the storage + * module. + */ +typedef struct StorageBaseImpl StorageBaseImpl; +typedef struct StorageBaseImplVtbl StorageBaseImplVtbl; +typedef struct StorageImpl StorageImpl; +typedef struct BlockChainStream BlockChainStream; +typedef struct SmallBlockChainStream SmallBlockChainStream; +typedef struct IEnumSTATSTGImpl IEnumSTATSTGImpl; +typedef struct DirEntry DirEntry; +typedef struct StgStreamImpl StgStreamImpl; + +/* + * A reference to a directory entry in the file or a transacted cache. + */ +typedef ULONG DirRef; + +/* + * This utility structure is used to read/write the information in a directory + * entry. + */ +struct DirEntry +{ + WCHAR name[DIRENTRY_NAME_MAX_LEN]; + WORD sizeOfNameString; + BYTE stgType; + DirRef leftChild; + DirRef rightChild; + DirRef dirRootEntry; + GUID clsid; + FILETIME ctime; + FILETIME mtime; + ULONG startingBlock; + ULARGE_INTEGER size; +}; + +HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes); + +/************************************************************************* + * Ole Convert support + */ + +HRESULT STORAGE_CreateOleStream(IStorage*, DWORD); +HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName); + +enum swmr_mode +{ + SWMR_None, + SWMR_Writer, + SWMR_Reader +}; + +/**************************************************************************** + * StorageBaseImpl definitions. + * + * This structure defines the base information contained in all implementations + * of IStorage contained in this file storage implementation. + * + * In OOP terms, this is the base class for all the IStorage implementations + * contained in this file. + */ +struct StorageBaseImpl +{ + IStorage IStorage_iface; + IPropertySetStorage IPropertySetStorage_iface; /* interface for adding a properties stream */ + IDirectWriterLock IDirectWriterLock_iface; + LONG ref; + + /* + * Stream tracking list + */ + + struct list strmHead; + + /* + * Storage tracking list + */ + struct list storageHead; + + /* + * TRUE if this object has been invalidated + */ + BOOL reverted; + + /* + * Index of the directory entry of this storage + */ + DirRef storageDirEntry; + + /* + * virtual methods. + */ + const StorageBaseImplVtbl *baseVtbl; + + /* + * flags that this storage was opened or created with + */ + DWORD openFlags; + + /* + * State bits appear to only be preserved while running. No in the stream + */ + DWORD stateBits; + + BOOL create; /* Was the storage created or opened. + The behaviour of STGM_SIMPLE depends on this */ + /* + * If this storage was opened in transacted mode, the object that implements + * the transacted snapshot or cache. + */ + StorageBaseImpl *transactedChild; + enum swmr_mode lockingrole; +}; + +/* virtual methods for StorageBaseImpl objects */ +struct StorageBaseImplVtbl { + void (*Destroy)(StorageBaseImpl*); + void (*Invalidate)(StorageBaseImpl*); + HRESULT (*Flush)(StorageBaseImpl*); + HRESULT (*GetFilename)(StorageBaseImpl*,LPWSTR*); + HRESULT (*CreateDirEntry)(StorageBaseImpl*,const DirEntry*,DirRef*); + HRESULT (*WriteDirEntry)(StorageBaseImpl*,DirRef,const DirEntry*); + HRESULT (*ReadDirEntry)(StorageBaseImpl*,DirRef,DirEntry*); + HRESULT (*DestroyDirEntry)(StorageBaseImpl*,DirRef); + HRESULT (*StreamReadAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,void*,ULONG*); + HRESULT (*StreamWriteAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,const void*,ULONG*); + HRESULT (*StreamSetSize)(StorageBaseImpl*,DirRef,ULARGE_INTEGER); + HRESULT (*StreamLink)(StorageBaseImpl*,DirRef,DirRef); + HRESULT (*GetTransactionSig)(StorageBaseImpl*,ULONG*,BOOL); + HRESULT (*SetTransactionSig)(StorageBaseImpl*,ULONG); + HRESULT (*LockTransaction)(StorageBaseImpl*,BOOL); + HRESULT (*UnlockTransaction)(StorageBaseImpl*,BOOL); +}; + +static inline void StorageBaseImpl_Destroy(StorageBaseImpl *This) +{ + This->baseVtbl->Destroy(This); +} + +static inline void StorageBaseImpl_Invalidate(StorageBaseImpl *This) +{ + This->baseVtbl->Invalidate(This); +} + +static inline HRESULT StorageBaseImpl_Flush(StorageBaseImpl *This) +{ + return This->baseVtbl->Flush(This); +} + +static inline HRESULT StorageBaseImpl_GetFilename(StorageBaseImpl *This, LPWSTR *result) +{ + return This->baseVtbl->GetFilename(This, result); +} + +static inline HRESULT StorageBaseImpl_CreateDirEntry(StorageBaseImpl *This, + const DirEntry *newData, DirRef *index) +{ + return This->baseVtbl->CreateDirEntry(This, newData, index); +} + +static inline HRESULT StorageBaseImpl_WriteDirEntry(StorageBaseImpl *This, + DirRef index, const DirEntry *data) +{ + return This->baseVtbl->WriteDirEntry(This, index, data); +} + +static inline HRESULT StorageBaseImpl_ReadDirEntry(StorageBaseImpl *This, + DirRef index, DirEntry *data) +{ + return This->baseVtbl->ReadDirEntry(This, index, data); +} + +static inline HRESULT StorageBaseImpl_DestroyDirEntry(StorageBaseImpl *This, + DirRef index) +{ + return This->baseVtbl->DestroyDirEntry(This, index); +} + +/* Read up to size bytes from this directory entry's stream at the given offset. */ +static inline HRESULT StorageBaseImpl_StreamReadAt(StorageBaseImpl *This, + DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) +{ + return This->baseVtbl->StreamReadAt(This, index, offset, size, buffer, bytesRead); +} + +/* Write size bytes to this directory entry's stream at the given offset, + * growing the stream if necessary. */ +static inline HRESULT StorageBaseImpl_StreamWriteAt(StorageBaseImpl *This, + DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) +{ + return This->baseVtbl->StreamWriteAt(This, index, offset, size, buffer, bytesWritten); +} + +static inline HRESULT StorageBaseImpl_StreamSetSize(StorageBaseImpl *This, + DirRef index, ULARGE_INTEGER newsize) +{ + return This->baseVtbl->StreamSetSize(This, index, newsize); +} + +/* Make dst point to the same stream that src points to. Other stream operations + * will not work properly for entries that point to the same stream, so this + * must be a very temporary state, and only one entry pointing to a given stream + * may be reachable at any given time. */ +static inline HRESULT StorageBaseImpl_StreamLink(StorageBaseImpl *This, + DirRef dst, DirRef src) +{ + return This->baseVtbl->StreamLink(This, dst, src); +} + +static inline HRESULT StorageBaseImpl_GetTransactionSig(StorageBaseImpl *This, + ULONG* result, BOOL refresh) +{ + return This->baseVtbl->GetTransactionSig(This, result, refresh); +} + +static inline HRESULT StorageBaseImpl_SetTransactionSig(StorageBaseImpl *This, + ULONG value) +{ + return This->baseVtbl->SetTransactionSig(This, value); +} + +static inline HRESULT StorageBaseImpl_LockTransaction(StorageBaseImpl *This, BOOL write) +{ + return This->baseVtbl->LockTransaction(This, write); +} + +static inline HRESULT StorageBaseImpl_UnlockTransaction(StorageBaseImpl *This, BOOL write) +{ + return This->baseVtbl->UnlockTransaction(This, write); +} + +/**************************************************************************** + * StorageBaseImpl stream list handlers + */ + +void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm); +void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm); + +/* Number of BlockChainStream objects to cache in a StorageImpl */ +#define BLOCKCHAIN_CACHE_SIZE 4 + +/**************************************************************************** + * StorageImpl definitions. + * + * This implementation of the IStorage interface represents a root + * storage. Basically, a document file. + */ +struct StorageImpl +{ + struct StorageBaseImpl base; + + /* + * File header + */ + WORD bigBlockSizeBits; + WORD smallBlockSizeBits; + ULONG bigBlockSize; + ULONG smallBlockSize; + ULONG bigBlockDepotCount; + ULONG rootStartBlock; + ULONG smallBlockLimit; + ULONG smallBlockDepotStart; + ULONG extBigBlockDepotStart; + ULONG *extBigBlockDepotLocations; + ULONG extBigBlockDepotLocationsSize; + ULONG extBigBlockDepotCount; + ULONG bigBlockDepotStart[COUNT_BBDEPOTINHEADER]; + ULONG transactionSig; + + ULONG extBlockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; + ULONG indexExtBlockDepotCached; + + ULONG blockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; + ULONG indexBlockDepotCached; + ULONG prevFreeBlock; + + /* All small blocks before this one are known to be in use. */ + ULONG firstFreeSmallBlock; + + /* + * Abstraction of the big block chains for the chains of the header. + */ + BlockChainStream* rootBlockChain; + BlockChainStream* smallBlockDepotChain; + BlockChainStream* smallBlockRootChain; + + /* Cache of block chain streams objects for directory entries */ + BlockChainStream* blockChainCache[BLOCKCHAIN_CACHE_SIZE]; + UINT blockChainToEvict; + + ULONG locks_supported; + + ILockBytes* lockBytes; + + ULONG locked_bytes[8]; +}; + +/**************************************************************************** + * StgStreamImpl definitions. + * + * This class implements the IStream interface and represents a stream + * located inside a storage object. + */ +struct StgStreamImpl +{ + IStream IStream_iface; + LONG ref; + + /* + * We are an entry in the storage object's stream handler list + */ + struct list StrmListEntry; + + /* + * Storage that is the parent(owner) of the stream + */ + StorageBaseImpl* parentStorage; + + /* + * Access mode of this stream. + */ + DWORD grfMode; + + /* + * Index of the directory entry that owns (points to) this stream. + */ + DirRef dirEntry; + + /* + * This is the current position of the cursor in the stream + */ + ULARGE_INTEGER currentPosition; +}; + +static inline StgStreamImpl *impl_from_IStream( IStream *iface ) +{ + return CONTAINING_RECORD(iface, StgStreamImpl, IStream_iface); +} + +/* + * Method definition for the StgStreamImpl class. + */ +StgStreamImpl* StgStreamImpl_Construct( + StorageBaseImpl* parentStorage, + DWORD grfMode, + DirRef dirEntry); + + +/* Range lock constants. + * + * The storage format reserves the region from 0x7fffff00-0x7fffffff for + * locking and synchronization. Because it reserves the entire block containing + * that range, and the minimum block size is 512 bytes, 0x7ffffe00-0x7ffffeff + * also cannot be used for any other purpose. + * Unfortunately, the spec doesn't say which bytes + * within that range are used, and for what. These are guesses based on testing. + * In particular, ends of ranges may be wrong. + + 0x0 through 0x57: Unknown. Causes read-only exclusive opens to fail. + 0x58 through 0x6b: Priority mode. + 0x6c through 0x7f: No snapshot mode. + 0x80: Commit lock. + 0x81 through 0x91: Priority mode, again. Not sure why it uses two regions. + 0x92: Lock-checking lock. Held while opening so ranges can be tested without + causing spurious failures if others try to grab or test those ranges at the + same time. + 0x93 through 0xa6: Read mode. + 0xa7 through 0xba: Write mode. + 0xbb through 0xce: Deny read. + 0xcf through 0xe2: Deny write. + 0xe2 through 0xff: Unknown. Causes read-only exclusive opens to fail. +*/ + +#define RANGELOCK_UNK1_FIRST 0x7ffffe00 +#define RANGELOCK_UNK1_LAST 0x7fffff57 +#define RANGELOCK_PRIORITY1_FIRST 0x7fffff58 +#define RANGELOCK_PRIORITY1_LAST 0x7fffff6b +#define RANGELOCK_NOSNAPSHOT_FIRST 0x7fffff6c +#define RANGELOCK_NOSNAPSHOT_LAST 0x7fffff7f +#define RANGELOCK_COMMIT 0x7fffff80 +#define RANGELOCK_PRIORITY2_FIRST 0x7fffff81 +#define RANGELOCK_PRIORITY2_LAST 0x7fffff91 +#define RANGELOCK_CHECKLOCKS 0x7fffff92 +#define RANGELOCK_READ_FIRST 0x7fffff93 +#define RANGELOCK_READ_LAST 0x7fffffa6 +#define RANGELOCK_WRITE_FIRST 0x7fffffa7 +#define RANGELOCK_WRITE_LAST 0x7fffffba +#define RANGELOCK_DENY_READ_FIRST 0x7fffffbb +#define RANGELOCK_DENY_READ_LAST 0x7fffffce +#define RANGELOCK_DENY_WRITE_FIRST 0x7fffffcf +#define RANGELOCK_DENY_WRITE_LAST 0x7fffffe2 +#define RANGELOCK_UNK2_FIRST 0x7fffffe3 +#define RANGELOCK_UNK2_LAST 0x7fffffff +#define RANGELOCK_TRANSACTION_FIRST RANGELOCK_COMMIT +#define RANGELOCK_TRANSACTION_LAST RANGELOCK_CHECKLOCKS +#define RANGELOCK_FIRST RANGELOCK_UNK1_FIRST +#define RANGELOCK_LAST RANGELOCK_UNK2_LAST + +/* internal value for LockRegion/UnlockRegion */ +#define WINE_LOCK_READ 0x80000000 + + +/****************************************************************************** + * Endian conversion macros + */ +#ifdef WORDS_BIGENDIAN + +#ifndef htole32 +#define htole32(x) RtlUlongByteSwap(x) +#endif +#ifndef htole16 +#define htole16(x) RtlUshortByteSwap(x) +#endif +#define lendian32toh(x) RtlUlongByteSwap(x) +#define lendian16toh(x) RtlUshortByteSwap(x) + +#else + +#ifndef htole32 +#define htole32(x) (x) +#endif +#ifndef htole16 +#define htole16(x) (x) +#endif +#define lendian32toh(x) (x) +#define lendian16toh(x) (x) + +#endif + +/****************************************************************************** + * The StorageUtl_ functions are miscellaneous utility functions. Most of which + * are abstractions used to read values from file buffers without having to + * worry about bit order + */ +void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value); +void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value); +void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value); +void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value); +void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, + ULARGE_INTEGER* value); +void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value); +void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value); +void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value); +void StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl *storage,STATSTG* destination, + const DirEntry* source, int statFlags); + + +#endif /* __STORAGE32_H__ */
From: Fabian Maurer dark.shadow4@web.de
--- dlls/coml2/Makefile.in | 12 +- dlls/coml2/coml2.spec | 23 + dlls/coml2/compobj.c | 174 + dlls/coml2/stg_prop.c | 4 +- dlls/coml2/storage32.c | 1109 +---- dlls/ole32/Makefile.in | 4 - dlls/ole32/dictionary.c | 191 - dlls/ole32/dictionary.h | 93 - dlls/ole32/filelockbytes.c | 401 -- dlls/ole32/ole2.c | 15 +- dlls/ole32/ole32.spec | 48 +- dlls/ole32/stg_prop.c | 3297 ------------- dlls/ole32/stg_stream.c | 705 --- dlls/ole32/storage32.c | 9440 +----------------------------------- dlls/ole32/storage32.h | 524 -- 15 files changed, 247 insertions(+), 15793 deletions(-) create mode 100644 dlls/coml2/compobj.c delete mode 100644 dlls/ole32/dictionary.c delete mode 100644 dlls/ole32/dictionary.h delete mode 100644 dlls/ole32/filelockbytes.c delete mode 100644 dlls/ole32/stg_prop.c delete mode 100644 dlls/ole32/stg_stream.c
diff --git a/dlls/coml2/Makefile.in b/dlls/coml2/Makefile.in index 8e4eba5f988..0803876564f 100644 --- a/dlls/coml2/Makefile.in +++ b/dlls/coml2/Makefile.in @@ -1,6 +1,14 @@ +EXTRADEFS = -DWINOLE32API= MODULE = coml2.dll IMPORTLIB = coml2 -IMPORTS = uuid +IMPORTS = uuid advapi32 user32 combase +DELAYIMPORTS = oleaut32
SOURCES = \ - memlockbytes.c + compobj.c \ + dictionary.c \ + filelockbytes.c \ + memlockbytes.c \ + stg_prop.c \ + stg_stream.c \ + storage32.c diff --git a/dlls/coml2/coml2.spec b/dlls/coml2/coml2.spec index 7070e5ca3b2..947ee94847d 100644 --- a/dlls/coml2/coml2.spec +++ b/dlls/coml2/coml2.spec @@ -1,2 +1,25 @@ @ stdcall CreateILockBytesOnHGlobal(ptr long ptr) +@ stdcall FmtIdToPropStgName(ptr wstr) +@ stdcall GetConvertStg(ptr) @ stdcall GetHGlobalFromILockBytes(ptr ptr) +@ stdcall InternalReadFmtUserTypeStg(ptr ptr ptr) +@ stdcall InternalSetConvertStg(ptr long) +@ stdcall InternalStgConvertPropertyToVariant(ptr long ptr ptr) +@ stdcall InternalWriteFmtUserTypeStg(ptr long ptr) +@ stdcall ReadClassStg(ptr ptr) +@ stdcall ReadClassStm(ptr ptr) +@ stdcall PropStgNameToFmtId(wstr ptr) +@ stdcall StgCreateDocfile(wstr long long ptr) +@ stdcall StgCreateDocfileOnILockBytes(ptr long long ptr) +@ stdcall StgCreatePropSetStg(ptr long ptr) +@ stdcall StgCreatePropStg(ptr ptr ptr long long ptr) +@ stdcall StgCreateStorageEx(wstr long long long ptr ptr ptr ptr) +@ stdcall StgIsStorageFile(wstr) +@ stdcall StgIsStorageILockBytes(ptr) +@ stdcall StgOpenPropStg(ptr ptr long long ptr) +@ stdcall StgOpenStorage(wstr ptr long ptr long ptr) +@ stdcall StgOpenStorageEx(wstr long long long ptr ptr ptr ptr) +@ stdcall StgOpenStorageOnILockBytes(ptr ptr long ptr long ptr) +@ stdcall StgSetTimes(wstr ptr ptr ptr) +@ stdcall WriteClassStg(ptr ptr) +@ stdcall WriteClassStm(ptr ptr) diff --git a/dlls/coml2/compobj.c b/dlls/coml2/compobj.c new file mode 100644 index 00000000000..7fc744bf6c9 --- /dev/null +++ b/dlls/coml2/compobj.c @@ -0,0 +1,174 @@ +/* + * COMPOBJ library + * + * Copyright 1995 Martin von Loewis + * Copyright 1998 Justin Bradford + * Copyright 1999 Francis Beaudet + * Copyright 1999 Sylvain St-Germain + * Copyright 2002 Marcus Meissner + * Copyright 2004 Mike Hearn + * Copyright 2005-2006 Robert Shearman (for CodeWeavers) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#define COBJMACROS +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "winuser.h" +#include "winternl.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ole); + +LSTATUS create_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey ); + +/* wrapper for NtCreateKey that creates the key recursively if necessary (copy from ole32)*/ +static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) +{ + NTSTATUS status = NtCreateKey( (HANDLE *)retkey, access, attr, 0, NULL, 0, NULL ); + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + HANDLE subkey, root = attr->RootDirectory; + WCHAR *buffer = attr->ObjectName->Buffer; + DWORD attrs, pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR); + UNICODE_STRING str; + + while (i < len && buffer[i] != '\') i++; + if (i == len) return status; + + attrs = attr->Attributes; + attr->ObjectName = &str; + + while (i < len) + { + str.Buffer = buffer + pos; + str.Length = (i - pos) * sizeof(WCHAR); + status = NtCreateKey( &subkey, access, attr, 0, NULL, 0, NULL ); + if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); + if (status) return status; + attr->RootDirectory = subkey; + while (i < len && buffer[i] == '\') i++; + pos = i; + while (i < len && buffer[i] != '\') i++; + } + str.Buffer = buffer + pos; + str.Length = (i - pos) * sizeof(WCHAR); + attr->Attributes = attrs; + status = NtCreateKey( (PHANDLE)retkey, access, attr, 0, NULL, 0, NULL ); + if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); + } + return status; +} + +static HKEY classes_root_hkey; + +/* create the special HKEY_CLASSES_ROOT key (copy from ole32) */ +static HKEY create_classes_root_hkey(DWORD access) +{ + HKEY hkey, ret = 0; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name = RTL_CONSTANT_STRING(L"\Registry\Machine\Software\Classes"); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &name; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + if (create_key( &hkey, access, &attr )) return 0; + TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey ); + + if (!(access & KEY_WOW64_64KEY)) + { + if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 ))) + ret = hkey; + else + NtClose( hkey ); /* somebody beat us to it */ + } + else + ret = hkey; + return ret; +} + +/* map the hkey from special root to normal key if necessary (copy from ole32) */ +static inline HKEY get_classes_root_hkey( HKEY hkey, REGSAM access ) +{ + HKEY ret = hkey; + const BOOL is_win64 = sizeof(void*) > sizeof(int); + const BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY); + + if (hkey == HKEY_CLASSES_ROOT && + ((access & KEY_WOW64_64KEY) || !(ret = classes_root_hkey))) + ret = create_classes_root_hkey(MAXIMUM_ALLOWED | (access & KEY_WOW64_64KEY)); + if (force_wow32 && ret && ret == classes_root_hkey) + { + access &= ~KEY_WOW64_32KEY; + if (create_classes_key(classes_root_hkey, L"Wow6432Node", access, &hkey)) + return 0; + ret = hkey; + } + + return ret; +} + +/* copy from ole32 */ +LSTATUS create_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + + if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE; + + attr.Length = sizeof(attr); + attr.RootDirectory = hkey; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, name ); + + return RtlNtStatusToDosError( create_key( retkey, access, &attr ) ); +} + +/* copy from ole32 */ +LSTATUS open_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + + if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE; + + attr.Length = sizeof(attr); + attr.RootDirectory = hkey; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, name ); + + return RtlNtStatusToDosError( NtOpenKey( (HANDLE *)retkey, access, &attr ) ); +} diff --git a/dlls/coml2/stg_prop.c b/dlls/coml2/stg_prop.c index 8bc159c7dd5..1776559915f 100644 --- a/dlls/coml2/stg_prop.c +++ b/dlls/coml2/stg_prop.c @@ -3178,7 +3178,7 @@ static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize)
#endif
-BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, +BOOLEAN WINAPI InternalStgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, USHORT CodePage, PROPVARIANT* pvar, void* pma) { struct read_buffer read_buffer; @@ -3197,7 +3197,7 @@ BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, return FALSE; }
-SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar, +SERIALIZEDPROPERTYVALUE* WINAPI InternalStgConvertVariantToProperty(const PROPVARIANT *pvar, USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid, BOOLEAN fReserved, ULONG *pcIndirect) { diff --git a/dlls/coml2/storage32.c b/dlls/coml2/storage32.c index 0d3f9922d78..17ed2e3b99a 100644 --- a/dlls/coml2/storage32.c +++ b/dlls/coml2/storage32.c @@ -48,7 +48,6 @@
#include "winreg.h" #include "wine/wingdi16.h" -#include "compobj_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(storage);
@@ -9077,69 +9076,6 @@ HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){ 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] * @@ -9307,7 +9243,7 @@ static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, /*********************************************************************** * WriteFmtUserTypeStg (OLE32.@) */ -HRESULT WINAPI WriteFmtUserTypeStg( +HRESULT WINAPI InternalWriteFmtUserTypeStg( LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType) { STATSTG stat; @@ -9350,7 +9286,7 @@ HRESULT WINAPI WriteFmtUserTypeStg( /****************************************************************************** * ReadFmtUserTypeStg [OLE32.@] */ -HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) +HRESULT WINAPI InternalReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) { HRESULT r; IStream *stm = 0; @@ -9521,1050 +9457,11 @@ HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid) return S_OK; }
- -/************************************************************************ - * OleConvert Functions - ***********************************************************************/ - -#define OLESTREAM_ID 0x501 -#define OLESTREAM_MAX_STR_LEN 255 - -/* OLESTREAM memory structure to use for Get and Put Routines */ -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 */ -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; - -/* Ole Presentation Stream structure */ -typedef struct -{ - BYTE byUnknown1[28]; - DWORD dwExtentX; - DWORD dwExtentY; - DWORD dwSize; - BYTE *pData; -} OLECONVERT_ISTORAGE_OLEPRES; - - -/************************************************************************* - * OLECONVERT_LoadOLE10 [Internal] - * - * Loads the OLE10 STREAM to memory - * - * PARAMS - * pOleStream [I] The OLESTREAM - * pData [I] Data Structure for the OLESTREAM Data - * - * RETURNS - * Success: S_OK - * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get - * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid - * - * NOTES - * This function is used by OleConvertOLESTREAMToIStorage only. - * - * Memory allocated for pData must be freed by the caller - */ -static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStream1) -{ - DWORD dwSize; - HRESULT hRes = S_OK; - int nTryCnt=0; - int max_try = 6; - - pData->pData = NULL; - pData->pstrOleObjFileName = NULL; - - for( nTryCnt=0;nTryCnt < max_try; nTryCnt++) - { - /* Get the OleID */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); - if(dwSize != sizeof(pData->dwOleID)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - else if(pData->dwOleID != OLESTREAM_ID) - { - hRes = CONVERT10_E_OLESTREAM_FMT; - } - else - { - hRes = S_OK; - break; - } - } - - if(hRes == S_OK) - { - /* Get the TypeID... more info needed for this field */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); - if(dwSize != sizeof(pData->dwTypeID)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - if(hRes == S_OK) - { - if(pData->dwTypeID != 0) - { - /* Get the length of the OleTypeName */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); - if(dwSize != sizeof(pData->dwOleTypeNameLength)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - - if(hRes == S_OK) - { - if(pData->dwOleTypeNameLength > 0) - { - /* Get the OleTypeName */ - dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); - if(dwSize != pData->dwOleTypeNameLength) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - } - if(bStream1) - { - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength)); - if(dwSize != sizeof(pData->dwOleObjFileNameLength)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - if(hRes == S_OK) - { - if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */ - pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength); - pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength); - if(pData->pstrOleObjFileName) - { - dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength); - if(dwSize != pData->dwOleObjFileNameLength) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - else - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - else - { - /* Get the Width of the Metafile */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); - if(dwSize != sizeof(pData->dwMetaFileWidth)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - if(hRes == S_OK) - { - /* Get the Height of the Metafile */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); - if(dwSize != sizeof(pData->dwMetaFileHeight)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - } - if(hRes == S_OK) - { - /* Get the Length of the Data */ - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); - if(dwSize != sizeof(pData->dwDataLength)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - - if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */ - { - if(!bStream1) /* if it is a second OLE stream data */ - { - pData->dwDataLength -= 8; - dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown)); - if(dwSize != sizeof(pData->strUnknown)) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - } - if(hRes == S_OK) - { - if(pData->dwDataLength > 0) - { - pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength); - - /* Get Data (ex. IStorage, Metafile, or BMP) */ - if(pData->pData) - { - dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength); - if(dwSize != pData->dwDataLength) - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - else - { - hRes = CONVERT10_E_OLESTREAM_GET; - } - } - } - } - } - return hRes; -} - -/************************************************************************* - * 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]; - - /* Create a temp File */ - GetTempPathW(MAX_PATH, wstrTempDir); - GetTempFileNameW(wstrTempDir, L"sis", 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]; - - *pData = NULL; - - /* Create temp Storage */ - GetTempPathW(MAX_PATH, wstrTempDir); - GetTempFileNameW(wstrTempDir, L"sis", 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; -} - -/************************************************************************* - * OLECONVERT_CreateCompObjStream [Internal] - * - * Creates a "\001CompObj" is the destination IStorage if necessary. - * - * PARAMS - * pStorage [I] The dest IStorage to create the CompObj Stream - * if necessary. - * strOleTypeName [I] The ProgID - * - * RETURNS - * Success: S_OK - * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream - * - * NOTES - * This function is used by OleConvertOLESTREAMToIStorage only. - * - * The stream data is stored in the OLESTREAM and there should be - * no need to recreate the stream. If the stream is manually - * deleted it will attempt to create it by querying the registry. - * - * - */ -HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName) -{ - IStream *pStream; - HRESULT hStorageRes, hRes = S_OK; - OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj; - WCHAR bufferW[OLESTREAM_MAX_STR_LEN]; - - static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; - static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71}; - - /* Initialize the CompObj structure */ - memset(&IStorageCompObj, 0, sizeof(IStorageCompObj)); - memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1)); - memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2)); - - - /* Create a CompObj stream if it doesn't exist */ - hStorageRes = IStorage_CreateStream(pStorage, L"\1CompObj", - STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); - if(hStorageRes == S_OK) - { - /* copy the OleTypeName to the compobj struct */ - IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1; - strcpy(IStorageCompObj.strOleTypeName, strOleTypeName); - - /* copy the OleTypeName to the compobj struct */ - /* Note: in the test made, these were Identical */ - IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1; - strcpy(IStorageCompObj.strProgIDName, strOleTypeName); - - /* Get the CLSID */ - MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1, - bufferW, OLESTREAM_MAX_STR_LEN ); - hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid)); - - if(hRes == S_OK) - { - HKEY hKey; - LONG hErr; - /* Get the CLSID Default Name from the Registry */ - hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey); - if(hErr == ERROR_SUCCESS) - { - char strTemp[OLESTREAM_MAX_STR_LEN]; - IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN; - hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength)); - if(hErr == ERROR_SUCCESS) - { - strcpy(IStorageCompObj.strCLSIDName, strTemp); - } - RegCloseKey(hKey); - } - } - - /* Write CompObj Structure to stream */ - hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL); - - WriteClassStm(pStream,&(IStorageCompObj.clsid)); - - hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL); - if(IStorageCompObj.dwCLSIDNameLength > 0) - { - hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL); - } - hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL); - if(IStorageCompObj.dwOleTypeNameLength > 0) - { - hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL); - } - hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL); - if(IStorageCompObj.dwProgIDNameLength > 0) - { - hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL); - } - hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL); - IStream_Release(pStream); - } - return hRes; -} - - -/************************************************************************* - * OLECONVERT_CreateOlePresStream[Internal] - * - * Creates the "\002OlePres000" Stream with the Metafile data - * - * PARAMS - * pStorage [I] The dest IStorage to create \002OLEPres000 stream in. - * dwExtentX [I] Width of the Metafile - * dwExtentY [I] Height of the Metafile - * pData [I] Metafile data - * dwDataLength [I] Size of the Metafile data - * - * RETURNS - * Success: S_OK - * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put - * - * NOTES - * This function is used by OleConvertOLESTREAMToIStorage only. - * - */ -static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength) -{ - HRESULT hRes; - IStream *pStream; - static const BYTE pOlePresStreamHeader[] = - { - 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - - static const BYTE pOlePresStreamHeaderEmpty[] = - { - 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - - /* Create the OlePres000 Stream */ - hRes = IStorage_CreateStream(pStorage, L"\2OlePres000", - STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); - - if(hRes == S_OK) - { - DWORD nHeaderSize; - OLECONVERT_ISTORAGE_OLEPRES OlePres; - - memset(&OlePres, 0, sizeof(OlePres)); - /* Do we have any metafile data to save */ - if(dwDataLength > 0) - { - memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader)); - nHeaderSize = sizeof(pOlePresStreamHeader); - } - else - { - memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty)); - nHeaderSize = sizeof(pOlePresStreamHeaderEmpty); - } - /* Set width and height of the metafile */ - OlePres.dwExtentX = dwExtentX; - OlePres.dwExtentY = -dwExtentY; - - /* Set Data and Length */ - if(dwDataLength > sizeof(METAFILEPICT16)) - { - OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16); - OlePres.pData = &(pData[8]); - } - /* Save OlePres000 Data to Stream */ - hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL); - hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL); - hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL); - hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL); - if(OlePres.dwSize > 0) - { - hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL); - } - IStream_Release(pStream); - } -} - -/************************************************************************* - * OLECONVERT_CreateOle10NativeStream [Internal] - * - * Creates the "\001Ole10Native" Stream (should contain a BMP) - * - * PARAMS - * pStorage [I] Dest storage to create the stream in - * pData [I] Ole10 Native Data (ex. bmp) - * dwDataLength [I] Size of the Ole10 Native Data - * - * RETURNS - * Nothing - * - * NOTES - * This function is used by OleConvertOLESTREAMToIStorage only. - * - * Might need to verify the data and return appropriate error message - * - */ -static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength) -{ - HRESULT hRes; - IStream *pStream; - - /* Create the Ole10Native Stream */ - hRes = IStorage_CreateStream(pStorage, L"\1Ole10Native", - STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); - - if(hRes == S_OK) - { - /* Write info to stream */ - hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL); - hRes = IStream_Write(pStream, pData, dwDataLength, NULL); - IStream_Release(pStream); - } - -} - -/************************************************************************* - * OLECONVERT_GetOLE10ProgID [Internal] - * - * Finds the ProgID (or OleTypeID) from the IStorage - * - * PARAMS - * pStorage [I] The Src IStorage to get the ProgID - * strProgID [I] the ProgID string to get - * dwSize [I] the size of the string - * - * RETURNS - * Success: S_OK - * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream - * - * NOTES - * This function is used by OleConvertIStorageToOLESTREAM only. - * - * - */ -static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize) -{ - HRESULT hRes; - IStream *pStream; - LARGE_INTEGER iSeekPos; - OLECONVERT_ISTORAGE_COMPOBJ CompObj; - - /* Open the CompObj Stream */ - hRes = IStorage_OpenStream(pStorage, L"\1CompObj", NULL, - STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); - if(hRes == S_OK) - { - - /*Get the OleType from the CompObj Stream */ - iSeekPos.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid); - iSeekPos.HighPart = 0; - - IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); - IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL); - iSeekPos.LowPart = CompObj.dwCLSIDNameLength; - IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); - IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL); - iSeekPos.LowPart = CompObj.dwOleTypeNameLength; - IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); - - IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL); - if(*dwSize > 0) - { - IStream_Read(pStream, strProgID, *dwSize, NULL); - } - IStream_Release(pStream); - } - else - { - STATSTG stat; - LPOLESTR wstrProgID; - - /* Get the OleType from the registry */ - REFCLSID clsid = &(stat.clsid); - IStorage_Stat(pStorage, &stat, STATFLAG_NONAME); - hRes = ProgIDFromCLSID(clsid, &wstrProgID); - if(hRes == S_OK) - { - *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE); - CoTaskMemFree(wstrProgID); - } - - } - return hRes; -} - -/************************************************************************* - * OLECONVERT_GetOle10PresData [Internal] - * - * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream - * - * PARAMS - * pStorage [I] Src IStroage - * pOleStream [I] Dest OleStream Mem Struct - * - * RETURNS - * Nothing - * - * NOTES - * This function is used by OleConvertIStorageToOLESTREAM only. - * - * Memory allocated for pData must be freed by the caller - * - * - */ -static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) -{ - - HRESULT hRes; - IStream *pStream; - - /* Initialize Default data for OLESTREAM */ - pOleStreamData[0].dwOleID = OLESTREAM_ID; - pOleStreamData[0].dwTypeID = 2; - pOleStreamData[1].dwOleID = OLESTREAM_ID; - pOleStreamData[1].dwTypeID = 0; - pOleStreamData[0].dwMetaFileWidth = 0; - pOleStreamData[0].dwMetaFileHeight = 0; - pOleStreamData[0].pData = NULL; - pOleStreamData[1].pData = NULL; - - /* Open Ole10Native Stream */ - hRes = IStorage_OpenStream(pStorage, L"\1Ole10Native", NULL, - STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); - if(hRes == S_OK) - { - - /* Read Size and Data */ - IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL); - if(pOleStreamData->dwDataLength > 0) - { - pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength); - IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL); - } - IStream_Release(pStream); - } - -} - - -/************************************************************************* - * OLECONVERT_GetOle20PresData[Internal] - * - * Converts IStorage "/002OlePres000" stream to a OLE10 Stream - * - * PARAMS - * pStorage [I] Src IStroage - * pOleStreamData [I] Dest OleStream Mem Struct - * - * RETURNS - * Nothing - * - * NOTES - * This function is used by OleConvertIStorageToOLESTREAM only. - * - * Memory allocated for pData must be freed by the caller - */ -static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) -{ - HRESULT hRes; - IStream *pStream; - OLECONVERT_ISTORAGE_OLEPRES olePress; - - /* Initialize Default data for OLESTREAM */ - pOleStreamData[0].dwOleID = OLESTREAM_ID; - pOleStreamData[0].dwTypeID = 2; - pOleStreamData[0].dwMetaFileWidth = 0; - pOleStreamData[0].dwMetaFileHeight = 0; - pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData)); - pOleStreamData[1].dwOleID = OLESTREAM_ID; - pOleStreamData[1].dwTypeID = 0; - pOleStreamData[1].dwOleTypeNameLength = 0; - pOleStreamData[1].strOleTypeName[0] = 0; - pOleStreamData[1].dwMetaFileWidth = 0; - pOleStreamData[1].dwMetaFileHeight = 0; - pOleStreamData[1].pData = NULL; - pOleStreamData[1].dwDataLength = 0; - - - /* Open OlePress000 stream */ - hRes = IStorage_OpenStream(pStorage, L"\2OlePres000", NULL, - STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); - if(hRes == S_OK) - { - LARGE_INTEGER iSeekPos; - METAFILEPICT16 MetaFilePict; - static const char strMetafilePictName[] = "METAFILEPICT"; - - /* Set the TypeID for a Metafile */ - pOleStreamData[1].dwTypeID = 5; - - /* Set the OleTypeName to Metafile */ - pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1; - strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName); - - iSeekPos.HighPart = 0; - iSeekPos.LowPart = sizeof(olePress.byUnknown1); - - /* Get Presentation Data */ - IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); - IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL); - IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL); - IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL); - - /*Set width and Height */ - pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX; - pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY; - if(olePress.dwSize > 0) - { - /* Set Length */ - pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16); - - /* Set MetaFilePict struct */ - MetaFilePict.mm = 8; - MetaFilePict.xExt = olePress.dwExtentX; - MetaFilePict.yExt = olePress.dwExtentY; - MetaFilePict.hMF = 0; - - /* Get Metafile Data */ - pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength); - memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict)); - IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL); - } - IStream_Release(pStream); - } -} - -/************************************************************************* - * OleConvertOLESTREAMToIStorage [OLE32.@] - * - * Read info on MSDN - * - * TODO - * DVTARGETDEVICE parameter is not handled - * Still unsure of some mem fields for OLE 10 Stream - * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", - * and "\001OLE" streams - * - */ -HRESULT WINAPI OleConvertOLESTREAMToIStorage ( - LPOLESTREAM pOleStream, - LPSTORAGE pstg, - const DVTARGETDEVICE* ptd) -{ - int i; - HRESULT hRes=S_OK; - OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; - - TRACE("%p %p %p\n", pOleStream, pstg, ptd); - - memset(pOleStreamData, 0, sizeof(pOleStreamData)); - - if(ptd != NULL) - { - FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n"); - } - - if(pstg == NULL || pOleStream == NULL) - { - hRes = E_INVALIDARG; - } - - if(hRes == S_OK) - { - /* Load the OLESTREAM to Memory */ - hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE); - } - - if(hRes == S_OK) - { - /* Load the OLESTREAM to Memory (part 2)*/ - hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE); - } - - if(hRes == S_OK) - { - - if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic)) - { - /* Do we have the IStorage Data in the OLESTREAM */ - if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0) - { - OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); - OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength); - } - else - { - /* It must be an original OLE 1.0 source */ - OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); - } - } - else - { - /* It must be an original OLE 1.0 source */ - OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); - } - - /* Create CompObj Stream if necessary */ - hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName); - if(hRes == S_OK) - { - /*Create the Ole Stream if necessary */ - STORAGE_CreateOleStream(pstg, 0); - } - } - - - /* Free allocated memory */ - for(i=0; i < 2; i++) - { - HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); - HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName); - pOleStreamData[i].pstrOleObjFileName = NULL; - } - return hRes; -} - -/************************************************************************* - * OleConvertIStorageToOLESTREAM [OLE32.@] - * - * Read info on MSDN - * - * Read info on MSDN - * - * TODO - * Still unsure of some mem fields for OLE 10 Stream - * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", - * and "\001OLE" streams. - * - */ -HRESULT WINAPI OleConvertIStorageToOLESTREAM ( - LPSTORAGE pstg, - LPOLESTREAM pOleStream) -{ - int i; - HRESULT hRes = S_OK; - IStream *pStream; - OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; - - TRACE("%p %p\n", pstg, pOleStream); - - memset(pOleStreamData, 0, sizeof(pOleStreamData)); - - if(pstg == NULL || pOleStream == NULL) - { - hRes = E_INVALIDARG; - } - if(hRes == S_OK) - { - /* Get the ProgID */ - pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN; - hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength)); - } - if(hRes == S_OK) - { - /* Was it originally Ole10 */ - hRes = IStorage_OpenStream(pstg, L"\1Ole10Native", 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream); - if(hRes == S_OK) - { - IStream_Release(pStream); - /* Get Presentation Data for Ole10Native */ - OLECONVERT_GetOle10PresData(pstg, pOleStreamData); - } - else - { - /* Get Presentation Data (OLE20) */ - OLECONVERT_GetOle20PresData(pstg, pOleStreamData); - } - - /* Save OLESTREAM */ - hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream); - if(hRes == S_OK) - { - hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream); - } - - } - - /* Free allocated memory */ - for(i=0; i < 2; i++) - { - HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); - } - - return hRes; -} - enum stream_1ole_flags { OleStream_LinkedObject = 0x00000001, OleStream_Convert = 0x00000004 };
-/************************************************************************* - * OleConvertIStorageToOLESTREAMEx [OLE32.@] - */ -HRESULT WINAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg, CLIPFORMAT cf, LONG width, LONG height, - DWORD size, LPSTGMEDIUM medium, LPOLESTREAM olestream ) -{ - FIXME("%p, %x, %ld, %ld, %ld, %p, %p: stub\n", stg, cf, width, height, size, medium, olestream); - - return E_NOTIMPL; -} - /*********************************************************************** * GetConvertStg (OLE32.@) */ @@ -10598,7 +9495,7 @@ HRESULT WINAPI GetConvertStg(IStorage *stg) /*********************************************************************** * SetConvertStg (OLE32.@) */ -HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert) +HRESULT WINAPI InternalSetConvertStg(IStorage *storage, BOOL convert) { DWORD flags = convert ? OleStream_Convert : 0; IStream *stream; diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index 84544a628a8..1ae64b064b4 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -15,11 +15,9 @@ SOURCES = \ datacache.c \ dcom.idl \ defaulthandler.c \ - dictionary.c \ drag_copy.svg \ drag_link.svg \ drag_move.svg \ - filelockbytes.c \ filemoniker.c \ ftmarshal.c \ git.c \ @@ -38,8 +36,6 @@ SOURCES = \ oleobj.c \ oleproxy.c \ pointermoniker.c \ - stg_prop.c \ - stg_stream.c \ storage32.c \ usrmarshal.c dlldata_EXTRADEFS = -DENTRY_PREFIX=OLE32_ -DPROXY_CLSID=CLSID_PSFactoryBuffer -DWINE_REGISTER_DLL diff --git a/dlls/ole32/dictionary.c b/dlls/ole32/dictionary.c deleted file mode 100644 index 593a4ff1588..00000000000 --- a/dlls/ole32/dictionary.c +++ /dev/null @@ -1,191 +0,0 @@ -/* Simple dictionary implementation using a linked list. - * FIXME: a skip list would be faster. - * - * Copyright 2005 Juan Lang - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ -#include <assert.h> -#include <stdarg.h> -#include "windef.h" -#include "winbase.h" -#include "dictionary.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(storage); - -struct dictionary_entry -{ - void *key; - void *value; - struct dictionary_entry *next; -}; - -struct dictionary -{ - comparefunc comp; - destroyfunc destroy; - void *extra; - struct dictionary_entry *head; - UINT num_entries; -}; - -struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra) -{ - struct dictionary *ret; - - TRACE("(%p, %p, %p)\n", c, d, extra); - if (!c) - return NULL; - ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary)); - if (ret) - { - ret->comp = c; - ret->destroy = d; - ret->extra = extra; - ret->head = NULL; - ret->num_entries = 0; - } - TRACE("returning %p\n", ret); - return ret; -} - -void dictionary_destroy(struct dictionary *d) -{ - TRACE("(%p)\n", d); - if (d) - { - struct dictionary_entry *p; - - for (p = d->head; p; ) - { - struct dictionary_entry *next = p->next; - - if (d->destroy) - d->destroy(p->key, p->value, d->extra); - HeapFree(GetProcessHeap(), 0, p); - p = next; - } - HeapFree(GetProcessHeap(), 0, d); - } -} - -UINT dictionary_num_entries(struct dictionary *d) -{ - return d ? d->num_entries : 0; -} - -/* Returns the address of the pointer to the node containing k. (It returns - * the address of either h->head or the address of the next member of the - * prior node. It's useful when you want to delete.) - * Assumes h and prev are not NULL. - */ -static struct dictionary_entry **dictionary_find_internal(struct dictionary *d, - const void *k) -{ - struct dictionary_entry **ret = NULL; - struct dictionary_entry *p; - - assert(d); - /* special case for head containing the desired element */ - if (d->head && d->comp(k, d->head->key, d->extra) == 0) - ret = &d->head; - for (p = d->head; !ret && p && p->next; p = p->next) - { - if (d->comp(k, p->next->key, d->extra) == 0) - ret = &p->next; - } - return ret; -} - -void dictionary_insert(struct dictionary *d, const void *k, const void *v) -{ - struct dictionary_entry **prior; - - TRACE("(%p, %p, %p)\n", d, k, v); - if (!d) - return; - if ((prior = dictionary_find_internal(d, k))) - { - if (d->destroy) - d->destroy((*prior)->key, (*prior)->value, d->extra); - (*prior)->key = (void *)k; - (*prior)->value = (void *)v; - } - else - { - struct dictionary_entry *elem = HeapAlloc(GetProcessHeap(), 0, - sizeof(struct dictionary_entry)); - - if (!elem) - return; - elem->key = (void *)k; - elem->value = (void *)v; - elem->next = d->head; - d->head = elem; - d->num_entries++; - } -} - -BOOL dictionary_find(struct dictionary *d, const void *k, void **value) -{ - struct dictionary_entry **prior; - BOOL ret = FALSE; - - TRACE("(%p, %p, %p)\n", d, k, value); - if (!d) - return FALSE; - if (!value) - return FALSE; - if ((prior = dictionary_find_internal(d, k))) - { - *value = (*prior)->value; - ret = TRUE; - } - TRACE("returning %d (%p)\n", ret, *value); - return ret; -} - -void dictionary_remove(struct dictionary *d, const void *k) -{ - struct dictionary_entry **prior, *temp; - - TRACE("(%p, %p)\n", d, k); - if (!d) - return; - if ((prior = dictionary_find_internal(d, k))) - { - temp = *prior; - if (d->destroy) - d->destroy((*prior)->key, (*prior)->value, d->extra); - *prior = (*prior)->next; - HeapFree(GetProcessHeap(), 0, temp); - d->num_entries--; - } -} - -void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure) -{ - struct dictionary_entry *p; - - TRACE("(%p, %p, %p)\n", d, e, closure); - if (!d) - return; - if (!e) - return; - for (p = d->head; p; p = p->next) - if (!e(p->key, p->value, d->extra, closure)) - break; -} diff --git a/dlls/ole32/dictionary.h b/dlls/ole32/dictionary.h deleted file mode 100644 index 231a6474723..00000000000 --- a/dlls/ole32/dictionary.h +++ /dev/null @@ -1,93 +0,0 @@ -/* Simple dictionary - * - * Copyright 2005 Juan Lang - * - * This is a pretty basic dictionary, or map if you prefer. It's not - * thread-safe. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ -#ifndef __DICTIONARY_H__ -#define __DICTIONARY_H__ - -#include <stdarg.h> -#include "windef.h" -#include "winbase.h" - -struct dictionary; - -/* Returns whether key a is less than, equal to, or greater than key b, in - * the same way (a - b) would for integers or strcmp(a, b) would for ANSI - * strings. - */ -typedef int (*comparefunc)(const void *a, const void *b, void *extra); - -/* Called for every element removed from the dictionary. See - * dictionary_destroy, dictionary_insert, and dictionary_remove. - */ -typedef void (*destroyfunc)(void *k, void *v, void *extra); - -/* Called for each element in the dictionary. Return FALSE if you don't want - * to enumerate any more. - */ -typedef BOOL (*enumeratefunc)(const void *k, const void *v, void *extra, - void *closure); - -/* Constructs a dictionary, using c as a comparison function for keys. - * If d is not NULL, it will be called whenever an item is about to be removed - * from the table, for example when dictionary_remove is called for a key, or - * when dictionary_destroy is called. - * extra is passed to c (and d, if it's provided). - * Assumes c is not NULL. - */ -struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra); - -/* Assumes d is not NULL. */ -void dictionary_destroy(struct dictionary *d); - -/* Returns how many entries have been stored in the dictionary. If two values - * with the same key are inserted, only one is counted. - */ -UINT dictionary_num_entries(struct dictionary *d); - -/* Sets an element with key k and value v to the dictionary. If a value - * already exists with key k, its value is replaced, and the destroyfunc (if - * set) is called for the previous item. - * Assumes k and v can be bitwise-copied. - * Both k and v are allowed to be NULL, in case you want to use integer - * values for either the key or the value. - * Assumes d is not NULL. - */ -void dictionary_insert(struct dictionary *d, const void *k, const void *v); - -/* If a value with key k has been inserted into the dictionary, *v is set - * to its associated value. Returns FALSE if the key is not found, and TRUE - * if it is. *v is undefined if it returns FALSE. (It is not set to NULL, - * because this dictionary doesn't prevent you from using NULL as a value - * value; see dictionary_insert.) - * Assumes d and v are not NULL. - */ -BOOL dictionary_find(struct dictionary *d, const void *k, void **v); - -/* Removes the element with key k from the dictionary. Calls the destroyfunc - * for the dictionary with the element if found (so you may destroy it if it's - * dynamically allocated.) - * Assumes d is not NULL. - */ -void dictionary_remove(struct dictionary *d, const void *k); - -void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure); - -#endif /* ndef __DICTIONARY_H__ */ diff --git a/dlls/ole32/filelockbytes.c b/dlls/ole32/filelockbytes.c deleted file mode 100644 index d806916f1d5..00000000000 --- a/dlls/ole32/filelockbytes.c +++ /dev/null @@ -1,401 +0,0 @@ -/****************************************************************************** - * - * File-based ILockBytes implementation - * - * Copyright 1999 Thuy Nguyen - * Copyright 2010 Vincent Povirk for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include <assert.h> -#include <stdlib.h> -#include <stdarg.h> -#include <stdio.h> -#include <string.h> -#include <limits.h> - -#define COBJMACROS -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winerror.h" -#include "objbase.h" -#include "ole2.h" - -#include "storage32.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(storage); - -typedef struct FileLockBytesImpl -{ - ILockBytes ILockBytes_iface; - LONG ref; - HANDLE hfile; - DWORD flProtect; - LPWSTR pwcsName; -} FileLockBytesImpl; - -static const ILockBytesVtbl FileLockBytesImpl_Vtbl; - -static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface) -{ - return CONTAINING_RECORD(iface, FileLockBytesImpl, ILockBytes_iface); -} - -/*********************************************************** - * Prototypes for private methods - */ - -/**************************************************************************** - * GetProtectMode - * - * This function will return a protection mode flag for a file-mapping object - * from the open flags of a file. - */ -static DWORD GetProtectMode(DWORD openFlags) -{ - switch(STGM_ACCESS_MODE(openFlags)) - { - case STGM_WRITE: - case STGM_READWRITE: - return PAGE_READWRITE; - } - return PAGE_READONLY; -} - -/****************************************************************************** - * FileLockBytesImpl_Construct - * - * Initialize a big block object supported by a file. - */ -HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes) -{ - FileLockBytesImpl *This; - WCHAR fullpath[MAX_PATH]; - - if (hFile == INVALID_HANDLE_VALUE) - return E_FAIL; - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(FileLockBytesImpl)); - - if (!This) - return E_OUTOFMEMORY; - - This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl; - This->ref = 1; - This->hfile = hFile; - This->flProtect = GetProtectMode(openFlags); - - if(pwcsName) { - if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL)) - { - lstrcpynW(fullpath, pwcsName, MAX_PATH); - } - This->pwcsName = HeapAlloc(GetProcessHeap(), 0, - (lstrlenW(fullpath)+1)*sizeof(WCHAR)); - if (!This->pwcsName) - { - HeapFree(GetProcessHeap(), 0, This); - return E_OUTOFMEMORY; - } - lstrcpyW(This->pwcsName, fullpath); - } - else - This->pwcsName = NULL; - - *pLockBytes = &This->ILockBytes_iface; - - return S_OK; -} - -/* ILockByte Interfaces */ - -static HRESULT WINAPI FileLockBytesImpl_QueryInterface(ILockBytes *iface, REFIID riid, - void **ppvObject) -{ - if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ILockBytes)) - *ppvObject = iface; - else - { - *ppvObject = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppvObject); - - return S_OK; -} - -static ULONG WINAPI FileLockBytesImpl_AddRef(ILockBytes *iface) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - return InterlockedIncrement(&This->ref); -} - -static ULONG WINAPI FileLockBytesImpl_Release(ILockBytes *iface) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - ULONG ref; - - ref = InterlockedDecrement(&This->ref); - - if (ref == 0) - { - CloseHandle(This->hfile); - HeapFree(GetProcessHeap(), 0, This->pwcsName); - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -/****************************************************************************** - * This method is part of the ILockBytes interface. - * - * It reads a block of information from the byte array at the specified - * offset. - * - * See the documentation of ILockBytes for more info. - */ -static HRESULT WINAPI FileLockBytesImpl_ReadAt( - ILockBytes* iface, - ULARGE_INTEGER ulOffset, /* [in] */ - void* pv, /* [length_is][size_is][out] */ - ULONG cb, /* [in] */ - ULONG* pcbRead) /* [out] */ -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - ULONG bytes_left = cb; - LPBYTE readPtr = pv; - BOOL ret; - LARGE_INTEGER offset; - ULONG cbRead; - - TRACE("%p, %ld, %p, %lu, %p.\n", iface, ulOffset.LowPart, pv, cb, pcbRead); - - /* verify a sane environment */ - if (!This) return E_FAIL; - - if (pcbRead) - *pcbRead = 0; - - offset.QuadPart = ulOffset.QuadPart; - - ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN); - - if (!ret) - return STG_E_READFAULT; - - while (bytes_left) - { - ret = ReadFile(This->hfile, readPtr, bytes_left, &cbRead, NULL); - - if (!ret || cbRead == 0) - return STG_E_READFAULT; - - if (pcbRead) - *pcbRead += cbRead; - - bytes_left -= cbRead; - readPtr += cbRead; - } - - TRACE("finished\n"); - return S_OK; -} - -/****************************************************************************** - * This method is part of the ILockBytes interface. - * - * It writes the specified bytes at the specified offset. - * position. If the file is too small, it will be resized. - * - * See the documentation of ILockBytes for more info. - */ -static HRESULT WINAPI FileLockBytesImpl_WriteAt( - ILockBytes* iface, - ULARGE_INTEGER ulOffset, /* [in] */ - const void* pv, /* [size_is][in] */ - ULONG cb, /* [in] */ - ULONG* pcbWritten) /* [out] */ -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - ULONG bytes_left = cb; - const BYTE *writePtr = pv; - BOOL ret; - LARGE_INTEGER offset; - ULONG cbWritten; - - TRACE("%p, %ld, %p, %lu, %p.\n", iface, ulOffset.LowPart, pv, cb, pcbWritten); - - /* verify a sane environment */ - if (!This) return E_FAIL; - - if (This->flProtect != PAGE_READWRITE) - return STG_E_ACCESSDENIED; - - if (pcbWritten) - *pcbWritten = 0; - - offset.QuadPart = ulOffset.QuadPart; - - ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN); - - if (!ret) - return STG_E_WRITEFAULT; - - while (bytes_left) - { - ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL); - - if (!ret) - return STG_E_WRITEFAULT; - - if (pcbWritten) - *pcbWritten += cbWritten; - - bytes_left -= cbWritten; - writePtr += cbWritten; - } - - TRACE("finished\n"); - return S_OK; -} - -static HRESULT WINAPI FileLockBytesImpl_Flush(ILockBytes* iface) -{ - return S_OK; -} - -/****************************************************************************** - * ILockBytes_SetSize - * - * Sets the size of the file. - * - */ -static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGER newSize) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - HRESULT hr = S_OK; - LARGE_INTEGER newpos; - - TRACE("new size %lu\n", newSize.LowPart); - - newpos.QuadPart = newSize.QuadPart; - if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN)) - { - SetEndOfFile(This->hfile); - } - - return hr; -} - -static HRESULT get_lock_error(void) -{ - switch (GetLastError()) - { - case ERROR_LOCK_VIOLATION: return STG_E_LOCKVIOLATION; break; - case ERROR_ACCESS_DENIED: return STG_E_ACCESSDENIED; break; - case ERROR_NOT_SUPPORTED: return STG_E_INVALIDFUNCTION; break; - default: - FIXME("no mapping for error %ld\n", GetLastError()); - return STG_E_INVALIDFUNCTION; - } -} - -static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface, - ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - OVERLAPPED ol; - DWORD lock_flags = LOCKFILE_FAIL_IMMEDIATELY; - - TRACE("ofs %lu count %lu flags %lx\n", libOffset.LowPart, cb.LowPart, dwLockType); - - if (dwLockType & LOCK_WRITE) - return STG_E_INVALIDFUNCTION; - - if (dwLockType & (LOCK_EXCLUSIVE|LOCK_ONLYONCE)) - lock_flags |= LOCKFILE_EXCLUSIVE_LOCK; - - ol.hEvent = 0; - ol.Offset = libOffset.LowPart; - ol.OffsetHigh = libOffset.HighPart; - - if (LockFileEx(This->hfile, lock_flags, 0, cb.LowPart, cb.HighPart, &ol)) - return S_OK; - return get_lock_error(); -} - -static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface, - ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - OVERLAPPED ol; - - TRACE("ofs %lu count %lu flags %lx\n", libOffset.LowPart, cb.LowPart, dwLockType); - - if (dwLockType & LOCK_WRITE) - return STG_E_INVALIDFUNCTION; - - ol.hEvent = 0; - ol.Offset = libOffset.LowPart; - ol.OffsetHigh = libOffset.HighPart; - - if (UnlockFileEx(This->hfile, 0, cb.LowPart, cb.HighPart, &ol)) - return S_OK; - return get_lock_error(); -} - -static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface, - STATSTG *pstatstg, DWORD grfStatFlag) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - - if (!(STATFLAG_NONAME & grfStatFlag) && This->pwcsName) - { - pstatstg->pwcsName = - CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR)); - - lstrcpyW(pstatstg->pwcsName, This->pwcsName); - } - else - pstatstg->pwcsName = NULL; - - pstatstg->type = STGTY_LOCKBYTES; - - pstatstg->cbSize.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.HighPart); - /* FIXME: If the implementation is exported, we'll need to set other fields. */ - - pstatstg->grfLocksSupported = LOCK_EXCLUSIVE|LOCK_ONLYONCE|WINE_LOCK_READ; - - return S_OK; -} - -static const ILockBytesVtbl FileLockBytesImpl_Vtbl = { - FileLockBytesImpl_QueryInterface, - FileLockBytesImpl_AddRef, - FileLockBytesImpl_Release, - FileLockBytesImpl_ReadAt, - FileLockBytesImpl_WriteAt, - FileLockBytesImpl_Flush, - FileLockBytesImpl_SetSize, - FileLockBytesImpl_LockRegion, - FileLockBytesImpl_UnlockRegion, - FileLockBytesImpl_Stat -}; diff --git a/dlls/ole32/ole2.c b/dlls/ole32/ole2.c index f4672b704ee..084f75fba8f 100644 --- a/dlls/ole32/ole2.c +++ b/dlls/ole32/ole2.c @@ -50,6 +50,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); WINE_DECLARE_DEBUG_CHANNEL(accel);
+/* coml2 private exports */ +HRESULT WINAPI InternalWriteFmtUserTypeStg(LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType); +HRESULT WINAPI InternalReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType); +HRESULT WINAPI InternalSetConvertStg(IStorage *storage, BOOL convert); + /****************************************************************************** * These are static/global variables and internal data structures that the * OLE module uses to maintain its state. @@ -2622,7 +2627,7 @@ HRESULT WINAPI OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pClsidNew) if(FAILED(hr)) return hr;
- hr = ReadFmtUserTypeStg(pStg, &cf, &user_type_old); + hr = InternalReadFmtUserTypeStg(pStg, &cf, &user_type_old); if(FAILED(hr)) { cf = 0; user_type_new = NULL; @@ -2632,7 +2637,7 @@ HRESULT WINAPI OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pClsidNew) if(FAILED(hr)) user_type_new = NULL;
- hr = WriteFmtUserTypeStg(pStg, cf, user_type_new); + hr = InternalWriteFmtUserTypeStg(pStg, cf, user_type_new); CoTaskMemFree(user_type_new); if(FAILED(hr)) { @@ -2641,10 +2646,10 @@ HRESULT WINAPI OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pClsidNew) return hr; }
- hr = SetConvertStg(pStg, TRUE); + hr = InternalSetConvertStg(pStg, TRUE); if(FAILED(hr)) { - WriteFmtUserTypeStg(pStg, cf, user_type_old); + InternalWriteFmtUserTypeStg(pStg, cf, user_type_old); IStorage_SetClass(pStg, &stat.clsid); } else @@ -2967,7 +2972,7 @@ HRESULT WINAPI OleCreateStaticFromData(IDataObject *data, REFIID iid, DWORD rend hr = IOleObject_GetUserType(ole_object, USERCLASSTYPE_FULL, &ole_typename); if(FAILED(hr)) ole_typename = NULL; - hr = WriteFmtUserTypeStg(stg, fmt->cfFormat, ole_typename); + hr = InternalWriteFmtUserTypeStg(stg, fmt->cfFormat, ole_typename); CoTaskMemFree(ole_typename); if (FAILED(hr)) goto end;
diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 944ff81b1b3..a3b30725d7e 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -115,10 +115,10 @@ @ stdcall -private DllUnregisterServer() @ stdcall DoDragDrop(ptr ptr long ptr) @ stub EnableHookObject -@ stdcall FmtIdToPropStgName(ptr wstr) +@ stdcall FmtIdToPropStgName(ptr wstr) coml2.FmtIdToPropStgName @ stdcall FreePropVariantArray(long ptr) combase.FreePropVariantArray @ stdcall GetClassFile(wstr ptr) -@ stdcall GetConvertStg(ptr) +@ stdcall GetConvertStg(ptr) coml2.GetConvertStg @ stub GetDocumentBitStg @ stdcall GetErrorInfo(long ptr) combase.GetErrorInfo @ stdcall GetHGlobalFromILockBytes(ptr ptr) coml2.GetHGlobalFromILockBytes @@ -241,14 +241,14 @@ @ stdcall OleUninitialize() @ stub OpenOrCreateStream @ stdcall ProgIDFromCLSID(ptr ptr) combase.ProgIDFromCLSID -@ stdcall PropStgNameToFmtId(wstr ptr) +@ stdcall PropStgNameToFmtId(wstr ptr) coml2.PropStgNameToFmtId @ stdcall PropSysAllocString(wstr) @ stdcall PropSysFreeString(wstr) @ stdcall PropVariantClear(ptr) combase.PropVariantClear @ stdcall PropVariantCopy(ptr ptr) combase.PropVariantCopy -@ stdcall ReadClassStg(ptr ptr) -@ stdcall ReadClassStm(ptr ptr) -@ stdcall ReadFmtUserTypeStg(ptr ptr ptr) +@ stdcall ReadClassStg(ptr ptr) coml2.ReadClassStg +@ stdcall ReadClassStm(ptr ptr) coml2.ReadClassStm +@ stdcall ReadFmtUserTypeStg(ptr ptr ptr) coml2.InternalReadFmtUserTypeStg @ stub ReadOleStg @ stub ReadStringStream @ stdcall RegisterDragDrop(long ptr) @@ -262,26 +262,26 @@ @ stdcall STGMEDIUM_UserMarshal(ptr ptr ptr) @ stdcall STGMEDIUM_UserSize(ptr long ptr) @ stdcall STGMEDIUM_UserUnmarshal(ptr ptr ptr) -@ stdcall SetConvertStg(ptr long) +@ stdcall SetConvertStg(ptr long) coml2.InternalSetConvertStg @ stub SetDocumentBitStg @ stdcall SetErrorInfo(long ptr) combase.SetErrorInfo -@ stdcall StgConvertPropertyToVariant(ptr long ptr ptr) -@ stdcall StgConvertVariantToProperty(ptr long ptr ptr long long ptr) -@ stdcall StgCreateDocfile(wstr long long ptr) -@ stdcall StgCreateDocfileOnILockBytes(ptr long long ptr) -@ stdcall StgCreatePropSetStg(ptr long ptr) -@ stdcall StgCreatePropStg(ptr ptr ptr long long ptr) -@ stdcall StgCreateStorageEx(wstr long long long ptr ptr ptr ptr) +@ stdcall StgConvertPropertyToVariant(ptr long ptr ptr) coml2.InternalStgConvertPropertyToVariant +@ stdcall StgConvertVariantToProperty(ptr long ptr ptr long long ptr) coml2.InternalStgConvertVariantToProperty +@ stdcall StgCreateDocfile(wstr long long ptr) coml2.StgCreateDocfile +@ stdcall StgCreateDocfileOnILockBytes(ptr long long ptr) coml2.StgCreateDocfileOnILockBytes +@ stdcall StgCreatePropSetStg(ptr long ptr) coml2.StgCreatePropSetStg +@ stdcall StgCreatePropStg(ptr ptr ptr long long ptr) coml2.StgCreatePropStg +@ stdcall StgCreateStorageEx(wstr long long long ptr ptr ptr ptr) coml2.StgCreateStorageEx @ stub StgGetIFillLockBytesOnFile @ stub StgGetIFillLockBytesOnILockBytes -@ stdcall StgIsStorageFile(wstr) -@ stdcall StgIsStorageILockBytes(ptr) +@ stdcall StgIsStorageFile(wstr) coml2.StgIsStorageFile +@ stdcall StgIsStorageILockBytes(ptr) coml2.StgIsStorageILockBytes @ stub StgOpenAsyncDocfileOnIFillLockBytes -@ stdcall StgOpenPropStg(ptr ptr long long ptr) -@ stdcall StgOpenStorage(wstr ptr long ptr long ptr) -@ stdcall StgOpenStorageEx(wstr long long long ptr ptr ptr ptr) -@ stdcall StgOpenStorageOnILockBytes(ptr ptr long ptr long ptr) -@ stdcall StgSetTimes(wstr ptr ptr ptr ) +@ stdcall StgOpenPropStg(ptr ptr long long ptr) coml2.StgOpenPropStg +@ stdcall StgOpenStorage(wstr ptr long ptr long ptr) coml2.StgOpenStorage +@ stdcall StgOpenStorageEx(wstr long long long ptr ptr ptr ptr) coml2.StgOpenStorageEx +@ stdcall StgOpenStorageOnILockBytes(ptr ptr long ptr long ptr) coml2.StgOpenStorageOnILockBytes +@ stdcall StgSetTimes(wstr ptr ptr ptr) coml2.StgSetTimes @ stdcall StringFromCLSID(ptr ptr) combase.StringFromCLSID @ stdcall StringFromGUID2(ptr ptr long) combase.StringFromGUID2 @ stdcall StringFromIID(ptr ptr) combase.StringFromIID @@ -294,8 +294,8 @@ @ stdcall WdtpInterfacePointer_UserMarshal(ptr long ptr ptr ptr) combase.WdtpInterfacePointer_UserMarshal @ stdcall WdtpInterfacePointer_UserSize(ptr long long ptr ptr) combase.WdtpInterfacePointer_UserSize @ stdcall WdtpInterfacePointer_UserUnmarshal(ptr ptr ptr ptr) combase.WdtpInterfacePointer_UserUnmarshal -@ stdcall WriteClassStg(ptr ptr) -@ stdcall WriteClassStm(ptr ptr) -@ stdcall WriteFmtUserTypeStg(ptr long ptr) +@ stdcall WriteClassStg(ptr ptr) coml2.WriteClassStg +@ stdcall WriteClassStm(ptr ptr) coml2.WriteClassStm +@ stdcall WriteFmtUserTypeStg(ptr long ptr) coml2.InternalWriteFmtUserTypeStg @ stub WriteOleStg @ stub WriteStringStream diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c deleted file mode 100644 index 8bc159c7dd5..00000000000 --- a/dlls/ole32/stg_prop.c +++ /dev/null @@ -1,3297 +0,0 @@ -/* - * Compound Storage (32 bit version) - * Storage implementation - * - * This file contains the compound file implementation - * of the storage interface. - * - * Copyright 1999 Francis Beaudet - * Copyright 1999 Sylvain St-Germain - * Copyright 1999 Thuy Nguyen - * Copyright 2005 Mike McCormack - * Copyright 2005 Juan Lang - * Copyright 2006 Mike McCormack - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * TODO: - * - I don't honor the maximum property set size. - * - Certain bogus files could result in reading past the end of a buffer. - * - Mac-generated files won't be read correctly, even if they're little - * endian, because I disregard whether the generator was a Mac. This means - * strings will probably be munged (as I don't understand Mac scripts.) - * - Not all PROPVARIANT types are supported. - * - User defined properties are not supported, see comment in - * PropertyStorage_ReadFromStream - */ - -#include <assert.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define COBJMACROS -#include "windef.h" -#include "winbase.h" -#include "winnls.h" -#include "winuser.h" -#include "wine/asm.h" -#include "wine/debug.h" -#include "wine/heap.h" -#include "dictionary.h" -#include "storage32.h" -#include "oleauto.h" - -WINE_DEFAULT_DEBUG_CHANNEL(storage); - -static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface ) -{ - return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface); -} - -/* These are documented in MSDN, - * but they don't seem to be in any header file. - */ -#define PROPSETHDR_BYTEORDER_MAGIC 0xfffe -#define PROPSETHDR_OSVER_KIND_WIN16 0 -#define PROPSETHDR_OSVER_KIND_MAC 1 -#define PROPSETHDR_OSVER_KIND_WIN32 2 - -#define CP_UNICODE 1200 - -#define MAX_VERSION_0_PROP_NAME_LENGTH 256 - -#define CFTAG_WINDOWS (-1L) -#define CFTAG_MACINTOSH (-2L) -#define CFTAG_FMTID (-3L) -#define CFTAG_NODATA 0L - -#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align)) - -typedef struct tagPROPERTYSETHEADER -{ - WORD wByteOrder; /* always 0xfffe */ - WORD wFormat; /* can be zero or one */ - DWORD dwOSVer; /* OS version of originating system */ - CLSID clsid; /* application CLSID */ - DWORD reserved; /* always 1 */ -} PROPERTYSETHEADER; - -typedef struct tagFORMATIDOFFSET -{ - FMTID fmtid; - DWORD dwOffset; /* from beginning of stream */ -} FORMATIDOFFSET; - -typedef struct tagPROPERTYSECTIONHEADER -{ - DWORD cbSection; - DWORD cProperties; -} PROPERTYSECTIONHEADER; - -typedef struct tagPROPERTYIDOFFSET -{ - DWORD propid; - DWORD dwOffset; /* from beginning of section */ -} PROPERTYIDOFFSET; - -typedef struct tagPropertyStorage_impl PropertyStorage_impl; - -/* Initializes the property storage from the stream (and undoes any uncommitted - * changes in the process.) Returns an error if there is an error reading or - * if the stream format doesn't match what's expected. - */ -static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *); - -static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *); - -/* Creates the dictionaries used by the property storage. If successful, all - * the dictionaries have been created. If failed, none has been. (This makes - * it a bit easier to deal with destroying them.) - */ -static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *); - -static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *); - -/* Copies from propvar to prop. If propvar's type is VT_LPSTR, copies the - * string using PropertyStorage_StringCopy. - */ -static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, - const PROPVARIANT *propvar, UINT targetCP, UINT srcCP); - -/* Copies the string src, which is encoded using code page srcCP, and returns - * it in *dst, in the code page specified by targetCP. The returned string is - * allocated using CoTaskMemAlloc. - * If srcCP is CP_UNICODE, src is in fact an LPCWSTR. Similarly, if targetCP - * is CP_UNICODE, the returned string is in fact an LPWSTR. - * Returns S_OK on success, something else on failure. - */ -static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, - UINT targetCP); - -static const IPropertyStorageVtbl IPropertyStorage_Vtbl; - -/*********************************************************************** - * Implementation of IPropertyStorage - */ -struct tagPropertyStorage_impl -{ - IPropertyStorage IPropertyStorage_iface; - LONG ref; - CRITICAL_SECTION cs; - IStream *stm; - BOOL dirty; - FMTID fmtid; - CLSID clsid; - WORD format; - DWORD originatorOS; - DWORD grfFlags; - DWORD grfMode; - UINT codePage; - LCID locale; - PROPID highestProp; - struct dictionary *name_to_propid; - struct dictionary *propid_to_name; - struct dictionary *propid_to_prop; -}; - -static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface) -{ - return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface); -} - -struct enum_stat_prop_stg -{ - IEnumSTATPROPSTG IEnumSTATPROPSTG_iface; - LONG refcount; - PropertyStorage_impl *storage; - STATPROPSTG *stats; - size_t current; - size_t count; -}; - -static struct enum_stat_prop_stg *impl_from_IEnumSTATPROPSTG(IEnumSTATPROPSTG *iface) -{ - return CONTAINING_RECORD(iface, struct enum_stat_prop_stg, IEnumSTATPROPSTG_iface); -} - -static HRESULT WINAPI enum_stat_prop_stg_QueryInterface(IEnumSTATPROPSTG *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IEnumSTATPROPSTG) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IEnumSTATPROPSTG_AddRef(iface); - return S_OK; - } - - WARN("Unsupported interface %s.\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI enum_stat_prop_stg_AddRef(IEnumSTATPROPSTG *iface) -{ - struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); - LONG refcount = InterlockedIncrement(&penum->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI enum_stat_prop_stg_Release(IEnumSTATPROPSTG *iface) -{ - struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); - LONG refcount = InterlockedDecrement(&penum->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - IPropertyStorage_Release(&penum->storage->IPropertyStorage_iface); - heap_free(penum->stats); - heap_free(penum); - } - - return refcount; -} - -static HRESULT WINAPI enum_stat_prop_stg_Next(IEnumSTATPROPSTG *iface, ULONG celt, STATPROPSTG *ret, ULONG *fetched) -{ - struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); - ULONG count = 0; - WCHAR *name; - - TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched); - - if (penum->current == ~0u) - penum->current = 0; - - while (count < celt && penum->current < penum->count) - { - *ret = penum->stats[penum->current++]; - - if (dictionary_find(penum->storage->propid_to_name, UlongToPtr(ret->propid), (void **)&name)) - { - SIZE_T size = (lstrlenW(name) + 1) * sizeof(WCHAR); - ret->lpwstrName = CoTaskMemAlloc(size); - if (ret->lpwstrName) - memcpy(ret->lpwstrName, name, size); - } - ret++; - count++; - } - - if (fetched) - *fetched = count; - - return count < celt ? S_FALSE : S_OK; -} - -static HRESULT WINAPI enum_stat_prop_stg_Skip(IEnumSTATPROPSTG *iface, ULONG celt) -{ - FIXME("%p, %lu.\n", iface, celt); - - return S_OK; -} - -static HRESULT WINAPI enum_stat_prop_stg_Reset(IEnumSTATPROPSTG *iface) -{ - struct enum_stat_prop_stg *penum = impl_from_IEnumSTATPROPSTG(iface); - - TRACE("%p.\n", iface); - - penum->current = ~0u; - - return S_OK; -} - -static HRESULT WINAPI enum_stat_prop_stg_Clone(IEnumSTATPROPSTG *iface, IEnumSTATPROPSTG **ppenum) -{ - FIXME("%p, %p.\n", iface, ppenum); - - return E_NOTIMPL; -} - -static const IEnumSTATPROPSTGVtbl enum_stat_prop_stg_vtbl = -{ - enum_stat_prop_stg_QueryInterface, - enum_stat_prop_stg_AddRef, - enum_stat_prop_stg_Release, - enum_stat_prop_stg_Next, - enum_stat_prop_stg_Skip, - enum_stat_prop_stg_Reset, - enum_stat_prop_stg_Clone, -}; - -static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg) -{ - struct enum_stat_prop_stg *stg = arg; - PROPID propid = PtrToUlong(k); - const PROPVARIANT *prop = v; - STATPROPSTG *dest; - - dest = &stg->stats[stg->count]; - - dest->lpwstrName = NULL; - dest->propid = propid; - dest->vt = prop->vt; - stg->count++; - - return TRUE; -} - -static BOOL prop_enum_stat_count(const void *k, const void *v, void *extra, void *arg) -{ - DWORD *count = arg; - - *count += 1; - - return TRUE; -} - -static HRESULT create_enum_stat_prop_stg(PropertyStorage_impl *storage, IEnumSTATPROPSTG **ret) -{ - struct enum_stat_prop_stg *enum_obj; - DWORD count; - - enum_obj = heap_alloc_zero(sizeof(*enum_obj)); - if (!enum_obj) - return E_OUTOFMEMORY; - - enum_obj->IEnumSTATPROPSTG_iface.lpVtbl = &enum_stat_prop_stg_vtbl; - enum_obj->refcount = 1; - enum_obj->storage = storage; - IPropertyStorage_AddRef(&storage->IPropertyStorage_iface); - - count = 0; - dictionary_enumerate(storage->propid_to_prop, prop_enum_stat_count, &count); - - if (count) - { - if (!(enum_obj->stats = heap_alloc(sizeof(*enum_obj->stats) * count))) - { - IEnumSTATPROPSTG_Release(&enum_obj->IEnumSTATPROPSTG_iface); - return E_OUTOFMEMORY; - } - - dictionary_enumerate(storage->propid_to_prop, prop_enum_stat, enum_obj); - } - - *ret = &enum_obj->IEnumSTATPROPSTG_iface; - - return S_OK; -} - -/************************************************************************ - * IPropertyStorage_fnQueryInterface (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnQueryInterface( - IPropertyStorage *iface, - REFIID riid, - void** ppvObject) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - - TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject); - - if (!ppvObject) - return E_INVALIDARG; - - *ppvObject = 0; - - if (IsEqualGUID(&IID_IUnknown, riid) || - IsEqualGUID(&IID_IPropertyStorage, riid)) - { - IPropertyStorage_AddRef(iface); - *ppvObject = iface; - return S_OK; - } - - return E_NOINTERFACE; -} - -/************************************************************************ - * IPropertyStorage_fnAddRef (IPropertyStorage) - */ -static ULONG WINAPI IPropertyStorage_fnAddRef( - IPropertyStorage *iface) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - return InterlockedIncrement(&This->ref); -} - -/************************************************************************ - * IPropertyStorage_fnRelease (IPropertyStorage) - */ -static ULONG WINAPI IPropertyStorage_fnRelease( - IPropertyStorage *iface) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - ULONG ref; - - ref = InterlockedDecrement(&This->ref); - if (ref == 0) - { - TRACE("Destroying %p\n", This); - if (This->dirty) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - IStream_Release(This->stm); - This->cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&This->cs); - PropertyStorage_DestroyDictionaries(This); - HeapFree(GetProcessHeap(), 0, This); - } - return ref; -} - -static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This, - DWORD propid) -{ - PROPVARIANT *ret = NULL; - - dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret); - TRACE("returning %p\n", ret); - return ret; -} - -/* Returns NULL if name is NULL. */ -static PROPVARIANT *PropertyStorage_FindPropertyByName( - PropertyStorage_impl *This, LPCWSTR name) -{ - PROPVARIANT *ret = NULL; - void *propid; - - if (!name) - return NULL; - if (This->codePage == CP_UNICODE) - { - if (dictionary_find(This->name_to_propid, name, &propid)) - ret = PropertyStorage_FindProperty(This, PtrToUlong(propid)); - } - else - { - LPSTR ansiName; - HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE, - &ansiName, This->codePage); - - if (SUCCEEDED(hr)) - { - if (dictionary_find(This->name_to_propid, ansiName, &propid)) - ret = PropertyStorage_FindProperty(This, PtrToUlong(propid)); - CoTaskMemFree(ansiName); - } - } - TRACE("returning %p\n", ret); - return ret; -} - -static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This, - DWORD propid) -{ - LPWSTR ret = NULL; - - dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret); - TRACE("returning %p\n", ret); - return ret; -} - -/************************************************************************ - * IPropertyStorage_fnReadMultiple (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnReadMultiple( - IPropertyStorage* iface, - ULONG cpspec, - const PROPSPEC rgpspec[], - PROPVARIANT rgpropvar[]) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - HRESULT hr = S_OK; - ULONG i; - - TRACE("%p, %lu, %p, %p\n", iface, cpspec, rgpspec, rgpropvar); - - if (!cpspec) - return S_FALSE; - if (!rgpspec || !rgpropvar) - return E_INVALIDARG; - EnterCriticalSection(&This->cs); - for (i = 0; i < cpspec; i++) - { - PropVariantInit(&rgpropvar[i]); - if (rgpspec[i].ulKind == PRSPEC_LPWSTR) - { - PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This, - rgpspec[i].lpwstr); - - if (prop) - PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(), - This->codePage); - } - else - { - switch (rgpspec[i].propid) - { - case PID_CODEPAGE: - rgpropvar[i].vt = VT_I2; - rgpropvar[i].iVal = This->codePage; - break; - case PID_LOCALE: - rgpropvar[i].vt = VT_I4; - rgpropvar[i].lVal = This->locale; - break; - default: - { - PROPVARIANT *prop = PropertyStorage_FindProperty(This, - rgpspec[i].propid); - - if (prop) - PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, - GetACP(), This->codePage); - else - hr = S_FALSE; - } - } - } - } - LeaveCriticalSection(&This->cs); - return hr; -} - -static HRESULT PropertyStorage_StringCopy(LPCSTR src, UINT srcCP, LPSTR *dst, UINT dstCP) -{ - HRESULT hr = S_OK; - int len; - - TRACE("%s, %p, %d, %d\n", - srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst, - dstCP, srcCP); - assert(src); - assert(dst); - *dst = NULL; - if (dstCP == srcCP) - { - size_t len; - - if (dstCP == CP_UNICODE) - len = (lstrlenW((LPCWSTR)src) + 1) * sizeof(WCHAR); - else - len = strlen(src) + 1; - *dst = CoTaskMemAlloc(len); - if (!*dst) - hr = STG_E_INSUFFICIENTMEMORY; - else - memcpy(*dst, src, len); - } - else - { - if (dstCP == CP_UNICODE) - { - len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0); - *dst = CoTaskMemAlloc(len * sizeof(WCHAR)); - if (!*dst) - hr = STG_E_INSUFFICIENTMEMORY; - else - MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len); - } - else - { - LPCWSTR wideStr = NULL; - LPWSTR wideStr_tmp = NULL; - - if (srcCP == CP_UNICODE) - wideStr = (LPCWSTR)src; - else - { - len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0); - wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - if (wideStr_tmp) - { - MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len); - wideStr = wideStr_tmp; - } - else - hr = STG_E_INSUFFICIENTMEMORY; - } - if (SUCCEEDED(hr)) - { - len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0, - NULL, NULL); - *dst = CoTaskMemAlloc(len); - if (!*dst) - hr = STG_E_INSUFFICIENTMEMORY; - else - { - BOOL defCharUsed = FALSE; - - if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len, - NULL, &defCharUsed) == 0 || defCharUsed) - { - CoTaskMemFree(*dst); - *dst = NULL; - hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION); - } - } - } - HeapFree(GetProcessHeap(), 0, wideStr_tmp); - } - } - TRACE("returning %#lx (%s)\n", hr, - dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst)); - return hr; -} - -static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop, const PROPVARIANT *propvar, - UINT targetCP, UINT srcCP) -{ - HRESULT hr = S_OK; - - assert(prop); - assert(propvar); - - switch (propvar->vt) - { - case VT_LPSTR: - hr = PropertyStorage_StringCopy(propvar->pszVal, srcCP, &prop->pszVal, targetCP); - if (SUCCEEDED(hr)) - prop->vt = VT_LPSTR; - break; - case VT_BSTR: - if ((prop->bstrVal = SysAllocStringLen(propvar->bstrVal, SysStringLen(propvar->bstrVal)))) - prop->vt = VT_BSTR; - else - hr = E_OUTOFMEMORY; - break; - default: - hr = PropVariantCopy(prop, propvar); - } - - return hr; -} - -/* Stores the property with id propid and value propvar into this property - * storage. lcid is ignored if propvar's type is not VT_LPSTR. If propvar's - * type is VT_LPSTR, converts the string using lcid as the source code page - * and This->codePage as the target code page before storing. - * As a side effect, may change This->format to 1 if the type of propvar is - * a version 1-only property. - */ -static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This, - PROPID propid, const PROPVARIANT *propvar, UINT cp) -{ - HRESULT hr = S_OK; - PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid); - - assert(propvar); - if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY) - This->format = 1; - switch (propvar->vt) - { - case VT_DECIMAL: - case VT_I1: - case VT_INT: - case VT_UINT: - case VT_VECTOR|VT_I1: - This->format = 1; - } - TRACE("Setting %#lx to type %d\n", propid, propvar->vt); - if (prop) - { - PropVariantClear(prop); - hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp); - } - else - { - prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - sizeof(PROPVARIANT)); - if (prop) - { - hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage, cp); - if (SUCCEEDED(hr)) - { - dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop); - if (propid > This->highestProp) - This->highestProp = propid; - } - else - HeapFree(GetProcessHeap(), 0, prop); - } - else - hr = STG_E_INSUFFICIENTMEMORY; - } - return hr; -} - -/* Adds the name srcName to the name dictionaries, mapped to property ID id. - * srcName is encoded in code page cp, and is converted to This->codePage. - * If cp is CP_UNICODE, srcName is actually a unicode string. - * As a side effect, may change This->format to 1 if srcName is too long for - * a version 0 property storage. - * Doesn't validate id. - */ -static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This, - LPCSTR srcName, UINT cp, PROPID id) -{ - LPSTR name; - HRESULT hr; - - assert(srcName); - - hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage); - if (SUCCEEDED(hr)) - { - if (This->codePage == CP_UNICODE) - { - if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH) - This->format = 1; - } - else - { - if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH) - This->format = 1; - } - TRACE("Adding prop name %s, propid %ld\n", - This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) : - debugstr_a(name), id); - dictionary_insert(This->name_to_propid, name, UlongToPtr(id)); - dictionary_insert(This->propid_to_name, UlongToPtr(id), name); - } - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnWriteMultiple (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnWriteMultiple( - IPropertyStorage* iface, - ULONG cpspec, - const PROPSPEC rgpspec[], - const PROPVARIANT rgpropvar[], - PROPID propidNameFirst) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - HRESULT hr = S_OK; - ULONG i; - - TRACE("%p, %lu, %p, %p.\n", iface, cpspec, rgpspec, rgpropvar); - - if (cpspec && (!rgpspec || !rgpropvar)) - return E_INVALIDARG; - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - EnterCriticalSection(&This->cs); - This->dirty = TRUE; - This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()), - PROPSETHDR_OSVER_KIND_WIN32) ; - for (i = 0; i < cpspec; i++) - { - if (rgpspec[i].ulKind == PRSPEC_LPWSTR) - { - PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This, - rgpspec[i].lpwstr); - - if (prop) - PropVariantCopy(prop, &rgpropvar[i]); - else - { - /* Note that I don't do the special cases here that I do below, - * because naming the special PIDs isn't supported. - */ - if (propidNameFirst < PID_FIRST_USABLE || - propidNameFirst >= PID_MIN_READONLY) - hr = STG_E_INVALIDPARAMETER; - else - { - PROPID nextId = max(propidNameFirst, This->highestProp + 1); - - hr = PropertyStorage_StoreNameWithId(This, - (LPCSTR)rgpspec[i].lpwstr, CP_UNICODE, nextId); - if (SUCCEEDED(hr)) - hr = PropertyStorage_StorePropWithId(This, nextId, - &rgpropvar[i], GetACP()); - } - } - } - else - { - switch (rgpspec[i].propid) - { - case PID_DICTIONARY: - /* Can't set the dictionary */ - hr = STG_E_INVALIDPARAMETER; - break; - case PID_CODEPAGE: - /* Can only set the code page if nothing else has been set */ - if (dictionary_num_entries(This->propid_to_prop) == 0 && - rgpropvar[i].vt == VT_I2) - { - This->codePage = (USHORT)rgpropvar[i].iVal; - if (This->codePage == CP_UNICODE) - This->grfFlags &= ~PROPSETFLAG_ANSI; - else - This->grfFlags |= PROPSETFLAG_ANSI; - } - else - hr = STG_E_INVALIDPARAMETER; - break; - case PID_LOCALE: - /* Can only set the locale if nothing else has been set */ - if (dictionary_num_entries(This->propid_to_prop) == 0 && - rgpropvar[i].vt == VT_I4) - This->locale = rgpropvar[i].lVal; - else - hr = STG_E_INVALIDPARAMETER; - break; - case PID_ILLEGAL: - /* silently ignore like MSDN says */ - break; - default: - if (rgpspec[i].propid >= PID_MIN_READONLY) - hr = STG_E_INVALIDPARAMETER; - else - hr = PropertyStorage_StorePropWithId(This, - rgpspec[i].propid, &rgpropvar[i], GetACP()); - } - } - } - if (This->grfFlags & PROPSETFLAG_UNBUFFERED) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnDeleteMultiple (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple( - IPropertyStorage* iface, - ULONG cpspec, - const PROPSPEC rgpspec[]) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - ULONG i; - HRESULT hr; - - TRACE("%p, %ld, %p.\n", iface, cpspec, rgpspec); - - if (cpspec && !rgpspec) - return E_INVALIDARG; - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - hr = S_OK; - EnterCriticalSection(&This->cs); - This->dirty = TRUE; - for (i = 0; i < cpspec; i++) - { - if (rgpspec[i].ulKind == PRSPEC_LPWSTR) - { - void *propid; - - if (dictionary_find(This->name_to_propid, rgpspec[i].lpwstr, &propid)) - dictionary_remove(This->propid_to_prop, propid); - } - else - { - if (rgpspec[i].propid >= PID_FIRST_USABLE && - rgpspec[i].propid < PID_MIN_READONLY) - dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].propid)); - else - hr = STG_E_INVALIDPARAMETER; - } - } - if (This->grfFlags & PROPSETFLAG_UNBUFFERED) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnReadPropertyNames (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames( - IPropertyStorage* iface, - ULONG cpropid, - const PROPID rgpropid[], - LPOLESTR rglpwstrName[]) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - ULONG i; - HRESULT hr = S_FALSE; - - TRACE("%p, %ld, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName); - - if (cpropid && (!rgpropid || !rglpwstrName)) - return E_INVALIDARG; - EnterCriticalSection(&This->cs); - for (i = 0; i < cpropid && SUCCEEDED(hr); i++) - { - LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]); - - if (name) - { - size_t len = lstrlenW(name); - - hr = S_OK; - rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR)); - if (rglpwstrName[i]) - memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR)); - else - hr = STG_E_INSUFFICIENTMEMORY; - } - else - rglpwstrName[i] = NULL; - } - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnWritePropertyNames (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames( - IPropertyStorage* iface, - ULONG cpropid, - const PROPID rgpropid[], - const LPOLESTR rglpwstrName[]) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - ULONG i; - HRESULT hr; - - TRACE("%p, %lu, %p, %p.\n", iface, cpropid, rgpropid, rglpwstrName); - - if (cpropid && (!rgpropid || !rglpwstrName)) - return E_INVALIDARG; - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - hr = S_OK; - EnterCriticalSection(&This->cs); - This->dirty = TRUE; - for (i = 0; SUCCEEDED(hr) && i < cpropid; i++) - { - if (rgpropid[i] != PID_ILLEGAL) - hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i], - CP_UNICODE, rgpropid[i]); - } - if (This->grfFlags & PROPSETFLAG_UNBUFFERED) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames( - IPropertyStorage* iface, - ULONG cpropid, - const PROPID rgpropid[]) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - ULONG i; - HRESULT hr; - - TRACE("%p, %ld, %p.\n", iface, cpropid, rgpropid); - - if (cpropid && !rgpropid) - return E_INVALIDARG; - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - hr = S_OK; - EnterCriticalSection(&This->cs); - This->dirty = TRUE; - for (i = 0; i < cpropid; i++) - { - LPWSTR name = NULL; - - if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name)) - { - dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i])); - dictionary_remove(This->name_to_propid, name); - } - } - if (This->grfFlags & PROPSETFLAG_UNBUFFERED) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnCommit (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnCommit( - IPropertyStorage* iface, - DWORD grfCommitFlags) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - HRESULT hr; - - TRACE("%p, %#lx.\n", iface, grfCommitFlags); - - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - EnterCriticalSection(&This->cs); - if (This->dirty) - hr = PropertyStorage_WriteToStream(This); - else - hr = S_OK; - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnRevert (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnRevert( - IPropertyStorage* iface) -{ - HRESULT hr; - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - - TRACE("%p\n", iface); - - EnterCriticalSection(&This->cs); - if (This->dirty) - { - PropertyStorage_DestroyDictionaries(This); - hr = PropertyStorage_CreateDictionaries(This); - if (SUCCEEDED(hr)) - hr = PropertyStorage_ReadFromStream(This); - } - else - hr = S_OK; - LeaveCriticalSection(&This->cs); - return hr; -} - -/************************************************************************ - * IPropertyStorage_fnEnum (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnEnum(IPropertyStorage *iface, IEnumSTATPROPSTG **ppenum) -{ - PropertyStorage_impl *storage = impl_from_IPropertyStorage(iface); - - TRACE("%p, %p.\n", iface, ppenum); - - return create_enum_stat_prop_stg(storage, ppenum); -} - -/************************************************************************ - * IPropertyStorage_fnSetTimes (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnSetTimes( - IPropertyStorage* iface, - const FILETIME* pctime, - const FILETIME* patime, - const FILETIME* pmtime) -{ - FIXME("\n"); - return E_NOTIMPL; -} - -/************************************************************************ - * IPropertyStorage_fnSetClass (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnSetClass( - IPropertyStorage* iface, - REFCLSID clsid) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - - TRACE("%p, %s\n", iface, debugstr_guid(clsid)); - - if (!clsid) - return E_INVALIDARG; - if (!(This->grfMode & STGM_READWRITE)) - return STG_E_ACCESSDENIED; - This->clsid = *clsid; - This->dirty = TRUE; - if (This->grfFlags & PROPSETFLAG_UNBUFFERED) - IPropertyStorage_Commit(iface, STGC_DEFAULT); - return S_OK; -} - -/************************************************************************ - * IPropertyStorage_fnStat (IPropertyStorage) - */ -static HRESULT WINAPI IPropertyStorage_fnStat( - IPropertyStorage* iface, - STATPROPSETSTG* statpsstg) -{ - PropertyStorage_impl *This = impl_from_IPropertyStorage(iface); - STATSTG stat; - HRESULT hr; - - TRACE("%p, %p\n", iface, statpsstg); - - if (!statpsstg) - return E_INVALIDARG; - - hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME); - if (SUCCEEDED(hr)) - { - statpsstg->fmtid = This->fmtid; - statpsstg->clsid = This->clsid; - statpsstg->grfFlags = This->grfFlags; - statpsstg->mtime = stat.mtime; - statpsstg->ctime = stat.ctime; - statpsstg->atime = stat.atime; - statpsstg->dwOSVersion = This->originatorOS; - } - return hr; -} - -static int PropertyStorage_PropNameCompare(const void *a, const void *b, - void *extra) -{ - PropertyStorage_impl *This = extra; - - if (This->codePage == CP_UNICODE) - { - TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b)); - if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) - return wcscmp(a, b); - else - return lstrcmpiW(a, b); - } - else - { - TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b)); - if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) - return lstrcmpA(a, b); - else - return lstrcmpiA(a, b); - } -} - -static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra) -{ - CoTaskMemFree(k); -} - -static int PropertyStorage_PropCompare(const void *a, const void *b, - void *extra) -{ - TRACE("%lu, %lu.\n", PtrToUlong(a), PtrToUlong(b)); - return PtrToUlong(a) - PtrToUlong(b); -} - -static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra) -{ - PropVariantClear(d); - HeapFree(GetProcessHeap(), 0, d); -} - -#ifdef WORDS_BIGENDIAN -/* Swaps each character in str to or from little endian; assumes the conversion - * is symmetric, that is, that lendian16toh is equivalent to htole16. - */ -static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len) -{ - DWORD i; - - /* Swap characters to host order. - * FIXME: alignment? - */ - for (i = 0; i < len; i++) - str[i] = lendian16toh(str[i]); -} -#else -#define PropertyStorage_ByteSwapString(s, l) -#endif - -static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size) -{ - return CoTaskMemAlloc(size); -} - -struct read_buffer -{ - BYTE *data; - size_t size; -}; - -static HRESULT buffer_test_offset(const struct read_buffer *buffer, size_t offset, size_t len) -{ - return len > buffer->size || offset > buffer->size - len ? STG_E_READFAULT : S_OK; -} - -static HRESULT buffer_read_uint64(const struct read_buffer *buffer, size_t offset, ULARGE_INTEGER *data) -{ - HRESULT hr; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) - StorageUtl_ReadULargeInteger(buffer->data, offset, data); - - return hr; -} - -static HRESULT buffer_read_dword(const struct read_buffer *buffer, size_t offset, DWORD *data) -{ - HRESULT hr; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) - StorageUtl_ReadDWord(buffer->data, offset, data); - - return hr; -} - -static HRESULT buffer_read_word(const struct read_buffer *buffer, size_t offset, WORD *data) -{ - HRESULT hr; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) - StorageUtl_ReadWord(buffer->data, offset, data); - - return hr; -} - -static HRESULT buffer_read_byte(const struct read_buffer *buffer, size_t offset, BYTE *data) -{ - HRESULT hr; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*data)))) - *data = *(buffer->data + offset); - - return hr; -} - -static HRESULT buffer_read_len(const struct read_buffer *buffer, size_t offset, void *dest, size_t len) -{ - HRESULT hr; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, len))) - memcpy(dest, buffer->data + offset, len); - - return hr; -} - -static HRESULT propertystorage_read_scalar(PROPVARIANT *prop, const struct read_buffer *buffer, size_t offset, - UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data) -{ - HRESULT hr; - - assert(!(prop->vt & (VT_ARRAY | VT_VECTOR))); - - switch (prop->vt) - { - case VT_EMPTY: - case VT_NULL: - hr = S_OK; - break; - case VT_I1: - hr = buffer_read_byte(buffer, offset, (BYTE *)&prop->cVal); - TRACE("Read char 0x%x ('%c')\n", prop->cVal, prop->cVal); - break; - case VT_UI1: - hr = buffer_read_byte(buffer, offset, &prop->bVal); - TRACE("Read byte 0x%x\n", prop->bVal); - break; - case VT_BOOL: - hr = buffer_read_word(buffer, offset, (WORD *)&prop->boolVal); - TRACE("Read bool %d\n", prop->boolVal); - break; - case VT_I2: - hr = buffer_read_word(buffer, offset, (WORD *)&prop->iVal); - TRACE("Read short %d\n", prop->iVal); - break; - case VT_UI2: - hr = buffer_read_word(buffer, offset, &prop->uiVal); - TRACE("Read ushort %d\n", prop->uiVal); - break; - case VT_INT: - case VT_I4: - hr = buffer_read_dword(buffer, offset, (DWORD *)&prop->lVal); - TRACE("Read long %ld\n", prop->lVal); - break; - case VT_UINT: - case VT_UI4: - hr = buffer_read_dword(buffer, offset, &prop->ulVal); - TRACE("Read ulong %ld\n", prop->ulVal); - break; - case VT_I8: - hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->hVal); - TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->hVal.QuadPart)); - break; - case VT_UI8: - hr = buffer_read_uint64(buffer, offset, &prop->uhVal); - TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->uhVal.QuadPart)); - break; - case VT_R8: - hr = buffer_read_len(buffer, offset, &prop->dblVal, sizeof(prop->dblVal)); - TRACE("Read double %f\n", prop->dblVal); - break; - case VT_LPSTR: - { - DWORD count; - - if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) - break; - - offset += sizeof(DWORD); - - if (codepage == CP_UNICODE && count % sizeof(WCHAR)) - { - WARN("Unicode string has odd number of bytes\n"); - hr = STG_E_INVALIDHEADER; - } - else - { - prop->pszVal = allocate(allocate_data, count); - if (prop->pszVal) - { - if (FAILED(hr = buffer_read_len(buffer, offset, prop->pszVal, count))) - break; - - /* This is stored in the code page specified in codepage. - * Don't convert it, the caller will just store it as-is. - */ - if (codepage == CP_UNICODE) - { - /* Make sure it's NULL-terminated */ - prop->pszVal[count / sizeof(WCHAR) - 1] = '\0'; - TRACE("Read string value %s\n", - debugstr_w(prop->pwszVal)); - } - else - { - /* Make sure it's NULL-terminated */ - prop->pszVal[count - 1] = '\0'; - TRACE("Read string value %s\n", debugstr_a(prop->pszVal)); - } - } - else - hr = STG_E_INSUFFICIENTMEMORY; - } - break; - } - case VT_BSTR: - { - DWORD count, wcount; - - if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) - break; - - offset += sizeof(DWORD); - - if (codepage == CP_UNICODE && count % sizeof(WCHAR)) - { - WARN("Unicode string has odd number of bytes\n"); - hr = STG_E_INVALIDHEADER; - } - else - { - if (codepage == CP_UNICODE) - wcount = count / sizeof(WCHAR); - else - { - if (FAILED(hr = buffer_test_offset(buffer, offset, count))) - break; - wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, NULL, 0); - } - - prop->bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */ - - if (prop->bstrVal) - { - if (codepage == CP_UNICODE) - hr = buffer_read_len(buffer, offset, prop->bstrVal, count); - else - MultiByteToWideChar(codepage, 0, (LPCSTR)(buffer->data + offset), count, prop->bstrVal, wcount); - - prop->bstrVal[wcount - 1] = '\0'; - TRACE("Read string value %s\n", debugstr_w(prop->bstrVal)); - } - else - hr = STG_E_INSUFFICIENTMEMORY; - } - break; - } - case VT_BLOB: - { - DWORD count; - - if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) - break; - - offset += sizeof(DWORD); - - prop->blob.cbSize = count; - prop->blob.pBlobData = allocate(allocate_data, count); - if (prop->blob.pBlobData) - { - hr = buffer_read_len(buffer, offset, prop->blob.pBlobData, count); - TRACE("Read blob value of size %ld\n", count); - } - else - hr = STG_E_INSUFFICIENTMEMORY; - break; - } - case VT_LPWSTR: - { - DWORD count; - - if (FAILED(hr = buffer_read_dword(buffer, offset, &count))) - break; - - offset += sizeof(DWORD); - - prop->pwszVal = allocate(allocate_data, count * sizeof(WCHAR)); - if (prop->pwszVal) - { - if (SUCCEEDED(hr = buffer_read_len(buffer, offset, prop->pwszVal, count * sizeof(WCHAR)))) - { - /* make sure string is NULL-terminated */ - prop->pwszVal[count - 1] = '\0'; - PropertyStorage_ByteSwapString(prop->pwszVal, count); - TRACE("Read string value %s\n", debugstr_w(prop->pwszVal)); - } - } - else - hr = STG_E_INSUFFICIENTMEMORY; - break; - } - case VT_FILETIME: - hr = buffer_read_uint64(buffer, offset, (ULARGE_INTEGER *)&prop->filetime); - break; - case VT_CF: - { - DWORD len = 0, tag = 0; - - if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &len))) - hr = buffer_read_dword(buffer, offset + sizeof(DWORD), &tag); - if (FAILED(hr)) - break; - - offset += 2 * sizeof(DWORD); - - if (len > 8) - { - len -= 8; - prop->pclipdata = allocate(allocate_data, sizeof (CLIPDATA)); - prop->pclipdata->cbSize = len; - prop->pclipdata->ulClipFmt = tag; - prop->pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->pclipdata->ulClipFmt)); - hr = buffer_read_len(buffer, offset, prop->pclipdata->pClipData, len - sizeof(prop->pclipdata->ulClipFmt)); - } - else - hr = STG_E_INVALIDPARAMETER; - } - break; - case VT_CLSID: - if (!(prop->puuid = allocate(allocate_data, sizeof (*prop->puuid)))) - return STG_E_INSUFFICIENTMEMORY; - - if (SUCCEEDED(hr = buffer_test_offset(buffer, offset, sizeof(*prop->puuid)))) - StorageUtl_ReadGUID(buffer->data, offset, prop->puuid); - - break; - default: - FIXME("unsupported type %d\n", prop->vt); - hr = STG_E_INVALIDPARAMETER; - } - - return hr; -} - -static size_t propertystorage_get_elemsize(const PROPVARIANT *prop) -{ - if (!(prop->vt & VT_VECTOR)) - return 0; - - switch (prop->vt & ~VT_VECTOR) - { - case VT_I1: return sizeof(*prop->cac.pElems); - case VT_UI1: return sizeof(*prop->caub.pElems); - case VT_I2: return sizeof(*prop->cai.pElems); - case VT_UI2: return sizeof(*prop->caui.pElems); - case VT_BOOL: return sizeof(*prop->cabool.pElems); - case VT_I4: return sizeof(*prop->cal.pElems); - case VT_UI4: return sizeof(*prop->caul.pElems); - case VT_R4: return sizeof(*prop->caflt.pElems); - case VT_ERROR: return sizeof(*prop->cascode.pElems); - case VT_I8: return sizeof(*prop->cah.pElems); - case VT_UI8: return sizeof(*prop->cauh.pElems); - case VT_R8: return sizeof(*prop->cadbl.pElems); - case VT_CY: return sizeof(*prop->cacy.pElems); - case VT_DATE: return sizeof(*prop->cadate.pElems); - case VT_FILETIME: return sizeof(*prop->cafiletime.pElems); - case VT_CLSID: return sizeof(*prop->cauuid.pElems); - case VT_VARIANT: return sizeof(*prop->capropvar.pElems); - default: - FIXME("Unhandled type %#x.\n", prop->vt); - return 0; - } -} - -static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const struct read_buffer *buffer, - size_t offset, UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data) -{ - HRESULT hr; - DWORD vt; - - assert(prop); - assert(buffer->data); - - if (FAILED(hr = buffer_read_dword(buffer, offset, &vt))) - return hr; - - offset += sizeof(DWORD); - prop->vt = vt; - - if (prop->vt & VT_VECTOR) - { - DWORD count, i; - - switch (prop->vt & VT_VECTOR) - { - case VT_BSTR: - case VT_VARIANT: - case VT_LPSTR: - case VT_LPWSTR: - case VT_CF: - FIXME("Vector with variable length elements are not supported.\n"); - return STG_E_INVALIDPARAMETER; - default: - ; - } - - if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &count))) - { - size_t elemsize = propertystorage_get_elemsize(prop); - PROPVARIANT elem; - - offset += sizeof(DWORD); - - if ((prop->capropvar.pElems = allocate(allocate_data, elemsize * count))) - { - prop->capropvar.cElems = count; - elem.vt = prop->vt & ~VT_VECTOR; - - for (i = 0; i < count; ++i) - { - if (SUCCEEDED(hr = propertystorage_read_scalar(&elem, buffer, offset + i * elemsize, codepage, - allocate, allocate_data))) - { - memcpy(&prop->capropvar.pElems[i], &elem.lVal, elemsize); - } - } - } - else - hr = STG_E_INSUFFICIENTMEMORY; - } - } - else if (prop->vt & VT_ARRAY) - { - FIXME("VT_ARRAY properties are not supported.\n"); - hr = STG_E_INVALIDPARAMETER; - } - else - hr = propertystorage_read_scalar(prop, buffer, offset, codepage, allocate, allocate_data); - - return hr; -} - -static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm, - PROPERTYSETHEADER *hdr) -{ - BYTE buf[sizeof(PROPERTYSETHEADER)]; - ULONG count = 0; - HRESULT hr; - - assert(stm); - assert(hdr); - hr = IStream_Read(stm, buf, sizeof(buf), &count); - if (SUCCEEDED(hr)) - { - if (count != sizeof(buf)) - { - WARN("read only %ld\n", count); - hr = STG_E_INVALIDHEADER; - } - else - { - StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder), - &hdr->wByteOrder); - StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat), - &hdr->wFormat); - StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer), - &hdr->dwOSVer); - StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid), - &hdr->clsid); - StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved), - &hdr->reserved); - } - } - TRACE("returning %#lx\n", hr); - return hr; -} - -static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm, - FORMATIDOFFSET *fmt) -{ - BYTE buf[sizeof(FORMATIDOFFSET)]; - ULONG count = 0; - HRESULT hr; - - assert(stm); - assert(fmt); - hr = IStream_Read(stm, buf, sizeof(buf), &count); - if (SUCCEEDED(hr)) - { - if (count != sizeof(buf)) - { - WARN("read only %ld\n", count); - hr = STG_E_INVALIDHEADER; - } - else - { - StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid), - &fmt->fmtid); - StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset), - &fmt->dwOffset); - } - } - TRACE("returning %#lx\n", hr); - return hr; -} - -static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm, - PROPERTYSECTIONHEADER *hdr) -{ - BYTE buf[sizeof(PROPERTYSECTIONHEADER)]; - ULONG count = 0; - HRESULT hr; - - assert(stm); - assert(hdr); - hr = IStream_Read(stm, buf, sizeof(buf), &count); - if (SUCCEEDED(hr)) - { - if (count != sizeof(buf)) - { - WARN("read only %ld\n", count); - hr = STG_E_INVALIDHEADER; - } - else - { - StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, - cbSection), &hdr->cbSection); - StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER, - cProperties), &hdr->cProperties); - } - } - TRACE("returning %#lx\n", hr); - return hr; -} - -/* Reads the dictionary from the memory buffer beginning at ptr. Interprets - * the entries according to the values of This->codePage and This->locale. - */ -static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This, const struct read_buffer *buffer, - size_t offset) -{ - DWORD numEntries, i; - HRESULT hr; - - assert(This->name_to_propid); - assert(This->propid_to_name); - - if (FAILED(hr = buffer_read_dword(buffer, offset, &numEntries))) - return hr; - - TRACE("Reading %ld entries:\n", numEntries); - - offset += sizeof(DWORD); - - for (i = 0; SUCCEEDED(hr) && i < numEntries; i++) - { - PROPID propid; - DWORD cbEntry; - WCHAR ch = 0; - - if (SUCCEEDED(hr = buffer_read_dword(buffer, offset, &propid))) - hr = buffer_read_dword(buffer, offset + sizeof(PROPID), &cbEntry); - if (FAILED(hr)) - break; - - offset += sizeof(PROPID) + sizeof(DWORD); - - if (FAILED(hr = buffer_test_offset(buffer, offset, This->codePage == CP_UNICODE ? - ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3) : cbEntry))) - { - WARN("Broken name length for entry %ld.\n", i); - return hr; - } - - /* Make sure the source string is NULL-terminated */ - if (This->codePage != CP_UNICODE) - buffer_read_byte(buffer, offset + cbEntry - 1, (BYTE *)&ch); - else - buffer_read_word(buffer, offset + (cbEntry - 1) * sizeof(WCHAR), &ch); - - if (ch) - { - WARN("Dictionary entry name %ld is not null-terminated.\n", i); - return E_FAIL; - } - - TRACE("Reading entry with ID %#lx, %ld chars, name %s.\n", propid, cbEntry, This->codePage == CP_UNICODE ? - debugstr_wn((WCHAR *)buffer->data, cbEntry) : debugstr_an((char *)buffer->data, cbEntry)); - - hr = PropertyStorage_StoreNameWithId(This, (char *)buffer->data + offset, This->codePage, propid); - /* Unicode entries are padded to DWORD boundaries */ - if (This->codePage == CP_UNICODE) - cbEntry = ALIGNED_LENGTH(cbEntry * sizeof(WCHAR), 3); - - offset += cbEntry; - } - - return hr; -} - -static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This) -{ - struct read_buffer read_buffer; - PROPERTYSETHEADER hdr; - FORMATIDOFFSET fmtOffset; - PROPERTYSECTIONHEADER sectionHdr; - LARGE_INTEGER seek; - ULONG i; - STATSTG stat; - HRESULT hr; - BYTE *buf = NULL; - ULONG count = 0; - DWORD dictOffset = 0; - - This->dirty = FALSE; - This->highestProp = 0; - hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME); - if (FAILED(hr)) - goto end; - if (stat.cbSize.HighPart) - { - WARN("stream too big\n"); - /* maximum size varies, but it can't be this big */ - hr = STG_E_INVALIDHEADER; - goto end; - } - if (stat.cbSize.LowPart == 0) - { - /* empty stream is okay */ - hr = S_OK; - goto end; - } - else if (stat.cbSize.LowPart < sizeof(PROPERTYSETHEADER) + - sizeof(FORMATIDOFFSET)) - { - WARN("stream too small\n"); - hr = STG_E_INVALIDHEADER; - goto end; - } - seek.QuadPart = 0; - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr); - /* I've only seen reserved == 1, but the article says I shouldn't disallow - * higher values. - */ - if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1) - { - WARN("bad magic in prop set header\n"); - hr = STG_E_INVALIDHEADER; - goto end; - } - if (hdr.wFormat != 0 && hdr.wFormat != 1) - { - WARN("bad format version %d\n", hdr.wFormat); - hr = STG_E_INVALIDHEADER; - goto end; - } - This->format = hdr.wFormat; - This->clsid = hdr.clsid; - This->originatorOS = hdr.dwOSVer; - if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC) - WARN("File comes from a Mac, strings will probably be screwed up\n"); - hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset); - if (FAILED(hr)) - goto end; - if (fmtOffset.dwOffset > stat.cbSize.LowPart) - { - WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset, stat.cbSize.LowPart); - hr = STG_E_INVALIDHEADER; - goto end; - } - /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there - * follows not one, but two sections. The first contains the standard properties - * for the document summary information, and the second consists of user-defined - * properties. This is the only case in which multiple sections are - * allowed. - * Reading the second stream isn't implemented yet. - */ - hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr); - if (FAILED(hr)) - goto end; - /* The section size includes the section header, so check it */ - if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER)) - { - WARN("section header too small, got %ld\n", sectionHdr.cbSection); - hr = STG_E_INVALIDHEADER; - goto end; - } - buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection - - sizeof(PROPERTYSECTIONHEADER)); - if (!buf) - { - hr = STG_E_INSUFFICIENTMEMORY; - goto end; - } - read_buffer.data = buf; - read_buffer.size = sectionHdr.cbSection - sizeof(sectionHdr); - - hr = IStream_Read(This->stm, read_buffer.data, read_buffer.size, &count); - if (FAILED(hr)) - goto end; - TRACE("Reading %ld properties:\n", sectionHdr.cProperties); - for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++) - { - PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(read_buffer.data + - i * sizeof(PROPERTYIDOFFSET)); - - if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) || - idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD)) - hr = STG_E_INVALIDPOINTER; - else - { - if (idOffset->propid >= PID_FIRST_USABLE && - idOffset->propid < PID_MIN_READONLY && idOffset->propid > - This->highestProp) - This->highestProp = idOffset->propid; - if (idOffset->propid == PID_DICTIONARY) - { - /* Don't read the dictionary yet, its entries depend on the - * code page. Just store the offset so we know to read it - * later. - */ - dictOffset = idOffset->dwOffset; - TRACE("Dictionary offset is %ld\n", dictOffset); - } - else - { - PROPVARIANT prop; - - PropVariantInit(&prop); - if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, &read_buffer, - idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), This->codePage, - Allocate_CoTaskMemAlloc, NULL))) - { - TRACE("Read property with ID %#lx, type %d\n", idOffset->propid, prop.vt); - switch(idOffset->propid) - { - case PID_CODEPAGE: - if (prop.vt == VT_I2) - This->codePage = (USHORT)prop.iVal; - break; - case PID_LOCALE: - if (prop.vt == VT_I4) - This->locale = (LCID)prop.lVal; - break; - case PID_BEHAVIOR: - if (prop.vt == VT_I4 && prop.lVal) - This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE; - /* The format should already be 1, but just in case */ - This->format = 1; - break; - default: - hr = PropertyStorage_StorePropWithId(This, - idOffset->propid, &prop, This->codePage); - } - } - PropVariantClear(&prop); - } - } - } - if (!This->codePage) - { - /* default to Unicode unless told not to, as specified on msdn */ - if (This->grfFlags & PROPSETFLAG_ANSI) - This->codePage = GetACP(); - else - This->codePage = CP_UNICODE; - } - if (!This->locale) - This->locale = LOCALE_SYSTEM_DEFAULT; - TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale); - if (dictOffset) - hr = PropertyStorage_ReadDictionary(This, &read_buffer, dictOffset - sizeof(PROPERTYSECTIONHEADER)); - -end: - HeapFree(GetProcessHeap(), 0, buf); - if (FAILED(hr)) - { - dictionary_destroy(This->name_to_propid); - This->name_to_propid = NULL; - dictionary_destroy(This->propid_to_name); - This->propid_to_name = NULL; - dictionary_destroy(This->propid_to_prop); - This->propid_to_prop = NULL; - } - return hr; -} - -static void PropertyStorage_MakeHeader(PropertyStorage_impl *This, - PROPERTYSETHEADER *hdr) -{ - assert(hdr); - StorageUtl_WriteWord(&hdr->wByteOrder, 0, PROPSETHDR_BYTEORDER_MAGIC); - StorageUtl_WriteWord(&hdr->wFormat, 0, This->format); - StorageUtl_WriteDWord(&hdr->dwOSVer, 0, This->originatorOS); - StorageUtl_WriteGUID(&hdr->clsid, 0, &This->clsid); - StorageUtl_WriteDWord(&hdr->reserved, 0, 1); -} - -static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This, - FORMATIDOFFSET *fmtOffset) -{ - assert(fmtOffset); - StorageUtl_WriteGUID(fmtOffset, 0, &This->fmtid); - StorageUtl_WriteDWord(fmtOffset, offsetof(FORMATIDOFFSET, dwOffset), - sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)); -} - -static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps, - PROPERTYSECTIONHEADER *hdr) -{ - assert(hdr); - StorageUtl_WriteDWord(hdr, 0, cbSection); - StorageUtl_WriteDWord(hdr, offsetof(PROPERTYSECTIONHEADER, cProperties), numProps); -} - -static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset, - PROPERTYIDOFFSET *propIdOffset) -{ - assert(propIdOffset); - StorageUtl_WriteDWord(propIdOffset, 0, propid); - StorageUtl_WriteDWord(propIdOffset, offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset); -} - -static inline HRESULT PropertyStorage_WriteWStringToStream(IStream *stm, - LPCWSTR str, DWORD len, DWORD *written) -{ -#ifdef WORDS_BIGENDIAN - WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - HRESULT hr; - - if (!leStr) - return E_OUTOFMEMORY; - memcpy(leStr, str, len * sizeof(WCHAR)); - PropertyStorage_ByteSwapString(leStr, len); - hr = IStream_Write(stm, leStr, len, written); - HeapFree(GetProcessHeap(), 0, leStr); - return hr; -#else - return IStream_Write(stm, str, len * sizeof(WCHAR), written); -#endif -} - -struct DictionaryClosure -{ - HRESULT hr; - DWORD bytesWritten; -}; - -static BOOL PropertyStorage_DictionaryWriter(const void *key, - const void *value, void *extra, void *closure) -{ - PropertyStorage_impl *This = extra; - struct DictionaryClosure *c = closure; - DWORD propid, keyLen; - ULONG count; - - assert(key); - assert(closure); - StorageUtl_WriteDWord(&propid, 0, PtrToUlong(value)); - c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count); - if (FAILED(c->hr)) - goto end; - c->bytesWritten += sizeof(DWORD); - if (This->codePage == CP_UNICODE) - { - DWORD pad = 0, pad_len; - - StorageUtl_WriteDWord(&keyLen, 0, lstrlenW((LPCWSTR)key) + 1); - c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); - if (FAILED(c->hr)) - goto end; - c->bytesWritten += sizeof(DWORD); - c->hr = PropertyStorage_WriteWStringToStream(This->stm, key, keyLen, - &count); - if (FAILED(c->hr)) - goto end; - keyLen *= sizeof(WCHAR); - c->bytesWritten += keyLen; - - /* Align to 4 bytes. */ - pad_len = sizeof(DWORD) - keyLen % sizeof(DWORD); - if (pad_len) - { - c->hr = IStream_Write(This->stm, &pad, pad_len, &count); - if (FAILED(c->hr)) - goto end; - c->bytesWritten += pad_len; - } - } - else - { - StorageUtl_WriteDWord(&keyLen, 0, strlen((LPCSTR)key) + 1); - c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count); - if (FAILED(c->hr)) - goto end; - c->bytesWritten += sizeof(DWORD); - c->hr = IStream_Write(This->stm, key, keyLen, &count); - if (FAILED(c->hr)) - goto end; - c->bytesWritten += keyLen; - } -end: - return SUCCEEDED(c->hr); -} - -#define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET) - -/* Writes the dictionary to the stream. Assumes without checking that the - * dictionary isn't empty. - */ -static HRESULT PropertyStorage_WriteDictionaryToStream( - PropertyStorage_impl *This, DWORD *sectionOffset) -{ - HRESULT hr; - LARGE_INTEGER seek; - PROPERTYIDOFFSET propIdOffset; - ULONG count; - DWORD dwTemp; - struct DictionaryClosure closure; - - assert(sectionOffset); - - /* The dictionary's always the first property written, so seek to its - * spot. - */ - seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER); - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset, - &propIdOffset); - hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); - if (FAILED(hr)) - goto end; - - seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - StorageUtl_WriteDWord(&dwTemp, 0, dictionary_num_entries(This->name_to_propid)); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (FAILED(hr)) - goto end; - *sectionOffset += sizeof(dwTemp); - - closure.hr = S_OK; - closure.bytesWritten = 0; - dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter, - &closure); - hr = closure.hr; - if (FAILED(hr)) - goto end; - *sectionOffset += closure.bytesWritten; - if (closure.bytesWritten % sizeof(DWORD)) - { - DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD); - TRACE("adding %ld bytes of padding\n", padding); - *sectionOffset += padding; - } - -end: - return hr; -} - -static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This, - DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset) -{ - DWORD len, dwType, dwTemp, bytesWritten; - HRESULT hr; - LARGE_INTEGER seek; - PROPERTYIDOFFSET propIdOffset; - ULARGE_INTEGER ularge; - ULONG count; - - assert(var); - assert(sectionOffset); - - TRACE("%p, %ld, %#lx, %d, %ld.\n", This, propNum, propid, var->vt, - *sectionOffset); - - seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) + - propNum * sizeof(PROPERTYIDOFFSET); - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset); - hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count); - if (FAILED(hr)) - goto end; - - seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset; - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - StorageUtl_WriteDWord(&dwType, 0, var->vt); - hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count); - if (FAILED(hr)) - goto end; - *sectionOffset += sizeof(dwType); - - switch (var->vt) - { - case VT_EMPTY: - case VT_NULL: - bytesWritten = 0; - break; - case VT_I1: - case VT_UI1: - hr = IStream_Write(This->stm, &var->cVal, sizeof(var->cVal), - &count); - bytesWritten = count; - break; - case VT_I2: - case VT_UI2: - { - WORD wTemp; - - StorageUtl_WriteWord(&wTemp, 0, var->iVal); - hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count); - bytesWritten = count; - break; - } - case VT_I4: - case VT_UI4: - { - StorageUtl_WriteDWord(&dwTemp, 0, var->lVal); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - bytesWritten = count; - break; - } - case VT_I8: - case VT_UI8: - { - StorageUtl_WriteULargeInteger(&ularge, 0, &var->uhVal); - hr = IStream_Write(This->stm, &ularge, sizeof(ularge), &bytesWritten); - break; - } - case VT_LPSTR: - { - if (This->codePage == CP_UNICODE) - len = (lstrlenW(var->pwszVal) + 1) * sizeof(WCHAR); - else - len = lstrlenA(var->pszVal) + 1; - StorageUtl_WriteDWord(&dwTemp, 0, len); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (FAILED(hr)) - goto end; - hr = IStream_Write(This->stm, var->pszVal, len, &count); - bytesWritten = count + sizeof(DWORD); - break; - } - case VT_BSTR: - { - if (This->codePage == CP_UNICODE) - { - len = SysStringByteLen(var->bstrVal) + sizeof(WCHAR); - StorageUtl_WriteDWord(&dwTemp, 0, len); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (SUCCEEDED(hr)) - hr = IStream_Write(This->stm, var->bstrVal, len, &count); - } - else - { - char *str; - - len = WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal) + 1, - NULL, 0, NULL, NULL); - - str = heap_alloc(len); - if (!str) - { - hr = E_OUTOFMEMORY; - goto end; - } - - WideCharToMultiByte(This->codePage, 0, var->bstrVal, SysStringLen(var->bstrVal), - str, len, NULL, NULL); - StorageUtl_WriteDWord(&dwTemp, 0, len); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (SUCCEEDED(hr)) - hr = IStream_Write(This->stm, str, len, &count); - heap_free(str); - } - - bytesWritten = count + sizeof(DWORD); - break; - } - case VT_LPWSTR: - { - len = lstrlenW(var->pwszVal) + 1; - - StorageUtl_WriteDWord(&dwTemp, 0, len); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (FAILED(hr)) - goto end; - hr = IStream_Write(This->stm, var->pwszVal, len * sizeof(WCHAR), - &count); - bytesWritten = count + sizeof(DWORD); - break; - } - case VT_FILETIME: - { - FILETIME temp; - - StorageUtl_WriteULargeInteger(&temp, 0, (const ULARGE_INTEGER *)&var->filetime); - hr = IStream_Write(This->stm, &temp, sizeof(temp), &count); - bytesWritten = count; - break; - } - case VT_CF: - { - DWORD cf_hdr[2]; - - len = var->pclipdata->cbSize; - StorageUtl_WriteDWord(&cf_hdr[0], 0, len + 8); - StorageUtl_WriteDWord(&cf_hdr[1], 0, var->pclipdata->ulClipFmt); - hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count); - if (FAILED(hr)) - goto end; - hr = IStream_Write(This->stm, var->pclipdata->pClipData, - len - sizeof(var->pclipdata->ulClipFmt), &count); - if (FAILED(hr)) - goto end; - bytesWritten = count + sizeof cf_hdr; - break; - } - case VT_CLSID: - { - CLSID temp; - - StorageUtl_WriteGUID(&temp, 0, var->puuid); - hr = IStream_Write(This->stm, &temp, sizeof(temp), &count); - bytesWritten = count; - break; - } - case VT_BLOB: - { - StorageUtl_WriteDWord(&dwTemp, 0, var->blob.cbSize); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - if (FAILED(hr)) - goto end; - hr = IStream_Write(This->stm, var->blob.pBlobData, var->blob.cbSize, &count); - bytesWritten = count + sizeof(DWORD); - break; - } - default: - FIXME("unsupported type: %d\n", var->vt); - return STG_E_INVALIDPARAMETER; - } - - if (SUCCEEDED(hr)) - { - *sectionOffset += bytesWritten; - if (bytesWritten % sizeof(DWORD)) - { - DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD); - TRACE("adding %ld bytes of padding\n", padding); - *sectionOffset += padding; - } - } - -end: - return hr; -} - -struct PropertyClosure -{ - HRESULT hr; - DWORD propNum; - DWORD *sectionOffset; -}; - -static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value, - void *extra, void *closure) -{ - PropertyStorage_impl *This = extra; - struct PropertyClosure *c = closure; - - assert(key); - assert(value); - assert(extra); - assert(closure); - c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++, - PtrToUlong(key), value, c->sectionOffset); - return SUCCEEDED(c->hr); -} - -static HRESULT PropertyStorage_WritePropertiesToStream( - PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset) -{ - struct PropertyClosure closure; - - assert(sectionOffset); - closure.hr = S_OK; - closure.propNum = startingPropNum; - closure.sectionOffset = sectionOffset; - dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter, - &closure); - return closure.hr; -} - -static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This) -{ - HRESULT hr; - ULONG count = 0; - LARGE_INTEGER seek = { {0} }; - PROPERTYSETHEADER hdr; - FORMATIDOFFSET fmtOffset; - - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - PropertyStorage_MakeHeader(This, &hdr); - hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count); - if (FAILED(hr)) - goto end; - if (count != sizeof(hdr)) - { - hr = STG_E_WRITEFAULT; - goto end; - } - - PropertyStorage_MakeFmtIdOffset(This, &fmtOffset); - hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count); - if (FAILED(hr)) - goto end; - if (count != sizeof(fmtOffset)) - { - hr = STG_E_WRITEFAULT; - goto end; - } - hr = S_OK; - -end: - return hr; -} - -static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This) -{ - PROPERTYSECTIONHEADER sectionHdr; - HRESULT hr; - ULONG count; - LARGE_INTEGER seek; - DWORD numProps, prop, sectionOffset, dwTemp; - PROPVARIANT var; - - PropertyStorage_WriteHeadersToStream(This); - - /* Count properties. Always at least one property, the code page */ - numProps = 1; - if (dictionary_num_entries(This->name_to_propid)) - numProps++; - if (This->locale != LOCALE_SYSTEM_DEFAULT) - numProps++; - if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) - numProps++; - numProps += dictionary_num_entries(This->propid_to_prop); - - /* Write section header with 0 bytes right now, I'll adjust it after - * writing properties. - */ - PropertyStorage_MakeSectionHdr(0, numProps, §ionHdr); - seek.QuadPart = SECTIONHEADER_OFFSET; - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - hr = IStream_Write(This->stm, §ionHdr, sizeof(sectionHdr), &count); - if (FAILED(hr)) - goto end; - - prop = 0; - sectionOffset = sizeof(PROPERTYSECTIONHEADER) + - numProps * sizeof(PROPERTYIDOFFSET); - - if (dictionary_num_entries(This->name_to_propid)) - { - prop++; - hr = PropertyStorage_WriteDictionaryToStream(This, §ionOffset); - if (FAILED(hr)) - goto end; - } - - PropVariantInit(&var); - - var.vt = VT_I2; - var.iVal = This->codePage; - hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE, - &var, §ionOffset); - if (FAILED(hr)) - goto end; - - if (This->locale != LOCALE_SYSTEM_DEFAULT) - { - var.vt = VT_I4; - var.lVal = This->locale; - hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE, - &var, §ionOffset); - if (FAILED(hr)) - goto end; - } - - if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE) - { - var.vt = VT_I4; - var.lVal = 1; - hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR, - &var, §ionOffset); - if (FAILED(hr)) - goto end; - } - - hr = PropertyStorage_WritePropertiesToStream(This, prop, §ionOffset); - if (FAILED(hr)) - goto end; - - /* Now write the byte count of the section */ - seek.QuadPart = SECTIONHEADER_OFFSET; - hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - goto end; - StorageUtl_WriteDWord(&dwTemp, 0, sectionOffset); - hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count); - -end: - return hr; -} - -/*********************************************************************** - * PropertyStorage_Construct - */ -static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This) -{ - dictionary_destroy(This->name_to_propid); - This->name_to_propid = NULL; - dictionary_destroy(This->propid_to_name); - This->propid_to_name = NULL; - dictionary_destroy(This->propid_to_prop); - This->propid_to_prop = NULL; -} - -static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This) -{ - HRESULT hr = S_OK; - - This->name_to_propid = dictionary_create( - PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy, - This); - if (!This->name_to_propid) - { - hr = STG_E_INSUFFICIENTMEMORY; - goto end; - } - This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, - NULL, This); - if (!This->propid_to_name) - { - hr = STG_E_INSUFFICIENTMEMORY; - goto end; - } - This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare, - PropertyStorage_PropertyDestroy, This); - if (!This->propid_to_prop) - { - hr = STG_E_INSUFFICIENTMEMORY; - goto end; - } -end: - if (FAILED(hr)) - PropertyStorage_DestroyDictionaries(This); - return hr; -} - -static HRESULT PropertyStorage_BaseConstruct(IStream *stm, - REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps) -{ - HRESULT hr = S_OK; - - assert(pps); - assert(rfmtid); - *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps); - if (!*pps) - return E_OUTOFMEMORY; - - (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl; - (*pps)->ref = 1; - InitializeCriticalSection(&(*pps)->cs); - (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs"); - (*pps)->stm = stm; - (*pps)->fmtid = *rfmtid; - (*pps)->grfMode = grfMode; - - hr = PropertyStorage_CreateDictionaries(*pps); - if (FAILED(hr)) - { - (*pps)->cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&(*pps)->cs); - HeapFree(GetProcessHeap(), 0, *pps); - *pps = NULL; - } - else IStream_AddRef( stm ); - - return hr; -} - -static HRESULT PropertyStorage_ConstructFromStream(IStream *stm, - REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps) -{ - PropertyStorage_impl *ps; - HRESULT hr; - - assert(pps); - hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); - if (SUCCEEDED(hr)) - { - hr = PropertyStorage_ReadFromStream(ps); - if (SUCCEEDED(hr)) - { - *pps = &ps->IPropertyStorage_iface; - TRACE("PropertyStorage %p constructed\n", ps); - hr = S_OK; - } - else IPropertyStorage_Release( &ps->IPropertyStorage_iface ); - } - return hr; -} - -static HRESULT PropertyStorage_ConstructEmpty(IStream *stm, - REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps) -{ - PropertyStorage_impl *ps; - HRESULT hr; - - assert(pps); - hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps); - if (SUCCEEDED(hr)) - { - ps->format = 0; - ps->grfFlags = grfFlags; - if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE) - ps->format = 1; - /* default to Unicode unless told not to, as specified on msdn */ - if (ps->grfFlags & PROPSETFLAG_ANSI) - ps->codePage = GetACP(); - else - ps->codePage = CP_UNICODE; - ps->locale = LOCALE_SYSTEM_DEFAULT; - TRACE("Code page is %d, locale is %ld\n", ps->codePage, ps->locale); - *pps = &ps->IPropertyStorage_iface; - TRACE("PropertyStorage %p constructed\n", ps); - hr = S_OK; - } - return hr; -} - - -/*********************************************************************** - * Implementation of IPropertySetStorage - */ - -struct enum_stat_propset_stg -{ - IEnumSTATPROPSETSTG IEnumSTATPROPSETSTG_iface; - LONG refcount; - STATPROPSETSTG *stats; - size_t current; - size_t count; -}; - -static struct enum_stat_propset_stg *impl_from_IEnumSTATPROPSETSTG(IEnumSTATPROPSETSTG *iface) -{ - return CONTAINING_RECORD(iface, struct enum_stat_propset_stg, IEnumSTATPROPSETSTG_iface); -} - -static HRESULT WINAPI enum_stat_propset_stg_QueryInterface(IEnumSTATPROPSETSTG *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IEnumSTATPROPSETSTG) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IEnumSTATPROPSETSTG_AddRef(iface); - return S_OK; - } - - WARN("Unsupported interface %s.\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI enum_stat_propset_stg_AddRef(IEnumSTATPROPSETSTG *iface) -{ - struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); - LONG refcount = InterlockedIncrement(&psenum->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI enum_stat_propset_stg_Release(IEnumSTATPROPSETSTG *iface) -{ - struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); - LONG refcount = InterlockedDecrement(&psenum->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - heap_free(psenum->stats); - heap_free(psenum); - } - - return refcount; -} - -static HRESULT WINAPI enum_stat_propset_stg_Next(IEnumSTATPROPSETSTG *iface, ULONG celt, - STATPROPSETSTG *ret, ULONG *fetched) -{ - struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); - ULONG count = 0; - - TRACE("%p, %lu, %p, %p.\n", iface, celt, ret, fetched); - - if (psenum->current == ~0u) - psenum->current = 0; - - while (count < celt && psenum->current < psenum->count) - ret[count++] = psenum->stats[psenum->current++]; - - if (fetched) - *fetched = count; - - return count < celt ? S_FALSE : S_OK; -} - -static HRESULT WINAPI enum_stat_propset_stg_Skip(IEnumSTATPROPSETSTG *iface, ULONG celt) -{ - FIXME("%p, %lu.\n", iface, celt); - - return S_OK; -} - -static HRESULT WINAPI enum_stat_propset_stg_Reset(IEnumSTATPROPSETSTG *iface) -{ - struct enum_stat_propset_stg *psenum = impl_from_IEnumSTATPROPSETSTG(iface); - - TRACE("%p.\n", iface); - - psenum->current = ~0u; - - return S_OK; -} - -static HRESULT WINAPI enum_stat_propset_stg_Clone(IEnumSTATPROPSETSTG *iface, IEnumSTATPROPSETSTG **ppenum) -{ - FIXME("%p, %p.\n", iface, ppenum); - - return E_NOTIMPL; -} - -static const IEnumSTATPROPSETSTGVtbl enum_stat_propset_stg_vtbl = -{ - enum_stat_propset_stg_QueryInterface, - enum_stat_propset_stg_AddRef, - enum_stat_propset_stg_Release, - enum_stat_propset_stg_Next, - enum_stat_propset_stg_Skip, - enum_stat_propset_stg_Reset, - enum_stat_propset_stg_Clone, -}; - -static HRESULT create_enum_stat_propset_stg(StorageImpl *storage, IEnumSTATPROPSETSTG **ret) -{ - IStorage *stg = &storage->base.IStorage_iface; - IEnumSTATSTG *penum = NULL; - STATSTG stat; - ULONG count; - HRESULT hr; - - struct enum_stat_propset_stg *enum_obj; - - enum_obj = heap_alloc_zero(sizeof(*enum_obj)); - if (!enum_obj) - return E_OUTOFMEMORY; - - enum_obj->IEnumSTATPROPSETSTG_iface.lpVtbl = &enum_stat_propset_stg_vtbl; - enum_obj->refcount = 1; - - /* add all the property set elements into a list */ - hr = IStorage_EnumElements(stg, 0, NULL, 0, &penum); - if (FAILED(hr)) - goto done; - - /* Allocate stats array and fill it. */ - while ((hr = IEnumSTATSTG_Next(penum, 1, &stat, &count)) == S_OK) - { - enum_obj->count++; - CoTaskMemFree(stat.pwcsName); - } - - if (FAILED(hr)) - goto done; - - enum_obj->stats = heap_alloc(enum_obj->count * sizeof(*enum_obj->stats)); - if (!enum_obj->stats) - { - hr = E_OUTOFMEMORY; - goto done; - } - enum_obj->count = 0; - - if (FAILED(hr = IEnumSTATSTG_Reset(penum))) - goto done; - - while (IEnumSTATSTG_Next(penum, 1, &stat, &count) == S_OK) - { - if (!stat.pwcsName) - continue; - - if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM) - { - STATPROPSETSTG *ptr = &enum_obj->stats[enum_obj->count++]; - - PropStgNameToFmtId(stat.pwcsName, &ptr->fmtid); - - TRACE("adding %s - %s.\n", debugstr_w(stat.pwcsName), debugstr_guid(&ptr->fmtid)); - - ptr->mtime = stat.mtime; - ptr->atime = stat.atime; - ptr->ctime = stat.ctime; - ptr->grfFlags = stat.grfMode; - ptr->clsid = stat.clsid; - } - CoTaskMemFree(stat.pwcsName); - } - -done: - - if (penum) - IEnumSTATSTG_Release(penum); - - if (SUCCEEDED(hr)) - { - *ret = &enum_obj->IEnumSTATPROPSETSTG_iface; - } - else - { - *ret = NULL; - IEnumSTATPROPSETSTG_Release(&enum_obj->IEnumSTATPROPSETSTG_iface); - } - - return hr; -} - -/************************************************************************ - * IPropertySetStorage_fnQueryInterface (IUnknown) - * - * This method forwards to the common QueryInterface implementation - */ -static HRESULT WINAPI IPropertySetStorage_fnQueryInterface( - IPropertySetStorage *ppstg, - REFIID riid, - void** ppvObject) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject ); -} - -/************************************************************************ - * IPropertySetStorage_fnAddRef (IUnknown) - * - * This method forwards to the common AddRef implementation - */ -static ULONG WINAPI IPropertySetStorage_fnAddRef( - IPropertySetStorage *ppstg) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - return IStorage_AddRef( &This->base.IStorage_iface ); -} - -/************************************************************************ - * IPropertySetStorage_fnRelease (IUnknown) - * - * This method forwards to the common Release implementation - */ -static ULONG WINAPI IPropertySetStorage_fnRelease( - IPropertySetStorage *ppstg) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - return IStorage_Release( &This->base.IStorage_iface ); -} - -/************************************************************************ - * IPropertySetStorage_fnCreate (IPropertySetStorage) - */ -static HRESULT WINAPI IPropertySetStorage_fnCreate( - IPropertySetStorage *ppstg, - REFFMTID rfmtid, - const CLSID* pclsid, - DWORD grfFlags, - DWORD grfMode, - IPropertyStorage** ppprstg) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; - IStream *stm = NULL; - HRESULT r; - - TRACE("%p, %s %#lx, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfFlags, - grfMode, ppprstg); - - /* be picky */ - if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE)) - { - r = STG_E_INVALIDFLAG; - goto end; - } - - if (!rfmtid) - { - r = E_INVALIDARG; - goto end; - } - - /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a - * storage, not a stream. For now, disallow it. - */ - if (grfFlags & PROPSETFLAG_NONSIMPLE) - { - FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); - r = STG_E_INVALIDFLAG; - goto end; - } - - r = FmtIdToPropStgName(rfmtid, name); - if (FAILED(r)) - goto end; - - r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm ); - if (FAILED(r)) - goto end; - - r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg); - - IStream_Release( stm ); - -end: - TRACE("returning %#lx\n", r); - return r; -} - -/************************************************************************ - * IPropertySetStorage_fnOpen (IPropertySetStorage) - */ -static HRESULT WINAPI IPropertySetStorage_fnOpen( - IPropertySetStorage *ppstg, - REFFMTID rfmtid, - DWORD grfMode, - IPropertyStorage** ppprstg) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - IStream *stm = NULL; - WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; - HRESULT r; - - TRACE("%p, %s, %#lx, %p.\n", This, debugstr_guid(rfmtid), grfMode, ppprstg); - - /* be picky */ - if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) && - grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE)) - { - r = STG_E_INVALIDFLAG; - goto end; - } - - if (!rfmtid) - { - r = E_INVALIDARG; - goto end; - } - - r = FmtIdToPropStgName(rfmtid, name); - if (FAILED(r)) - goto end; - - r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm ); - if (FAILED(r)) - goto end; - - r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg); - - IStream_Release( stm ); - -end: - TRACE("returning %#lx\n", r); - return r; -} - -/************************************************************************ - * IPropertySetStorage_fnDelete (IPropertySetStorage) - */ -static HRESULT WINAPI IPropertySetStorage_fnDelete( - IPropertySetStorage *ppstg, - REFFMTID rfmtid) -{ - StorageImpl *This = impl_from_IPropertySetStorage(ppstg); - WCHAR name[CCH_MAX_PROPSTG_NAME + 1]; - HRESULT r; - - TRACE("%p %s\n", This, debugstr_guid(rfmtid)); - - if (!rfmtid) - return E_INVALIDARG; - - r = FmtIdToPropStgName(rfmtid, name); - if (FAILED(r)) - return r; - - return IStorage_DestroyElement(&This->base.IStorage_iface, name); -} - -static HRESULT WINAPI IPropertySetStorage_fnEnum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **enum_obj) -{ - StorageImpl *storage = impl_from_IPropertySetStorage(iface); - - TRACE("%p, %p.\n", iface, enum_obj); - - if (!enum_obj) - return E_INVALIDARG; - - return create_enum_stat_propset_stg(storage, enum_obj); -} - -/*********************************************************************** - * vtables - */ -const IPropertySetStorageVtbl IPropertySetStorage_Vtbl = -{ - IPropertySetStorage_fnQueryInterface, - IPropertySetStorage_fnAddRef, - IPropertySetStorage_fnRelease, - IPropertySetStorage_fnCreate, - IPropertySetStorage_fnOpen, - IPropertySetStorage_fnDelete, - IPropertySetStorage_fnEnum -}; - -static const IPropertyStorageVtbl IPropertyStorage_Vtbl = -{ - IPropertyStorage_fnQueryInterface, - IPropertyStorage_fnAddRef, - IPropertyStorage_fnRelease, - IPropertyStorage_fnReadMultiple, - IPropertyStorage_fnWriteMultiple, - IPropertyStorage_fnDeleteMultiple, - IPropertyStorage_fnReadPropertyNames, - IPropertyStorage_fnWritePropertyNames, - IPropertyStorage_fnDeletePropertyNames, - IPropertyStorage_fnCommit, - IPropertyStorage_fnRevert, - IPropertyStorage_fnEnum, - IPropertyStorage_fnSetTimes, - IPropertyStorage_fnSetClass, - IPropertyStorage_fnStat, -}; - -/*********************************************************************** - * Format ID <-> name conversion - */ -static const WCHAR szSummaryInfo[] = L"\5SummaryInformation"; -static const WCHAR szDocSummaryInfo[] = L"\5DocumentSummaryInformation"; - -#define BITS_PER_BYTE 8 -#define CHARMASK 0x1f -#define BITS_IN_CHARMASK 5 -#define NUM_ALPHA_CHARS 26 - -/*********************************************************************** - * FmtIdToPropStgName [ole32.@] - * Returns the storage name of the format ID rfmtid. - * PARAMS - * rfmtid [I] Format ID for which to return a storage name - * str [O] Storage name associated with rfmtid. - * - * RETURNS - * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise. - * - * NOTES - * str must be at least CCH_MAX_PROPSTG_NAME characters in length. - */ -HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str) -{ - static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345"; - - TRACE("%s, %p\n", debugstr_guid(rfmtid), str); - - if (!rfmtid) return E_INVALIDARG; - if (!str) return E_INVALIDARG; - - if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid)) - lstrcpyW(str, szSummaryInfo); - else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid)) - lstrcpyW(str, szDocSummaryInfo); - else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid)) - lstrcpyW(str, szDocSummaryInfo); - else - { - const BYTE *fmtptr; - WCHAR *pstr = str; - ULONG bitsRemaining = BITS_PER_BYTE; - - *pstr++ = 5; - for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); ) - { - ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining); - - if (bitsRemaining >= BITS_IN_CHARMASK) - { - *pstr = (WCHAR)(fmtMap[i & CHARMASK]); - if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' && - *pstr <= 'z') - *pstr += 'A' - 'a'; - pstr++; - bitsRemaining -= BITS_IN_CHARMASK; - if (bitsRemaining == 0) - { - fmtptr++; - bitsRemaining = BITS_PER_BYTE; - } - } - else - { - if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID)) - i |= *fmtptr << bitsRemaining; - *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]); - bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK; - } - } - *pstr = 0; - } - TRACE("returning %s\n", debugstr_w(str)); - return S_OK; -} - -/*********************************************************************** - * PropStgNameToFmtId [ole32.@] - * Returns the format ID corresponding to the given name. - * PARAMS - * str [I] Storage name to convert to a format ID. - * rfmtid [O] Format ID corresponding to str. - * - * RETURNS - * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to - * a format ID, S_OK otherwise. - */ -HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid) -{ - HRESULT hr = STG_E_INVALIDNAME; - - TRACE("%s, %p\n", debugstr_w(str), rfmtid); - - if (!rfmtid) return E_INVALIDARG; - if (!str) return STG_E_INVALIDNAME; - - if (!lstrcmpiW(str, szDocSummaryInfo)) - { - *rfmtid = FMTID_DocSummaryInformation; - hr = S_OK; - } - else if (!lstrcmpiW(str, szSummaryInfo)) - { - *rfmtid = FMTID_SummaryInformation; - hr = S_OK; - } - else - { - ULONG bits; - BYTE *fmtptr = (BYTE *)rfmtid - 1; - const WCHAR *pstr = str; - - memset(rfmtid, 0, sizeof(*rfmtid)); - for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE; - bits += BITS_IN_CHARMASK) - { - ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored; - WCHAR wc; - - if (bitsUsed == 0) - fmtptr++; - wc = *++pstr - 'A'; - if (wc > NUM_ALPHA_CHARS) - { - wc += 'A' - 'a'; - if (wc > NUM_ALPHA_CHARS) - { - wc += 'a' - '0' + NUM_ALPHA_CHARS; - if (wc > CHARMASK) - { - WARN("invalid character (%d)\n", *pstr); - goto end; - } - } - } - *fmtptr |= wc << bitsUsed; - bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK); - if (bitsStored < BITS_IN_CHARMASK) - { - wc >>= BITS_PER_BYTE - bitsUsed; - if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE) - { - if (wc != 0) - { - WARN("extra bits\n"); - goto end; - } - break; - } - fmtptr++; - *fmtptr |= (BYTE)wc; - } - } - hr = S_OK; - } -end: - return hr; -} - -#ifdef __i386__ /* thiscall functions are i386-specific */ - -#define DEFINE_STDCALL_WRAPPER(num,func,args) \ - __ASM_STDCALL_FUNC(func, args, \ - "popl %eax\n\t" \ - "popl %ecx\n\t" \ - "pushl %eax\n\t" \ - "movl (%ecx), %eax\n\t" \ - "jmp *(4*(" #num "))(%eax)" ) - -DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8) -extern void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize); - -#else - -static void* WINAPI Allocate_PMemoryAllocator(void *this, ULONG cbSize) -{ - void* (WINAPI *fn)(void*,ULONG) = **(void***)this; - return fn(this, cbSize); -} - -#endif - -BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, - USHORT CodePage, PROPVARIANT* pvar, void* pma) -{ - struct read_buffer read_buffer; - HRESULT hr; - - read_buffer.data = (BYTE *)prop; - read_buffer.size = ~(size_t)0; - hr = PropertyStorage_ReadProperty(pvar, &read_buffer, 0, CodePage, Allocate_PMemoryAllocator, pma); - - if (FAILED(hr)) - { - FIXME("should raise C++ exception on failure\n"); - PropVariantInit(pvar); - } - - return FALSE; -} - -SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar, - USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid, - BOOLEAN fReserved, ULONG *pcIndirect) -{ - FIXME("%p, %d, %p, %p, %ld, %d, %p.\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect); - - return NULL; -} - -HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid, - DWORD flags, DWORD reserved, IPropertyStorage **prop_stg) -{ - IStorage *stg; - IStream *stm; - HRESULT r; - - TRACE("%p, %s, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg); - - if (!fmt || reserved) - { - r = E_INVALIDARG; - goto end; - } - - if (flags & PROPSETFLAG_NONSIMPLE) - { - r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); - if (FAILED(r)) - goto end; - - /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a - * storage, not a stream. For now, disallow it. - */ - FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); - IStorage_Release(stg); - r = STG_E_INVALIDFLAG; - } - else - { - r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); - if (FAILED(r)) - goto end; - - r = PropertyStorage_ConstructEmpty(stm, fmt, flags, - STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); - - IStream_Release( stm ); - } - -end: - TRACE("returning %#lx\n", r); - return r; -} - -HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags, - DWORD reserved, IPropertyStorage **prop_stg) -{ - IStorage *stg; - IStream *stm; - HRESULT r; - - TRACE("%p, %s, %#lx, %ld, %p.\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg); - - if (!fmt || reserved) - { - r = E_INVALIDARG; - goto end; - } - - if (flags & PROPSETFLAG_NONSIMPLE) - { - r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg); - if (FAILED(r)) - goto end; - - /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a - * storage, not a stream. For now, disallow it. - */ - FIXME("PROPSETFLAG_NONSIMPLE not supported\n"); - IStorage_Release(stg); - r = STG_E_INVALIDFLAG; - } - else - { - r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm); - if (FAILED(r)) - goto end; - - r = PropertyStorage_ConstructFromStream(stm, fmt, - STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg); - - IStream_Release( stm ); - } - -end: - TRACE("returning %#lx\n", r); - return r; -} diff --git a/dlls/ole32/stg_stream.c b/dlls/ole32/stg_stream.c deleted file mode 100644 index 25ead2c7c49..00000000000 --- a/dlls/ole32/stg_stream.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * Compound Storage (32 bit version) - * Stream implementation - * - * This file contains the implementation of the stream interface - * for streams contained in a compound storage. - * - * Copyright 1999 Francis Beaudet - * Copyright 1999 Thuy Nguyen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include <stdlib.h> -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -#define COBJMACROS -#include "windef.h" -#include "winbase.h" -#include "winerror.h" -#include "winternl.h" -#include "wine/debug.h" - -#include "storage32.h" - -WINE_DEFAULT_DEBUG_CHANNEL(storage); - -/*** - * This implements the IUnknown method QueryInterface for this - * class - */ -static HRESULT WINAPI StgStreamImpl_QueryInterface( - IStream* iface, - REFIID riid, /* [in] */ - void** ppvObject) /* [iid_is][out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - if (ppvObject==0) - return E_INVALIDARG; - - *ppvObject = 0; - - if (IsEqualIID(&IID_IUnknown, riid) || - IsEqualIID(&IID_ISequentialStream, riid) || - IsEqualIID(&IID_IStream, riid)) - { - *ppvObject = &This->IStream_iface; - } - else - return E_NOINTERFACE; - - IStream_AddRef(iface); - - return S_OK; -} - -/*** - * This implements the IUnknown method AddRef for this - * class - */ -static ULONG WINAPI StgStreamImpl_AddRef( - IStream* iface) -{ - StgStreamImpl* This = impl_from_IStream(iface); - return InterlockedIncrement(&This->ref); -} - -/*** - * This implements the IUnknown method Release for this - * class - */ -static ULONG WINAPI StgStreamImpl_Release( - IStream* iface) -{ - StgStreamImpl* This = impl_from_IStream(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - if (!ref) - { - TRACE("(%p)\n", This); - - /* - * Release the reference we are holding on the parent storage. - * IStorage_Release(&This->parentStorage->IStorage_iface); - * - * No, don't do this. Some apps call IStorage_Release without - * calling IStream_Release first. If we grab a reference the - * file is not closed, and the app fails when it tries to - * reopen the file (Easy-PC, for example). Just inform the - * storage that we have closed the stream - */ - - if (This->parentStorage) - StorageBaseImpl_RemoveStream(This->parentStorage, This); - This->parentStorage = 0; - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -/*** - * This method is part of the ISequentialStream interface. - * - * It reads a block of information from the stream at the current - * position. It then moves the current position at the end of the - * read block - * - * See the documentation of ISequentialStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Read( - IStream* iface, - void* pv, /* [length_is][size_is][out] */ - ULONG cb, /* [in] */ - ULONG* pcbRead) /* [out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - ULONG bytesReadBuffer; - HRESULT res; - - TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbRead); - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - /* - * If the caller is not interested in the number of bytes read, - * we use another buffer to avoid "if" statements in the code. - */ - if (pcbRead==0) - pcbRead = &bytesReadBuffer; - - res = StorageBaseImpl_StreamReadAt(This->parentStorage, - This->dirEntry, - This->currentPosition, - cb, - pv, - pcbRead); - - if (SUCCEEDED(res)) - { - /* - * Advance the pointer for the number of positions read. - */ - This->currentPosition.QuadPart += *pcbRead; - } - - TRACE("<-- %#lx\n", res); - return res; -} - -/*** - * This method is part of the ISequentialStream interface. - * - * It writes a block of information to the stream at the current - * position. It then moves the current position at the end of the - * written block. If the stream is too small to fit the block, - * the stream is grown to fit. - * - * See the documentation of ISequentialStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Write( - IStream* iface, - const void* pv, /* [size_is][in] */ - ULONG cb, /* [in] */ - ULONG* pcbWritten) /* [out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - ULONG bytesWritten = 0; - HRESULT res; - - TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbWritten); - - /* - * Do we have permission to write to this stream? - */ - switch(STGM_ACCESS_MODE(This->grfMode)) - { - case STGM_WRITE: - case STGM_READWRITE: - break; - default: - WARN("access denied by flags: %#lx\n", STGM_ACCESS_MODE(This->grfMode)); - return STG_E_ACCESSDENIED; - } - - if (!pv) - return STG_E_INVALIDPOINTER; - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - /* - * If the caller is not interested in the number of bytes written, - * we use another buffer to avoid "if" statements in the code. - */ - if (pcbWritten == 0) - pcbWritten = &bytesWritten; - - /* - * Initialize the out parameter - */ - *pcbWritten = 0; - - if (cb == 0) - { - TRACE("<-- S_OK, written 0\n"); - return S_OK; - } - - res = StorageBaseImpl_StreamWriteAt(This->parentStorage, - This->dirEntry, - This->currentPosition, - cb, - pv, - pcbWritten); - - /* - * Advance the position pointer for the number of positions written. - */ - This->currentPosition.QuadPart += *pcbWritten; - - if (SUCCEEDED(res)) - res = StorageBaseImpl_Flush(This->parentStorage); - - TRACE("<-- %#lx, written %lu\n", res, *pcbWritten); - return res; -} - -/*** - * This method is part of the IStream interface. - * - * It will move the current stream pointer according to the parameters - * given. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Seek( - IStream* iface, - LARGE_INTEGER dlibMove, /* [in] */ - DWORD dwOrigin, /* [in] */ - ULARGE_INTEGER* plibNewPosition) /* [out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - ULARGE_INTEGER newPosition; - DirEntry currentEntry; - HRESULT hr; - - TRACE("%p, %ld, %ld, %p.\n", iface, dlibMove.LowPart, dwOrigin, plibNewPosition); - - /* - * fail if the stream has no parent (as does windows) - */ - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - /* - * The caller is allowed to pass in NULL as the new position return value. - * If it happens, we assign it to a dynamic variable to avoid special cases - * in the code below. - */ - if (plibNewPosition == 0) - { - plibNewPosition = &newPosition; - } - - /* - * The file pointer is moved depending on the given "function" - * parameter. - */ - switch (dwOrigin) - { - case STREAM_SEEK_SET: - plibNewPosition->u.HighPart = 0; - plibNewPosition->u.LowPart = 0; - break; - case STREAM_SEEK_CUR: - *plibNewPosition = This->currentPosition; - break; - case STREAM_SEEK_END: - hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, ¤tEntry); - if (FAILED(hr)) return hr; - *plibNewPosition = currentEntry.size; - break; - default: - WARN("invalid dwOrigin %ld\n", dwOrigin); - return STG_E_INVALIDFUNCTION; - } - - plibNewPosition->QuadPart += dlibMove.QuadPart; - - /* - * tell the caller what we calculated - */ - This->currentPosition = *plibNewPosition; - - return S_OK; -} - -/*** - * This method is part of the IStream interface. - * - * It will change the size of a stream. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_SetSize( - IStream* iface, - ULARGE_INTEGER libNewSize) /* [in] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - HRESULT hr; - - TRACE("%p, %ld.\n", iface, libNewSize.LowPart); - - if(!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - /* - * As documented. - */ - if (libNewSize.HighPart != 0) - { - WARN("invalid value for libNewSize.HighPart %ld\n", libNewSize.HighPart); - return STG_E_INVALIDFUNCTION; - } - - /* - * Do we have permission? - */ - if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) - { - WARN("access denied\n"); - return STG_E_ACCESSDENIED; - } - - hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_Flush(This->parentStorage); - - return hr; -} - -/*** - * This method is part of the IStream interface. - * - * It will copy the 'cb' Bytes to 'pstm' IStream. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_CopyTo( - IStream* iface, - IStream* pstm, /* [unique][in] */ - ULARGE_INTEGER cb, /* [in] */ - ULARGE_INTEGER* pcbRead, /* [out] */ - ULARGE_INTEGER* pcbWritten) /* [out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - HRESULT hr = S_OK; - BYTE tmpBuffer[128]; - ULONG bytesRead, bytesWritten, copySize; - ULARGE_INTEGER totalBytesRead; - ULARGE_INTEGER totalBytesWritten; - - TRACE("%p, %p, %ld, %p, %p.\n", iface, pstm, cb.LowPart, pcbRead, pcbWritten); - - /* - * Sanity check - */ - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - if ( pstm == 0 ) - return STG_E_INVALIDPOINTER; - - totalBytesRead.QuadPart = 0; - totalBytesWritten.QuadPart = 0; - - while ( cb.QuadPart > 0 ) - { - if ( cb.QuadPart >= sizeof(tmpBuffer) ) - copySize = sizeof(tmpBuffer); - else - copySize = cb.LowPart; - - IStream_Read(iface, tmpBuffer, copySize, &bytesRead); - - totalBytesRead.QuadPart += bytesRead; - - IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); - - totalBytesWritten.QuadPart += bytesWritten; - - /* - * Check that read & write operations were successful - */ - if (bytesRead != bytesWritten) - { - hr = STG_E_MEDIUMFULL; - WARN("medium full\n"); - break; - } - - if (bytesRead!=copySize) - cb.QuadPart = 0; - else - cb.QuadPart -= bytesRead; - } - - if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; - if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; - - return hr; -} - -/*** - * This method is part of the IStream interface. - * - * For streams contained in structured storages, this method - * does nothing. This is what the documentation tells us. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Commit( - IStream* iface, - DWORD grfCommitFlags) /* [in] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - return StorageBaseImpl_Flush(This->parentStorage); -} - -/*** - * This method is part of the IStream interface. - * - * For streams contained in structured storages, this method - * does nothing. This is what the documentation tells us. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Revert( - IStream* iface) -{ - return S_OK; -} - -static HRESULT WINAPI StgStreamImpl_LockRegion( - IStream* iface, - ULARGE_INTEGER libOffset, /* [in] */ - ULARGE_INTEGER cb, /* [in] */ - DWORD dwLockType) /* [in] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - FIXME("not implemented!\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI StgStreamImpl_UnlockRegion( - IStream* iface, - ULARGE_INTEGER libOffset, /* [in] */ - ULARGE_INTEGER cb, /* [in] */ - DWORD dwLockType) /* [in] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - FIXME("not implemented!\n"); - return E_NOTIMPL; -} - -/*** - * This method is part of the IStream interface. - * - * This method returns information about the current - * stream. - * - * See the documentation of IStream for more info. - */ -static HRESULT WINAPI StgStreamImpl_Stat( - IStream* iface, - STATSTG* pstatstg, /* [out] */ - DWORD grfStatFlag) /* [in] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - - DirEntry currentEntry; - HRESULT hr; - - TRACE("%p, %p, %#lx.\n", This, pstatstg, grfStatFlag); - - /* - * if stream has no parent, return STG_E_REVERTED - */ - - if (!This->parentStorage) - { - WARN("storage reverted\n"); - return STG_E_REVERTED; - } - - /* - * Read the information from the directory entry. - */ - hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, - This->dirEntry, - ¤tEntry); - - if (SUCCEEDED(hr)) - { - StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, - pstatstg, - ¤tEntry, - grfStatFlag); - - pstatstg->grfMode = This->grfMode; - - /* In simple create mode cbSize is the current pos */ - if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create) - pstatstg->cbSize = This->currentPosition; - - return S_OK; - } - - WARN("failed to read entry\n"); - return hr; -} - -/*** - * This method is part of the IStream interface. - * - * This method returns a clone of the interface that allows for - * another seek pointer - * - * See the documentation of IStream for more info. - * - * I am not totally sure what I am doing here but I presume that this - * should be basically as simple as creating a new stream with the same - * parent etc and positioning its seek cursor. - */ -static HRESULT WINAPI StgStreamImpl_Clone( - IStream* iface, - IStream** ppstm) /* [out] */ -{ - StgStreamImpl* This = impl_from_IStream(iface); - StgStreamImpl* new_stream; - LARGE_INTEGER seek_pos; - - TRACE("%p %p\n", This, ppstm); - - /* - * Sanity check - */ - - if (!This->parentStorage) - return STG_E_REVERTED; - - if ( ppstm == 0 ) - return STG_E_INVALIDPOINTER; - - new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry); - - if (!new_stream) - return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */ - - *ppstm = &new_stream->IStream_iface; - IStream_AddRef(*ppstm); - - seek_pos.QuadPart = This->currentPosition.QuadPart; - - return IStream_Seek(*ppstm, seek_pos, STREAM_SEEK_SET, NULL); -} - -/* - * Virtual function table for the StgStreamImpl class. - */ -static const IStreamVtbl StgStreamVtbl = -{ - StgStreamImpl_QueryInterface, - StgStreamImpl_AddRef, - StgStreamImpl_Release, - StgStreamImpl_Read, - StgStreamImpl_Write, - StgStreamImpl_Seek, - StgStreamImpl_SetSize, - StgStreamImpl_CopyTo, - StgStreamImpl_Commit, - StgStreamImpl_Revert, - StgStreamImpl_LockRegion, - StgStreamImpl_UnlockRegion, - StgStreamImpl_Stat, - StgStreamImpl_Clone -}; - -/****************************************************************************** -** StgStreamImpl implementation -*/ - -/*** - * This is the constructor for the StgStreamImpl class. - * - * Params: - * parentStorage - Pointer to the storage that contains the stream to open - * dirEntry - Index of the directory entry that points to this stream. - */ -StgStreamImpl* StgStreamImpl_Construct( - StorageBaseImpl* parentStorage, - DWORD grfMode, - DirRef dirEntry) -{ - StgStreamImpl* newStream; - - newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl)); - - if (newStream) - { - /* - * Set-up the virtual function table and reference count. - */ - newStream->IStream_iface.lpVtbl = &StgStreamVtbl; - newStream->ref = 0; - - newStream->parentStorage = parentStorage; - - /* - * We want to nail-down the reference to the storage in case the - * stream out-lives the storage in the client application. - * - * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface); - * - * No, don't do this. Some apps call IStorage_Release without - * calling IStream_Release first. If we grab a reference the - * file is not closed, and the app fails when it tries to - * reopen the file (Easy-PC, for example) - */ - - newStream->grfMode = grfMode; - newStream->dirEntry = dirEntry; - - /* - * Start the stream at the beginning. - */ - newStream->currentPosition.HighPart = 0; - newStream->currentPosition.LowPart = 0; - - /* add us to the storage's list of active streams */ - StorageBaseImpl_AddStream(parentStorage, newStream); - } - - return newStream; -} diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index 0d3f9922d78..9690772eadc 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -57,9025 +57,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage); * These are signatures to detect the type of Document file. */ 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}; - -extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; - - -/**************************************************************************** - * StorageInternalImpl definitions. - * - * Definition of the implementation structure for the IStorage interface. - * This one implements the IStorage interface for storage that are - * inside another storage. - */ -typedef struct StorageInternalImpl -{ - struct StorageBaseImpl base; - - /* - * Entry in the parent's stream tracking list - */ - struct list ParentListEntry; - - StorageBaseImpl *parentStorage; -} StorageInternalImpl; - -static const IStorageVtbl StorageInternalImpl_Vtbl; -static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef); - -typedef struct TransactedDirEntry -{ - /* If applicable, a reference to the original DirEntry in the transacted - * parent. If this is a newly-created entry, DIRENTRY_NULL. */ - DirRef transactedParentEntry; - - /* True if this entry is being used. */ - BOOL inuse; - - /* True if data is up to date. */ - BOOL read; - - /* True if this entry has been modified. */ - BOOL dirty; - - /* True if this entry's stream has been modified. */ - BOOL stream_dirty; - - /* True if this entry has been deleted in the transacted storage, but the - * delete has not yet been committed. */ - BOOL deleted; - - /* If this entry's stream has been modified, a reference to where the stream - * is stored in the snapshot file. */ - DirRef stream_entry; - - /* This directory entry's data, including any changes that have been made. */ - DirEntry data; - - /* A reference to the parent of this node. This is only valid while we are - * committing changes. */ - DirRef parent; - - /* A reference to a newly-created entry in the transacted parent. This is - * always equal to transactedParentEntry except when committing changes. */ - DirRef newTransactedParentEntry; -} TransactedDirEntry; - - -/**************************************************************************** - * Transacted storage object. - */ -typedef struct TransactedSnapshotImpl -{ - struct StorageBaseImpl base; - - /* - * Modified streams are temporarily saved to the scratch file. - */ - StorageBaseImpl *scratch; - - /* The directory structure is kept here, so that we can track how these - * entries relate to those in the parent storage. */ - TransactedDirEntry *entries; - ULONG entries_size; - ULONG firstFreeEntry; - - /* - * Changes are committed to the transacted parent. - */ - StorageBaseImpl *transactedParent; - - /* The transaction signature from when we last committed */ - ULONG lastTransactionSig; -} TransactedSnapshotImpl; - -static const IStorageVtbl TransactedSnapshotImpl_Vtbl; -static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**); - -typedef struct TransactedSharedImpl -{ - struct StorageBaseImpl base; - - /* - * Snapshot and uncommitted changes go here. - */ - TransactedSnapshotImpl *scratch; - - /* - * Changes are committed to the transacted parent. - */ - StorageBaseImpl *transactedParent; - - /* The transaction signature from when we last committed */ - ULONG lastTransactionSig; -} TransactedSharedImpl; - - -/**************************************************************************** - * BlockChainStream definitions. - * - * The BlockChainStream class is a utility class that is used to create an - * abstraction of the big block chains in the storage file. - */ - -struct BlockChainRun -{ - /* This represents a range of blocks that happen reside in consecutive sectors. */ - ULONG firstSector; - ULONG firstOffset; - ULONG lastOffset; -}; - -typedef struct BlockChainBlock -{ - ULONG index; - ULONG sector; - BOOL read; - BOOL dirty; - BYTE data[MAX_BIG_BLOCK_SIZE]; -} BlockChainBlock; - -struct BlockChainStream -{ - StorageImpl* parentStorage; - ULONG* headOfStreamPlaceHolder; - DirRef ownerDirEntry; - struct BlockChainRun* indexCache; - ULONG indexCacheLen; - ULONG indexCacheSize; - BlockChainBlock cachedBlocks[2]; - ULONG blockToEvict; - ULONG tailIndex; - ULONG numBlocks; -}; - -/* 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; -} - -static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); -static void BlockChainStream_Destroy(BlockChainStream*); -static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); -static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); -static HRESULT BlockChainStream_Flush(BlockChainStream*); -static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*); -static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER); - - -/**************************************************************************** - * SmallBlockChainStream definitions. - * - * The SmallBlockChainStream class is a utility class that is used to create an - * abstraction of the small block chains in the storage file. - */ - -struct SmallBlockChainStream -{ - StorageImpl* parentStorage; - DirRef ownerDirEntry; - ULONG* headOfStreamPlaceHolder; -}; - -static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); -static void SmallBlockChainStream_Destroy(SmallBlockChainStream*); -static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); -static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); -static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*); -static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER); - - -/************************************************************************ - * STGM Functions - ***********************************************************************/ - -/************************************************************************ - * 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 validateSTGM(DWORD stgm) -{ - 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 %#lx\n", stgm); - return E_FAIL; - } - - switch (access) - { - case STGM_READ: - case STGM_WRITE: - case STGM_READWRITE: - break; - default: - return E_FAIL; - } - - 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; - } - - switch (create) - { - case STGM_CREATE: - case STGM_FAILIFTHERE: - break; - default: - return E_FAIL; - } - - /* - * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE - */ - if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) ) - return E_FAIL; - - /* - * STGM_CREATE | STGM_CONVERT - * if both are false, STGM_FAILIFTHERE is set to TRUE - */ - if ( create == STGM_CREATE && (stgm & STGM_CONVERT) ) - return E_FAIL; - - /* - * STGM_NOSCRATCH requires STGM_TRANSACTED - */ - if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) ) - return E_FAIL; - - /* - * 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; -} - -/************************************************************************ - * 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; -} - -/************************************************************************ - * GetAccessModeFromSTGM - * - * This method will return an access mode flag from a STGM value. - * The STGM value is assumed valid. - */ -static DWORD GetAccessModeFromSTGM(DWORD stgm) -{ - 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; -} - - -/************************************************************************ - * IDirectWriterLock implementation - ***********************************************************************/ - -static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface ) -{ - return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface); -} - -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); -} - -static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface) -{ - StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); - return IStorage_AddRef(&This->IStorage_iface); -} - -static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface) -{ - StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); - return IStorage_Release(&This->IStorage_iface); -} - -static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout) -{ - StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); - FIXME("%p, %ld: stub\n", This, timeout); - return E_NOTIMPL; -} - -static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface) -{ - StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); - FIXME("(%p): stub\n", This); - return E_NOTIMPL; -} - -static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface) -{ - StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); - FIXME("(%p): stub\n", This); - return E_NOTIMPL; -} - -static const IDirectWriterLockVtbl DirectWriterLockVtbl = -{ - directwriterlock_QueryInterface, - directwriterlock_AddRef, - directwriterlock_Release, - directwriterlock_WaitForWriteAccess, - directwriterlock_ReleaseWriteAccess, - directwriterlock_HaveWriteAccess -}; - - -/************************************************************************ - * StorageBaseImpl implementation : Tree helper functions - ***********************************************************************/ - -/**************************************************************************** - * - * 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) -{ - LONG diff = lstrlenW(name1) - lstrlenW(name2); - - while (diff == 0 && *name1 != 0) - { - /* - * We compare the string themselves only when they are of the same length - */ - diff = towupper(*name1++) - towupper(*name2++); - } - - return diff; -} - -/**************************************************************************** - * - * Internal Method - * - * Find and read the element of a storage with the given name. - */ -static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry, - const OLECHAR *name, DirEntry *data) -{ - DirRef currentEntry; - - /* Read the storage entry to find the root of the tree. */ - StorageBaseImpl_ReadDirEntry(storage, storageEntry, data); - - currentEntry = data->dirRootEntry; - - while (currentEntry != DIRENTRY_NULL) - { - 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 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; - - /* Read the storage entry to find the root of the tree. */ - StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData); - - *parentEntry = storageEntry; - *relation = DIRENTRY_RELATION_DIR; - - childEntry = parentData->dirRootEntry; - - while (childEntry != DIRENTRY_NULL) - { - LONG cmp; - - StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData); - - cmp = entryNameCmp(childName, childData.name); - - if (cmp == 0) - /* found it */ - break; - - else if (cmp < 0) - { - *parentData = childData; - *parentEntry = childEntry; - *relation = DIRENTRY_RELATION_PREVIOUS; - - childEntry = parentData->leftChild; - } - - else if (cmp > 0) - { - *parentData = childData; - *parentEntry = childEntry; - *relation = DIRENTRY_RELATION_NEXT; - - childEntry = parentData->rightChild; - } - } - - if (childEntry == DIRENTRY_NULL) - return STG_E_FILENOTFOUND; - else - return S_OK; -} - -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 - * - * Add a directory entry to a storage - */ -static HRESULT insertIntoTree( - StorageBaseImpl *This, - DirRef parentStorageIndex, - DirRef newEntryIndex) -{ - DirEntry currentEntry; - DirEntry newEntry; - - /* - * Read the inserted entry - */ - StorageBaseImpl_ReadDirEntry(This, - newEntryIndex, - &newEntry); - - /* - * Read the storage entry - */ - StorageBaseImpl_ReadDirEntry(This, - parentStorageIndex, - ¤tEntry); - - 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; - - /* - * Keep a reference to the root of the storage's element tree - */ - currentEntryId = currentEntry.dirRootEntry; - - /* - * Read - */ - StorageBaseImpl_ReadDirEntry(This, - currentEntry.dirRootEntry, - ¤tEntry); - - previous = currentEntry.leftChild; - next = currentEntry.rightChild; - current = currentEntryId; - - while (!found) - { - LONG diff = entryNameCmp( newEntry.name, currentEntry.name); - - 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; - } - - previous = currentEntry.leftChild; - next = currentEntry.rightChild; - } - } - else - { - /* - * The storage is empty, make the new entry the root of its element tree - */ - currentEntry.dirRootEntry = newEntryIndex; - StorageBaseImpl_WriteDirEntry(This, - parentStorageIndex, - ¤tEntry); - } - - return S_OK; -} - -/************************************************************************* - * - * 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 (entryToDelete.leftChild != DIRENTRY_NULL) - { - /* - * Replace the deleted entry with its left child - */ - setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild); - - hr = StorageBaseImpl_WriteDirEntry( - This, - parentEntryRef, - &parentEntry); - if(FAILED(hr)) - { - return hr; - } - - 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; - - do - { - hr = StorageBaseImpl_ReadDirEntry( - This, - newRightChildParent, - &newRightChildParentEntry); - if (FAILED(hr)) - { - return hr; - } - - if (newRightChildParentEntry.rightChild != DIRENTRY_NULL) - newRightChildParent = newRightChildParentEntry.rightChild; - } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL); - - newRightChildParentEntry.rightChild = entryToDelete.rightChild; - - hr = StorageBaseImpl_WriteDirEntry( - This, - newRightChildParent, - &newRightChildParentEntry); - if (FAILED(hr)) - { - return hr; - } - } - } - 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; - } - } - - return hr; -} - - -/************************************************************************ - * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements - ***********************************************************************/ - -/* - * IEnumSTATSTGImpl definitions. - * - * Definition of the implementation structure for the IEnumSTATSTGImpl interface. - * This class allows iterating through the content of a storage and finding - * specific items inside it. - */ -struct IEnumSTATSTGImpl -{ - 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 */ -}; - -static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface) -{ - return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface); -} - -static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This) -{ - IStorage_Release(&This->parentStorage->IStorage_iface); - HeapFree(GetProcessHeap(), 0, This); -} - -static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface( - IEnumSTATSTG* iface, - REFIID riid, - void** ppvObject) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); - - if (ppvObject==0) - return E_INVALIDARG; - - *ppvObject = 0; - - if (IsEqualGUID(&IID_IUnknown, riid) || - IsEqualGUID(&IID_IEnumSTATSTG, riid)) - { - *ppvObject = &This->IEnumSTATSTG_iface; - IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface); - TRACE("<-- %p\n", *ppvObject); - return S_OK; - } - - TRACE("<-- E_NOINTERFACE\n"); - return E_NOINTERFACE; -} - -static ULONG WINAPI IEnumSTATSTGImpl_AddRef( - IEnumSTATSTG* iface) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - return InterlockedIncrement(&This->ref); -} - -static ULONG WINAPI IEnumSTATSTGImpl_Release( - IEnumSTATSTG* iface) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - ULONG newRef; - - newRef = InterlockedDecrement(&This->ref); - - if (newRef==0) - { - IEnumSTATSTGImpl_Destroy(This); - } - - return newRef; -} - -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]; - - TRACE("%p,%p\n", This, ref); - - hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, - This->parentStorage->storageDirEntry, &entry); - searchNode = entry.dirRootEntry; - - while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL) - { - hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry); - - if (SUCCEEDED(hr)) - { - 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; - } - } - } - - if (SUCCEEDED(hr)) - { - *ref = result; - if (result != DIRENTRY_NULL) - memcpy(This->name, result_name, sizeof(result_name)); - } - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static HRESULT WINAPI IEnumSTATSTGImpl_Next( - IEnumSTATSTG* iface, - ULONG celt, - STATSTG* rgelt, - ULONG* pceltFetched) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - DirEntry currentEntry; - STATSTG* currentReturnStruct = rgelt; - ULONG objectFetched = 0; - DirRef currentSearchNode; - HRESULT hr=S_OK; - - TRACE("%p, %lu, %p, %p.\n", iface, celt, rgelt, pceltFetched); - - if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) - return E_INVALIDARG; - - if (This->parentStorage->reverted) - { - TRACE("<-- STG_E_REVERTED\n"); - return STG_E_REVERTED; - } - - /* - * 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; - - while ( *pceltFetched < celt ) - { - hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); - - if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) - { - memset(currentReturnStruct, 0, sizeof(*currentReturnStruct)); - break; - } - - /* - * Read the entry from the storage. - */ - hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, - currentSearchNode, - ¤tEntry); - if (FAILED(hr)) break; - - /* - * 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++; - } - - if (SUCCEEDED(hr) && *pceltFetched != celt) - hr = S_FALSE; - - TRACE("<-- %#lx (asked %lu, got %lu)\n", hr, celt, *pceltFetched); - return hr; -} - - -static HRESULT WINAPI IEnumSTATSTGImpl_Skip( - IEnumSTATSTG* iface, - ULONG celt) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - ULONG objectFetched = 0; - DirRef currentSearchNode; - HRESULT hr=S_OK; - - TRACE("%p, %lu.\n", iface, celt); - - if (This->parentStorage->reverted) - { - TRACE("<-- STG_E_REVERTED\n"); - return STG_E_REVERTED; - } - - while ( (objectFetched < celt) ) - { - hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); - - if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) - break; - - objectFetched++; - } - - if (SUCCEEDED(hr) && objectFetched != celt) - return S_FALSE; - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static HRESULT WINAPI IEnumSTATSTGImpl_Reset( - IEnumSTATSTG* iface) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - TRACE("%p\n", iface); - - if (This->parentStorage->reverted) - { - TRACE("<-- STG_E_REVERTED\n"); - return STG_E_REVERTED; - } - - This->name[0] = 0; - - return S_OK; -} - -static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef); - -static HRESULT WINAPI IEnumSTATSTGImpl_Clone( - IEnumSTATSTG* iface, - IEnumSTATSTG** ppenum) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - IEnumSTATSTGImpl* newClone; - - TRACE("%p,%p\n", iface, ppenum); - - if (This->parentStorage->reverted) - { - TRACE("<-- STG_E_REVERTED\n"); - return STG_E_REVERTED; - } - - if (ppenum==0) - return E_INVALIDARG; - - newClone = IEnumSTATSTGImpl_Construct(This->parentStorage, - This->storageDirEntry); - if (!newClone) - { - *ppenum = NULL; - return E_OUTOFMEMORY; - } - - /* - * The new clone enumeration must point to the same current node as - * the old one. - */ - memcpy(newClone->name, This->name, sizeof(newClone->name)); - - *ppenum = &newClone->IEnumSTATSTG_iface; - - return S_OK; -} - -/* - * 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 -}; - -static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct( - StorageBaseImpl* parentStorage, - DirRef storageDirEntry) -{ - IEnumSTATSTGImpl* newEnumeration; - - newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl)); - - if (newEnumeration) - { - 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; - } - - return newEnumeration; -} - - -/************************************************************************ - * StorageBaseImpl implementation - ***********************************************************************/ - -static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface ) -{ - return CONTAINING_RECORD(iface, StorageBaseImpl, 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) -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - - TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); - - if (!ppvObject) - return E_INVALIDARG; - - *ppvObject = 0; - - 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 - { - TRACE("<-- E_NOINTERFACE\n"); - return E_NOINTERFACE; - } - - IStorage_AddRef(iface); - TRACE("<-- %p\n", *ppvObject); - return S_OK; -} - -/************************************************************************ - * StorageBaseImpl_AddRef (IUnknown) - * - * This method implements the common AddRef for all IStorage - * implementations contained in this file. - * - * See Windows documentation for more details on IUnknown methods. - */ -static ULONG WINAPI StorageBaseImpl_AddRef( - IStorage* iface) -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("%p, refcount %lu.\n", iface, 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, refcount %lu.\n", iface, ref); - - if (ref == 0) - { - /* - * 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); - } - - 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 ) - { - if ( wcscmp(data.name, *snb) == 0 ) - skip = TRUE; - ++snb; - } - } - - 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) - { - 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 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 ); - - /* - * 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; - } - } - - if (hr == S_OK) - { - /* - * Get the size of the source stream - */ - IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME ); - - /* - * Set the size of the destination stream. - */ - IStream_SetSize(pstrTmp, strStat.cbSize); - - /* - * do the copy - */ - hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize, - NULL, NULL ); - - IStream_Release( pstrChild ); - } - - IStream_Release( pstrTmp ); - } - } - - /* copy siblings */ - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage, - skip_stream, snbExclude, pstgDest ); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage, - skip_stream, snbExclude, pstgDest ); - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) -{ - StgStreamImpl *strm; - - TRACE("%p, %ld.\n", stg, streamEntry); - - LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry) - { - if (strm->dirEntry == streamEntry) - { - return TRUE; - } - } - - return FALSE; -} - -static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry) -{ - StorageInternalImpl *childstg; - - TRACE("%p, %ld.\n", stg, storageEntry); - - LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) - { - if (childstg->base.storageDirEntry == storageEntry) - { - return TRUE; - } - } - - return FALSE; -} - -/************************************************************************ - * StorageBaseImpl_OpenStream (IStorage) - * - * This method will open the specified stream object from the current storage. - * - * See Windows documentation for more details on IStorage methods. - */ -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] */ -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - StgStreamImpl* newStream; - DirEntry currentEntry; - DirRef streamEntryRef; - HRESULT res = STG_E_UNKNOWN; - - TRACE("%p, %s, %p, %#lx, %ld, %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; - } - - /* - * As documented. - */ - if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) ) - { - res = STG_E_INVALIDFUNCTION; - goto end; - } - - if (This->reverted) - { - res = STG_E_REVERTED; - goto end; - } - - /* - * 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; - } - } - - /* - * Search for the element with the given name - */ - streamEntryRef = findElement( - This, - This->storageDirEntry, - pwcsName, - ¤tEntry); - - /* - * 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; - } - - newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef); - - if (newStream) - { - newStream->grfMode = grfMode; - *ppstm = &newStream->IStream_iface; - - IStream_AddRef(*ppstm); - - res = S_OK; - goto end; - } - - res = E_OUTOFMEMORY; - goto end; - } - - res = STG_E_FILENOTFOUND; - -end: - if (res == S_OK) - TRACE("<-- IStream %p\n", *ppstm); - TRACE("<-- %#lx\n", res); - return res; -} - -/************************************************************************ - * StorageBaseImpl_OpenStorage (IStorage) - * - * This method will open a new storage object from the current storage. - * - * See Windows documentation for more details on IStorage methods. - */ -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] */ -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - StorageInternalImpl* newStorage; - StorageBaseImpl* newTransactedStorage; - DirEntry currentEntry; - DirRef storageEntryRef; - HRESULT res = STG_E_UNKNOWN; - - TRACE("%p, %s, %p, %#lx, %p, %ld, %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; - } - - /* - * As documented. - */ - 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; - - /* - * 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; - } - } - - *ppstg = NULL; - - storageEntryRef = findElement( - This, - This->storageDirEntry, - pwcsName, - ¤tEntry); - - 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; - } - - newStorage = StorageInternalImpl_Construct( - This, - grfMode, - storageEntryRef); - - if (newStorage != 0) - { - if (grfMode & STGM_TRANSACTED) - { - res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage); - - if (FAILED(res)) - { - HeapFree(GetProcessHeap(), 0, newStorage); - goto end; - } - - *ppstg = &newTransactedStorage->IStorage_iface; - } - else - { - *ppstg = &newStorage->base.IStorage_iface; - } - - list_add_tail(&This->storageHead, &newStorage->ParentListEntry); - - res = S_OK; - goto end; - } - - res = STG_E_INSUFFICIENTMEMORY; - goto end; - } - - res = STG_E_FILENOTFOUND; - -end: - TRACE("<-- %#lx\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, %ld, %p, %ld, %p.\n", iface, reserved1, reserved2, reserved3, ppenum); - - if (!ppenum) - return E_INVALIDARG; - - if (This->reverted) - return STG_E_REVERTED; - - newEnum = IEnumSTATSTGImpl_Construct( - This, - This->storageDirEntry); - - if (newEnum) - { - *ppenum = &newEnum->IEnumSTATSTG_iface; - return S_OK; - } - - return E_OUTOFMEMORY; -} - -/************************************************************************ - * 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; - - TRACE("%p, %p, %#lx.\n", iface, pstatstg, grfStatFlag); - - if (!pstatstg) - { - res = E_INVALIDARG; - goto end; - } - - if (This->reverted) - { - res = STG_E_REVERTED; - goto end; - } - - res = StorageBaseImpl_ReadDirEntry( - This, - This->storageDirEntry, - ¤tEntry); - - if (SUCCEEDED(res)) - { - StorageUtl_CopyDirEntryToSTATSTG( - This, - pstatstg, - ¤tEntry, - grfStatFlag); - - pstatstg->grfMode = This->openFlags; - pstatstg->grfStateBits = This->stateBits; - } - -end: - if (res == S_OK) - { - TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %#lx, grfLocksSupported: %ld, grfStateBits: %#lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.LowPart, pstatstg->cbSize.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits); - } - TRACE("<-- %#lx\n", res); - return res; -} - -/************************************************************************ - * 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; - - 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); - - if (currentEntryRef != DIRENTRY_NULL) - { - /* - * There is already an element with the new name - */ - return STG_E_FILEALREADYEXISTS; - } - - /* - * Search for the old element name - */ - currentEntryRef = findElement(This, - This->storageDirEntry, - pwcsOldName, - ¤tEntry); - - 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; - } - - /* Remove the element from its current position in the tree */ - removeFromTree(This, This->storageDirEntry, - currentEntryRef); - - /* Change the name of the element */ - lstrcpyW(currentEntry.name, pwcsNewName); - - /* Delete any sibling links */ - currentEntry.leftChild = DIRENTRY_NULL; - currentEntry.rightChild = DIRENTRY_NULL; - - StorageBaseImpl_WriteDirEntry(This, currentEntryRef, - ¤tEntry); - - /* 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 StorageBaseImpl_Flush(This); -} - -/************************************************************************ - * 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] */ -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - StgStreamImpl* newStream; - DirEntry currentEntry, newStreamEntry; - DirRef currentEntryRef, newStreamEntryRef; - HRESULT hr; - - TRACE("%p, %s, %#lx, %ld, %ld, %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; - - if (This->reverted) - return STG_E_REVERTED; - - /* - * As documented. - */ - if ((grfMode & STGM_DELETEONRELEASE) || - (grfMode & STGM_TRANSACTED)) - return STG_E_INVALIDFUNCTION; - - /* - * 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; - - /* 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(This->openFlags & STGM_SIMPLE) - if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG; - - *ppstm = 0; - - currentEntryRef = findElement(This, - This->storageDirEntry, - pwcsName, - ¤tEntry); - - 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; - } - - /* - * 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; - - lstrcpyW(newStreamEntry.name, pwcsName); - - newStreamEntry.stgType = STGTY_STREAM; - newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN; - newStreamEntry.size.LowPart = 0; - newStreamEntry.size.HighPart = 0; - - newStreamEntry.leftChild = DIRENTRY_NULL; - newStreamEntry.rightChild = DIRENTRY_NULL; - newStreamEntry.dirRootEntry = DIRENTRY_NULL; - - /* call CoFileTime to get the current time - newStreamEntry.ctime - newStreamEntry.mtime - */ - - /* newStreamEntry.clsid */ - - /* - * Create an entry with the new data - */ - hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef); - if (FAILED(hr)) - return hr; - - /* - * Insert the new entry in the parent storage's tree. - */ - hr = insertIntoTree( - This, - This->storageDirEntry, - newStreamEntryRef); - if (FAILED(hr)) - { - StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef); - return hr; - } - - /* - * Open the stream to return it. - */ - newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef); - - if (newStream) - { - *ppstm = &newStream->IStream_iface; - IStream_AddRef(*ppstm); - } - else - { - return STG_E_INSUFFICIENTMEMORY; - } - - return StorageBaseImpl_Flush(This); -} - -/************************************************************************ - * 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; - - TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid)); - - if (This->reverted) - return STG_E_REVERTED; - - hRes = StorageBaseImpl_ReadDirEntry(This, - This->storageDirEntry, - ¤tEntry); - if (SUCCEEDED(hRes)) - { - currentEntry.clsid = *clsid; - - hRes = StorageBaseImpl_WriteDirEntry(This, - This->storageDirEntry, - ¤tEntry); - } - - if (SUCCEEDED(hRes)) - hRes = StorageBaseImpl_Flush(This); - - return hRes; -} - -/************************************************************************ - * 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] */ -{ - StorageBaseImpl* This = impl_from_IStorage(iface); - - DirEntry currentEntry; - DirEntry newEntry; - DirRef currentEntryRef; - DirRef newEntryRef; - HRESULT hr; - - TRACE("%p, %s, %#lx, %ld, %ld, %p.\n", iface, debugstr_w(pwcsName), grfMode, - reserved1, reserved2, ppstg); - - if (ppstg == 0) - return STG_E_INVALIDPOINTER; - - if (This->openFlags & STGM_SIMPLE) - { - return STG_E_INVALIDFUNCTION; - } - - if (pwcsName == 0) - return STG_E_INVALIDNAME; - - *ppstg = NULL; - - if ( FAILED( validateSTGM(grfMode) ) || - (grfMode & STGM_DELETEONRELEASE) ) - { - WARN("bad grfMode: %#lx\n", grfMode); - return STG_E_INVALIDFLAG; - } - - if (This->reverted) - return STG_E_REVERTED; - - /* - * 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; - } - - currentEntryRef = findElement(This, - This->storageDirEntry, - pwcsName, - ¤tEntry); - - 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; - } - - memset(&newEntry, 0, sizeof(DirEntry)); - - newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR); - - if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) - { - FIXME("name too long\n"); - return STG_E_INVALIDNAME; - } - - lstrcpyW(newEntry.name, pwcsName); - - newEntry.stgType = STGTY_STORAGE; - newEntry.startingBlock = BLOCK_END_OF_CHAIN; - newEntry.size.LowPart = 0; - newEntry.size.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 */ - - /* - * Create a new directory entry for the storage - */ - hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef); - if (FAILED(hr)) - return hr; - - /* - * 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; - } - - /* - * Open it to get a pointer to return. - */ - hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg); - - if( (hr != S_OK) || (*ppstg == NULL)) - { - return hr; - } - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_Flush(This); - - return S_OK; -} - -static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, - DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, - SNB snbExclude, IStorage *pstgDest) -{ - DirEntry data; - HRESULT hr; - - hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); - - if (SUCCEEDED(hr)) - hr = IStorage_SetClass( pstgDest, &data.clsid ); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage, - skip_stream, snbExclude, pstgDest ); - - TRACE("<-- %#lx\n", hr); - return hr; -} - -/************************************************************************* - * CopyTo (IStorage) - */ -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] */ -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - - BOOL skip_storage = FALSE, skip_stream = FALSE; - DWORD i; - - TRACE("%p, %ld, %p, %p, %p.\n", iface, ciidExclude, rgiidExclude, snbExclude, pstgDest); - - if ( pstgDest == 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 (!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; - - /* Go up the chain from the destination until we find the source storage. */ - while (pstgDestAncestor != iface) { - pstgDestAncestorChild = pstgDest; - - if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl) - { - TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor; - - pstgDestAncestor = &snapshot->transactedParent->IStorage_iface; - } - else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl) - { - StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor; - - pstgDestAncestor = &internal->parentStorage->IStorage_iface; - } - else - break; - } - - if (pstgDestAncestor == iface) - { - BOOL fail = TRUE; - - if (pstgDestAncestorChild && snbExclude) - { - StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild; - DirEntry data; - WCHAR **snb = snbExclude; - - StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data); - - while ( *snb != NULL && fail ) - { - if ( wcscmp(data.name, *snb) == 0 ) - fail = FALSE; - ++snb; - } - } - - if (fail) - return STG_E_ACCESSDENIED; - } - } - - return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry, - skip_storage, skip_stream, snbExclude, pstgDest ); -} - -/************************************************************************* - * 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, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest, - debugstr_w(pwcsNewName), grfFlags); - return E_NOTIMPL; -} - -/************************************************************************* - * 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_Commit( - IStorage* iface, - DWORD grfCommitFlags)/* [in] */ -{ - StorageBaseImpl* This = impl_from_IStorage(iface); - TRACE("%p, %#lx.\n", iface, grfCommitFlags); - return StorageBaseImpl_Flush(This); -} - -/************************************************************************* - * Revert (IStorage) - * - * Discard all changes that have been made since the last commit operation - */ -static HRESULT WINAPI StorageBaseImpl_Revert( - IStorage* iface) -{ - TRACE("(%p)\n", iface); - return S_OK; -} - -/********************************************************************* - * - * Internal helper function for StorageBaseImpl_DestroyElement() - * - * 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; - - TRACE("%p, %ld.\n", parentStorage, indexToDelete); - - /* Invalidate any open storage objects. */ - LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry) - { - if (stg->base.storageDirEntry == indexToDelete) - { - StorageBaseImpl_Invalidate(&stg->base); - } - } - - /* - * Open the storage and enumerate it - */ - hr = IStorage_OpenStorage( - &parentStorage->IStorage_iface, - entryDataToDelete.name, - 0, - STGM_WRITE | STGM_SHARE_EXCLUSIVE, - 0, - 0, - &childStorage); - - if (hr != S_OK) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - /* - * Enumerate the elements - */ - hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements); - if (FAILED(hr)) - { - IStorage_Release(childStorage); - TRACE("<-- %#lx\n", hr); - return hr; - } - - do - { - /* - * Obtain the next element - */ - hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL); - if (hr==S_OK) - { - destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName); - - CoTaskMemFree(currentElement.pwcsName); - } - - /* - * We need to Reset the enumeration every time because we delete elements - * and the enumeration could be invalid - */ - IEnumSTATSTG_Reset(elements); - - } while ((hr == S_OK) && (destroyHr == S_OK)); - - IStorage_Release(childStorage); - IEnumSTATSTG_Release(elements); - - TRACE("%#lx\n", hr); - return destroyHr; -} - -/********************************************************************* - * - * Internal helper function for StorageBaseImpl_DestroyElement() - * - * Perform the deletion of a stream's data - * - */ -static HRESULT deleteStreamContents( - StorageBaseImpl *parentStorage, - DirRef indexToDelete, - DirEntry entryDataToDelete) -{ - IStream *pis; - HRESULT hr; - ULARGE_INTEGER size; - StgStreamImpl *strm, *strm2; - - /* 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); - } - } - - size.HighPart = 0; - size.LowPart = 0; - - hr = IStorage_OpenStream(&parentStorage->IStorage_iface, - entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); - - if (hr!=S_OK) - { - TRACE("<-- %#lx\n", hr); - return(hr); - } - - /* - * Zap the stream - */ - hr = IStream_SetSize(pis, size); - - if(hr != S_OK) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - /* - * Release the stream object. - */ - IStream_Release(pis); - TRACE("<-- %#lx\n", hr); - return S_OK; -} - -/************************************************************************* - * 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 allows reinvoking 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); - - HRESULT hr = S_OK; - DirEntry entryToDelete; - DirRef entryToDeleteRef; - - TRACE("(%p, %s)\n", - iface, debugstr_w(pwcsName)); - - if (pwcsName==NULL) - return STG_E_INVALIDPOINTER; - - if (This->reverted) - return STG_E_REVERTED; - - if ( !(This->openFlags & STGM_TRANSACTED) && - STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) - return STG_E_ACCESSDENIED; - - entryToDeleteRef = findElement( - This, - This->storageDirEntry, - pwcsName, - &entryToDelete); - - if ( entryToDeleteRef == DIRENTRY_NULL ) - { - TRACE("<-- STG_E_FILENOTFOUND\n"); - return STG_E_FILENOTFOUND; - } - - if ( entryToDelete.stgType == STGTY_STORAGE ) - { - hr = deleteStorageContents( - This, - entryToDeleteRef, - entryToDelete); - } - else if ( entryToDelete.stgType == STGTY_STREAM ) - { - hr = deleteStreamContents( - This, - entryToDeleteRef, - entryToDelete); - } - - if (hr!=S_OK) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - /* - * Remove the entry from its parent storage - */ - hr = removeFromTree( - This, - This->storageDirEntry, - entryToDeleteRef); - - /* - * Invalidate the entry - */ - if (SUCCEEDED(hr)) - StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_Flush(This); - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg) -{ - struct list *cur, *cur2; - StgStreamImpl *strm=NULL; - StorageInternalImpl *childstg=NULL; - - 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); - } - - LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) { - childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry); - StorageBaseImpl_Invalidate( &childstg->base ); - } - - if (stg->transactedChild) - { - StorageBaseImpl_Invalidate(stg->transactedChild); - - stg->transactedChild = NULL; - } -} - -/****************************************************************************** - * 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; -} - -/****************************************************************************** - * SetStateBits (IStorage) - */ -static HRESULT WINAPI StorageBaseImpl_SetStateBits( - 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; -} - -/****************************************************************************** - * Internal stream list handlers - */ - -void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) -{ - TRACE("Stream added (stg=%p strm=%p)\n", stg, strm); - list_add_tail(&stg->strmHead,&strm->StrmListEntry); -} - -void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) -{ - TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm); - list_remove(&(strm->StrmListEntry)); -} - -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; - - hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata); - - if (SUCCEEDED(hr)) - { - hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size); - - bytes_copied.QuadPart = 0; - while (bytes_copied.QuadPart < srcdata.size.QuadPart && 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 (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT; - bytes_copied.QuadPart += byteswritten; - } - } - } - - return hr; -} - -static HRESULT StorageBaseImpl_DupStorageTree( - StorageBaseImpl *dst, DirRef *dst_entry, - StorageBaseImpl *src, DirRef src_entry) -{ - HRESULT hr; - DirEntry data; - BOOL has_stream=FALSE; - - if (src_entry == DIRENTRY_NULL) - { - *dst_entry = DIRENTRY_NULL; - return S_OK; - } - - hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data); - if (SUCCEEDED(hr)) - { - 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); - } - - 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); - - return hr; -} - -static HRESULT StorageBaseImpl_CopyStorageTree( - StorageBaseImpl *dst, DirRef dst_entry, - StorageBaseImpl *src, DirRef src_entry) -{ - HRESULT hr; - DirEntry src_data, dst_data; - DirRef new_root_entry; - - hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data); - - if (SUCCEEDED(hr)) - { - hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry); - } - - 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; - } - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data); - - return hr; -} - -static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings) -{ - HRESULT hr; - DirEntry data; - ULARGE_INTEGER zero; - - if (entry == DIRENTRY_NULL) - return S_OK; - - zero.QuadPart = 0; - - hr = StorageBaseImpl_ReadDirEntry(This, entry, &data); - - if (SUCCEEDED(hr) && include_siblings) - hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE); - - if (SUCCEEDED(hr) && include_siblings) - hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE); - - if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM) - hr = StorageBaseImpl_StreamSetSize(This, entry, zero); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_DestroyDirEntry(This, entry); - - return hr; -} - - -/************************************************************************ - * StorageImpl implementation - ***********************************************************************/ - -static HRESULT StorageImpl_ReadAt(StorageImpl* This, - ULARGE_INTEGER offset, - void* buffer, - ULONG size, - ULONG* bytesRead) -{ - return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead); -} - -static HRESULT StorageImpl_WriteAt(StorageImpl* This, - ULARGE_INTEGER offset, - const void* buffer, - const ULONG size, - ULONG* bytesWritten) -{ - return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten); -} - -/****************************************************************************** - * StorageImpl_LoadFileHeader - * - * This method will read in the file header - */ -static HRESULT StorageImpl_LoadFileHeader( - StorageImpl* This) -{ - HRESULT hr; - BYTE headerBigBlock[HEADER_SIZE]; - int index; - ULARGE_INTEGER offset; - DWORD bytes_read; - - TRACE("\n"); - /* - * Get a pointer to the big block of data containing the header. - */ - offset.HighPart = 0; - offset.LowPart = 0; - hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); - if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) - hr = STG_E_FILENOTFOUND; - - /* - * 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; - } - - if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0) - { - return STG_E_INVALIDHEADER; - } - - StorageUtl_ReadWord( - headerBigBlock, - OFFSET_BIGBLOCKSIZEBITS, - &This->bigBlockSizeBits); - - StorageUtl_ReadWord( - headerBigBlock, - OFFSET_SMALLBLOCKSIZEBITS, - &This->smallBlockSizeBits); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_BBDEPOTCOUNT, - &This->bigBlockDepotCount); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_ROOTSTARTBLOCK, - &This->rootStartBlock); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_TRANSACTIONSIG, - &This->transactionSig); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_SMALLBLOCKLIMIT, - &This->smallBlockLimit); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_SBDEPOTSTART, - &This->smallBlockDepotStart); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_EXTBBDEPOTSTART, - &This->extBigBlockDepotStart); - - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_EXTBBDEPOTCOUNT, - &This->extBigBlockDepotCount); - - for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) - { - StorageUtl_ReadDWord( - headerBigBlock, - OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), - &(This->bigBlockDepotStart[index])); - } - - /* - * 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=%#lx, smallblock=%#lx, sblimit=%#lx\n", - This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); - hr = STG_E_INVALIDHEADER; - } - else - hr = S_OK; - } - - return hr; -} - -/****************************************************************************** - * StorageImpl_SaveFileHeader - * - * This method will save to the file the header - */ -static void StorageImpl_SaveFileHeader( - StorageImpl* This) -{ - BYTE headerBigBlock[HEADER_SIZE]; - int index; - ULARGE_INTEGER offset; - DWORD bytes_written; - DWORD major_version, dirsectorcount; - - 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; - } - - memset(headerBigBlock, 0, HEADER_SIZE); - memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic)); - - /* - * Write the information to the header. - */ - StorageUtl_WriteWord( - headerBigBlock, - OFFSET_MINORVERSION, - 0x3e); - - StorageUtl_WriteWord( - headerBigBlock, - OFFSET_MAJORVERSION, - major_version); - - StorageUtl_WriteWord( - headerBigBlock, - OFFSET_BYTEORDERMARKER, - (WORD)-2); - - StorageUtl_WriteWord( - headerBigBlock, - OFFSET_BIGBLOCKSIZEBITS, - This->bigBlockSizeBits); - - StorageUtl_WriteWord( - headerBigBlock, - OFFSET_SMALLBLOCKSIZEBITS, - This->smallBlockSizeBits); - - 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; - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_DIRSECTORCOUNT, - dirsectorcount); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_BBDEPOTCOUNT, - This->bigBlockDepotCount); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_ROOTSTARTBLOCK, - This->rootStartBlock); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_TRANSACTIONSIG, - This->transactionSig); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_SMALLBLOCKLIMIT, - This->smallBlockLimit); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_SBDEPOTSTART, - This->smallBlockDepotStart); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_SBDEPOTCOUNT, - This->smallBlockDepotChain ? - BlockChainStream_GetCount(This->smallBlockDepotChain) : 0); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_EXTBBDEPOTSTART, - This->extBigBlockDepotStart); - - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_EXTBBDEPOTCOUNT, - This->extBigBlockDepotCount); - - for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) - { - StorageUtl_WriteDWord( - headerBigBlock, - OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), - (This->bigBlockDepotStart[index])); - } - - offset.QuadPart = 0; - StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written); -} - - -/************************************************************************ - * StorageImpl implementation : DirEntry methods - ***********************************************************************/ - -/****************************************************************************** - * StorageImpl_ReadRawDirEntry - * - * This method will read the raw data from a directory entry in the file. - * - * buffer must be RAW_DIRENTRY_SIZE bytes long. - */ -static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer) -{ - ULARGE_INTEGER offset; - HRESULT hr; - ULONG bytesRead; - - offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; - - hr = BlockChainStream_ReadAt( - This->rootBlockChain, - offset, - RAW_DIRENTRY_SIZE, - buffer, - &bytesRead); - - if (bytesRead != RAW_DIRENTRY_SIZE) - return STG_E_READFAULT; - - return hr; -} - -/****************************************************************************** - * StorageImpl_WriteRawDirEntry - * - * This method will write the raw data from a directory entry in the file. - * - * buffer must be RAW_DIRENTRY_SIZE bytes long. - */ -static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer) -{ - ULARGE_INTEGER offset; - ULONG bytesRead; - - offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; - - return BlockChainStream_WriteAt( - This->rootBlockChain, - offset, - RAW_DIRENTRY_SIZE, - buffer, - &bytesRead); -} - -/*************************************************************************** - * - * 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; - - memset(emptyData, 0, RAW_DIRENTRY_SIZE); - - return StorageImpl_WriteRawDirEntry(storage, index, emptyData); -} - -/****************************************************************************** - * UpdateRawDirEntry - * - * Update raw directory entry data from the fields in newData. - * - * buffer must be RAW_DIRENTRY_SIZE bytes long. - */ -static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData) -{ - memset(buffer, 0, RAW_DIRENTRY_SIZE); - - memcpy( - buffer + OFFSET_PS_NAME, - newData->name, - DIRENTRY_NAME_BUFFER_LEN ); - - memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1); - - StorageUtl_WriteWord( - buffer, - OFFSET_PS_NAMELENGTH, - newData->sizeOfNameString); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_LEFTCHILD, - newData->leftChild); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_RIGHTCHILD, - newData->rightChild); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_DIRROOT, - newData->dirRootEntry); - - StorageUtl_WriteGUID( - buffer, - OFFSET_PS_GUID, - &newData->clsid); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_CTIMELOW, - newData->ctime.dwLowDateTime); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_CTIMEHIGH, - newData->ctime.dwHighDateTime); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_MTIMELOW, - newData->mtime.dwLowDateTime); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_MTIMEHIGH, - newData->ctime.dwHighDateTime); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_STARTBLOCK, - newData->startingBlock); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_SIZE, - newData->size.LowPart); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_SIZE_HIGH, - newData->size.HighPart); -} - -/*************************************************************************** - * - * Internal Method - * - * Reserve a directory entry in the file and initialize it. - */ -static HRESULT StorageImpl_CreateDirEntry( - StorageBaseImpl *base, - const DirEntry *newData, - DirRef *index) -{ - StorageImpl *storage = (StorageImpl*)base; - ULONG currentEntryIndex = 0; - ULONG newEntryIndex = DIRENTRY_NULL; - HRESULT hr = S_OK; - BYTE currentData[RAW_DIRENTRY_SIZE]; - WORD sizeOfNameString; - - do - { - hr = StorageImpl_ReadRawDirEntry(storage, - currentEntryIndex, - currentData); - - if (SUCCEEDED(hr)) - { - StorageUtl_ReadWord( - currentData, - OFFSET_PS_NAMELENGTH, - &sizeOfNameString); - - 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++; - - } while (newEntryIndex == DIRENTRY_NULL); - - /* - * grow the directory stream - */ - if (FAILED(hr)) - { - BYTE emptyData[RAW_DIRENTRY_SIZE]; - ULARGE_INTEGER newSize; - ULONG entryIndex; - ULONG lastEntry = 0; - ULONG blockCount = 0; - - /* - * obtain the new count of blocks in the directory stream - */ - blockCount = BlockChainStream_GetCount( - storage->rootBlockChain)+1; - - /* - * initialize the size used by the directory stream - */ - newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount; - - /* - * add a block to the directory stream - */ - BlockChainStream_SetSize(storage->rootBlockChain, newSize); - - /* - * memset the empty entry in order to initialize the unused newly - * created entries - */ - memset(emptyData, 0, RAW_DIRENTRY_SIZE); - - /* - * initialize them - */ - lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount; - - for( - entryIndex = newEntryIndex + 1; - entryIndex < lastEntry; - entryIndex++) - { - StorageImpl_WriteRawDirEntry( - storage, - entryIndex, - emptyData); - } - - StorageImpl_SaveFileHeader(storage); - } - - UpdateRawDirEntry(currentData, newData); - - hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData); - - if (SUCCEEDED(hr)) - *index = newEntryIndex; - - return hr; -} - -/****************************************************************************** - * StorageImpl_ReadDirEntry - * - * This method will read the specified directory entry. - */ -static HRESULT StorageImpl_ReadDirEntry( - StorageImpl* This, - DirRef index, - DirEntry* buffer) -{ - BYTE currentEntry[RAW_DIRENTRY_SIZE]; - HRESULT readRes; - - readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry); - - if (SUCCEEDED(readRes)) - { - 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); - - StorageUtl_ReadWord( - currentEntry, - OFFSET_PS_NAMELENGTH, - &buffer->sizeOfNameString); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_LEFTCHILD, - &buffer->leftChild); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_RIGHTCHILD, - &buffer->rightChild); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_DIRROOT, - &buffer->dirRootEntry); - - StorageUtl_ReadGUID( - currentEntry, - OFFSET_PS_GUID, - &buffer->clsid); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_CTIMELOW, - &buffer->ctime.dwLowDateTime); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_CTIMEHIGH, - &buffer->ctime.dwHighDateTime); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_MTIMELOW, - &buffer->mtime.dwLowDateTime); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_MTIMEHIGH, - &buffer->mtime.dwHighDateTime); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_STARTBLOCK, - &buffer->startingBlock); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_SIZE, - &buffer->size.LowPart); - - if (This->bigBlockSize < 4096) - { - /* Version 3 files may have junk in the high part of size. */ - buffer->size.HighPart = 0; - } - else - { - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_SIZE_HIGH, - &buffer->size.HighPart); - } - } - - return readRes; -} - -/********************************************************************* - * Write the specified directory entry to the file - */ -static HRESULT StorageImpl_WriteDirEntry( - StorageImpl* This, - DirRef index, - const DirEntry* buffer) -{ - BYTE currentEntry[RAW_DIRENTRY_SIZE]; - - UpdateRawDirEntry(currentEntry, buffer); - - return StorageImpl_WriteRawDirEntry(This, index, currentEntry); -} - - -/************************************************************************ - * StorageImpl implementation : Block methods - ***********************************************************************/ - -static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index) -{ - return (ULONGLONG)(index+1) * This->bigBlockSize; -} - -static HRESULT StorageImpl_ReadBigBlock( - StorageImpl* This, - ULONG blockIndex, - void* buffer, - ULONG* out_read) -{ - ULARGE_INTEGER ulOffset; - DWORD read=0; - HRESULT hr; - - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); - - hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read); - - if (SUCCEEDED(hr) && read < This->bigBlockSize) - { - /* File ends during this block; fill the rest with 0's. */ - memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read); - } - - if (out_read) *out_read = read; - - return hr; -} - -static BOOL StorageImpl_ReadDWordFromBigBlock( - StorageImpl* This, - ULONG blockIndex, - ULONG offset, - DWORD* value) -{ - ULARGE_INTEGER ulOffset; - DWORD read; - DWORD tmp; - - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); - ulOffset.QuadPart += offset; - - StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read); - *value = lendian32toh(tmp); - return (read == sizeof(DWORD)); -} - -static BOOL StorageImpl_WriteBigBlock( - StorageImpl* This, - ULONG blockIndex, - const void* buffer) -{ - ULARGE_INTEGER ulOffset; - DWORD wrote; - - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); - - StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote); - return (wrote == This->bigBlockSize); -} - -static BOOL StorageImpl_WriteDWordToBigBlock( - StorageImpl* This, - ULONG blockIndex, - ULONG offset, - DWORD value) -{ - ULARGE_INTEGER ulOffset; - DWORD wrote; - - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); - ulOffset.QuadPart += offset; - - value = htole32(value); - StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote); - return (wrote == sizeof(DWORD)); -} - -/****************************************************************************** - * Storage32Impl_SmallBlocksToBigBlocks - * - * This method will convert a small block chain to a big block chain. - * The small block chain will be destroyed. - */ -static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( - StorageImpl* This, - SmallBlockChainStream** ppsbChain) -{ - 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; - - /* - * 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); - - /* - * Copy the contents of the small block chain to the big block chain - * by small block size increments. - */ - offset.LowPart = 0; - offset.HighPart = 0; - cbTotalRead.QuadPart = 0; - - buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); - do - { - resRead = SmallBlockChainStream_ReadAt(*ppsbChain, - offset, - min(This->smallBlockSize, size.LowPart - offset.LowPart), - buffer, - &cbRead); - if (FAILED(resRead)) - break; - - if (cbRead > 0) - { - cbTotalRead.QuadPart += cbRead; - - resWrite = BlockChainStream_WriteAt(bbTempChain, - offset, - cbRead, - buffer, - &cbWritten); - - if (FAILED(resWrite)) - break; - - offset.LowPart += cbRead; - } - else - { - resRead = STG_E_READFAULT; - break; - } - } while (cbTotalRead.QuadPart < size.QuadPart); - HeapFree(GetProcessHeap(),0,buffer); - - size.HighPart = 0; - size.LowPart = 0; - - if (FAILED(resRead) || FAILED(resWrite)) - { - ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite); - BlockChainStream_SetSize(bbTempChain, size); - BlockChainStream_Destroy(bbTempChain); - return NULL; - } - - /* - * 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; -} - -/****************************************************************************** - * Storage32Impl_BigBlocksToSmallBlocks - * - * This method will convert a big block chain to a small block chain. - * The big block chain will be destroyed on success. - */ -static 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; - - TRACE("%p %p\n", This, ppbbChain); - - sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain, - DIRENTRY_NULL); - - if(!sbTempChain) - return NULL; - - SmallBlockChainStream_SetSize(sbTempChain, newSize); - size = BlockChainStream_GetSize(*ppbbChain); - size.QuadPart = min(size.QuadPart, newSize.QuadPart); - - offset.HighPart = 0; - offset.LowPart = 0; - cbTotalRead.QuadPart = 0; - buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize); - while(cbTotalRead.QuadPart < size.QuadPart) - { - resRead = BlockChainStream_ReadAt(*ppbbChain, offset, - min(This->bigBlockSize, size.LowPart - offset.LowPart), - buffer, &cbRead); - - if(FAILED(resRead)) - break; - - if(cbRead > 0) - { - cbTotalRead.QuadPart += cbRead; - - resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset, - cbRead, buffer, &cbWritten); - - if(FAILED(resWrite)) - break; - - offset.LowPart += cbRead; - } - else - { - resRead = STG_E_READFAULT; - break; - } - } - HeapFree(GetProcessHeap(), 0, buffer); - - size.HighPart = 0; - size.LowPart = 0; - - if(FAILED(resRead) || FAILED(resWrite)) - { - ERR("conversion failed: resRead = %#lx, resWrite = %#lx\n", resRead, resWrite); - SmallBlockChainStream_SetSize(sbTempChain, size); - SmallBlockChainStream_Destroy(sbTempChain); - return NULL; - } - - /* destroy the original big block chain */ - streamEntryRef = (*ppbbChain)->ownerDirEntry; - BlockChainStream_SetSize(*ppbbChain, size); - BlockChainStream_Destroy(*ppbbChain); - *ppbbChain = NULL; - - StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); - streamEntry.startingBlock = sbHeadOfChain; - StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); - - SmallBlockChainStream_Destroy(sbTempChain); - return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); -} - -/****************************************************************************** - * 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) -{ - BYTE blockBuffer[MAX_BIG_BLOCK_SIZE]; - ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1; - ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); - ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot; - - /* - * Initialize blocks as free - */ - memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize); - - /* Reserve the range lock sector */ - if (depotIndex == rangeLockDepot) - { - ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN; - } - - StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer); -} - -/****************************************************************************** - * 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) -{ - 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); - - if (extBlockCount >= This->extBigBlockDepotCount) - return BLOCK_UNUSED; - - if (This->indexExtBlockDepotCached != extBlockCount) - { - 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; - } - - 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) - { - StorageImpl_WriteDWordToBigBlock(This, extBlockIndex, - extBlockOffset * sizeof(ULONG), - blockIndex); - } - - if (This->indexExtBlockDepotCached == extBlockCount) - { - This->extBlockDepotCached[extBlockOffset] = blockIndex; - } -} - -/****************************************************************************** - * 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; - - index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) * - blocksPerDepotBlock; - - if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN)) - { - /* - * The first extended block. - */ - This->extBigBlockDepotStart = index; - } - 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); - } - - /* - * Initialize this block. - */ - memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize); - StorageImpl_WriteBigBlock(This, index, depotBuffer); - - /* 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); - - memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize); - HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); - - This->extBigBlockDepotLocations = new_cache; - This->extBigBlockDepotLocationsSize = new_cache_size; - } - This->extBigBlockDepotLocations[numExtBlocks] = index; - - return index; -} - -/************************************************************************ - * 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; - - *nextBlockIndex = BLOCK_SPECIAL; - - if(depotBlockCount >= This->bigBlockDepotCount) - { - WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount, This->bigBlockDepotCount); - return STG_E_READFAULT; - } - - /* - * Cache the currently accessed depot block. - */ - if (depotBlockCount != This->indexBlockDepotCached) - { - This->indexBlockDepotCached = depotBlockCount; - - if (depotBlockCount < COUNT_BBDEPOTINHEADER) - { - depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; - } - else - { - /* - * We have to look in the extended depot. - */ - depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); - } - - StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); - - if (!read) - return STG_E_READFAULT; - - num_blocks = This->bigBlockSize / 4; - - for (index = 0; index < num_blocks; index++) - { - StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex); - This->blockDepotCached[index] = *nextBlockIndex; - } - } - - *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; - - return S_OK; -} - -/****************************************************************************** - * 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. - * - * Return values: - * - The index of the next extended block - * - BLOCK_UNUSED: there is no next extended block. - * - Any other return values denotes failure. - */ -static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) -{ - ULONG nextBlockIndex = BLOCK_SPECIAL; - ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); - - StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, - &nextBlockIndex); - - 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) - { - depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; - } - else - { - /* - * We have to look in the extended depot. - */ - depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); - } - - StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset, - nextBlock); - /* - * Update the cached block depot, if necessary. - */ - if (depotBlockCount == This->indexBlockDepotCached) - { - This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock; - } -} - -/****************************************************************************** - * 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 neededAddNumBlocks) -{ - 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; - - depotIndex = This->prevFreeBlock / blocksPerDepot; - depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG); - - /* - * 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]; - - /* - * Grow the primary depot. - */ - if (depotBlockIndexPos == BLOCK_UNUSED) - { - depotBlockIndexPos = depotIndex*blocksPerDepot; - - /* - * 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 - { - depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex); - - if (depotBlockIndexPos == BLOCK_UNUSED) - { - /* - * Grow the extended depot. - */ - ULONG extIndex = BLOCK_UNUSED; - ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; - ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1); - - if (extBlockOffset == 0) - { - /* We need an extended block. - */ - extIndex = Storage32Impl_AddExtBlockDepot(This); - This->extBigBlockDepotCount++; - depotBlockIndexPos = extIndex + 1; - } - else - depotBlockIndexPos = depotIndex * blocksPerDepot; - - /* - * Add a block depot and mark it in the extended block. - */ - Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex); - This->bigBlockDepotCount++; - Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos); - - /* Flag the block depot. - */ - StorageImpl_SetNextBlockInChain(This, - depotBlockIndexPos, - BLOCK_SPECIAL); - - /* If necessary, flag the extended depot block. - */ - if (extIndex != BLOCK_UNUSED) - StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT); - - /* Save header information. - */ - StorageImpl_SaveFileHeader(This); - } - } - - StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); - - if (read) - { - while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) && - ( nextBlockIndex != BLOCK_UNUSED)) - { - StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex); - - if (nextBlockIndex == BLOCK_UNUSED) - { - freeBlock = (depotIndex * blocksPerDepot) + - (depotBlockOffset/sizeof(ULONG)); - } - - depotBlockOffset += sizeof(ULONG); - } - } - - depotIndex++; - depotBlockOffset = 0; - } - - /* - * make sure that the block physically exists before using it - */ - neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize * neededAddNumBlocks; - - ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME); - - if (neededSize.QuadPart > statstg.cbSize.QuadPart) - ILockBytes_SetSize(This->lockBytes, neededSize); - - This->prevFreeBlock = freeBlock; - - return freeBlock; -} - -/****************************************************************************** - * StorageImpl_FreeBigBlock - * - * This method will flag the specified block as free in the big block depot. - */ -static void StorageImpl_FreeBigBlock( - StorageImpl* This, - ULONG blockIndex) -{ - StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); - - if (blockIndex < This->prevFreeBlock) - This->prevFreeBlock = blockIndex; -} - - -static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base, - DirRef index, const DirEntry *data) -{ - StorageImpl *This = (StorageImpl*)base; - return StorageImpl_WriteDirEntry(This, index, data); -} - -static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base, - DirRef index, DirEntry *data) -{ - StorageImpl *This = (StorageImpl*)base; - return StorageImpl_ReadDirEntry(This, index, data); -} - -static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This) -{ - int i; - - for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) - { - if (!This->blockChainCache[i]) - { - return &This->blockChainCache[i]; - } - } - - i = This->blockChainToEvict; - - BlockChainStream_Destroy(This->blockChainCache[i]); - This->blockChainCache[i] = NULL; - - This->blockChainToEvict++; - if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) - This->blockChainToEvict = 0; - - return &This->blockChainCache[i]; -} - -static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This, - DirRef index) -{ - int i, free_index=-1; - - 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]; - } - } - - if (free_index == -1) - { - free_index = This->blockChainToEvict; - - BlockChainStream_Destroy(This->blockChainCache[free_index]); - This->blockChainCache[free_index] = NULL; - - This->blockChainToEvict++; - if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) - This->blockChainToEvict = 0; - } - - This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index); - return &This->blockChainCache[free_index]; -} - -static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index) -{ - int i; - - for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) - { - if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index) - { - BlockChainStream_Destroy(This->blockChainCache[i]); - This->blockChainCache[i] = NULL; - return; - } - } -} - -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; - - hr = StorageImpl_ReadDirEntry(This, index, &data); - if (FAILED(hr)) return hr; - - if (data.size.QuadPart == 0) - { - *bytesRead = 0; - return S_OK; - } - - if (offset.QuadPart + size > data.size.QuadPart) - { - bytesToRead = data.size.QuadPart - offset.QuadPart; - } - else - { - bytesToRead = size; - } - - if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) - { - 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); - - return hr; - } -} - -static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index, - ULARGE_INTEGER newsize) -{ - StorageImpl *This = (StorageImpl*)base; - DirEntry data; - HRESULT hr; - SmallBlockChainStream *smallblock=NULL; - BlockChainStream **pbigblock=NULL, *bigblock=NULL; - - hr = StorageImpl_ReadDirEntry(This, index, &data); - if (FAILED(hr)) return hr; - - /* 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); - - if (data.size.QuadPart == newsize.QuadPart) - return S_OK; - - /* 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; - } - - /* 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; - } - - pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This); - *pbigblock = bigblock; - } - else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) - { - smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize); - if (!smallblock) - return E_FAIL; - } - - /* 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 HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, - ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) -{ - 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; - - if (newSize.QuadPart > data.size.QuadPart) - { - hr = StorageImpl_StreamSetSize(base, index, newSize); - if (FAILED(hr)) - return hr; - - hr = StorageImpl_ReadDirEntry(This, index, &data); - if (FAILED(hr)) return hr; - } - - if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) - { - SmallBlockChainStream *stream; - - stream = SmallBlockChainStream_Construct(This, NULL, index); - if (!stream) return E_OUTOFMEMORY; - - hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); - - 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); - } -} - -static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst, - DirRef src) -{ - StorageImpl *This = (StorageImpl*)base; - DirEntry dst_data, src_data; - HRESULT hr; - - hr = StorageImpl_ReadDirEntry(This, dst, &dst_data); - - if (SUCCEEDED(hr)) - hr = StorageImpl_ReadDirEntry(This, src, &src_data); - - if (SUCCEEDED(hr)) - { - StorageImpl_DeleteCachedBlockChainStream(This, src); - dst_data.startingBlock = src_data.startingBlock; - dst_data.size = src_data.size; - - hr = StorageImpl_WriteDirEntry(This, dst, &dst_data); - } - - return hr; -} - -static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create) -{ - HRESULT hr=S_OK; - DirEntry currentEntry; - DirRef currentEntryRef; - BlockChainStream *blockChainStream; - - if (create) - { - ULARGE_INTEGER size; - BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE]; - - /* Discard any existing data. */ - size.QuadPart = 0; - ILockBytes_SetSize(This->lockBytes, size); - - /* - * 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_SaveFileHeader(This); - - /* - * Add one block for the big block depot and one block for the directory table - */ - size.HighPart = 0; - size.LowPart = This->bigBlockSize * 3; - ILockBytes_SetSize(This->lockBytes, 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); - - if (FAILED(hr)) - { - return hr; - } - } - - /* - * There is no block depot cached yet. - */ - This->indexBlockDepotCached = 0xFFFFFFFF; - This->indexExtBlockDepotCached = 0xFFFFFFFF; - - /* - * Start searching for free blocks with block 0. - */ - This->prevFreeBlock = 0; - - This->firstFreeSmallBlock = 0; - - /* Read the extended big block depot locations. */ - if (This->extBigBlockDepotCount != 0) - { - ULONG current_block = This->extBigBlockDepotStart; - ULONG cache_size = This->extBigBlockDepotCount * 2; - ULONG i; - - This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size); - if (!This->extBigBlockDepotLocations) - { - return E_OUTOFMEMORY; - } - - This->extBigBlockDepotLocationsSize = cache_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; - } - - /* - * 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; - - if(!(blockChainStream = - BlockChainStream_Construct(This, &This->smallBlockDepotStart, - DIRENTRY_NULL))) - { - return STG_E_READFAULT; - } - if (!new_object) - BlockChainStream_Destroy(This->smallBlockDepotChain); - This->smallBlockDepotChain = blockChainStream; - - /* - * Write the root storage entry (memory only) - */ - if (create) - { - DirEntry rootEntry; - /* - * Initialize the directory table - */ - memset(&rootEntry, 0, sizeof(rootEntry)); - lstrcpyW(rootEntry.name, L"Root Entry"); - rootEntry.sizeOfNameString = sizeof(L"Root Entry"); - rootEntry.stgType = STGTY_ROOT; - rootEntry.leftChild = DIRENTRY_NULL; - rootEntry.rightChild = DIRENTRY_NULL; - rootEntry.dirRootEntry = DIRENTRY_NULL; - rootEntry.startingBlock = BLOCK_END_OF_CHAIN; - rootEntry.size.HighPart = 0; - rootEntry.size.LowPart = 0; - - StorageImpl_WriteDirEntry(This, 0, &rootEntry); - } - - /* - * Find the ID of the root storage. - */ - currentEntryRef = 0; - - do - { - hr = StorageImpl_ReadDirEntry( - This, - currentEntryRef, - ¤tEntry); - - if (SUCCEEDED(hr)) - { - if ( (currentEntry.sizeOfNameString != 0 ) && - (currentEntry.stgType == STGTY_ROOT) ) - { - This->base.storageDirEntry = currentEntryRef; - } - } - - currentEntryRef++; - - } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) ); - - if (FAILED(hr)) - { - return STG_E_READFAULT; - } - - /* - * 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; - - 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 StorageImpl_GetTransactionSig(StorageBaseImpl *base, - ULONG* result, BOOL refresh) -{ - StorageImpl *This = (StorageImpl*)base; - HRESULT hr=S_OK; - DWORD oldTransactionSig = This->transactionSig; - - if (refresh) - { - ULARGE_INTEGER offset; - ULONG bytes_read; - BYTE data[4]; - - offset.HighPart = 0; - offset.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; - } - } - - *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_LockRegion(StorageImpl *This, ULARGE_INTEGER offset, - ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) -{ - if ((dwLockType & This->locks_supported) == 0) - { - if (supported) *supported = FALSE; - return S_OK; - } - - if (supported) *supported = TRUE; - return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); -} - -static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset, - ULARGE_INTEGER cb, DWORD dwLockType) -{ - if ((dwLockType & This->locks_supported) == 0) - return S_OK; - - return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType); -} - -/* Internal function */ -static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, - ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) -{ - HRESULT hr; - int delay = 0; - DWORD start_time = GetTickCount(); - DWORD last_sanity_check = start_time; - ULARGE_INTEGER sanity_offset, sanity_cb; - - sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST; - sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1; - - do - { - hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported); - - 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 = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL); - if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) - break; - if (SUCCEEDED(hr)) - { - StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ); - hr = STG_E_ACCESSDENIED; - } - - last_sanity_check = current_time; - } - Sleep(delay); - if (delay < 150) delay++; - } - } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION); - - return hr; -} - -static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write) -{ - StorageImpl *This = (StorageImpl*)base; - HRESULT hr; - ULARGE_INTEGER offset, cb; - - if (write) - { - /* 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; - } - - hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL); - - return hr; -} - -static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) -{ - StorageImpl *This = (StorageImpl*)base; - HRESULT hr; - ULARGE_INTEGER offset, cb; - - if (write) - { - offset.QuadPart = RANGELOCK_TRANSACTION_FIRST; - cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1; - } - else - { - offset.QuadPart = RANGELOCK_COMMIT; - cb.QuadPart = 1; - } - - hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); - - return hr; -} - -static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) -{ - StorageImpl *This = (StorageImpl*) iface; - STATSTG statstg; - HRESULT hr; - - hr = ILockBytes_Stat(This->lockBytes, &statstg, 0); - - *result = statstg.pwcsName; - - return hr; -} - -static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start, - ULONG end, HRESULT fail_hr) -{ - HRESULT hr; - ULARGE_INTEGER offset, cb; - - offset.QuadPart = start; - cb.QuadPart = 1 + end - start; - - hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); - if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); - - if (FAILED(hr)) - 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 = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); - if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION) - break; - } - - if (SUCCEEDED(hr)) - { - for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++) - { - if (This->locked_bytes[j] == 0) - { - This->locked_bytes[j] = i; - break; - } - } - } - - return hr; -} - -static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) -{ - HRESULT hr; - ULARGE_INTEGER offset; - ULARGE_INTEGER cb; - DWORD share_mode = STGM_SHARE_MODE(openFlags); - BOOL supported; - - 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; - } - - /* 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, &supported); - - /* If the ILockBytes doesn't support locking that's ok. */ - if (!supported) return S_OK; - else if (FAILED(hr)) return hr; - - hr = S_OK; - - /* First check for any conflicting locks. */ - if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY) - hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION); - - if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) - hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION); - - if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) - hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION); - - 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); - - if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE) - { - hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION); - - if (SUCCEEDED(hr)) - hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION); - } - - /* 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 (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) - hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST); - - if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) - hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST); - - 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 (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 (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT) - hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST); - - offset.QuadPart = RANGELOCK_CHECKLOCKS; - cb.QuadPart = 1; - StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); - - return hr; -} - -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); - - if (SUCCEEDED(hr)) - hr = BlockChainStream_Flush(This->smallBlockDepotChain); - - for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++) - if (This->blockChainCache[i]) - hr = BlockChainStream_Flush(This->blockChainCache[i]); - - if (SUCCEEDED(hr)) - hr = ILockBytes_Flush(This->lockBytes); - - 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); - - StorageImpl_Flush(iface); - - StorageImpl_Invalidate(iface); - - HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); - - BlockChainStream_Destroy(This->smallBlockRootChain); - BlockChainStream_Destroy(This->rootBlockChain); - BlockChainStream_Destroy(This->smallBlockDepotChain); - - for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++) - BlockChainStream_Destroy(This->blockChainCache[i]); - - for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++) - { - ULARGE_INTEGER offset, cb; - cb.QuadPart = 1; - if (This->locked_bytes[i] != 0) - { - offset.QuadPart = This->locked_bytes[i]; - StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); - } - } - - if (This->lockBytes) - ILockBytes_Release(This->lockBytes); - HeapFree(GetProcessHeap(), 0, This); -} - - -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 -}; - - -/* - * 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 -}; - -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; - STATSTG stat; - - 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); - - list_init(&This->base.storageHead); - - 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 (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; - - This->base.reverted = FALSE; - - /* - * 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 - { - This->lockBytes = pLkbyt; - ILockBytes_AddRef(pLkbyt); - } - - if (SUCCEEDED(hr)) - hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME); - - if (SUCCEEDED(hr)) - { - This->locks_supported = stat.grfLocksSupported; - if (!hFile) - /* Don't try to use wine-internal locking flag with custom ILockBytes */ - This->locks_supported &= ~WINE_LOCK_READ; - - 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; -} - - -/************************************************************************ - * StorageInternalImpl implementation - ***********************************************************************/ - -static void StorageInternalImpl_Invalidate( StorageBaseImpl *base ) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - if (!This->base.reverted) - { - TRACE("Storage invalidated (stg=%p)\n", This); - - This->base.reverted = TRUE; - - This->parentStorage = NULL; - - StorageBaseImpl_DeleteAll(&This->base); - - list_remove(&This->ParentListEntry); - } -} - -static void StorageInternalImpl_Destroy( StorageBaseImpl *iface) -{ - StorageInternalImpl* This = (StorageInternalImpl*) iface; - - StorageInternalImpl_Invalidate(&This->base); - - HeapFree(GetProcessHeap(), 0, This); -} - -static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface) -{ - StorageInternalImpl* This = (StorageInternalImpl*) iface; - - return StorageBaseImpl_Flush(This->parentStorage); -} - -static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) -{ - StorageInternalImpl* This = (StorageInternalImpl*) iface; - - return StorageBaseImpl_GetFilename(This->parentStorage, result); -} - -static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base, - const DirEntry *newData, DirRef *index) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_CreateDirEntry(This->parentStorage, - newData, index); -} - -static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base, - DirRef index, const DirEntry *data) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_WriteDirEntry(This->parentStorage, - index, data); -} - -static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base, - DirRef index, DirEntry *data) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_ReadDirEntry(This->parentStorage, - index, data); -} - -static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base, - DirRef index) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_DestroyDirEntry(This->parentStorage, - index); -} - -static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_StreamReadAt(This->parentStorage, - index, offset, size, buffer, bytesRead); -} - -static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_StreamWriteAt(This->parentStorage, - index, offset, size, buffer, bytesWritten); -} - -static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER newsize) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_StreamSetSize(This->parentStorage, - index, newsize); -} - -static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base, - DirRef dst, DirRef src) -{ - StorageInternalImpl* This = (StorageInternalImpl*) base; - - return StorageBaseImpl_StreamLink(This->parentStorage, - dst, src); -} - -static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base, - ULONG* result, BOOL refresh) -{ - return E_NOTIMPL; -} - -static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base, - ULONG value) -{ - return E_NOTIMPL; -} - -static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write) -{ - return E_NOTIMPL; -} - -static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) -{ - return E_NOTIMPL; -} - -/****************************************************************************** -** -** StorageInternalImpl_Commit -** -*/ -static HRESULT WINAPI StorageInternalImpl_Commit( - IStorage* iface, - DWORD grfCommitFlags) /* [in] */ -{ - StorageBaseImpl* This = impl_from_IStorage(iface); - TRACE("%p, %#lx.\n", iface, grfCommitFlags); - return StorageBaseImpl_Flush(This); -} - -/****************************************************************************** -** -** StorageInternalImpl_Revert -** -*/ -static HRESULT WINAPI StorageInternalImpl_Revert( - IStorage* iface) -{ - FIXME("(%p): stub\n", iface); - return S_OK; -} - -/* - * Virtual function table for the StorageInternalImpl class. - */ -static const IStorageVtbl StorageInternalImpl_Vtbl = -{ - StorageBaseImpl_QueryInterface, - StorageBaseImpl_AddRef, - StorageBaseImpl_Release, - StorageBaseImpl_CreateStream, - StorageBaseImpl_OpenStream, - StorageBaseImpl_CreateStorage, - StorageBaseImpl_OpenStorage, - StorageBaseImpl_CopyTo, - StorageBaseImpl_MoveElementTo, - StorageInternalImpl_Commit, - StorageInternalImpl_Revert, - StorageBaseImpl_EnumElements, - StorageBaseImpl_DestroyElement, - StorageBaseImpl_RenameElement, - StorageBaseImpl_SetElementTimes, - StorageBaseImpl_SetClass, - StorageBaseImpl_SetStateBits, - StorageBaseImpl_Stat -}; - -static const StorageBaseImplVtbl StorageInternalImpl_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 -}; - -static StorageInternalImpl* StorageInternalImpl_Construct( - StorageBaseImpl* parentStorage, - DWORD openFlags, - DirRef storageDirEntry) -{ - StorageInternalImpl* newStorage; - - newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl)); - - if (newStorage!=0) - { - list_init(&newStorage->base.strmHead); - - list_init(&newStorage->base.storageHead); - - /* - * 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); - - newStorage->base.reverted = FALSE; - - newStorage->base.ref = 1; - - newStorage->parentStorage = parentStorage; - - /* - * Keep a reference to the directory entry of this storage - */ - newStorage->base.storageDirEntry = storageDirEntry; - - newStorage->base.create = FALSE; - - return newStorage; - } - - return 0; -} - - -/************************************************************************ - * TransactedSnapshotImpl implementation - ***********************************************************************/ - -static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This) -{ - DirRef result=This->firstFreeEntry; - - while (result < This->entries_size && This->entries[result].inuse) - result++; - - if (result == This->entries_size) - { - 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; - } - - This->entries[result].inuse = TRUE; - - This->firstFreeEntry = result+1; - - return result; -} - -static DirRef TransactedSnapshotImpl_CreateStubEntry( - TransactedSnapshotImpl *This, DirRef parentEntryRef) -{ - DirRef stubEntryRef; - TransactedDirEntry *entry; - - stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This); - - if (stubEntryRef != DIRENTRY_NULL) - { - entry = &This->entries[stubEntryRef]; - - entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef; - - entry->read = FALSE; - } - - return stubEntryRef; -} - -static HRESULT TransactedSnapshotImpl_EnsureReadEntry( - TransactedSnapshotImpl *This, DirRef entry) -{ - HRESULT hr=S_OK; - DirEntry data; - - if (!This->entries[entry].read) - { - hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, - This->entries[entry].transactedParentEntry, - &data); - - if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL) - { - data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild); - - if (data.leftChild == DIRENTRY_NULL) - hr = E_OUTOFMEMORY; - } - - if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL) - { - data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild); - - if (data.rightChild == DIRENTRY_NULL) - hr = E_OUTOFMEMORY; - } - - if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL) - { - data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry); - - if (data.dirRootEntry == DIRENTRY_NULL) - hr = E_OUTOFMEMORY; - } - - if (SUCCEEDED(hr)) - { - memcpy(&This->entries[entry].data, &data, sizeof(DirEntry)); - This->entries[entry].read = TRUE; - } - } - - return hr; -} - -static HRESULT TransactedSnapshotImpl_MakeStreamDirty( - TransactedSnapshotImpl *This, DirRef entry) -{ - 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); - - 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); - - if (FAILED(hr)) - StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry); - } - - if (SUCCEEDED(hr)) - This->entries[entry].stream_dirty = TRUE; - - 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 (delete_ref != DIRENTRY_NULL) - This->entries[delete_ref].deleted = TRUE; - - This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL; - } - } - - return hr; -} - -/* Find the first entry in a depth-first traversal. */ -static DirRef TransactedSnapshotImpl_FindFirstChild( - TransactedSnapshotImpl* This, DirRef parent) -{ - DirRef cursor, prev; - TransactedDirEntry *entry; - - 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) - { - prev = cursor; - cursor = entry->data.dirRootEntry; - entry = &This->entries[cursor]; - entry->parent = prev; - } - else - break; - } - - return cursor; -} - -/* Find the next entry in a depth-first traversal. */ -static DirRef TransactedSnapshotImpl_FindNextChild( - TransactedSnapshotImpl* This, DirRef current) -{ - DirRef parent; - TransactedDirEntry *parent_entry; - - parent = This->entries[current].parent; - parent_entry = &This->entries[parent]; - - 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 (parent_entry->data.dirRootEntry != DIRENTRY_NULL) - { - This->entries[parent_entry->data.dirRootEntry].parent = parent; - return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry); - } - } - - return parent; -} - -/* 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; -} - -/* Destroy the entries created by CopyTree. */ -static void TransactedSnapshotImpl_DestroyTemporaryCopy( - TransactedSnapshotImpl* This, DirRef stop) -{ - DirRef cursor; - TransactedDirEntry *entry; - ULARGE_INTEGER zero; - - zero.QuadPart = 0; - - if (!This->entries[This->base.storageDirEntry].read) - return; - - cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry; - - if (cursor == DIRENTRY_NULL) - return; - - cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor); - - while (cursor != DIRENTRY_NULL && cursor != stop) - { - if (TransactedSnapshotImpl_MadeCopy(This, cursor)) - { - entry = &This->entries[cursor]; - - if (entry->stream_dirty) - StorageBaseImpl_StreamSetSize(This->transactedParent, - entry->newTransactedParentEntry, zero); - - StorageBaseImpl_DestroyDirEntry(This->transactedParent, - entry->newTransactedParentEntry); - - entry->newTransactedParentEntry = entry->transactedParentEntry; - } - - cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); - } -} - -/* 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; - - cursor = This->base.storageDirEntry; - entry = &This->entries[cursor]; - entry->parent = DIRENTRY_NULL; - entry->newTransactedParentEntry = entry->transactedParentEntry; - - if (entry->data.dirRootEntry == DIRENTRY_NULL) - return S_OK; - - This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL; - - cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry); - entry = &This->entries[cursor]; - - 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; - - memcpy(&newData, &entry->data, sizeof(DirEntry)); - - newData.size.QuadPart = 0; - newData.startingBlock = BLOCK_END_OF_CHAIN; - - if (newData.leftChild != DIRENTRY_NULL) - newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry; - - if (newData.rightChild != DIRENTRY_NULL) - newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry; - - 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; - } - - 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)) - { - cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); - TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); - return hr; - } - } - - cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); - entry = &This->entries[cursor]; - } - - return hr; -} - -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; - - zero.QuadPart = 0; - - TRACE("%p, %#lx.\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)) - { - 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; - - 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. */ - - 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 - 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); - } - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static HRESULT WINAPI TransactedSnapshotImpl_Revert( - IStorage* 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); - - This->firstFreeEntry = 0; - This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry); - - return S_OK; -} - -static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This) -{ - if (!This->reverted) - { - TRACE("Storage invalidated (stg=%p)\n", This); - - This->reverted = TRUE; - - StorageBaseImpl_DeleteAll(This); - } -} - -static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) 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); -} - -static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface) -{ - /* We only need to flush when committing. */ - return S_OK; -} - -static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; - - return StorageBaseImpl_GetFilename(This->transactedParent, result); -} - -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=%lx r=%lx d=%lx <-- %lx\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) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - HRESULT hr; - - TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); - - hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", 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; - } - } - TRACE("<-- S_OK\n"); - return S_OK; -} - -static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, - DirRef index, DirEntry *data) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - HRESULT hr; - - hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - memcpy(data, &This->entries[index].data, sizeof(DirEntry)); - - TRACE("%lx %s l=%lx r=%lx d=%lx\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); - - return S_OK; -} - -static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, - DirRef index) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - - 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)) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", 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); - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER newsize) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - HRESULT hr; - - hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", 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; - - TRACE("<-- %#lx\n", hr); - return hr; -} - -static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base, - DirRef dst, DirRef src) -{ - TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - HRESULT hr; - TransactedDirEntry *dst_entry, *src_entry; - - hr = TransactedSnapshotImpl_EnsureReadEntry(This, src); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", hr); - return hr; - } - - hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst); - if (FAILED(hr)) - { - TRACE("<-- %#lx\n", 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 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 = -{ - 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 -}; - -static const StorageBaseImplVtbl TransactedSnapshotImpl_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 -}; - -static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, - TransactedSnapshotImpl** result) -{ - HRESULT hr; - - *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl)); - if (*result) - { - IStorage *scratch; - - (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl; - - /* 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; - - list_init(&(*result)->base.strmHead); - - list_init(&(*result)->base.storageHead); - - (*result)->base.ref = 1; - - (*result)->base.openFlags = parentStorage->openFlags; - - /* This cannot fail, except with E_NOTIMPL in which case we don't care */ - StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, 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); - - if (SUCCEEDED(hr)) - { - ULONG num_entries = 20; - - (*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; - - parentStorage->transactedChild = &(*result)->base; - - (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry); - } - else - { - IStorage_Release(scratch); - - hr = E_OUTOFMEMORY; - } - } - - if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result); - - return hr; - } - else - return E_OUTOFMEMORY; -} - - -/************************************************************************ - * TransactedSharedImpl implementation - ***********************************************************************/ - -static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This) -{ - if (!This->reverted) - { - TRACE("Storage invalidated (stg=%p)\n", This); - - This->reverted = TRUE; - - StorageBaseImpl_DeleteAll(This); - } -} - -static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) iface; - - 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 TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) iface; - - return StorageBaseImpl_GetFilename(This->transactedParent, result); -} - -static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base, - const DirEntry *newData, DirRef *index) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_CreateDirEntry(&This->scratch->base, - newData, index); -} - -static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base, - DirRef index, const DirEntry *data) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_WriteDirEntry(&This->scratch->base, - index, data); -} - -static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base, - DirRef index, DirEntry *data) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_ReadDirEntry(&This->scratch->base, - index, data); -} - -static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base, - DirRef index) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_DestroyDirEntry(&This->scratch->base, - index); -} - -static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_StreamReadAt(&This->scratch->base, - index, offset, size, buffer, bytesRead); -} - -static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_StreamWriteAt(&This->scratch->base, - index, offset, size, buffer, bytesWritten); -} - -static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base, - DirRef index, ULARGE_INTEGER newsize) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_StreamSetSize(&This->scratch->base, - index, newsize); -} - -static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base, - DirRef dst, DirRef src) -{ - TransactedSharedImpl* This = (TransactedSharedImpl*) base; - - return StorageBaseImpl_StreamLink(&This->scratch->base, - dst, src); -} - -static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base, - ULONG* result, BOOL refresh) -{ - return E_NOTIMPL; -} - -static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base, - ULONG value) -{ - return E_NOTIMPL; -} - -static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write) -{ - return E_NOTIMPL; -} - -static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) -{ - return E_NOTIMPL; -} - -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; - - TRACE("%p, %#lx\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)) - { - hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE); - if (SUCCEEDED(hr)) - { - if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig) - hr = STG_E_NOTCURRENT; - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1); - } - 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)) - { - 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; - } - } - TRACE("<-- %#lx\n", hr); - return hr; -} - -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, - 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 -}; - -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 -}; - -static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage, - TransactedSharedImpl** result) -{ - HRESULT hr; - - *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl)); - if (*result) - { - IStorage *scratch; - - (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl; - - /* 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(&(*result)->base.strmHead); - - list_init(&(*result)->base.storageHead); - - (*result)->base.ref = 1; - - (*result)->base.openFlags = parentStorage->openFlags; - - hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE); - - if (SUCCEEDED(hr)) - { - STGOPTIONS stgo; - - /* This cannot fail, except with E_NOTIMPL in which case we don't care */ - StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE); - - 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 %lx\n", parentStorage->openFlags); - } - - 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); -} - -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) -{ - WORD tmp; - - memcpy(&tmp, buffer+offset, sizeof(WORD)); - *value = lendian16toh(tmp); -} - -void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value) -{ - value = htole16(value); - memcpy((BYTE *)buffer + offset, &value, sizeof(WORD)); -} - -void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value) -{ - DWORD tmp; - - memcpy(&tmp, buffer+offset, sizeof(DWORD)); - *value = lendian32toh(tmp); -} - -void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value) -{ - value = htole32(value); - memcpy((BYTE *)buffer + offset, &value, sizeof(DWORD)); -} - -void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, - ULARGE_INTEGER* value) -{ -#ifdef WORDS_BIGENDIAN - ULARGE_INTEGER tmp; - - memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER)); - value->u.LowPart = htole32(tmp.HighPart); - value->u.HighPart = htole32(tmp.LowPart); -#else - memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER)); -#endif -} - -void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value) -{ -#ifdef WORDS_BIGENDIAN - ULARGE_INTEGER tmp; - - tmp.LowPart = htole32(value->u.HighPart); - tmp.HighPart = htole32(value->u.LowPart); - memcpy((BYTE *)buffer + offset, &tmp, sizeof(ULARGE_INTEGER)); -#else - memcpy((BYTE *)buffer + offset, value, sizeof(ULARGE_INTEGER)); -#endif -} - -void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value) -{ - StorageUtl_ReadDWord(buffer, offset, (DWORD *)&value->Data1); - StorageUtl_ReadWord(buffer, offset+4, &(value->Data2)); - StorageUtl_ReadWord(buffer, offset+6, &(value->Data3)); - - memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4)); -} - -void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value) -{ - StorageUtl_WriteDWord(buffer, offset, value->Data1); - StorageUtl_WriteWord(buffer, offset+4, value->Data2); - StorageUtl_WriteWord(buffer, offset+6, value->Data3); - - memcpy((BYTE *)buffer + offset + 8, value->Data4, sizeof(value->Data4)); -} - -void StorageUtl_CopyDirEntryToSTATSTG( - StorageBaseImpl* storage, - STATSTG* destination, - const DirEntry* source, - int statFlags) -{ - /* - * The copy of the string occurs only when the flag is not set - */ - if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT) - { - /* Use the filename for the root storage. */ - destination->pwcsName = 0; - StorageBaseImpl_GetFilename(storage, &destination->pwcsName); - } - else if( ((statFlags & STATFLAG_NONAME) != 0) || - (source->name[0] == 0) ) - { - destination->pwcsName = 0; - } - else - { - destination->pwcsName = - CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR)); - - lstrcpyW(destination->pwcsName, source->name); - } - - switch (source->stgType) - { - case STGTY_STORAGE: - case STGTY_ROOT: - destination->type = STGTY_STORAGE; - break; - case STGTY_STREAM: - destination->type = STGTY_STREAM; - break; - default: - destination->type = STGTY_STREAM; - break; - } - - destination->cbSize = source->size; -/* - currentReturnStruct->mtime = {0}; TODO - currentReturnStruct->ctime = {0}; - currentReturnStruct->atime = {0}; -*/ - destination->grfMode = 0; - destination->grfLocksSupported = 0; - destination->clsid = source->clsid; - destination->grfStateBits = 0; - destination->reserved = 0; -} - - -/************************************************************************ - * 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. */ -static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) -{ - ULONG next_sector, next_offset; - HRESULT hr; - struct BlockChainRun *last_run; - - if (This->indexCacheLen == 0) - { - last_run = NULL; - next_offset = 0; - next_sector = BlockChainStream_GetHeadOfChain(This); - } - else - { - last_run = &This->indexCache[This->indexCacheLen-1]; - next_offset = last_run->lastOffset+1; - hr = StorageImpl_GetNextBlockInChain(This->parentStorage, - last_run->firstSector + last_run->lastOffset - last_run->firstOffset, - &next_sector); - if (FAILED(hr)) return hr; - } - - while (next_sector != BLOCK_END_OF_CHAIN) - { - if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset) - { - /* Add the current block to the cache. */ - if (This->indexCacheSize == 0) - { - This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16); - if (!This->indexCache) return E_OUTOFMEMORY; - This->indexCacheSize = 16; - } - else if (This->indexCacheSize == This->indexCacheLen) - { - struct BlockChainRun *new_cache; - ULONG new_size; - - new_size = This->indexCacheSize * 2; - new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size); - if (!new_cache) return E_OUTOFMEMORY; - memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen); - - HeapFree(GetProcessHeap(), 0, This->indexCache); - This->indexCache = new_cache; - This->indexCacheSize = new_size; - } - - This->indexCacheLen++; - last_run = &This->indexCache[This->indexCacheLen-1]; - last_run->firstSector = next_sector; - last_run->firstOffset = next_offset; - } - - last_run->lastOffset = next_offset; - - /* Find the next block. */ - next_offset++; - hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector); - if (FAILED(hr)) return hr; - } - - if (This->indexCacheLen) - { - This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset; - This->numBlocks = last_run->lastOffset+1; - } - else - { - This->tailIndex = BLOCK_END_OF_CHAIN; - This->numBlocks = 0; - } - - return S_OK; -} - -/* Locate the nth block in this stream. */ -static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) -{ - ULONG min_offset = 0, max_offset = This->numBlocks-1; - ULONG min_run = 0, max_run = This->indexCacheLen-1; - - if (offset >= This->numBlocks) - return BLOCK_END_OF_CHAIN; - - while (min_run < max_run) - { - ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset); - if (offset < This->indexCache[run_to_check].firstOffset) - { - max_offset = This->indexCache[run_to_check].firstOffset-1; - max_run = run_to_check-1; - } - else if (offset > This->indexCache[run_to_check].lastOffset) - { - min_offset = This->indexCache[run_to_check].lastOffset+1; - min_run = run_to_check+1; - } - else - /* Block is in this run. */ - min_run = max_run = run_to_check; - } - - return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; -} - -static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, - ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) -{ - BlockChainBlock *result=NULL; - int i; - - for (i=0; i<2; i++) - if (This->cachedBlocks[i].index == index) - { - *sector = This->cachedBlocks[i].sector; - *block = &This->cachedBlocks[i]; - return S_OK; - } - - *sector = BlockChainStream_GetSectorOfOffset(This, index); - if (*sector == BLOCK_END_OF_CHAIN) - return STG_E_DOCFILECORRUPT; - - if (create) - { - if (This->cachedBlocks[0].index == 0xffffffff) - result = &This->cachedBlocks[0]; - else if (This->cachedBlocks[1].index == 0xffffffff) - result = &This->cachedBlocks[1]; - else - { - result = &This->cachedBlocks[This->blockToEvict++]; - if (This->blockToEvict == 2) - This->blockToEvict = 0; - } - - if (result->dirty) - { - if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data)) - return STG_E_WRITEFAULT; - result->dirty = FALSE; - } - - result->read = FALSE; - result->index = index; - result->sector = *sector; - } - - *block = result; - return S_OK; -} - -BlockChainStream* BlockChainStream_Construct( - StorageImpl* parentStorage, - ULONG* headOfStreamPlaceHolder, - DirRef dirEntry) -{ - BlockChainStream* newStream; - - newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream)); - if(!newStream) - return NULL; - - newStream->parentStorage = parentStorage; - newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; - newStream->ownerDirEntry = dirEntry; - newStream->indexCache = NULL; - newStream->indexCacheLen = 0; - newStream->indexCacheSize = 0; - newStream->cachedBlocks[0].index = 0xffffffff; - newStream->cachedBlocks[0].dirty = FALSE; - newStream->cachedBlocks[1].index = 0xffffffff; - newStream->cachedBlocks[1].dirty = FALSE; - newStream->blockToEvict = 0; - - if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) - { - HeapFree(GetProcessHeap(), 0, newStream->indexCache); - HeapFree(GetProcessHeap(), 0, newStream); - return NULL; - } - - return newStream; -} - -HRESULT BlockChainStream_Flush(BlockChainStream* This) -{ - int i; - if (!This) return S_OK; - for (i=0; i<2; i++) - { - if (This->cachedBlocks[i].dirty) - { - if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data)) - This->cachedBlocks[i].dirty = FALSE; - else - return STG_E_WRITEFAULT; - } - } - return S_OK; -} - -void BlockChainStream_Destroy(BlockChainStream* This) -{ - if (This) - { - BlockChainStream_Flush(This); - HeapFree(GetProcessHeap(), 0, This->indexCache); - } - HeapFree(GetProcessHeap(), 0, This); -} - -/****************************************************************************** - * 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) - { - /* - * Go to the new end of chain - */ - blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1); - - /* Mark the new end of chain */ - StorageImpl_SetNextBlockInChain( - This->parentStorage, - blockIndex, - BLOCK_END_OF_CHAIN); - - This->tailIndex = blockIndex; - } - else - { - if (This->headOfStreamPlaceHolder != 0) - { - *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN; - } - else - { - DirEntry chainEntry; - assert(This->ownerDirEntry != DIRENTRY_NULL); - - StorageImpl_ReadDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - chainEntry.startingBlock = BLOCK_END_OF_CHAIN; - - StorageImpl_WriteDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - } - - This->tailIndex = BLOCK_END_OF_CHAIN; - } - - This->numBlocks = numBlocks; - - /* - * Mark the extra blocks as free - */ - while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks) - { - struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1]; - StorageImpl_FreeBigBlock(This->parentStorage, - last_run->firstSector + last_run->lastOffset - last_run->firstOffset); - if (last_run->lastOffset == last_run->firstOffset) - This->indexCacheLen--; - else - last_run->lastOffset--; - } - - /* - * Reset the last accessed block cache. - */ - for (i=0; i<2; i++) - { - if (This->cachedBlocks[i].index >= numBlocks) - { - This->cachedBlocks[i].index = 0xffffffff; - This->cachedBlocks[i].dirty = FALSE; - } - } - - return TRUE; -} - -/****************************************************************************** - * BlockChainStream_Enlarge - * - * Grows this chain in the big block depot. - */ -static BOOL BlockChainStream_Enlarge(BlockChainStream* This, - ULARGE_INTEGER newSize) -{ - ULONG blockIndex, currentBlock; - ULONG newNumBlocks; - ULONG oldNumBlocks = 0; - - blockIndex = BlockChainStream_GetHeadOfChain(This); - - /* - * Empty chain. Create the head. - */ - if (blockIndex == BLOCK_END_OF_CHAIN) - { - blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, 1); - StorageImpl_SetNextBlockInChain(This->parentStorage, - blockIndex, - BLOCK_END_OF_CHAIN); - - if (This->headOfStreamPlaceHolder != 0) - { - *(This->headOfStreamPlaceHolder) = blockIndex; - } - else - { - DirEntry chainEntry; - assert(This->ownerDirEntry != DIRENTRY_NULL); - - StorageImpl_ReadDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - chainEntry.startingBlock = blockIndex; - - StorageImpl_WriteDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - } - - This->tailIndex = blockIndex; - This->numBlocks = 1; - } - - /* - * Figure out how many blocks are needed to contain this stream - */ - newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize; - - if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0) - newNumBlocks++; - - /* - * Go to the current end of chain - */ - if (This->tailIndex == BLOCK_END_OF_CHAIN) - { - currentBlock = blockIndex; - - while (blockIndex != BLOCK_END_OF_CHAIN) - { - This->numBlocks++; - currentBlock = blockIndex; - - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock, - &blockIndex))) - return FALSE; - } - - This->tailIndex = currentBlock; - } - - currentBlock = This->tailIndex; - oldNumBlocks = This->numBlocks; - - /* - * Add new blocks to the chain - */ - if (oldNumBlocks < newNumBlocks) - { - while (oldNumBlocks < newNumBlocks) - { - blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage, newNumBlocks - oldNumBlocks); - - StorageImpl_SetNextBlockInChain( - This->parentStorage, - currentBlock, - blockIndex); - - StorageImpl_SetNextBlockInChain( - This->parentStorage, - blockIndex, - BLOCK_END_OF_CHAIN); - - currentBlock = blockIndex; - oldNumBlocks++; - } - - This->tailIndex = blockIndex; - This->numBlocks = newNumBlocks; - } - - if (FAILED(BlockChainStream_UpdateIndexCache(This))) - return FALSE; - - return TRUE; -} - - -/****************************************************************************** - * BlockChainStream_GetSize - * - * Returns the size of this chain. - * Will return the block count if this chain doesn't have a directory entry. - */ -static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This) -{ - DirEntry chainEntry; - - if(This->headOfStreamPlaceHolder == NULL) - { - /* - * This chain has a directory entry so use the size value from there. - */ - StorageImpl_ReadDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - return chainEntry.size; - } - else - { - /* - * this chain is a chain that does not have a directory entry, figure out the - * size by making the product number of used blocks times the - * size of them - */ - ULARGE_INTEGER result; - result.QuadPart = - (ULONGLONG)BlockChainStream_GetCount(This) * - This->parentStorage->bigBlockSize; - - return result; - } -} - -/****************************************************************************** - * 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_ReadAt - * - * 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, %li, %p, %lu, %p.\n",This, offset.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, - ULONG* nextBlockInChain) -{ - ULARGE_INTEGER offsetOfBlockInDepot; - DWORD buffer; - ULONG bytesRead; - HRESULT res; - - *nextBlockInChain = BLOCK_END_OF_CHAIN; - - offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); - - /* - * Read those bytes in the buffer from the small block file. - */ - res = BlockChainStream_ReadAt( - This->parentStorage->smallBlockDepotChain, - offsetOfBlockInDepot, - sizeof(DWORD), - &buffer, - &bytesRead); - - if (SUCCEEDED(res) && bytesRead != sizeof(DWORD)) - res = STG_E_READFAULT; - - if (SUCCEEDED(res)) - { - StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain); - return S_OK; - } - - return res; -} - -/****************************************************************************** - * SmallBlockChainStream_SetNextBlockInChain - * - * Writes the index of the next block of the specified block in the small - * block depot. - * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock. - * To flag a block as free use BLOCK_UNUSED as nextBlock. - */ -static void SmallBlockChainStream_SetNextBlockInChain( - SmallBlockChainStream* This, - ULONG blockIndex, - ULONG nextBlock) -{ - ULARGE_INTEGER offsetOfBlockInDepot; - DWORD buffer; - ULONG bytesWritten; - - offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); - - StorageUtl_WriteDWord(&buffer, 0, nextBlock); - - /* - * Read those bytes in the buffer from the small block file. - */ - BlockChainStream_WriteAt( - This->parentStorage->smallBlockDepotChain, - offsetOfBlockInDepot, - sizeof(DWORD), - &buffer, - &bytesWritten); -} - -/****************************************************************************** - * SmallBlockChainStream_FreeBlock - * - * Flag small block 'blockIndex' as free in the small block depot. - */ -static void SmallBlockChainStream_FreeBlock( - SmallBlockChainStream* This, - ULONG blockIndex) -{ - SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); -} - -/****************************************************************************** - * SmallBlockChainStream_GetNextFreeBlock - * - * Returns the index of a free small block. The small block depot will be - * enlarged if necessary. The small block chain will also be enlarged if - * necessary. - */ -static ULONG SmallBlockChainStream_GetNextFreeBlock( - SmallBlockChainStream* This) -{ - ULARGE_INTEGER offsetOfBlockInDepot; - DWORD buffer; - ULONG bytesRead; - ULONG blockIndex = This->parentStorage->firstFreeSmallBlock; - ULONG nextBlockIndex = BLOCK_END_OF_CHAIN; - HRESULT res = S_OK; - ULONG smallBlocksPerBigBlock; - DirEntry rootEntry; - ULONG blocksRequired; - ULARGE_INTEGER old_size, size_required; - - offsetOfBlockInDepot.HighPart = 0; - - /* - * Scan the small block depot for a free block - */ - while (nextBlockIndex != BLOCK_UNUSED) - { - offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); - - res = BlockChainStream_ReadAt( - This->parentStorage->smallBlockDepotChain, - offsetOfBlockInDepot, - sizeof(DWORD), - &buffer, - &bytesRead); - - /* - * If we run out of space for the small block depot, enlarge it - */ - if (SUCCEEDED(res) && bytesRead == sizeof(DWORD)) - { - StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex); - - if (nextBlockIndex != BLOCK_UNUSED) - blockIndex++; - } - else - { - ULONG count = - BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain); - - BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE]; - ULARGE_INTEGER newSize, offset; - ULONG bytesWritten; - - newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize; - BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize); - - /* - * Initialize all the small blocks to free - */ - memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize); - offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize; - BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain, - offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten); - - StorageImpl_SaveFileHeader(This->parentStorage); - } - } - - This->parentStorage->firstFreeSmallBlock = blockIndex+1; - - smallBlocksPerBigBlock = - This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize; - - /* - * Verify if we have to allocate big blocks to contain small blocks - */ - blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; - - size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize; - - old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain); - - if (size_required.QuadPart > old_size.QuadPart) - { - BlockChainStream_SetSize( - This->parentStorage->smallBlockRootChain, - size_required); - - StorageImpl_ReadDirEntry( - This->parentStorage, - This->parentStorage->base.storageDirEntry, - &rootEntry); - - rootEntry.size = size_required; - - StorageImpl_WriteDirEntry( - This->parentStorage, - This->parentStorage->base.storageDirEntry, - &rootEntry); - } - - return blockIndex; -} - -/****************************************************************************** - * SmallBlockChainStream_ReadAt - * - * 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 SmallBlockChainStream_ReadAt( - SmallBlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - void* buffer, - ULONG* bytesRead) -{ - HRESULT rc = S_OK; - ULARGE_INTEGER offsetInBigBlockFile; - ULONG blockNoInSequence = - offset.LowPart / This->parentStorage->smallBlockSize; - - ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize; - ULONG bytesToReadInBuffer; - ULONG blockIndex; - ULONG bytesReadFromBigBlockFile; - BYTE* bufferWalker; - ULARGE_INTEGER stream_size; - - /* - * This should never happen on a small block file. - */ - assert(offset.HighPart==0); - - *bytesRead = 0; - - stream_size = SmallBlockChainStream_GetSize(This); - if (stream_size.QuadPart > offset.QuadPart) - size = min(stream_size.QuadPart - offset.QuadPart, size); - else - return S_OK; - - /* - * Find the first block in the stream that contains part of the buffer. - */ - blockIndex = SmallBlockChainStream_GetHeadOfChain(This); - - while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) - { - rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); - if(FAILED(rc)) - return rc; - blockNoInSequence--; - } - - /* - * Start reading the buffer. - */ - bufferWalker = buffer; - - while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) - { - /* - * Calculate how many bytes we can copy from this small block. - */ - bytesToReadInBuffer = - min(This->parentStorage->smallBlockSize - offsetInBlock, size); - - /* - * Calculate the offset of the small block in the small block file. - */ - offsetInBigBlockFile.QuadPart = - (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; - - offsetInBigBlockFile.QuadPart += offsetInBlock; - - /* - * Read those bytes in the buffer from the small block file. - * The small block has already been identified so it shouldn't fail - * unless the file is corrupt. - */ - rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain, - offsetInBigBlockFile, - bytesToReadInBuffer, - bufferWalker, - &bytesReadFromBigBlockFile); - - if (FAILED(rc)) - return rc; - - if (!bytesReadFromBigBlockFile) - return STG_E_DOCFILECORRUPT; - - /* - * Step to the next big block. - */ - rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); - if(FAILED(rc)) - return STG_E_DOCFILECORRUPT; - - bufferWalker += bytesReadFromBigBlockFile; - size -= bytesReadFromBigBlockFile; - *bytesRead += bytesReadFromBigBlockFile; - offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize; - } - - return S_OK; -} - -/****************************************************************************** - * SmallBlockChainStream_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 SmallBlockChainStream_WriteAt( - SmallBlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - const void* buffer, - ULONG* bytesWritten) -{ - ULARGE_INTEGER offsetInBigBlockFile; - ULONG blockNoInSequence = - offset.LowPart / This->parentStorage->smallBlockSize; - - ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize; - ULONG bytesToWriteInBuffer; - ULONG blockIndex; - ULONG bytesWrittenToBigBlockFile; - const BYTE* bufferWalker; - HRESULT res; - - /* - * This should never happen on a small block file. - */ - assert(offset.HighPart==0); - - /* - * Find the first block in the stream that contains part of the buffer. - */ - blockIndex = SmallBlockChainStream_GetHeadOfChain(This); - - while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) - { - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex))) - return STG_E_DOCFILECORRUPT; - blockNoInSequence--; - } - - /* - * Start writing the buffer. - */ - *bytesWritten = 0; - bufferWalker = buffer; - while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) - { - /* - * Calculate how many bytes we can copy to this small block. - */ - bytesToWriteInBuffer = - min(This->parentStorage->smallBlockSize - offsetInBlock, size); - - /* - * Calculate the offset of the small block in the small block file. - */ - offsetInBigBlockFile.QuadPart = - (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; - - offsetInBigBlockFile.QuadPart += offsetInBlock; - - /* - * Write those bytes in the buffer to the small block file. - */ - res = BlockChainStream_WriteAt( - This->parentStorage->smallBlockRootChain, - offsetInBigBlockFile, - bytesToWriteInBuffer, - bufferWalker, - &bytesWrittenToBigBlockFile); - if (FAILED(res)) - return res; - - /* - * Step to the next big block. - */ - res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); - if (FAILED(res)) - return res; - bufferWalker += bytesWrittenToBigBlockFile; - size -= bytesWrittenToBigBlockFile; - *bytesWritten += bytesWrittenToBigBlockFile; - offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize; - } - - return (size == 0) ? S_OK : STG_E_WRITEFAULT; -} - -/****************************************************************************** - * SmallBlockChainStream_Shrink - * - * Shrinks this chain in the small block depot. - */ -static BOOL SmallBlockChainStream_Shrink( - SmallBlockChainStream* This, - ULARGE_INTEGER newSize) -{ - ULONG blockIndex, extraBlock; - ULONG numBlocks; - ULONG count = 0; - - numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize; - - if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0) - numBlocks++; - - blockIndex = SmallBlockChainStream_GetHeadOfChain(This); - - /* - * Go to the new end of chain - */ - while (count < numBlocks) - { - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, - &blockIndex))) - return FALSE; - count++; - } - - /* - * If the count is 0, we have a special case, the head of the chain was - * just freed. - */ - if (count == 0) - { - DirEntry chainEntry; - - StorageImpl_ReadDirEntry(This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - chainEntry.startingBlock = BLOCK_END_OF_CHAIN; - - StorageImpl_WriteDirEntry(This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - /* - * We start freeing the chain at the head block. - */ - extraBlock = blockIndex; - } - else - { - /* Get the next block before marking the new end */ - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, - &extraBlock))) - return FALSE; - - /* Mark the new end of chain */ - SmallBlockChainStream_SetNextBlockInChain( - This, - blockIndex, - BLOCK_END_OF_CHAIN); - } - - /* - * Mark the extra blocks as free - */ - while (extraBlock != BLOCK_END_OF_CHAIN) - { - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock, - &blockIndex))) - return FALSE; - SmallBlockChainStream_FreeBlock(This, extraBlock); - This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock); - extraBlock = blockIndex; - } - - return TRUE; -} - -/****************************************************************************** - * SmallBlockChainStream_Enlarge - * - * Grows this chain in the small block depot. - */ -static BOOL SmallBlockChainStream_Enlarge( - SmallBlockChainStream* This, - ULARGE_INTEGER newSize) -{ - ULONG blockIndex, currentBlock; - ULONG newNumBlocks; - ULONG oldNumBlocks = 0; - - blockIndex = SmallBlockChainStream_GetHeadOfChain(This); - - /* - * Empty chain. Create the head. - */ - if (blockIndex == BLOCK_END_OF_CHAIN) - { - blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); - SmallBlockChainStream_SetNextBlockInChain( - This, - blockIndex, - BLOCK_END_OF_CHAIN); - - if (This->headOfStreamPlaceHolder != NULL) - { - *(This->headOfStreamPlaceHolder) = blockIndex; - } - else - { - DirEntry chainEntry; - - StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry, - &chainEntry); - - chainEntry.startingBlock = blockIndex; - - StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry, - &chainEntry); - } - } - - currentBlock = blockIndex; - - /* - * Figure out how many blocks are needed to contain this stream - */ - newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize; - - if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0) - newNumBlocks++; - - /* - * Go to the current end of chain - */ - while (blockIndex != BLOCK_END_OF_CHAIN) - { - oldNumBlocks++; - currentBlock = blockIndex; - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex))) - return FALSE; - } - - /* - * Add new blocks to the chain - */ - while (oldNumBlocks < newNumBlocks) - { - blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); - SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex); - - SmallBlockChainStream_SetNextBlockInChain( - This, - blockIndex, - BLOCK_END_OF_CHAIN); - - currentBlock = blockIndex; - oldNumBlocks++; - } - - return TRUE; -} - -/****************************************************************************** - * SmallBlockChainStream_SetSize - * - * Sets the size of this stream. - * 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 SmallBlockChainStream_SetSize( - SmallBlockChainStream* This, - ULARGE_INTEGER newSize) -{ - ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This); - - if (newSize.LowPart == size.LowPart) - return TRUE; - - if (newSize.LowPart < size.LowPart) - { - SmallBlockChainStream_Shrink(This, newSize); - } - else - { - SmallBlockChainStream_Enlarge(This, newSize); - } - - return TRUE; -} - -/****************************************************************************** - * SmallBlockChainStream_GetCount - * - * Returns the number of small blocks that comprises this chain. - * This is not the size of the stream as the last block may not be full! - * - */ -static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This) -{ - ULONG blockIndex; - ULONG count = 0; - - blockIndex = SmallBlockChainStream_GetHeadOfChain(This); - - while(blockIndex != BLOCK_END_OF_CHAIN) - { - count++; - - if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, - blockIndex, &blockIndex))) - return 0; - } - - return count; -} - -/****************************************************************************** - * SmallBlockChainStream_GetSize - * - * Returns the size of this chain. - */ -static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This) -{ - DirEntry chainEntry; - - if(This->headOfStreamPlaceHolder != NULL) - { - ULARGE_INTEGER result; - result.HighPart = 0; - - result.LowPart = SmallBlockChainStream_GetCount(This) * - This->parentStorage->smallBlockSize; - - return result; - } - - StorageImpl_ReadDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - return chainEntry.size; -} - - -/************************************************************************ - * Miscellaneous storage functions - ***********************************************************************/ - -static HRESULT create_storagefile( - LPCOLESTR pwcsName, - DWORD grfMode, - DWORD grfAttrs, - STGOPTIONS* pStgOptions, - REFIID riid, - void** ppstgOpen) -{ - StorageBaseImpl* newStorage = 0; - HANDLE hFile = INVALID_HANDLE_VALUE; - HRESULT hr = STG_E_INVALIDFLAG; - DWORD shareMode; - DWORD accessMode; - DWORD creationMode; - DWORD fileAttributes; - WCHAR tempFileName[MAX_PATH]; - - if (ppstgOpen == 0) - return STG_E_INVALIDPOINTER; - - if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE) - return STG_E_INVALIDPARAMETER; - - /* if no share mode given then DENY_NONE is the default */ - if (STGM_SHARE_MODE(grfMode) == 0) - grfMode |= STGM_SHARE_DENY_NONE; - - if ( FAILED( validateSTGM(grfMode) )) - goto end; - - /* StgCreateDocFile seems to refuse readonly access, despite MSDN */ - switch(STGM_ACCESS_MODE(grfMode)) - { - case STGM_WRITE: - case STGM_READWRITE: - break; - default: - goto end; - } - - /* in direct mode, can only use SHARE_EXCLUSIVE */ - if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)) - goto end; - - /* but in transacted mode, any share mode is valid */ - - /* - * Generate a unique name. - */ - if (pwcsName == 0) - { - WCHAR tempPath[MAX_PATH]; - - memset(tempPath, 0, sizeof(tempPath)); - memset(tempFileName, 0, sizeof(tempFileName)); - - if ((GetTempPathW(MAX_PATH, tempPath)) == 0 ) - tempPath[0] = '.'; - - if (GetTempFileNameW(tempPath, L"STO", 0, tempFileName) != 0) - pwcsName = tempFileName; - else - { - hr = STG_E_INSUFFICIENTMEMORY; - goto end; - } - - creationMode = TRUNCATE_EXISTING; - } - else - { - creationMode = GetCreationModeFromSTGM(grfMode); - } - - /* - * Interpret the STGM value grfMode - */ - shareMode = GetShareModeFromSTGM(grfMode); - accessMode = GetAccessModeFromSTGM(grfMode); - - if (grfMode & STGM_DELETEONRELEASE) - fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE; - else - fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; - - *ppstgOpen = 0; - - hFile = CreateFileW(pwcsName, - accessMode, - shareMode, - NULL, - creationMode, - fileAttributes, - 0); - - if (hFile == INVALID_HANDLE_VALUE) - { - if(GetLastError() == ERROR_FILE_EXISTS) - hr = STG_E_FILEALREADYEXISTS; - else - hr = E_FAIL; - goto end; - } - - /* - * Allocate and initialize the new IStorage object. - */ - hr = Storage_Construct( - hFile, - pwcsName, - NULL, - grfMode, - TRUE, - TRUE, - pStgOptions->ulSectorSize, - &newStorage); - - if (FAILED(hr)) - { - goto end; - } - - hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen); - IStorage_Release(&newStorage->IStorage_iface); - -end: - TRACE("<-- %p r = %#lx\n", *ppstgOpen, hr); - - return hr; -} - -/****************************************************************************** - * StgCreateDocfile [OLE32.@] - * Creates a new compound file storage object - * - * PARAMS - * pwcsName [ I] Unicode string with filename (can be relative or NULL) - * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants) - * reserved [ ?] unused?, usually 0 - * ppstgOpen [IO] A pointer to IStorage pointer to the new object - * - * RETURNS - * S_OK if the file was successfully created - * some STG_E_ value if error - * NOTES - * if pwcsName is NULL, create file with new unique name - * the function can returns - * STG_S_CONVERTED if the specified file was successfully converted to storage format - * (unrealized now) - */ -HRESULT WINAPI StgCreateDocfile( - LPCOLESTR pwcsName, - DWORD grfMode, - DWORD reserved, - IStorage **ppstgOpen) -{ - STGOPTIONS stgoptions = {1, 0, 512}; - - TRACE("%s, %#lx, %ld, %p.\n", debugstr_w(pwcsName), grfMode, reserved, ppstgOpen); - - if (ppstgOpen == 0) - return STG_E_INVALIDPOINTER; - if (reserved != 0) - return STG_E_INVALIDPARAMETER; - - return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen); -} - -/****************************************************************************** - * StgCreateStorageEx [OLE32.@] - */ -HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) -{ - TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName), - grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); - - if (stgfmt != STGFMT_FILE && grfAttrs != 0) - { - ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n"); - 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; - } - - if (stgfmt == STGFMT_FILE) - { - ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); - return STG_E_INVALIDPARAMETER; - } - - if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE) - { - STGOPTIONS defaultOptions = {1, 0, 512}; - - if (!pStgOptions) pStgOptions = &defaultOptions; - return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen); - } - - - ERR("Invalid stgfmt argument\n"); - return STG_E_INVALIDPARAMETER; -} - -/****************************************************************************** - * StgCreatePropSetStg [OLE32.@] - */ -HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved, - IPropertySetStorage **propset) -{ - TRACE("%p, %#lx, %p.\n", pstg, reserved, propset); - if (reserved) - return STG_E_INVALIDPARAMETER; - - return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset); -} - -/****************************************************************************** - * StgOpenStorageEx [OLE32.@] - */ -HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) -{ - TRACE("%s, %#lx, %#lx, %#lx, %p, %p, %p, %p.\n", debugstr_w(pwcsName), - grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); - - if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0) - { - ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n"); - return STG_E_INVALIDPARAMETER; - } - - switch (stgfmt) - { - case STGFMT_FILE: - ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); - return STG_E_INVALIDPARAMETER; - - case STGFMT_STORAGE: - break; - - case STGFMT_DOCFILE: - 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; - } - FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n"); - break; - - case STGFMT_ANY: - WARN("STGFMT_ANY assuming storage\n"); - break; - - default: - return STG_E_INVALIDPARAMETER; - } - - return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen); -} - - -/****************************************************************************** - * StgOpenStorage [OLE32.@] - */ -HRESULT WINAPI StgOpenStorage( - const OLECHAR *pwcsName, - IStorage *pstgPriority, - DWORD grfMode, - SNB snbExclude, - DWORD reserved, - IStorage **ppstgOpen) -{ - StorageBaseImpl* newStorage = 0; - HRESULT hr = S_OK; - HANDLE hFile = 0; - DWORD shareMode; - DWORD accessMode; - LPWSTR temp_name = NULL; - - TRACE("%s, %p, %#lx, %p, %ld, %p.\n", debugstr_w(pwcsName), pstgPriority, grfMode, - snbExclude, reserved, ppstgOpen); - - if (pstgPriority) - { - /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */ - hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name); - if (FAILED(hr)) goto end; - pwcsName = temp_name; - TRACE("using filename %s\n", debugstr_w(temp_name)); - } - - if (pwcsName == 0) - { - hr = STG_E_INVALIDNAME; - goto end; - } - - if (ppstgOpen == 0) - { - hr = STG_E_INVALIDPOINTER; - goto end; - } - - if (reserved) - { - hr = STG_E_INVALIDPARAMETER; - goto end; - } - - if (grfMode & STGM_PRIORITY) - { - if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT)) - return STG_E_INVALIDFLAG; - if (grfMode & STGM_DELETEONRELEASE) - return STG_E_INVALIDFUNCTION; - if(STGM_ACCESS_MODE(grfMode) != STGM_READ) - return STG_E_INVALIDFLAG; - grfMode &= ~0xf0; /* remove the existing sharing mode */ - grfMode |= STGM_SHARE_DENY_NONE; - } - - /* - * Validate the sharing mode - */ - if (grfMode & STGM_DIRECT_SWMR) - { - if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) && - (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE)) - { - hr = STG_E_INVALIDFLAG; - goto end; - } - } - else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY))) - switch(STGM_SHARE_MODE(grfMode)) - { - case STGM_SHARE_EXCLUSIVE: - case STGM_SHARE_DENY_WRITE: - break; - default: - hr = STG_E_INVALIDFLAG; - goto end; - } - - if ( FAILED( validateSTGM(grfMode) ) || - (grfMode&STGM_CREATE)) - { - hr = STG_E_INVALIDFLAG; - goto end; - } - - /* shared reading requires transacted or single writer mode */ - if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE && - STGM_ACCESS_MODE(grfMode) == STGM_READWRITE && - !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR)) - { - hr = STG_E_INVALIDFLAG; - goto end; - } - - /* - * Interpret the STGM value grfMode - */ - shareMode = GetShareModeFromSTGM(grfMode); - accessMode = GetAccessModeFromSTGM(grfMode); - - *ppstgOpen = 0; - - hFile = CreateFileW( pwcsName, - accessMode, - shareMode, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - 0); - - if (hFile==INVALID_HANDLE_VALUE) - { - DWORD last_error = GetLastError(); - - hr = E_FAIL; - - switch (last_error) - { - case ERROR_FILE_NOT_FOUND: - hr = STG_E_FILENOTFOUND; - break; - - case ERROR_PATH_NOT_FOUND: - hr = STG_E_PATHNOTFOUND; - break; - - case ERROR_ACCESS_DENIED: - case ERROR_WRITE_PROTECT: - hr = STG_E_ACCESSDENIED; - break; - - case ERROR_SHARING_VIOLATION: - hr = STG_E_SHAREVIOLATION; - break; - - default: - hr = E_FAIL; - } - - goto end; - } - - /* - * Refuse to open the file if it's too small to be a structured storage file - * FIXME: verify the file when reading instead of here - */ - if (GetFileSize(hFile, NULL) < HEADER_SIZE) - { - CloseHandle(hFile); - hr = STG_E_FILEALREADYEXISTS; - goto end; - } - - /* - * Allocate and initialize the new IStorage object. - */ - hr = Storage_Construct( - hFile, - pwcsName, - NULL, - grfMode, - TRUE, - FALSE, - 512, - &newStorage); - - if (FAILED(hr)) - { - /* - * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS - */ - if(hr == STG_E_INVALIDHEADER) - hr = STG_E_FILEALREADYEXISTS; - goto end; - } - - *ppstgOpen = &newStorage->IStorage_iface; - -end: - CoTaskMemFree(temp_name); - if (pstgPriority) IStorage_Release(pstgPriority); - TRACE("<-- %#lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL); - return hr; -} - -/****************************************************************************** - * StgCreateDocfileOnILockBytes [OLE32.@] - */ -HRESULT WINAPI StgCreateDocfileOnILockBytes( - ILockBytes *plkbyt, - DWORD grfMode, - DWORD reserved, - IStorage** ppstgOpen) -{ - StorageBaseImpl* newStorage = 0; - HRESULT hr = S_OK; - - if ((ppstgOpen == 0) || (plkbyt == 0)) - return STG_E_INVALIDPOINTER; - - /* - * Allocate and initialize the new IStorage object. - */ - hr = Storage_Construct( - 0, - 0, - plkbyt, - grfMode, - FALSE, - TRUE, - 512, - &newStorage); - - if (FAILED(hr)) - { - return hr; - } - - *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.HighPart = 0; - offset.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.@) @@ -9141,7 +122,7 @@ HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm) }
/************************************************************************* - * STORAGE_CreateOleStream [Internal] + * STORAGE_CreateOleStream [Internal] (copy from coml2) * * Creates the "\001OLE" stream in the IStorage if necessary. * @@ -9191,337 +172,6 @@ HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags) 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("%ld bytes\n",len); - - str = CoTaskMemAlloc( len ); - if( !str ) - return E_OUTOFMEMORY; - count = 0; - r = IStream_Read( stm, str, len, &count ); - if( FAILED( r ) ) - { - CoTaskMemFree( str ); - 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 r; -} - - -static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, - LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) -{ - IStream *pstm; - HRESULT r = S_OK; - - 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, L"\1CompObj", - 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 ) - { - n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName)); - 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; - - ProgIDFromCLSID(&clsid, &wstrProgID); - - TRACE("progid is %s\n",debugstr_w(wstrProgID)); - - r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType, - cf ? szwClipName : NULL, wstrProgID ); - - CoTaskMemFree(wstrProgID); - - return r; -} - - -/****************************************************************************** - * ReadFmtUserTypeStg [OLE32.@] - */ -HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) -{ - HRESULT r; - IStream *stm = 0; - unsigned char unknown1[12]; - unsigned char unknown2[16]; - DWORD count; - LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; - CLSID clsid; - - TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); - - r = IStorage_OpenStream( pstg, L"\1CompObj", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); - if( FAILED ( r ) ) - { - WARN("Failed to open stream r = %#lx\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; - - r = STREAM_ReadString( stm, &szCLSIDName ); - if( FAILED( r ) ) - goto end; - - r = STREAM_ReadString( stm, &szOleTypeName ); - if( FAILED( r ) ) - goto end; - - r = STREAM_ReadString( stm, &szProgIDName ); - if( FAILED( r ) ) - goto end; - - r = IStream_Read( stm, unknown2, sizeof(unknown2), &count ); - if( FAILED( r ) || ( count != sizeof(unknown2) ) ) - goto end; - - /* ok, success... now we just need to store what we found */ - if( pcf ) - *pcf = RegisterClipboardFormatW( szOleTypeName ); - - if( lplpszUserType ) - { - *lplpszUserType = szCLSIDName; - szCLSIDName = NULL; - } - -end: - CoTaskMemFree( szCLSIDName ); - CoTaskMemFree( szOleTypeName ); - CoTaskMemFree( szProgIDName ); - IStream_Release( stm ); - - return r; -} - -/****************************************************************************** - * 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; -} - - /************************************************************************ * OleConvert Functions ***********************************************************************/ @@ -10549,11 +1199,6 @@ HRESULT WINAPI OleConvertIStorageToOLESTREAM ( return hRes; }
-enum stream_1ole_flags { - OleStream_LinkedObject = 0x00000001, - OleStream_Convert = 0x00000004 -}; - /************************************************************************* * OleConvertIStorageToOLESTREAMEx [OLE32.@] */ @@ -10564,86 +1209,3 @@ HRESULT WINAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE stg, CLIPFORMAT cf, L
return E_NOTIMPL; } - -/*********************************************************************** - * GetConvertStg (OLE32.@) - */ -HRESULT WINAPI GetConvertStg(IStorage *stg) -{ - static const DWORD version_magic = 0x02000001; - DWORD header[2]; - IStream *stream; - HRESULT hr; - - TRACE("%p\n", stg); - - if (!stg) return E_INVALIDARG; - - hr = IStorage_OpenStream(stg, L"\1Ole", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream); - if (FAILED(hr)) return hr; - - hr = IStream_Read(stream, header, sizeof(header), NULL); - IStream_Release(stream); - if (FAILED(hr)) return hr; - - if (header[0] != version_magic) - { - ERR("got wrong version magic for 1Ole stream, %#lx.\n", header[0]); - return E_FAIL; - } - - return header[1] & OleStream_Convert ? S_OK : S_FALSE; -} - -/*********************************************************************** - * SetConvertStg (OLE32.@) - */ -HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert) -{ - DWORD flags = convert ? OleStream_Convert : 0; - IStream *stream; - DWORD header[2]; - HRESULT hr; - - TRACE("(%p, %d)\n", storage, convert); - - hr = IStorage_OpenStream(storage, L"\1Ole", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream); - if (FAILED(hr)) - { - if (hr != STG_E_FILENOTFOUND) - return hr; - - return STORAGE_CreateOleStream(storage, flags); - } - - hr = IStream_Read(stream, header, sizeof(header), NULL); - if (FAILED(hr)) - { - IStream_Release(stream); - return hr; - } - - /* update flag if differs */ - if ((header[1] ^ flags) & OleStream_Convert) - { - LARGE_INTEGER pos = {{0}}; - - if (header[1] & OleStream_Convert) - flags = header[1] & ~OleStream_Convert; - else - flags = header[1] | OleStream_Convert; - - pos.QuadPart = sizeof(DWORD); - hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - { - IStream_Release(stream); - return hr; - } - - hr = IStream_Write(stream, &flags, sizeof(flags), NULL); - } - - IStream_Release(stream); - return hr; -} diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h index dd4b89aceec..591b389c329 100644 --- a/dlls/ole32/storage32.h +++ b/dlls/ole32/storage32.h @@ -40,532 +40,8 @@ #include "winternl.h" #include "wine/list.h"
-/* - * Definitions for the file format offsets. - */ -static const ULONG OFFSET_MINORVERSION = 0x00000018; -static const ULONG OFFSET_MAJORVERSION = 0x0000001a; -static const ULONG OFFSET_BYTEORDERMARKER = 0x0000001c; -static const ULONG OFFSET_BIGBLOCKSIZEBITS = 0x0000001e; -static const ULONG OFFSET_SMALLBLOCKSIZEBITS = 0x00000020; -static const ULONG OFFSET_DIRSECTORCOUNT = 0x00000028; -static const ULONG OFFSET_BBDEPOTCOUNT = 0x0000002C; -static const ULONG OFFSET_ROOTSTARTBLOCK = 0x00000030; -static const ULONG OFFSET_TRANSACTIONSIG = 0x00000034; -static const ULONG OFFSET_SMALLBLOCKLIMIT = 0x00000038; -static const ULONG OFFSET_SBDEPOTSTART = 0x0000003C; -static const ULONG OFFSET_SBDEPOTCOUNT = 0x00000040; -static const ULONG OFFSET_EXTBBDEPOTSTART = 0x00000044; -static const ULONG OFFSET_EXTBBDEPOTCOUNT = 0x00000048; -static const ULONG OFFSET_BBDEPOTSTART = 0x0000004C; -static const ULONG OFFSET_PS_NAME = 0x00000000; -static const ULONG OFFSET_PS_NAMELENGTH = 0x00000040; -static const ULONG OFFSET_PS_STGTYPE = 0x00000042; -static const ULONG OFFSET_PS_LEFTCHILD = 0x00000044; -static const ULONG OFFSET_PS_RIGHTCHILD = 0x00000048; -static const ULONG OFFSET_PS_DIRROOT = 0x0000004C; -static const ULONG OFFSET_PS_GUID = 0x00000050; -static const ULONG OFFSET_PS_CTIMELOW = 0x00000064; -static const ULONG OFFSET_PS_CTIMEHIGH = 0x00000068; -static const ULONG OFFSET_PS_MTIMELOW = 0x0000006C; -static const ULONG OFFSET_PS_MTIMEHIGH = 0x00000070; -static const ULONG OFFSET_PS_STARTBLOCK = 0x00000074; -static const ULONG OFFSET_PS_SIZE = 0x00000078; -static const ULONG OFFSET_PS_SIZE_HIGH = 0x0000007C; -static const WORD DEF_BIG_BLOCK_SIZE_BITS = 0x0009; -static const WORD MIN_BIG_BLOCK_SIZE_BITS = 0x0009; -static const WORD MAX_BIG_BLOCK_SIZE_BITS = 0x000c; -static const WORD DEF_SMALL_BLOCK_SIZE_BITS = 0x0006; -static const WORD DEF_BIG_BLOCK_SIZE = 0x0200; -static const WORD DEF_SMALL_BLOCK_SIZE = 0x0040; -static const ULONG BLOCK_FIRST_SPECIAL = 0xFFFFFFFB; -static const ULONG BLOCK_EXTBBDEPOT = 0xFFFFFFFC; -static const ULONG BLOCK_SPECIAL = 0xFFFFFFFD; -static const ULONG BLOCK_END_OF_CHAIN = 0xFFFFFFFE; -static const ULONG BLOCK_UNUSED = 0xFFFFFFFF; -static const ULONG DIRENTRY_NULL = 0xFFFFFFFF; - -#define DIRENTRY_NAME_MAX_LEN 0x20 -#define DIRENTRY_NAME_BUFFER_LEN 0x40 - -#define RAW_DIRENTRY_SIZE 0x00000080 - -#define HEADER_SIZE 512 - -#define MIN_BIG_BLOCK_SIZE 0x200 -#define MAX_BIG_BLOCK_SIZE 0x1000 - -/* - * Type of child entry link - */ -#define DIRENTRY_RELATION_PREVIOUS 0 -#define DIRENTRY_RELATION_NEXT 1 -#define DIRENTRY_RELATION_DIR 2 - -/* - * type constant used in files for the root storage - */ -#define STGTY_ROOT 0x05 - -#define COUNT_BBDEPOTINHEADER 109 - -/* FIXME: This value is stored in the header, but we hard-code it to 0x1000. */ -#define LIMIT_TO_USE_SMALL_BLOCK 0x1000 - -#define STGM_ACCESS_MODE(stgm) ((stgm)&0x0000f) -#define STGM_SHARE_MODE(stgm) ((stgm)&0x000f0) -#define STGM_CREATE_MODE(stgm) ((stgm)&0x0f000) - -#define STGM_KNOWN_FLAGS (0xf0ff | \ - STGM_TRANSACTED | STGM_CONVERT | STGM_PRIORITY | STGM_NOSCRATCH | \ - STGM_NOSNAPSHOT | STGM_DIRECT_SWMR | STGM_DELETEONRELEASE | STGM_SIMPLE) - -/* - * Forward declarations of all the structures used by the storage - * module. - */ -typedef struct StorageBaseImpl StorageBaseImpl; -typedef struct StorageBaseImplVtbl StorageBaseImplVtbl; -typedef struct StorageImpl StorageImpl; -typedef struct BlockChainStream BlockChainStream; -typedef struct SmallBlockChainStream SmallBlockChainStream; -typedef struct IEnumSTATSTGImpl IEnumSTATSTGImpl; -typedef struct DirEntry DirEntry; -typedef struct StgStreamImpl StgStreamImpl; - -/* - * A reference to a directory entry in the file or a transacted cache. - */ -typedef ULONG DirRef; - -/* - * This utility structure is used to read/write the information in a directory - * entry. - */ -struct DirEntry -{ - WCHAR name[DIRENTRY_NAME_MAX_LEN]; - WORD sizeOfNameString; - BYTE stgType; - DirRef leftChild; - DirRef rightChild; - DirRef dirRootEntry; - GUID clsid; - FILETIME ctime; - FILETIME mtime; - ULONG startingBlock; - ULARGE_INTEGER size; -}; - -HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes); - -/************************************************************************* - * Ole Convert support - */
HRESULT STORAGE_CreateOleStream(IStorage*, DWORD); HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName);
-enum swmr_mode -{ - SWMR_None, - SWMR_Writer, - SWMR_Reader -}; - -/**************************************************************************** - * StorageBaseImpl definitions. - * - * This structure defines the base information contained in all implementations - * of IStorage contained in this file storage implementation. - * - * In OOP terms, this is the base class for all the IStorage implementations - * contained in this file. - */ -struct StorageBaseImpl -{ - IStorage IStorage_iface; - IPropertySetStorage IPropertySetStorage_iface; /* interface for adding a properties stream */ - IDirectWriterLock IDirectWriterLock_iface; - LONG ref; - - /* - * Stream tracking list - */ - - struct list strmHead; - - /* - * Storage tracking list - */ - struct list storageHead; - - /* - * TRUE if this object has been invalidated - */ - BOOL reverted; - - /* - * Index of the directory entry of this storage - */ - DirRef storageDirEntry; - - /* - * virtual methods. - */ - const StorageBaseImplVtbl *baseVtbl; - - /* - * flags that this storage was opened or created with - */ - DWORD openFlags; - - /* - * State bits appear to only be preserved while running. No in the stream - */ - DWORD stateBits; - - BOOL create; /* Was the storage created or opened. - The behaviour of STGM_SIMPLE depends on this */ - /* - * If this storage was opened in transacted mode, the object that implements - * the transacted snapshot or cache. - */ - StorageBaseImpl *transactedChild; - enum swmr_mode lockingrole; -}; - -/* virtual methods for StorageBaseImpl objects */ -struct StorageBaseImplVtbl { - void (*Destroy)(StorageBaseImpl*); - void (*Invalidate)(StorageBaseImpl*); - HRESULT (*Flush)(StorageBaseImpl*); - HRESULT (*GetFilename)(StorageBaseImpl*,LPWSTR*); - HRESULT (*CreateDirEntry)(StorageBaseImpl*,const DirEntry*,DirRef*); - HRESULT (*WriteDirEntry)(StorageBaseImpl*,DirRef,const DirEntry*); - HRESULT (*ReadDirEntry)(StorageBaseImpl*,DirRef,DirEntry*); - HRESULT (*DestroyDirEntry)(StorageBaseImpl*,DirRef); - HRESULT (*StreamReadAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,void*,ULONG*); - HRESULT (*StreamWriteAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,const void*,ULONG*); - HRESULT (*StreamSetSize)(StorageBaseImpl*,DirRef,ULARGE_INTEGER); - HRESULT (*StreamLink)(StorageBaseImpl*,DirRef,DirRef); - HRESULT (*GetTransactionSig)(StorageBaseImpl*,ULONG*,BOOL); - HRESULT (*SetTransactionSig)(StorageBaseImpl*,ULONG); - HRESULT (*LockTransaction)(StorageBaseImpl*,BOOL); - HRESULT (*UnlockTransaction)(StorageBaseImpl*,BOOL); -}; - -static inline void StorageBaseImpl_Destroy(StorageBaseImpl *This) -{ - This->baseVtbl->Destroy(This); -} - -static inline void StorageBaseImpl_Invalidate(StorageBaseImpl *This) -{ - This->baseVtbl->Invalidate(This); -} - -static inline HRESULT StorageBaseImpl_Flush(StorageBaseImpl *This) -{ - return This->baseVtbl->Flush(This); -} - -static inline HRESULT StorageBaseImpl_GetFilename(StorageBaseImpl *This, LPWSTR *result) -{ - return This->baseVtbl->GetFilename(This, result); -} - -static inline HRESULT StorageBaseImpl_CreateDirEntry(StorageBaseImpl *This, - const DirEntry *newData, DirRef *index) -{ - return This->baseVtbl->CreateDirEntry(This, newData, index); -} - -static inline HRESULT StorageBaseImpl_WriteDirEntry(StorageBaseImpl *This, - DirRef index, const DirEntry *data) -{ - return This->baseVtbl->WriteDirEntry(This, index, data); -} - -static inline HRESULT StorageBaseImpl_ReadDirEntry(StorageBaseImpl *This, - DirRef index, DirEntry *data) -{ - return This->baseVtbl->ReadDirEntry(This, index, data); -} - -static inline HRESULT StorageBaseImpl_DestroyDirEntry(StorageBaseImpl *This, - DirRef index) -{ - return This->baseVtbl->DestroyDirEntry(This, index); -} - -/* Read up to size bytes from this directory entry's stream at the given offset. */ -static inline HRESULT StorageBaseImpl_StreamReadAt(StorageBaseImpl *This, - DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) -{ - return This->baseVtbl->StreamReadAt(This, index, offset, size, buffer, bytesRead); -} - -/* Write size bytes to this directory entry's stream at the given offset, - * growing the stream if necessary. */ -static inline HRESULT StorageBaseImpl_StreamWriteAt(StorageBaseImpl *This, - DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) -{ - return This->baseVtbl->StreamWriteAt(This, index, offset, size, buffer, bytesWritten); -} - -static inline HRESULT StorageBaseImpl_StreamSetSize(StorageBaseImpl *This, - DirRef index, ULARGE_INTEGER newsize) -{ - return This->baseVtbl->StreamSetSize(This, index, newsize); -} - -/* Make dst point to the same stream that src points to. Other stream operations - * will not work properly for entries that point to the same stream, so this - * must be a very temporary state, and only one entry pointing to a given stream - * may be reachable at any given time. */ -static inline HRESULT StorageBaseImpl_StreamLink(StorageBaseImpl *This, - DirRef dst, DirRef src) -{ - return This->baseVtbl->StreamLink(This, dst, src); -} - -static inline HRESULT StorageBaseImpl_GetTransactionSig(StorageBaseImpl *This, - ULONG* result, BOOL refresh) -{ - return This->baseVtbl->GetTransactionSig(This, result, refresh); -} - -static inline HRESULT StorageBaseImpl_SetTransactionSig(StorageBaseImpl *This, - ULONG value) -{ - return This->baseVtbl->SetTransactionSig(This, value); -} - -static inline HRESULT StorageBaseImpl_LockTransaction(StorageBaseImpl *This, BOOL write) -{ - return This->baseVtbl->LockTransaction(This, write); -} - -static inline HRESULT StorageBaseImpl_UnlockTransaction(StorageBaseImpl *This, BOOL write) -{ - return This->baseVtbl->UnlockTransaction(This, write); -} - -/**************************************************************************** - * StorageBaseImpl stream list handlers - */ - -void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm); -void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm); - -/* Number of BlockChainStream objects to cache in a StorageImpl */ -#define BLOCKCHAIN_CACHE_SIZE 4 - -/**************************************************************************** - * StorageImpl definitions. - * - * This implementation of the IStorage interface represents a root - * storage. Basically, a document file. - */ -struct StorageImpl -{ - struct StorageBaseImpl base; - - /* - * File header - */ - WORD bigBlockSizeBits; - WORD smallBlockSizeBits; - ULONG bigBlockSize; - ULONG smallBlockSize; - ULONG bigBlockDepotCount; - ULONG rootStartBlock; - ULONG smallBlockLimit; - ULONG smallBlockDepotStart; - ULONG extBigBlockDepotStart; - ULONG *extBigBlockDepotLocations; - ULONG extBigBlockDepotLocationsSize; - ULONG extBigBlockDepotCount; - ULONG bigBlockDepotStart[COUNT_BBDEPOTINHEADER]; - ULONG transactionSig; - - ULONG extBlockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; - ULONG indexExtBlockDepotCached; - - ULONG blockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; - ULONG indexBlockDepotCached; - ULONG prevFreeBlock; - - /* All small blocks before this one are known to be in use. */ - ULONG firstFreeSmallBlock; - - /* - * Abstraction of the big block chains for the chains of the header. - */ - BlockChainStream* rootBlockChain; - BlockChainStream* smallBlockDepotChain; - BlockChainStream* smallBlockRootChain; - - /* Cache of block chain streams objects for directory entries */ - BlockChainStream* blockChainCache[BLOCKCHAIN_CACHE_SIZE]; - UINT blockChainToEvict; - - ULONG locks_supported; - - ILockBytes* lockBytes; - - ULONG locked_bytes[8]; -}; - -/**************************************************************************** - * StgStreamImpl definitions. - * - * This class implements the IStream interface and represents a stream - * located inside a storage object. - */ -struct StgStreamImpl -{ - IStream IStream_iface; - LONG ref; - - /* - * We are an entry in the storage object's stream handler list - */ - struct list StrmListEntry; - - /* - * Storage that is the parent(owner) of the stream - */ - StorageBaseImpl* parentStorage; - - /* - * Access mode of this stream. - */ - DWORD grfMode; - - /* - * Index of the directory entry that owns (points to) this stream. - */ - DirRef dirEntry; - - /* - * This is the current position of the cursor in the stream - */ - ULARGE_INTEGER currentPosition; -}; - -static inline StgStreamImpl *impl_from_IStream( IStream *iface ) -{ - return CONTAINING_RECORD(iface, StgStreamImpl, IStream_iface); -} - -/* - * Method definition for the StgStreamImpl class. - */ -StgStreamImpl* StgStreamImpl_Construct( - StorageBaseImpl* parentStorage, - DWORD grfMode, - DirRef dirEntry); - - -/* Range lock constants. - * - * The storage format reserves the region from 0x7fffff00-0x7fffffff for - * locking and synchronization. Because it reserves the entire block containing - * that range, and the minimum block size is 512 bytes, 0x7ffffe00-0x7ffffeff - * also cannot be used for any other purpose. - * Unfortunately, the spec doesn't say which bytes - * within that range are used, and for what. These are guesses based on testing. - * In particular, ends of ranges may be wrong. - - 0x0 through 0x57: Unknown. Causes read-only exclusive opens to fail. - 0x58 through 0x6b: Priority mode. - 0x6c through 0x7f: No snapshot mode. - 0x80: Commit lock. - 0x81 through 0x91: Priority mode, again. Not sure why it uses two regions. - 0x92: Lock-checking lock. Held while opening so ranges can be tested without - causing spurious failures if others try to grab or test those ranges at the - same time. - 0x93 through 0xa6: Read mode. - 0xa7 through 0xba: Write mode. - 0xbb through 0xce: Deny read. - 0xcf through 0xe2: Deny write. - 0xe2 through 0xff: Unknown. Causes read-only exclusive opens to fail. -*/ - -#define RANGELOCK_UNK1_FIRST 0x7ffffe00 -#define RANGELOCK_UNK1_LAST 0x7fffff57 -#define RANGELOCK_PRIORITY1_FIRST 0x7fffff58 -#define RANGELOCK_PRIORITY1_LAST 0x7fffff6b -#define RANGELOCK_NOSNAPSHOT_FIRST 0x7fffff6c -#define RANGELOCK_NOSNAPSHOT_LAST 0x7fffff7f -#define RANGELOCK_COMMIT 0x7fffff80 -#define RANGELOCK_PRIORITY2_FIRST 0x7fffff81 -#define RANGELOCK_PRIORITY2_LAST 0x7fffff91 -#define RANGELOCK_CHECKLOCKS 0x7fffff92 -#define RANGELOCK_READ_FIRST 0x7fffff93 -#define RANGELOCK_READ_LAST 0x7fffffa6 -#define RANGELOCK_WRITE_FIRST 0x7fffffa7 -#define RANGELOCK_WRITE_LAST 0x7fffffba -#define RANGELOCK_DENY_READ_FIRST 0x7fffffbb -#define RANGELOCK_DENY_READ_LAST 0x7fffffce -#define RANGELOCK_DENY_WRITE_FIRST 0x7fffffcf -#define RANGELOCK_DENY_WRITE_LAST 0x7fffffe2 -#define RANGELOCK_UNK2_FIRST 0x7fffffe3 -#define RANGELOCK_UNK2_LAST 0x7fffffff -#define RANGELOCK_TRANSACTION_FIRST RANGELOCK_COMMIT -#define RANGELOCK_TRANSACTION_LAST RANGELOCK_CHECKLOCKS -#define RANGELOCK_FIRST RANGELOCK_UNK1_FIRST -#define RANGELOCK_LAST RANGELOCK_UNK2_LAST - -/* internal value for LockRegion/UnlockRegion */ -#define WINE_LOCK_READ 0x80000000 - - -/****************************************************************************** - * Endian conversion macros - */ -#ifdef WORDS_BIGENDIAN - -#ifndef htole32 -#define htole32(x) RtlUlongByteSwap(x) -#endif -#ifndef htole16 -#define htole16(x) RtlUshortByteSwap(x) -#endif -#define lendian32toh(x) RtlUlongByteSwap(x) -#define lendian16toh(x) RtlUshortByteSwap(x) - -#else - -#ifndef htole32 -#define htole32(x) (x) -#endif -#ifndef htole16 -#define htole16(x) (x) -#endif -#define lendian32toh(x) (x) -#define lendian16toh(x) (x) - -#endif - -/****************************************************************************** - * The StorageUtl_ functions are miscellaneous utility functions. Most of which - * are abstractions used to read values from file buffers without having to - * worry about bit order - */ -void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value); -void StorageUtl_WriteWord(void *buffer, ULONG offset, WORD value); -void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value); -void StorageUtl_WriteDWord(void *buffer, ULONG offset, DWORD value); -void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, - ULARGE_INTEGER* value); -void StorageUtl_WriteULargeInteger(void *buffer, ULONG offset, const ULARGE_INTEGER *value); -void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value); -void StorageUtl_WriteGUID(void *buffer, ULONG offset, const GUID* value); -void StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl *storage,STATSTG* destination, - const DirEntry* source, int statFlags); - - #endif /* __STORAGE32_H__ */
From: Fabian Maurer dark.shadow4@web.de
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55075 --- dlls/apisetschema/apisetschema.spec | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/apisetschema/apisetschema.spec b/dlls/apisetschema/apisetschema.spec index 9b09f01e5e0..23ef274268e 100644 --- a/dlls/apisetschema/apisetschema.spec +++ b/dlls/apisetschema/apisetschema.spec @@ -9,6 +9,7 @@ apiset api-ms-win-core-atoms-l1-1-0 = kernel32.dll apiset api-ms-win-core-backgroundtask-l1-1-0 = kernelbase.dll apiset api-ms-win-core-calendar-l1-1-0 = kernel32.dll apiset api-ms-win-core-com-l1-1-1 = combase.dll +apiset api-ms-win-core-com-l2-1-1 = coml2 apiset api-ms-win-core-com-midlproxystub-l1-1-0 = combase.dll apiset api-ms-win-core-com-private-l1-1-0 = combase.dll apiset api-ms-win-core-com-private-l1-2-0 = combase.dll
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=139566
Your paranoid android.
=== debian11 (32 bit report) ===
ole32: propvariant.c:609: Test failed: StgConvertVariantToProperty not available
=== debian11b (64 bit WoW report) ===
ole32: propvariant.c:609: Test failed: StgConvertVariantToProperty not available
On Tue Jun 20 20:46:13 2023 +0000, Alexandre Julliard wrote:
Wine seems to already have a bunch of the functions, e.g.
"ReadClassStm", but they're all defined in ole32. I don't know if they belong into coml2 though, or if coml2 redirects to ole32. They should be in coml2 and imported from ole32, along the lines of what was done for combase.dll.
Updated. Could someone please take a look, because I never did such a large refactor...