diff --git a/reactos/dll/win32/ole32/bindctx.c b/reactos/dll/win32/ole32/bindctx.c index d3f27d7b905..45013e68e0b 100644 --- a/reactos/dll/win32/ole32/bindctx.c +++ b/reactos/dll/win32/ole32/bindctx.c @@ -81,7 +81,7 @@ BindCtxImpl_QueryInterface(IBindCtx* iface,REFIID riid,void** ppvObject) if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IBindCtx, riid)) { - *ppvObject = This; + *ppvObject = &This->IBindCtx_iface; IBindCtx_AddRef(iface); return S_OK; } diff --git a/reactos/dll/win32/ole32/clipboard.c b/reactos/dll/win32/ole32/clipboard.c index 17312040e2e..12a1058bb8e 100644 --- a/reactos/dll/win32/ole32/clipboard.c +++ b/reactos/dll/win32/ole32/clipboard.c @@ -63,8 +63,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); -#define HANDLE_ERROR(err) do { hr = err; TRACE("(HRESULT=%x)\n", (HRESULT)err); goto CLEANUP; } while (0) - /* Structure of 'Ole Private Data' clipboard format */ typedef struct { @@ -559,6 +557,12 @@ static HRESULT render_embed_source_hack(IDataObject *data, LPFORMATETC fmt) hStorage = GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, 0); if (hStorage == NULL) return E_OUTOFMEMORY; hr = CreateILockBytesOnHGlobal(hStorage, FALSE, &ptrILockBytes); + if (FAILED(hr)) + { + GlobalFree(hStorage); + return hr; + } + hr = StgCreateDocfileOnILockBytes(ptrILockBytes, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &std.u.pstg); ILockBytes_Release(ptrILockBytes); diff --git a/reactos/dll/win32/ole32/compobj.c b/reactos/dll/win32/ole32/compobj.c index 431e2833269..89a6a47b410 100644 --- a/reactos/dll/win32/ole32/compobj.c +++ b/reactos/dll/win32/ole32/compobj.c @@ -2196,7 +2196,7 @@ HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id ) /****************************************************************************** * IIDFromString [OLE32.@] * - * Converts a interface identifier from its string representation into + * Converts an interface identifier from its string representation to * the IID struct. * * PARAMS @@ -4930,8 +4930,8 @@ HRESULT WINAPI CoGetObjectContext(REFIID riid, void **ppv) else context->apttype = APTTYPE_STA; - hr = IUnknown_QueryInterface((IUnknown *)&context->IComThreadingInfo_iface, riid, ppv); - IUnknown_Release((IUnknown *)&context->IComThreadingInfo_iface); + hr = IComThreadingInfo_QueryInterface(&context->IComThreadingInfo_iface, riid, ppv); + IComThreadingInfo_Release(&context->IComThreadingInfo_iface); return hr; } diff --git a/reactos/dll/win32/ole32/compositemoniker.c b/reactos/dll/win32/ole32/compositemoniker.c index 15417ceb482..1476744f8fe 100644 --- a/reactos/dll/win32/ole32/compositemoniker.c +++ b/reactos/dll/win32/ole32/compositemoniker.c @@ -707,6 +707,8 @@ CompositeMonikerImpl_GetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc, IRunningObjectTable* rot; res = IMoniker_ComposeWith(pmkToLeft, iface, FALSE, &leftMk); + if (FAILED(res)) + return res; res = IBindCtx_GetRunningObjectTable(pbc,&rot); if (FAILED(res)) @@ -761,9 +763,16 @@ CompositeMonikerImpl_Inverse(IMoniker* iface,IMoniker** ppmk) /* This method returns a composite moniker that consists of the inverses of each of the components */ /* of the original composite, stored in reverse order */ + *ppmk = NULL; + res=CreateAntiMoniker(&antiMk); - res=IMoniker_ComposeWith(iface,antiMk,0,&tempMk); + if (FAILED(res)) + return res; + + res=IMoniker_ComposeWith(iface,antiMk,FALSE,&tempMk); IMoniker_Release(antiMk); + if (FAILED(res)) + return res; if (tempMk==NULL) diff --git a/reactos/dll/win32/ole32/datacache.c b/reactos/dll/win32/ole32/datacache.c index 07b29bd7d89..48b173fccaa 100644 --- a/reactos/dll/win32/ole32/datacache.c +++ b/reactos/dll/win32/ole32/datacache.c @@ -74,6 +74,13 @@ typedef struct PresentationDataHeader DWORD dwSize; } PresentationDataHeader; +enum stream_type +{ + no_stream, + pres_stream, + contents_stream +}; + typedef struct DataCacheEntry { struct list entry; @@ -84,11 +91,12 @@ typedef struct DataCacheEntry /* cached data */ STGMEDIUM stgmedium; /* - * This storage pointer is set through a call to + * This stream pointer is set through a call to * IPersistStorage_Load. This is where the visual * representation of the object is stored. */ - IStorage *storage; + IStream *stream; + enum stream_type stream_type; /* connection ID */ DWORD id; /* dirty flag */ @@ -204,8 +212,8 @@ static const char * debugstr_formatetc(const FORMATETC *formatetc) static void DataCacheEntry_Destroy(DataCache *cache, DataCacheEntry *cache_entry) { list_remove(&cache_entry->entry); - if (cache_entry->storage) - IStorage_Release(cache_entry->storage); + if (cache_entry->stream) + IStream_Release(cache_entry->stream); HeapFree(GetProcessHeap(), 0, cache_entry->fmtetc.ptd); ReleaseStgMedium(&cache_entry->stgmedium); if(cache_entry->sink_id) @@ -300,7 +308,8 @@ static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc (*cache_entry)->data_cf = 0; (*cache_entry)->stgmedium.tymed = TYMED_NULL; (*cache_entry)->stgmedium.pUnkForRelease = NULL; - (*cache_entry)->storage = NULL; + (*cache_entry)->stream = NULL; + (*cache_entry)->stream_type = no_stream; (*cache_entry)->id = This->last_cache_id++; (*cache_entry)->dirty = TRUE; (*cache_entry)->stream_number = -1; @@ -463,63 +472,136 @@ static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat) */ static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStream **ppStm) { - STATSTG elem; - IEnumSTATSTG *pEnum; HRESULT hr; + LARGE_INTEGER offset; - if (!ppStm) return E_POINTER; - - hr = IStorage_EnumElements(cache_entry->storage, 0, NULL, 0, &pEnum); - if (FAILED(hr)) return hr; - - while ((hr = IEnumSTATSTG_Next(pEnum, 1, &elem, NULL)) == S_OK) + if (cache_entry->stream) { - if (DataCache_IsPresentationStream(&elem)) - { - IStream *pStm; + /* Rewind the stream before returning it. */ + offset.QuadPart = 0; - hr = IStorage_OpenStream(cache_entry->storage, elem.pwcsName, - NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, - &pStm); - if (SUCCEEDED(hr)) - { - PresentationDataHeader header; - ULONG actual_read; - CLIPFORMAT clipformat; + hr = IStream_Seek( cache_entry->stream, offset, STREAM_SEEK_SET, NULL ); + if (SUCCEEDED( hr )) + { + *ppStm = cache_entry->stream; + IStream_AddRef( cache_entry->stream ); + } + } + else + hr = OLE_E_BLANK; - hr = read_clipformat(pStm, &clipformat); + return hr; +} - if (hr == S_OK) - hr = IStream_Read(pStm, &header, sizeof(header), &actual_read); - /* can't use SUCCEEDED(hr): S_FALSE counts as an error */ - if (hr == S_OK && actual_read == sizeof(header) - && header.dvAspect == cache_entry->fmtetc.dwAspect) - { - /* Rewind the stream before returning it. */ - LARGE_INTEGER offset; - offset.u.LowPart = 0; - offset.u.HighPart = 0; - IStream_Seek(pStm, offset, STREAM_SEEK_SET, NULL); +static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm ) +{ + HRESULT hr; + STATSTG stat; + ULARGE_INTEGER current_pos; + void *bits; + METAFILEPICT *mfpict; + HGLOBAL hmfpict; + PresentationDataHeader header; + CLIPFORMAT clipformat; + static const LARGE_INTEGER offset_zero; + ULONG read; - *ppStm = pStm; - - CoTaskMemFree(elem.pwcsName); - IEnumSTATSTG_Release(pEnum); - - return S_OK; - } - - IStream_Release(pStm); - } - } - - CoTaskMemFree(elem.pwcsName); + if (cache_entry->stream_type != pres_stream) + { + FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type ); + return E_FAIL; } - IEnumSTATSTG_Release(pEnum); + hr = IStream_Stat( stm, &stat, STATFLAG_NONAME ); + if (FAILED( hr )) return hr; - return (hr == S_FALSE ? OLE_E_BLANK : hr); + hr = read_clipformat( stm, &clipformat ); + if (FAILED( hr )) return hr; + + hr = IStream_Read( stm, &header, sizeof(header), &read ); + if (hr != S_OK || read != sizeof(header)) return E_FAIL; + + hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, ¤t_pos ); + if (FAILED( hr )) return hr; + + stat.cbSize.QuadPart -= current_pos.QuadPart; + + hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) ); + if (!hmfpict) return E_OUTOFMEMORY; + mfpict = GlobalLock( hmfpict ); + + bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart); + if (!bits) + { + GlobalFree( hmfpict ); + return E_OUTOFMEMORY; + } + + hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read ); + if (hr != S_OK || read != stat.cbSize.u.LowPart) hr = E_FAIL; + + if (SUCCEEDED( hr )) + { + /* FIXME: get this from the stream */ + mfpict->mm = MM_ANISOTROPIC; + mfpict->xExt = header.dwObjectExtentX; + mfpict->yExt = header.dwObjectExtentY; + mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits ); + if (!mfpict->hMF) + hr = E_FAIL; + } + + GlobalUnlock( hmfpict ); + if (SUCCEEDED( hr )) + { + cache_entry->data_cf = cache_entry->fmtetc.cfFormat; + cache_entry->stgmedium.tymed = TYMED_MFPICT; + cache_entry->stgmedium.u.hMetaFilePict = hmfpict; + } + else + GlobalFree( hmfpict ); + + HeapFree( GetProcessHeap(), 0, bits ); + + return hr; +} + +static HRESULT load_dib( DataCacheEntry *cache_entry, IStream *stm ) +{ + HRESULT hr; + STATSTG stat; + void *dib; + HGLOBAL hglobal; + ULONG read; + + if (cache_entry->stream_type != contents_stream) + { + FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type ); + return E_FAIL; + } + + hr = IStream_Stat( stm, &stat, STATFLAG_NONAME ); + if (FAILED( hr )) return hr; + + hglobal = GlobalAlloc( GMEM_MOVEABLE, stat.cbSize.u.LowPart ); + if (!hglobal) return E_OUTOFMEMORY; + dib = GlobalLock( hglobal ); + + hr = IStream_Read( stm, dib, stat.cbSize.u.LowPart, &read ); + GlobalUnlock( hglobal ); + + if (hr != S_OK || read != stat.cbSize.u.LowPart) + { + GlobalFree( hglobal ); + return E_FAIL; + } + + cache_entry->data_cf = cache_entry->fmtetc.cfFormat; + cache_entry->stgmedium.tymed = TYMED_HGLOBAL; + cache_entry->stgmedium.u.hGlobal = hglobal; + + return S_OK; } /************************************************************************ @@ -537,113 +619,29 @@ static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStrea */ static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry) { - IStream* presStream = NULL; - HRESULT hres; - ULARGE_INTEGER current_pos; - STATSTG streamInfo; - void* metafileBits; - METAFILEPICT *mfpict; - HGLOBAL hmfpict; - PresentationDataHeader header; - CLIPFORMAT clipformat; - static const LARGE_INTEGER offset_zero; + HRESULT hr; + IStream *stm; - /* - * Open the presentation stream. - */ - hres = DataCacheEntry_OpenPresStream(cache_entry, &presStream); + hr = DataCacheEntry_OpenPresStream( cache_entry, &stm ); + if (FAILED(hr)) return hr; - if (FAILED(hres)) - return hres; + switch (cache_entry->fmtetc.cfFormat) + { + case CF_METAFILEPICT: + hr = load_mf_pict( cache_entry, stm ); + break; - /* - * Get the size of the stream. - */ - hres = IStream_Stat(presStream, - &streamInfo, - STATFLAG_NONAME); + case CF_DIB: + hr = load_dib( cache_entry, stm ); + break; - /* - * Read the header. - */ + default: + FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat ); + hr = E_NOTIMPL; + } - hres = read_clipformat(presStream, &clipformat); - if (FAILED(hres)) - { - IStream_Release(presStream); - return hres; - } - - hres = IStream_Read( - presStream, - &header, - sizeof(PresentationDataHeader), - NULL); - if (hres != S_OK) - { - IStream_Release(presStream); - return E_FAIL; - } - - hres = IStream_Seek(presStream, offset_zero, STREAM_SEEK_CUR, ¤t_pos); - - streamInfo.cbSize.QuadPart -= current_pos.QuadPart; - - hmfpict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT)); - if (!hmfpict) - { - IStream_Release(presStream); - return E_OUTOFMEMORY; - } - mfpict = GlobalLock(hmfpict); - - /* - * Allocate a buffer for the metafile bits. - */ - metafileBits = HeapAlloc(GetProcessHeap(), - 0, - streamInfo.cbSize.u.LowPart); - - /* - * Read the metafile bits. - */ - hres = IStream_Read( - presStream, - metafileBits, - streamInfo.cbSize.u.LowPart, - NULL); - - /* - * Create a metafile with those bits. - */ - if (SUCCEEDED(hres)) - { - /* FIXME: get this from the stream */ - mfpict->mm = MM_ANISOTROPIC; - mfpict->xExt = header.dwObjectExtentX; - mfpict->yExt = header.dwObjectExtentY; - mfpict->hMF = SetMetaFileBitsEx(streamInfo.cbSize.u.LowPart, metafileBits); - if (!mfpict->hMF) - hres = E_FAIL; - } - - GlobalUnlock(hmfpict); - if (SUCCEEDED(hres)) - { - cache_entry->data_cf = cache_entry->fmtetc.cfFormat; - cache_entry->stgmedium.tymed = TYMED_MFPICT; - cache_entry->stgmedium.u.hMetaFilePict = hmfpict; - } - else - GlobalFree(hmfpict); - - /* - * Cleanup. - */ - HeapFree(GetProcessHeap(), 0, metafileBits); - IStream_Release(presStream); - - return hres; + IStream_Release( stm ); + return hr; } static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *cache_entry, @@ -822,7 +820,7 @@ static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry, static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, STGMEDIUM *stgmedium) { - if (stgmedium->tymed == TYMED_NULL && cache_entry->storage) + if (stgmedium->tymed == TYMED_NULL && cache_entry->stream) { HRESULT hr = DataCacheEntry_LoadData(cache_entry); if (FAILED(hr)) @@ -842,10 +840,10 @@ static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry) static inline void DataCacheEntry_HandsOffStorage(DataCacheEntry *cache_entry) { - if (cache_entry->storage) + if (cache_entry->stream) { - IStorage_Release(cache_entry->storage); - cache_entry->storage = NULL; + IStream_Release(cache_entry->stream); + cache_entry->stream = NULL; } } @@ -1014,12 +1012,15 @@ static HRESULT WINAPI DataCache_GetDataHere( return E_NOTIMPL; } -static HRESULT WINAPI DataCache_QueryGetData( - IDataObject* iface, - LPFORMATETC pformatetc) +static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt ) { - FIXME("stub\n"); - return E_NOTIMPL; + DataCache *This = impl_from_IDataObject( iface ); + DataCacheEntry *cache_entry; + + TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) ); + cache_entry = DataCache_GetEntryForFormatEtc( This, fmt ); + + return cache_entry ? S_OK : S_FALSE; } /************************************************************************ @@ -1163,34 +1164,28 @@ static ULONG WINAPI DataCache_IPersistStorage_Release( /************************************************************************ * DataCache_GetClassID (IPersistStorage) * - * The data cache doesn't implement this method. */ -static HRESULT WINAPI DataCache_GetClassID( - IPersistStorage* iface, - CLSID* pClassID) +static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid) { - DataCache *This = impl_from_IPersistStorage(iface); - DataCacheEntry *cache_entry; + DataCache *This = impl_from_IPersistStorage( iface ); + HRESULT hr; + STATSTG statstg; - TRACE("(%p, %p)\n", iface, pClassID); + TRACE( "(%p, %p)\n", iface, clsid ); - LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) - { - if (cache_entry->storage != NULL) + if (This->presentationStorage) { - STATSTG statstg; - HRESULT hr = IStorage_Stat(cache_entry->storage, &statstg, STATFLAG_NONAME); - if (SUCCEEDED(hr)) - { - *pClassID = statstg.clsid; - return S_OK; - } + hr = IStorage_Stat( This->presentationStorage, &statstg, STATFLAG_NONAME ); + if (SUCCEEDED(hr)) + { + *clsid = statstg.clsid; + return S_OK; + } } - } - *pClassID = CLSID_NULL; + *clsid = CLSID_NULL; - return S_OK; + return S_OK; } /************************************************************************ @@ -1239,6 +1234,98 @@ static HRESULT WINAPI DataCache_InitNew( return S_OK; } + +static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, IStream *stm, + enum stream_type type ) +{ + DataCacheEntry *cache_entry; + HRESULT hr = S_OK; + + TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) ); + + cache_entry = DataCache_GetEntryForFormatEtc( This, fmt ); + if (!cache_entry) + hr = DataCache_CreateEntry( This, fmt, &cache_entry ); + if (SUCCEEDED( hr )) + { + DataCacheEntry_DiscardData( cache_entry ); + if (cache_entry->stream) IStream_Release( cache_entry->stream ); + cache_entry->stream = stm; + IStream_AddRef( stm ); + cache_entry->stream_type = type; + cache_entry->dirty = FALSE; + } + return hr; +} + +static HRESULT parse_pres_streams( DataCache *This, IStorage *stg ) +{ + HRESULT hr; + IEnumSTATSTG *stat_enum; + STATSTG stat; + IStream *stm; + PresentationDataHeader header; + ULONG actual_read; + CLIPFORMAT clipformat; + FORMATETC fmtetc; + + hr = IStorage_EnumElements( stg, 0, NULL, 0, &stat_enum ); + if (FAILED( hr )) return hr; + + while ((hr = IEnumSTATSTG_Next( stat_enum, 1, &stat, NULL )) == S_OK) + { + if (DataCache_IsPresentationStream( &stat )) + { + hr = IStorage_OpenStream( stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, + 0, &stm ); + if (SUCCEEDED( hr )) + { + hr = read_clipformat( stm, &clipformat ); + + if (hr == S_OK) + hr = IStream_Read( stm, &header, sizeof(header), &actual_read ); + + if (hr == S_OK && actual_read == sizeof(header)) + { + fmtetc.cfFormat = clipformat; + fmtetc.ptd = NULL; /* FIXME */ + fmtetc.dwAspect = header.dvAspect; + fmtetc.lindex = header.lindex; + fmtetc.tymed = header.tymed; + + add_cache_entry( This, &fmtetc, stm, pres_stream ); + } + IStream_Release( stm ); + } + } + CoTaskMemFree( stat.pwcsName ); + } + IEnumSTATSTG_Release( stat_enum ); + + return S_OK; +} + +static const FORMATETC static_dib_fmt = { CF_DIB, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + +static HRESULT parse_contents_stream( DataCache *This, IStorage *stg, IStream *stm ) +{ + HRESULT hr; + STATSTG stat; + const FORMATETC *fmt; + + hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); + if (FAILED( hr )) return hr; + + if (IsEqualCLSID( &stat.clsid, &CLSID_Picture_Dib )) + fmt = &static_dib_fmt; + else + return E_FAIL; + + return add_cache_entry( This, fmt, stm, contents_stream ); +} + +static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0}; + /************************************************************************ * DataCache_Load (IPersistStorage) * @@ -1247,86 +1334,34 @@ static HRESULT WINAPI DataCache_InitNew( * and it will load the presentation information when the * IDataObject_GetData or IViewObject2_Draw methods are called. */ -static HRESULT WINAPI DataCache_Load( - IPersistStorage* iface, - IStorage* pStg) +static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *pStg ) { DataCache *This = impl_from_IPersistStorage(iface); - STATSTG elem; - IEnumSTATSTG *pEnum; HRESULT hr; + IStream *stm; TRACE("(%p, %p)\n", iface, pStg); - if (This->presentationStorage != NULL) - IStorage_Release(This->presentationStorage); + IPersistStorage_HandsOffStorage( iface ); - This->presentationStorage = pStg; - - hr = IStorage_EnumElements(pStg, 0, NULL, 0, &pEnum); - if (FAILED(hr)) return hr; - - while ((hr = IEnumSTATSTG_Next(pEnum, 1, &elem, NULL)) == S_OK) + hr = IStorage_OpenStream( pStg, CONTENTS, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, + 0, &stm ); + if (SUCCEEDED( hr )) { - if (DataCache_IsPresentationStream(&elem)) - { - IStream *pStm; + hr = parse_contents_stream( This, pStg, stm ); + IStream_Release( stm ); + } + else + hr = parse_pres_streams( This, pStg ); - hr = IStorage_OpenStream(This->presentationStorage, elem.pwcsName, - NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, - &pStm); - if (SUCCEEDED(hr)) - { - PresentationDataHeader header; - ULONG actual_read; - CLIPFORMAT clipformat; - - hr = read_clipformat(pStm, &clipformat); - - if (hr == S_OK) - hr = IStream_Read(pStm, &header, sizeof(header), - &actual_read); - - /* can't use SUCCEEDED(hr): S_FALSE counts as an error */ - if (hr == S_OK && actual_read == sizeof(header)) - { - DataCacheEntry *cache_entry; - FORMATETC fmtetc; - - fmtetc.cfFormat = clipformat; - fmtetc.ptd = NULL; /* FIXME */ - fmtetc.dwAspect = header.dvAspect; - fmtetc.lindex = header.lindex; - fmtetc.tymed = header.tymed; - - TRACE("loading entry with formatetc: %s\n", debugstr_formatetc(&fmtetc)); - - cache_entry = DataCache_GetEntryForFormatEtc(This, &fmtetc); - if (!cache_entry) - hr = DataCache_CreateEntry(This, &fmtetc, &cache_entry); - if (SUCCEEDED(hr)) - { - DataCacheEntry_DiscardData(cache_entry); - if (cache_entry->storage) IStorage_Release(cache_entry->storage); - cache_entry->storage = pStg; - IStorage_AddRef(pStg); - cache_entry->dirty = FALSE; - } - } - - IStream_Release(pStm); - } - } - - CoTaskMemFree(elem.pwcsName); + if (SUCCEEDED( hr )) + { + This->dirty = FALSE; + This->presentationStorage = pStg; + IStorage_AddRef( This->presentationStorage ); } - This->dirty = FALSE; - - IEnumSTATSTG_Release(pEnum); - - IStorage_AddRef(This->presentationStorage); - return S_OK; + return hr; } /************************************************************************ @@ -1529,7 +1564,7 @@ static HRESULT WINAPI DataCache_Draw( continue; /* if the data hasn't been loaded yet, do it now */ - if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->storage) + if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream) { hres = DataCacheEntry_LoadData(cache_entry); if (FAILED(hres)) @@ -1600,6 +1635,26 @@ static HRESULT WINAPI DataCache_Draw( return S_OK; } + case CF_DIB: + { + BITMAPFILEHEADER *file_head; + BITMAPINFO *info; + BYTE *bits; + + if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) || + !((file_head = GlobalLock( cache_entry->stgmedium.u.hGlobal )))) + continue; + + info = (BITMAPINFO *)(file_head + 1); + bits = (BYTE *) file_head + file_head->bfOffBits; + StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top, + lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top, + 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight, + bits, info, DIB_RGB_COLORS, SRCCOPY ); + + GlobalUnlock( cache_entry->stgmedium.u.hGlobal ); + return S_OK; + } } } @@ -1771,7 +1826,7 @@ static HRESULT WINAPI DataCache_GetExtent( continue; /* if the data hasn't been loaded yet, do it now */ - if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->storage) + if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream) { hres = DataCacheEntry_LoadData(cache_entry); if (FAILED(hres)) @@ -1800,6 +1855,41 @@ static HRESULT WINAPI DataCache_GetExtent( return S_OK; } + case CF_DIB: + { + BITMAPFILEHEADER *file_head; + BITMAPINFOHEADER *info; + LONG x_pels_m, y_pels_m; + + + if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) || + !((file_head = GlobalLock( cache_entry->stgmedium.u.hGlobal )))) + continue; + + info = (BITMAPINFOHEADER *)(file_head + 1); + + x_pels_m = info->biXPelsPerMeter; + y_pels_m = info->biYPelsPerMeter; + + /* Size in units of 0.01mm (ie. MM_HIMETRIC) */ + if (x_pels_m != 0 && y_pels_m != 0) + { + lpsizel->cx = info->biWidth * 100000 / x_pels_m; + lpsizel->cy = info->biHeight * 100000 / y_pels_m; + } + else + { + HDC hdc = GetDC( 0 ); + lpsizel->cx = info->biWidth * 2540 / GetDeviceCaps( hdc, LOGPIXELSX ); + lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY ); + + ReleaseDC( 0, hdc ); + } + + GlobalUnlock( cache_entry->stgmedium.u.hGlobal ); + + return S_OK; + } } } diff --git a/reactos/dll/win32/ole32/defaulthandler.c b/reactos/dll/win32/ole32/defaulthandler.c index f3a450a0351..e54bb25cedf 100644 --- a/reactos/dll/win32/ole32/defaulthandler.c +++ b/reactos/dll/win32/ole32/defaulthandler.c @@ -2171,7 +2171,7 @@ HRESULT HandlerCF_Create(REFCLSID rclsid, REFIID riid, LPVOID *ppv) This->refs = 0; This->clsid = *rclsid; - hr = IUnknown_QueryInterface((IUnknown *)&This->IClassFactory_iface, riid, ppv); + hr = IClassFactory_QueryInterface(&This->IClassFactory_iface, riid, ppv); if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, This); diff --git a/reactos/dll/win32/ole32/filelockbytes.c b/reactos/dll/win32/ole32/filelockbytes.c index d45cc3fccf8..ba50adb8695 100644 --- a/reactos/dll/win32/ole32/filelockbytes.c +++ b/reactos/dll/win32/ole32/filelockbytes.c @@ -45,10 +45,6 @@ static inline FileLockBytesImpl *impl_from_ILockBytes(ILockBytes *iface) * Prototypes for private methods */ -/* Note that this evaluates a and b multiple times, so don't - * pass expressions with side effects. */ -#define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b)) - /**************************************************************************** * GetProtectMode * @@ -330,24 +326,6 @@ static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface, return get_lock_error(); } -HRESULT FileLockBytesImpl_LockRegionSync(ILockBytes* iface, - ULARGE_INTEGER libOffset, ULARGE_INTEGER cb) -{ - FileLockBytesImpl* This = impl_from_ILockBytes(iface); - OVERLAPPED ol; - - if (iface->lpVtbl != &FileLockBytesImpl_Vtbl) - return E_NOTIMPL; - - ol.hEvent = 0; - ol.u.s.Offset = libOffset.u.LowPart; - ol.u.s.OffsetHigh = libOffset.u.HighPart; - - if (LockFileEx(This->hfile, LOCKFILE_EXCLUSIVE_LOCK, 0, cb.u.LowPart, cb.u.HighPart, &ol)) - return S_OK; - return get_lock_error(); -} - static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { @@ -388,6 +366,8 @@ static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface, pstatstg->cbSize.u.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.u.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; } diff --git a/reactos/dll/win32/ole32/filemoniker.c b/reactos/dll/win32/ole32/filemoniker.c index e3b5ab973e2..757df82bbbb 100644 --- a/reactos/dll/win32/ole32/filemoniker.c +++ b/reactos/dll/win32/ole32/filemoniker.c @@ -907,7 +907,8 @@ static HRESULT WINAPI FileMonikerImpl_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix) { - LPOLESTR pathThis = NULL, pathOther = NULL,*stringTable1,*stringTable2,commonPath = NULL; + LPOLESTR pathThis = NULL, pathOther = NULL, *stringTable1 = NULL; + LPOLESTR *stringTable2 = NULL, commonPath = NULL; IBindCtx *bindctx; DWORD mkSys; ULONG nb1,nb2,i,sameIdx; @@ -995,8 +996,8 @@ failed: CoTaskMemFree(pathThis); CoTaskMemFree(pathOther); CoTaskMemFree(commonPath); - free_stringtable(stringTable1); - free_stringtable(stringTable2); + if (stringTable1) free_stringtable(stringTable1); + if (stringTable2) free_stringtable(stringTable2); return ret; } diff --git a/reactos/dll/win32/ole32/hglobalstream.c b/reactos/dll/win32/ole32/hglobalstream.c index d16df82f7fd..732e6c6c81a 100644 --- a/reactos/dll/win32/ole32/hglobalstream.c +++ b/reactos/dll/win32/ole32/hglobalstream.c @@ -70,7 +70,7 @@ static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface( IsEqualIID(&IID_ISequentialStream, riid) || IsEqualIID(&IID_IStream, riid)) { - *ppvObject = This; + *ppvObject = &This->IStream_iface; } if ((*ppvObject)==0) diff --git a/reactos/dll/win32/ole32/itemmoniker.c b/reactos/dll/win32/ole32/itemmoniker.c index c88b4dec311..ac75d43e6a1 100644 --- a/reactos/dll/win32/ole32/itemmoniker.c +++ b/reactos/dll/win32/ole32/itemmoniker.c @@ -586,8 +586,14 @@ static HRESULT WINAPI ItemMonikerImpl_GetTimeOfLastChange(IMoniker* iface, /* IMoniker::GetTimeOfLastChange on the pmkToLeft parameter. */ res=CreateGenericComposite(pmkToLeft,iface,&compositeMk); + if (FAILED(res)) + return res; res=IBindCtx_GetRunningObjectTable(pbc,&rot); + if (FAILED(res)) { + IMoniker_Release(compositeMk); + return res; + } if (IRunningObjectTable_GetTimeOfLastChange(rot,compositeMk,pItemTime)!=S_OK) diff --git a/reactos/dll/win32/ole32/marshal.c b/reactos/dll/win32/ole32/marshal.c index 411f508d98b..3de95d73a06 100644 --- a/reactos/dll/win32/ole32/marshal.c +++ b/reactos/dll/win32/ole32/marshal.c @@ -789,11 +789,12 @@ static HRESULT proxy_manager_construct( static inline void proxy_manager_set_context(struct proxy_manager *This, MSHCTX dest_context, void *dest_context_data) { - MSHCTX old_dest_context = This->dest_context; + MSHCTX old_dest_context; MSHCTX new_dest_context; do { + old_dest_context = This->dest_context; new_dest_context = old_dest_context; /* "stronger" values overwrite "weaker" values. stronger values are * ones that disable more optimisations */ @@ -837,7 +838,7 @@ static inline void proxy_manager_set_context(struct proxy_manager *This, MSHCTX if (old_dest_context == new_dest_context) break; - old_dest_context = InterlockedCompareExchange((PLONG)&This->dest_context, new_dest_context, old_dest_context); + new_dest_context = InterlockedCompareExchange((PLONG)&This->dest_context, new_dest_context, old_dest_context); } while (new_dest_context != old_dest_context); if (dest_context_data) @@ -1028,8 +1029,11 @@ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnk IRemUnknown_AddRef(*remunk); } else if (!This->parent) + { /* disconnected - we can't create IRemUnknown */ + *remunk = NULL; hr = S_FALSE; + } else { STDOBJREF stdobjref; diff --git a/reactos/dll/win32/ole32/moniker.c b/reactos/dll/win32/ole32/moniker.c index 369a50b1758..198e60ec302 100644 --- a/reactos/dll/win32/ole32/moniker.c +++ b/reactos/dll/win32/ole32/moniker.c @@ -298,7 +298,7 @@ RunningObjectTableImpl_QueryInterface(IRunningObjectTable* iface, if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IRunningObjectTable, riid)) - *ppvObject = This; + *ppvObject = &This->IRunningObjectTable_iface; if ((*ppvObject)==0) return E_NOINTERFACE; @@ -1180,7 +1180,7 @@ HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid) IStorage *pstg=0; HRESULT res; int nbElm, length, i; - LONG sizeProgId; + LONG sizeProgId, ret; LPOLESTR *pathDec=0,absFile=0,progId=0; LPWSTR extension; static const WCHAR bkslashW[] = {'\\',0}; @@ -1193,10 +1193,10 @@ HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid) res=StgOpenStorage(filePathName,NULL,STGM_READ | STGM_SHARE_DENY_WRITE,NULL,0,&pstg); - if (SUCCEEDED(res)) + if (SUCCEEDED(res)) { res=ReadClassStg(pstg,pclsid); - - IStorage_Release(pstg); + IStorage_Release(pstg); + } return res; } @@ -1246,26 +1246,23 @@ HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid) return MK_E_INVALIDEXTENSION; } - res=RegQueryValueW(HKEY_CLASSES_ROOT, extension, NULL, &sizeProgId); + ret = RegQueryValueW(HKEY_CLASSES_ROOT, extension, NULL, &sizeProgId); /* get the progId associated to the extension */ progId = CoTaskMemAlloc(sizeProgId); - res = RegQueryValueW(HKEY_CLASSES_ROOT, extension, progId, &sizeProgId); - - if (res==ERROR_SUCCESS) + ret = RegQueryValueW(HKEY_CLASSES_ROOT, extension, progId, &sizeProgId); + if (!ret) /* return the clsid associated to the progId */ - res= CLSIDFromProgID(progId,pclsid); + res = CLSIDFromProgID(progId,pclsid); + else + res = HRESULT_FROM_WIN32(ret); for(i=0; pathDec[i]!=NULL;i++) CoTaskMemFree(pathDec[i]); CoTaskMemFree(pathDec); CoTaskMemFree(progId); - - if (res==ERROR_SUCCESS) - return res; - - return MK_E_INVALIDEXTENSION; + return res != S_OK ? MK_E_INVALIDEXTENSION : res; } /*********************************************************************** @@ -1283,17 +1280,12 @@ static HRESULT WINAPI EnumMonikerImpl_QueryInterface(IEnumMoniker* iface,REFIID *ppvObject = NULL; - if (IsEqualIID(&IID_IUnknown, riid)) - *ppvObject = This; + if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumMoniker, riid)) + *ppvObject = &This->IEnumMoniker_iface; else - if (IsEqualIID(&IID_IEnumMoniker, riid)) - *ppvObject = This; - - if ((*ppvObject)==NULL) return E_NOINTERFACE; IEnumMoniker_AddRef(iface); - return S_OK; } diff --git a/reactos/dll/win32/ole32/ole2.c b/reactos/dll/win32/ole32/ole2.c index ec2e9e884c9..354d4bdeeee 100644 --- a/reactos/dll/win32/ole32/ole2.c +++ b/reactos/dll/win32/ole32/ole2.c @@ -1253,6 +1253,8 @@ HRESULT WINAPI OleLoad( * Get the class ID for the object. */ hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME); + if (FAILED(hres)) + return hres; /* * Now, try and create the handler for the object @@ -2426,6 +2428,9 @@ static DWORD OLEDD_GetButtonState(void) if ( (keyboardState[VK_CONTROL] & 0x80) !=0) keyMask |= MK_CONTROL; + if ( (keyboardState[VK_MENU] & 0x80) !=0) + keyMask |= MK_ALT; + if ( (keyboardState[VK_LBUTTON] & 0x80) !=0) keyMask |= MK_LBUTTON; diff --git a/reactos/dll/win32/ole32/ole2impl.c b/reactos/dll/win32/ole32/ole2impl.c index b6c9c115e1e..12d3c0174af 100644 --- a/reactos/dll/win32/ole32/ole2impl.c +++ b/reactos/dll/win32/ole32/ole2impl.c @@ -22,8 +22,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); -#define MAX_CLIPFORMAT_NAME 80 - /****************************************************************************** * OleQueryCreateFromData [OLE32.@] * diff --git a/reactos/dll/win32/ole32/ole32.spec b/reactos/dll/win32/ole32/ole32.spec index 40fdf1df824..9dc51fbc3ff 100644 --- a/reactos/dll/win32/ole32/ole32.spec +++ b/reactos/dll/win32/ole32/ole32.spec @@ -156,10 +156,10 @@ @ stdcall HBITMAP_UserMarshal(ptr ptr ptr) @ stdcall HBITMAP_UserSize(ptr long ptr) @ stdcall HBITMAP_UserUnmarshal(ptr ptr ptr) -@ stub HBRUSH_UserFree -@ stub HBRUSH_UserMarshal -@ stub HBRUSH_UserSize -@ stub HBRUSH_UserUnmarshal +@ stdcall HBRUSH_UserFree(ptr ptr) +@ stdcall HBRUSH_UserMarshal(ptr ptr ptr) +@ stdcall HBRUSH_UserSize(ptr long ptr) +@ stdcall HBRUSH_UserUnmarshal(ptr ptr ptr) @ stdcall HDC_UserFree(ptr ptr) @ stdcall HDC_UserMarshal(ptr ptr ptr) @ stdcall HDC_UserSize(ptr long ptr) diff --git a/reactos/dll/win32/ole32/olestd.h b/reactos/dll/win32/ole32/olestd.h index 02a4d3b2aab..fbc4442eb20 100644 --- a/reactos/dll/win32/ole32/olestd.h +++ b/reactos/dll/win32/ole32/olestd.h @@ -19,10 +19,6 @@ #if !defined( __WINE_OLESTD_H_ ) #define __WINE_OLESTD_H_ -#if !defined(__cplusplus) && !defined( __TURBOC__) -#define NONAMELESSUNION /* use strict ANSI standard (for DVOBJ.H) */ -#endif - /* Clipboard format strings */ #define CF_EMBEDSOURCE "Embed Source" #define CF_EMBEDDEDOBJECT "Embedded Object" diff --git a/reactos/dll/win32/ole32/pointermoniker.c b/reactos/dll/win32/ole32/pointermoniker.c index 26e2d3b4216..e8cca62e9f1 100644 --- a/reactos/dll/win32/ole32/pointermoniker.c +++ b/reactos/dll/win32/ole32/pointermoniker.c @@ -95,7 +95,7 @@ PointerMonikerImpl_Release(IMoniker* iface) ref = InterlockedDecrement(&This->ref); - /* destroy the object if there's no more reference on it */ + /* destroy the object if there are no more references on it */ if (ref == 0) { if (This->pObject) IUnknown_Release(This->pObject); diff --git a/reactos/dll/win32/ole32/rpc.c b/reactos/dll/win32/ole32/rpc.c index eaf67f74a8b..45ee59e2ebe 100644 --- a/reactos/dll/win32/ole32/rpc.c +++ b/reactos/dll/win32/ole32/rpc.c @@ -1828,6 +1828,7 @@ HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv) bufferlen = 0; if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) { FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid)); + CloseHandle(hPipe); Sleep(1000); continue; } diff --git a/reactos/dll/win32/ole32/stg_prop.c b/reactos/dll/win32/ole32/stg_prop.c index 6eea4f9a4f5..067a7d3e3e5 100644 --- a/reactos/dll/win32/ole32/stg_prop.c +++ b/reactos/dll/win32/ole32/stg_prop.c @@ -1023,7 +1023,7 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This, #define __thiscall_wrapper __cdecl #endif -static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *userdata, ULONG size) +static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *this, ULONG size) { return CoTaskMemAlloc(size); } @@ -1032,7 +1032,7 @@ static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *userdata, ULONG si * end of the buffer. */ static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data, - UINT codepage, void* (__thiscall_wrapper *allocate)(void *userdata, ULONG size), void *allocate_data) + UINT codepage, void* (__thiscall_wrapper *allocate)(void *this, ULONG size), void *allocate_data) { HRESULT hr = S_OK; diff --git a/reactos/dll/win32/ole32/storage32.c b/reactos/dll/win32/ole32/storage32.c index 2cdff46c15c..9b2f0cd7e9b 100644 --- a/reactos/dll/win32/ole32/storage32.c +++ b/reactos/dll/win32/ole32/storage32.c @@ -37,9 +37,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage); -/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ -#define OLESTREAM_ID 0x501 -#define OLESTREAM_MAX_STR_LEN 255 /* * These are signatures to detect the type of Document file. @@ -47,24 +44,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage); static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1}; static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d}; -static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface ) -{ - return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface); -} +extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; -static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface ) -{ - return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface); -} /**************************************************************************** - * Storage32InternalImpl definitions. + * StorageInternalImpl definitions. * - * Definition of the implementation structure for the IStorage32 interface. - * This one implements the IStorage32 interface for storage that are + * Definition of the implementation structure for the IStorage interface. + * This one implements the IStorage interface for storage that are * inside another storage. */ -struct StorageInternalImpl +typedef struct StorageInternalImpl { struct StorageBaseImpl base; @@ -74,45 +64,10 @@ struct StorageInternalImpl struct list ParentListEntry; StorageBaseImpl *parentStorage; -}; -typedef struct StorageInternalImpl StorageInternalImpl; +} StorageInternalImpl; -static const IStorageVtbl TransactedSnapshotImpl_Vtbl; -static const IStorageVtbl Storage32InternalImpl_Vtbl; - -/* Method definitions for the Storage32InternalImpl class. */ -static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage, - DWORD openFlags, DirRef storageDirEntry); -static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create); -static void StorageImpl_Destroy(StorageBaseImpl* iface); -static void StorageImpl_Invalidate(StorageBaseImpl* iface); -static HRESULT StorageImpl_Flush(StorageBaseImpl* iface); -static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read ); -static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer); -static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock); -static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This); -static void StorageImpl_SaveFileHeader(StorageImpl* This); -static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType); - -static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex); -static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This); -static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex); -static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex); -static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex); - -static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This); -static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This); -static ULONG BlockChainStream_GetCount(BlockChainStream* This); - -static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This); -static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This); -static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This, - ULONG blockIndex, ULONG offset, DWORD value); -static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This, - ULONG blockIndex, ULONG offset, DWORD* value); - -static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry); -static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry); +static const IStorageVtbl StorageInternalImpl_Vtbl; +static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef); typedef struct TransactedDirEntry { @@ -152,6 +107,7 @@ typedef struct TransactedDirEntry DirRef newTransactedParentEntry; } TransactedDirEntry; + /**************************************************************************** * Transacted storage object. */ @@ -179,6 +135,9 @@ typedef struct TransactedSnapshotImpl ULONG lastTransactionSig; } TransactedSnapshotImpl; +static const IStorageVtbl TransactedSnapshotImpl_Vtbl; +static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**); + typedef struct TransactedSharedImpl { struct StorageBaseImpl base; @@ -197,113 +156,705 @@ typedef struct TransactedSharedImpl ULONG lastTransactionSig; } TransactedSharedImpl; -/* Generic function to create a transacted wrapper for a direct storage object. */ -static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, BOOL toplevel, StorageBaseImpl** result); -/* OLESTREAM memory structure to use for Get and Put Routines */ -/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ -typedef struct -{ - DWORD dwOleID; - DWORD dwTypeID; - DWORD dwOleTypeNameLength; - CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; - CHAR *pstrOleObjFileName; - DWORD dwOleObjFileNameLength; - DWORD dwMetaFileWidth; - DWORD dwMetaFileHeight; - CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */ - DWORD dwDataLength; - BYTE *pData; -}OLECONVERT_OLESTREAM_DATA; - -/* CompObj Stream structure */ -/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ -typedef struct -{ - BYTE byUnknown1[12]; - CLSID clsid; - DWORD dwCLSIDNameLength; - CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN]; - DWORD dwOleTypeNameLength; - CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; - DWORD dwProgIDNameLength; - CHAR strProgIDName[OLESTREAM_MAX_STR_LEN]; - BYTE byUnknown2[16]; -}OLECONVERT_ISTORAGE_COMPOBJ; - - -/* Ole Presentation Stream structure */ -/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ -typedef struct -{ - BYTE byUnknown1[28]; - DWORD dwExtentX; - DWORD dwExtentY; - DWORD dwSize; - BYTE *pData; -}OLECONVERT_ISTORAGE_OLEPRES; - - - -/*********************************************************************** - * Forward declaration of internal functions used by the method DestroyElement - */ -static HRESULT deleteStorageContents( - StorageBaseImpl *parentStorage, - DirRef indexToDelete, - DirEntry entryDataToDelete); - -static HRESULT deleteStreamContents( - StorageBaseImpl *parentStorage, - DirRef indexToDelete, - DirEntry entryDataToDelete); - -static HRESULT removeFromTree( - StorageBaseImpl *This, - DirRef parentStorageIndex, - DirRef deletedIndex); - -/*********************************************************************** - * Declaration of the functions used to manipulate DirEntry +/**************************************************************************** + * 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. */ -static HRESULT insertIntoTree( - StorageBaseImpl *This, - DirRef parentStorageIndex, - DirRef newEntryIndex); +struct BlockChainRun +{ + /* This represents a range of blocks that happen reside in consecutive sectors. */ + ULONG firstSector; + ULONG firstOffset; + ULONG lastOffset; +}; -static LONG entryNameCmp( - const OLECHAR *name1, - const OLECHAR *name2); +typedef struct BlockChainBlock +{ + ULONG index; + ULONG sector; + BOOL read; + BOOL dirty; + BYTE data[MAX_BIG_BLOCK_SIZE]; +} BlockChainBlock; -static DirRef findElement( - StorageBaseImpl *storage, - DirRef storageEntry, - const OLECHAR *name, - DirEntry *data); +struct BlockChainStream +{ + StorageImpl* parentStorage; + ULONG* headOfStreamPlaceHolder; + DirRef ownerDirEntry; + struct BlockChainRun* indexCache; + ULONG indexCacheLen; + ULONG indexCacheSize; + BlockChainBlock cachedBlocks[2]; + ULONG blockToEvict; + ULONG tailIndex; + ULONG numBlocks; +}; -static HRESULT findTreeParent( - StorageBaseImpl *storage, - DirRef storageEntry, - const OLECHAR *childName, - DirEntry *parentData, - DirRef *parentEntry, - ULONG *relation); - -/*********************************************************************** - * Declaration of miscellaneous functions... +/* 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 HRESULT validateSTGM(DWORD stgmValue); +static inline ULONG BlockChainStream_GetCount(BlockChainStream* This) +{ + return This->numBlocks; +} -static DWORD GetShareModeFromSTGM(DWORD stgm); -static DWORD GetAccessModeFromSTGM(DWORD stgm); -static DWORD GetCreationModeFromSTGM(DWORD stgm); - -extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; +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 %08x\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)->(%d): 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 = toupperW(*name1++) - toupperW(*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. @@ -326,44 +877,304 @@ 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); + + 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); + return S_OK; + } + + 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]; + + 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)); + } + + 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; + + if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) + return E_INVALIDARG; + + if (This->parentStorage->reverted) + 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) + break; + + /* + * Read the entry from the storage. + */ + StorageBaseImpl_ReadDirEntry(This->parentStorage, + currentSearchNode, + ¤tEntry); + + /* + * Copy the information to the return buffer. + */ + StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, + currentReturnStruct, + ¤tEntry, + STATFLAG_DEFAULT); + + /* + * Step to the next item in the iteration + */ + (*pceltFetched)++; + currentReturnStruct++; + } + + if (SUCCEEDED(hr) && *pceltFetched != celt) + hr = S_FALSE; + + 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; + + if (This->parentStorage->reverted) + 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; + + return hr; +} + +static HRESULT WINAPI IEnumSTATSTGImpl_Reset( + IEnumSTATSTG* iface) +{ + IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); + + if (This->parentStorage->reverted) + 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; + + if (This->parentStorage->reverted) + 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; +} -static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry); -static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This); /************************************************************************ -** Block Functions -*/ + * StorageBaseImpl implementation + ***********************************************************************/ -static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index) +static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface ) { - return (ULONGLONG)(index+1) * This->bigBlockSize; + return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface); } /************************************************************************ -** Storage32BaseImpl 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); -} - -/************************************************************************ - * Storage32BaseImpl_QueryInterface (IUnknown) + * StorageBaseImpl_QueryInterface (IUnknown) * - * This method implements the common QueryInterface for all IStorage32 + * This method implements the common QueryInterface for all IStorage * implementations contained in this file. * * See Windows documentation for more details on IUnknown methods. @@ -403,9 +1214,9 @@ static HRESULT WINAPI StorageBaseImpl_QueryInterface( } /************************************************************************ - * Storage32BaseImpl_AddRef (IUnknown) + * StorageBaseImpl_AddRef (IUnknown) * - * This method implements the common AddRef for all IStorage32 + * This method implements the common AddRef for all IStorage * implementations contained in this file. * * See Windows documentation for more details on IUnknown methods. @@ -422,9 +1233,9 @@ static ULONG WINAPI StorageBaseImpl_AddRef( } /************************************************************************ - * Storage32BaseImpl_Release (IUnknown) + * StorageBaseImpl_Release (IUnknown) * - * This method implements the common Release for all IStorage32 + * This method implements the common Release for all IStorage * implementations contained in this file. * * See Windows documentation for more details on IUnknown methods. @@ -451,8 +1262,171 @@ static ULONG WINAPI StorageBaseImpl_Release( 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 ( lstrcmpW(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 ); + + return hr; +} + +static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) +{ + StgStreamImpl *strm; + + 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; + + LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) + { + if (childstg->base.storageDirEntry == storageEntry) + { + return TRUE; + } + } + + return FALSE; +} + /************************************************************************ - * Storage32BaseImpl_OpenStream (IStorage) + * StorageBaseImpl_OpenStream (IStorage) * * This method will open the specified stream object from the current storage. * @@ -566,7 +1540,7 @@ end: } /************************************************************************ - * Storage32BaseImpl_OpenStorage (IStorage) + * StorageBaseImpl_OpenStorage (IStorage) * * This method will open a new storage object from the current storage. * @@ -703,7 +1677,7 @@ end: } /************************************************************************ - * Storage32BaseImpl_EnumElements (IStorage) + * 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. @@ -743,7 +1717,7 @@ static HRESULT WINAPI StorageBaseImpl_EnumElements( } /************************************************************************ - * Storage32BaseImpl_Stat (IStorage) + * StorageBaseImpl_Stat (IStorage) * * This method will retrieve information about this storage object. * @@ -800,7 +1774,7 @@ end: } /************************************************************************ - * Storage32BaseImpl_RenameElement (IStorage) + * StorageBaseImpl_RenameElement (IStorage) * * This method will rename the specified element. * @@ -881,7 +1855,7 @@ static HRESULT WINAPI StorageBaseImpl_RenameElement( } /************************************************************************ - * Storage32BaseImpl_CreateStream (IStorage) + * StorageBaseImpl_CreateStream (IStorage) * * This method will create a stream object within this storage * @@ -917,7 +1891,7 @@ static HRESULT WINAPI StorageBaseImpl_CreateStream( if ( FAILED( validateSTGM(grfMode) )) return STG_E_INVALIDFLAG; - if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) + if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) return STG_E_INVALIDFLAG; if (This->reverted) @@ -1035,7 +2009,7 @@ static HRESULT WINAPI StorageBaseImpl_CreateStream( } /************************************************************************ - * Storage32BaseImpl_SetClass (IStorage) + * StorageBaseImpl_SetClass (IStorage) * * This method will write the specified CLSID in the directory entry of this * storage. @@ -1074,11 +2048,7 @@ static HRESULT WINAPI StorageBaseImpl_SetClass( } /************************************************************************ -** Storage32Impl implementation -*/ - -/************************************************************************ - * Storage32BaseImpl_CreateStorage (IStorage) + * StorageBaseImpl_CreateStorage (IStorage) * * This method will create the storage object within the provided storage. * @@ -1232,6 +2202,1038 @@ static HRESULT WINAPI StorageBaseImpl_CreateStorage( 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 ); + + 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, %d, %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 ( lstrcmpW(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 %u): 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 %d)\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; + + /* 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) + { + return hr; + } + + /* + * Enumerate the elements + */ + hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements); + if (FAILED(hr)) + { + IStorage_Release(childStorage); + 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); + + 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.u.HighPart = 0; + size.u.LowPart = 0; + + hr = IStorage_OpenStream(&parentStorage->IStorage_iface, + entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); + + if (hr!=S_OK) + { + return(hr); + } + + /* + * Zap the stream + */ + hr = IStream_SetSize(pis, size); + + if(hr != S_OK) + { + return hr; + } + + /* + * Release the stream object. + */ + IStream_Release(pis); + + 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 allow to reinvoke DestroyElement whenever we encounter a + * storage object. The optimisation resides in the usage of another + * enumeration strategy that would give all the leaves of a storage + * first. (postfix order) + */ +static HRESULT WINAPI StorageBaseImpl_DestroyElement( + IStorage* iface, + const OLECHAR *pwcsName)/* [string][in] */ +{ + StorageBaseImpl *This = impl_from_IStorage(iface); + + 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 ) + { + 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) + 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); + + 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.u.HighPart = 0; + offset.u.LowPart = 0; + hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); + if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) + hr = STG_E_FILENOTFOUND; + + /* + * 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=0x%x, smallblock=0x%x, sblimit=0x%x\n", + This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); + hr = STG_E_INVALIDHEADER; + } + else + hr = S_OK; + } + + return hr; +} + +/****************************************************************************** + * StorageImpl_SaveFileHeader + * + * This method will save to the file the header + */ +static void StorageImpl_SaveFileHeader( + StorageImpl* This) +{ + BYTE headerBigBlock[HEADER_SIZE]; + int index; + HRESULT hr; + ULARGE_INTEGER offset; + DWORD bytes_read, bytes_written; + DWORD major_version, dirsectorcount; + + /* + * Get a pointer to the big block of data containing the header. + */ + offset.u.HighPart = 0; + offset.u.LowPart = 0; + hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); + if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) + hr = STG_E_FILENOTFOUND; + + if (This->bigBlockSizeBits == 0x9) + major_version = 3; + else if (This->bigBlockSizeBits == 0xc) + major_version = 4; + else + { + ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits); + major_version = 4; + } + + /* + * If the block read failed, the file is probably new. + */ + if (FAILED(hr)) + { + /* + * Initialize for all unknown fields. + */ + memset(headerBigBlock, 0, HEADER_SIZE); + + /* + * Initialize the magic number. + */ + 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])); + } + + /* + * Write the big block back to the file. + */ + 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.u.LowPart); + + StorageUtl_WriteDWord( + buffer, + OFFSET_PS_SIZE_HIGH, + newData->size.u.HighPart); +} /*************************************************************************** * @@ -1345,1005 +3347,900 @@ static HRESULT StorageImpl_CreateDirEntry( return hr; } -/*************************************************************************** - * - * 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); -} - - -/**************************************************************************** - * - * 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 = toupperW(*name1++) - toupperW(*name2++); - } - - return diff; -} - -/**************************************************************************** - * - * 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 - * - * 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 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 ( lstrcmpW(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 ); - - return hr; -} - -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 ); - - 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, %d, %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 == &Storage32InternalImpl_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 ( lstrcmpW(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 %u): 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 StorageImpl_Commit( - IStorage* iface, - DWORD grfCommitFlags)/* [in] */ -{ - StorageBaseImpl* This = impl_from_IStorage(iface); - TRACE("(%p %d)\n", iface, grfCommitFlags); - return StorageBaseImpl_Flush(This); -} - -/************************************************************************* - * Revert (IStorage) - * - * Discard all changes that have been made since the last commit operation - */ -static HRESULT WINAPI StorageImpl_Revert( - IStorage* iface) -{ - TRACE("(%p)\n", iface); - return S_OK; -} - -/************************************************************************* - * DestroyElement (IStorage) - * - * Strategy: This implementation is built this way for simplicity not for speed. - * I always delete the topmost element of the enumeration and adjust - * the deleted element pointer all the time. This takes longer to - * do but allow to reinvoke DestroyElement whenever we encounter a - * storage object. The optimisation resides in the usage of another - * enumeration strategy that would give all the leaves of a storage - * first. (postfix order) - */ -static HRESULT WINAPI StorageBaseImpl_DestroyElement( - IStorage* iface, - const OLECHAR *pwcsName)/* [string][in] */ -{ - StorageBaseImpl *This = impl_from_IStorage(iface); - - 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 ) - { - 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) - 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); - - return hr; -} - - /****************************************************************************** - * Internal stream list handlers + * StorageImpl_ReadDirEntry + * + * This method will read the specified directory entry. */ - -void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) +static HRESULT StorageImpl_ReadDirEntry( + StorageImpl* This, + DirRef index, + DirEntry* buffer) { - TRACE("Stream added (stg=%p strm=%p)\n", stg, strm); - list_add_tail(&stg->strmHead,&strm->StrmListEntry); -} + BYTE currentEntry[RAW_DIRENTRY_SIZE]; + HRESULT readRes; -void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) -{ - TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm); - list_remove(&(strm->StrmListEntry)); -} + readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry); -static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) -{ - StgStreamImpl *strm; - - LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry) + if (SUCCEEDED(readRes)) { - if (strm->dirEntry == streamEntry) - { - return TRUE; - } + 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.u.LowPart); + + StorageUtl_ReadDWord( + currentEntry, + OFFSET_PS_SIZE_HIGH, + &buffer->size.u.HighPart); } - return FALSE; + return readRes; } -static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry) -{ - StorageInternalImpl *childstg; - - LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) - { - if (childstg->base.storageDirEntry == storageEntry) - { - return TRUE; - } - } - - return FALSE; -} - -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; - } -} - - /********************************************************************* - * - * Internal Method - * - * Delete the contents of a storage entry. - * + * Write the specified directory entry to the file */ -static HRESULT deleteStorageContents( - StorageBaseImpl *parentStorage, - DirRef indexToDelete, - DirEntry entryDataToDelete) +static HRESULT StorageImpl_WriteDirEntry( + StorageImpl* This, + DirRef index, + const DirEntry* buffer) { - IEnumSTATSTG *elements = 0; - IStorage *childStorage = 0; - STATSTG currentElement; - HRESULT hr; - HRESULT destroyHr = S_OK; - StorageInternalImpl *stg, *stg2; + BYTE currentEntry[RAW_DIRENTRY_SIZE]; - /* Invalidate any open storage objects. */ - LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry) + 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) { - if (stg->base.storageDirEntry == indexToDelete) - { - StorageBaseImpl_Invalidate(&stg->base); - } + /* File ends during this block; fill the rest with 0's. */ + memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read); } - /* - * Open the storage and enumerate it - */ - hr = IStorage_OpenStorage( - &parentStorage->IStorage_iface, - entryDataToDelete.name, - 0, - STGM_WRITE | STGM_SHARE_EXCLUSIVE, - 0, - 0, - &childStorage); + if (out_read) *out_read = read; - if (hr != S_OK) - { - return hr; - } + 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; /* - * Enumerate the elements + * 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. */ - IStorage_EnumElements( childStorage, 0, 0, 0, &elements); + 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.u.LowPart = 0; + offset.u.HighPart = 0; + cbTotalRead.QuadPart = 0; + + buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); do { - /* - * Obtain the next element - */ - hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL); - if (hr==S_OK) + resRead = SmallBlockChainStream_ReadAt(*ppsbChain, + offset, + min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart), + buffer, + &cbRead); + if (FAILED(resRead)) + break; + + if (cbRead > 0) { - destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName); + cbTotalRead.QuadPart += cbRead; - CoTaskMemFree(currentElement.pwcsName); + resWrite = BlockChainStream_WriteAt(bbTempChain, + offset, + cbRead, + buffer, + &cbWritten); + + if (FAILED(resWrite)) + break; + + offset.u.LowPart += cbRead; } - - /* - * 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); - - return destroyHr; -} - -/********************************************************************* - * - * Internal Method - * - * 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) + else { - TRACE("Stream deleted %p\n", strm); - strm->parentStorage = NULL; - list_remove(&strm->StrmListEntry); + resRead = STG_E_READFAULT; + break; } - } + } while (cbTotalRead.QuadPart < size.QuadPart); + HeapFree(GetProcessHeap(),0,buffer); size.u.HighPart = 0; - size.u.LowPart = 0; + size.u.LowPart = 0; - hr = IStorage_OpenStream(&parentStorage->IStorage_iface, - entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); - - if (hr!=S_OK) + if (FAILED(resRead) || FAILED(resWrite)) { - return(hr); + ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite); + BlockChainStream_SetSize(bbTempChain, size); + BlockChainStream_Destroy(bbTempChain); + return NULL; } /* - * Zap the stream + * Destroy the small block chain. */ - hr = IStream_SetSize(pis, size); - - if(hr != S_OK) - { - return hr; - } + streamEntryRef = (*ppsbChain)->ownerDirEntry; + SmallBlockChainStream_SetSize(*ppsbChain, size); + SmallBlockChainStream_Destroy(*ppsbChain); + *ppsbChain = 0; /* - * Release the stream object. + * Change the directory entry. This chain is now a big block chain + * and it doesn't reside in the small blocks chain anymore. */ - IStream_Release(pis); + StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); - return S_OK; + 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; } -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); - } -} - -/************************************************************************* +/****************************************************************************** + * Storage32Impl_BigBlocksToSmallBlocks * - * Internal Method - * - * This method removes a directory entry from its parent storage tree without - * freeing any resources attached to it. + * This method will convert a big block chain to a small block chain. + * The big block chain will be destroyed on success. */ -static HRESULT removeFromTree( - StorageBaseImpl *This, - DirRef parentStorageIndex, - DirRef deletedIndex) +static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( + StorageImpl* This, + BlockChainStream** ppbbChain, + ULARGE_INTEGER newSize) { - DirEntry entryToDelete; - DirEntry parentEntry; - DirRef parentEntryRef; - ULONG typeOfRelation; - HRESULT hr; + 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; - hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete); + TRACE("%p %p\n", This, ppbbChain); - if (hr != S_OK) - return hr; + 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.u.HighPart = 0; + offset.u.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.u.LowPart - offset.u.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.u.LowPart += cbRead; + } + else + { + resRead = STG_E_READFAULT; + break; + } + } + HeapFree(GetProcessHeap(), 0, buffer); + + size.u.HighPart = 0; + size.u.LowPart = 0; + + if(FAILED(resRead) || FAILED(resWrite)) + { + ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\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; /* - * Find the element that links to the one we want to delete. + * Initialize blocks as free */ - hr = findTreeParent(This, parentStorageIndex, entryToDelete.name, - &parentEntry, &parentEntryRef, &typeOfRelation); + memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize); - if (hr != S_OK) - return hr; + /* Reserve the range lock sector */ + if (depotIndex == rangeLockDepot) + { + ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN; + } - if (entryToDelete.leftChild != DIRENTRY_NULL) + 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)) { /* - * Replace the deleted entry with its left child + * The first extended block. */ - 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; - } - } + This->extBigBlockDepotStart = index; } else { /* - * Replace the deleted entry with its right child + * Find the last existing extended block. */ - setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild); + nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1]; - hr = StorageBaseImpl_WriteDirEntry( - This, - parentEntryRef, - &parentEntry); - if(FAILED(hr)) + /* + * 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 %d, bigBlockDepotCount %d\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) { - return hr; + 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; } } - return hr; -} + *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; - -/****************************************************************************** - * 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) + * 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 HRESULT WINAPI StorageBaseImpl_SetStateBits( - IStorage* iface, - DWORD grfStateBits,/* [in] */ - DWORD grfMask) /* [in] */ +static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) { - StorageBaseImpl *This = impl_from_IStorage(iface); + ULONG nextBlockIndex = BLOCK_SPECIAL; + ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); - if (This->reverted) - return STG_E_REVERTED; + StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, + &nextBlockIndex); - This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask); - return S_OK; + 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 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; + + 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) { @@ -2642,359 +4539,6 @@ static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst, 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.u.HighPart = 0; - offset.u.LowPart = OFFSET_TRANSACTIONSIG; - hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read); - - if (SUCCEEDED(hr)) - { - StorageUtl_ReadDWord(data, 0, &This->transactionSig); - - if (oldTransactionSig != This->transactionSig) - { - /* Someone else wrote to this, so toss all cached information. */ - TRACE("signature changed\n"); - - hr = StorageImpl_Refresh(This, FALSE, FALSE); - } - - if (FAILED(hr)) - This->transactionSig = oldTransactionSig; - } - } - - *result = This->transactionSig; - - return hr; -} - -static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base, - ULONG value) -{ - StorageImpl *This = (StorageImpl*)base; - - This->transactionSig = value; - StorageImpl_SaveFileHeader(This); - - return S_OK; -} - -static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write) -{ - 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); - - if (hr == STG_E_INVALIDFUNCTION) - hr = S_OK; - - 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 = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - - if (hr == STG_E_INVALIDFUNCTION) - hr = S_OK; - - 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 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)->(%d): 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 -}; - -/* - * Virtual function table for the IStorage32Impl class. - */ -static const IStorageVtbl Storage32Impl_Vtbl = -{ - StorageBaseImpl_QueryInterface, - StorageBaseImpl_AddRef, - StorageBaseImpl_Release, - StorageBaseImpl_CreateStream, - StorageBaseImpl_OpenStream, - StorageBaseImpl_CreateStorage, - StorageBaseImpl_OpenStorage, - StorageBaseImpl_CopyTo, - StorageBaseImpl_MoveElementTo, - StorageImpl_Commit, - StorageImpl_Revert, - StorageBaseImpl_EnumElements, - StorageBaseImpl_DestroyElement, - StorageBaseImpl_RenameElement, - StorageBaseImpl_SetElementTimes, - StorageBaseImpl_SetClass, - StorageBaseImpl_SetStateBits, - StorageBaseImpl_Stat -}; - -static const StorageBaseImplVtbl StorageImpl_BaseVtbl = -{ - 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 -}; - -static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, - ULARGE_INTEGER cb, DWORD dwLockType) -{ - HRESULT hr; - int delay = 0; - - /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */ - if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb))) - return S_OK; - - /* otherwise we have to fake it based on an async lock */ - do - { - hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); - - if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) - { - Sleep(delay); - if (delay < 150) delay++; - } - } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION); - - 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 = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - - if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) - return fail_hr; - else - return S_OK; -} - -static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end) -{ - HRESULT hr=S_OK; - int i, j; - ULARGE_INTEGER offset, cb; - - cb.QuadPart = 1; - - for (i=start; i<=end; i++) - { - offset.QuadPart = i; - hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION) - break; - } - - if (SUCCEEDED(hr)) - { - for (j=0; jlocked_bytes)/sizeof(This->locked_bytes[0]); 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); - - 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); - - /* If the ILockBytes doesn't support locking that's ok. */ - if (FAILED(hr)) return S_OK; - - 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); - - /* 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; - ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - - return hr; -} - static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create) { HRESULT hr=S_OK; @@ -3027,9 +4571,9 @@ static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL crea This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK; This->smallBlockDepotStart = BLOCK_END_OF_CHAIN; if (This->bigBlockSize == 4096) - This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS; + This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS; else - This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS; + This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS; This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS; This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN; This->extBigBlockDepotCount = 0; @@ -3210,78 +4754,341 @@ static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL crea return hr; } -static HRESULT StorageImpl_Construct( - HANDLE hFile, - LPCOLESTR pwcsName, - ILockBytes* pLkbyt, - DWORD openFlags, - BOOL fileBased, - BOOL create, - ULONG sector_size, - StorageImpl** result) +static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base, + ULONG* result, BOOL refresh) { - StorageImpl* This; - HRESULT hr = S_OK; + StorageImpl *This = (StorageImpl*)base; + HRESULT hr=S_OK; + DWORD oldTransactionSig = This->transactionSig; - if ( FAILED( validateSTGM(openFlags) )) - return STG_E_INVALIDFLAG; + if (refresh) + { + ULARGE_INTEGER offset; + ULONG bytes_read; + BYTE data[4]; - This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl)); - if (!This) - return E_OUTOFMEMORY; + offset.u.HighPart = 0; + offset.u.LowPart = OFFSET_TRANSACTIONSIG; + hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read); - memset(This, 0, sizeof(StorageImpl)); + if (SUCCEEDED(hr)) + { + StorageUtl_ReadDWord(data, 0, &This->transactionSig); - list_init(&This->base.strmHead); + if (oldTransactionSig != This->transactionSig) + { + /* Someone else wrote to this, so toss all cached information. */ + TRACE("signature changed\n"); - list_init(&This->base.storageHead); + hr = StorageImpl_Refresh(This, FALSE, FALSE); + } - This->base.IStorage_iface.lpVtbl = &Storage32Impl_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 (FAILED(hr)) + This->transactionSig = oldTransactionSig; + } + } - 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; + *result = This->transactionSig; - This->base.reverted = FALSE; + return hr; +} - /* - * Initialize the big block cache. - */ - This->bigBlockSize = sector_size; - This->smallBlockSize = DEF_SMALL_BLOCK_SIZE; - if (hFile) - hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes); +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 { - This->lockBytes = pLkbyt; - ILockBytes_AddRef(pLkbyt); + 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; jlocked_bytes)/sizeof(This->locked_bytes[0]); 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 = StorageImpl_GrabLocks(This, openFlags); + hr = BlockChainStream_Flush(This->rootBlockChain); if (SUCCEEDED(hr)) - hr = StorageImpl_Refresh(This, TRUE, create); + hr = BlockChainStream_Flush(This->smallBlockDepotChain); - if (FAILED(hr)) - { - IStorage_Release(&This->base.IStorage_iface); - *result = NULL; - } - else - { - StorageImpl_Flush(&This->base); - *result = This; - } + for (i=0; SUCCEEDED(hr) && iblockChainCache[i]) + hr = BlockChainStream_Flush(This->blockChainCache[i]); + + if (SUCCEEDED(hr)) + hr = ILockBytes_Flush(This->lockBytes); return hr; } @@ -3321,7 +5128,7 @@ static void StorageImpl_Destroy(StorageBaseImpl* iface) if (This->locked_bytes[i] != 0) { offset.QuadPart = This->locked_bytes[i]; - ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); + StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); } } @@ -3330,1452 +5137,397 @@ static void StorageImpl_Destroy(StorageBaseImpl* iface) HeapFree(GetProcessHeap(), 0, This); } -static HRESULT StorageImpl_Flush(StorageBaseImpl *storage) + +static const StorageBaseImplVtbl StorageImpl_BaseVtbl = { - StorageImpl *This = (StorageImpl*)storage; - int i; - HRESULT hr; - TRACE("(%p)\n", This); + StorageImpl_Destroy, + StorageImpl_Invalidate, + StorageImpl_Flush, + StorageImpl_GetFilename, + StorageImpl_CreateDirEntry, + StorageImpl_BaseWriteDirEntry, + StorageImpl_BaseReadDirEntry, + StorageImpl_DestroyDirEntry, + StorageImpl_StreamReadAt, + StorageImpl_StreamWriteAt, + StorageImpl_StreamSetSize, + StorageImpl_StreamLink, + StorageImpl_GetTransactionSig, + StorageImpl_SetTransactionSig, + StorageImpl_LockTransaction, + StorageImpl_UnlockTransaction +}; - hr = BlockChainStream_Flush(This->smallBlockRootChain); + +/* + * 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 = BlockChainStream_Flush(This->rootBlockChain); + hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME); if (SUCCEEDED(hr)) - hr = BlockChainStream_Flush(This->smallBlockDepotChain); + { + 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; - for (i=0; SUCCEEDED(hr) && iblockChainCache[i]) - hr = BlockChainStream_Flush(This->blockChainCache[i]); + hr = StorageImpl_GrabLocks(This, openFlags); + } if (SUCCEEDED(hr)) - hr = ILockBytes_Flush(This->lockBytes); + 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; } -/****************************************************************************** - * Storage32Impl_GetNextFreeBigBlock - * - * Returns the index of the next free big block. - * If the big block depot is filled, this method will enlarge it. - * - */ -static ULONG StorageImpl_GetNextFreeBigBlock( - StorageImpl* This) -{ - ULONG depotBlockIndexPos; - BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; - ULONG depotBlockOffset; - ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); - ULONG nextBlockIndex = BLOCK_SPECIAL; - int depotIndex = 0; - ULONG freeBlock = BLOCK_UNUSED; - ULONG read; - ULARGE_INTEGER neededSize; - STATSTG statstg; - - 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; - - ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME); - - if (neededSize.QuadPart > statstg.cbSize.QuadPart) - ILockBytes_SetSize(This->lockBytes, neededSize); - - This->prevFreeBlock = freeBlock; - - return freeBlock; -} - -/****************************************************************************** - * 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; -} - -/****************************************************************************** - * Storage32Impl_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; -} /************************************************************************ - * Storage32Impl_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) + * StorageInternalImpl implementation + ***********************************************************************/ + +static void StorageInternalImpl_Invalidate( StorageBaseImpl *base ) { - 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; + StorageInternalImpl* This = (StorageInternalImpl*) base; - *nextBlockIndex = BLOCK_SPECIAL; - - if(depotBlockCount >= This->bigBlockDepotCount) + if (!This->base.reverted) { - WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount, - This->bigBlockDepotCount); - return STG_E_READFAULT; + TRACE("Storage invalidated (stg=%p)\n", This); + + This->base.reverted = TRUE; + + This->parentStorage = NULL; + + StorageBaseImpl_DeleteAll(&This->base); + + list_remove(&This->ParentListEntry); } +} - /* - * Cache the currently accessed depot block. - */ - if (depotBlockCount != This->indexBlockDepotCached) - { - This->indexBlockDepotCached = depotBlockCount; +static void StorageInternalImpl_Destroy( StorageBaseImpl *iface) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; - if (depotBlockCount < COUNT_BBDEPOTINHEADER) - { - depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; - } - else - { - /* - * We have to look in the extended depot. - */ - depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); - } + StorageInternalImpl_Invalidate(&This->base); - StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); + HeapFree(GetProcessHeap(), 0, This); +} - if (!read) - return STG_E_READFAULT; +static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; - num_blocks = This->bigBlockSize / 4; + return StorageBaseImpl_Flush(This->parentStorage); +} - for (index = 0; index < num_blocks; index++) - { - StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex); - This->blockDepotCached[index] = *nextBlockIndex; - } - } +static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) +{ + StorageInternalImpl* This = (StorageInternalImpl*) iface; - *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; + 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,%x)\n", iface, grfCommitFlags); + return StorageBaseImpl_Flush(This); +} + +/****************************************************************************** +** +** StorageInternalImpl_Revert +** +*/ +static HRESULT WINAPI StorageInternalImpl_Revert( + IStorage* iface) +{ + FIXME("(%p): stub\n", iface); 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. +/* + * Virtual function table for the StorageInternalImpl class. */ -static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) +static const IStorageVtbl StorageInternalImpl_Vtbl = { - ULONG nextBlockIndex = BLOCK_SPECIAL; - ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); + 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 +}; - StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, - &nextBlockIndex); - - return nextBlockIndex; -} - -/****************************************************************************** - * Storage32Impl_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 - * - * Storage32Impl_SetNextBlockInChain(This, 3, 1); - * Storage32Impl_SetNextBlockInChain(This, 1, 7); - * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN); - * - */ -static void StorageImpl_SetNextBlockInChain( - StorageImpl* This, - ULONG blockIndex, - ULONG nextBlock) +static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl = { - ULONG offsetInDepot = blockIndex * sizeof (ULONG); - ULONG depotBlockCount = offsetInDepot / This->bigBlockSize; - ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize; - ULONG depotBlockIndexPos; + 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 +}; - 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; - } -} - -/****************************************************************************** - * Storage32Impl_LoadFileHeader - * - * This method will read in the file header - */ -static HRESULT StorageImpl_LoadFileHeader( - StorageImpl* This) +static StorageInternalImpl* StorageInternalImpl_Construct( + StorageBaseImpl* parentStorage, + DWORD openFlags, + DirRef storageDirEntry) { - HRESULT hr; - BYTE headerBigBlock[HEADER_SIZE]; - int index; - ULARGE_INTEGER offset; - DWORD bytes_read; + StorageInternalImpl* newStorage; - TRACE("\n"); - /* - * Get a pointer to the big block of data containing the header. - */ - offset.u.HighPart = 0; - offset.u.LowPart = 0; - hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); - if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) - hr = STG_E_FILENOTFOUND; + newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl)); - /* - * Extract the information from the header. - */ - if (SUCCEEDED(hr)) + if (newStorage!=0) { - /* - * 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; - } + list_init(&newStorage->base.strmHead); - 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])); - } + list_init(&newStorage->base.storageHead); /* - * Make the bitwise arithmetic to get the size of the blocks in bytes. + * Initialize the virtual function table. */ - This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits; - This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits; + 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; /* - * Right now, the code is making some assumptions about the size of the - * blocks, just make sure they are what we're expecting. + * Keep a reference to the directory entry of this storage */ - if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) || - This->smallBlockSize != DEF_SMALL_BLOCK_SIZE || - This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK) - { - FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n", - This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); - hr = STG_E_INVALIDHEADER; - } - else - hr = S_OK; + newStorage->base.storageDirEntry = storageDirEntry; + + newStorage->base.create = FALSE; + + return newStorage; } - return hr; + return 0; } -/****************************************************************************** - * Storage32Impl_SaveFileHeader - * - * This method will save to the file the header - */ -static void StorageImpl_SaveFileHeader( - StorageImpl* This) -{ - BYTE headerBigBlock[HEADER_SIZE]; - int index; - HRESULT hr; - ULARGE_INTEGER offset; - DWORD bytes_read, bytes_written; - DWORD major_version, dirsectorcount; - /* - * Get a pointer to the big block of data containing the header. - */ - offset.u.HighPart = 0; - offset.u.LowPart = 0; - hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); - if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) - hr = STG_E_FILENOTFOUND; - - if (This->bigBlockSizeBits == 0x9) - major_version = 3; - else if (This->bigBlockSizeBits == 0xc) - major_version = 4; - else - { - ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits); - major_version = 4; - } - - /* - * If the block read failed, the file is probably new. - */ - if (FAILED(hr)) - { - /* - * Initialize for all unknown fields. - */ - memset(headerBigBlock, 0, HEADER_SIZE); - - /* - * Initialize the magic number. - */ - 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])); - } - - /* - * Write the big block back to the file. - */ - StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written); -} - -/****************************************************************************** - * StorageImpl_ReadRawDirEntry - * - * This method will read the raw data from a directory entry in the file. - * - * buffer must be RAW_DIRENTRY_SIZE bytes long. - */ -HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer) -{ - ULARGE_INTEGER offset; - HRESULT hr; - ULONG bytesRead; - - 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. - */ -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); -} - -/****************************************************************************** - * UpdateRawDirEntry - * - * Update raw directory entry data from the fields in newData. - * - * buffer must be RAW_DIRENTRY_SIZE bytes long. - */ -void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData) -{ - memset(buffer, 0, RAW_DIRENTRY_SIZE); - - memcpy( - buffer + OFFSET_PS_NAME, - newData->name, - DIRENTRY_NAME_BUFFER_LEN ); - - 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.u.LowPart); - - StorageUtl_WriteDWord( - buffer, - OFFSET_PS_SIZE_HIGH, - newData->size.u.HighPart); -} - -/****************************************************************************** - * Storage32Impl_ReadDirEntry - * - * This method will read the specified directory entry. - */ -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.u.LowPart); - - StorageUtl_ReadDWord( - currentEntry, - OFFSET_PS_SIZE_HIGH, - &buffer->size.u.HighPart); - } - - return readRes; -} - -/********************************************************************* - * Write the specified directory entry to the file - */ -HRESULT StorageImpl_WriteDirEntry( - StorageImpl* This, - DirRef index, - const DirEntry* buffer) -{ - BYTE currentEntry[RAW_DIRENTRY_SIZE]; - - UpdateRawDirEntry(currentEntry, buffer); - - return StorageImpl_WriteRawDirEntry(This, index, currentEntry); -} - -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. - */ -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.u.LowPart = 0; - offset.u.HighPart = 0; - cbTotalRead.QuadPart = 0; - - buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); - do - { - resRead = SmallBlockChainStream_ReadAt(*ppsbChain, - offset, - min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart), - buffer, - &cbRead); - if (FAILED(resRead)) - break; - - if (cbRead > 0) - { - cbTotalRead.QuadPart += cbRead; - - resWrite = BlockChainStream_WriteAt(bbTempChain, - offset, - cbRead, - buffer, - &cbWritten); - - if (FAILED(resWrite)) - break; - - offset.u.LowPart += cbRead; - } - else - { - resRead = STG_E_READFAULT; - break; - } - } while (cbTotalRead.QuadPart < size.QuadPart); - HeapFree(GetProcessHeap(),0,buffer); - - size.u.HighPart = 0; - size.u.LowPart = 0; - - if (FAILED(resRead) || FAILED(resWrite)) - { - ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\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. - */ -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.u.HighPart = 0; - offset.u.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.u.LowPart - offset.u.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.u.LowPart += cbRead; - } - else - { - resRead = STG_E_READFAULT; - break; - } - } - HeapFree(GetProcessHeap(), 0, buffer); - - size.u.HighPart = 0; - size.u.LowPart = 0; - - if(FAILED(resRead) || FAILED(resWrite)) - { - ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\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); -} - -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; -} +/************************************************************************ + * TransactedSnapshotImpl implementation + ***********************************************************************/ static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This) { @@ -5674,6 +6426,11 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, return E_OUTOFMEMORY; } + +/************************************************************************ + * TransactedSharedImpl implementation + ***********************************************************************/ + static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This) { if (!This->reverted) @@ -6066,547 +6823,10 @@ end: return hr; } -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; -} - -/****************************************************************************** -** -** Storage32InternalImpl_Commit -** -*/ -static HRESULT WINAPI StorageInternalImpl_Commit( - IStorage* iface, - DWORD grfCommitFlags) /* [in] */ -{ - StorageBaseImpl* This = impl_from_IStorage(iface); - TRACE("(%p,%x)\n", iface, grfCommitFlags); - return StorageBaseImpl_Flush(This); -} - -/****************************************************************************** -** -** Storage32InternalImpl_Revert -** -*/ -static HRESULT WINAPI StorageInternalImpl_Revert( - IStorage* iface) -{ - FIXME("(%p): stub\n", iface); - return S_OK; -} - -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); - - if (ppvObject==0) - return E_INVALIDARG; - - *ppvObject = 0; - - if (IsEqualGUID(&IID_IUnknown, riid) || - IsEqualGUID(&IID_IEnumSTATSTG, riid)) - { - *ppvObject = This; - IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface); - return S_OK; - } - - return E_NOINTERFACE; -} - -static ULONG WINAPI IEnumSTATSTGImpl_AddRef( - IEnumSTATSTG* iface) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - return InterlockedIncrement(&This->ref); -} - -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]; - - 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)); - } - - 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; - - if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) - return E_INVALIDARG; - - if (This->parentStorage->reverted) - 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) - break; - - /* - * Read the entry from the storage. - */ - StorageBaseImpl_ReadDirEntry(This->parentStorage, - currentSearchNode, - ¤tEntry); - - /* - * Copy the information to the return buffer. - */ - StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, - currentReturnStruct, - ¤tEntry, - STATFLAG_DEFAULT); - - /* - * Step to the next item in the iteration - */ - (*pceltFetched)++; - currentReturnStruct++; - } - - if (SUCCEEDED(hr) && *pceltFetched != celt) - hr = S_FALSE; - - 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; - - if (This->parentStorage->reverted) - 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; - - return hr; -} - -static HRESULT WINAPI IEnumSTATSTGImpl_Reset( - IEnumSTATSTG* iface) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - - if (This->parentStorage->reverted) - return STG_E_REVERTED; - - This->name[0] = 0; - - return S_OK; -} - -static HRESULT WINAPI IEnumSTATSTGImpl_Clone( - IEnumSTATSTG* iface, - IEnumSTATSTG** ppenum) -{ - IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); - IEnumSTATSTGImpl* newClone; - - if (This->parentStorage->reverted) - 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 -}; - -/****************************************************************************** -** IEnumSTATSTGImpl implementation -*/ - -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; -} - -/* - * Virtual function table for the Storage32InternalImpl class. - */ -static const IStorageVtbl Storage32InternalImpl_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 -}; - -/****************************************************************************** -** Storage32InternalImpl implementation -*/ - -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 = &Storage32InternalImpl_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; -} - -/****************************************************************************** -** StorageUtl implementation -*/ +/************************************************************************ + * StorageUtl helper functions + ***********************************************************************/ void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value) { @@ -6737,12 +6957,43 @@ void StorageUtl_CopyDirEntryToSTATSTG( destination->reserved = 0; } + +/************************************************************************ + * BlockChainStream implementation + ***********************************************************************/ + /****************************************************************************** -** BlockChainStream implementation -*/ + * BlockChainStream_GetHeadOfChain + * + * Returns the head of this stream chain. + * Some special chains don't have directory entries, their heads are kept in + * This->headOfStreamPlaceHolder. + * + */ +static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This) +{ + DirEntry chainEntry; + HRESULT hr; + + if (This->headOfStreamPlaceHolder != 0) + return *(This->headOfStreamPlaceHolder); + + if (This->ownerDirEntry != DIRENTRY_NULL) + { + hr = StorageImpl_ReadDirEntry( + This->parentStorage, + This->ownerDirEntry, + &chainEntry); + + if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL) + return chainEntry.startingBlock; + } + + return BLOCK_END_OF_CHAIN; +} /* Read and save the index of all blocks in this stream. */ -HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) +static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) { ULONG next_sector, next_offset; HRESULT hr; @@ -6819,7 +7070,7 @@ HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) } /* Locate the nth block in this stream. */ -ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) +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; @@ -6848,7 +7099,7 @@ ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; } -HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, +static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) { BlockChainBlock *result=NULL; @@ -6953,229 +7204,6 @@ void BlockChainStream_Destroy(BlockChainStream* This) HeapFree(GetProcessHeap(), 0, This); } -/****************************************************************************** - * BlockChainStream_GetHeadOfChain - * - * Returns the head of this stream chain. - * Some special chains don't have directory entries, their heads are kept in - * This->headOfStreamPlaceHolder. - * - */ -static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This) -{ - DirEntry chainEntry; - HRESULT hr; - - if (This->headOfStreamPlaceHolder != 0) - return *(This->headOfStreamPlaceHolder); - - if (This->ownerDirEntry != DIRENTRY_NULL) - { - hr = StorageImpl_ReadDirEntry( - This->parentStorage, - This->ownerDirEntry, - &chainEntry); - - if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL) - return chainEntry.startingBlock; - } - - return BLOCK_END_OF_CHAIN; -} - -/****************************************************************************** - * BlockChainStream_GetCount - * - * Returns the number of blocks that comprises this chain. - * This is not the size of the stream as the last block may not be full! - */ -static ULONG BlockChainStream_GetCount(BlockChainStream* This) -{ - return This->numBlocks; -} - -/****************************************************************************** - * BlockChainStream_ReadAt - * - * Reads a specified number of bytes from this chain at the specified offset. - * bytesRead may be NULL. - * Failure will be returned if the specified number of bytes has not been read. - */ -HRESULT BlockChainStream_ReadAt(BlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - void* buffer, - ULONG* bytesRead) -{ - ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize; - ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize; - ULONG bytesToReadInBuffer; - ULONG blockIndex; - BYTE* bufferWalker; - ULARGE_INTEGER stream_size; - HRESULT hr; - BlockChainBlock *cachedBlock; - - TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); - - /* - * Find the first block in the stream that contains part of the buffer. - */ - blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); - - *bytesRead = 0; - - stream_size = BlockChainStream_GetSize(This); - if (stream_size.QuadPart > offset.QuadPart) - size = min(stream_size.QuadPart - offset.QuadPart, size); - else - return S_OK; - - /* - * Start reading the buffer. - */ - bufferWalker = buffer; - - while (size > 0) - { - ULARGE_INTEGER ulOffset; - DWORD bytesReadAt; - - /* - * Calculate how many bytes we can copy from this big block. - */ - bytesToReadInBuffer = - min(This->parentStorage->bigBlockSize - offsetInBlock, size); - - hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer); - - if (FAILED(hr)) - return hr; - - if (!cachedBlock) - { - /* Not in cache, and we're going to read past the end of the block. */ - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + - offsetInBlock; - - StorageImpl_ReadAt(This->parentStorage, - ulOffset, - bufferWalker, - bytesToReadInBuffer, - &bytesReadAt); - } - else - { - if (!cachedBlock->read) - { - ULONG read; - if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) - return STG_E_READFAULT; - - cachedBlock->read = TRUE; - } - - memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer); - bytesReadAt = bytesToReadInBuffer; - } - - blockNoInSequence++; - bufferWalker += bytesReadAt; - size -= bytesReadAt; - *bytesRead += bytesReadAt; - offsetInBlock = 0; /* There is no offset on the next block */ - - if (bytesToReadInBuffer != bytesReadAt) - break; - } - - return S_OK; -} - -/****************************************************************************** - * BlockChainStream_WriteAt - * - * Writes the specified number of bytes to this chain at the specified offset. - * Will fail if not all specified number of bytes have been written. - */ -HRESULT BlockChainStream_WriteAt(BlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - const void* buffer, - ULONG* bytesWritten) -{ - ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize; - ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize; - ULONG bytesToWrite; - ULONG blockIndex; - const BYTE* bufferWalker; - HRESULT hr; - BlockChainBlock *cachedBlock; - - *bytesWritten = 0; - bufferWalker = buffer; - - while (size > 0) - { - ULARGE_INTEGER ulOffset; - DWORD bytesWrittenAt; - - /* - * Calculate how many bytes we can copy to this big block. - */ - bytesToWrite = - min(This->parentStorage->bigBlockSize - offsetInBlock, size); - - hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite); - - /* BlockChainStream_SetSize should have already been called to ensure we have - * enough blocks in the chain to write into */ - if (FAILED(hr)) - { - ERR("not enough blocks in chain to write data\n"); - return hr; - } - - if (!cachedBlock) - { - /* Not in cache, and we're going to write past the end of the block. */ - ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + - offsetInBlock; - - StorageImpl_WriteAt(This->parentStorage, - ulOffset, - bufferWalker, - bytesToWrite, - &bytesWrittenAt); - } - else - { - if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize) - { - ULONG read; - if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) - return STG_E_READFAULT; - } - - memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite); - bytesWrittenAt = bytesToWrite; - cachedBlock->read = TRUE; - cachedBlock->dirty = TRUE; - } - - blockNoInSequence++; - bufferWalker += bytesWrittenAt; - size -= bytesWrittenAt; - *bytesWritten += bytesWrittenAt; - offsetInBlock = 0; /* There is no offset on the next block */ - - if (bytesWrittenAt != bytesToWrite) - break; - } - - return (size == 0) ? S_OK : STG_E_WRITEFAULT; -} - /****************************************************************************** * BlockChainStream_Shrink * @@ -7383,36 +7411,6 @@ static BOOL BlockChainStream_Enlarge(BlockChainStream* This, return TRUE; } -/****************************************************************************** - * BlockChainStream_SetSize - * - * Sets the size of this stream. The big block depot will be updated. - * The file will grow if we grow the chain. - * - * TODO: Free the actual blocks in the file when we shrink the chain. - * Currently, the blocks are still in the file. So the file size - * doesn't shrink even if we shrink streams. - */ -BOOL BlockChainStream_SetSize( - BlockChainStream* This, - ULARGE_INTEGER newSize) -{ - ULARGE_INTEGER size = BlockChainStream_GetSize(This); - - if (newSize.QuadPart == size.QuadPart) - return TRUE; - - if (newSize.QuadPart < size.QuadPart) - { - BlockChainStream_Shrink(This, newSize); - } - else - { - BlockChainStream_Enlarge(This, newSize); - } - - return TRUE; -} /****************************************************************************** * BlockChainStream_GetSize @@ -7453,8 +7451,222 @@ static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This) } /****************************************************************************** -** SmallBlockChainStream implementation -*/ + * 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)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); + + /* + * Find the first block in the stream that contains part of the buffer. + */ + blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); + + *bytesRead = 0; + + stream_size = BlockChainStream_GetSize(This); + if (stream_size.QuadPart > offset.QuadPart) + size = min(stream_size.QuadPart - offset.QuadPart, size); + else + return S_OK; + + /* + * Start reading the buffer. + */ + bufferWalker = buffer; + + while (size > 0) + { + ULARGE_INTEGER ulOffset; + DWORD bytesReadAt; + + /* + * Calculate how many bytes we can copy from this big block. + */ + bytesToReadInBuffer = + min(This->parentStorage->bigBlockSize - offsetInBlock, size); + + hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer); + + if (FAILED(hr)) + return hr; + + if (!cachedBlock) + { + /* Not in cache, and we're going to read past the end of the block. */ + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + + offsetInBlock; + + StorageImpl_ReadAt(This->parentStorage, + ulOffset, + bufferWalker, + bytesToReadInBuffer, + &bytesReadAt); + } + else + { + if (!cachedBlock->read) + { + ULONG read; + if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) + return STG_E_READFAULT; + + cachedBlock->read = TRUE; + } + + memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer); + bytesReadAt = bytesToReadInBuffer; + } + + blockNoInSequence++; + bufferWalker += bytesReadAt; + size -= bytesReadAt; + *bytesRead += bytesReadAt; + offsetInBlock = 0; /* There is no offset on the next block */ + + if (bytesToReadInBuffer != bytesReadAt) + break; + } + + return S_OK; +} + +/****************************************************************************** + * BlockChainStream_WriteAt + * + * Writes the specified number of bytes to this chain at the specified offset. + * Will fail if not all specified number of bytes have been written. + */ +HRESULT BlockChainStream_WriteAt(BlockChainStream* This, + ULARGE_INTEGER offset, + ULONG size, + const void* buffer, + ULONG* bytesWritten) +{ + ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize; + ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize; + ULONG bytesToWrite; + ULONG blockIndex; + const BYTE* bufferWalker; + HRESULT hr; + BlockChainBlock *cachedBlock; + + *bytesWritten = 0; + bufferWalker = buffer; + + while (size > 0) + { + ULARGE_INTEGER ulOffset; + DWORD bytesWrittenAt; + + /* + * Calculate how many bytes we can copy to this big block. + */ + bytesToWrite = + min(This->parentStorage->bigBlockSize - offsetInBlock, size); + + hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite); + + /* BlockChainStream_SetSize should have already been called to ensure we have + * enough blocks in the chain to write into */ + if (FAILED(hr)) + { + ERR("not enough blocks in chain to write data\n"); + return hr; + } + + if (!cachedBlock) + { + /* Not in cache, and we're going to write past the end of the block. */ + ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + + offsetInBlock; + + StorageImpl_WriteAt(This->parentStorage, + ulOffset, + bufferWalker, + bytesToWrite, + &bytesWrittenAt); + } + else + { + if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize) + { + ULONG read; + if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) + return STG_E_READFAULT; + } + + memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite); + bytesWrittenAt = bytesToWrite; + cachedBlock->read = TRUE; + cachedBlock->dirty = TRUE; + } + + blockNoInSequence++; + bufferWalker += bytesWrittenAt; + size -= bytesWrittenAt; + *bytesWritten += bytesWrittenAt; + offsetInBlock = 0; /* There is no offset on the next block */ + + if (bytesWrittenAt != bytesToWrite) + break; + } + + return (size == 0) ? S_OK : STG_E_WRITEFAULT; +} + + +/************************************************************************ + * SmallBlockChainStream implementation + ***********************************************************************/ SmallBlockChainStream* SmallBlockChainStream_Construct( StorageImpl* parentStorage, @@ -8151,6 +8363,11 @@ static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This) return chainEntry.size; } + +/************************************************************************ + * Miscellaneous storage functions + ***********************************************************************/ + static HRESULT create_storagefile( LPCOLESTR pwcsName, DWORD grfMode, @@ -8257,7 +8474,7 @@ static HRESULT create_storagefile( } /* - * Allocate and initialize the new IStorage32object. + * Allocate and initialize the new IStorage object. */ hr = Storage_Construct( hFile, @@ -8333,18 +8550,18 @@ HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD st if (stgfmt != STGFMT_FILE && grfAttrs != 0) { ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n"); - return STG_E_INVALIDPARAMETER; + return STG_E_INVALIDPARAMETER; } if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING) { ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n"); - return STG_E_INVALIDPARAMETER; + return STG_E_INVALIDPARAMETER; } if (stgfmt == STGFMT_FILE) { - ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); + ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); return STG_E_INVALIDPARAMETER; } @@ -8385,15 +8602,15 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0) { ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n"); - return STG_E_INVALIDPARAMETER; + return STG_E_INVALIDPARAMETER; } switch (stgfmt) { case STGFMT_FILE: - ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); + ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); return STG_E_INVALIDPARAMETER; - + case STGFMT_STORAGE: break; @@ -8401,7 +8618,7 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING) { ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n"); - return STG_E_INVALIDPARAMETER; + return STG_E_INVALIDPARAMETER; } FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n"); break; @@ -8578,7 +8795,7 @@ HRESULT WINAPI StgOpenStorage( } /* - * Allocate and initialize the new IStorage32object. + * Allocate and initialize the new IStorage object. */ hr = Storage_Construct( hFile, @@ -8703,7 +8920,7 @@ HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime, { 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, @@ -8853,183 +9070,437 @@ HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm) return res; } -/**************************************************************************** - * This method validate a STGM parameter that can contain the values below +/************************************************************************* + * STORAGE_CreateOleStream [Internal] * - * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values. - * The stgm values contained in 0xffff0000 are bitmasks. + * Creates the "\001OLE" stream in the IStorage if necessary. * - * STGM_DIRECT 0x00000000 - * STGM_TRANSACTED 0x00010000 - * STGM_SIMPLE 0x08000000 + * PARAMS + * storage [I] Dest storage to create the stream in + * flags [I] flags to be set for newly created stream * - * STGM_READ 0x00000000 - * STGM_WRITE 0x00000001 - * STGM_READWRITE 0x00000002 + * RETURNS + * HRESULT return value * - * STGM_SHARE_DENY_NONE 0x00000040 - * STGM_SHARE_DENY_READ 0x00000030 - * STGM_SHARE_DENY_WRITE 0x00000020 - * STGM_SHARE_EXCLUSIVE 0x00000010 + * NOTES * - * STGM_PRIORITY 0x00040000 - * STGM_DELETEONRELEASE 0x04000000 + * 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. * - * STGM_CREATE 0x00001000 - * STGM_CONVERT 0x00020000 - * STGM_FAILIFTHERE 0x00000000 - * - * STGM_NOSCRATCH 0x00100000 - * STGM_NOSNAPSHOT 0x00200000 */ -static HRESULT validateSTGM(DWORD stgm) +HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags) { - DWORD access = STGM_ACCESS_MODE(stgm); - DWORD share = STGM_SHARE_MODE(stgm); - DWORD create = STGM_CREATE_MODE(stgm); + static const WCHAR stream_1oleW[] = {1,'O','l','e',0}; + static const DWORD version_magic = 0x02000001; + IStream *stream; + HRESULT hr; - if (stgm&~STGM_KNOWN_FLAGS) - { - ERR("unknown flags %08x\n", stgm); - return E_FAIL; - } + hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream); + if (hr == S_OK) + { + struct empty_1ole_stream { + DWORD version_magic; + DWORD flags; + DWORD update_options; + DWORD reserved; + DWORD mon_stream_size; + }; + struct empty_1ole_stream stream_data; - switch (access) - { - case STGM_READ: - case STGM_WRITE: - case STGM_READWRITE: - break; - default: - return E_FAIL; - } + 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; - 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; - } + hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL); + IStream_Release(stream); + } - 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; + return hr; } -/**************************************************************************** - * GetShareModeFromSTGM - * - * This method will return a share mode flag from a STGM value. - * The STGM value is assumed valid. - */ -static DWORD GetShareModeFromSTGM(DWORD stgm) +/* write a string to a stream, preceded by its length */ +static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string ) { - 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; + 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; } -/**************************************************************************** - * GetAccessModeFromSTGM - * - * This method will return an access mode flag from a STGM value. - * The STGM value is assumed valid. - */ -static DWORD GetAccessModeFromSTGM(DWORD stgm) +/* read a string preceded by its length from a stream */ +static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string ) { - 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; + HRESULT r; + DWORD len, count = 0; + LPSTR str; + LPWSTR wstr; + + r = IStream_Read( stm, &len, sizeof(len), &count ); + if( FAILED( r ) ) + return r; + if( count != sizeof(len) ) + return E_OUTOFMEMORY; + + TRACE("%d bytes\n",len); + + str = CoTaskMemAlloc( len ); + if( !str ) + return E_OUTOFMEMORY; + count = 0; + r = IStream_Read( stm, str, len, &count ); + if( FAILED( r ) ) + return r; + if( count != len ) + { + CoTaskMemFree( str ); + return E_OUTOFMEMORY; + } + + TRACE("Read string %s\n",debugstr_an(str,len)); + + len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 ); + wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) ); + if( wstr ) + { + MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len ); + wstr[len] = 0; + } + CoTaskMemFree( str ); + + *string = wstr; + + return r; } -/**************************************************************************** - * GetCreationModeFromSTGM - * - * This method will return a creation mode flag from a STGM value. - * The STGM value is assumed valid. - */ -static DWORD GetCreationModeFromSTGM(DWORD stgm) + +static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, + LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) { - 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; + IStream *pstm; + HRESULT r = S_OK; + static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; + + static const BYTE unknown1[12] = + { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF}; + static const BYTE unknown2[16] = + { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid), + debugstr_w(lpszUserType), debugstr_w(szClipName), + debugstr_w(szProgIDName)); + + /* Create a CompObj stream */ + r = IStorage_CreateStream(pstg, szwStreamName, + STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm ); + if( FAILED (r) ) + return r; + + /* Write CompObj Structure to stream */ + r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL); + + if( SUCCEEDED( r ) ) + r = WriteClassStm( pstm, clsid ); + + if( SUCCEEDED( r ) ) + r = STREAM_WriteString( pstm, lpszUserType ); + if( SUCCEEDED( r ) ) + r = STREAM_WriteString( pstm, szClipName ); + if( SUCCEEDED( r ) ) + r = STREAM_WriteString( pstm, szProgIDName ); + if( SUCCEEDED( r ) ) + r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL); + + IStream_Release( pstm ); + + return r; } +/*********************************************************************** + * WriteFmtUserTypeStg (OLE32.@) + */ +HRESULT WINAPI WriteFmtUserTypeStg( + LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType) +{ + STATSTG stat; + HRESULT r; + WCHAR szwClipName[0x40]; + CLSID clsid; + LPWSTR wstrProgID = NULL; + DWORD n; + + TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType)); + + /* get the clipboard format name */ + if( cf ) + { + n = GetClipboardFormatNameW( cf, szwClipName, + sizeof(szwClipName)/sizeof(szwClipName[0]) ); + szwClipName[n]=0; + } + + TRACE("Clipboard name is %s\n", debugstr_w(szwClipName)); + + r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME); + if(SUCCEEDED(r)) + clsid = stat.clsid; + else + clsid = CLSID_NULL; + + 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; + static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 }; + unsigned char unknown1[12]; + unsigned char unknown2[16]; + DWORD count; + LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; + CLSID clsid; + + TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); + + r = IStorage_OpenStream( pstg, szCompObj, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); + if( FAILED ( r ) ) + { + WARN("Failed to open stream r = %08x\n", r); + return r; + } + + /* 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] @@ -9415,281 +9886,6 @@ static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData) return nDataLength; } -/************************************************************************* - * STORAGE_CreateOleStream [Internal] - * - * Creates the "\001OLE" stream in the IStorage if necessary. - * - * PARAMS - * storage [I] Dest storage to create the stream in - * flags [I] flags to be set for newly created stream - * - * RETURNS - * HRESULT return value - * - * NOTES - * - * This stream is still unknown, MS Word seems to have extra data - * but since the data is stored in the OLESTREAM there should be - * no need to recreate the stream. If the stream is manually - * deleted it will create it with this default data. - * - */ -HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags) -{ - static const WCHAR stream_1oleW[] = {1,'O','l','e',0}; - static const DWORD version_magic = 0x02000001; - IStream *stream; - HRESULT hr; - - hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream); - if (hr == S_OK) - { - struct empty_1ole_stream { - DWORD version_magic; - DWORD flags; - DWORD update_options; - DWORD reserved; - DWORD mon_stream_size; - }; - struct empty_1ole_stream stream_data; - - stream_data.version_magic = version_magic; - stream_data.flags = flags; - stream_data.update_options = 0; - stream_data.reserved = 0; - stream_data.mon_stream_size = 0; - - hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL); - IStream_Release(stream); - } - - return hr; -} - -/* write a string to a stream, preceded by its length */ -static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string ) -{ - HRESULT r; - LPSTR str; - DWORD len = 0; - - if( string ) - len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL); - r = IStream_Write( stm, &len, sizeof(len), NULL); - if( FAILED( r ) ) - return r; - if(len == 0) - return r; - str = CoTaskMemAlloc( len ); - WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL); - r = IStream_Write( stm, str, len, NULL); - CoTaskMemFree( str ); - return r; -} - -/* read a string preceded by its length from a stream */ -static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string ) -{ - HRESULT r; - DWORD len, count = 0; - LPSTR str; - LPWSTR wstr; - - r = IStream_Read( stm, &len, sizeof(len), &count ); - if( FAILED( r ) ) - return r; - if( count != sizeof(len) ) - return E_OUTOFMEMORY; - - TRACE("%d bytes\n",len); - - str = CoTaskMemAlloc( len ); - if( !str ) - return E_OUTOFMEMORY; - count = 0; - r = IStream_Read( stm, str, len, &count ); - if( FAILED( r ) ) - return r; - if( count != len ) - { - CoTaskMemFree( str ); - return E_OUTOFMEMORY; - } - - TRACE("Read string %s\n",debugstr_an(str,len)); - - len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 ); - wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) ); - if( wstr ) - { - MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len ); - wstr[len] = 0; - } - CoTaskMemFree( str ); - - *string = wstr; - - return r; -} - - -static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, - LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) -{ - IStream *pstm; - HRESULT r = S_OK; - static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; - - static const BYTE unknown1[12] = - { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF}; - static const BYTE unknown2[16] = - { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid), - debugstr_w(lpszUserType), debugstr_w(szClipName), - debugstr_w(szProgIDName)); - - /* Create a CompObj stream */ - r = IStorage_CreateStream(pstg, szwStreamName, - STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm ); - if( FAILED (r) ) - return r; - - /* Write CompObj Structure to stream */ - r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL); - - if( SUCCEEDED( r ) ) - r = WriteClassStm( pstm, clsid ); - - if( SUCCEEDED( r ) ) - r = STREAM_WriteString( pstm, lpszUserType ); - if( SUCCEEDED( r ) ) - r = STREAM_WriteString( pstm, szClipName ); - if( SUCCEEDED( r ) ) - r = STREAM_WriteString( pstm, szProgIDName ); - if( SUCCEEDED( r ) ) - r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL); - - IStream_Release( pstm ); - - return r; -} - -/*********************************************************************** - * WriteFmtUserTypeStg (OLE32.@) - */ -HRESULT WINAPI WriteFmtUserTypeStg( - LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType) -{ - STATSTG stat; - HRESULT r; - WCHAR szwClipName[0x40]; - CLSID clsid; - LPWSTR wstrProgID = NULL; - DWORD n; - - TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType)); - - /* get the clipboard format name */ - if( cf ) - { - n = GetClipboardFormatNameW( cf, szwClipName, - sizeof(szwClipName)/sizeof(szwClipName[0]) ); - szwClipName[n]=0; - } - - TRACE("Clipboard name is %s\n", debugstr_w(szwClipName)); - - r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME); - if(SUCCEEDED(r)) - clsid = stat.clsid; - else - clsid = CLSID_NULL; - - 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; - static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 }; - unsigned char unknown1[12]; - unsigned char unknown2[16]; - DWORD count; - LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; - CLSID clsid; - - TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); - - r = IStorage_OpenStream( pstg, szCompObj, NULL, - STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); - if( FAILED ( r ) ) - { - WARN("Failed to open stream r = %08x\n", r); - return r; - } - - /* 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; -} - - /************************************************************************* * OLECONVERT_CreateCompObjStream [Internal] * @@ -10383,112 +10579,3 @@ HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert) IStream_Release(stream); return hr; } - -/****************************************************************************** - * StgIsStorageFile [OLE32.@] - * Verify if the file contains a storage object - * - * PARAMS - * fn [ I] Filename - * - * RETURNS - * S_OK if file has magic bytes as a storage object - * S_FALSE if file is not storage - */ -HRESULT WINAPI -StgIsStorageFile(LPCOLESTR fn) -{ - HANDLE hf; - BYTE magic[8]; - DWORD bytes_read; - - TRACE("%s\n", debugstr_w(fn)); - hf = CreateFileW(fn, GENERIC_READ, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - - if (hf == INVALID_HANDLE_VALUE) - return STG_E_FILENOTFOUND; - - if (!ReadFile(hf, magic, 8, &bytes_read, NULL)) - { - WARN(" unable to read file\n"); - CloseHandle(hf); - return S_FALSE; - } - - CloseHandle(hf); - - if (bytes_read != 8) { - TRACE(" too short\n"); - return S_FALSE; - } - - if (!memcmp(magic,STORAGE_magic,8)) { - TRACE(" -> YES\n"); - return S_OK; - } - - TRACE(" -> Invalid header.\n"); - return S_FALSE; -} - -/*********************************************************************** - * WriteClassStm (OLE32.@) - * - * Writes a CLSID to a stream. - * - * PARAMS - * pStm [I] Stream to write to. - * rclsid [I] CLSID to write. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - */ -HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid) -{ - TRACE("(%p,%p)\n",pStm,rclsid); - - if (!pStm || !rclsid) - return E_INVALIDARG; - - return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL); -} - -/*********************************************************************** - * ReadClassStm (OLE32.@) - * - * Reads a CLSID from a stream. - * - * PARAMS - * pStm [I] Stream to read from. - * rclsid [O] CLSID to read. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - */ -HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid) -{ - ULONG nbByte; - HRESULT res; - - TRACE("(%p,%p)\n",pStm,pclsid); - - if (!pStm || !pclsid) - return E_INVALIDARG; - - /* clear the output args */ - *pclsid = CLSID_NULL; - - res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte); - - if (FAILED(res)) - return res; - - if (nbByte != sizeof(CLSID)) - return STG_E_READFAULT; - else - return S_OK; -} diff --git a/reactos/dll/win32/ole32/storage32.h b/reactos/dll/win32/ole32/storage32.h index 96c9307e9cb..78e6b69e749 100644 --- a/reactos/dll/win32/ole32/storage32.h +++ b/reactos/dll/win32/ole32/storage32.h @@ -150,8 +150,6 @@ struct DirEntry HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes) DECLSPEC_HIDDEN; -HRESULT FileLockBytesImpl_LockRegionSync(ILockBytes* iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb) DECLSPEC_HIDDEN; - /************************************************************************* * Ole Convert support */ @@ -356,9 +354,9 @@ void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) D #define BLOCKCHAIN_CACHE_SIZE 4 /**************************************************************************** - * Storage32Impl definitions. + * StorageImpl definitions. * - * This implementation of the IStorage32 interface represents a root + * This implementation of the IStorage interface represents a root * storage. Basically, a document file. */ struct StorageImpl @@ -404,44 +402,13 @@ struct StorageImpl BlockChainStream* blockChainCache[BLOCKCHAIN_CACHE_SIZE]; UINT blockChainToEvict; + ULONG locks_supported; + ILockBytes* lockBytes; ULONG locked_bytes[8]; }; -HRESULT StorageImpl_ReadRawDirEntry( - StorageImpl *This, - ULONG index, - BYTE *buffer) DECLSPEC_HIDDEN; - -void UpdateRawDirEntry( - BYTE *buffer, - const DirEntry *newData) DECLSPEC_HIDDEN; - -HRESULT StorageImpl_WriteRawDirEntry( - StorageImpl *This, - ULONG index, - const BYTE *buffer) DECLSPEC_HIDDEN; - -HRESULT StorageImpl_ReadDirEntry( - StorageImpl* This, - DirRef index, - DirEntry* buffer) DECLSPEC_HIDDEN; - -HRESULT StorageImpl_WriteDirEntry( - StorageImpl* This, - DirRef index, - const DirEntry* buffer) DECLSPEC_HIDDEN; - -BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( - StorageImpl* This, - SmallBlockChainStream** ppsbChain) DECLSPEC_HIDDEN; - -SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( - StorageImpl* This, - BlockChainStream** ppbbChain, - ULARGE_INTEGER newSize) DECLSPEC_HIDDEN; - /**************************************************************************** * StgStreamImpl definitions. * @@ -496,7 +463,10 @@ StgStreamImpl* StgStreamImpl_Construct( /* Range lock constants. * * The storage format reserves the region from 0x7fffff00-0x7fffffff for - * locking and synchronization. Unfortunately, the spec doesn't say which bytes + * 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. @@ -515,7 +485,7 @@ StgStreamImpl* StgStreamImpl_Construct( 0xe2 through 0xff: Unknown. Causes read-only exclusive opens to fail. */ -#define RANGELOCK_UNK1_FIRST 0x7fffff00 +#define RANGELOCK_UNK1_FIRST 0x7ffffe00 #define RANGELOCK_UNK1_LAST 0x7fffff57 #define RANGELOCK_PRIORITY1_FIRST 0x7fffff58 #define RANGELOCK_PRIORITY1_LAST 0x7fffff6b @@ -540,6 +510,9 @@ StgStreamImpl* StgStreamImpl_Construct( #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 @@ -578,116 +551,5 @@ void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value) DECLSPE void StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl *storage,STATSTG* destination, const DirEntry* source, int statFlags) DECLSPEC_HIDDEN; -/**************************************************************************** - * 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; -}; - -/* - * Methods for the BlockChainStream class. - */ -BlockChainStream* BlockChainStream_Construct( - StorageImpl* parentStorage, - ULONG* headOfStreamPlaceHolder, - DirRef dirEntry) DECLSPEC_HIDDEN; - -void BlockChainStream_Destroy( - BlockChainStream* This) DECLSPEC_HIDDEN; - -HRESULT BlockChainStream_ReadAt( - BlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - void* buffer, - ULONG* bytesRead) DECLSPEC_HIDDEN; - -HRESULT BlockChainStream_WriteAt( - BlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - const void* buffer, - ULONG* bytesWritten) DECLSPEC_HIDDEN; - -BOOL BlockChainStream_SetSize( - BlockChainStream* This, - ULARGE_INTEGER newSize) DECLSPEC_HIDDEN; - -HRESULT BlockChainStream_Flush( - BlockChainStream* This) DECLSPEC_HIDDEN; - -/**************************************************************************** - * 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; -}; - -/* - * Methods of the SmallBlockChainStream class. - */ -SmallBlockChainStream* SmallBlockChainStream_Construct( - StorageImpl* parentStorage, - ULONG* headOfStreamPlaceHolder, - DirRef dirEntry) DECLSPEC_HIDDEN; - -void SmallBlockChainStream_Destroy( - SmallBlockChainStream* This) DECLSPEC_HIDDEN; - -HRESULT SmallBlockChainStream_ReadAt( - SmallBlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - void* buffer, - ULONG* bytesRead) DECLSPEC_HIDDEN; - -HRESULT SmallBlockChainStream_WriteAt( - SmallBlockChainStream* This, - ULARGE_INTEGER offset, - ULONG size, - const void* buffer, - ULONG* bytesWritten) DECLSPEC_HIDDEN; - -BOOL SmallBlockChainStream_SetSize( - SmallBlockChainStream* This, - ULARGE_INTEGER newSize) DECLSPEC_HIDDEN; - #endif /* __STORAGE32_H__ */ diff --git a/reactos/dll/win32/ole32/usrmarshal.c b/reactos/dll/win32/ole32/usrmarshal.c index ba857324d4d..9cae09e21ca 100644 --- a/reactos/dll/win32/ole32/usrmarshal.c +++ b/reactos/dll/win32/ole32/usrmarshal.c @@ -290,7 +290,7 @@ static unsigned char * handle_UserUnmarshal(ULONG *pFlags, unsigned char *pBuffe return pBuffer + sizeof(RemotableHandle); } -static void handle_UserFree(ULONG *pFlags, HANDLE *phMenu) +static void handle_UserFree(ULONG *pFlags, HANDLE *handle) { /* nothing to do */ } @@ -323,6 +323,9 @@ static void handle_UserFree(ULONG *pFlags, HANDLE *phMenu) IMPL_WIREM_HANDLE(HACCEL) IMPL_WIREM_HANDLE(HMENU) IMPL_WIREM_HANDLE(HWND) +IMPL_WIREM_HANDLE(HDC) +IMPL_WIREM_HANDLE(HICON) +IMPL_WIREM_HANDLE(HBRUSH) /****************************************************************************** * HGLOBAL_UserSize [OLE32.@] @@ -642,204 +645,6 @@ void __RPC_USER HBITMAP_UserFree(ULONG *pFlags, HBITMAP *phBmp) FIXME(":stub\n"); } -/****************************************************************************** - * HICON_UserSize [OLE32.@] - * - * Calculates the buffer size required to marshal an icon. - * - * PARAMS - * pFlags [I] Flags. See notes. - * StartingSize [I] Starting size of the buffer. This value is added on to - * the buffer size required for the icon. - * phIcon [I] Icon to size. - * - * RETURNS - * The buffer size required to marshal an icon plus the starting size. - * - * NOTES - * Even though the function is documented to take a pointer to a ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which - * the first parameter is a ULONG. - * This function is only intended to be called by the RPC runtime. - */ -ULONG __RPC_USER HICON_UserSize(ULONG *pFlags, ULONG StartingSize, HICON *phIcon) -{ - FIXME(":stub\n"); - return StartingSize; -} - -/****************************************************************************** -* HICON_UserMarshal [OLE32.@] -* -* Marshals an icon into a buffer. -* -* PARAMS -* pFlags [I] Flags. See notes. -* pBuffer [I] Buffer to marshal the icon into. -* phIcon [I] Icon to marshal. -* -* RETURNS -* The end of the marshaled data in the buffer. -* -* NOTES -* Even though the function is documented to take a pointer to a ULONG in -* pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which -* the first parameter is a ULONG. -* This function is only intended to be called by the RPC runtime. -*/ -unsigned char * __RPC_USER HICON_UserMarshal(ULONG *pFlags, unsigned char *pBuffer, HICON *phIcon) -{ - FIXME(":stub\n"); - return pBuffer; -} - -/****************************************************************************** - * HICON_UserUnmarshal [OLE32.@] - * - * Unmarshals an icon from a buffer. - * - * PARAMS - * pFlags [I] Flags. See notes. - * pBuffer [I] Buffer to marshal the icon from. - * phIcon [O] Address that receive the unmarshaled icon. - * - * RETURNS - * The end of the marshaled data in the buffer. - * - * NOTES - * Even though the function is documented to take a pointer to an ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which - * the first parameter is an ULONG. - * This function is only intended to be called by the RPC runtime. - */ -unsigned char * __RPC_USER HICON_UserUnmarshal(ULONG *pFlags, unsigned char *pBuffer, HICON *phIcon) -{ - FIXME(":stub\n"); - return pBuffer; -} - -/****************************************************************************** - * HICON_UserFree [OLE32.@] - * - * Frees an unmarshaled icon. - * - * PARAMS - * pFlags [I] Flags. See notes. - * phIcon [I] Icon to free. - * - * RETURNS - * The end of the marshaled data in the buffer. - * - * NOTES - * Even though the function is documented to take a pointer to a ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of - * which the first parameter is a ULONG. - * This function is only intended to be called by the RPC runtime. - */ -void __RPC_USER HICON_UserFree(ULONG *pFlags, HICON *phIcon) -{ - FIXME(":stub\n"); -} - -/****************************************************************************** - * HDC_UserSize [OLE32.@] - * - * Calculates the buffer size required to marshal an HDC. - * - * PARAMS - * pFlags [I] Flags. See notes. - * StartingSize [I] Starting size of the buffer. This value is added on to - * the buffer size required for the clip format. - * phGlobal [I] HDC to size. - * - * RETURNS - * The buffer size required to marshal an HDC plus the starting size. - * - * NOTES - * Even though the function is documented to take a pointer to a ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which - * the first parameter is a ULONG. - * This function is only intended to be called by the RPC runtime. - */ -ULONG __RPC_USER HDC_UserSize(ULONG *pFlags, ULONG StartingSize, HDC *phdc) -{ - FIXME(":stub\n"); - return StartingSize; -} - -/****************************************************************************** - * HDC_UserMarshal [OLE32.@] - * - * Marshals an HDC into a buffer. - * - * PARAMS - * pFlags [I] Flags. See notes. - * pBuffer [I] Buffer to marshal the clip format into. - * phdc [I] HDC to marshal. - * - * RETURNS - * The end of the marshaled data in the buffer. - * - * NOTES - * Even though the function is documented to take a pointer to a ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which - * the first parameter is a ULONG. - * This function is only intended to be called by the RPC runtime. - */ -unsigned char * __RPC_USER HDC_UserMarshal(ULONG *pFlags, unsigned char *pBuffer, HDC *phdc) -{ - FIXME(":stub\n"); - return pBuffer; -} - -/****************************************************************************** - * HDC_UserUnmarshal [OLE32.@] - * - * Unmarshals an HDC from a buffer. - * - * PARAMS - * pFlags [I] Flags. See notes. - * pBuffer [I] Buffer to marshal the clip format from. - * phdc [O] Address that receive the unmarshaled HDC. - * - * RETURNS - * The end of the marshaled data in the buffer. - * - * NOTES - * Even though the function is documented to take a pointer to an ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of which - * the first parameter is an ULONG. - * This function is only intended to be called by the RPC runtime. - */ -unsigned char * __RPC_USER HDC_UserUnmarshal(ULONG *pFlags, unsigned char *pBuffer, HDC *phdc) -{ - FIXME(":stub\n"); - return pBuffer; -} - -/****************************************************************************** - * HDC_UserFree [OLE32.@] - * - * Frees an unmarshaled HDC. - * - * PARAMS - * pFlags [I] Flags. See notes. - * phdc [I] HDC to free. - * - * RETURNS - * The end of the marshaled data in the buffer. - * - * NOTES - * Even though the function is documented to take a pointer to a ULONG in - * pFlags, it actually takes a pointer to a USER_MARSHAL_CB structure, of - * which the first parameter is a ULONG. - * This function is only intended to be called by the RPC runtime. - */ -void __RPC_USER HDC_UserFree(ULONG *pFlags, HDC *phdc) -{ - FIXME(":stub\n"); -} - /****************************************************************************** * HPALETTE_UserSize [OLE32.@] * @@ -2142,25 +1947,117 @@ void __RPC_USER FLAG_STGMEDIUM_UserFree(ULONG *pFlags, FLAG_STGMEDIUM *pStgMediu ULONG __RPC_USER SNB_UserSize(ULONG *pFlags, ULONG StartingSize, SNB *pSnb) { - FIXME(":stub\n"); - return StartingSize; + ULONG size = StartingSize; + + TRACE("(%s, %d, %p\n", debugstr_user_flags(pFlags), StartingSize, pSnb); + + ALIGN_LENGTH(size, 3); + + /* two counters from RemSNB header, plus one more ULONG */ + size += 3*sizeof(ULONG); + + /* now actual data length */ + if (*pSnb) + { + WCHAR **ptrW = *pSnb; + + while (*ptrW) + { + size += (strlenW(*ptrW) + 1)*sizeof(WCHAR); + ptrW++; + } + } + + return size; } -unsigned char * __RPC_USER SNB_UserMarshal( ULONG *pFlags, unsigned char *pBuffer, SNB *pSnb) +struct SNB_wire { + ULONG charcnt; + ULONG strcnt; + ULONG datalen; + WCHAR data[1]; +}; + +unsigned char * __RPC_USER SNB_UserMarshal(ULONG *pFlags, unsigned char *pBuffer, SNB *pSnb) { - FIXME(":stub\n"); - return pBuffer; + struct SNB_wire *wire; + ULONG size; + + TRACE("(%s, %p, %p)\n", debugstr_user_flags(pFlags), pBuffer, pSnb); + + ALIGN_POINTER(pBuffer, 3); + + wire = (struct SNB_wire*)pBuffer; + wire->charcnt = wire->strcnt = 0; + size = 3*sizeof(ULONG); + + if (*pSnb) + { + WCHAR **ptrW = *pSnb; + WCHAR *dataW = wire->data; + + while (*ptrW) + { + ULONG len = strlenW(*ptrW) + 1; + + wire->strcnt++; + wire->charcnt += len; + memcpy(dataW, *ptrW, len*sizeof(WCHAR)); + dataW += len; + + size += len*sizeof(WCHAR); + ptrW++; + } + } + + wire->datalen = wire->charcnt; + return pBuffer + size; } unsigned char * __RPC_USER SNB_UserUnmarshal(ULONG *pFlags, unsigned char *pBuffer, SNB *pSnb) { - FIXME(":stub\n"); - return pBuffer; + USER_MARSHAL_CB *umcb = (USER_MARSHAL_CB*)pFlags; + struct SNB_wire *wire; + + TRACE("(%s, %p, %p)\n", debugstr_user_flags(pFlags), pBuffer, pSnb); + + wire = (struct SNB_wire*)pBuffer; + + if (*pSnb) + umcb->pStubMsg->pfnFree(*pSnb); + + if (wire->datalen == 0) + *pSnb = NULL; + else + { + WCHAR *src = wire->data, *dest; + WCHAR **ptrW; + ULONG i; + + ptrW = *pSnb = umcb->pStubMsg->pfnAllocate((wire->strcnt+1)*sizeof(WCHAR*) + wire->datalen*sizeof(WCHAR)); + dest = (WCHAR*)(*pSnb + wire->strcnt + 1); + + for (i = 0; i < wire->strcnt; i++) + { + ULONG len = strlenW(src); + memcpy(dest, src, (len + 1)*sizeof(WCHAR)); + *ptrW = dest; + src += len + 1; + dest += len + 1; + ptrW++; + } + *ptrW = NULL; + } + + return pBuffer + 3*sizeof(ULONG) + wire->datalen*sizeof(WCHAR); } void __RPC_USER SNB_UserFree(ULONG *pFlags, SNB *pSnb) { - FIXME(":stub\n"); + USER_MARSHAL_CB *umcb = (USER_MARSHAL_CB*)pFlags; + TRACE("(%p)\n", pSnb); + if (*pSnb) + umcb->pStubMsg->pfnFree(*pSnb); } /* call_as/local stubs for unknwn.idl */ @@ -2901,15 +2798,15 @@ HRESULT CALLBACK IOleInPlaceActiveObject_TranslateAccelerator_Proxy( IOleInPlaceActiveObject* This, LPMSG lpmsg) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p %p)\n", This, lpmsg); + return IOleInPlaceActiveObject_RemoteTranslateAccelerator_Proxy(This); } HRESULT __RPC_STUB IOleInPlaceActiveObject_TranslateAccelerator_Stub( IOleInPlaceActiveObject* This) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p)\n", This); + return S_FALSE; } HRESULT CALLBACK IOleInPlaceActiveObject_ResizeBorder_Proxy( @@ -2939,8 +2836,8 @@ HRESULT CALLBACK IOleCache2_UpdateCache_Proxy( DWORD grfUpdf, LPVOID pReserved) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p, %p, 0x%08x, %p)\n", This, pDataObject, grfUpdf, pReserved); + return IOleCache2_RemoteUpdateCache_Proxy(This, pDataObject, grfUpdf, (LONG_PTR)pReserved); } HRESULT __RPC_STUB IOleCache2_UpdateCache_Stub( @@ -2949,8 +2846,8 @@ HRESULT __RPC_STUB IOleCache2_UpdateCache_Stub( DWORD grfUpdf, LONG_PTR pReserved) { - FIXME(":stub\n"); - return E_NOTIMPL; + TRACE("(%p, %p, 0x%08x, %li)\n", This, pDataObject, grfUpdf, pReserved); + return IOleCache2_UpdateCache(This, pDataObject, grfUpdf, (void*)pReserved); } HRESULT CALLBACK IEnumOLEVERB_Next_Proxy( diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 056e8d4331d..a5bfdcfe837 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -145,7 +145,7 @@ reactos/dll/win32/ntprint # Synced to WineStaging-1.7.37 reactos/dll/win32/objsel # Synced to WineStaging-1.7.37 reactos/dll/win32/odbc32 # Synced to WineStaging-1.7.37. Depends on port of Linux ODBC. reactos/dll/win32/odbccp32 # Synced to WineStaging-1.7.47 -reactos/dll/win32/ole32 # Synced to WineStaging-1.7.37 +reactos/dll/win32/ole32 # Synced to WineStaging-1.7.47 reactos/dll/win32/oleacc # Synced to WineStaging-1.7.37 reactos/dll/win32/oleaut32 # Synced to WineStaging-1.7.37 reactos/dll/win32/olecli32 # Synced to WineStaging-1.7.37