diff --git a/reactos/dll/win32/ole32/clipboard.c b/reactos/dll/win32/ole32/clipboard.c index 2afc77e35f0..3064bb04156 100644 --- a/reactos/dll/win32/ole32/clipboard.c +++ b/reactos/dll/win32/ole32/clipboard.c @@ -1266,10 +1266,10 @@ static HRESULT WINAPI snapshot_GetDataHere(IDataObject *iface, FORMATETC *fmt, ole_priv_data_entry *entry; TYMED supported; - TRACE("(%p, %p {%s}, %p (tymed %x)\n", iface, fmt, dump_fmtetc(fmt), med, med->tymed); - if ( !fmt || !med ) return E_INVALIDARG; + TRACE("(%p, %p {%s}, %p (tymed %x)\n", iface, fmt, dump_fmtetc(fmt), med, med->tymed); + if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; if(!This->data) diff --git a/reactos/dll/win32/ole32/comcat.c b/reactos/dll/win32/ole32/comcat.c index b8060bdefca..fe0f7e5962e 100644 --- a/reactos/dll/win32/ole32/comcat.c +++ b/reactos/dll/win32/ole32/comcat.c @@ -232,18 +232,20 @@ static HRESULT COMCAT_IsClassOfCategories( LPCWSTR string; /* Check that every given category is implemented by class. */ - res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey); - if (res != ERROR_SUCCESS) return S_FALSE; - for (string = categories->impl_strings; *string; string += 39) { - HKEY catkey; - res = RegOpenKeyExW(subkey, string, 0, 0, &catkey); - if (res != ERROR_SUCCESS) { - RegCloseKey(subkey); - return S_FALSE; + if (*categories->impl_strings) { + res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey); + if (res != ERROR_SUCCESS) return S_FALSE; + for (string = categories->impl_strings; *string; string += 39) { + HKEY catkey; + res = RegOpenKeyExW(subkey, string, 0, 0, &catkey); + if (res != ERROR_SUCCESS) { + RegCloseKey(subkey); + return S_FALSE; + } + RegCloseKey(catkey); } - RegCloseKey(catkey); + RegCloseKey(subkey); } - RegCloseKey(subkey); /* Check that all categories required by class are given. */ res = RegOpenKeyExW(key, req_keyname, 0, KEY_READ, &subkey); diff --git a/reactos/dll/win32/ole32/compobj.c b/reactos/dll/win32/ole32/compobj.c index 0fb074dc50c..937f3d8a06c 100644 --- a/reactos/dll/win32/ole32/compobj.c +++ b/reactos/dll/win32/ole32/compobj.c @@ -4156,7 +4156,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) break; case DLL_PROCESS_DETACH: - OLEDD_UnInitialize(); COMPOBJ_UninitProcess(); RPC_UnregisterAllChannelHooks(); COMPOBJ_DllList_Free(); diff --git a/reactos/dll/win32/ole32/compositemoniker.c b/reactos/dll/win32/ole32/compositemoniker.c index 7402b8edbbf..275d875362b 100644 --- a/reactos/dll/win32/ole32/compositemoniker.c +++ b/reactos/dll/win32/ole32/compositemoniker.c @@ -445,7 +445,6 @@ static HRESULT WINAPI CompositeMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar, IMoniker** ppmkToLeft, IMoniker** ppmkReduced) { - HRESULT res; IMoniker *tempMk,*antiMk,*mostRigthMk,*leftReducedComposedMk,*mostRigthReducedMk; IEnumMoniker *enumMoniker; @@ -462,8 +461,8 @@ CompositeMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar IEnumMoniker_Next(enumMoniker,1,&mostRigthMk,NULL); IEnumMoniker_Release(enumMoniker); - res=CreateAntiMoniker(&antiMk); - res=IMoniker_ComposeWith(iface,antiMk,0,&tempMk); + CreateAntiMoniker(&antiMk); + IMoniker_ComposeWith(iface,antiMk,0,&tempMk); IMoniker_Release(antiMk); return IMoniker_Reduce(mostRigthMk,pbc,dwReduceHowFar,&tempMk, ppmkReduced); @@ -479,8 +478,8 @@ CompositeMonikerImpl_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar IEnumMoniker_Next(enumMoniker,1,&mostRigthMk,NULL); IEnumMoniker_Release(enumMoniker); - res=CreateAntiMoniker(&antiMk); - res=IMoniker_ComposeWith(iface,antiMk,0,&tempMk); + CreateAntiMoniker(&antiMk); + IMoniker_ComposeWith(iface,antiMk,0,&tempMk); IMoniker_Release(antiMk); /* If any of the components reduces itself, the method returns S_OK and passes back a composite */ diff --git a/reactos/dll/win32/ole32/defaulthandler.c b/reactos/dll/win32/ole32/defaulthandler.c index 5130513d57f..d0a871f17c0 100644 --- a/reactos/dll/win32/ole32/defaulthandler.c +++ b/reactos/dll/win32/ole32/defaulthandler.c @@ -365,7 +365,7 @@ static HRESULT WINAPI DefaultHandler_SetClientSite( if (This->clientSite) IOleClientSite_AddRef(This->clientSite); - return S_OK; + return hr; } /************************************************************************ diff --git a/reactos/dll/win32/ole32/hglobalstream.c b/reactos/dll/win32/ole32/hglobalstream.c index 5742f99619a..ef616defec5 100644 --- a/reactos/dll/win32/ole32/hglobalstream.c +++ b/reactos/dll/win32/ole32/hglobalstream.c @@ -371,12 +371,6 @@ static HRESULT WINAPI HGLOBALStreamImpl_Seek( TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition); - if (dlibMove.u.LowPart >= 0x80000000) - { - hr = STG_E_SEEKERROR; - goto end; - } - /* * The file pointer is moved depending on the given "function" * parameter. @@ -405,10 +399,19 @@ static HRESULT WINAPI HGLOBALStreamImpl_Seek( newPosition.u.HighPart = 0; newPosition.u.LowPart += dlibMove.QuadPart; -end: - if (plibNewPosition) *plibNewPosition = newPosition; + if (dlibMove.u.LowPart >= 0x80000000 && + newPosition.u.LowPart >= dlibMove.u.LowPart) + { + /* We tried to seek backwards and went past the start. */ + hr = STG_E_SEEKERROR; + goto end; + } + This->currentPosition = newPosition; +end: + if (plibNewPosition) *plibNewPosition = This->currentPosition; + return hr; } diff --git a/reactos/dll/win32/ole32/ifs.c b/reactos/dll/win32/ole32/ifs.c index c6a6962cd72..1a10c3e1f53 100644 --- a/reactos/dll/win32/ole32/ifs.c +++ b/reactos/dll/win32/ole32/ifs.c @@ -501,7 +501,7 @@ HRESULT WINAPI CoRevokeMallocSpy(void) } LeaveCriticalSection(&IMalloc32_SpyCS); - return S_OK; + return hres; } /****************************************************************************** diff --git a/reactos/dll/win32/ole32/marshal.c b/reactos/dll/win32/ole32/marshal.c index 54a3a87b2b6..d14516f6dec 100644 --- a/reactos/dll/win32/ole32/marshal.c +++ b/reactos/dll/win32/ole32/marshal.c @@ -1214,16 +1214,13 @@ StdMarshalImpl_MarshalInterface( RPC_StartRemoting(apt); hres = marshal_object(apt, &stdobjref, riid, pv, mshlflags); - if (hres) + if (hres != S_OK) { ERR("Failed to create ifstub, hres=0x%x\n", hres); return hres; } - hres = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), &res); - if (hres) return hres; - - return S_OK; + return IStream_Write(pStm, &stdobjref, sizeof(stdobjref), &res); } /* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with @@ -1321,10 +1318,10 @@ StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, v /* read STDOBJREF from wire */ hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); - if (hres) return STG_E_READFAULT; + if (hres != S_OK) return STG_E_READFAULT; hres = apartment_getoxid(apt, &oxid); - if (hres) return hres; + if (hres != S_OK) return hres; /* check if we're marshalling back to ourselves */ if ((oxid == stdobjref.oxid) && (stubmgr = get_stub_manager(apt, stdobjref.oid))) @@ -1374,7 +1371,7 @@ StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, v if (stubmgr) stub_manager_int_release(stubmgr); if (stub_apt) apartment_release(stub_apt); - if (hres) WARN("Failed with error 0x%08x\n", hres); + if (hres != S_OK) WARN("Failed with error 0x%08x\n", hres); else TRACE("Successfully created proxy %p\n", *ppv); return hres; @@ -1392,7 +1389,7 @@ StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm) TRACE("iface=%p, pStm=%p\n", iface, pStm); hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); - if (hres) return STG_E_READFAULT; + if (hres != S_OK) return STG_E_READFAULT; TRACE("oxid = %s, oid = %s, ipid = %s\n", wine_dbgstr_longlong(stdobjref.oxid), @@ -1516,7 +1513,7 @@ static HRESULT get_marshaler(REFIID riid, IUnknown *pUnk, DWORD dwDestContext, if (!pUnk) return E_POINTER; hr = IUnknown_QueryInterface(pUnk, &IID_IMarshal, (LPVOID*)pMarshal); - if (hr) + if (hr != S_OK) hr = CoGetStandardMarshal(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, pMarshal); return hr; @@ -1537,7 +1534,7 @@ static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, /* read common OBJREF header */ hr = IStream_Read(stream, &objref, FIELD_OFFSET(OBJREF, u_objref), &res); - if (hr || (res != FIELD_OFFSET(OBJREF, u_objref))) + if (hr != S_OK || (res != FIELD_OFFSET(OBJREF, u_objref))) { ERR("Failed to read common OBJREF header, 0x%08x\n", hr); return STG_E_READFAULT; @@ -1566,7 +1563,7 @@ static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, /* read constant sized OR_CUSTOM data from stream */ hr = IStream_Read(stream, &objref.u_objref.u_custom, custom_header_size, &res); - if (hr || (res != custom_header_size)) + if (hr != S_OK || (res != custom_header_size)) { ERR("Failed to read OR_CUSTOM header, 0x%08x\n", hr); return STG_E_READFAULT; @@ -1583,7 +1580,7 @@ static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, return RPC_E_INVALID_OBJREF; } - if (hr) + if (hr != S_OK) ERR("Failed to create marshal, 0x%08x\n", hr); return hr; @@ -1618,12 +1615,12 @@ HRESULT WINAPI CoGetMarshalSizeMax(ULONG *pulSize, REFIID riid, IUnknown *pUnk, CLSID marshaler_clsid; hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal); - if (hr) + if (hr != S_OK) return hr; hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &marshaler_clsid); - if (hr) + if (hr != S_OK) { ERR("IMarshal::GetUnmarshalClass failed, 0x%08x\n", hr); IMarshal_Release(pMarshal); @@ -1711,7 +1708,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, /* get the marshaler for the specified interface */ hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal); - if (hr) + if (hr != S_OK) { ERR("Failed to get marshaller, 0x%08x\n", hr); return hr; @@ -1719,7 +1716,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &marshaler_clsid); - if (hr) + if (hr != S_OK) { ERR("IMarshal::GetUnmarshalClass failed, 0x%08x\n", hr); goto cleanup; @@ -1733,7 +1730,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, /* write the common OBJREF header to the stream */ hr = IStream_Write(pStream, &objref, FIELD_OFFSET(OBJREF, u_objref), NULL); - if (hr) + if (hr != S_OK) { ERR("Failed to write OBJREF header to stream, 0x%08x\n", hr); goto cleanup; @@ -1749,7 +1746,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, hr = IMarshal_GetMarshalSizeMax(pMarshal, riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &objref.u_objref.u_custom.size); - if (hr) + if (hr != S_OK) { ERR("Failed to get max size of marshal data, error 0x%08x\n", hr); goto cleanup; @@ -1757,7 +1754,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, /* write constant sized common header and OR_CUSTOM data into stream */ hr = IStream_Write(pStream, &objref, FIELD_OFFSET(OBJREF, u_objref.u_custom.pData), NULL); - if (hr) + if (hr != S_OK) { ERR("Failed to write OR_CUSTOM header to stream with 0x%08x\n", hr); goto cleanup; @@ -1769,7 +1766,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, hr = IMarshal_MarshalInterface(pMarshal, pStream, riid, pUnk, dwDestContext, pvDestContext, mshlFlags); - if (hr) + if (hr != S_OK) { ERR("Failed to marshal the interface %s, %x\n", debugstr_guid(riid), hr); goto cleanup; @@ -1821,7 +1818,7 @@ HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) /* call the helper object to do the actual unmarshaling */ hr = IMarshal_UnmarshalInterface(pMarshal, pStream, &iid, (LPVOID*)&object); - if (hr) + if (hr != S_OK) ERR("IMarshal::UnmarshalInterface failed, 0x%08x\n", hr); if (hr == S_OK) @@ -1831,7 +1828,7 @@ HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) { TRACE("requested interface != marshalled interface, additional QI needed\n"); hr = IUnknown_QueryInterface(object, riid, ppv); - if (hr) + if (hr != S_OK) ERR("Couldn't query for interface %s, hr = 0x%08x\n", debugstr_guid(riid), hr); IUnknown_Release(object); @@ -1885,7 +1882,7 @@ HRESULT WINAPI CoReleaseMarshalData(IStream *pStream) /* call the helper object to do the releasing of marshal data */ hr = IMarshal_ReleaseMarshalData(pMarshal, pStream); - if (hr) + if (hr != S_OK) ERR("IMarshal::ReleaseMarshalData failed with error 0x%08x\n", hr); IMarshal_Release(pMarshal); diff --git a/reactos/dll/win32/ole32/moniker.c b/reactos/dll/win32/ole32/moniker.c index 2fb03ae6650..b0030dee59b 100644 --- a/reactos/dll/win32/ole32/moniker.c +++ b/reactos/dll/win32/ole32/moniker.c @@ -1109,7 +1109,13 @@ HRESULT WINAPI MkParseDisplayName(LPBC pbc, LPCOLESTR szDisplayName, TRACE("(%p, %s, %p, %p)\n", pbc, debugstr_w(szDisplayName), pchEaten, ppmk); - if (!(IsValidInterface((LPUNKNOWN) pbc))) + if (!pbc || !IsValidInterface((LPUNKNOWN) pbc)) + return E_INVALIDARG; + + if (!szDisplayName || !*szDisplayName) + return E_INVALIDARG; + + if (!pchEaten || !ppmk) return E_INVALIDARG; *pchEaten = 0; diff --git a/reactos/dll/win32/ole32/ole2.c b/reactos/dll/win32/ole32/ole2.c index 6bf79fdb507..41989f3331b 100644 --- a/reactos/dll/win32/ole32/ole2.c +++ b/reactos/dll/win32/ole32/ole2.c @@ -57,13 +57,6 @@ WINE_DECLARE_DEBUG_CHANNEL(accel); * These are static/global variables and internal data structures that the * OLE module uses to maintain it's state. */ -typedef struct tagDropTargetNode -{ - HWND hwndTarget; - IDropTarget* dropTarget; - struct list entry; -} DropTargetNode; - typedef struct tagTrackerWindowInfo { IDataObject* dataObject; @@ -110,12 +103,25 @@ static LONG OLE_moduleLockCount = 0; /* * Name of our registered window class. */ -static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32"; +static const WCHAR OLEDD_DRAGTRACKERCLASS[] = + {'W','i','n','e','D','r','a','g','D','r','o','p','T','r','a','c','k','e','r','3','2',0}; /* - * This is the head of the Drop target container. + * Name of menu descriptor property. */ -static struct list targetListHead = LIST_INIT(targetListHead); +static const WCHAR prop_olemenuW[] = + {'P','R','O','P','_','O','L','E','M','e','n','u','D','e','s','c','r','i','p','t','o','r',0}; + +/* property to store IDropTarget pointer */ +static const WCHAR prop_oledroptarget[] = + {'O','l','e','D','r','o','p','T','a','r','g','e','t','I','n','t','e','r','f','a','c','e',0}; + +static const WCHAR clsidfmtW[] = + {'C','L','S','I','D','\\','{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-', + '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x', + '%','0','2','x','%','0','2','x','}','\\',0}; + +static const WCHAR emptyW[] = { 0 }; /****************************************************************************** * These are the prototypes of miscellaneous utility methods @@ -144,22 +150,12 @@ extern void OLEClipbrd_Initialize(void); /****************************************************************************** * These are the prototypes of the utility methods used for OLE Drag n Drop */ -static void OLEDD_Initialize(void); -static DropTargetNode* OLEDD_FindDropTarget( - HWND hwndOfTarget); -static void OLEDD_FreeDropTarget(DropTargetNode*, BOOL); -static LRESULT WINAPI OLEDD_DragTrackerWindowProc( - HWND hwnd, - UINT uMsg, - WPARAM wParam, - LPARAM lParam); -static void OLEDD_TrackMouseMove( - TrackerWindowInfo* trackerInfo); -static void OLEDD_TrackStateChange( - TrackerWindowInfo* trackerInfo); +static void OLEDD_Initialize(void); +static LRESULT WINAPI OLEDD_DragTrackerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo); +static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo); static DWORD OLEDD_GetButtonState(void); - /****************************************************************************** * OleBuildVersion [OLE32.@] */ @@ -270,14 +266,22 @@ HRESULT WINAPI OleInitializeWOW(DWORD x, DWORD y) { return 0; } +/*** + * OLEDD_FindDropTarget() + * + * Returns IDropTarget pointer registered for this window. + */ +static inline IDropTarget* OLEDD_FindDropTarget(HWND hwnd) +{ + return GetPropW(hwnd, prop_oledroptarget); +} + /*********************************************************************** * RegisterDragDrop (OLE32.@) */ -HRESULT WINAPI RegisterDragDrop( - HWND hwnd, - LPDROPTARGET pDropTarget) +HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget) { - DropTargetNode* dropTargetInfo; + DWORD pid = 0; TRACE("(%p,%p)\n", hwnd, pDropTarget); @@ -296,32 +300,20 @@ HRESULT WINAPI RegisterDragDrop( return DRAGDROP_E_INVALIDHWND; } - /* - * First, check if the window is already registered. - */ - dropTargetInfo = OLEDD_FindDropTarget(hwnd); + /* block register for other processes windows */ + GetWindowThreadProcessId(hwnd, &pid); + if (pid != GetCurrentProcessId()) + { + FIXME("register for another process windows is disabled\n"); + return DRAGDROP_E_INVALIDHWND; + } - if (dropTargetInfo!=NULL) + /* check if the window is already registered */ + if (OLEDD_FindDropTarget(hwnd)) return DRAGDROP_E_ALREADYREGISTERED; - /* - * If it's not there, we can add it. We first create a node for it. - */ - dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode)); - - if (dropTargetInfo==NULL) - return E_OUTOFMEMORY; - - dropTargetInfo->hwndTarget = hwnd; - - /* - * Don't forget that this is an interface pointer, need to nail it down since - * we keep a copy of it. - */ IDropTarget_AddRef(pDropTarget); - dropTargetInfo->dropTarget = pDropTarget; - - list_add_tail(&targetListHead, &dropTargetInfo->entry); + SetPropW(hwnd, prop_oledroptarget, pDropTarget); return S_OK; } @@ -329,10 +321,9 @@ HRESULT WINAPI RegisterDragDrop( /*********************************************************************** * RevokeDragDrop (OLE32.@) */ -HRESULT WINAPI RevokeDragDrop( - HWND hwnd) +HRESULT WINAPI RevokeDragDrop(HWND hwnd) { - DropTargetNode* dropTargetInfo; + IDropTarget* droptarget; TRACE("(%p)\n", hwnd); @@ -342,18 +333,12 @@ HRESULT WINAPI RevokeDragDrop( return DRAGDROP_E_INVALIDHWND; } - /* - * First, check if the window is already registered. - */ - dropTargetInfo = OLEDD_FindDropTarget(hwnd); - - /* - * If it ain't in there, it's an error. - */ - if (dropTargetInfo==NULL) + /* no registration data */ + if (!(droptarget = OLEDD_FindDropTarget(hwnd))) return DRAGDROP_E_NOTREGISTERED; - OLEDD_FreeDropTarget(dropTargetInfo, TRUE); + IDropTarget_Release(droptarget); + RemovePropW(hwnd, prop_oledroptarget); return S_OK; } @@ -371,13 +356,12 @@ HRESULT WINAPI OleRegGetUserType( DWORD dwFormOfType, LPOLESTR* pszUserType) { - char keyName[60]; + WCHAR keyName[60]; DWORD dwKeyType; DWORD cbData; HKEY clsidKey; LONG hres; - LPSTR buffer; - HRESULT retVal; + /* * Initialize the out parameter. */ @@ -386,17 +370,17 @@ HRESULT WINAPI OleRegGetUserType( /* * Build the key name we're looking for */ - sprintf( keyName, "CLSID\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\", - clsid->Data1, clsid->Data2, clsid->Data3, - clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3], - clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] ); + sprintfW( keyName, clsidfmtW, + clsid->Data1, clsid->Data2, clsid->Data3, + clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3], + clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] ); - TRACE("(%s, %d, %p)\n", keyName, dwFormOfType, pszUserType); + TRACE("(%s, %d, %p)\n", debugstr_w(keyName), dwFormOfType, pszUserType); /* * Open the class id Key */ - hres = RegOpenKeyA(HKEY_CLASSES_ROOT, + hres = RegOpenKeyW(HKEY_CLASSES_ROOT, keyName, &clsidKey); @@ -408,8 +392,8 @@ HRESULT WINAPI OleRegGetUserType( */ cbData = 0; - hres = RegQueryValueExA(clsidKey, - "", + hres = RegQueryValueExW(clsidKey, + emptyW, NULL, &dwKeyType, NULL, @@ -424,7 +408,7 @@ HRESULT WINAPI OleRegGetUserType( /* * Allocate a buffer for the registry value. */ - *pszUserType = CoTaskMemAlloc(cbData*2); + *pszUserType = CoTaskMemAlloc(cbData); if (*pszUserType==NULL) { @@ -432,41 +416,24 @@ HRESULT WINAPI OleRegGetUserType( return E_OUTOFMEMORY; } - buffer = HeapAlloc(GetProcessHeap(), 0, cbData); - - if (buffer == NULL) - { - RegCloseKey(clsidKey); - CoTaskMemFree(*pszUserType); - *pszUserType=NULL; - return E_OUTOFMEMORY; - } - - hres = RegQueryValueExA(clsidKey, - "", + hres = RegQueryValueExW(clsidKey, + emptyW, NULL, &dwKeyType, - (LPBYTE) buffer, + (LPBYTE) *pszUserType, &cbData); RegCloseKey(clsidKey); - - if (hres!=ERROR_SUCCESS) + if (hres != ERROR_SUCCESS) { CoTaskMemFree(*pszUserType); - *pszUserType=NULL; + *pszUserType = NULL; - retVal = REGDB_E_READREGDB; + return REGDB_E_READREGDB; } - else - { - MultiByteToWideChar( CP_ACP, 0, buffer, -1, *pszUserType, cbData /*FIXME*/ ); - retVal = S_OK; - } - HeapFree(GetProcessHeap(), 0, buffer); - return retVal; + return S_OK; } /*********************************************************************** @@ -478,17 +445,19 @@ HRESULT WINAPI DoDragDrop ( DWORD dwOKEffect, /* [in] effects allowed by the source */ DWORD *pdwEffect) /* [out] ptr to effects of the source */ { + static const WCHAR trackerW[] = {'T','r','a','c','k','e','r','W','i','n','d','o','w',0}; TrackerWindowInfo trackerInfo; HWND hwndTrackWindow; MSG msg; - TRACE("(DataObject %p, DropSource %p)\n", pDataObject, pDropSource); + TRACE("(%p, %p, %d, %p)\n", pDataObject, pDropSource, dwOKEffect, pdwEffect); + + if (!pDataObject || !pDropSource || !pdwEffect) + return E_INVALIDARG; /* * Setup the drag n drop tracking window. */ - if (!IsValidInterface((LPUNKNOWN)pDropSource)) - return E_INVALIDARG; trackerInfo.dataObject = pDataObject; trackerInfo.dropSource = pDropSource; @@ -500,12 +469,12 @@ HRESULT WINAPI DoDragDrop ( trackerInfo.curTargetHWND = 0; trackerInfo.curDragTarget = 0; - hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS, "TrackerWindow", + hwndTrackWindow = CreateWindowW(OLEDD_DRAGTRACKERCLASS, trackerW, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, &trackerInfo); - if (hwndTrackWindow!=0) + if (hwndTrackWindow) { /* * Capture the mouse input @@ -517,7 +486,7 @@ HRESULT WINAPI DoDragDrop ( /* * Pump messages. All mouse input should go to the capture window. */ - while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) ) + while (!trackerInfo.trackingDone && GetMessageW(&msg, 0, 0, 0) ) { trackerInfo.curMousePos.x = msg.pt.x; trackerInfo.curMousePos.y = msg.pt.y; @@ -548,7 +517,7 @@ HRESULT WINAPI DoDragDrop ( /* * Dispatch the messages only when it's not a keyboard message. */ - DispatchMessageA(&msg); + DispatchMessageW(&msg); } } @@ -584,7 +553,9 @@ HRESULT WINAPI OleRegGetMiscStatus( DWORD dwAspect, DWORD* pdwStatus) { - char keyName[60]; + static const WCHAR miscstatusW[] = {'M','i','s','c','S','t','a','t','u','s',0}; + static const WCHAR dfmtW[] = {'%','d',0}; + WCHAR keyName[60]; HKEY clsidKey; HKEY miscStatusKey; HKEY aspectKey; @@ -598,17 +569,17 @@ HRESULT WINAPI OleRegGetMiscStatus( /* * Build the key name we're looking for */ - sprintf( keyName, "CLSID\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\", - clsid->Data1, clsid->Data2, clsid->Data3, - clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3], - clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] ); + sprintfW( keyName, clsidfmtW, + clsid->Data1, clsid->Data2, clsid->Data3, + clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3], + clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] ); - TRACE("(%s, %d, %p)\n", keyName, dwAspect, pdwStatus); + TRACE("(%s, %d, %p)\n", debugstr_w(keyName), dwAspect, pdwStatus); /* * Open the class id Key */ - result = RegOpenKeyA(HKEY_CLASSES_ROOT, + result = RegOpenKeyW(HKEY_CLASSES_ROOT, keyName, &clsidKey); @@ -618,8 +589,8 @@ HRESULT WINAPI OleRegGetMiscStatus( /* * Get the MiscStatus */ - result = RegOpenKeyA(clsidKey, - "MiscStatus", + result = RegOpenKeyW(clsidKey, + miscstatusW, &miscStatusKey); @@ -637,9 +608,9 @@ HRESULT WINAPI OleRegGetMiscStatus( /* * Open the key specific to the requested aspect. */ - sprintf(keyName, "%d", dwAspect); + sprintfW(keyName, dfmtW, dwAspect); - result = RegOpenKeyA(miscStatusKey, + result = RegOpenKeyW(miscStatusKey, keyName, &aspectKey); @@ -1180,7 +1151,7 @@ static void OLEMenu_UnInitialize(void) */ static BOOL OLEMenu_InstallHooks( DWORD tid ) { - OleMenuHookItem *pHookItem = NULL; + OleMenuHookItem *pHookItem; /* Create an entry for the hook table */ if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0, @@ -1189,15 +1160,16 @@ static BOOL OLEMenu_InstallHooks( DWORD tid ) pHookItem->tid = tid; pHookItem->hHeap = GetProcessHeap(); + pHookItem->CallWndProc_hHook = NULL; /* Install a thread scope message hook for WH_GETMESSAGE */ - pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc, + pHookItem->GetMsg_hHook = SetWindowsHookExW( WH_GETMESSAGE, OLEMenu_GetMsgProc, 0, GetCurrentThreadId() ); if ( !pHookItem->GetMsg_hHook ) goto CLEANUP; /* Install a thread scope message hook for WH_CALLWNDPROC */ - pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc, + pHookItem->CallWndProc_hHook = SetWindowsHookExW( WH_CALLWNDPROC, OLEMenu_CallWndProc, 0, GetCurrentThreadId() ); if ( !pHookItem->CallWndProc_hHook ) goto CLEANUP; @@ -1271,7 +1243,7 @@ CLEANUP: */ static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid ) { - OleMenuHookItem *pHookItem = NULL; + OleMenuHookItem *pHookItem; /* Do a simple linear search for an entry whose tid matches ours. * We really need a map but efficiency is not a concern here. */ @@ -1376,7 +1348,7 @@ static BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDes */ static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam) { - LPCWPSTRUCT pMsg = NULL; + LPCWPSTRUCT pMsg; HOLEMENU hOleMenu = 0; OleMenuDescriptor *pOleMenuDescriptor = NULL; OleMenuHookItem *pHookItem = NULL; @@ -1395,7 +1367,7 @@ static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lPar * If the window has an OLEMenu property we may need to dispatch * the menu message to its active objects window instead. */ - hOleMenu = GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); + hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW ); if ( !hOleMenu ) goto NEXTHOOK; @@ -1413,7 +1385,7 @@ static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lPar pOleMenuDescriptor->bIsServerItem = FALSE; /* Send this message to the server as well */ - SendMessageA( pOleMenuDescriptor->hwndActiveObject, + SendMessageW( pOleMenuDescriptor->hwndActiveObject, pMsg->message, pMsg->wParam, pMsg->lParam ); goto NEXTHOOK; } @@ -1454,7 +1426,7 @@ static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lPar /* If the message was for the server dispatch it accordingly */ if ( pOleMenuDescriptor->bIsServerItem ) { - SendMessageA( pOleMenuDescriptor->hwndActiveObject, + SendMessageW( pOleMenuDescriptor->hwndActiveObject, pMsg->message, pMsg->wParam, pMsg->lParam ); } @@ -1481,7 +1453,7 @@ NEXTHOOK: */ static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam) { - LPMSG pMsg = NULL; + LPMSG pMsg; HOLEMENU hOleMenu = 0; OleMenuDescriptor *pOleMenuDescriptor = NULL; OleMenuHookItem *pHookItem = NULL; @@ -1500,7 +1472,7 @@ static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lPara * If the window has an OLEMenu property we may need to dispatch * the menu message to its active objects window instead. */ - hOleMenu = GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); + hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW ); if ( !hOleMenu ) goto NEXTHOOK; @@ -1671,7 +1643,7 @@ HRESULT WINAPI OleSetMenuDescriptor( pOleMenuDescriptor = NULL; /* Add a menu descriptor windows property to the frame window */ - SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu ); + SetPropW( hwndFrame, prop_olemenuW, hOleMenu ); /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */ if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) ) @@ -1684,7 +1656,7 @@ HRESULT WINAPI OleSetMenuDescriptor( return E_FAIL; /* Remove the menu descriptor property from the frame window */ - RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" ); + RemovePropW( hwndFrame, prop_olemenuW ); } return S_OK; @@ -1870,9 +1842,9 @@ void WINAPI ReleaseStgMedium( */ static void OLEDD_Initialize(void) { - WNDCLASSA wndClass; + WNDCLASSW wndClass; - ZeroMemory (&wndClass, sizeof(WNDCLASSA)); + ZeroMemory (&wndClass, sizeof(WNDCLASSW)); wndClass.style = CS_GLOBALCLASS; wndClass.lpfnWndProc = OLEDD_DragTrackerWindowProc; wndClass.cbClsExtra = 0; @@ -1881,58 +1853,7 @@ static void OLEDD_Initialize(void) wndClass.hbrBackground = 0; wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS; - RegisterClassA (&wndClass); -} - -/*** - * OLEDD_FreeDropTarget() - * - * Frees the drag and drop data structure - */ -static void OLEDD_FreeDropTarget(DropTargetNode *dropTargetInfo, BOOL release_drop_target) -{ - list_remove(&dropTargetInfo->entry); - if (release_drop_target) IDropTarget_Release(dropTargetInfo->dropTarget); - HeapFree(GetProcessHeap(), 0, dropTargetInfo); -} - -/*** - * OLEDD_UnInitialize() - * - * Releases the OLE drag and drop data structures. - */ -void OLEDD_UnInitialize(void) -{ - /* - * Simply empty the list. - */ - while (!list_empty(&targetListHead)) - { - DropTargetNode* curNode = LIST_ENTRY(list_head(&targetListHead), DropTargetNode, entry); - OLEDD_FreeDropTarget(curNode, FALSE); - } -} - -/*** - * OLEDD_FindDropTarget() - * - * Finds information about the drop target. - */ -static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget) -{ - DropTargetNode* curNode; - - /* - * Iterate the list to find the HWND value. - */ - LIST_FOR_EACH_ENTRY(curNode, &targetListHead, DropTargetNode, entry) - if (hwndOfTarget==curNode->hwndTarget) - return curNode; - - /* - * If we get here, the item is not in the list - */ - return NULL; + RegisterClassW (&wndClass); } /*** @@ -1958,7 +1879,7 @@ static LRESULT WINAPI OLEDD_DragTrackerWindowProc( { LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam; - SetWindowLongPtrA(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams); + SetWindowLongPtrW(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams); SetTimer(hwnd, DRAG_TIMER_ID, 50, NULL); break; @@ -1989,7 +1910,7 @@ static LRESULT WINAPI OLEDD_DragTrackerWindowProc( /* * This is a window proc after all. Let's call the default. */ - return DefWindowProcA (hwnd, uMsg, wParam, lParam); + return DefWindowProcW (hwnd, uMsg, wParam, lParam); } /*** @@ -2038,46 +1959,65 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo) } else { - DropTargetNode* newDropTargetNode = 0; - /* * If we changed window, we have to notify our old target and check for * the new one. */ - if (trackerInfo->curDragTarget!=0) - { + if (trackerInfo->curDragTarget) IDropTarget_DragLeave(trackerInfo->curDragTarget); - } /* * Make sure we're hovering over a window. */ - if (hwndNewTarget!=0) + if (hwndNewTarget) { /* * Find-out if there is a drag target under the mouse */ - HWND nexttar = hwndNewTarget; + HWND next_target_wnd = hwndNewTarget; + IDropTarget *new_target; + DWORD pid; + trackerInfo->curTargetHWND = hwndNewTarget; do { - newDropTargetNode = OLEDD_FindDropTarget(nexttar); - } while (!newDropTargetNode && (nexttar = GetParent(nexttar)) != 0); - if(nexttar) hwndNewTarget = nexttar; + new_target = OLEDD_FindDropTarget(next_target_wnd); + } while (!new_target && (next_target_wnd = GetParent(next_target_wnd))); - trackerInfo->curDragTargetHWND = hwndNewTarget; - trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0; + if (next_target_wnd) hwndNewTarget = next_target_wnd; + + GetWindowThreadProcessId(hwndNewTarget, &pid); + if (pid != GetCurrentProcessId()) + { + FIXME("drop to another process window is unsupported\n"); + trackerInfo->curDragTargetHWND = 0; + trackerInfo->curTargetHWND = 0; + trackerInfo->curDragTarget = 0; + } + else + { + trackerInfo->curDragTargetHWND = hwndNewTarget; + trackerInfo->curDragTarget = new_target; + } /* * If there is, notify it that we just dragged-in */ - if (trackerInfo->curDragTarget!=0) + if (trackerInfo->curDragTarget) { - IDropTarget_DragEnter(trackerInfo->curDragTarget, - trackerInfo->dataObject, - trackerInfo->dwKeyState, - trackerInfo->curMousePos, - trackerInfo->pdwEffect); + hr = IDropTarget_DragEnter(trackerInfo->curDragTarget, + trackerInfo->dataObject, + trackerInfo->dwKeyState, + trackerInfo->curMousePos, + trackerInfo->pdwEffect); + + /* failed DragEnter() means invalid target */ + if (hr != S_OK) + { + trackerInfo->curDragTargetHWND = 0; + trackerInfo->curTargetHWND = 0; + trackerInfo->curDragTarget = 0; + } } } else @@ -2110,24 +2050,28 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo) * when that's the case, we must display the standard drag and drop * cursors. */ - if (hr==DRAGDROP_S_USEDEFAULTCURSORS) + if (hr == DRAGDROP_S_USEDEFAULTCURSORS) { + HCURSOR hCur; + if (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) { - SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(1))); + hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(1)); } else if (*trackerInfo->pdwEffect & DROPEFFECT_COPY) { - SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(2))); + hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(2)); } else if (*trackerInfo->pdwEffect & DROPEFFECT_LINK) { - SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(3))); + hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(3)); } else { - SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(0))); + hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(0)); } + + SetCursor(hCur); } } @@ -2174,7 +2118,7 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) * If we end-up over a target, drop the object in the target or * inform the target that the operation was cancelled. */ - if (trackerInfo->curDragTarget!=0) + if (trackerInfo->curDragTarget) { switch (trackerInfo->returnValue) { @@ -2183,14 +2127,16 @@ static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo) * the drop target that we just dropped the object in it. */ case DRAGDROP_S_DROP: - { - IDropTarget_Drop(trackerInfo->curDragTarget, - trackerInfo->dataObject, - trackerInfo->dwKeyState, - trackerInfo->curMousePos, - trackerInfo->pdwEffect); - break; - } + if (*trackerInfo->pdwEffect != DROPEFFECT_NONE) + IDropTarget_Drop(trackerInfo->curDragTarget, + trackerInfo->dataObject, + trackerInfo->dwKeyState, + trackerInfo->curMousePos, + trackerInfo->pdwEffect); + else + IDropTarget_DragLeave(trackerInfo->curDragTarget); + break; + /* * If the source told us that we should cancel, fool the drop * target by telling it that the mouse left it's window. @@ -2256,13 +2202,13 @@ static void OLEUTL_ReadRegistryDWORDValue( HKEY regKey, DWORD* pdwValue) { - char buffer[20]; + WCHAR buffer[20]; + DWORD cbData = sizeof(buffer); DWORD dwKeyType; - DWORD cbData = 20; LONG lres; - lres = RegQueryValueExA(regKey, - "", + lres = RegQueryValueExW(regKey, + emptyW, NULL, &dwKeyType, (LPBYTE)buffer, @@ -2278,7 +2224,7 @@ static void OLEUTL_ReadRegistryDWORDValue( case REG_EXPAND_SZ: case REG_MULTI_SZ: case REG_SZ: - *pdwValue = (DWORD)strtoul(buffer, NULL, 10); + *pdwValue = (DWORD)strtoulW(buffer, NULL, 10); break; } } diff --git a/reactos/dll/win32/ole32/stg_prop.c b/reactos/dll/win32/ole32/stg_prop.c index b3362bc21de..f887d10c0cf 100644 --- a/reactos/dll/win32/ole32/stg_prop.c +++ b/reactos/dll/win32/ole32/stg_prop.c @@ -1314,6 +1314,13 @@ static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This) hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset); if (FAILED(hr)) goto end; + if (!IsEqualGUID(&fmtOffset.fmtid, &FMTID_DocSummaryInformation) && + !IsEqualGUID(&fmtOffset.fmtid, &FMTID_SummaryInformation)) + { + WARN("not reading unknown fmtid %s\n", debugstr_guid(&fmtOffset.fmtid)); + hr = S_FALSE; + goto end; + } if (fmtOffset.dwOffset > stat.cbSize.u.LowPart) { WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset, diff --git a/reactos/dll/win32/ole32/storage32.c b/reactos/dll/win32/ole32/storage32.c index 434d8b24606..98df144976c 100644 --- a/reactos/dll/win32/ole32/storage32.c +++ b/reactos/dll/win32/ole32/storage32.c @@ -118,17 +118,61 @@ static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This, static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry); static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry); +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. */ + int inuse; + + /* True if data is up to date. */ + int read; + + /* True if this entry has been modified. */ + int dirty; + + /* True if this entry's stream has been modified. */ + int stream_dirty; + + /* True if this entry has been deleted in the transacted storage, but the + * delete has not yet been committed. */ + int 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 that reads/writes a snapshot file. + * Transacted storage object. */ typedef struct TransactedSnapshotImpl { struct StorageBaseImpl base; /* - * Changes are temporarily saved to the snapshot. + * Modified streams are temporarily saved to the scratch file. */ - StorageBaseImpl *snapshot; + 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. @@ -1296,46 +1340,6 @@ static HRESULT StorageImpl_DestroyDirEntry( } -/*************************************************************************** - * - * Internal Method - * - * Destroy an entry, its attached data, and all entries reachable from it. - */ -static HRESULT DestroyReachableEntries( - StorageBaseImpl *base, - DirRef index) -{ - HRESULT hr = S_OK; - DirEntry data; - ULARGE_INTEGER zero; - - zero.QuadPart = 0; - - if (index != DIRENTRY_NULL) - { - hr = StorageBaseImpl_ReadDirEntry(base, index, &data); - - if (SUCCEEDED(hr)) - hr = DestroyReachableEntries(base, data.dirRootEntry); - - if (SUCCEEDED(hr)) - hr = DestroyReachableEntries(base, data.leftChild); - - if (SUCCEEDED(hr)) - hr = DestroyReachableEntries(base, data.rightChild); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_StreamSetSize(base, index, zero); - - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_DestroyDirEntry(base, index); - } - - return hr; -} - - /**************************************************************************** * * Internal Method @@ -2347,6 +2351,21 @@ static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *Thi return &This->blockChainCache[free_index]; } +static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index) +{ + int i; + + for (i=0; iblockChainCache[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) { @@ -2538,6 +2557,30 @@ static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, } } +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; +} + /* * Virtual function table for the IStorage32Impl class. */ @@ -2573,7 +2616,8 @@ static const StorageBaseImplVtbl StorageImpl_BaseVtbl = StorageImpl_DestroyDirEntry, StorageImpl_StreamReadAt, StorageImpl_StreamWriteAt, - StorageImpl_StreamSetSize + StorageImpl_StreamSetSize, + StorageImpl_StreamLink }; static HRESULT StorageImpl_Construct( @@ -3540,6 +3584,9 @@ HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer buffer, &bytesRead); + if (bytesRead != RAW_DIRENTRY_SIZE) + return STG_E_READFAULT; + return hr; } @@ -3885,6 +3932,11 @@ BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( offset.u.LowPart += cbRead; } + else + { + resRead = STG_E_READFAULT; + break; + } } while (cbTotalRead.QuadPart < size.QuadPart); HeapFree(GetProcessHeap(),0,buffer); @@ -3983,6 +4035,11 @@ SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( offset.u.LowPart += cbRead; } + else + { + resRead = STG_E_READFAULT; + break; + } }while(cbTotalRead.QuadPart < size.QuadPart); HeapFree(GetProcessHeap(), 0, buffer); @@ -4011,40 +4068,384 @@ SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); } -static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot) +static HRESULT StorageBaseImpl_CopyStream( + StorageBaseImpl *dst, DirRef dst_entry, + StorageBaseImpl *src, DirRef src_entry) { HRESULT hr; - DirEntry parentData, snapshotData; + BYTE data[4096]; + DirEntry srcdata; + ULARGE_INTEGER bytes_copied; + ULONG bytestocopy, bytesread, byteswritten; - hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE, - 0, (IStorage**)snapshot); + hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata); if (SUCCEEDED(hr)) { - hr = StorageBaseImpl_ReadDirEntry(original, - original->storageDirEntry, &parentData); + hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size); - if (SUCCEEDED(hr)) - hr = StorageBaseImpl_ReadDirEntry((*snapshot), - (*snapshot)->storageDirEntry, &snapshotData); - - if (SUCCEEDED(hr)) + bytes_copied.QuadPart = 0; + while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr)) { - memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name)); - snapshotData.sizeOfNameString = parentData.sizeOfNameString; - snapshotData.stgType = parentData.stgType; - snapshotData.clsid = parentData.clsid; - snapshotData.ctime = parentData.ctime; - snapshotData.mtime = parentData.mtime; - hr = StorageBaseImpl_WriteDirEntry((*snapshot), - (*snapshot)->storageDirEntry, &snapshotData); + 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 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 = 1; + + 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 = 0; + } + + 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)) - hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL, - (IStorage*)(*snapshot)); + { + memcpy(&This->entries[entry].data, &data, sizeof(DirEntry)); + This->entries[entry].read = 1; + } + } - if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot)); + 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 = 1; + + 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 = 1; + + 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; @@ -4055,10 +4456,13 @@ static HRESULT WINAPI TransactedSnapshotImpl_Commit( DWORD grfCommitFlags) /* [in] */ { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; + TransactedDirEntry *root_entry; + DirRef i, dir_root_ref; + DirEntry data; + ULARGE_INTEGER zero; HRESULT hr; - DirEntry data, tempStorageData, snapshotRootData; - DirRef tempStorageEntry, oldDirRoot; - StorageInternalImpl *tempStorage; + + zero.QuadPart = 0; TRACE("(%p,%x)\n", iface, grfCommitFlags); @@ -4072,75 +4476,71 @@ static HRESULT WINAPI TransactedSnapshotImpl_Commit( * needed in the rare situation where we have just enough free disk space to * overwrite the existing data. */ - /* Create an orphaned storage in the parent for the new directory structure. */ - memset(&data, 0, sizeof(data)); - data.name[0] = 'D'; - data.sizeOfNameString = 1; - data.stgType = STGTY_STORAGE; - data.leftChild = DIRENTRY_NULL; - data.rightChild = DIRENTRY_NULL; - data.dirRootEntry = DIRENTRY_NULL; - hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry); + root_entry = &This->entries[This->base.storageDirEntry]; + if (!root_entry->read) + return S_OK; + + hr = TransactedSnapshotImpl_CopyTree(This); if (FAILED(hr)) return hr; - tempStorage = StorageInternalImpl_Construct(This->transactedParent, - STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry); - if (tempStorage) - { - hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL, - (IStorage*)tempStorage); - - list_init(&tempStorage->ParentListEntry); - - IStorage_Release((IStorage*) tempStorage); - } + if (root_entry->data.dirRootEntry == DIRENTRY_NULL) + dir_root_ref = DIRENTRY_NULL; else - hr = E_OUTOFMEMORY; - - if (FAILED(hr)) - { - DestroyReachableEntries(This->transactedParent, tempStorageEntry); - return hr; - } + dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry; /* Update the storage to use the new data in one step. */ hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, - This->transactedParent->storageDirEntry, &data); + root_entry->transactedParentEntry, &data); if (SUCCEEDED(hr)) { - hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, - tempStorageEntry, &tempStorageData); - } - - if (SUCCEEDED(hr)) - { - hr = StorageBaseImpl_ReadDirEntry(This->snapshot, - This->snapshot->storageDirEntry, &snapshotRootData); - } - - if (SUCCEEDED(hr)) - { - oldDirRoot = data.dirRootEntry; - data.dirRootEntry = tempStorageData.dirRootEntry; - data.clsid = snapshotRootData.clsid; - data.ctime = snapshotRootData.ctime; - data.mtime = snapshotRootData.mtime; + 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, - This->transactedParent->storageDirEntry, &data); + root_entry->transactedParentEntry, &data); } if (SUCCEEDED(hr)) { /* Destroy the old now-orphaned data. */ - DestroyReachableEntries(This->transactedParent, oldDirRoot); - StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry); + for (i=0; ientries_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 = 0; + } + entry->dirty = 0; + entry->transactedParentEntry = entry->newTransactedParentEntry; + } + } + } } else { - DestroyReachableEntries(This->transactedParent, tempStorageEntry); + TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL); } return hr; @@ -4150,21 +4550,31 @@ static HRESULT WINAPI TransactedSnapshotImpl_Revert( IStorage* iface) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; - StorageBaseImpl *newSnapshot; - HRESULT hr; + ULARGE_INTEGER zero; + ULONG i; TRACE("(%p)\n", iface); - /* Create a new copy of the parent data. */ - hr = CreateSnapshotFile(This->transactedParent, &newSnapshot); - if (FAILED(hr)) return hr; - /* Destroy the open objects. */ StorageBaseImpl_DeleteAll(&This->base); - /* Replace our current snapshot. */ - IStorage_Release((IStorage*)This->snapshot); - This->snapshot = newSnapshot; + /* Clear out the scratch file. */ + zero.QuadPart = 0; + for (i=0; ientries_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; } @@ -4185,11 +4595,13 @@ static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; - TransactedSnapshotImpl_Invalidate(iface); + TransactedSnapshotImpl_Revert((IStorage*)iface); IStorage_Release((IStorage*)This->transactedParent); - IStorage_Release((IStorage*)This->snapshot); + IStorage_Release((IStorage*)This->scratch); + + HeapFree(GetProcessHeap(), 0, This->entries); HeapFree(GetProcessHeap(), 0, This); } @@ -4198,27 +4610,76 @@ static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base, const DirEntry *newData, DirRef *index) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + DirRef new_ref; + TransactedDirEntry *new_entry; - return StorageBaseImpl_CreateDirEntry(This->snapshot, - newData, index); + 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 = 1; + new_entry->dirty = 1; + memcpy(&new_entry->data, newData, sizeof(DirEntry)); + + *index = new_ref; + + TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index); + + return S_OK; } static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base, DirRef index, const DirEntry *data) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; - return StorageBaseImpl_WriteDirEntry(This->snapshot, - index, data); + TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) return hr; + + memcpy(&This->entries[index].data, data, sizeof(DirEntry)); + + if (index != This->base.storageDirEntry) + { + This->entries[index].dirty = 1; + + 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 = 1; + + This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; + } + } + + return S_OK; } static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, DirRef index, DirEntry *data) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; - return StorageBaseImpl_ReadDirEntry(This->snapshot, - index, data); + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) return hr; + + memcpy(data, &This->entries[index].data, sizeof(DirEntry)); + + TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); + + return S_OK; } static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, @@ -4226,8 +4687,21 @@ static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - return StorageBaseImpl_DestroyDirEntry(This->snapshot, - index); + if (This->entries[index].transactedParentEntry == DIRENTRY_NULL || + This->entries[index].data.size.QuadPart != 0) + { + /* If we deleted this entry while it has stream data. We must have left the + * data because some other entry is using it, and we need to leave the + * original entry alone. */ + memset(&This->entries[index], 0, sizeof(TransactedDirEntry)); + This->firstFreeEntry = min(index, This->firstFreeEntry); + } + else + { + This->entries[index].deleted = 1; + } + + return S_OK; } static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base, @@ -4235,26 +4709,122 @@ static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base, { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; - return StorageBaseImpl_StreamReadAt(This->snapshot, - index, offset, size, buffer, bytesRead); + if (This->entries[index].stream_dirty) + { + return StorageBaseImpl_StreamReadAt(This->scratch, + This->entries[index].stream_entry, offset, size, buffer, bytesRead); + } + else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL) + { + /* This stream doesn't live in the parent, and we haven't allocated storage + * for it yet */ + *bytesRead = 0; + return S_OK; + } + else + { + return StorageBaseImpl_StreamReadAt(This->transactedParent, + This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead); + } } static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; - return StorageBaseImpl_StreamWriteAt(This->snapshot, - index, offset, size, buffer, bytesWritten); + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) return hr; + + hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); + if (FAILED(hr)) return hr; + + hr = StorageBaseImpl_StreamWriteAt(This->scratch, + This->entries[index].stream_entry, offset, size, buffer, bytesWritten); + + if (SUCCEEDED(hr) && size != 0) + This->entries[index].data.size.QuadPart = max( + This->entries[index].data.size.QuadPart, + offset.QuadPart + size); + + return hr; } static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, DirRef index, ULARGE_INTEGER newsize) { TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; + HRESULT hr; - return StorageBaseImpl_StreamSetSize(This->snapshot, - index, newsize); + hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); + if (FAILED(hr)) return hr; + + if (This->entries[index].data.size.QuadPart == newsize.QuadPart) + return S_OK; + + if (newsize.QuadPart == 0) + { + /* Destroy any parent references or entries in the scratch file. */ + if (This->entries[index].stream_dirty) + { + ULARGE_INTEGER zero; + zero.QuadPart = 0; + StorageBaseImpl_StreamSetSize(This->scratch, + This->entries[index].stream_entry, zero); + StorageBaseImpl_DestroyDirEntry(This->scratch, + This->entries[index].stream_entry); + This->entries[index].stream_dirty = 0; + } + 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 = 1; + + This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; + } + } + else + { + hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); + if (FAILED(hr)) return hr; + + hr = StorageBaseImpl_StreamSetSize(This->scratch, + This->entries[index].stream_entry, newsize); + } + + if (SUCCEEDED(hr)) + This->entries[index].data.size = newsize; + + return hr; +} + +static HRESULT 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)) return hr; + + hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst); + if (FAILED(hr)) return hr; + + dst_entry = &This->entries[dst]; + src_entry = &This->entries[src]; + + dst_entry->stream_dirty = src_entry->stream_dirty; + dst_entry->stream_entry = src_entry->stream_entry; + dst_entry->transactedParentEntry = src_entry->transactedParentEntry; + dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry; + dst_entry->data.size = src_entry->data.size; + + return S_OK; } static const IStorageVtbl TransactedSnapshotImpl_Vtbl = @@ -4289,7 +4859,8 @@ static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl = TransactedSnapshotImpl_DestroyDirEntry, TransactedSnapshotImpl_StreamReadAt, TransactedSnapshotImpl_StreamWriteAt, - TransactedSnapshotImpl_StreamSetSize + TransactedSnapshotImpl_StreamSetSize, + TransactedSnapshotImpl_StreamLink }; static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, @@ -4317,17 +4888,35 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, (*result)->base.filename = parentStorage->filename; - /* Create a new temporary storage to act as the snapshot */ - hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot); + /* Create a new temporary storage to act as the scratch file. */ + hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE, + 0, (IStorage**)&(*result)->scratch); if (SUCCEEDED(hr)) { - (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry; + ULONG num_entries = 20; - /* parentStorage already has 1 reference, which we take over here. */ - (*result)->transactedParent = parentStorage; + (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries); - parentStorage->transactedChild = (StorageBaseImpl*)*result; + (*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 = (StorageBaseImpl*)*result; + + (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry); + } + else + { + IStorage_Release((IStorage*)(*result)->scratch); + + hr = E_OUTOFMEMORY; + } } if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result)); @@ -4474,6 +5063,15 @@ static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base, index, newsize); } +static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base, + DirRef dst, DirRef src) +{ + StorageInternalImpl* This = (StorageInternalImpl*) base; + + return StorageBaseImpl_StreamLink(This->parentStorage, + dst, src); +} + /****************************************************************************** ** ** Storage32InternalImpl_Commit @@ -4833,7 +5431,8 @@ static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl = StorageInternalImpl_DestroyDirEntry, StorageInternalImpl_StreamReadAt, StorageInternalImpl_StreamWriteAt, - StorageInternalImpl_StreamSetSize + StorageInternalImpl_StreamSetSize, + StorageInternalImpl_StreamLink }; /****************************************************************************** @@ -5026,38 +5625,134 @@ void StorageUtl_CopyDirEntryToSTATSTG( ** BlockChainStream implementation */ +/* Read and save the index of all blocks in this stream. */ +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. */ +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; +} + BlockChainStream* BlockChainStream_Construct( StorageImpl* parentStorage, ULONG* headOfStreamPlaceHolder, DirRef dirEntry) { BlockChainStream* newStream; - ULONG blockIndex; newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream)); newStream->parentStorage = parentStorage; newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; newStream->ownerDirEntry = dirEntry; - newStream->lastBlockNoInSequence = 0xFFFFFFFF; - newStream->tailIndex = BLOCK_END_OF_CHAIN; - newStream->numBlocks = 0; + newStream->indexCache = NULL; + newStream->indexCacheLen = 0; + newStream->indexCacheSize = 0; - blockIndex = BlockChainStream_GetHeadOfChain(newStream); - - while (blockIndex != BLOCK_END_OF_CHAIN) + if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) { - newStream->numBlocks++; - newStream->tailIndex = blockIndex; - - if(FAILED(StorageImpl_GetNextBlockInChain( - parentStorage, - blockIndex, - &blockIndex))) - { - HeapFree(GetProcessHeap(), 0, newStream); - return NULL; - } + HeapFree(GetProcessHeap(), 0, newStream->indexCache); + HeapFree(GetProcessHeap(), 0, newStream); + return NULL; } return newStream; @@ -5065,6 +5760,8 @@ BlockChainStream* BlockChainStream_Construct( void BlockChainStream_Destroy(BlockChainStream* This) { + if (This) + HeapFree(GetProcessHeap(), 0, This->indexCache); HeapFree(GetProcessHeap(), 0, This); } @@ -5105,27 +5802,10 @@ static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This) * * Returns the number of blocks that comprises this chain. * This is not the size of the stream as the last block may not be full! - * */ static ULONG BlockChainStream_GetCount(BlockChainStream* This) { - ULONG blockIndex; - ULONG count = 0; - - blockIndex = BlockChainStream_GetHeadOfChain(This); - - while (blockIndex != BLOCK_END_OF_CHAIN) - { - count++; - - if(FAILED(StorageImpl_GetNextBlockInChain( - This->parentStorage, - blockIndex, - &blockIndex))) - return 0; - } - - return count; + return This->numBlocks; } /****************************************************************************** @@ -5146,44 +5826,26 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This, ULONG bytesToReadInBuffer; ULONG blockIndex; BYTE* bufferWalker; + ULARGE_INTEGER stream_size; TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); /* * Find the first block in the stream that contains part of the buffer. */ - if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) || - (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) || - (blockNoInSequence < This->lastBlockNoInSequence) ) - { - blockIndex = BlockChainStream_GetHeadOfChain(This); - This->lastBlockNoInSequence = blockNoInSequence; - } + 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 - { - ULONG temp = blockNoInSequence; - - blockIndex = This->lastBlockNoInSequenceIndex; - blockNoInSequence -= This->lastBlockNoInSequence; - This->lastBlockNoInSequence = temp; - } - - while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) - { - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex))) - return STG_E_DOCFILECORRUPT; - blockNoInSequence--; - } - - if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN)) - return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */ - - This->lastBlockNoInSequenceIndex = blockIndex; + return S_OK; /* * Start reading the buffer. */ - *bytesRead = 0; bufferWalker = buffer; while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) @@ -5221,7 +5883,7 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This, break; } - return (size == 0) ? S_OK : STG_E_READFAULT; + return S_OK; } /****************************************************************************** @@ -5245,31 +5907,7 @@ HRESULT BlockChainStream_WriteAt(BlockChainStream* This, /* * Find the first block in the stream that contains part of the buffer. */ - if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) || - (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) || - (blockNoInSequence < This->lastBlockNoInSequence) ) - { - blockIndex = BlockChainStream_GetHeadOfChain(This); - This->lastBlockNoInSequence = blockNoInSequence; - } - else - { - ULONG temp = blockNoInSequence; - - blockIndex = This->lastBlockNoInSequenceIndex; - blockNoInSequence -= This->lastBlockNoInSequence; - This->lastBlockNoInSequence = temp; - } - - while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) - { - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, - &blockIndex))) - return STG_E_DOCFILECORRUPT; - blockNoInSequence--; - } - - This->lastBlockNoInSequenceIndex = blockIndex; + blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); /* BlockChainStream_SetSize should have already been called to ensure we have * enough blocks in the chain to write into */ @@ -5330,15 +5968,8 @@ HRESULT BlockChainStream_WriteAt(BlockChainStream* This, static BOOL BlockChainStream_Shrink(BlockChainStream* This, ULARGE_INTEGER newSize) { - ULONG blockIndex, extraBlock; + ULONG blockIndex; ULONG numBlocks; - ULONG count = 1; - - /* - * Reset the last accessed block cache. - */ - This->lastBlockNoInSequence = 0xFFFFFFFF; - This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN; /* * Figure out how many blocks are needed to contain the new size @@ -5348,43 +5979,62 @@ static BOOL BlockChainStream_Shrink(BlockChainStream* This, if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0) numBlocks++; - blockIndex = BlockChainStream_GetHeadOfChain(This); - - /* - * Go to the new end of chain - */ - while (count < numBlocks) + if (numBlocks) { - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, - &blockIndex))) - return FALSE; - count++; + /* + * 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; } - /* Get the next block before marking the new end */ - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, - &extraBlock))) - return FALSE; - - /* Mark the new end of chain */ - StorageImpl_SetNextBlockInChain( - This->parentStorage, - blockIndex, - BLOCK_END_OF_CHAIN); - - This->tailIndex = blockIndex; This->numBlocks = numBlocks; /* * Mark the extra blocks as free */ - while (extraBlock != BLOCK_END_OF_CHAIN) + while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks) { - if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock, - &blockIndex))) - return FALSE; - StorageImpl_FreeBigBlock(This->parentStorage, extraBlock); - extraBlock = blockIndex; + 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--; } return TRUE; @@ -5498,6 +6148,9 @@ static BOOL BlockChainStream_Enlarge(BlockChainStream* This, This->numBlocks = newNumBlocks; } + if (FAILED(BlockChainStream_UpdateIndexCache(This))) + return FALSE; + return TRUE; } @@ -5663,6 +6316,9 @@ static HRESULT SmallBlockChainStream_GetNextBlockInChain( &buffer, &bytesRead); + if (SUCCEEDED(res) && bytesRead != sizeof(DWORD)) + res = STG_E_READFAULT; + if (SUCCEEDED(res)) { StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain); @@ -5734,6 +6390,9 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock( ULONG nextBlockIndex = BLOCK_END_OF_CHAIN; HRESULT res = S_OK; ULONG smallBlocksPerBigBlock; + DirEntry rootEntry; + ULONG blocksRequired; + ULARGE_INTEGER old_size, size_required; offsetOfBlockInDepot.u.HighPart = 0; @@ -5754,7 +6413,7 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock( /* * If we run out of space for the small block depot, enlarge it */ - if (SUCCEEDED(res)) + if (SUCCEEDED(res) && bytesRead == sizeof(DWORD)) { StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex); @@ -5766,76 +6425,22 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock( ULONG count = BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain); - ULONG sbdIndex = This->parentStorage->smallBlockDepotStart; - ULONG nextBlock, newsbdIndex; BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE]; + ULARGE_INTEGER newSize, offset; + ULONG bytesWritten; - nextBlock = sbdIndex; - while (nextBlock != BLOCK_END_OF_CHAIN) - { - sbdIndex = nextBlock; - StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock); - } - - newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage); - if (sbdIndex != BLOCK_END_OF_CHAIN) - StorageImpl_SetNextBlockInChain( - This->parentStorage, - sbdIndex, - newsbdIndex); - - StorageImpl_SetNextBlockInChain( - This->parentStorage, - newsbdIndex, - BLOCK_END_OF_CHAIN); + newSize.QuadPart = (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); - StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot); + offset.QuadPart = count * This->parentStorage->bigBlockSize; + BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain, + offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten); - if (count == 0) - { - /* - * We have just created the small block depot. - */ - DirEntry rootEntry; - ULONG sbStartIndex; - - /* - * Save it in the header - */ - This->parentStorage->smallBlockDepotStart = newsbdIndex; - StorageImpl_SaveFileHeader(This->parentStorage); - - /* - * And allocate the first big block that will contain small blocks - */ - sbStartIndex = - StorageImpl_GetNextFreeBigBlock(This->parentStorage); - - StorageImpl_SetNextBlockInChain( - This->parentStorage, - sbStartIndex, - BLOCK_END_OF_CHAIN); - - StorageImpl_ReadDirEntry( - This->parentStorage, - This->parentStorage->base.storageDirEntry, - &rootEntry); - - rootEntry.startingBlock = sbStartIndex; - rootEntry.size.u.HighPart = 0; - rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize; - - StorageImpl_WriteDirEntry( - This->parentStorage, - This->parentStorage->base.storageDirEntry, - &rootEntry); - } - else - StorageImpl_SaveFileHeader(This->parentStorage); + StorageImpl_SaveFileHeader(This->parentStorage); } } @@ -5847,30 +6452,29 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock( /* * Verify if we have to allocate big blocks to contain small blocks */ - if (blockIndex % smallBlocksPerBigBlock == 0) + blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; + + size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize; + + old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain); + + if (size_required.QuadPart > old_size.QuadPart) { - DirEntry rootEntry; - ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; + BlockChainStream_SetSize( + This->parentStorage->smallBlockRootChain, + size_required); StorageImpl_ReadDirEntry( This->parentStorage, This->parentStorage->base.storageDirEntry, &rootEntry); - if (rootEntry.size.u.LowPart < - (blocksRequired * This->parentStorage->bigBlockSize)) - { - rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize; + rootEntry.size = size_required; - BlockChainStream_SetSize( - This->parentStorage->smallBlockRootChain, - rootEntry.size); - - StorageImpl_WriteDirEntry( - This->parentStorage, - This->parentStorage->base.storageDirEntry, - &rootEntry); - } + StorageImpl_WriteDirEntry( + This->parentStorage, + This->parentStorage->base.storageDirEntry, + &rootEntry); } return blockIndex; @@ -5900,12 +6504,21 @@ HRESULT SmallBlockChainStream_ReadAt( ULONG blockIndex; ULONG bytesReadFromBigBlockFile; BYTE* bufferWalker; + ULARGE_INTEGER stream_size; /* * This should never happen on a small block file. */ assert(offset.u.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. */ @@ -5922,7 +6535,6 @@ HRESULT SmallBlockChainStream_ReadAt( /* * Start reading the buffer. */ - *bytesRead = 0; bufferWalker = buffer; while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) @@ -5956,6 +6568,9 @@ HRESULT SmallBlockChainStream_ReadAt( if (FAILED(rc)) return rc; + if (!bytesReadFromBigBlockFile) + return STG_E_DOCFILECORRUPT; + /* * Step to the next big block. */ @@ -5969,7 +6584,7 @@ HRESULT SmallBlockChainStream_ReadAt( offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize; } - return (size == 0) ? S_OK : STG_E_READFAULT; + return S_OK; } /****************************************************************************** diff --git a/reactos/dll/win32/ole32/storage32.h b/reactos/dll/win32/ole32/storage32.h index 36a7d679af6..19f3f2d682c 100644 --- a/reactos/dll/win32/ole32/storage32.h +++ b/reactos/dll/win32/ole32/storage32.h @@ -263,6 +263,7 @@ struct StorageBaseImplVtbl { 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); }; static inline void StorageBaseImpl_Destroy(StorageBaseImpl *This) @@ -320,6 +321,16 @@ static inline HRESULT StorageBaseImpl_StreamSetSize(StorageBaseImpl *This, 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); +} + /**************************************************************************** * StorageBaseImpl stream list handlers */ @@ -513,13 +524,22 @@ void StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl *storage,STATSTG* destinat * 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; +}; + struct BlockChainStream { StorageImpl* parentStorage; ULONG* headOfStreamPlaceHolder; DirRef ownerDirEntry; - ULONG lastBlockNoInSequence; - ULONG lastBlockNoInSequenceIndex; + struct BlockChainRun* indexCache; + ULONG indexCacheLen; + ULONG indexCacheSize; ULONG tailIndex; ULONG numBlocks; }; diff --git a/reactos/dll/win32/ole32/usrmarshal.c b/reactos/dll/win32/ole32/usrmarshal.c index ed316201f69..2d238e6ccc3 100644 --- a/reactos/dll/win32/ole32/usrmarshal.c +++ b/reactos/dll/win32/ole32/usrmarshal.c @@ -2761,7 +2761,8 @@ void CALLBACK IAdviseSink_OnDataChange_Proxy( FORMATETC *pFormatetc, STGMEDIUM *pStgmed) { - FIXME(":stub\n"); + TRACE("(%p)->(%p, %p)\n", This, pFormatetc, pStgmed); + IAdviseSink_RemoteOnDataChange_Proxy(This, pFormatetc, pStgmed); } HRESULT __RPC_STUB IAdviseSink_OnDataChange_Stub( @@ -2769,8 +2770,9 @@ HRESULT __RPC_STUB IAdviseSink_OnDataChange_Stub( FORMATETC *pFormatetc, ASYNC_STGMEDIUM *pStgmed) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)->(%p, %p)\n", This, pFormatetc, pStgmed); + IAdviseSink_OnDataChange(This, pFormatetc, pStgmed); + return S_OK; } void CALLBACK IAdviseSink_OnViewChange_Proxy( @@ -2778,7 +2780,8 @@ void CALLBACK IAdviseSink_OnViewChange_Proxy( DWORD dwAspect, LONG lindex) { - FIXME(":stub\n"); + TRACE("(%p)->(%d, %d)\n", This, dwAspect, lindex); + IAdviseSink_RemoteOnViewChange_Proxy(This, dwAspect, lindex); } HRESULT __RPC_STUB IAdviseSink_OnViewChange_Stub( @@ -2786,64 +2789,73 @@ HRESULT __RPC_STUB IAdviseSink_OnViewChange_Stub( DWORD dwAspect, LONG lindex) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)->(%d, %d)\n", This, dwAspect, lindex); + IAdviseSink_OnViewChange(This, dwAspect, lindex); + return S_OK; } void CALLBACK IAdviseSink_OnRename_Proxy( IAdviseSink* This, IMoniker *pmk) { - FIXME(":stub\n"); + TRACE("(%p)->(%p)\n", This, pmk); + IAdviseSink_RemoteOnRename_Proxy(This, pmk); } HRESULT __RPC_STUB IAdviseSink_OnRename_Stub( IAdviseSink* This, IMoniker *pmk) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, pmk); + IAdviseSink_OnRename(This, pmk); + return S_OK; } void CALLBACK IAdviseSink_OnSave_Proxy( IAdviseSink* This) { - FIXME(":stub\n"); + TRACE("(%p)\n", This); + IAdviseSink_RemoteOnSave_Proxy(This); } HRESULT __RPC_STUB IAdviseSink_OnSave_Stub( IAdviseSink* This) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)\n", This); + IAdviseSink_OnSave(This); + return S_OK; } void CALLBACK IAdviseSink_OnClose_Proxy( IAdviseSink* This) { - FIXME(":stub\n"); + TRACE("(%p)\n", This); + IAdviseSink_RemoteOnClose_Proxy(This); } HRESULT __RPC_STUB IAdviseSink_OnClose_Stub( IAdviseSink* This) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)\n", This); + IAdviseSink_OnClose(This); + return S_OK; } void CALLBACK IAdviseSink2_OnLinkSrcChange_Proxy( IAdviseSink2* This, IMoniker *pmk) { - FIXME(":stub\n"); + TRACE("(%p)->(%p)\n", This, pmk); + IAdviseSink2_RemoteOnLinkSrcChange_Proxy(This, pmk); } HRESULT __RPC_STUB IAdviseSink2_OnLinkSrcChange_Stub( IAdviseSink2* This, IMoniker *pmk) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)->(%p)\n", This, pmk); + IAdviseSink2_OnLinkSrcChange(This, pmk); + return S_OK; } HRESULT CALLBACK IDataObject_GetData_Proxy(