Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenstorage32.c
Go to the documentation of this file.
00001 /* 00002 * Compound Storage (32 bit version) 00003 * Storage implementation 00004 * 00005 * This file contains the compound file implementation 00006 * of the storage interface. 00007 * 00008 * Copyright 1999 Francis Beaudet 00009 * Copyright 1999 Sylvain St-Germain 00010 * Copyright 1999 Thuy Nguyen 00011 * Copyright 2005 Mike McCormack 00012 * 00013 * This library is free software; you can redistribute it and/or 00014 * modify it under the terms of the GNU Lesser General Public 00015 * License as published by the Free Software Foundation; either 00016 * version 2.1 of the License, or (at your option) any later version. 00017 * 00018 * This library is distributed in the hope that it will be useful, 00019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00021 * Lesser General Public License for more details. 00022 * 00023 * You should have received a copy of the GNU Lesser General Public 00024 * License along with this library; if not, write to the Free Software 00025 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00026 * 00027 * NOTES 00028 * The compound file implementation of IStorage used for create 00029 * and manage substorages and streams within a storage object 00030 * residing in a compound file object. 00031 */ 00032 00033 #include <assert.h> 00034 #include <stdarg.h> 00035 #include <stdio.h> 00036 #include <stdlib.h> 00037 #include <string.h> 00038 00039 #define COBJMACROS 00040 #define NONAMELESSUNION 00041 #define NONAMELESSSTRUCT 00042 00043 #include "windef.h" 00044 #include "winbase.h" 00045 #include "winnls.h" 00046 #include "winuser.h" 00047 #include "wine/unicode.h" 00048 #include "wine/debug.h" 00049 00050 #include "storage32.h" 00051 #include "ole2.h" /* For Write/ReadClassStm */ 00052 00053 #include "winreg.h" 00054 #include "wine/wingdi16.h" 00055 00056 WINE_DEFAULT_DEBUG_CHANNEL(storage); 00057 00058 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ 00059 #define OLESTREAM_ID 0x501 00060 #define OLESTREAM_MAX_STR_LEN 255 00061 00062 /* 00063 * These are signatures to detect the type of Document file. 00064 */ 00065 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1}; 00066 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d}; 00067 00068 static const char rootEntryName[] = "Root Entry"; 00069 00070 /**************************************************************************** 00071 * Storage32InternalImpl definitions. 00072 * 00073 * Definition of the implementation structure for the IStorage32 interface. 00074 * This one implements the IStorage32 interface for storage that are 00075 * inside another storage. 00076 */ 00077 struct StorageInternalImpl 00078 { 00079 struct StorageBaseImpl base; 00080 00081 /* 00082 * Entry in the parent's stream tracking list 00083 */ 00084 struct list ParentListEntry; 00085 00086 StorageBaseImpl *parentStorage; 00087 }; 00088 typedef struct StorageInternalImpl StorageInternalImpl; 00089 00090 static const IStorageVtbl TransactedSnapshotImpl_Vtbl; 00091 static const IStorageVtbl Storage32InternalImpl_Vtbl; 00092 00093 /* Method definitions for the Storage32InternalImpl class. */ 00094 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage, 00095 DWORD openFlags, DirRef storageDirEntry); 00096 static void StorageImpl_Destroy(StorageBaseImpl* iface); 00097 static void StorageImpl_Invalidate(StorageBaseImpl* iface); 00098 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface); 00099 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer); 00100 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer); 00101 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock); 00102 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This); 00103 static void StorageImpl_SaveFileHeader(StorageImpl* This); 00104 00105 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex); 00106 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This); 00107 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex); 00108 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex); 00109 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex); 00110 00111 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This); 00112 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This); 00113 static ULONG BlockChainStream_GetCount(BlockChainStream* This); 00114 00115 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This); 00116 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This); 00117 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This, 00118 ULONG blockIndex, ULONG offset, DWORD value); 00119 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This, 00120 ULONG blockIndex, ULONG offset, DWORD* value); 00121 00122 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry); 00123 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry); 00124 00125 typedef struct TransactedDirEntry 00126 { 00127 /* If applicable, a reference to the original DirEntry in the transacted 00128 * parent. If this is a newly-created entry, DIRENTRY_NULL. */ 00129 DirRef transactedParentEntry; 00130 00131 /* True if this entry is being used. */ 00132 int inuse; 00133 00134 /* True if data is up to date. */ 00135 int read; 00136 00137 /* True if this entry has been modified. */ 00138 int dirty; 00139 00140 /* True if this entry's stream has been modified. */ 00141 int stream_dirty; 00142 00143 /* True if this entry has been deleted in the transacted storage, but the 00144 * delete has not yet been committed. */ 00145 int deleted; 00146 00147 /* If this entry's stream has been modified, a reference to where the stream 00148 * is stored in the snapshot file. */ 00149 DirRef stream_entry; 00150 00151 /* This directory entry's data, including any changes that have been made. */ 00152 DirEntry data; 00153 00154 /* A reference to the parent of this node. This is only valid while we are 00155 * committing changes. */ 00156 DirRef parent; 00157 00158 /* A reference to a newly-created entry in the transacted parent. This is 00159 * always equal to transactedParentEntry except when committing changes. */ 00160 DirRef newTransactedParentEntry; 00161 } TransactedDirEntry; 00162 00163 /**************************************************************************** 00164 * Transacted storage object. 00165 */ 00166 typedef struct TransactedSnapshotImpl 00167 { 00168 struct StorageBaseImpl base; 00169 00170 /* 00171 * Modified streams are temporarily saved to the scratch file. 00172 */ 00173 StorageBaseImpl *scratch; 00174 00175 /* The directory structure is kept here, so that we can track how these 00176 * entries relate to those in the parent storage. */ 00177 TransactedDirEntry *entries; 00178 ULONG entries_size; 00179 ULONG firstFreeEntry; 00180 00181 /* 00182 * Changes are committed to the transacted parent. 00183 */ 00184 StorageBaseImpl *transactedParent; 00185 } TransactedSnapshotImpl; 00186 00187 /* Generic function to create a transacted wrapper for a direct storage object. */ 00188 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result); 00189 00190 /* OLESTREAM memory structure to use for Get and Put Routines */ 00191 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ 00192 typedef struct 00193 { 00194 DWORD dwOleID; 00195 DWORD dwTypeID; 00196 DWORD dwOleTypeNameLength; 00197 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; 00198 CHAR *pstrOleObjFileName; 00199 DWORD dwOleObjFileNameLength; 00200 DWORD dwMetaFileWidth; 00201 DWORD dwMetaFileHeight; 00202 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */ 00203 DWORD dwDataLength; 00204 BYTE *pData; 00205 }OLECONVERT_OLESTREAM_DATA; 00206 00207 /* CompObj Stream structure */ 00208 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ 00209 typedef struct 00210 { 00211 BYTE byUnknown1[12]; 00212 CLSID clsid; 00213 DWORD dwCLSIDNameLength; 00214 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN]; 00215 DWORD dwOleTypeNameLength; 00216 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; 00217 DWORD dwProgIDNameLength; 00218 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN]; 00219 BYTE byUnknown2[16]; 00220 }OLECONVERT_ISTORAGE_COMPOBJ; 00221 00222 00223 /* Ole Presentation Stream structure */ 00224 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */ 00225 typedef struct 00226 { 00227 BYTE byUnknown1[28]; 00228 DWORD dwExtentX; 00229 DWORD dwExtentY; 00230 DWORD dwSize; 00231 BYTE *pData; 00232 }OLECONVERT_ISTORAGE_OLEPRES; 00233 00234 00235 00236 /*********************************************************************** 00237 * Forward declaration of internal functions used by the method DestroyElement 00238 */ 00239 static HRESULT deleteStorageContents( 00240 StorageBaseImpl *parentStorage, 00241 DirRef indexToDelete, 00242 DirEntry entryDataToDelete); 00243 00244 static HRESULT deleteStreamContents( 00245 StorageBaseImpl *parentStorage, 00246 DirRef indexToDelete, 00247 DirEntry entryDataToDelete); 00248 00249 static HRESULT removeFromTree( 00250 StorageBaseImpl *This, 00251 DirRef parentStorageIndex, 00252 DirRef deletedIndex); 00253 00254 /*********************************************************************** 00255 * Declaration of the functions used to manipulate DirEntry 00256 */ 00257 00258 static HRESULT insertIntoTree( 00259 StorageBaseImpl *This, 00260 DirRef parentStorageIndex, 00261 DirRef newEntryIndex); 00262 00263 static LONG entryNameCmp( 00264 const OLECHAR *name1, 00265 const OLECHAR *name2); 00266 00267 static DirRef findElement( 00268 StorageBaseImpl *storage, 00269 DirRef storageEntry, 00270 const OLECHAR *name, 00271 DirEntry *data); 00272 00273 static HRESULT findTreeParent( 00274 StorageBaseImpl *storage, 00275 DirRef storageEntry, 00276 const OLECHAR *childName, 00277 DirEntry *parentData, 00278 DirRef *parentEntry, 00279 ULONG *relation); 00280 00281 /*********************************************************************** 00282 * Declaration of miscellaneous functions... 00283 */ 00284 static HRESULT validateSTGM(DWORD stgmValue); 00285 00286 static DWORD GetShareModeFromSTGM(DWORD stgm); 00287 static DWORD GetAccessModeFromSTGM(DWORD stgm); 00288 static DWORD GetCreationModeFromSTGM(DWORD stgm); 00289 00290 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; 00291 00292 00293 /**************************************************************************** 00294 * IEnumSTATSTGImpl definitions. 00295 * 00296 * Definition of the implementation structure for the IEnumSTATSTGImpl interface. 00297 * This class allows iterating through the content of a storage and to find 00298 * specific items inside it. 00299 */ 00300 struct IEnumSTATSTGImpl 00301 { 00302 IEnumSTATSTG IEnumSTATSTG_iface; 00303 00304 LONG ref; /* Reference count */ 00305 StorageBaseImpl* parentStorage; /* Reference to the parent storage */ 00306 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */ 00307 00308 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */ 00309 }; 00310 00311 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface) 00312 { 00313 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface); 00314 } 00315 00316 00317 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry); 00318 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This); 00319 00320 /************************************************************************ 00321 ** Block Functions 00322 */ 00323 00324 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index) 00325 { 00326 return (index+1) * This->bigBlockSize; 00327 } 00328 00329 /************************************************************************ 00330 ** Storage32BaseImpl implementation 00331 */ 00332 static HRESULT StorageImpl_ReadAt(StorageImpl* This, 00333 ULARGE_INTEGER offset, 00334 void* buffer, 00335 ULONG size, 00336 ULONG* bytesRead) 00337 { 00338 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead); 00339 } 00340 00341 static HRESULT StorageImpl_WriteAt(StorageImpl* This, 00342 ULARGE_INTEGER offset, 00343 const void* buffer, 00344 const ULONG size, 00345 ULONG* bytesWritten) 00346 { 00347 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten); 00348 } 00349 00350 /************************************************************************ 00351 * Storage32BaseImpl_QueryInterface (IUnknown) 00352 * 00353 * This method implements the common QueryInterface for all IStorage32 00354 * implementations contained in this file. 00355 * 00356 * See Windows documentation for more details on IUnknown methods. 00357 */ 00358 static HRESULT WINAPI StorageBaseImpl_QueryInterface( 00359 IStorage* iface, 00360 REFIID riid, 00361 void** ppvObject) 00362 { 00363 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00364 00365 if ( (This==0) || (ppvObject==0) ) 00366 return E_INVALIDARG; 00367 00368 *ppvObject = 0; 00369 00370 if (IsEqualGUID(&IID_IUnknown, riid) || 00371 IsEqualGUID(&IID_IStorage, riid)) 00372 { 00373 *ppvObject = This; 00374 } 00375 else if (IsEqualGUID(&IID_IPropertySetStorage, riid)) 00376 { 00377 *ppvObject = &This->pssVtbl; 00378 } 00379 00380 if ((*ppvObject)==0) 00381 return E_NOINTERFACE; 00382 00383 IStorage_AddRef(iface); 00384 00385 return S_OK; 00386 } 00387 00388 /************************************************************************ 00389 * Storage32BaseImpl_AddRef (IUnknown) 00390 * 00391 * This method implements the common AddRef for all IStorage32 00392 * implementations contained in this file. 00393 * 00394 * See Windows documentation for more details on IUnknown methods. 00395 */ 00396 static ULONG WINAPI StorageBaseImpl_AddRef( 00397 IStorage* iface) 00398 { 00399 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00400 ULONG ref = InterlockedIncrement(&This->ref); 00401 00402 TRACE("(%p) AddRef to %d\n", This, ref); 00403 00404 return ref; 00405 } 00406 00407 /************************************************************************ 00408 * Storage32BaseImpl_Release (IUnknown) 00409 * 00410 * This method implements the common Release for all IStorage32 00411 * implementations contained in this file. 00412 * 00413 * See Windows documentation for more details on IUnknown methods. 00414 */ 00415 static ULONG WINAPI StorageBaseImpl_Release( 00416 IStorage* iface) 00417 { 00418 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00419 00420 ULONG ref = InterlockedDecrement(&This->ref); 00421 00422 TRACE("(%p) ReleaseRef to %d\n", This, ref); 00423 00424 if (ref == 0) 00425 { 00426 /* 00427 * Since we are using a system of base-classes, we want to call the 00428 * destructor of the appropriate derived class. To do this, we are 00429 * using virtual functions to implement the destructor. 00430 */ 00431 StorageBaseImpl_Destroy(This); 00432 } 00433 00434 return ref; 00435 } 00436 00437 /************************************************************************ 00438 * Storage32BaseImpl_OpenStream (IStorage) 00439 * 00440 * This method will open the specified stream object from the current storage. 00441 * 00442 * See Windows documentation for more details on IStorage methods. 00443 */ 00444 static HRESULT WINAPI StorageBaseImpl_OpenStream( 00445 IStorage* iface, 00446 const OLECHAR* pwcsName, /* [string][in] */ 00447 void* reserved1, /* [unique][in] */ 00448 DWORD grfMode, /* [in] */ 00449 DWORD reserved2, /* [in] */ 00450 IStream** ppstm) /* [out] */ 00451 { 00452 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00453 StgStreamImpl* newStream; 00454 DirEntry currentEntry; 00455 DirRef streamEntryRef; 00456 HRESULT res = STG_E_UNKNOWN; 00457 00458 TRACE("(%p, %s, %p, %x, %d, %p)\n", 00459 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm); 00460 00461 if ( (pwcsName==NULL) || (ppstm==0) ) 00462 { 00463 res = E_INVALIDARG; 00464 goto end; 00465 } 00466 00467 *ppstm = NULL; 00468 00469 if ( FAILED( validateSTGM(grfMode) ) || 00470 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 00471 { 00472 res = STG_E_INVALIDFLAG; 00473 goto end; 00474 } 00475 00476 /* 00477 * As documented. 00478 */ 00479 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) ) 00480 { 00481 res = STG_E_INVALIDFUNCTION; 00482 goto end; 00483 } 00484 00485 if (This->reverted) 00486 { 00487 res = STG_E_REVERTED; 00488 goto end; 00489 } 00490 00491 /* 00492 * Check that we're compatible with the parent's storage mode, but 00493 * only if we are not in transacted mode 00494 */ 00495 if(!(This->openFlags & STGM_TRANSACTED)) { 00496 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 00497 { 00498 res = STG_E_INVALIDFLAG; 00499 goto end; 00500 } 00501 } 00502 00503 /* 00504 * Search for the element with the given name 00505 */ 00506 streamEntryRef = findElement( 00507 This, 00508 This->storageDirEntry, 00509 pwcsName, 00510 ¤tEntry); 00511 00512 /* 00513 * If it was found, construct the stream object and return a pointer to it. 00514 */ 00515 if ( (streamEntryRef!=DIRENTRY_NULL) && 00516 (currentEntry.stgType==STGTY_STREAM) ) 00517 { 00518 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef)) 00519 { 00520 /* A single stream cannot be opened a second time. */ 00521 res = STG_E_ACCESSDENIED; 00522 goto end; 00523 } 00524 00525 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef); 00526 00527 if (newStream!=0) 00528 { 00529 newStream->grfMode = grfMode; 00530 *ppstm = (IStream*)newStream; 00531 00532 IStream_AddRef(*ppstm); 00533 00534 res = S_OK; 00535 goto end; 00536 } 00537 00538 res = E_OUTOFMEMORY; 00539 goto end; 00540 } 00541 00542 res = STG_E_FILENOTFOUND; 00543 00544 end: 00545 if (res == S_OK) 00546 TRACE("<-- IStream %p\n", *ppstm); 00547 TRACE("<-- %08x\n", res); 00548 return res; 00549 } 00550 00551 /************************************************************************ 00552 * Storage32BaseImpl_OpenStorage (IStorage) 00553 * 00554 * This method will open a new storage object from the current storage. 00555 * 00556 * See Windows documentation for more details on IStorage methods. 00557 */ 00558 static HRESULT WINAPI StorageBaseImpl_OpenStorage( 00559 IStorage* iface, 00560 const OLECHAR* pwcsName, /* [string][unique][in] */ 00561 IStorage* pstgPriority, /* [unique][in] */ 00562 DWORD grfMode, /* [in] */ 00563 SNB snbExclude, /* [unique][in] */ 00564 DWORD reserved, /* [in] */ 00565 IStorage** ppstg) /* [out] */ 00566 { 00567 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00568 StorageInternalImpl* newStorage; 00569 StorageBaseImpl* newTransactedStorage; 00570 DirEntry currentEntry; 00571 DirRef storageEntryRef; 00572 HRESULT res = STG_E_UNKNOWN; 00573 00574 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n", 00575 iface, debugstr_w(pwcsName), pstgPriority, 00576 grfMode, snbExclude, reserved, ppstg); 00577 00578 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) ) 00579 { 00580 res = E_INVALIDARG; 00581 goto end; 00582 } 00583 00584 if (This->openFlags & STGM_SIMPLE) 00585 { 00586 res = STG_E_INVALIDFUNCTION; 00587 goto end; 00588 } 00589 00590 /* as documented */ 00591 if (snbExclude != NULL) 00592 { 00593 res = STG_E_INVALIDPARAMETER; 00594 goto end; 00595 } 00596 00597 if ( FAILED( validateSTGM(grfMode) )) 00598 { 00599 res = STG_E_INVALIDFLAG; 00600 goto end; 00601 } 00602 00603 /* 00604 * As documented. 00605 */ 00606 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE || 00607 (grfMode & STGM_DELETEONRELEASE) || 00608 (grfMode & STGM_PRIORITY) ) 00609 { 00610 res = STG_E_INVALIDFUNCTION; 00611 goto end; 00612 } 00613 00614 if (This->reverted) 00615 return STG_E_REVERTED; 00616 00617 /* 00618 * Check that we're compatible with the parent's storage mode, 00619 * but only if we are not transacted 00620 */ 00621 if(!(This->openFlags & STGM_TRANSACTED)) { 00622 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 00623 { 00624 res = STG_E_ACCESSDENIED; 00625 goto end; 00626 } 00627 } 00628 00629 *ppstg = NULL; 00630 00631 storageEntryRef = findElement( 00632 This, 00633 This->storageDirEntry, 00634 pwcsName, 00635 ¤tEntry); 00636 00637 if ( (storageEntryRef!=DIRENTRY_NULL) && 00638 (currentEntry.stgType==STGTY_STORAGE) ) 00639 { 00640 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef)) 00641 { 00642 /* A single storage cannot be opened a second time. */ 00643 res = STG_E_ACCESSDENIED; 00644 goto end; 00645 } 00646 00647 newStorage = StorageInternalImpl_Construct( 00648 This, 00649 grfMode, 00650 storageEntryRef); 00651 00652 if (newStorage != 0) 00653 { 00654 if (grfMode & STGM_TRANSACTED) 00655 { 00656 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage); 00657 00658 if (FAILED(res)) 00659 { 00660 HeapFree(GetProcessHeap(), 0, newStorage); 00661 goto end; 00662 } 00663 00664 *ppstg = (IStorage*)newTransactedStorage; 00665 } 00666 else 00667 { 00668 *ppstg = (IStorage*)newStorage; 00669 } 00670 00671 list_add_tail(&This->storageHead, &newStorage->ParentListEntry); 00672 00673 res = S_OK; 00674 goto end; 00675 } 00676 00677 res = STG_E_INSUFFICIENTMEMORY; 00678 goto end; 00679 } 00680 00681 res = STG_E_FILENOTFOUND; 00682 00683 end: 00684 TRACE("<-- %08x\n", res); 00685 return res; 00686 } 00687 00688 /************************************************************************ 00689 * Storage32BaseImpl_EnumElements (IStorage) 00690 * 00691 * This method will create an enumerator object that can be used to 00692 * retrieve information about all the elements in the storage object. 00693 * 00694 * See Windows documentation for more details on IStorage methods. 00695 */ 00696 static HRESULT WINAPI StorageBaseImpl_EnumElements( 00697 IStorage* iface, 00698 DWORD reserved1, /* [in] */ 00699 void* reserved2, /* [size_is][unique][in] */ 00700 DWORD reserved3, /* [in] */ 00701 IEnumSTATSTG** ppenum) /* [out] */ 00702 { 00703 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00704 IEnumSTATSTGImpl* newEnum; 00705 00706 TRACE("(%p, %d, %p, %d, %p)\n", 00707 iface, reserved1, reserved2, reserved3, ppenum); 00708 00709 if ( (This==0) || (ppenum==0)) 00710 return E_INVALIDARG; 00711 00712 if (This->reverted) 00713 return STG_E_REVERTED; 00714 00715 newEnum = IEnumSTATSTGImpl_Construct( 00716 This, 00717 This->storageDirEntry); 00718 00719 if (newEnum!=0) 00720 { 00721 *ppenum = &newEnum->IEnumSTATSTG_iface; 00722 00723 IEnumSTATSTG_AddRef(*ppenum); 00724 00725 return S_OK; 00726 } 00727 00728 return E_OUTOFMEMORY; 00729 } 00730 00731 /************************************************************************ 00732 * Storage32BaseImpl_Stat (IStorage) 00733 * 00734 * This method will retrieve information about this storage object. 00735 * 00736 * See Windows documentation for more details on IStorage methods. 00737 */ 00738 static HRESULT WINAPI StorageBaseImpl_Stat( 00739 IStorage* iface, 00740 STATSTG* pstatstg, /* [out] */ 00741 DWORD grfStatFlag) /* [in] */ 00742 { 00743 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00744 DirEntry currentEntry; 00745 HRESULT res = STG_E_UNKNOWN; 00746 00747 TRACE("(%p, %p, %x)\n", 00748 iface, pstatstg, grfStatFlag); 00749 00750 if ( (This==0) || (pstatstg==0)) 00751 { 00752 res = E_INVALIDARG; 00753 goto end; 00754 } 00755 00756 if (This->reverted) 00757 { 00758 res = STG_E_REVERTED; 00759 goto end; 00760 } 00761 00762 res = StorageBaseImpl_ReadDirEntry( 00763 This, 00764 This->storageDirEntry, 00765 ¤tEntry); 00766 00767 if (SUCCEEDED(res)) 00768 { 00769 StorageUtl_CopyDirEntryToSTATSTG( 00770 This, 00771 pstatstg, 00772 ¤tEntry, 00773 grfStatFlag); 00774 00775 pstatstg->grfMode = This->openFlags; 00776 pstatstg->grfStateBits = This->stateBits; 00777 } 00778 00779 end: 00780 if (res == S_OK) 00781 { 00782 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits); 00783 } 00784 TRACE("<-- %08x\n", res); 00785 return res; 00786 } 00787 00788 /************************************************************************ 00789 * Storage32BaseImpl_RenameElement (IStorage) 00790 * 00791 * This method will rename the specified element. 00792 * 00793 * See Windows documentation for more details on IStorage methods. 00794 */ 00795 static HRESULT WINAPI StorageBaseImpl_RenameElement( 00796 IStorage* iface, 00797 const OLECHAR* pwcsOldName, /* [in] */ 00798 const OLECHAR* pwcsNewName) /* [in] */ 00799 { 00800 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00801 DirEntry currentEntry; 00802 DirRef currentEntryRef; 00803 00804 TRACE("(%p, %s, %s)\n", 00805 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName)); 00806 00807 if (This->reverted) 00808 return STG_E_REVERTED; 00809 00810 currentEntryRef = findElement(This, 00811 This->storageDirEntry, 00812 pwcsNewName, 00813 ¤tEntry); 00814 00815 if (currentEntryRef != DIRENTRY_NULL) 00816 { 00817 /* 00818 * There is already an element with the new name 00819 */ 00820 return STG_E_FILEALREADYEXISTS; 00821 } 00822 00823 /* 00824 * Search for the old element name 00825 */ 00826 currentEntryRef = findElement(This, 00827 This->storageDirEntry, 00828 pwcsOldName, 00829 ¤tEntry); 00830 00831 if (currentEntryRef != DIRENTRY_NULL) 00832 { 00833 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) || 00834 StorageBaseImpl_IsStorageOpen(This, currentEntryRef)) 00835 { 00836 WARN("Element is already open; cannot rename.\n"); 00837 return STG_E_ACCESSDENIED; 00838 } 00839 00840 /* Remove the element from its current position in the tree */ 00841 removeFromTree(This, This->storageDirEntry, 00842 currentEntryRef); 00843 00844 /* Change the name of the element */ 00845 strcpyW(currentEntry.name, pwcsNewName); 00846 00847 /* Delete any sibling links */ 00848 currentEntry.leftChild = DIRENTRY_NULL; 00849 currentEntry.rightChild = DIRENTRY_NULL; 00850 00851 StorageBaseImpl_WriteDirEntry(This, currentEntryRef, 00852 ¤tEntry); 00853 00854 /* Insert the element in a new position in the tree */ 00855 insertIntoTree(This, This->storageDirEntry, 00856 currentEntryRef); 00857 } 00858 else 00859 { 00860 /* 00861 * There is no element with the old name 00862 */ 00863 return STG_E_FILENOTFOUND; 00864 } 00865 00866 return StorageBaseImpl_Flush(This); 00867 } 00868 00869 /************************************************************************ 00870 * Storage32BaseImpl_CreateStream (IStorage) 00871 * 00872 * This method will create a stream object within this storage 00873 * 00874 * See Windows documentation for more details on IStorage methods. 00875 */ 00876 static HRESULT WINAPI StorageBaseImpl_CreateStream( 00877 IStorage* iface, 00878 const OLECHAR* pwcsName, /* [string][in] */ 00879 DWORD grfMode, /* [in] */ 00880 DWORD reserved1, /* [in] */ 00881 DWORD reserved2, /* [in] */ 00882 IStream** ppstm) /* [out] */ 00883 { 00884 StorageBaseImpl *This = (StorageBaseImpl *)iface; 00885 StgStreamImpl* newStream; 00886 DirEntry currentEntry, newStreamEntry; 00887 DirRef currentEntryRef, newStreamEntryRef; 00888 HRESULT hr; 00889 00890 TRACE("(%p, %s, %x, %d, %d, %p)\n", 00891 iface, debugstr_w(pwcsName), grfMode, 00892 reserved1, reserved2, ppstm); 00893 00894 if (ppstm == 0) 00895 return STG_E_INVALIDPOINTER; 00896 00897 if (pwcsName == 0) 00898 return STG_E_INVALIDNAME; 00899 00900 if (reserved1 || reserved2) 00901 return STG_E_INVALIDPARAMETER; 00902 00903 if ( FAILED( validateSTGM(grfMode) )) 00904 return STG_E_INVALIDFLAG; 00905 00906 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 00907 return STG_E_INVALIDFLAG; 00908 00909 if (This->reverted) 00910 return STG_E_REVERTED; 00911 00912 /* 00913 * As documented. 00914 */ 00915 if ((grfMode & STGM_DELETEONRELEASE) || 00916 (grfMode & STGM_TRANSACTED)) 00917 return STG_E_INVALIDFUNCTION; 00918 00919 /* 00920 * Don't worry about permissions in transacted mode, as we can always write 00921 * changes; we just can't always commit them. 00922 */ 00923 if(!(This->openFlags & STGM_TRANSACTED)) { 00924 /* Can't create a stream on read-only storage */ 00925 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) 00926 return STG_E_ACCESSDENIED; 00927 00928 /* Can't create a stream with greater access than the parent. */ 00929 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 00930 return STG_E_ACCESSDENIED; 00931 } 00932 00933 if(This->openFlags & STGM_SIMPLE) 00934 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG; 00935 00936 *ppstm = 0; 00937 00938 currentEntryRef = findElement(This, 00939 This->storageDirEntry, 00940 pwcsName, 00941 ¤tEntry); 00942 00943 if (currentEntryRef != DIRENTRY_NULL) 00944 { 00945 /* 00946 * An element with this name already exists 00947 */ 00948 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE) 00949 { 00950 IStorage_DestroyElement(iface, pwcsName); 00951 } 00952 else 00953 return STG_E_FILEALREADYEXISTS; 00954 } 00955 00956 /* 00957 * memset the empty entry 00958 */ 00959 memset(&newStreamEntry, 0, sizeof(DirEntry)); 00960 00961 newStreamEntry.sizeOfNameString = 00962 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR); 00963 00964 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) 00965 return STG_E_INVALIDNAME; 00966 00967 strcpyW(newStreamEntry.name, pwcsName); 00968 00969 newStreamEntry.stgType = STGTY_STREAM; 00970 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN; 00971 newStreamEntry.size.u.LowPart = 0; 00972 newStreamEntry.size.u.HighPart = 0; 00973 00974 newStreamEntry.leftChild = DIRENTRY_NULL; 00975 newStreamEntry.rightChild = DIRENTRY_NULL; 00976 newStreamEntry.dirRootEntry = DIRENTRY_NULL; 00977 00978 /* call CoFileTime to get the current time 00979 newStreamEntry.ctime 00980 newStreamEntry.mtime 00981 */ 00982 00983 /* newStreamEntry.clsid */ 00984 00985 /* 00986 * Create an entry with the new data 00987 */ 00988 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef); 00989 if (FAILED(hr)) 00990 return hr; 00991 00992 /* 00993 * Insert the new entry in the parent storage's tree. 00994 */ 00995 hr = insertIntoTree( 00996 This, 00997 This->storageDirEntry, 00998 newStreamEntryRef); 00999 if (FAILED(hr)) 01000 { 01001 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef); 01002 return hr; 01003 } 01004 01005 /* 01006 * Open the stream to return it. 01007 */ 01008 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef); 01009 01010 if (newStream != 0) 01011 { 01012 *ppstm = (IStream*)newStream; 01013 01014 IStream_AddRef(*ppstm); 01015 } 01016 else 01017 { 01018 return STG_E_INSUFFICIENTMEMORY; 01019 } 01020 01021 return StorageBaseImpl_Flush(This); 01022 } 01023 01024 /************************************************************************ 01025 * Storage32BaseImpl_SetClass (IStorage) 01026 * 01027 * This method will write the specified CLSID in the directory entry of this 01028 * storage. 01029 * 01030 * See Windows documentation for more details on IStorage methods. 01031 */ 01032 static HRESULT WINAPI StorageBaseImpl_SetClass( 01033 IStorage* iface, 01034 REFCLSID clsid) /* [in] */ 01035 { 01036 StorageBaseImpl *This = (StorageBaseImpl *)iface; 01037 HRESULT hRes; 01038 DirEntry currentEntry; 01039 01040 TRACE("(%p, %p)\n", iface, clsid); 01041 01042 if (This->reverted) 01043 return STG_E_REVERTED; 01044 01045 hRes = StorageBaseImpl_ReadDirEntry(This, 01046 This->storageDirEntry, 01047 ¤tEntry); 01048 if (SUCCEEDED(hRes)) 01049 { 01050 currentEntry.clsid = *clsid; 01051 01052 hRes = StorageBaseImpl_WriteDirEntry(This, 01053 This->storageDirEntry, 01054 ¤tEntry); 01055 } 01056 01057 if (SUCCEEDED(hRes)) 01058 hRes = StorageBaseImpl_Flush(This); 01059 01060 return hRes; 01061 } 01062 01063 /************************************************************************ 01064 ** Storage32Impl implementation 01065 */ 01066 01067 /************************************************************************ 01068 * Storage32BaseImpl_CreateStorage (IStorage) 01069 * 01070 * This method will create the storage object within the provided storage. 01071 * 01072 * See Windows documentation for more details on IStorage methods. 01073 */ 01074 static HRESULT WINAPI StorageBaseImpl_CreateStorage( 01075 IStorage* iface, 01076 const OLECHAR *pwcsName, /* [string][in] */ 01077 DWORD grfMode, /* [in] */ 01078 DWORD reserved1, /* [in] */ 01079 DWORD reserved2, /* [in] */ 01080 IStorage **ppstg) /* [out] */ 01081 { 01082 StorageBaseImpl* const This=(StorageBaseImpl*)iface; 01083 01084 DirEntry currentEntry; 01085 DirEntry newEntry; 01086 DirRef currentEntryRef; 01087 DirRef newEntryRef; 01088 HRESULT hr; 01089 01090 TRACE("(%p, %s, %x, %d, %d, %p)\n", 01091 iface, debugstr_w(pwcsName), grfMode, 01092 reserved1, reserved2, ppstg); 01093 01094 if (ppstg == 0) 01095 return STG_E_INVALIDPOINTER; 01096 01097 if (This->openFlags & STGM_SIMPLE) 01098 { 01099 return STG_E_INVALIDFUNCTION; 01100 } 01101 01102 if (pwcsName == 0) 01103 return STG_E_INVALIDNAME; 01104 01105 *ppstg = NULL; 01106 01107 if ( FAILED( validateSTGM(grfMode) ) || 01108 (grfMode & STGM_DELETEONRELEASE) ) 01109 { 01110 WARN("bad grfMode: 0x%x\n", grfMode); 01111 return STG_E_INVALIDFLAG; 01112 } 01113 01114 if (This->reverted) 01115 return STG_E_REVERTED; 01116 01117 /* 01118 * Check that we're compatible with the parent's storage mode 01119 */ 01120 if ( !(This->openFlags & STGM_TRANSACTED) && 01121 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 01122 { 01123 WARN("access denied\n"); 01124 return STG_E_ACCESSDENIED; 01125 } 01126 01127 currentEntryRef = findElement(This, 01128 This->storageDirEntry, 01129 pwcsName, 01130 ¤tEntry); 01131 01132 if (currentEntryRef != DIRENTRY_NULL) 01133 { 01134 /* 01135 * An element with this name already exists 01136 */ 01137 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE && 01138 ((This->openFlags & STGM_TRANSACTED) || 01139 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)) 01140 { 01141 hr = IStorage_DestroyElement(iface, pwcsName); 01142 if (FAILED(hr)) 01143 return hr; 01144 } 01145 else 01146 { 01147 WARN("file already exists\n"); 01148 return STG_E_FILEALREADYEXISTS; 01149 } 01150 } 01151 else if (!(This->openFlags & STGM_TRANSACTED) && 01152 STGM_ACCESS_MODE(This->openFlags) == STGM_READ) 01153 { 01154 WARN("read-only storage\n"); 01155 return STG_E_ACCESSDENIED; 01156 } 01157 01158 memset(&newEntry, 0, sizeof(DirEntry)); 01159 01160 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR); 01161 01162 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) 01163 { 01164 FIXME("name too long\n"); 01165 return STG_E_INVALIDNAME; 01166 } 01167 01168 strcpyW(newEntry.name, pwcsName); 01169 01170 newEntry.stgType = STGTY_STORAGE; 01171 newEntry.startingBlock = BLOCK_END_OF_CHAIN; 01172 newEntry.size.u.LowPart = 0; 01173 newEntry.size.u.HighPart = 0; 01174 01175 newEntry.leftChild = DIRENTRY_NULL; 01176 newEntry.rightChild = DIRENTRY_NULL; 01177 newEntry.dirRootEntry = DIRENTRY_NULL; 01178 01179 /* call CoFileTime to get the current time 01180 newEntry.ctime 01181 newEntry.mtime 01182 */ 01183 01184 /* newEntry.clsid */ 01185 01186 /* 01187 * Create a new directory entry for the storage 01188 */ 01189 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef); 01190 if (FAILED(hr)) 01191 return hr; 01192 01193 /* 01194 * Insert the new directory entry into the parent storage's tree 01195 */ 01196 hr = insertIntoTree( 01197 This, 01198 This->storageDirEntry, 01199 newEntryRef); 01200 if (FAILED(hr)) 01201 { 01202 StorageBaseImpl_DestroyDirEntry(This, newEntryRef); 01203 return hr; 01204 } 01205 01206 /* 01207 * Open it to get a pointer to return. 01208 */ 01209 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg); 01210 01211 if( (hr != S_OK) || (*ppstg == NULL)) 01212 { 01213 return hr; 01214 } 01215 01216 if (SUCCEEDED(hr)) 01217 hr = StorageBaseImpl_Flush(This); 01218 01219 return S_OK; 01220 } 01221 01222 01223 /*************************************************************************** 01224 * 01225 * Internal Method 01226 * 01227 * Reserve a directory entry in the file and initialize it. 01228 */ 01229 static HRESULT StorageImpl_CreateDirEntry( 01230 StorageBaseImpl *base, 01231 const DirEntry *newData, 01232 DirRef *index) 01233 { 01234 StorageImpl *storage = (StorageImpl*)base; 01235 ULONG currentEntryIndex = 0; 01236 ULONG newEntryIndex = DIRENTRY_NULL; 01237 HRESULT hr = S_OK; 01238 BYTE currentData[RAW_DIRENTRY_SIZE]; 01239 WORD sizeOfNameString; 01240 01241 do 01242 { 01243 hr = StorageImpl_ReadRawDirEntry(storage, 01244 currentEntryIndex, 01245 currentData); 01246 01247 if (SUCCEEDED(hr)) 01248 { 01249 StorageUtl_ReadWord( 01250 currentData, 01251 OFFSET_PS_NAMELENGTH, 01252 &sizeOfNameString); 01253 01254 if (sizeOfNameString == 0) 01255 { 01256 /* 01257 * The entry exists and is available, we found it. 01258 */ 01259 newEntryIndex = currentEntryIndex; 01260 } 01261 } 01262 else 01263 { 01264 /* 01265 * We exhausted the directory entries, we will create more space below 01266 */ 01267 newEntryIndex = currentEntryIndex; 01268 } 01269 currentEntryIndex++; 01270 01271 } while (newEntryIndex == DIRENTRY_NULL); 01272 01273 /* 01274 * grow the directory stream 01275 */ 01276 if (FAILED(hr)) 01277 { 01278 BYTE emptyData[RAW_DIRENTRY_SIZE]; 01279 ULARGE_INTEGER newSize; 01280 ULONG entryIndex; 01281 ULONG lastEntry = 0; 01282 ULONG blockCount = 0; 01283 01284 /* 01285 * obtain the new count of blocks in the directory stream 01286 */ 01287 blockCount = BlockChainStream_GetCount( 01288 storage->rootBlockChain)+1; 01289 01290 /* 01291 * initialize the size used by the directory stream 01292 */ 01293 newSize.u.HighPart = 0; 01294 newSize.u.LowPart = storage->bigBlockSize * blockCount; 01295 01296 /* 01297 * add a block to the directory stream 01298 */ 01299 BlockChainStream_SetSize(storage->rootBlockChain, newSize); 01300 01301 /* 01302 * memset the empty entry in order to initialize the unused newly 01303 * created entries 01304 */ 01305 memset(emptyData, 0, RAW_DIRENTRY_SIZE); 01306 01307 /* 01308 * initialize them 01309 */ 01310 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount; 01311 01312 for( 01313 entryIndex = newEntryIndex + 1; 01314 entryIndex < lastEntry; 01315 entryIndex++) 01316 { 01317 StorageImpl_WriteRawDirEntry( 01318 storage, 01319 entryIndex, 01320 emptyData); 01321 } 01322 01323 StorageImpl_SaveFileHeader(storage); 01324 } 01325 01326 UpdateRawDirEntry(currentData, newData); 01327 01328 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData); 01329 01330 if (SUCCEEDED(hr)) 01331 *index = newEntryIndex; 01332 01333 return hr; 01334 } 01335 01336 /*************************************************************************** 01337 * 01338 * Internal Method 01339 * 01340 * Mark a directory entry in the file as free. 01341 */ 01342 static HRESULT StorageImpl_DestroyDirEntry( 01343 StorageBaseImpl *base, 01344 DirRef index) 01345 { 01346 HRESULT hr; 01347 BYTE emptyData[RAW_DIRENTRY_SIZE]; 01348 StorageImpl *storage = (StorageImpl*)base; 01349 01350 memset(emptyData, 0, RAW_DIRENTRY_SIZE); 01351 01352 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData); 01353 01354 return hr; 01355 } 01356 01357 01358 /**************************************************************************** 01359 * 01360 * Internal Method 01361 * 01362 * Case insensitive comparison of DirEntry.name by first considering 01363 * their size. 01364 * 01365 * Returns <0 when name1 < name2 01366 * >0 when name1 > name2 01367 * 0 when name1 == name2 01368 */ 01369 static LONG entryNameCmp( 01370 const OLECHAR *name1, 01371 const OLECHAR *name2) 01372 { 01373 LONG diff = lstrlenW(name1) - lstrlenW(name2); 01374 01375 while (diff == 0 && *name1 != 0) 01376 { 01377 /* 01378 * We compare the string themselves only when they are of the same length 01379 */ 01380 diff = toupperW(*name1++) - toupperW(*name2++); 01381 } 01382 01383 return diff; 01384 } 01385 01386 /**************************************************************************** 01387 * 01388 * Internal Method 01389 * 01390 * Add a directory entry to a storage 01391 */ 01392 static HRESULT insertIntoTree( 01393 StorageBaseImpl *This, 01394 DirRef parentStorageIndex, 01395 DirRef newEntryIndex) 01396 { 01397 DirEntry currentEntry; 01398 DirEntry newEntry; 01399 01400 /* 01401 * Read the inserted entry 01402 */ 01403 StorageBaseImpl_ReadDirEntry(This, 01404 newEntryIndex, 01405 &newEntry); 01406 01407 /* 01408 * Read the storage entry 01409 */ 01410 StorageBaseImpl_ReadDirEntry(This, 01411 parentStorageIndex, 01412 ¤tEntry); 01413 01414 if (currentEntry.dirRootEntry != DIRENTRY_NULL) 01415 { 01416 /* 01417 * The root storage contains some element, therefore, start the research 01418 * for the appropriate location. 01419 */ 01420 BOOL found = 0; 01421 DirRef current, next, previous, currentEntryId; 01422 01423 /* 01424 * Keep a reference to the root of the storage's element tree 01425 */ 01426 currentEntryId = currentEntry.dirRootEntry; 01427 01428 /* 01429 * Read 01430 */ 01431 StorageBaseImpl_ReadDirEntry(This, 01432 currentEntry.dirRootEntry, 01433 ¤tEntry); 01434 01435 previous = currentEntry.leftChild; 01436 next = currentEntry.rightChild; 01437 current = currentEntryId; 01438 01439 while (found == 0) 01440 { 01441 LONG diff = entryNameCmp( newEntry.name, currentEntry.name); 01442 01443 if (diff < 0) 01444 { 01445 if (previous != DIRENTRY_NULL) 01446 { 01447 StorageBaseImpl_ReadDirEntry(This, 01448 previous, 01449 ¤tEntry); 01450 current = previous; 01451 } 01452 else 01453 { 01454 currentEntry.leftChild = newEntryIndex; 01455 StorageBaseImpl_WriteDirEntry(This, 01456 current, 01457 ¤tEntry); 01458 found = 1; 01459 } 01460 } 01461 else if (diff > 0) 01462 { 01463 if (next != DIRENTRY_NULL) 01464 { 01465 StorageBaseImpl_ReadDirEntry(This, 01466 next, 01467 ¤tEntry); 01468 current = next; 01469 } 01470 else 01471 { 01472 currentEntry.rightChild = newEntryIndex; 01473 StorageBaseImpl_WriteDirEntry(This, 01474 current, 01475 ¤tEntry); 01476 found = 1; 01477 } 01478 } 01479 else 01480 { 01481 /* 01482 * Trying to insert an item with the same name in the 01483 * subtree structure. 01484 */ 01485 return STG_E_FILEALREADYEXISTS; 01486 } 01487 01488 previous = currentEntry.leftChild; 01489 next = currentEntry.rightChild; 01490 } 01491 } 01492 else 01493 { 01494 /* 01495 * The storage is empty, make the new entry the root of its element tree 01496 */ 01497 currentEntry.dirRootEntry = newEntryIndex; 01498 StorageBaseImpl_WriteDirEntry(This, 01499 parentStorageIndex, 01500 ¤tEntry); 01501 } 01502 01503 return S_OK; 01504 } 01505 01506 /**************************************************************************** 01507 * 01508 * Internal Method 01509 * 01510 * Find and read the element of a storage with the given name. 01511 */ 01512 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry, 01513 const OLECHAR *name, DirEntry *data) 01514 { 01515 DirRef currentEntry; 01516 01517 /* Read the storage entry to find the root of the tree. */ 01518 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data); 01519 01520 currentEntry = data->dirRootEntry; 01521 01522 while (currentEntry != DIRENTRY_NULL) 01523 { 01524 LONG cmp; 01525 01526 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data); 01527 01528 cmp = entryNameCmp(name, data->name); 01529 01530 if (cmp == 0) 01531 /* found it */ 01532 break; 01533 01534 else if (cmp < 0) 01535 currentEntry = data->leftChild; 01536 01537 else if (cmp > 0) 01538 currentEntry = data->rightChild; 01539 } 01540 01541 return currentEntry; 01542 } 01543 01544 /**************************************************************************** 01545 * 01546 * Internal Method 01547 * 01548 * Find and read the binary tree parent of the element with the given name. 01549 * 01550 * If there is no such element, find a place where it could be inserted and 01551 * return STG_E_FILENOTFOUND. 01552 */ 01553 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry, 01554 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry, 01555 ULONG *relation) 01556 { 01557 DirRef childEntry; 01558 DirEntry childData; 01559 01560 /* Read the storage entry to find the root of the tree. */ 01561 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData); 01562 01563 *parentEntry = storageEntry; 01564 *relation = DIRENTRY_RELATION_DIR; 01565 01566 childEntry = parentData->dirRootEntry; 01567 01568 while (childEntry != DIRENTRY_NULL) 01569 { 01570 LONG cmp; 01571 01572 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData); 01573 01574 cmp = entryNameCmp(childName, childData.name); 01575 01576 if (cmp == 0) 01577 /* found it */ 01578 break; 01579 01580 else if (cmp < 0) 01581 { 01582 *parentData = childData; 01583 *parentEntry = childEntry; 01584 *relation = DIRENTRY_RELATION_PREVIOUS; 01585 01586 childEntry = parentData->leftChild; 01587 } 01588 01589 else if (cmp > 0) 01590 { 01591 *parentData = childData; 01592 *parentEntry = childEntry; 01593 *relation = DIRENTRY_RELATION_NEXT; 01594 01595 childEntry = parentData->rightChild; 01596 } 01597 } 01598 01599 if (childEntry == DIRENTRY_NULL) 01600 return STG_E_FILENOTFOUND; 01601 else 01602 return S_OK; 01603 } 01604 01605 01606 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, 01607 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 01608 SNB snbExclude, IStorage *pstgDest); 01609 01610 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This, 01611 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 01612 SNB snbExclude, IStorage *pstgDest) 01613 { 01614 DirEntry data; 01615 HRESULT hr; 01616 BOOL skip = FALSE; 01617 IStorage *pstgTmp; 01618 IStream *pstrChild, *pstrTmp; 01619 STATSTG strStat; 01620 01621 if (srcEntry == DIRENTRY_NULL) 01622 return S_OK; 01623 01624 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); 01625 01626 if (FAILED(hr)) 01627 return hr; 01628 01629 if ( snbExclude ) 01630 { 01631 WCHAR **snb = snbExclude; 01632 01633 while ( *snb != NULL && !skip ) 01634 { 01635 if ( lstrcmpW(data.name, *snb) == 0 ) 01636 skip = TRUE; 01637 ++snb; 01638 } 01639 } 01640 01641 if (!skip) 01642 { 01643 if (data.stgType == STGTY_STORAGE && !skip_storage) 01644 { 01645 /* 01646 * create a new storage in destination storage 01647 */ 01648 hr = IStorage_CreateStorage( pstgDest, data.name, 01649 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 01650 0, 0, 01651 &pstgTmp ); 01652 01653 /* 01654 * if it already exist, don't create a new one use this one 01655 */ 01656 if (hr == STG_E_FILEALREADYEXISTS) 01657 { 01658 hr = IStorage_OpenStorage( pstgDest, data.name, NULL, 01659 STGM_WRITE|STGM_SHARE_EXCLUSIVE, 01660 NULL, 0, &pstgTmp ); 01661 } 01662 01663 if (SUCCEEDED(hr)) 01664 { 01665 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage, 01666 skip_stream, NULL, pstgTmp ); 01667 01668 IStorage_Release(pstgTmp); 01669 } 01670 } 01671 else if (data.stgType == STGTY_STREAM && !skip_stream) 01672 { 01673 /* 01674 * create a new stream in destination storage. If the stream already 01675 * exist, it will be deleted and a new one will be created. 01676 */ 01677 hr = IStorage_CreateStream( pstgDest, data.name, 01678 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 01679 0, 0, &pstrTmp ); 01680 01681 /* 01682 * open child stream storage. This operation must succeed even if the 01683 * stream is already open, so we use internal functions to do it. 01684 */ 01685 if (hr == S_OK) 01686 { 01687 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry); 01688 if (pstrChild) 01689 IStream_AddRef(pstrChild); 01690 else 01691 hr = E_OUTOFMEMORY; 01692 } 01693 01694 if (hr == S_OK) 01695 { 01696 /* 01697 * Get the size of the source stream 01698 */ 01699 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME ); 01700 01701 /* 01702 * Set the size of the destination stream. 01703 */ 01704 IStream_SetSize(pstrTmp, strStat.cbSize); 01705 01706 /* 01707 * do the copy 01708 */ 01709 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize, 01710 NULL, NULL ); 01711 01712 IStream_Release( pstrChild ); 01713 } 01714 01715 IStream_Release( pstrTmp ); 01716 } 01717 } 01718 01719 /* copy siblings */ 01720 if (SUCCEEDED(hr)) 01721 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage, 01722 skip_stream, snbExclude, pstgDest ); 01723 01724 if (SUCCEEDED(hr)) 01725 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage, 01726 skip_stream, snbExclude, pstgDest ); 01727 01728 return hr; 01729 } 01730 01731 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, 01732 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 01733 SNB snbExclude, IStorage *pstgDest) 01734 { 01735 DirEntry data; 01736 HRESULT hr; 01737 01738 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); 01739 01740 if (SUCCEEDED(hr)) 01741 hr = IStorage_SetClass( pstgDest, &data.clsid ); 01742 01743 if (SUCCEEDED(hr)) 01744 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage, 01745 skip_stream, snbExclude, pstgDest ); 01746 01747 return hr; 01748 } 01749 01750 /************************************************************************* 01751 * CopyTo (IStorage) 01752 */ 01753 static HRESULT WINAPI StorageBaseImpl_CopyTo( 01754 IStorage* iface, 01755 DWORD ciidExclude, /* [in] */ 01756 const IID* rgiidExclude, /* [size_is][unique][in] */ 01757 SNB snbExclude, /* [unique][in] */ 01758 IStorage* pstgDest) /* [unique][in] */ 01759 { 01760 StorageBaseImpl* const This=(StorageBaseImpl*)iface; 01761 01762 BOOL skip_storage = FALSE, skip_stream = FALSE; 01763 int i; 01764 01765 TRACE("(%p, %d, %p, %p, %p)\n", 01766 iface, ciidExclude, rgiidExclude, 01767 snbExclude, pstgDest); 01768 01769 if ( pstgDest == 0 ) 01770 return STG_E_INVALIDPOINTER; 01771 01772 for(i = 0; i < ciidExclude; ++i) 01773 { 01774 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i])) 01775 skip_storage = TRUE; 01776 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i])) 01777 skip_stream = TRUE; 01778 else 01779 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i])); 01780 } 01781 01782 if (!skip_storage) 01783 { 01784 /* Give up early if it looks like this would be infinitely recursive. 01785 * Oddly enough, this includes some cases that aren't really recursive, like 01786 * copying to a transacted child. */ 01787 IStorage *pstgDestAncestor = pstgDest; 01788 IStorage *pstgDestAncestorChild = NULL; 01789 01790 /* Go up the chain from the destination until we find the source storage. */ 01791 while (pstgDestAncestor != iface) { 01792 pstgDestAncestorChild = pstgDest; 01793 01794 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl) 01795 { 01796 TransactedSnapshotImpl *impl = (TransactedSnapshotImpl*) pstgDestAncestor; 01797 01798 pstgDestAncestor = (IStorage*)impl->transactedParent; 01799 } 01800 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl) 01801 { 01802 StorageInternalImpl *impl = (StorageInternalImpl*) pstgDestAncestor; 01803 01804 pstgDestAncestor = (IStorage*)impl->parentStorage; 01805 } 01806 else 01807 break; 01808 } 01809 01810 if (pstgDestAncestor == iface) 01811 { 01812 BOOL fail = TRUE; 01813 01814 if (pstgDestAncestorChild && snbExclude) 01815 { 01816 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild; 01817 DirEntry data; 01818 WCHAR **snb = snbExclude; 01819 01820 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data); 01821 01822 while ( *snb != NULL && fail ) 01823 { 01824 if ( lstrcmpW(data.name, *snb) == 0 ) 01825 fail = FALSE; 01826 ++snb; 01827 } 01828 } 01829 01830 if (fail) 01831 return STG_E_ACCESSDENIED; 01832 } 01833 } 01834 01835 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry, 01836 skip_storage, skip_stream, snbExclude, pstgDest ); 01837 } 01838 01839 /************************************************************************* 01840 * MoveElementTo (IStorage) 01841 */ 01842 static HRESULT WINAPI StorageBaseImpl_MoveElementTo( 01843 IStorage* iface, 01844 const OLECHAR *pwcsName, /* [string][in] */ 01845 IStorage *pstgDest, /* [unique][in] */ 01846 const OLECHAR *pwcsNewName,/* [string][in] */ 01847 DWORD grfFlags) /* [in] */ 01848 { 01849 FIXME("(%p %s %p %s %u): stub\n", iface, 01850 debugstr_w(pwcsName), pstgDest, 01851 debugstr_w(pwcsNewName), grfFlags); 01852 return E_NOTIMPL; 01853 } 01854 01855 /************************************************************************* 01856 * Commit (IStorage) 01857 * 01858 * Ensures that any changes made to a storage object open in transacted mode 01859 * are reflected in the parent storage 01860 * 01861 * In a non-transacted mode, this ensures all cached writes are completed. 01862 */ 01863 static HRESULT WINAPI StorageImpl_Commit( 01864 IStorage* iface, 01865 DWORD grfCommitFlags)/* [in] */ 01866 { 01867 StorageBaseImpl* const base=(StorageBaseImpl*)iface; 01868 TRACE("(%p %d)\n", iface, grfCommitFlags); 01869 return StorageBaseImpl_Flush(base); 01870 } 01871 01872 /************************************************************************* 01873 * Revert (IStorage) 01874 * 01875 * Discard all changes that have been made since the last commit operation 01876 */ 01877 static HRESULT WINAPI StorageImpl_Revert( 01878 IStorage* iface) 01879 { 01880 TRACE("(%p)\n", iface); 01881 return S_OK; 01882 } 01883 01884 /************************************************************************* 01885 * DestroyElement (IStorage) 01886 * 01887 * Strategy: This implementation is built this way for simplicity not for speed. 01888 * I always delete the topmost element of the enumeration and adjust 01889 * the deleted element pointer all the time. This takes longer to 01890 * do but allow to reinvoke DestroyElement whenever we encounter a 01891 * storage object. The optimisation resides in the usage of another 01892 * enumeration strategy that would give all the leaves of a storage 01893 * first. (postfix order) 01894 */ 01895 static HRESULT WINAPI StorageBaseImpl_DestroyElement( 01896 IStorage* iface, 01897 const OLECHAR *pwcsName)/* [string][in] */ 01898 { 01899 StorageBaseImpl* const This=(StorageBaseImpl*)iface; 01900 01901 HRESULT hr = S_OK; 01902 DirEntry entryToDelete; 01903 DirRef entryToDeleteRef; 01904 01905 TRACE("(%p, %s)\n", 01906 iface, debugstr_w(pwcsName)); 01907 01908 if (pwcsName==NULL) 01909 return STG_E_INVALIDPOINTER; 01910 01911 if (This->reverted) 01912 return STG_E_REVERTED; 01913 01914 if ( !(This->openFlags & STGM_TRANSACTED) && 01915 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) 01916 return STG_E_ACCESSDENIED; 01917 01918 entryToDeleteRef = findElement( 01919 This, 01920 This->storageDirEntry, 01921 pwcsName, 01922 &entryToDelete); 01923 01924 if ( entryToDeleteRef == DIRENTRY_NULL ) 01925 { 01926 return STG_E_FILENOTFOUND; 01927 } 01928 01929 if ( entryToDelete.stgType == STGTY_STORAGE ) 01930 { 01931 hr = deleteStorageContents( 01932 This, 01933 entryToDeleteRef, 01934 entryToDelete); 01935 } 01936 else if ( entryToDelete.stgType == STGTY_STREAM ) 01937 { 01938 hr = deleteStreamContents( 01939 This, 01940 entryToDeleteRef, 01941 entryToDelete); 01942 } 01943 01944 if (hr!=S_OK) 01945 return hr; 01946 01947 /* 01948 * Remove the entry from its parent storage 01949 */ 01950 hr = removeFromTree( 01951 This, 01952 This->storageDirEntry, 01953 entryToDeleteRef); 01954 01955 /* 01956 * Invalidate the entry 01957 */ 01958 if (SUCCEEDED(hr)) 01959 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef); 01960 01961 if (SUCCEEDED(hr)) 01962 hr = StorageBaseImpl_Flush(This); 01963 01964 return hr; 01965 } 01966 01967 01968 /****************************************************************************** 01969 * Internal stream list handlers 01970 */ 01971 01972 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) 01973 { 01974 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm); 01975 list_add_tail(&stg->strmHead,&strm->StrmListEntry); 01976 } 01977 01978 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) 01979 { 01980 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm); 01981 list_remove(&(strm->StrmListEntry)); 01982 } 01983 01984 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) 01985 { 01986 StgStreamImpl *strm; 01987 01988 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry) 01989 { 01990 if (strm->dirEntry == streamEntry) 01991 { 01992 return TRUE; 01993 } 01994 } 01995 01996 return FALSE; 01997 } 01998 01999 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry) 02000 { 02001 StorageInternalImpl *childstg; 02002 02003 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) 02004 { 02005 if (childstg->base.storageDirEntry == storageEntry) 02006 { 02007 return TRUE; 02008 } 02009 } 02010 02011 return FALSE; 02012 } 02013 02014 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg) 02015 { 02016 struct list *cur, *cur2; 02017 StgStreamImpl *strm=NULL; 02018 StorageInternalImpl *childstg=NULL; 02019 02020 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) { 02021 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry); 02022 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev); 02023 strm->parentStorage = NULL; 02024 list_remove(cur); 02025 } 02026 02027 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) { 02028 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry); 02029 StorageBaseImpl_Invalidate( &childstg->base ); 02030 } 02031 02032 if (stg->transactedChild) 02033 { 02034 StorageBaseImpl_Invalidate(stg->transactedChild); 02035 02036 stg->transactedChild = NULL; 02037 } 02038 } 02039 02040 02041 /********************************************************************* 02042 * 02043 * Internal Method 02044 * 02045 * Delete the contents of a storage entry. 02046 * 02047 */ 02048 static HRESULT deleteStorageContents( 02049 StorageBaseImpl *parentStorage, 02050 DirRef indexToDelete, 02051 DirEntry entryDataToDelete) 02052 { 02053 IEnumSTATSTG *elements = 0; 02054 IStorage *childStorage = 0; 02055 STATSTG currentElement; 02056 HRESULT hr; 02057 HRESULT destroyHr = S_OK; 02058 StorageInternalImpl *stg, *stg2; 02059 02060 /* Invalidate any open storage objects. */ 02061 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry) 02062 { 02063 if (stg->base.storageDirEntry == indexToDelete) 02064 { 02065 StorageBaseImpl_Invalidate(&stg->base); 02066 } 02067 } 02068 02069 /* 02070 * Open the storage and enumerate it 02071 */ 02072 hr = StorageBaseImpl_OpenStorage( 02073 (IStorage*)parentStorage, 02074 entryDataToDelete.name, 02075 0, 02076 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 02077 0, 02078 0, 02079 &childStorage); 02080 02081 if (hr != S_OK) 02082 { 02083 return hr; 02084 } 02085 02086 /* 02087 * Enumerate the elements 02088 */ 02089 IStorage_EnumElements( childStorage, 0, 0, 0, &elements); 02090 02091 do 02092 { 02093 /* 02094 * Obtain the next element 02095 */ 02096 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL); 02097 if (hr==S_OK) 02098 { 02099 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName); 02100 02101 CoTaskMemFree(currentElement.pwcsName); 02102 } 02103 02104 /* 02105 * We need to Reset the enumeration every time because we delete elements 02106 * and the enumeration could be invalid 02107 */ 02108 IEnumSTATSTG_Reset(elements); 02109 02110 } while ((hr == S_OK) && (destroyHr == S_OK)); 02111 02112 IStorage_Release(childStorage); 02113 IEnumSTATSTG_Release(elements); 02114 02115 return destroyHr; 02116 } 02117 02118 /********************************************************************* 02119 * 02120 * Internal Method 02121 * 02122 * Perform the deletion of a stream's data 02123 * 02124 */ 02125 static HRESULT deleteStreamContents( 02126 StorageBaseImpl *parentStorage, 02127 DirRef indexToDelete, 02128 DirEntry entryDataToDelete) 02129 { 02130 IStream *pis; 02131 HRESULT hr; 02132 ULARGE_INTEGER size; 02133 StgStreamImpl *strm, *strm2; 02134 02135 /* Invalidate any open stream objects. */ 02136 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry) 02137 { 02138 if (strm->dirEntry == indexToDelete) 02139 { 02140 TRACE("Stream deleted %p\n", strm); 02141 strm->parentStorage = NULL; 02142 list_remove(&strm->StrmListEntry); 02143 } 02144 } 02145 02146 size.u.HighPart = 0; 02147 size.u.LowPart = 0; 02148 02149 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage, 02150 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); 02151 02152 if (hr!=S_OK) 02153 { 02154 return(hr); 02155 } 02156 02157 /* 02158 * Zap the stream 02159 */ 02160 hr = IStream_SetSize(pis, size); 02161 02162 if(hr != S_OK) 02163 { 02164 return hr; 02165 } 02166 02167 /* 02168 * Release the stream object. 02169 */ 02170 IStream_Release(pis); 02171 02172 return S_OK; 02173 } 02174 02175 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target) 02176 { 02177 switch (relation) 02178 { 02179 case DIRENTRY_RELATION_PREVIOUS: 02180 entry->leftChild = new_target; 02181 break; 02182 case DIRENTRY_RELATION_NEXT: 02183 entry->rightChild = new_target; 02184 break; 02185 case DIRENTRY_RELATION_DIR: 02186 entry->dirRootEntry = new_target; 02187 break; 02188 default: 02189 assert(0); 02190 } 02191 } 02192 02193 /************************************************************************* 02194 * 02195 * Internal Method 02196 * 02197 * This method removes a directory entry from its parent storage tree without 02198 * freeing any resources attached to it. 02199 */ 02200 static HRESULT removeFromTree( 02201 StorageBaseImpl *This, 02202 DirRef parentStorageIndex, 02203 DirRef deletedIndex) 02204 { 02205 HRESULT hr = S_OK; 02206 DirEntry entryToDelete; 02207 DirEntry parentEntry; 02208 DirRef parentEntryRef; 02209 ULONG typeOfRelation; 02210 02211 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete); 02212 02213 if (hr != S_OK) 02214 return hr; 02215 02216 /* 02217 * Find the element that links to the one we want to delete. 02218 */ 02219 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name, 02220 &parentEntry, &parentEntryRef, &typeOfRelation); 02221 02222 if (hr != S_OK) 02223 return hr; 02224 02225 if (entryToDelete.leftChild != DIRENTRY_NULL) 02226 { 02227 /* 02228 * Replace the deleted entry with its left child 02229 */ 02230 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild); 02231 02232 hr = StorageBaseImpl_WriteDirEntry( 02233 This, 02234 parentEntryRef, 02235 &parentEntry); 02236 if(FAILED(hr)) 02237 { 02238 return hr; 02239 } 02240 02241 if (entryToDelete.rightChild != DIRENTRY_NULL) 02242 { 02243 /* 02244 * We need to reinsert the right child somewhere. We already know it and 02245 * its children are greater than everything in the left tree, so we 02246 * insert it at the rightmost point in the left tree. 02247 */ 02248 DirRef newRightChildParent = entryToDelete.leftChild; 02249 DirEntry newRightChildParentEntry; 02250 02251 do 02252 { 02253 hr = StorageBaseImpl_ReadDirEntry( 02254 This, 02255 newRightChildParent, 02256 &newRightChildParentEntry); 02257 if (FAILED(hr)) 02258 { 02259 return hr; 02260 } 02261 02262 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL) 02263 newRightChildParent = newRightChildParentEntry.rightChild; 02264 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL); 02265 02266 newRightChildParentEntry.rightChild = entryToDelete.rightChild; 02267 02268 hr = StorageBaseImpl_WriteDirEntry( 02269 This, 02270 newRightChildParent, 02271 &newRightChildParentEntry); 02272 if (FAILED(hr)) 02273 { 02274 return hr; 02275 } 02276 } 02277 } 02278 else 02279 { 02280 /* 02281 * Replace the deleted entry with its right child 02282 */ 02283 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild); 02284 02285 hr = StorageBaseImpl_WriteDirEntry( 02286 This, 02287 parentEntryRef, 02288 &parentEntry); 02289 if(FAILED(hr)) 02290 { 02291 return hr; 02292 } 02293 } 02294 02295 return hr; 02296 } 02297 02298 02299 /****************************************************************************** 02300 * SetElementTimes (IStorage) 02301 */ 02302 static HRESULT WINAPI StorageBaseImpl_SetElementTimes( 02303 IStorage* iface, 02304 const OLECHAR *pwcsName,/* [string][in] */ 02305 const FILETIME *pctime, /* [in] */ 02306 const FILETIME *patime, /* [in] */ 02307 const FILETIME *pmtime) /* [in] */ 02308 { 02309 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName)); 02310 return S_OK; 02311 } 02312 02313 /****************************************************************************** 02314 * SetStateBits (IStorage) 02315 */ 02316 static HRESULT WINAPI StorageBaseImpl_SetStateBits( 02317 IStorage* iface, 02318 DWORD grfStateBits,/* [in] */ 02319 DWORD grfMask) /* [in] */ 02320 { 02321 StorageBaseImpl* const This = (StorageBaseImpl*)iface; 02322 02323 if (This->reverted) 02324 return STG_E_REVERTED; 02325 02326 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask); 02327 return S_OK; 02328 } 02329 02330 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base, 02331 DirRef index, const DirEntry *data) 02332 { 02333 StorageImpl *This = (StorageImpl*)base; 02334 return StorageImpl_WriteDirEntry(This, index, data); 02335 } 02336 02337 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base, 02338 DirRef index, DirEntry *data) 02339 { 02340 StorageImpl *This = (StorageImpl*)base; 02341 return StorageImpl_ReadDirEntry(This, index, data); 02342 } 02343 02344 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This) 02345 { 02346 int i; 02347 02348 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 02349 { 02350 if (!This->blockChainCache[i]) 02351 { 02352 return &This->blockChainCache[i]; 02353 } 02354 } 02355 02356 i = This->blockChainToEvict; 02357 02358 BlockChainStream_Destroy(This->blockChainCache[i]); 02359 This->blockChainCache[i] = NULL; 02360 02361 This->blockChainToEvict++; 02362 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) 02363 This->blockChainToEvict = 0; 02364 02365 return &This->blockChainCache[i]; 02366 } 02367 02368 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This, 02369 DirRef index) 02370 { 02371 int i, free_index=-1; 02372 02373 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 02374 { 02375 if (!This->blockChainCache[i]) 02376 { 02377 if (free_index == -1) free_index = i; 02378 } 02379 else if (This->blockChainCache[i]->ownerDirEntry == index) 02380 { 02381 return &This->blockChainCache[i]; 02382 } 02383 } 02384 02385 if (free_index == -1) 02386 { 02387 free_index = This->blockChainToEvict; 02388 02389 BlockChainStream_Destroy(This->blockChainCache[free_index]); 02390 This->blockChainCache[free_index] = NULL; 02391 02392 This->blockChainToEvict++; 02393 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) 02394 This->blockChainToEvict = 0; 02395 } 02396 02397 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index); 02398 return &This->blockChainCache[free_index]; 02399 } 02400 02401 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index) 02402 { 02403 int i; 02404 02405 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 02406 { 02407 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index) 02408 { 02409 BlockChainStream_Destroy(This->blockChainCache[i]); 02410 This->blockChainCache[i] = NULL; 02411 return; 02412 } 02413 } 02414 } 02415 02416 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index, 02417 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 02418 { 02419 StorageImpl *This = (StorageImpl*)base; 02420 DirEntry data; 02421 HRESULT hr; 02422 ULONG bytesToRead; 02423 02424 hr = StorageImpl_ReadDirEntry(This, index, &data); 02425 if (FAILED(hr)) return hr; 02426 02427 if (data.size.QuadPart == 0) 02428 { 02429 *bytesRead = 0; 02430 return S_OK; 02431 } 02432 02433 if (offset.QuadPart + size > data.size.QuadPart) 02434 { 02435 bytesToRead = data.size.QuadPart - offset.QuadPart; 02436 } 02437 else 02438 { 02439 bytesToRead = size; 02440 } 02441 02442 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 02443 { 02444 SmallBlockChainStream *stream; 02445 02446 stream = SmallBlockChainStream_Construct(This, NULL, index); 02447 if (!stream) return E_OUTOFMEMORY; 02448 02449 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead); 02450 02451 SmallBlockChainStream_Destroy(stream); 02452 02453 return hr; 02454 } 02455 else 02456 { 02457 BlockChainStream *stream = NULL; 02458 02459 stream = *StorageImpl_GetCachedBlockChainStream(This, index); 02460 if (!stream) return E_OUTOFMEMORY; 02461 02462 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead); 02463 02464 return hr; 02465 } 02466 } 02467 02468 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index, 02469 ULARGE_INTEGER newsize) 02470 { 02471 StorageImpl *This = (StorageImpl*)base; 02472 DirEntry data; 02473 HRESULT hr; 02474 SmallBlockChainStream *smallblock=NULL; 02475 BlockChainStream **pbigblock=NULL, *bigblock=NULL; 02476 02477 hr = StorageImpl_ReadDirEntry(This, index, &data); 02478 if (FAILED(hr)) return hr; 02479 02480 /* In simple mode keep the stream size above the small block limit */ 02481 if (This->base.openFlags & STGM_SIMPLE) 02482 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK); 02483 02484 if (data.size.QuadPart == newsize.QuadPart) 02485 return S_OK; 02486 02487 /* Create a block chain object of the appropriate type */ 02488 if (data.size.QuadPart == 0) 02489 { 02490 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 02491 { 02492 smallblock = SmallBlockChainStream_Construct(This, NULL, index); 02493 if (!smallblock) return E_OUTOFMEMORY; 02494 } 02495 else 02496 { 02497 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index); 02498 bigblock = *pbigblock; 02499 if (!bigblock) return E_OUTOFMEMORY; 02500 } 02501 } 02502 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 02503 { 02504 smallblock = SmallBlockChainStream_Construct(This, NULL, index); 02505 if (!smallblock) return E_OUTOFMEMORY; 02506 } 02507 else 02508 { 02509 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index); 02510 bigblock = *pbigblock; 02511 if (!bigblock) return E_OUTOFMEMORY; 02512 } 02513 02514 /* Change the block chain type if necessary. */ 02515 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK) 02516 { 02517 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock); 02518 if (!bigblock) 02519 { 02520 SmallBlockChainStream_Destroy(smallblock); 02521 return E_FAIL; 02522 } 02523 02524 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This); 02525 *pbigblock = bigblock; 02526 } 02527 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 02528 { 02529 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize); 02530 if (!smallblock) 02531 return E_FAIL; 02532 } 02533 02534 /* Set the size of the block chain. */ 02535 if (smallblock) 02536 { 02537 SmallBlockChainStream_SetSize(smallblock, newsize); 02538 SmallBlockChainStream_Destroy(smallblock); 02539 } 02540 else 02541 { 02542 BlockChainStream_SetSize(bigblock, newsize); 02543 } 02544 02545 /* Set the size in the directory entry. */ 02546 hr = StorageImpl_ReadDirEntry(This, index, &data); 02547 if (SUCCEEDED(hr)) 02548 { 02549 data.size = newsize; 02550 02551 hr = StorageImpl_WriteDirEntry(This, index, &data); 02552 } 02553 return hr; 02554 } 02555 02556 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, 02557 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 02558 { 02559 StorageImpl *This = (StorageImpl*)base; 02560 DirEntry data; 02561 HRESULT hr; 02562 ULARGE_INTEGER newSize; 02563 02564 hr = StorageImpl_ReadDirEntry(This, index, &data); 02565 if (FAILED(hr)) return hr; 02566 02567 /* Grow the stream if necessary */ 02568 newSize.QuadPart = 0; 02569 newSize.QuadPart = offset.QuadPart + size; 02570 02571 if (newSize.QuadPart > data.size.QuadPart) 02572 { 02573 hr = StorageImpl_StreamSetSize(base, index, newSize); 02574 if (FAILED(hr)) 02575 return hr; 02576 02577 hr = StorageImpl_ReadDirEntry(This, index, &data); 02578 if (FAILED(hr)) return hr; 02579 } 02580 02581 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 02582 { 02583 SmallBlockChainStream *stream; 02584 02585 stream = SmallBlockChainStream_Construct(This, NULL, index); 02586 if (!stream) return E_OUTOFMEMORY; 02587 02588 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); 02589 02590 SmallBlockChainStream_Destroy(stream); 02591 02592 return hr; 02593 } 02594 else 02595 { 02596 BlockChainStream *stream; 02597 02598 stream = *StorageImpl_GetCachedBlockChainStream(This, index); 02599 if (!stream) return E_OUTOFMEMORY; 02600 02601 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); 02602 02603 return hr; 02604 } 02605 } 02606 02607 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst, 02608 DirRef src) 02609 { 02610 StorageImpl *This = (StorageImpl*)base; 02611 DirEntry dst_data, src_data; 02612 HRESULT hr; 02613 02614 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data); 02615 02616 if (SUCCEEDED(hr)) 02617 hr = StorageImpl_ReadDirEntry(This, src, &src_data); 02618 02619 if (SUCCEEDED(hr)) 02620 { 02621 StorageImpl_DeleteCachedBlockChainStream(This, src); 02622 dst_data.startingBlock = src_data.startingBlock; 02623 dst_data.size = src_data.size; 02624 02625 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data); 02626 } 02627 02628 return hr; 02629 } 02630 02631 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 02632 { 02633 StorageImpl *This = (StorageImpl*) iface; 02634 STATSTG statstg; 02635 HRESULT hr; 02636 02637 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0); 02638 02639 *result = statstg.pwcsName; 02640 02641 return hr; 02642 } 02643 02644 /* 02645 * Virtual function table for the IStorage32Impl class. 02646 */ 02647 static const IStorageVtbl Storage32Impl_Vtbl = 02648 { 02649 StorageBaseImpl_QueryInterface, 02650 StorageBaseImpl_AddRef, 02651 StorageBaseImpl_Release, 02652 StorageBaseImpl_CreateStream, 02653 StorageBaseImpl_OpenStream, 02654 StorageBaseImpl_CreateStorage, 02655 StorageBaseImpl_OpenStorage, 02656 StorageBaseImpl_CopyTo, 02657 StorageBaseImpl_MoveElementTo, 02658 StorageImpl_Commit, 02659 StorageImpl_Revert, 02660 StorageBaseImpl_EnumElements, 02661 StorageBaseImpl_DestroyElement, 02662 StorageBaseImpl_RenameElement, 02663 StorageBaseImpl_SetElementTimes, 02664 StorageBaseImpl_SetClass, 02665 StorageBaseImpl_SetStateBits, 02666 StorageBaseImpl_Stat 02667 }; 02668 02669 static const StorageBaseImplVtbl StorageImpl_BaseVtbl = 02670 { 02671 StorageImpl_Destroy, 02672 StorageImpl_Invalidate, 02673 StorageImpl_Flush, 02674 StorageImpl_GetFilename, 02675 StorageImpl_CreateDirEntry, 02676 StorageImpl_BaseWriteDirEntry, 02677 StorageImpl_BaseReadDirEntry, 02678 StorageImpl_DestroyDirEntry, 02679 StorageImpl_StreamReadAt, 02680 StorageImpl_StreamWriteAt, 02681 StorageImpl_StreamSetSize, 02682 StorageImpl_StreamLink 02683 }; 02684 02685 static HRESULT StorageImpl_Construct( 02686 HANDLE hFile, 02687 LPCOLESTR pwcsName, 02688 ILockBytes* pLkbyt, 02689 DWORD openFlags, 02690 BOOL fileBased, 02691 BOOL create, 02692 ULONG sector_size, 02693 StorageImpl** result) 02694 { 02695 StorageImpl* This; 02696 HRESULT hr = S_OK; 02697 DirEntry currentEntry; 02698 DirRef currentEntryRef; 02699 02700 if ( FAILED( validateSTGM(openFlags) )) 02701 return STG_E_INVALIDFLAG; 02702 02703 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl)); 02704 if (!This) 02705 return E_OUTOFMEMORY; 02706 02707 memset(This, 0, sizeof(StorageImpl)); 02708 02709 list_init(&This->base.strmHead); 02710 02711 list_init(&This->base.storageHead); 02712 02713 This->base.lpVtbl = &Storage32Impl_Vtbl; 02714 This->base.pssVtbl = &IPropertySetStorage_Vtbl; 02715 This->base.baseVtbl = &StorageImpl_BaseVtbl; 02716 This->base.openFlags = (openFlags & ~STGM_CREATE); 02717 This->base.ref = 1; 02718 This->base.create = create; 02719 02720 This->base.reverted = 0; 02721 02722 /* 02723 * Initialize the big block cache. 02724 */ 02725 This->bigBlockSize = sector_size; 02726 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE; 02727 if (hFile) 02728 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes); 02729 else 02730 { 02731 This->lockBytes = pLkbyt; 02732 ILockBytes_AddRef(pLkbyt); 02733 } 02734 02735 if (FAILED(hr)) 02736 goto end; 02737 02738 if (create) 02739 { 02740 ULARGE_INTEGER size; 02741 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE]; 02742 02743 /* Discard any existing data. */ 02744 size.QuadPart = 0; 02745 ILockBytes_SetSize(This->lockBytes, size); 02746 02747 /* 02748 * Initialize all header variables: 02749 * - The big block depot consists of one block and it is at block 0 02750 * - The directory table starts at block 1 02751 * - There is no small block depot 02752 */ 02753 memset( This->bigBlockDepotStart, 02754 BLOCK_UNUSED, 02755 sizeof(This->bigBlockDepotStart)); 02756 02757 This->bigBlockDepotCount = 1; 02758 This->bigBlockDepotStart[0] = 0; 02759 This->rootStartBlock = 1; 02760 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK; 02761 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN; 02762 if (sector_size == 4096) 02763 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS; 02764 else 02765 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS; 02766 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS; 02767 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN; 02768 This->extBigBlockDepotCount = 0; 02769 02770 StorageImpl_SaveFileHeader(This); 02771 02772 /* 02773 * Add one block for the big block depot and one block for the directory table 02774 */ 02775 size.u.HighPart = 0; 02776 size.u.LowPart = This->bigBlockSize * 3; 02777 ILockBytes_SetSize(This->lockBytes, size); 02778 02779 /* 02780 * Initialize the big block depot 02781 */ 02782 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize); 02783 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL); 02784 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN); 02785 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer); 02786 } 02787 else 02788 { 02789 /* 02790 * Load the header for the file. 02791 */ 02792 hr = StorageImpl_LoadFileHeader(This); 02793 02794 if (FAILED(hr)) 02795 { 02796 goto end; 02797 } 02798 } 02799 02800 /* 02801 * There is no block depot cached yet. 02802 */ 02803 This->indexBlockDepotCached = 0xFFFFFFFF; 02804 This->indexExtBlockDepotCached = 0xFFFFFFFF; 02805 02806 /* 02807 * Start searching for free blocks with block 0. 02808 */ 02809 This->prevFreeBlock = 0; 02810 02811 This->firstFreeSmallBlock = 0; 02812 02813 /* Read the extended big block depot locations. */ 02814 if (This->extBigBlockDepotCount != 0) 02815 { 02816 ULONG current_block = This->extBigBlockDepotStart; 02817 ULONG cache_size = This->extBigBlockDepotCount * 2; 02818 int i; 02819 02820 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size); 02821 if (!This->extBigBlockDepotLocations) 02822 { 02823 hr = E_OUTOFMEMORY; 02824 goto end; 02825 } 02826 02827 This->extBigBlockDepotLocationsSize = cache_size; 02828 02829 for (i=0; i<This->extBigBlockDepotCount; i++) 02830 { 02831 if (current_block == BLOCK_END_OF_CHAIN) 02832 { 02833 WARN("File has too few extended big block depot blocks.\n"); 02834 hr = STG_E_DOCFILECORRUPT; 02835 goto end; 02836 } 02837 This->extBigBlockDepotLocations[i] = current_block; 02838 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block); 02839 } 02840 } 02841 else 02842 { 02843 This->extBigBlockDepotLocations = NULL; 02844 This->extBigBlockDepotLocationsSize = 0; 02845 } 02846 02847 /* 02848 * Create the block chain abstractions. 02849 */ 02850 if(!(This->rootBlockChain = 02851 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL))) 02852 { 02853 hr = STG_E_READFAULT; 02854 goto end; 02855 } 02856 02857 if(!(This->smallBlockDepotChain = 02858 BlockChainStream_Construct(This, &This->smallBlockDepotStart, 02859 DIRENTRY_NULL))) 02860 { 02861 hr = STG_E_READFAULT; 02862 goto end; 02863 } 02864 02865 /* 02866 * Write the root storage entry (memory only) 02867 */ 02868 if (create) 02869 { 02870 DirEntry rootEntry; 02871 /* 02872 * Initialize the directory table 02873 */ 02874 memset(&rootEntry, 0, sizeof(rootEntry)); 02875 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name, 02876 sizeof(rootEntry.name)/sizeof(WCHAR) ); 02877 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR); 02878 rootEntry.stgType = STGTY_ROOT; 02879 rootEntry.leftChild = DIRENTRY_NULL; 02880 rootEntry.rightChild = DIRENTRY_NULL; 02881 rootEntry.dirRootEntry = DIRENTRY_NULL; 02882 rootEntry.startingBlock = BLOCK_END_OF_CHAIN; 02883 rootEntry.size.u.HighPart = 0; 02884 rootEntry.size.u.LowPart = 0; 02885 02886 StorageImpl_WriteDirEntry(This, 0, &rootEntry); 02887 } 02888 02889 /* 02890 * Find the ID of the root storage. 02891 */ 02892 currentEntryRef = 0; 02893 02894 do 02895 { 02896 hr = StorageImpl_ReadDirEntry( 02897 This, 02898 currentEntryRef, 02899 ¤tEntry); 02900 02901 if (SUCCEEDED(hr)) 02902 { 02903 if ( (currentEntry.sizeOfNameString != 0 ) && 02904 (currentEntry.stgType == STGTY_ROOT) ) 02905 { 02906 This->base.storageDirEntry = currentEntryRef; 02907 } 02908 } 02909 02910 currentEntryRef++; 02911 02912 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) ); 02913 02914 if (FAILED(hr)) 02915 { 02916 hr = STG_E_READFAULT; 02917 goto end; 02918 } 02919 02920 /* 02921 * Create the block chain abstraction for the small block root chain. 02922 */ 02923 if(!(This->smallBlockRootChain = 02924 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry))) 02925 { 02926 hr = STG_E_READFAULT; 02927 } 02928 02929 end: 02930 if (FAILED(hr)) 02931 { 02932 IStorage_Release((IStorage*)This); 02933 *result = NULL; 02934 } 02935 else 02936 { 02937 StorageImpl_Flush((StorageBaseImpl*)This); 02938 *result = This; 02939 } 02940 02941 return hr; 02942 } 02943 02944 static void StorageImpl_Invalidate(StorageBaseImpl* iface) 02945 { 02946 StorageImpl *This = (StorageImpl*) iface; 02947 02948 StorageBaseImpl_DeleteAll(&This->base); 02949 02950 This->base.reverted = 1; 02951 } 02952 02953 static void StorageImpl_Destroy(StorageBaseImpl* iface) 02954 { 02955 StorageImpl *This = (StorageImpl*) iface; 02956 int i; 02957 TRACE("(%p)\n", This); 02958 02959 StorageImpl_Flush(iface); 02960 02961 StorageImpl_Invalidate(iface); 02962 02963 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); 02964 02965 BlockChainStream_Destroy(This->smallBlockRootChain); 02966 BlockChainStream_Destroy(This->rootBlockChain); 02967 BlockChainStream_Destroy(This->smallBlockDepotChain); 02968 02969 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 02970 BlockChainStream_Destroy(This->blockChainCache[i]); 02971 02972 if (This->lockBytes) 02973 ILockBytes_Release(This->lockBytes); 02974 HeapFree(GetProcessHeap(), 0, This); 02975 } 02976 02977 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface) 02978 { 02979 StorageImpl *This = (StorageImpl*) iface; 02980 int i; 02981 HRESULT hr; 02982 TRACE("(%p)\n", This); 02983 02984 hr = BlockChainStream_Flush(This->smallBlockRootChain); 02985 02986 if (SUCCEEDED(hr)) 02987 hr = BlockChainStream_Flush(This->rootBlockChain); 02988 02989 if (SUCCEEDED(hr)) 02990 hr = BlockChainStream_Flush(This->smallBlockDepotChain); 02991 02992 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++) 02993 if (This->blockChainCache[i]) 02994 hr = BlockChainStream_Flush(This->blockChainCache[i]); 02995 02996 if (SUCCEEDED(hr)) 02997 hr = ILockBytes_Flush(This->lockBytes); 02998 02999 return hr; 03000 } 03001 03002 /****************************************************************************** 03003 * Storage32Impl_GetNextFreeBigBlock 03004 * 03005 * Returns the index of the next free big block. 03006 * If the big block depot is filled, this method will enlarge it. 03007 * 03008 */ 03009 static ULONG StorageImpl_GetNextFreeBigBlock( 03010 StorageImpl* This) 03011 { 03012 ULONG depotBlockIndexPos; 03013 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 03014 BOOL success; 03015 ULONG depotBlockOffset; 03016 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); 03017 ULONG nextBlockIndex = BLOCK_SPECIAL; 03018 int depotIndex = 0; 03019 ULONG freeBlock = BLOCK_UNUSED; 03020 ULARGE_INTEGER neededSize; 03021 STATSTG statstg; 03022 03023 depotIndex = This->prevFreeBlock / blocksPerDepot; 03024 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG); 03025 03026 /* 03027 * Scan the entire big block depot until we find a block marked free 03028 */ 03029 while (nextBlockIndex != BLOCK_UNUSED) 03030 { 03031 if (depotIndex < COUNT_BBDEPOTINHEADER) 03032 { 03033 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex]; 03034 03035 /* 03036 * Grow the primary depot. 03037 */ 03038 if (depotBlockIndexPos == BLOCK_UNUSED) 03039 { 03040 depotBlockIndexPos = depotIndex*blocksPerDepot; 03041 03042 /* 03043 * Add a block depot. 03044 */ 03045 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos); 03046 This->bigBlockDepotCount++; 03047 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos; 03048 03049 /* 03050 * Flag it as a block depot. 03051 */ 03052 StorageImpl_SetNextBlockInChain(This, 03053 depotBlockIndexPos, 03054 BLOCK_SPECIAL); 03055 03056 /* Save new header information. 03057 */ 03058 StorageImpl_SaveFileHeader(This); 03059 } 03060 } 03061 else 03062 { 03063 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex); 03064 03065 if (depotBlockIndexPos == BLOCK_UNUSED) 03066 { 03067 /* 03068 * Grow the extended depot. 03069 */ 03070 ULONG extIndex = BLOCK_UNUSED; 03071 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 03072 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1); 03073 03074 if (extBlockOffset == 0) 03075 { 03076 /* We need an extended block. 03077 */ 03078 extIndex = Storage32Impl_AddExtBlockDepot(This); 03079 This->extBigBlockDepotCount++; 03080 depotBlockIndexPos = extIndex + 1; 03081 } 03082 else 03083 depotBlockIndexPos = depotIndex * blocksPerDepot; 03084 03085 /* 03086 * Add a block depot and mark it in the extended block. 03087 */ 03088 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos); 03089 This->bigBlockDepotCount++; 03090 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos); 03091 03092 /* Flag the block depot. 03093 */ 03094 StorageImpl_SetNextBlockInChain(This, 03095 depotBlockIndexPos, 03096 BLOCK_SPECIAL); 03097 03098 /* If necessary, flag the extended depot block. 03099 */ 03100 if (extIndex != BLOCK_UNUSED) 03101 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT); 03102 03103 /* Save header information. 03104 */ 03105 StorageImpl_SaveFileHeader(This); 03106 } 03107 } 03108 03109 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer); 03110 03111 if (success) 03112 { 03113 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) && 03114 ( nextBlockIndex != BLOCK_UNUSED)) 03115 { 03116 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex); 03117 03118 if (nextBlockIndex == BLOCK_UNUSED) 03119 { 03120 freeBlock = (depotIndex * blocksPerDepot) + 03121 (depotBlockOffset/sizeof(ULONG)); 03122 } 03123 03124 depotBlockOffset += sizeof(ULONG); 03125 } 03126 } 03127 03128 depotIndex++; 03129 depotBlockOffset = 0; 03130 } 03131 03132 /* 03133 * make sure that the block physically exists before using it 03134 */ 03135 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize; 03136 03137 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME); 03138 03139 if (neededSize.QuadPart > statstg.cbSize.QuadPart) 03140 ILockBytes_SetSize(This->lockBytes, neededSize); 03141 03142 This->prevFreeBlock = freeBlock; 03143 03144 return freeBlock; 03145 } 03146 03147 /****************************************************************************** 03148 * Storage32Impl_AddBlockDepot 03149 * 03150 * This will create a depot block, essentially it is a block initialized 03151 * to BLOCK_UNUSEDs. 03152 */ 03153 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex) 03154 { 03155 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE]; 03156 03157 /* 03158 * Initialize blocks as free 03159 */ 03160 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize); 03161 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer); 03162 } 03163 03164 /****************************************************************************** 03165 * Storage32Impl_GetExtDepotBlock 03166 * 03167 * Returns the index of the block that corresponds to the specified depot 03168 * index. This method is only for depot indexes equal or greater than 03169 * COUNT_BBDEPOTINHEADER. 03170 */ 03171 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex) 03172 { 03173 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1; 03174 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 03175 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock; 03176 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock; 03177 ULONG blockIndex = BLOCK_UNUSED; 03178 ULONG extBlockIndex; 03179 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 03180 int index, num_blocks; 03181 03182 assert(depotIndex >= COUNT_BBDEPOTINHEADER); 03183 03184 if (extBlockCount >= This->extBigBlockDepotCount) 03185 return BLOCK_UNUSED; 03186 03187 if (This->indexExtBlockDepotCached != extBlockCount) 03188 { 03189 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount]; 03190 03191 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer); 03192 03193 num_blocks = This->bigBlockSize / 4; 03194 03195 for (index = 0; index < num_blocks; index++) 03196 { 03197 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex); 03198 This->extBlockDepotCached[index] = blockIndex; 03199 } 03200 03201 This->indexExtBlockDepotCached = extBlockCount; 03202 } 03203 03204 blockIndex = This->extBlockDepotCached[extBlockOffset]; 03205 03206 return blockIndex; 03207 } 03208 03209 /****************************************************************************** 03210 * Storage32Impl_SetExtDepotBlock 03211 * 03212 * Associates the specified block index to the specified depot index. 03213 * This method is only for depot indexes equal or greater than 03214 * COUNT_BBDEPOTINHEADER. 03215 */ 03216 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex) 03217 { 03218 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1; 03219 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 03220 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock; 03221 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock; 03222 ULONG extBlockIndex; 03223 03224 assert(depotIndex >= COUNT_BBDEPOTINHEADER); 03225 03226 assert(extBlockCount < This->extBigBlockDepotCount); 03227 03228 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount]; 03229 03230 if (extBlockIndex != BLOCK_UNUSED) 03231 { 03232 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex, 03233 extBlockOffset * sizeof(ULONG), 03234 blockIndex); 03235 } 03236 03237 if (This->indexExtBlockDepotCached == extBlockCount) 03238 { 03239 This->extBlockDepotCached[extBlockOffset] = blockIndex; 03240 } 03241 } 03242 03243 /****************************************************************************** 03244 * Storage32Impl_AddExtBlockDepot 03245 * 03246 * Creates an extended depot block. 03247 */ 03248 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This) 03249 { 03250 ULONG numExtBlocks = This->extBigBlockDepotCount; 03251 ULONG nextExtBlock = This->extBigBlockDepotStart; 03252 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 03253 ULONG index = BLOCK_UNUSED; 03254 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG); 03255 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG); 03256 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1; 03257 03258 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) * 03259 blocksPerDepotBlock; 03260 03261 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN)) 03262 { 03263 /* 03264 * The first extended block. 03265 */ 03266 This->extBigBlockDepotStart = index; 03267 } 03268 else 03269 { 03270 /* 03271 * Find the last existing extended block. 03272 */ 03273 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1]; 03274 03275 /* 03276 * Add the new extended block to the chain. 03277 */ 03278 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset, 03279 index); 03280 } 03281 03282 /* 03283 * Initialize this block. 03284 */ 03285 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize); 03286 StorageImpl_WriteBigBlock(This, index, depotBuffer); 03287 03288 /* Add the block to our cache. */ 03289 if (This->extBigBlockDepotLocationsSize == numExtBlocks) 03290 { 03291 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2; 03292 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size); 03293 03294 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize); 03295 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); 03296 03297 This->extBigBlockDepotLocations = new_cache; 03298 This->extBigBlockDepotLocationsSize = new_cache_size; 03299 } 03300 This->extBigBlockDepotLocations[numExtBlocks] = index; 03301 03302 return index; 03303 } 03304 03305 /****************************************************************************** 03306 * Storage32Impl_FreeBigBlock 03307 * 03308 * This method will flag the specified block as free in the big block depot. 03309 */ 03310 static void StorageImpl_FreeBigBlock( 03311 StorageImpl* This, 03312 ULONG blockIndex) 03313 { 03314 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); 03315 03316 if (blockIndex < This->prevFreeBlock) 03317 This->prevFreeBlock = blockIndex; 03318 } 03319 03320 /************************************************************************ 03321 * Storage32Impl_GetNextBlockInChain 03322 * 03323 * This method will retrieve the block index of the next big block in 03324 * in the chain. 03325 * 03326 * Params: This - Pointer to the Storage object. 03327 * blockIndex - Index of the block to retrieve the chain 03328 * for. 03329 * nextBlockIndex - receives the return value. 03330 * 03331 * Returns: This method returns the index of the next block in the chain. 03332 * It will return the constants: 03333 * BLOCK_SPECIAL - If the block given was not part of a 03334 * chain. 03335 * BLOCK_END_OF_CHAIN - If the block given was the last in 03336 * a chain. 03337 * BLOCK_UNUSED - If the block given was not past of a chain 03338 * and is available. 03339 * BLOCK_EXTBBDEPOT - This block is part of the extended 03340 * big block depot. 03341 * 03342 * See Windows documentation for more details on IStorage methods. 03343 */ 03344 static HRESULT StorageImpl_GetNextBlockInChain( 03345 StorageImpl* This, 03346 ULONG blockIndex, 03347 ULONG* nextBlockIndex) 03348 { 03349 ULONG offsetInDepot = blockIndex * sizeof (ULONG); 03350 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize; 03351 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize; 03352 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 03353 BOOL success; 03354 ULONG depotBlockIndexPos; 03355 int index, num_blocks; 03356 03357 *nextBlockIndex = BLOCK_SPECIAL; 03358 03359 if(depotBlockCount >= This->bigBlockDepotCount) 03360 { 03361 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount, 03362 This->bigBlockDepotCount); 03363 return STG_E_READFAULT; 03364 } 03365 03366 /* 03367 * Cache the currently accessed depot block. 03368 */ 03369 if (depotBlockCount != This->indexBlockDepotCached) 03370 { 03371 This->indexBlockDepotCached = depotBlockCount; 03372 03373 if (depotBlockCount < COUNT_BBDEPOTINHEADER) 03374 { 03375 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; 03376 } 03377 else 03378 { 03379 /* 03380 * We have to look in the extended depot. 03381 */ 03382 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); 03383 } 03384 03385 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer); 03386 03387 if (!success) 03388 return STG_E_READFAULT; 03389 03390 num_blocks = This->bigBlockSize / 4; 03391 03392 for (index = 0; index < num_blocks; index++) 03393 { 03394 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex); 03395 This->blockDepotCached[index] = *nextBlockIndex; 03396 } 03397 } 03398 03399 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; 03400 03401 return S_OK; 03402 } 03403 03404 /****************************************************************************** 03405 * Storage32Impl_GetNextExtendedBlock 03406 * 03407 * Given an extended block this method will return the next extended block. 03408 * 03409 * NOTES: 03410 * The last ULONG of an extended block is the block index of the next 03411 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the 03412 * depot. 03413 * 03414 * Return values: 03415 * - The index of the next extended block 03416 * - BLOCK_UNUSED: there is no next extended block. 03417 * - Any other return values denotes failure. 03418 */ 03419 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) 03420 { 03421 ULONG nextBlockIndex = BLOCK_SPECIAL; 03422 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); 03423 03424 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, 03425 &nextBlockIndex); 03426 03427 return nextBlockIndex; 03428 } 03429 03430 /****************************************************************************** 03431 * Storage32Impl_SetNextBlockInChain 03432 * 03433 * This method will write the index of the specified block's next block 03434 * in the big block depot. 03435 * 03436 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain 03437 * do the following 03438 * 03439 * Storage32Impl_SetNextBlockInChain(This, 3, 1); 03440 * Storage32Impl_SetNextBlockInChain(This, 1, 7); 03441 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN); 03442 * 03443 */ 03444 static void StorageImpl_SetNextBlockInChain( 03445 StorageImpl* This, 03446 ULONG blockIndex, 03447 ULONG nextBlock) 03448 { 03449 ULONG offsetInDepot = blockIndex * sizeof (ULONG); 03450 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize; 03451 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize; 03452 ULONG depotBlockIndexPos; 03453 03454 assert(depotBlockCount < This->bigBlockDepotCount); 03455 assert(blockIndex != nextBlock); 03456 03457 if (depotBlockCount < COUNT_BBDEPOTINHEADER) 03458 { 03459 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; 03460 } 03461 else 03462 { 03463 /* 03464 * We have to look in the extended depot. 03465 */ 03466 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); 03467 } 03468 03469 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset, 03470 nextBlock); 03471 /* 03472 * Update the cached block depot, if necessary. 03473 */ 03474 if (depotBlockCount == This->indexBlockDepotCached) 03475 { 03476 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock; 03477 } 03478 } 03479 03480 /****************************************************************************** 03481 * Storage32Impl_LoadFileHeader 03482 * 03483 * This method will read in the file header 03484 */ 03485 static HRESULT StorageImpl_LoadFileHeader( 03486 StorageImpl* This) 03487 { 03488 HRESULT hr; 03489 BYTE headerBigBlock[HEADER_SIZE]; 03490 int index; 03491 ULARGE_INTEGER offset; 03492 DWORD bytes_read; 03493 03494 TRACE("\n"); 03495 /* 03496 * Get a pointer to the big block of data containing the header. 03497 */ 03498 offset.u.HighPart = 0; 03499 offset.u.LowPart = 0; 03500 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); 03501 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) 03502 hr = STG_E_FILENOTFOUND; 03503 03504 /* 03505 * Extract the information from the header. 03506 */ 03507 if (SUCCEEDED(hr)) 03508 { 03509 /* 03510 * Check for the "magic number" signature and return an error if it is not 03511 * found. 03512 */ 03513 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0) 03514 { 03515 return STG_E_OLDFORMAT; 03516 } 03517 03518 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0) 03519 { 03520 return STG_E_INVALIDHEADER; 03521 } 03522 03523 StorageUtl_ReadWord( 03524 headerBigBlock, 03525 OFFSET_BIGBLOCKSIZEBITS, 03526 &This->bigBlockSizeBits); 03527 03528 StorageUtl_ReadWord( 03529 headerBigBlock, 03530 OFFSET_SMALLBLOCKSIZEBITS, 03531 &This->smallBlockSizeBits); 03532 03533 StorageUtl_ReadDWord( 03534 headerBigBlock, 03535 OFFSET_BBDEPOTCOUNT, 03536 &This->bigBlockDepotCount); 03537 03538 StorageUtl_ReadDWord( 03539 headerBigBlock, 03540 OFFSET_ROOTSTARTBLOCK, 03541 &This->rootStartBlock); 03542 03543 StorageUtl_ReadDWord( 03544 headerBigBlock, 03545 OFFSET_SMALLBLOCKLIMIT, 03546 &This->smallBlockLimit); 03547 03548 StorageUtl_ReadDWord( 03549 headerBigBlock, 03550 OFFSET_SBDEPOTSTART, 03551 &This->smallBlockDepotStart); 03552 03553 StorageUtl_ReadDWord( 03554 headerBigBlock, 03555 OFFSET_EXTBBDEPOTSTART, 03556 &This->extBigBlockDepotStart); 03557 03558 StorageUtl_ReadDWord( 03559 headerBigBlock, 03560 OFFSET_EXTBBDEPOTCOUNT, 03561 &This->extBigBlockDepotCount); 03562 03563 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) 03564 { 03565 StorageUtl_ReadDWord( 03566 headerBigBlock, 03567 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), 03568 &(This->bigBlockDepotStart[index])); 03569 } 03570 03571 /* 03572 * Make the bitwise arithmetic to get the size of the blocks in bytes. 03573 */ 03574 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits; 03575 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits; 03576 03577 /* 03578 * Right now, the code is making some assumptions about the size of the 03579 * blocks, just make sure they are what we're expecting. 03580 */ 03581 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) || 03582 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE || 03583 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK) 03584 { 03585 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n", 03586 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); 03587 hr = STG_E_INVALIDHEADER; 03588 } 03589 else 03590 hr = S_OK; 03591 } 03592 03593 return hr; 03594 } 03595 03596 /****************************************************************************** 03597 * Storage32Impl_SaveFileHeader 03598 * 03599 * This method will save to the file the header 03600 */ 03601 static void StorageImpl_SaveFileHeader( 03602 StorageImpl* This) 03603 { 03604 BYTE headerBigBlock[HEADER_SIZE]; 03605 int index; 03606 HRESULT hr; 03607 ULARGE_INTEGER offset; 03608 DWORD bytes_read, bytes_written; 03609 DWORD major_version, dirsectorcount; 03610 03611 /* 03612 * Get a pointer to the big block of data containing the header. 03613 */ 03614 offset.u.HighPart = 0; 03615 offset.u.LowPart = 0; 03616 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); 03617 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) 03618 hr = STG_E_FILENOTFOUND; 03619 03620 if (This->bigBlockSizeBits == 0x9) 03621 major_version = 3; 03622 else if (This->bigBlockSizeBits == 0xc) 03623 major_version = 4; 03624 else 03625 { 03626 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits); 03627 major_version = 4; 03628 } 03629 03630 /* 03631 * If the block read failed, the file is probably new. 03632 */ 03633 if (FAILED(hr)) 03634 { 03635 /* 03636 * Initialize for all unknown fields. 03637 */ 03638 memset(headerBigBlock, 0, HEADER_SIZE); 03639 03640 /* 03641 * Initialize the magic number. 03642 */ 03643 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic)); 03644 } 03645 03646 /* 03647 * Write the information to the header. 03648 */ 03649 StorageUtl_WriteWord( 03650 headerBigBlock, 03651 OFFSET_MINORVERSION, 03652 0x3e); 03653 03654 StorageUtl_WriteWord( 03655 headerBigBlock, 03656 OFFSET_MAJORVERSION, 03657 major_version); 03658 03659 StorageUtl_WriteWord( 03660 headerBigBlock, 03661 OFFSET_BYTEORDERMARKER, 03662 (WORD)-2); 03663 03664 StorageUtl_WriteWord( 03665 headerBigBlock, 03666 OFFSET_BIGBLOCKSIZEBITS, 03667 This->bigBlockSizeBits); 03668 03669 StorageUtl_WriteWord( 03670 headerBigBlock, 03671 OFFSET_SMALLBLOCKSIZEBITS, 03672 This->smallBlockSizeBits); 03673 03674 if (major_version >= 4) 03675 { 03676 if (This->rootBlockChain) 03677 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain); 03678 else 03679 /* This file is being created, and it will start out with one block. */ 03680 dirsectorcount = 1; 03681 } 03682 else 03683 /* This field must be 0 in versions older than 4 */ 03684 dirsectorcount = 0; 03685 03686 StorageUtl_WriteDWord( 03687 headerBigBlock, 03688 OFFSET_DIRSECTORCOUNT, 03689 dirsectorcount); 03690 03691 StorageUtl_WriteDWord( 03692 headerBigBlock, 03693 OFFSET_BBDEPOTCOUNT, 03694 This->bigBlockDepotCount); 03695 03696 StorageUtl_WriteDWord( 03697 headerBigBlock, 03698 OFFSET_ROOTSTARTBLOCK, 03699 This->rootStartBlock); 03700 03701 StorageUtl_WriteDWord( 03702 headerBigBlock, 03703 OFFSET_SMALLBLOCKLIMIT, 03704 This->smallBlockLimit); 03705 03706 StorageUtl_WriteDWord( 03707 headerBigBlock, 03708 OFFSET_SBDEPOTSTART, 03709 This->smallBlockDepotStart); 03710 03711 StorageUtl_WriteDWord( 03712 headerBigBlock, 03713 OFFSET_SBDEPOTCOUNT, 03714 This->smallBlockDepotChain ? 03715 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0); 03716 03717 StorageUtl_WriteDWord( 03718 headerBigBlock, 03719 OFFSET_EXTBBDEPOTSTART, 03720 This->extBigBlockDepotStart); 03721 03722 StorageUtl_WriteDWord( 03723 headerBigBlock, 03724 OFFSET_EXTBBDEPOTCOUNT, 03725 This->extBigBlockDepotCount); 03726 03727 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) 03728 { 03729 StorageUtl_WriteDWord( 03730 headerBigBlock, 03731 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), 03732 (This->bigBlockDepotStart[index])); 03733 } 03734 03735 /* 03736 * Write the big block back to the file. 03737 */ 03738 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written); 03739 } 03740 03741 /****************************************************************************** 03742 * StorageImpl_ReadRawDirEntry 03743 * 03744 * This method will read the raw data from a directory entry in the file. 03745 * 03746 * buffer must be RAW_DIRENTRY_SIZE bytes long. 03747 */ 03748 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer) 03749 { 03750 ULARGE_INTEGER offset; 03751 HRESULT hr; 03752 ULONG bytesRead; 03753 03754 offset.u.HighPart = 0; 03755 offset.u.LowPart = index * RAW_DIRENTRY_SIZE; 03756 03757 hr = BlockChainStream_ReadAt( 03758 This->rootBlockChain, 03759 offset, 03760 RAW_DIRENTRY_SIZE, 03761 buffer, 03762 &bytesRead); 03763 03764 if (bytesRead != RAW_DIRENTRY_SIZE) 03765 return STG_E_READFAULT; 03766 03767 return hr; 03768 } 03769 03770 /****************************************************************************** 03771 * StorageImpl_WriteRawDirEntry 03772 * 03773 * This method will write the raw data from a directory entry in the file. 03774 * 03775 * buffer must be RAW_DIRENTRY_SIZE bytes long. 03776 */ 03777 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer) 03778 { 03779 ULARGE_INTEGER offset; 03780 HRESULT hr; 03781 ULONG bytesRead; 03782 03783 offset.u.HighPart = 0; 03784 offset.u.LowPart = index * RAW_DIRENTRY_SIZE; 03785 03786 hr = BlockChainStream_WriteAt( 03787 This->rootBlockChain, 03788 offset, 03789 RAW_DIRENTRY_SIZE, 03790 buffer, 03791 &bytesRead); 03792 03793 return hr; 03794 } 03795 03796 /****************************************************************************** 03797 * UpdateRawDirEntry 03798 * 03799 * Update raw directory entry data from the fields in newData. 03800 * 03801 * buffer must be RAW_DIRENTRY_SIZE bytes long. 03802 */ 03803 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData) 03804 { 03805 memset(buffer, 0, RAW_DIRENTRY_SIZE); 03806 03807 memcpy( 03808 buffer + OFFSET_PS_NAME, 03809 newData->name, 03810 DIRENTRY_NAME_BUFFER_LEN ); 03811 03812 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1); 03813 03814 StorageUtl_WriteWord( 03815 buffer, 03816 OFFSET_PS_NAMELENGTH, 03817 newData->sizeOfNameString); 03818 03819 StorageUtl_WriteDWord( 03820 buffer, 03821 OFFSET_PS_LEFTCHILD, 03822 newData->leftChild); 03823 03824 StorageUtl_WriteDWord( 03825 buffer, 03826 OFFSET_PS_RIGHTCHILD, 03827 newData->rightChild); 03828 03829 StorageUtl_WriteDWord( 03830 buffer, 03831 OFFSET_PS_DIRROOT, 03832 newData->dirRootEntry); 03833 03834 StorageUtl_WriteGUID( 03835 buffer, 03836 OFFSET_PS_GUID, 03837 &newData->clsid); 03838 03839 StorageUtl_WriteDWord( 03840 buffer, 03841 OFFSET_PS_CTIMELOW, 03842 newData->ctime.dwLowDateTime); 03843 03844 StorageUtl_WriteDWord( 03845 buffer, 03846 OFFSET_PS_CTIMEHIGH, 03847 newData->ctime.dwHighDateTime); 03848 03849 StorageUtl_WriteDWord( 03850 buffer, 03851 OFFSET_PS_MTIMELOW, 03852 newData->mtime.dwLowDateTime); 03853 03854 StorageUtl_WriteDWord( 03855 buffer, 03856 OFFSET_PS_MTIMEHIGH, 03857 newData->ctime.dwHighDateTime); 03858 03859 StorageUtl_WriteDWord( 03860 buffer, 03861 OFFSET_PS_STARTBLOCK, 03862 newData->startingBlock); 03863 03864 StorageUtl_WriteDWord( 03865 buffer, 03866 OFFSET_PS_SIZE, 03867 newData->size.u.LowPart); 03868 } 03869 03870 /****************************************************************************** 03871 * Storage32Impl_ReadDirEntry 03872 * 03873 * This method will read the specified directory entry. 03874 */ 03875 HRESULT StorageImpl_ReadDirEntry( 03876 StorageImpl* This, 03877 DirRef index, 03878 DirEntry* buffer) 03879 { 03880 BYTE currentEntry[RAW_DIRENTRY_SIZE]; 03881 HRESULT readRes; 03882 03883 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry); 03884 03885 if (SUCCEEDED(readRes)) 03886 { 03887 memset(buffer->name, 0, sizeof(buffer->name)); 03888 memcpy( 03889 buffer->name, 03890 (WCHAR *)currentEntry+OFFSET_PS_NAME, 03891 DIRENTRY_NAME_BUFFER_LEN ); 03892 TRACE("storage name: %s\n", debugstr_w(buffer->name)); 03893 03894 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1); 03895 03896 StorageUtl_ReadWord( 03897 currentEntry, 03898 OFFSET_PS_NAMELENGTH, 03899 &buffer->sizeOfNameString); 03900 03901 StorageUtl_ReadDWord( 03902 currentEntry, 03903 OFFSET_PS_LEFTCHILD, 03904 &buffer->leftChild); 03905 03906 StorageUtl_ReadDWord( 03907 currentEntry, 03908 OFFSET_PS_RIGHTCHILD, 03909 &buffer->rightChild); 03910 03911 StorageUtl_ReadDWord( 03912 currentEntry, 03913 OFFSET_PS_DIRROOT, 03914 &buffer->dirRootEntry); 03915 03916 StorageUtl_ReadGUID( 03917 currentEntry, 03918 OFFSET_PS_GUID, 03919 &buffer->clsid); 03920 03921 StorageUtl_ReadDWord( 03922 currentEntry, 03923 OFFSET_PS_CTIMELOW, 03924 &buffer->ctime.dwLowDateTime); 03925 03926 StorageUtl_ReadDWord( 03927 currentEntry, 03928 OFFSET_PS_CTIMEHIGH, 03929 &buffer->ctime.dwHighDateTime); 03930 03931 StorageUtl_ReadDWord( 03932 currentEntry, 03933 OFFSET_PS_MTIMELOW, 03934 &buffer->mtime.dwLowDateTime); 03935 03936 StorageUtl_ReadDWord( 03937 currentEntry, 03938 OFFSET_PS_MTIMEHIGH, 03939 &buffer->mtime.dwHighDateTime); 03940 03941 StorageUtl_ReadDWord( 03942 currentEntry, 03943 OFFSET_PS_STARTBLOCK, 03944 &buffer->startingBlock); 03945 03946 StorageUtl_ReadDWord( 03947 currentEntry, 03948 OFFSET_PS_SIZE, 03949 &buffer->size.u.LowPart); 03950 03951 buffer->size.u.HighPart = 0; 03952 } 03953 03954 return readRes; 03955 } 03956 03957 /********************************************************************* 03958 * Write the specified directory entry to the file 03959 */ 03960 HRESULT StorageImpl_WriteDirEntry( 03961 StorageImpl* This, 03962 DirRef index, 03963 const DirEntry* buffer) 03964 { 03965 BYTE currentEntry[RAW_DIRENTRY_SIZE]; 03966 HRESULT writeRes; 03967 03968 UpdateRawDirEntry(currentEntry, buffer); 03969 03970 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry); 03971 return writeRes; 03972 } 03973 03974 static BOOL StorageImpl_ReadBigBlock( 03975 StorageImpl* This, 03976 ULONG blockIndex, 03977 void* buffer) 03978 { 03979 ULARGE_INTEGER ulOffset; 03980 DWORD read=0; 03981 03982 ulOffset.u.HighPart = 0; 03983 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 03984 03985 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read); 03986 03987 if (read && read < This->bigBlockSize) 03988 { 03989 /* File ends during this block; fill the rest with 0's. */ 03990 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read); 03991 } 03992 03993 return (read != 0); 03994 } 03995 03996 static BOOL StorageImpl_ReadDWordFromBigBlock( 03997 StorageImpl* This, 03998 ULONG blockIndex, 03999 ULONG offset, 04000 DWORD* value) 04001 { 04002 ULARGE_INTEGER ulOffset; 04003 DWORD read; 04004 DWORD tmp; 04005 04006 ulOffset.u.HighPart = 0; 04007 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 04008 ulOffset.u.LowPart += offset; 04009 04010 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read); 04011 *value = lendian32toh(tmp); 04012 return (read == sizeof(DWORD)); 04013 } 04014 04015 static BOOL StorageImpl_WriteBigBlock( 04016 StorageImpl* This, 04017 ULONG blockIndex, 04018 const void* buffer) 04019 { 04020 ULARGE_INTEGER ulOffset; 04021 DWORD wrote; 04022 04023 ulOffset.u.HighPart = 0; 04024 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 04025 04026 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote); 04027 return (wrote == This->bigBlockSize); 04028 } 04029 04030 static BOOL StorageImpl_WriteDWordToBigBlock( 04031 StorageImpl* This, 04032 ULONG blockIndex, 04033 ULONG offset, 04034 DWORD value) 04035 { 04036 ULARGE_INTEGER ulOffset; 04037 DWORD wrote; 04038 04039 ulOffset.u.HighPart = 0; 04040 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 04041 ulOffset.u.LowPart += offset; 04042 04043 value = htole32(value); 04044 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote); 04045 return (wrote == sizeof(DWORD)); 04046 } 04047 04048 /****************************************************************************** 04049 * Storage32Impl_SmallBlocksToBigBlocks 04050 * 04051 * This method will convert a small block chain to a big block chain. 04052 * The small block chain will be destroyed. 04053 */ 04054 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( 04055 StorageImpl* This, 04056 SmallBlockChainStream** ppsbChain) 04057 { 04058 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN; 04059 ULARGE_INTEGER size, offset; 04060 ULONG cbRead, cbWritten; 04061 ULARGE_INTEGER cbTotalRead; 04062 DirRef streamEntryRef; 04063 HRESULT resWrite = S_OK; 04064 HRESULT resRead; 04065 DirEntry streamEntry; 04066 BYTE *buffer; 04067 BlockChainStream *bbTempChain = NULL; 04068 BlockChainStream *bigBlockChain = NULL; 04069 04070 /* 04071 * Create a temporary big block chain that doesn't have 04072 * an associated directory entry. This temporary chain will be 04073 * used to copy data from small blocks to big blocks. 04074 */ 04075 bbTempChain = BlockChainStream_Construct(This, 04076 &bbHeadOfChain, 04077 DIRENTRY_NULL); 04078 if(!bbTempChain) return NULL; 04079 /* 04080 * Grow the big block chain. 04081 */ 04082 size = SmallBlockChainStream_GetSize(*ppsbChain); 04083 BlockChainStream_SetSize(bbTempChain, size); 04084 04085 /* 04086 * Copy the contents of the small block chain to the big block chain 04087 * by small block size increments. 04088 */ 04089 offset.u.LowPart = 0; 04090 offset.u.HighPart = 0; 04091 cbTotalRead.QuadPart = 0; 04092 04093 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); 04094 do 04095 { 04096 resRead = SmallBlockChainStream_ReadAt(*ppsbChain, 04097 offset, 04098 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart), 04099 buffer, 04100 &cbRead); 04101 if (FAILED(resRead)) 04102 break; 04103 04104 if (cbRead > 0) 04105 { 04106 cbTotalRead.QuadPart += cbRead; 04107 04108 resWrite = BlockChainStream_WriteAt(bbTempChain, 04109 offset, 04110 cbRead, 04111 buffer, 04112 &cbWritten); 04113 04114 if (FAILED(resWrite)) 04115 break; 04116 04117 offset.u.LowPart += cbRead; 04118 } 04119 else 04120 { 04121 resRead = STG_E_READFAULT; 04122 break; 04123 } 04124 } while (cbTotalRead.QuadPart < size.QuadPart); 04125 HeapFree(GetProcessHeap(),0,buffer); 04126 04127 size.u.HighPart = 0; 04128 size.u.LowPart = 0; 04129 04130 if (FAILED(resRead) || FAILED(resWrite)) 04131 { 04132 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite); 04133 BlockChainStream_SetSize(bbTempChain, size); 04134 BlockChainStream_Destroy(bbTempChain); 04135 return NULL; 04136 } 04137 04138 /* 04139 * Destroy the small block chain. 04140 */ 04141 streamEntryRef = (*ppsbChain)->ownerDirEntry; 04142 SmallBlockChainStream_SetSize(*ppsbChain, size); 04143 SmallBlockChainStream_Destroy(*ppsbChain); 04144 *ppsbChain = 0; 04145 04146 /* 04147 * Change the directory entry. This chain is now a big block chain 04148 * and it doesn't reside in the small blocks chain anymore. 04149 */ 04150 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); 04151 04152 streamEntry.startingBlock = bbHeadOfChain; 04153 04154 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); 04155 04156 /* 04157 * Destroy the temporary entryless big block chain. 04158 * Create a new big block chain associated with this entry. 04159 */ 04160 BlockChainStream_Destroy(bbTempChain); 04161 bigBlockChain = BlockChainStream_Construct(This, 04162 NULL, 04163 streamEntryRef); 04164 04165 return bigBlockChain; 04166 } 04167 04168 /****************************************************************************** 04169 * Storage32Impl_BigBlocksToSmallBlocks 04170 * 04171 * This method will convert a big block chain to a small block chain. 04172 * The big block chain will be destroyed on success. 04173 */ 04174 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( 04175 StorageImpl* This, 04176 BlockChainStream** ppbbChain, 04177 ULARGE_INTEGER newSize) 04178 { 04179 ULARGE_INTEGER size, offset, cbTotalRead; 04180 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN; 04181 DirRef streamEntryRef; 04182 HRESULT resWrite = S_OK, resRead = S_OK; 04183 DirEntry streamEntry; 04184 BYTE* buffer; 04185 SmallBlockChainStream* sbTempChain; 04186 04187 TRACE("%p %p\n", This, ppbbChain); 04188 04189 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain, 04190 DIRENTRY_NULL); 04191 04192 if(!sbTempChain) 04193 return NULL; 04194 04195 SmallBlockChainStream_SetSize(sbTempChain, newSize); 04196 size = BlockChainStream_GetSize(*ppbbChain); 04197 size.QuadPart = min(size.QuadPart, newSize.QuadPart); 04198 04199 offset.u.HighPart = 0; 04200 offset.u.LowPart = 0; 04201 cbTotalRead.QuadPart = 0; 04202 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize); 04203 while(cbTotalRead.QuadPart < size.QuadPart) 04204 { 04205 resRead = BlockChainStream_ReadAt(*ppbbChain, offset, 04206 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart), 04207 buffer, &cbRead); 04208 04209 if(FAILED(resRead)) 04210 break; 04211 04212 if(cbRead > 0) 04213 { 04214 cbTotalRead.QuadPart += cbRead; 04215 04216 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset, 04217 cbRead, buffer, &cbWritten); 04218 04219 if(FAILED(resWrite)) 04220 break; 04221 04222 offset.u.LowPart += cbRead; 04223 } 04224 else 04225 { 04226 resRead = STG_E_READFAULT; 04227 break; 04228 } 04229 } 04230 HeapFree(GetProcessHeap(), 0, buffer); 04231 04232 size.u.HighPart = 0; 04233 size.u.LowPart = 0; 04234 04235 if(FAILED(resRead) || FAILED(resWrite)) 04236 { 04237 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite); 04238 SmallBlockChainStream_SetSize(sbTempChain, size); 04239 SmallBlockChainStream_Destroy(sbTempChain); 04240 return NULL; 04241 } 04242 04243 /* destroy the original big block chain */ 04244 streamEntryRef = (*ppbbChain)->ownerDirEntry; 04245 BlockChainStream_SetSize(*ppbbChain, size); 04246 BlockChainStream_Destroy(*ppbbChain); 04247 *ppbbChain = NULL; 04248 04249 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); 04250 streamEntry.startingBlock = sbHeadOfChain; 04251 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); 04252 04253 SmallBlockChainStream_Destroy(sbTempChain); 04254 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); 04255 } 04256 04257 static HRESULT StorageBaseImpl_CopyStream( 04258 StorageBaseImpl *dst, DirRef dst_entry, 04259 StorageBaseImpl *src, DirRef src_entry) 04260 { 04261 HRESULT hr; 04262 BYTE data[4096]; 04263 DirEntry srcdata; 04264 ULARGE_INTEGER bytes_copied; 04265 ULONG bytestocopy, bytesread, byteswritten; 04266 04267 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata); 04268 04269 if (SUCCEEDED(hr)) 04270 { 04271 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size); 04272 04273 bytes_copied.QuadPart = 0; 04274 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr)) 04275 { 04276 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart); 04277 04278 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy, 04279 data, &bytesread); 04280 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT; 04281 04282 if (SUCCEEDED(hr)) 04283 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy, 04284 data, &byteswritten); 04285 if (SUCCEEDED(hr)) 04286 { 04287 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT; 04288 bytes_copied.QuadPart += byteswritten; 04289 } 04290 } 04291 } 04292 04293 return hr; 04294 } 04295 04296 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This) 04297 { 04298 DirRef result=This->firstFreeEntry; 04299 04300 while (result < This->entries_size && This->entries[result].inuse) 04301 result++; 04302 04303 if (result == This->entries_size) 04304 { 04305 ULONG new_size = This->entries_size * 2; 04306 TransactedDirEntry *new_entries; 04307 04308 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size); 04309 if (!new_entries) return DIRENTRY_NULL; 04310 04311 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size); 04312 HeapFree(GetProcessHeap(), 0, This->entries); 04313 04314 This->entries = new_entries; 04315 This->entries_size = new_size; 04316 } 04317 04318 This->entries[result].inuse = 1; 04319 04320 This->firstFreeEntry = result+1; 04321 04322 return result; 04323 } 04324 04325 static DirRef TransactedSnapshotImpl_CreateStubEntry( 04326 TransactedSnapshotImpl *This, DirRef parentEntryRef) 04327 { 04328 DirRef stubEntryRef; 04329 TransactedDirEntry *entry; 04330 04331 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This); 04332 04333 if (stubEntryRef != DIRENTRY_NULL) 04334 { 04335 entry = &This->entries[stubEntryRef]; 04336 04337 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef; 04338 04339 entry->read = 0; 04340 } 04341 04342 return stubEntryRef; 04343 } 04344 04345 static HRESULT TransactedSnapshotImpl_EnsureReadEntry( 04346 TransactedSnapshotImpl *This, DirRef entry) 04347 { 04348 HRESULT hr=S_OK; 04349 DirEntry data; 04350 04351 if (!This->entries[entry].read) 04352 { 04353 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, 04354 This->entries[entry].transactedParentEntry, 04355 &data); 04356 04357 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL) 04358 { 04359 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild); 04360 04361 if (data.leftChild == DIRENTRY_NULL) 04362 hr = E_OUTOFMEMORY; 04363 } 04364 04365 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL) 04366 { 04367 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild); 04368 04369 if (data.rightChild == DIRENTRY_NULL) 04370 hr = E_OUTOFMEMORY; 04371 } 04372 04373 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL) 04374 { 04375 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry); 04376 04377 if (data.dirRootEntry == DIRENTRY_NULL) 04378 hr = E_OUTOFMEMORY; 04379 } 04380 04381 if (SUCCEEDED(hr)) 04382 { 04383 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry)); 04384 This->entries[entry].read = 1; 04385 } 04386 } 04387 04388 return hr; 04389 } 04390 04391 static HRESULT TransactedSnapshotImpl_MakeStreamDirty( 04392 TransactedSnapshotImpl *This, DirRef entry) 04393 { 04394 HRESULT hr = S_OK; 04395 04396 if (!This->entries[entry].stream_dirty) 04397 { 04398 DirEntry new_entrydata; 04399 04400 memset(&new_entrydata, 0, sizeof(DirEntry)); 04401 new_entrydata.name[0] = 'S'; 04402 new_entrydata.sizeOfNameString = 1; 04403 new_entrydata.stgType = STGTY_STREAM; 04404 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN; 04405 new_entrydata.leftChild = DIRENTRY_NULL; 04406 new_entrydata.rightChild = DIRENTRY_NULL; 04407 new_entrydata.dirRootEntry = DIRENTRY_NULL; 04408 04409 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata, 04410 &This->entries[entry].stream_entry); 04411 04412 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL) 04413 { 04414 hr = StorageBaseImpl_CopyStream( 04415 This->scratch, This->entries[entry].stream_entry, 04416 This->transactedParent, This->entries[entry].transactedParentEntry); 04417 04418 if (FAILED(hr)) 04419 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry); 04420 } 04421 04422 if (SUCCEEDED(hr)) 04423 This->entries[entry].stream_dirty = 1; 04424 04425 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL) 04426 { 04427 /* Since this entry is modified, and we aren't using its stream data, we 04428 * no longer care about the original entry. */ 04429 DirRef delete_ref; 04430 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry); 04431 04432 if (delete_ref != DIRENTRY_NULL) 04433 This->entries[delete_ref].deleted = 1; 04434 04435 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL; 04436 } 04437 } 04438 04439 return hr; 04440 } 04441 04442 /* Find the first entry in a depth-first traversal. */ 04443 static DirRef TransactedSnapshotImpl_FindFirstChild( 04444 TransactedSnapshotImpl* This, DirRef parent) 04445 { 04446 DirRef cursor, prev; 04447 TransactedDirEntry *entry; 04448 04449 cursor = parent; 04450 entry = &This->entries[cursor]; 04451 while (entry->read) 04452 { 04453 if (entry->data.leftChild != DIRENTRY_NULL) 04454 { 04455 prev = cursor; 04456 cursor = entry->data.leftChild; 04457 entry = &This->entries[cursor]; 04458 entry->parent = prev; 04459 } 04460 else if (entry->data.rightChild != DIRENTRY_NULL) 04461 { 04462 prev = cursor; 04463 cursor = entry->data.rightChild; 04464 entry = &This->entries[cursor]; 04465 entry->parent = prev; 04466 } 04467 else if (entry->data.dirRootEntry != DIRENTRY_NULL) 04468 { 04469 prev = cursor; 04470 cursor = entry->data.dirRootEntry; 04471 entry = &This->entries[cursor]; 04472 entry->parent = prev; 04473 } 04474 else 04475 break; 04476 } 04477 04478 return cursor; 04479 } 04480 04481 /* Find the next entry in a depth-first traversal. */ 04482 static DirRef TransactedSnapshotImpl_FindNextChild( 04483 TransactedSnapshotImpl* This, DirRef current) 04484 { 04485 DirRef parent; 04486 TransactedDirEntry *parent_entry; 04487 04488 parent = This->entries[current].parent; 04489 parent_entry = &This->entries[parent]; 04490 04491 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current) 04492 { 04493 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL) 04494 { 04495 This->entries[parent_entry->data.rightChild].parent = parent; 04496 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild); 04497 } 04498 04499 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL) 04500 { 04501 This->entries[parent_entry->data.dirRootEntry].parent = parent; 04502 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry); 04503 } 04504 } 04505 04506 return parent; 04507 } 04508 04509 /* Return TRUE if we've made a copy of this entry for committing to the parent. */ 04510 static inline BOOL TransactedSnapshotImpl_MadeCopy( 04511 TransactedSnapshotImpl* This, DirRef entry) 04512 { 04513 return entry != DIRENTRY_NULL && 04514 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry; 04515 } 04516 04517 /* Destroy the entries created by CopyTree. */ 04518 static void TransactedSnapshotImpl_DestroyTemporaryCopy( 04519 TransactedSnapshotImpl* This, DirRef stop) 04520 { 04521 DirRef cursor; 04522 TransactedDirEntry *entry; 04523 ULARGE_INTEGER zero; 04524 04525 zero.QuadPart = 0; 04526 04527 if (!This->entries[This->base.storageDirEntry].read) 04528 return; 04529 04530 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry; 04531 04532 if (cursor == DIRENTRY_NULL) 04533 return; 04534 04535 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor); 04536 04537 while (cursor != DIRENTRY_NULL && cursor != stop) 04538 { 04539 if (TransactedSnapshotImpl_MadeCopy(This, cursor)) 04540 { 04541 entry = &This->entries[cursor]; 04542 04543 if (entry->stream_dirty) 04544 StorageBaseImpl_StreamSetSize(This->transactedParent, 04545 entry->newTransactedParentEntry, zero); 04546 04547 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 04548 entry->newTransactedParentEntry); 04549 04550 entry->newTransactedParentEntry = entry->transactedParentEntry; 04551 } 04552 04553 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 04554 } 04555 } 04556 04557 /* Make a copy of our edited tree that we can use in the parent. */ 04558 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This) 04559 { 04560 DirRef cursor; 04561 TransactedDirEntry *entry; 04562 HRESULT hr = S_OK; 04563 04564 cursor = This->base.storageDirEntry; 04565 entry = &This->entries[cursor]; 04566 entry->parent = DIRENTRY_NULL; 04567 entry->newTransactedParentEntry = entry->transactedParentEntry; 04568 04569 if (entry->data.dirRootEntry == DIRENTRY_NULL) 04570 return S_OK; 04571 04572 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL; 04573 04574 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry); 04575 entry = &This->entries[cursor]; 04576 04577 while (cursor != DIRENTRY_NULL) 04578 { 04579 /* Make a copy of this entry in the transacted parent. */ 04580 if (!entry->read || 04581 (!entry->dirty && !entry->stream_dirty && 04582 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) && 04583 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) && 04584 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry))) 04585 entry->newTransactedParentEntry = entry->transactedParentEntry; 04586 else 04587 { 04588 DirEntry newData; 04589 04590 memcpy(&newData, &entry->data, sizeof(DirEntry)); 04591 04592 newData.size.QuadPart = 0; 04593 newData.startingBlock = BLOCK_END_OF_CHAIN; 04594 04595 if (newData.leftChild != DIRENTRY_NULL) 04596 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry; 04597 04598 if (newData.rightChild != DIRENTRY_NULL) 04599 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry; 04600 04601 if (newData.dirRootEntry != DIRENTRY_NULL) 04602 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry; 04603 04604 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData, 04605 &entry->newTransactedParentEntry); 04606 if (FAILED(hr)) 04607 { 04608 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); 04609 return hr; 04610 } 04611 04612 if (entry->stream_dirty) 04613 { 04614 hr = StorageBaseImpl_CopyStream( 04615 This->transactedParent, entry->newTransactedParentEntry, 04616 This->scratch, entry->stream_entry); 04617 } 04618 else if (entry->data.size.QuadPart) 04619 { 04620 hr = StorageBaseImpl_StreamLink( 04621 This->transactedParent, entry->newTransactedParentEntry, 04622 entry->transactedParentEntry); 04623 } 04624 04625 if (FAILED(hr)) 04626 { 04627 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 04628 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); 04629 return hr; 04630 } 04631 } 04632 04633 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 04634 entry = &This->entries[cursor]; 04635 } 04636 04637 return hr; 04638 } 04639 04640 static HRESULT WINAPI TransactedSnapshotImpl_Commit( 04641 IStorage* iface, 04642 DWORD grfCommitFlags) /* [in] */ 04643 { 04644 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 04645 TransactedDirEntry *root_entry; 04646 DirRef i, dir_root_ref; 04647 DirEntry data; 04648 ULARGE_INTEGER zero; 04649 HRESULT hr; 04650 04651 zero.QuadPart = 0; 04652 04653 TRACE("(%p,%x)\n", iface, grfCommitFlags); 04654 04655 /* Cannot commit a read-only transacted storage */ 04656 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ ) 04657 return STG_E_ACCESSDENIED; 04658 04659 /* To prevent data loss, we create the new structure in the file before we 04660 * delete the old one, so that in case of errors the old data is intact. We 04661 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be 04662 * needed in the rare situation where we have just enough free disk space to 04663 * overwrite the existing data. */ 04664 04665 root_entry = &This->entries[This->base.storageDirEntry]; 04666 04667 if (!root_entry->read) 04668 return S_OK; 04669 04670 hr = TransactedSnapshotImpl_CopyTree(This); 04671 if (FAILED(hr)) return hr; 04672 04673 if (root_entry->data.dirRootEntry == DIRENTRY_NULL) 04674 dir_root_ref = DIRENTRY_NULL; 04675 else 04676 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry; 04677 04678 hr = StorageBaseImpl_Flush(This->transactedParent); 04679 04680 /* Update the storage to use the new data in one step. */ 04681 if (SUCCEEDED(hr)) 04682 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, 04683 root_entry->transactedParentEntry, &data); 04684 04685 if (SUCCEEDED(hr)) 04686 { 04687 data.dirRootEntry = dir_root_ref; 04688 data.clsid = root_entry->data.clsid; 04689 data.ctime = root_entry->data.ctime; 04690 data.mtime = root_entry->data.mtime; 04691 04692 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, 04693 root_entry->transactedParentEntry, &data); 04694 } 04695 04696 /* Try to flush after updating the root storage, but if the flush fails, keep 04697 * going, on the theory that it'll either succeed later or the subsequent 04698 * writes will fail. */ 04699 StorageBaseImpl_Flush(This->transactedParent); 04700 04701 if (SUCCEEDED(hr)) 04702 { 04703 /* Destroy the old now-orphaned data. */ 04704 for (i=0; i<This->entries_size; i++) 04705 { 04706 TransactedDirEntry *entry = &This->entries[i]; 04707 if (entry->inuse) 04708 { 04709 if (entry->deleted) 04710 { 04711 StorageBaseImpl_StreamSetSize(This->transactedParent, 04712 entry->transactedParentEntry, zero); 04713 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 04714 entry->transactedParentEntry); 04715 memset(entry, 0, sizeof(TransactedDirEntry)); 04716 This->firstFreeEntry = min(i, This->firstFreeEntry); 04717 } 04718 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry) 04719 { 04720 if (entry->transactedParentEntry != DIRENTRY_NULL) 04721 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 04722 entry->transactedParentEntry); 04723 if (entry->stream_dirty) 04724 { 04725 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero); 04726 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry); 04727 entry->stream_dirty = 0; 04728 } 04729 entry->dirty = 0; 04730 entry->transactedParentEntry = entry->newTransactedParentEntry; 04731 } 04732 } 04733 } 04734 } 04735 else 04736 { 04737 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL); 04738 } 04739 04740 if (SUCCEEDED(hr)) 04741 hr = StorageBaseImpl_Flush(This->transactedParent); 04742 04743 return hr; 04744 } 04745 04746 static HRESULT WINAPI TransactedSnapshotImpl_Revert( 04747 IStorage* iface) 04748 { 04749 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 04750 ULARGE_INTEGER zero; 04751 ULONG i; 04752 04753 TRACE("(%p)\n", iface); 04754 04755 /* Destroy the open objects. */ 04756 StorageBaseImpl_DeleteAll(&This->base); 04757 04758 /* Clear out the scratch file. */ 04759 zero.QuadPart = 0; 04760 for (i=0; i<This->entries_size; i++) 04761 { 04762 if (This->entries[i].stream_dirty) 04763 { 04764 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry, 04765 zero); 04766 04767 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry); 04768 } 04769 } 04770 04771 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size); 04772 04773 This->firstFreeEntry = 0; 04774 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry); 04775 04776 return S_OK; 04777 } 04778 04779 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This) 04780 { 04781 if (!This->reverted) 04782 { 04783 TRACE("Storage invalidated (stg=%p)\n", This); 04784 04785 This->reverted = 1; 04786 04787 StorageBaseImpl_DeleteAll(This); 04788 } 04789 } 04790 04791 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) 04792 { 04793 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 04794 04795 TransactedSnapshotImpl_Revert((IStorage*)iface); 04796 04797 IStorage_Release((IStorage*)This->transactedParent); 04798 04799 IStorage_Release((IStorage*)This->scratch); 04800 04801 HeapFree(GetProcessHeap(), 0, This->entries); 04802 04803 HeapFree(GetProcessHeap(), 0, This); 04804 } 04805 04806 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface) 04807 { 04808 /* We only need to flush when committing. */ 04809 return S_OK; 04810 } 04811 04812 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 04813 { 04814 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 04815 04816 return StorageBaseImpl_GetFilename(This->transactedParent, result); 04817 } 04818 04819 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base, 04820 const DirEntry *newData, DirRef *index) 04821 { 04822 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04823 DirRef new_ref; 04824 TransactedDirEntry *new_entry; 04825 04826 new_ref = TransactedSnapshotImpl_FindFreeEntry(This); 04827 if (new_ref == DIRENTRY_NULL) 04828 return E_OUTOFMEMORY; 04829 04830 new_entry = &This->entries[new_ref]; 04831 04832 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL; 04833 new_entry->read = 1; 04834 new_entry->dirty = 1; 04835 memcpy(&new_entry->data, newData, sizeof(DirEntry)); 04836 04837 *index = new_ref; 04838 04839 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index); 04840 04841 return S_OK; 04842 } 04843 04844 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base, 04845 DirRef index, const DirEntry *data) 04846 { 04847 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04848 HRESULT hr; 04849 04850 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); 04851 04852 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 04853 if (FAILED(hr)) return hr; 04854 04855 memcpy(&This->entries[index].data, data, sizeof(DirEntry)); 04856 04857 if (index != This->base.storageDirEntry) 04858 { 04859 This->entries[index].dirty = 1; 04860 04861 if (data->size.QuadPart == 0 && 04862 This->entries[index].transactedParentEntry != DIRENTRY_NULL) 04863 { 04864 /* Since this entry is modified, and we aren't using its stream data, we 04865 * no longer care about the original entry. */ 04866 DirRef delete_ref; 04867 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry); 04868 04869 if (delete_ref != DIRENTRY_NULL) 04870 This->entries[delete_ref].deleted = 1; 04871 04872 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; 04873 } 04874 } 04875 04876 return S_OK; 04877 } 04878 04879 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, 04880 DirRef index, DirEntry *data) 04881 { 04882 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04883 HRESULT hr; 04884 04885 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 04886 if (FAILED(hr)) return hr; 04887 04888 memcpy(data, &This->entries[index].data, sizeof(DirEntry)); 04889 04890 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); 04891 04892 return S_OK; 04893 } 04894 04895 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, 04896 DirRef index) 04897 { 04898 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04899 04900 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL || 04901 This->entries[index].data.size.QuadPart != 0) 04902 { 04903 /* If we deleted this entry while it has stream data. We must have left the 04904 * data because some other entry is using it, and we need to leave the 04905 * original entry alone. */ 04906 memset(&This->entries[index], 0, sizeof(TransactedDirEntry)); 04907 This->firstFreeEntry = min(index, This->firstFreeEntry); 04908 } 04909 else 04910 { 04911 This->entries[index].deleted = 1; 04912 } 04913 04914 return S_OK; 04915 } 04916 04917 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base, 04918 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 04919 { 04920 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04921 04922 if (This->entries[index].stream_dirty) 04923 { 04924 return StorageBaseImpl_StreamReadAt(This->scratch, 04925 This->entries[index].stream_entry, offset, size, buffer, bytesRead); 04926 } 04927 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL) 04928 { 04929 /* This stream doesn't live in the parent, and we haven't allocated storage 04930 * for it yet */ 04931 *bytesRead = 0; 04932 return S_OK; 04933 } 04934 else 04935 { 04936 return StorageBaseImpl_StreamReadAt(This->transactedParent, 04937 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead); 04938 } 04939 } 04940 04941 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base, 04942 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 04943 { 04944 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04945 HRESULT hr; 04946 04947 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 04948 if (FAILED(hr)) return hr; 04949 04950 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); 04951 if (FAILED(hr)) return hr; 04952 04953 hr = StorageBaseImpl_StreamWriteAt(This->scratch, 04954 This->entries[index].stream_entry, offset, size, buffer, bytesWritten); 04955 04956 if (SUCCEEDED(hr) && size != 0) 04957 This->entries[index].data.size.QuadPart = max( 04958 This->entries[index].data.size.QuadPart, 04959 offset.QuadPart + size); 04960 04961 return hr; 04962 } 04963 04964 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, 04965 DirRef index, ULARGE_INTEGER newsize) 04966 { 04967 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 04968 HRESULT hr; 04969 04970 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 04971 if (FAILED(hr)) return hr; 04972 04973 if (This->entries[index].data.size.QuadPart == newsize.QuadPart) 04974 return S_OK; 04975 04976 if (newsize.QuadPart == 0) 04977 { 04978 /* Destroy any parent references or entries in the scratch file. */ 04979 if (This->entries[index].stream_dirty) 04980 { 04981 ULARGE_INTEGER zero; 04982 zero.QuadPart = 0; 04983 StorageBaseImpl_StreamSetSize(This->scratch, 04984 This->entries[index].stream_entry, zero); 04985 StorageBaseImpl_DestroyDirEntry(This->scratch, 04986 This->entries[index].stream_entry); 04987 This->entries[index].stream_dirty = 0; 04988 } 04989 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL) 04990 { 04991 DirRef delete_ref; 04992 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry); 04993 04994 if (delete_ref != DIRENTRY_NULL) 04995 This->entries[delete_ref].deleted = 1; 04996 04997 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; 04998 } 04999 } 05000 else 05001 { 05002 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); 05003 if (FAILED(hr)) return hr; 05004 05005 hr = StorageBaseImpl_StreamSetSize(This->scratch, 05006 This->entries[index].stream_entry, newsize); 05007 } 05008 05009 if (SUCCEEDED(hr)) 05010 This->entries[index].data.size = newsize; 05011 05012 return hr; 05013 } 05014 05015 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base, 05016 DirRef dst, DirRef src) 05017 { 05018 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 05019 HRESULT hr; 05020 TransactedDirEntry *dst_entry, *src_entry; 05021 05022 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src); 05023 if (FAILED(hr)) return hr; 05024 05025 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst); 05026 if (FAILED(hr)) return hr; 05027 05028 dst_entry = &This->entries[dst]; 05029 src_entry = &This->entries[src]; 05030 05031 dst_entry->stream_dirty = src_entry->stream_dirty; 05032 dst_entry->stream_entry = src_entry->stream_entry; 05033 dst_entry->transactedParentEntry = src_entry->transactedParentEntry; 05034 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry; 05035 dst_entry->data.size = src_entry->data.size; 05036 05037 return S_OK; 05038 } 05039 05040 static const IStorageVtbl TransactedSnapshotImpl_Vtbl = 05041 { 05042 StorageBaseImpl_QueryInterface, 05043 StorageBaseImpl_AddRef, 05044 StorageBaseImpl_Release, 05045 StorageBaseImpl_CreateStream, 05046 StorageBaseImpl_OpenStream, 05047 StorageBaseImpl_CreateStorage, 05048 StorageBaseImpl_OpenStorage, 05049 StorageBaseImpl_CopyTo, 05050 StorageBaseImpl_MoveElementTo, 05051 TransactedSnapshotImpl_Commit, 05052 TransactedSnapshotImpl_Revert, 05053 StorageBaseImpl_EnumElements, 05054 StorageBaseImpl_DestroyElement, 05055 StorageBaseImpl_RenameElement, 05056 StorageBaseImpl_SetElementTimes, 05057 StorageBaseImpl_SetClass, 05058 StorageBaseImpl_SetStateBits, 05059 StorageBaseImpl_Stat 05060 }; 05061 05062 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl = 05063 { 05064 TransactedSnapshotImpl_Destroy, 05065 TransactedSnapshotImpl_Invalidate, 05066 TransactedSnapshotImpl_Flush, 05067 TransactedSnapshotImpl_GetFilename, 05068 TransactedSnapshotImpl_CreateDirEntry, 05069 TransactedSnapshotImpl_WriteDirEntry, 05070 TransactedSnapshotImpl_ReadDirEntry, 05071 TransactedSnapshotImpl_DestroyDirEntry, 05072 TransactedSnapshotImpl_StreamReadAt, 05073 TransactedSnapshotImpl_StreamWriteAt, 05074 TransactedSnapshotImpl_StreamSetSize, 05075 TransactedSnapshotImpl_StreamLink 05076 }; 05077 05078 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, 05079 TransactedSnapshotImpl** result) 05080 { 05081 HRESULT hr; 05082 05083 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl)); 05084 if (*result) 05085 { 05086 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl; 05087 05088 /* This is OK because the property set storage functions use the IStorage functions. */ 05089 (*result)->base.pssVtbl = parentStorage->pssVtbl; 05090 05091 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl; 05092 05093 list_init(&(*result)->base.strmHead); 05094 05095 list_init(&(*result)->base.storageHead); 05096 05097 (*result)->base.ref = 1; 05098 05099 (*result)->base.openFlags = parentStorage->openFlags; 05100 05101 /* Create a new temporary storage to act as the scratch file. */ 05102 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE, 05103 0, (IStorage**)&(*result)->scratch); 05104 05105 if (SUCCEEDED(hr)) 05106 { 05107 ULONG num_entries = 20; 05108 05109 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries); 05110 05111 (*result)->entries_size = num_entries; 05112 05113 (*result)->firstFreeEntry = 0; 05114 05115 if ((*result)->entries) 05116 { 05117 /* parentStorage already has 1 reference, which we take over here. */ 05118 (*result)->transactedParent = parentStorage; 05119 05120 parentStorage->transactedChild = (StorageBaseImpl*)*result; 05121 05122 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry); 05123 } 05124 else 05125 { 05126 IStorage_Release((IStorage*)(*result)->scratch); 05127 05128 hr = E_OUTOFMEMORY; 05129 } 05130 } 05131 05132 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result)); 05133 05134 return hr; 05135 } 05136 else 05137 return E_OUTOFMEMORY; 05138 } 05139 05140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage, 05141 StorageBaseImpl** result) 05142 { 05143 static int fixme=0; 05144 05145 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++) 05146 { 05147 FIXME("Unimplemented flags %x\n", parentStorage->openFlags); 05148 } 05149 05150 return TransactedSnapshotImpl_Construct(parentStorage, 05151 (TransactedSnapshotImpl**)result); 05152 } 05153 05154 static HRESULT Storage_Construct( 05155 HANDLE hFile, 05156 LPCOLESTR pwcsName, 05157 ILockBytes* pLkbyt, 05158 DWORD openFlags, 05159 BOOL fileBased, 05160 BOOL create, 05161 ULONG sector_size, 05162 StorageBaseImpl** result) 05163 { 05164 StorageImpl *newStorage; 05165 StorageBaseImpl *newTransactedStorage; 05166 HRESULT hr; 05167 05168 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage); 05169 if (FAILED(hr)) goto end; 05170 05171 if (openFlags & STGM_TRANSACTED) 05172 { 05173 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage); 05174 if (FAILED(hr)) 05175 IStorage_Release((IStorage*)newStorage); 05176 else 05177 *result = newTransactedStorage; 05178 } 05179 else 05180 *result = &newStorage->base; 05181 05182 end: 05183 return hr; 05184 } 05185 05186 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base ) 05187 { 05188 StorageInternalImpl* This = (StorageInternalImpl*) base; 05189 05190 if (!This->base.reverted) 05191 { 05192 TRACE("Storage invalidated (stg=%p)\n", This); 05193 05194 This->base.reverted = 1; 05195 05196 This->parentStorage = NULL; 05197 05198 StorageBaseImpl_DeleteAll(&This->base); 05199 05200 list_remove(&This->ParentListEntry); 05201 } 05202 } 05203 05204 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface) 05205 { 05206 StorageInternalImpl* This = (StorageInternalImpl*) iface; 05207 05208 StorageInternalImpl_Invalidate(&This->base); 05209 05210 HeapFree(GetProcessHeap(), 0, This); 05211 } 05212 05213 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface) 05214 { 05215 StorageInternalImpl* This = (StorageInternalImpl*) iface; 05216 05217 return StorageBaseImpl_Flush(This->parentStorage); 05218 } 05219 05220 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 05221 { 05222 StorageInternalImpl* This = (StorageInternalImpl*) iface; 05223 05224 return StorageBaseImpl_GetFilename(This->parentStorage, result); 05225 } 05226 05227 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base, 05228 const DirEntry *newData, DirRef *index) 05229 { 05230 StorageInternalImpl* This = (StorageInternalImpl*) base; 05231 05232 return StorageBaseImpl_CreateDirEntry(This->parentStorage, 05233 newData, index); 05234 } 05235 05236 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base, 05237 DirRef index, const DirEntry *data) 05238 { 05239 StorageInternalImpl* This = (StorageInternalImpl*) base; 05240 05241 return StorageBaseImpl_WriteDirEntry(This->parentStorage, 05242 index, data); 05243 } 05244 05245 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base, 05246 DirRef index, DirEntry *data) 05247 { 05248 StorageInternalImpl* This = (StorageInternalImpl*) base; 05249 05250 return StorageBaseImpl_ReadDirEntry(This->parentStorage, 05251 index, data); 05252 } 05253 05254 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base, 05255 DirRef index) 05256 { 05257 StorageInternalImpl* This = (StorageInternalImpl*) base; 05258 05259 return StorageBaseImpl_DestroyDirEntry(This->parentStorage, 05260 index); 05261 } 05262 05263 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base, 05264 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 05265 { 05266 StorageInternalImpl* This = (StorageInternalImpl*) base; 05267 05268 return StorageBaseImpl_StreamReadAt(This->parentStorage, 05269 index, offset, size, buffer, bytesRead); 05270 } 05271 05272 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base, 05273 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 05274 { 05275 StorageInternalImpl* This = (StorageInternalImpl*) base; 05276 05277 return StorageBaseImpl_StreamWriteAt(This->parentStorage, 05278 index, offset, size, buffer, bytesWritten); 05279 } 05280 05281 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base, 05282 DirRef index, ULARGE_INTEGER newsize) 05283 { 05284 StorageInternalImpl* This = (StorageInternalImpl*) base; 05285 05286 return StorageBaseImpl_StreamSetSize(This->parentStorage, 05287 index, newsize); 05288 } 05289 05290 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base, 05291 DirRef dst, DirRef src) 05292 { 05293 StorageInternalImpl* This = (StorageInternalImpl*) base; 05294 05295 return StorageBaseImpl_StreamLink(This->parentStorage, 05296 dst, src); 05297 } 05298 05299 /****************************************************************************** 05300 ** 05301 ** Storage32InternalImpl_Commit 05302 ** 05303 */ 05304 static HRESULT WINAPI StorageInternalImpl_Commit( 05305 IStorage* iface, 05306 DWORD grfCommitFlags) /* [in] */ 05307 { 05308 StorageBaseImpl* base = (StorageBaseImpl*) iface; 05309 TRACE("(%p,%x)\n", iface, grfCommitFlags); 05310 return StorageBaseImpl_Flush(base); 05311 } 05312 05313 /****************************************************************************** 05314 ** 05315 ** Storage32InternalImpl_Revert 05316 ** 05317 */ 05318 static HRESULT WINAPI StorageInternalImpl_Revert( 05319 IStorage* iface) 05320 { 05321 FIXME("(%p): stub\n", iface); 05322 return S_OK; 05323 } 05324 05325 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This) 05326 { 05327 IStorage_Release((IStorage*)This->parentStorage); 05328 HeapFree(GetProcessHeap(), 0, This); 05329 } 05330 05331 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface( 05332 IEnumSTATSTG* iface, 05333 REFIID riid, 05334 void** ppvObject) 05335 { 05336 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05337 05338 if (ppvObject==0) 05339 return E_INVALIDARG; 05340 05341 *ppvObject = 0; 05342 05343 if (IsEqualGUID(&IID_IUnknown, riid) || 05344 IsEqualGUID(&IID_IEnumSTATSTG, riid)) 05345 { 05346 *ppvObject = This; 05347 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface); 05348 return S_OK; 05349 } 05350 05351 return E_NOINTERFACE; 05352 } 05353 05354 static ULONG WINAPI IEnumSTATSTGImpl_AddRef( 05355 IEnumSTATSTG* iface) 05356 { 05357 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05358 return InterlockedIncrement(&This->ref); 05359 } 05360 05361 static ULONG WINAPI IEnumSTATSTGImpl_Release( 05362 IEnumSTATSTG* iface) 05363 { 05364 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05365 05366 ULONG newRef; 05367 05368 newRef = InterlockedDecrement(&This->ref); 05369 05370 if (newRef==0) 05371 { 05372 IEnumSTATSTGImpl_Destroy(This); 05373 } 05374 05375 return newRef; 05376 } 05377 05378 static HRESULT IEnumSTATSTGImpl_GetNextRef( 05379 IEnumSTATSTGImpl* This, 05380 DirRef *ref) 05381 { 05382 DirRef result = DIRENTRY_NULL; 05383 DirRef searchNode; 05384 DirEntry entry; 05385 HRESULT hr; 05386 WCHAR result_name[DIRENTRY_NAME_MAX_LEN]; 05387 05388 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, 05389 This->parentStorage->storageDirEntry, &entry); 05390 searchNode = entry.dirRootEntry; 05391 05392 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL) 05393 { 05394 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry); 05395 05396 if (SUCCEEDED(hr)) 05397 { 05398 LONG diff = entryNameCmp( entry.name, This->name); 05399 05400 if (diff <= 0) 05401 { 05402 searchNode = entry.rightChild; 05403 } 05404 else 05405 { 05406 result = searchNode; 05407 memcpy(result_name, entry.name, sizeof(result_name)); 05408 searchNode = entry.leftChild; 05409 } 05410 } 05411 } 05412 05413 if (SUCCEEDED(hr)) 05414 { 05415 *ref = result; 05416 if (result != DIRENTRY_NULL) 05417 memcpy(This->name, result_name, sizeof(result_name)); 05418 } 05419 05420 return hr; 05421 } 05422 05423 static HRESULT WINAPI IEnumSTATSTGImpl_Next( 05424 IEnumSTATSTG* iface, 05425 ULONG celt, 05426 STATSTG* rgelt, 05427 ULONG* pceltFetched) 05428 { 05429 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05430 05431 DirEntry currentEntry; 05432 STATSTG* currentReturnStruct = rgelt; 05433 ULONG objectFetched = 0; 05434 DirRef currentSearchNode; 05435 HRESULT hr=S_OK; 05436 05437 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) 05438 return E_INVALIDARG; 05439 05440 if (This->parentStorage->reverted) 05441 return STG_E_REVERTED; 05442 05443 /* 05444 * To avoid the special case, get another pointer to a ULONG value if 05445 * the caller didn't supply one. 05446 */ 05447 if (pceltFetched==0) 05448 pceltFetched = &objectFetched; 05449 05450 /* 05451 * Start the iteration, we will iterate until we hit the end of the 05452 * linked list or until we hit the number of items to iterate through 05453 */ 05454 *pceltFetched = 0; 05455 05456 while ( *pceltFetched < celt ) 05457 { 05458 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); 05459 05460 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) 05461 break; 05462 05463 /* 05464 * Read the entry from the storage. 05465 */ 05466 StorageBaseImpl_ReadDirEntry(This->parentStorage, 05467 currentSearchNode, 05468 ¤tEntry); 05469 05470 /* 05471 * Copy the information to the return buffer. 05472 */ 05473 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, 05474 currentReturnStruct, 05475 ¤tEntry, 05476 STATFLAG_DEFAULT); 05477 05478 /* 05479 * Step to the next item in the iteration 05480 */ 05481 (*pceltFetched)++; 05482 currentReturnStruct++; 05483 } 05484 05485 if (SUCCEEDED(hr) && *pceltFetched != celt) 05486 hr = S_FALSE; 05487 05488 return hr; 05489 } 05490 05491 05492 static HRESULT WINAPI IEnumSTATSTGImpl_Skip( 05493 IEnumSTATSTG* iface, 05494 ULONG celt) 05495 { 05496 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05497 05498 ULONG objectFetched = 0; 05499 DirRef currentSearchNode; 05500 HRESULT hr=S_OK; 05501 05502 if (This->parentStorage->reverted) 05503 return STG_E_REVERTED; 05504 05505 while ( (objectFetched < celt) ) 05506 { 05507 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); 05508 05509 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) 05510 break; 05511 05512 objectFetched++; 05513 } 05514 05515 if (SUCCEEDED(hr) && objectFetched != celt) 05516 return S_FALSE; 05517 05518 return hr; 05519 } 05520 05521 static HRESULT WINAPI IEnumSTATSTGImpl_Reset( 05522 IEnumSTATSTG* iface) 05523 { 05524 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05525 05526 if (This->parentStorage->reverted) 05527 return STG_E_REVERTED; 05528 05529 This->name[0] = 0; 05530 05531 return S_OK; 05532 } 05533 05534 static HRESULT WINAPI IEnumSTATSTGImpl_Clone( 05535 IEnumSTATSTG* iface, 05536 IEnumSTATSTG** ppenum) 05537 { 05538 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 05539 05540 IEnumSTATSTGImpl* newClone; 05541 05542 if (This->parentStorage->reverted) 05543 return STG_E_REVERTED; 05544 05545 /* 05546 * Perform a sanity check on the parameters. 05547 */ 05548 if (ppenum==0) 05549 return E_INVALIDARG; 05550 05551 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage, 05552 This->storageDirEntry); 05553 05554 05555 /* 05556 * The new clone enumeration must point to the same current node as 05557 * the ole one. 05558 */ 05559 memcpy(newClone->name, This->name, sizeof(newClone->name)); 05560 05561 *ppenum = &newClone->IEnumSTATSTG_iface; 05562 05563 /* 05564 * Don't forget to nail down a reference to the clone before 05565 * returning it. 05566 */ 05567 IEnumSTATSTGImpl_AddRef(*ppenum); 05568 05569 return S_OK; 05570 } 05571 05572 /* 05573 * Virtual function table for the IEnumSTATSTGImpl class. 05574 */ 05575 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl = 05576 { 05577 IEnumSTATSTGImpl_QueryInterface, 05578 IEnumSTATSTGImpl_AddRef, 05579 IEnumSTATSTGImpl_Release, 05580 IEnumSTATSTGImpl_Next, 05581 IEnumSTATSTGImpl_Skip, 05582 IEnumSTATSTGImpl_Reset, 05583 IEnumSTATSTGImpl_Clone 05584 }; 05585 05586 /****************************************************************************** 05587 ** IEnumSTATSTGImpl implementation 05588 */ 05589 05590 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct( 05591 StorageBaseImpl* parentStorage, 05592 DirRef storageDirEntry) 05593 { 05594 IEnumSTATSTGImpl* newEnumeration; 05595 05596 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl)); 05597 05598 if (newEnumeration!=0) 05599 { 05600 /* 05601 * Set-up the virtual function table and reference count. 05602 */ 05603 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl; 05604 newEnumeration->ref = 0; 05605 05606 /* 05607 * We want to nail-down the reference to the storage in case the 05608 * enumeration out-lives the storage in the client application. 05609 */ 05610 newEnumeration->parentStorage = parentStorage; 05611 IStorage_AddRef((IStorage*)newEnumeration->parentStorage); 05612 05613 newEnumeration->storageDirEntry = storageDirEntry; 05614 05615 /* 05616 * Make sure the current node of the iterator is the first one. 05617 */ 05618 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface); 05619 } 05620 05621 return newEnumeration; 05622 } 05623 05624 /* 05625 * Virtual function table for the Storage32InternalImpl class. 05626 */ 05627 static const IStorageVtbl Storage32InternalImpl_Vtbl = 05628 { 05629 StorageBaseImpl_QueryInterface, 05630 StorageBaseImpl_AddRef, 05631 StorageBaseImpl_Release, 05632 StorageBaseImpl_CreateStream, 05633 StorageBaseImpl_OpenStream, 05634 StorageBaseImpl_CreateStorage, 05635 StorageBaseImpl_OpenStorage, 05636 StorageBaseImpl_CopyTo, 05637 StorageBaseImpl_MoveElementTo, 05638 StorageInternalImpl_Commit, 05639 StorageInternalImpl_Revert, 05640 StorageBaseImpl_EnumElements, 05641 StorageBaseImpl_DestroyElement, 05642 StorageBaseImpl_RenameElement, 05643 StorageBaseImpl_SetElementTimes, 05644 StorageBaseImpl_SetClass, 05645 StorageBaseImpl_SetStateBits, 05646 StorageBaseImpl_Stat 05647 }; 05648 05649 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl = 05650 { 05651 StorageInternalImpl_Destroy, 05652 StorageInternalImpl_Invalidate, 05653 StorageInternalImpl_Flush, 05654 StorageInternalImpl_GetFilename, 05655 StorageInternalImpl_CreateDirEntry, 05656 StorageInternalImpl_WriteDirEntry, 05657 StorageInternalImpl_ReadDirEntry, 05658 StorageInternalImpl_DestroyDirEntry, 05659 StorageInternalImpl_StreamReadAt, 05660 StorageInternalImpl_StreamWriteAt, 05661 StorageInternalImpl_StreamSetSize, 05662 StorageInternalImpl_StreamLink 05663 }; 05664 05665 /****************************************************************************** 05666 ** Storage32InternalImpl implementation 05667 */ 05668 05669 static StorageInternalImpl* StorageInternalImpl_Construct( 05670 StorageBaseImpl* parentStorage, 05671 DWORD openFlags, 05672 DirRef storageDirEntry) 05673 { 05674 StorageInternalImpl* newStorage; 05675 05676 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl)); 05677 05678 if (newStorage!=0) 05679 { 05680 list_init(&newStorage->base.strmHead); 05681 05682 list_init(&newStorage->base.storageHead); 05683 05684 /* 05685 * Initialize the virtual function table. 05686 */ 05687 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl; 05688 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl; 05689 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl; 05690 newStorage->base.openFlags = (openFlags & ~STGM_CREATE); 05691 05692 newStorage->base.reverted = 0; 05693 05694 newStorage->base.ref = 1; 05695 05696 newStorage->parentStorage = parentStorage; 05697 05698 /* 05699 * Keep a reference to the directory entry of this storage 05700 */ 05701 newStorage->base.storageDirEntry = storageDirEntry; 05702 05703 newStorage->base.create = 0; 05704 05705 return newStorage; 05706 } 05707 05708 return 0; 05709 } 05710 05711 /****************************************************************************** 05712 ** StorageUtl implementation 05713 */ 05714 05715 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value) 05716 { 05717 WORD tmp; 05718 05719 memcpy(&tmp, buffer+offset, sizeof(WORD)); 05720 *value = lendian16toh(tmp); 05721 } 05722 05723 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value) 05724 { 05725 value = htole16(value); 05726 memcpy(buffer+offset, &value, sizeof(WORD)); 05727 } 05728 05729 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value) 05730 { 05731 DWORD tmp; 05732 05733 memcpy(&tmp, buffer+offset, sizeof(DWORD)); 05734 *value = lendian32toh(tmp); 05735 } 05736 05737 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value) 05738 { 05739 value = htole32(value); 05740 memcpy(buffer+offset, &value, sizeof(DWORD)); 05741 } 05742 05743 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, 05744 ULARGE_INTEGER* value) 05745 { 05746 #ifdef WORDS_BIGENDIAN 05747 ULARGE_INTEGER tmp; 05748 05749 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER)); 05750 value->u.LowPart = htole32(tmp.u.HighPart); 05751 value->u.HighPart = htole32(tmp.u.LowPart); 05752 #else 05753 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER)); 05754 #endif 05755 } 05756 05757 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset, 05758 const ULARGE_INTEGER *value) 05759 { 05760 #ifdef WORDS_BIGENDIAN 05761 ULARGE_INTEGER tmp; 05762 05763 tmp.u.LowPart = htole32(value->u.HighPart); 05764 tmp.u.HighPart = htole32(value->u.LowPart); 05765 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER)); 05766 #else 05767 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER)); 05768 #endif 05769 } 05770 05771 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value) 05772 { 05773 StorageUtl_ReadDWord(buffer, offset, &(value->Data1)); 05774 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2)); 05775 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3)); 05776 05777 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4)); 05778 } 05779 05780 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value) 05781 { 05782 StorageUtl_WriteDWord(buffer, offset, value->Data1); 05783 StorageUtl_WriteWord(buffer, offset+4, value->Data2); 05784 StorageUtl_WriteWord(buffer, offset+6, value->Data3); 05785 05786 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4)); 05787 } 05788 05789 void StorageUtl_CopyDirEntryToSTATSTG( 05790 StorageBaseImpl* storage, 05791 STATSTG* destination, 05792 const DirEntry* source, 05793 int statFlags) 05794 { 05795 /* 05796 * The copy of the string occurs only when the flag is not set 05797 */ 05798 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT) 05799 { 05800 /* Use the filename for the root storage. */ 05801 destination->pwcsName = 0; 05802 StorageBaseImpl_GetFilename(storage, &destination->pwcsName); 05803 } 05804 else if( ((statFlags & STATFLAG_NONAME) != 0) || 05805 (source->name[0] == 0) ) 05806 { 05807 destination->pwcsName = 0; 05808 } 05809 else 05810 { 05811 destination->pwcsName = 05812 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR)); 05813 05814 strcpyW(destination->pwcsName, source->name); 05815 } 05816 05817 switch (source->stgType) 05818 { 05819 case STGTY_STORAGE: 05820 case STGTY_ROOT: 05821 destination->type = STGTY_STORAGE; 05822 break; 05823 case STGTY_STREAM: 05824 destination->type = STGTY_STREAM; 05825 break; 05826 default: 05827 destination->type = STGTY_STREAM; 05828 break; 05829 } 05830 05831 destination->cbSize = source->size; 05832 /* 05833 currentReturnStruct->mtime = {0}; TODO 05834 currentReturnStruct->ctime = {0}; 05835 currentReturnStruct->atime = {0}; 05836 */ 05837 destination->grfMode = 0; 05838 destination->grfLocksSupported = 0; 05839 destination->clsid = source->clsid; 05840 destination->grfStateBits = 0; 05841 destination->reserved = 0; 05842 } 05843 05844 /****************************************************************************** 05845 ** BlockChainStream implementation 05846 */ 05847 05848 /* Read and save the index of all blocks in this stream. */ 05849 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) 05850 { 05851 ULONG next_sector, next_offset; 05852 HRESULT hr; 05853 struct BlockChainRun *last_run; 05854 05855 if (This->indexCacheLen == 0) 05856 { 05857 last_run = NULL; 05858 next_offset = 0; 05859 next_sector = BlockChainStream_GetHeadOfChain(This); 05860 } 05861 else 05862 { 05863 last_run = &This->indexCache[This->indexCacheLen-1]; 05864 next_offset = last_run->lastOffset+1; 05865 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, 05866 last_run->firstSector + last_run->lastOffset - last_run->firstOffset, 05867 &next_sector); 05868 if (FAILED(hr)) return hr; 05869 } 05870 05871 while (next_sector != BLOCK_END_OF_CHAIN) 05872 { 05873 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset) 05874 { 05875 /* Add the current block to the cache. */ 05876 if (This->indexCacheSize == 0) 05877 { 05878 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16); 05879 if (!This->indexCache) return E_OUTOFMEMORY; 05880 This->indexCacheSize = 16; 05881 } 05882 else if (This->indexCacheSize == This->indexCacheLen) 05883 { 05884 struct BlockChainRun *new_cache; 05885 ULONG new_size; 05886 05887 new_size = This->indexCacheSize * 2; 05888 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size); 05889 if (!new_cache) return E_OUTOFMEMORY; 05890 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen); 05891 05892 HeapFree(GetProcessHeap(), 0, This->indexCache); 05893 This->indexCache = new_cache; 05894 This->indexCacheSize = new_size; 05895 } 05896 05897 This->indexCacheLen++; 05898 last_run = &This->indexCache[This->indexCacheLen-1]; 05899 last_run->firstSector = next_sector; 05900 last_run->firstOffset = next_offset; 05901 } 05902 05903 last_run->lastOffset = next_offset; 05904 05905 /* Find the next block. */ 05906 next_offset++; 05907 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector); 05908 if (FAILED(hr)) return hr; 05909 } 05910 05911 if (This->indexCacheLen) 05912 { 05913 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset; 05914 This->numBlocks = last_run->lastOffset+1; 05915 } 05916 else 05917 { 05918 This->tailIndex = BLOCK_END_OF_CHAIN; 05919 This->numBlocks = 0; 05920 } 05921 05922 return S_OK; 05923 } 05924 05925 /* Locate the nth block in this stream. */ 05926 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) 05927 { 05928 ULONG min_offset = 0, max_offset = This->numBlocks-1; 05929 ULONG min_run = 0, max_run = This->indexCacheLen-1; 05930 05931 if (offset >= This->numBlocks) 05932 return BLOCK_END_OF_CHAIN; 05933 05934 while (min_run < max_run) 05935 { 05936 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset); 05937 if (offset < This->indexCache[run_to_check].firstOffset) 05938 { 05939 max_offset = This->indexCache[run_to_check].firstOffset-1; 05940 max_run = run_to_check-1; 05941 } 05942 else if (offset > This->indexCache[run_to_check].lastOffset) 05943 { 05944 min_offset = This->indexCache[run_to_check].lastOffset+1; 05945 min_run = run_to_check+1; 05946 } 05947 else 05948 /* Block is in this run. */ 05949 min_run = max_run = run_to_check; 05950 } 05951 05952 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; 05953 } 05954 05955 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, 05956 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) 05957 { 05958 BlockChainBlock *result=NULL; 05959 int i; 05960 05961 for (i=0; i<2; i++) 05962 if (This->cachedBlocks[i].index == index) 05963 { 05964 *sector = This->cachedBlocks[i].sector; 05965 *block = &This->cachedBlocks[i]; 05966 return S_OK; 05967 } 05968 05969 *sector = BlockChainStream_GetSectorOfOffset(This, index); 05970 if (*sector == BLOCK_END_OF_CHAIN) 05971 return STG_E_DOCFILECORRUPT; 05972 05973 if (create) 05974 { 05975 if (This->cachedBlocks[0].index == 0xffffffff) 05976 result = &This->cachedBlocks[0]; 05977 else if (This->cachedBlocks[1].index == 0xffffffff) 05978 result = &This->cachedBlocks[1]; 05979 else 05980 { 05981 result = &This->cachedBlocks[This->blockToEvict++]; 05982 if (This->blockToEvict == 2) 05983 This->blockToEvict = 0; 05984 } 05985 05986 if (result->dirty) 05987 { 05988 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data)) 05989 return STG_E_WRITEFAULT; 05990 result->dirty = 0; 05991 } 05992 05993 result->read = 0; 05994 result->index = index; 05995 result->sector = *sector; 05996 } 05997 05998 *block = result; 05999 return S_OK; 06000 } 06001 06002 BlockChainStream* BlockChainStream_Construct( 06003 StorageImpl* parentStorage, 06004 ULONG* headOfStreamPlaceHolder, 06005 DirRef dirEntry) 06006 { 06007 BlockChainStream* newStream; 06008 06009 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream)); 06010 06011 newStream->parentStorage = parentStorage; 06012 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; 06013 newStream->ownerDirEntry = dirEntry; 06014 newStream->indexCache = NULL; 06015 newStream->indexCacheLen = 0; 06016 newStream->indexCacheSize = 0; 06017 newStream->cachedBlocks[0].index = 0xffffffff; 06018 newStream->cachedBlocks[0].dirty = 0; 06019 newStream->cachedBlocks[1].index = 0xffffffff; 06020 newStream->cachedBlocks[1].dirty = 0; 06021 newStream->blockToEvict = 0; 06022 06023 if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) 06024 { 06025 HeapFree(GetProcessHeap(), 0, newStream->indexCache); 06026 HeapFree(GetProcessHeap(), 0, newStream); 06027 return NULL; 06028 } 06029 06030 return newStream; 06031 } 06032 06033 HRESULT BlockChainStream_Flush(BlockChainStream* This) 06034 { 06035 int i; 06036 if (!This) return S_OK; 06037 for (i=0; i<2; i++) 06038 { 06039 if (This->cachedBlocks[i].dirty) 06040 { 06041 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data)) 06042 This->cachedBlocks[i].dirty = 0; 06043 else 06044 return STG_E_WRITEFAULT; 06045 } 06046 } 06047 return S_OK; 06048 } 06049 06050 void BlockChainStream_Destroy(BlockChainStream* This) 06051 { 06052 if (This) 06053 { 06054 BlockChainStream_Flush(This); 06055 HeapFree(GetProcessHeap(), 0, This->indexCache); 06056 } 06057 HeapFree(GetProcessHeap(), 0, This); 06058 } 06059 06060 /****************************************************************************** 06061 * BlockChainStream_GetHeadOfChain 06062 * 06063 * Returns the head of this stream chain. 06064 * Some special chains don't have directory entries, their heads are kept in 06065 * This->headOfStreamPlaceHolder. 06066 * 06067 */ 06068 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This) 06069 { 06070 DirEntry chainEntry; 06071 HRESULT hr; 06072 06073 if (This->headOfStreamPlaceHolder != 0) 06074 return *(This->headOfStreamPlaceHolder); 06075 06076 if (This->ownerDirEntry != DIRENTRY_NULL) 06077 { 06078 hr = StorageImpl_ReadDirEntry( 06079 This->parentStorage, 06080 This->ownerDirEntry, 06081 &chainEntry); 06082 06083 if (SUCCEEDED(hr)) 06084 { 06085 return chainEntry.startingBlock; 06086 } 06087 } 06088 06089 return BLOCK_END_OF_CHAIN; 06090 } 06091 06092 /****************************************************************************** 06093 * BlockChainStream_GetCount 06094 * 06095 * Returns the number of blocks that comprises this chain. 06096 * This is not the size of the stream as the last block may not be full! 06097 */ 06098 static ULONG BlockChainStream_GetCount(BlockChainStream* This) 06099 { 06100 return This->numBlocks; 06101 } 06102 06103 /****************************************************************************** 06104 * BlockChainStream_ReadAt 06105 * 06106 * Reads a specified number of bytes from this chain at the specified offset. 06107 * bytesRead may be NULL. 06108 * Failure will be returned if the specified number of bytes has not been read. 06109 */ 06110 HRESULT BlockChainStream_ReadAt(BlockChainStream* This, 06111 ULARGE_INTEGER offset, 06112 ULONG size, 06113 void* buffer, 06114 ULONG* bytesRead) 06115 { 06116 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize; 06117 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize; 06118 ULONG bytesToReadInBuffer; 06119 ULONG blockIndex; 06120 BYTE* bufferWalker; 06121 ULARGE_INTEGER stream_size; 06122 HRESULT hr; 06123 BlockChainBlock *cachedBlock; 06124 06125 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); 06126 06127 /* 06128 * Find the first block in the stream that contains part of the buffer. 06129 */ 06130 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); 06131 06132 *bytesRead = 0; 06133 06134 stream_size = BlockChainStream_GetSize(This); 06135 if (stream_size.QuadPart > offset.QuadPart) 06136 size = min(stream_size.QuadPart - offset.QuadPart, size); 06137 else 06138 return S_OK; 06139 06140 /* 06141 * Start reading the buffer. 06142 */ 06143 bufferWalker = buffer; 06144 06145 while (size > 0) 06146 { 06147 ULARGE_INTEGER ulOffset; 06148 DWORD bytesReadAt; 06149 06150 /* 06151 * Calculate how many bytes we can copy from this big block. 06152 */ 06153 bytesToReadInBuffer = 06154 min(This->parentStorage->bigBlockSize - offsetInBlock, size); 06155 06156 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer); 06157 06158 if (FAILED(hr)) 06159 return hr; 06160 06161 if (!cachedBlock) 06162 { 06163 /* Not in cache, and we're going to read past the end of the block. */ 06164 ulOffset.u.HighPart = 0; 06165 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + 06166 offsetInBlock; 06167 06168 StorageImpl_ReadAt(This->parentStorage, 06169 ulOffset, 06170 bufferWalker, 06171 bytesToReadInBuffer, 06172 &bytesReadAt); 06173 } 06174 else 06175 { 06176 if (!cachedBlock->read) 06177 { 06178 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data)) 06179 return STG_E_READFAULT; 06180 06181 cachedBlock->read = 1; 06182 } 06183 06184 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer); 06185 bytesReadAt = bytesToReadInBuffer; 06186 } 06187 06188 blockNoInSequence++; 06189 bufferWalker += bytesReadAt; 06190 size -= bytesReadAt; 06191 *bytesRead += bytesReadAt; 06192 offsetInBlock = 0; /* There is no offset on the next block */ 06193 06194 if (bytesToReadInBuffer != bytesReadAt) 06195 break; 06196 } 06197 06198 return S_OK; 06199 } 06200 06201 /****************************************************************************** 06202 * BlockChainStream_WriteAt 06203 * 06204 * Writes the specified number of bytes to this chain at the specified offset. 06205 * Will fail if not all specified number of bytes have been written. 06206 */ 06207 HRESULT BlockChainStream_WriteAt(BlockChainStream* This, 06208 ULARGE_INTEGER offset, 06209 ULONG size, 06210 const void* buffer, 06211 ULONG* bytesWritten) 06212 { 06213 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize; 06214 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize; 06215 ULONG bytesToWrite; 06216 ULONG blockIndex; 06217 const BYTE* bufferWalker; 06218 HRESULT hr; 06219 BlockChainBlock *cachedBlock; 06220 06221 *bytesWritten = 0; 06222 bufferWalker = buffer; 06223 06224 while (size > 0) 06225 { 06226 ULARGE_INTEGER ulOffset; 06227 DWORD bytesWrittenAt; 06228 06229 /* 06230 * Calculate how many bytes we can copy to this big block. 06231 */ 06232 bytesToWrite = 06233 min(This->parentStorage->bigBlockSize - offsetInBlock, size); 06234 06235 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite); 06236 06237 /* BlockChainStream_SetSize should have already been called to ensure we have 06238 * enough blocks in the chain to write into */ 06239 if (FAILED(hr)) 06240 { 06241 ERR("not enough blocks in chain to write data\n"); 06242 return hr; 06243 } 06244 06245 if (!cachedBlock) 06246 { 06247 /* Not in cache, and we're going to write past the end of the block. */ 06248 ulOffset.u.HighPart = 0; 06249 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + 06250 offsetInBlock; 06251 06252 StorageImpl_WriteAt(This->parentStorage, 06253 ulOffset, 06254 bufferWalker, 06255 bytesToWrite, 06256 &bytesWrittenAt); 06257 } 06258 else 06259 { 06260 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize) 06261 { 06262 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data)) 06263 return STG_E_READFAULT; 06264 } 06265 06266 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite); 06267 bytesWrittenAt = bytesToWrite; 06268 cachedBlock->read = 1; 06269 cachedBlock->dirty = 1; 06270 } 06271 06272 blockNoInSequence++; 06273 bufferWalker += bytesWrittenAt; 06274 size -= bytesWrittenAt; 06275 *bytesWritten += bytesWrittenAt; 06276 offsetInBlock = 0; /* There is no offset on the next block */ 06277 06278 if (bytesWrittenAt != bytesToWrite) 06279 break; 06280 } 06281 06282 return (size == 0) ? S_OK : STG_E_WRITEFAULT; 06283 } 06284 06285 /****************************************************************************** 06286 * BlockChainStream_Shrink 06287 * 06288 * Shrinks this chain in the big block depot. 06289 */ 06290 static BOOL BlockChainStream_Shrink(BlockChainStream* This, 06291 ULARGE_INTEGER newSize) 06292 { 06293 ULONG blockIndex; 06294 ULONG numBlocks; 06295 int i; 06296 06297 /* 06298 * Figure out how many blocks are needed to contain the new size 06299 */ 06300 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize; 06301 06302 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0) 06303 numBlocks++; 06304 06305 if (numBlocks) 06306 { 06307 /* 06308 * Go to the new end of chain 06309 */ 06310 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1); 06311 06312 /* Mark the new end of chain */ 06313 StorageImpl_SetNextBlockInChain( 06314 This->parentStorage, 06315 blockIndex, 06316 BLOCK_END_OF_CHAIN); 06317 06318 This->tailIndex = blockIndex; 06319 } 06320 else 06321 { 06322 if (This->headOfStreamPlaceHolder != 0) 06323 { 06324 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN; 06325 } 06326 else 06327 { 06328 DirEntry chainEntry; 06329 assert(This->ownerDirEntry != DIRENTRY_NULL); 06330 06331 StorageImpl_ReadDirEntry( 06332 This->parentStorage, 06333 This->ownerDirEntry, 06334 &chainEntry); 06335 06336 chainEntry.startingBlock = BLOCK_END_OF_CHAIN; 06337 06338 StorageImpl_WriteDirEntry( 06339 This->parentStorage, 06340 This->ownerDirEntry, 06341 &chainEntry); 06342 } 06343 06344 This->tailIndex = BLOCK_END_OF_CHAIN; 06345 } 06346 06347 This->numBlocks = numBlocks; 06348 06349 /* 06350 * Mark the extra blocks as free 06351 */ 06352 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks) 06353 { 06354 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1]; 06355 StorageImpl_FreeBigBlock(This->parentStorage, 06356 last_run->firstSector + last_run->lastOffset - last_run->firstOffset); 06357 if (last_run->lastOffset == last_run->firstOffset) 06358 This->indexCacheLen--; 06359 else 06360 last_run->lastOffset--; 06361 } 06362 06363 /* 06364 * Reset the last accessed block cache. 06365 */ 06366 for (i=0; i<2; i++) 06367 { 06368 if (This->cachedBlocks[i].index >= numBlocks) 06369 { 06370 This->cachedBlocks[i].index = 0xffffffff; 06371 This->cachedBlocks[i].dirty = 0; 06372 } 06373 } 06374 06375 return TRUE; 06376 } 06377 06378 /****************************************************************************** 06379 * BlockChainStream_Enlarge 06380 * 06381 * Grows this chain in the big block depot. 06382 */ 06383 static BOOL BlockChainStream_Enlarge(BlockChainStream* This, 06384 ULARGE_INTEGER newSize) 06385 { 06386 ULONG blockIndex, currentBlock; 06387 ULONG newNumBlocks; 06388 ULONG oldNumBlocks = 0; 06389 06390 blockIndex = BlockChainStream_GetHeadOfChain(This); 06391 06392 /* 06393 * Empty chain. Create the head. 06394 */ 06395 if (blockIndex == BLOCK_END_OF_CHAIN) 06396 { 06397 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage); 06398 StorageImpl_SetNextBlockInChain(This->parentStorage, 06399 blockIndex, 06400 BLOCK_END_OF_CHAIN); 06401 06402 if (This->headOfStreamPlaceHolder != 0) 06403 { 06404 *(This->headOfStreamPlaceHolder) = blockIndex; 06405 } 06406 else 06407 { 06408 DirEntry chainEntry; 06409 assert(This->ownerDirEntry != DIRENTRY_NULL); 06410 06411 StorageImpl_ReadDirEntry( 06412 This->parentStorage, 06413 This->ownerDirEntry, 06414 &chainEntry); 06415 06416 chainEntry.startingBlock = blockIndex; 06417 06418 StorageImpl_WriteDirEntry( 06419 This->parentStorage, 06420 This->ownerDirEntry, 06421 &chainEntry); 06422 } 06423 06424 This->tailIndex = blockIndex; 06425 This->numBlocks = 1; 06426 } 06427 06428 /* 06429 * Figure out how many blocks are needed to contain this stream 06430 */ 06431 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize; 06432 06433 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0) 06434 newNumBlocks++; 06435 06436 /* 06437 * Go to the current end of chain 06438 */ 06439 if (This->tailIndex == BLOCK_END_OF_CHAIN) 06440 { 06441 currentBlock = blockIndex; 06442 06443 while (blockIndex != BLOCK_END_OF_CHAIN) 06444 { 06445 This->numBlocks++; 06446 currentBlock = blockIndex; 06447 06448 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock, 06449 &blockIndex))) 06450 return FALSE; 06451 } 06452 06453 This->tailIndex = currentBlock; 06454 } 06455 06456 currentBlock = This->tailIndex; 06457 oldNumBlocks = This->numBlocks; 06458 06459 /* 06460 * Add new blocks to the chain 06461 */ 06462 if (oldNumBlocks < newNumBlocks) 06463 { 06464 while (oldNumBlocks < newNumBlocks) 06465 { 06466 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage); 06467 06468 StorageImpl_SetNextBlockInChain( 06469 This->parentStorage, 06470 currentBlock, 06471 blockIndex); 06472 06473 StorageImpl_SetNextBlockInChain( 06474 This->parentStorage, 06475 blockIndex, 06476 BLOCK_END_OF_CHAIN); 06477 06478 currentBlock = blockIndex; 06479 oldNumBlocks++; 06480 } 06481 06482 This->tailIndex = blockIndex; 06483 This->numBlocks = newNumBlocks; 06484 } 06485 06486 if (FAILED(BlockChainStream_UpdateIndexCache(This))) 06487 return FALSE; 06488 06489 return TRUE; 06490 } 06491 06492 /****************************************************************************** 06493 * BlockChainStream_SetSize 06494 * 06495 * Sets the size of this stream. The big block depot will be updated. 06496 * The file will grow if we grow the chain. 06497 * 06498 * TODO: Free the actual blocks in the file when we shrink the chain. 06499 * Currently, the blocks are still in the file. So the file size 06500 * doesn't shrink even if we shrink streams. 06501 */ 06502 BOOL BlockChainStream_SetSize( 06503 BlockChainStream* This, 06504 ULARGE_INTEGER newSize) 06505 { 06506 ULARGE_INTEGER size = BlockChainStream_GetSize(This); 06507 06508 if (newSize.u.LowPart == size.u.LowPart) 06509 return TRUE; 06510 06511 if (newSize.u.LowPart < size.u.LowPart) 06512 { 06513 BlockChainStream_Shrink(This, newSize); 06514 } 06515 else 06516 { 06517 BlockChainStream_Enlarge(This, newSize); 06518 } 06519 06520 return TRUE; 06521 } 06522 06523 /****************************************************************************** 06524 * BlockChainStream_GetSize 06525 * 06526 * Returns the size of this chain. 06527 * Will return the block count if this chain doesn't have a directory entry. 06528 */ 06529 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This) 06530 { 06531 DirEntry chainEntry; 06532 06533 if(This->headOfStreamPlaceHolder == NULL) 06534 { 06535 /* 06536 * This chain has a directory entry so use the size value from there. 06537 */ 06538 StorageImpl_ReadDirEntry( 06539 This->parentStorage, 06540 This->ownerDirEntry, 06541 &chainEntry); 06542 06543 return chainEntry.size; 06544 } 06545 else 06546 { 06547 /* 06548 * this chain is a chain that does not have a directory entry, figure out the 06549 * size by making the product number of used blocks times the 06550 * size of them 06551 */ 06552 ULARGE_INTEGER result; 06553 result.u.HighPart = 0; 06554 06555 result.u.LowPart = 06556 BlockChainStream_GetCount(This) * 06557 This->parentStorage->bigBlockSize; 06558 06559 return result; 06560 } 06561 } 06562 06563 /****************************************************************************** 06564 ** SmallBlockChainStream implementation 06565 */ 06566 06567 SmallBlockChainStream* SmallBlockChainStream_Construct( 06568 StorageImpl* parentStorage, 06569 ULONG* headOfStreamPlaceHolder, 06570 DirRef dirEntry) 06571 { 06572 SmallBlockChainStream* newStream; 06573 06574 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream)); 06575 06576 newStream->parentStorage = parentStorage; 06577 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; 06578 newStream->ownerDirEntry = dirEntry; 06579 06580 return newStream; 06581 } 06582 06583 void SmallBlockChainStream_Destroy( 06584 SmallBlockChainStream* This) 06585 { 06586 HeapFree(GetProcessHeap(), 0, This); 06587 } 06588 06589 /****************************************************************************** 06590 * SmallBlockChainStream_GetHeadOfChain 06591 * 06592 * Returns the head of this chain of small blocks. 06593 */ 06594 static ULONG SmallBlockChainStream_GetHeadOfChain( 06595 SmallBlockChainStream* This) 06596 { 06597 DirEntry chainEntry; 06598 HRESULT hr; 06599 06600 if (This->headOfStreamPlaceHolder != NULL) 06601 return *(This->headOfStreamPlaceHolder); 06602 06603 if (This->ownerDirEntry) 06604 { 06605 hr = StorageImpl_ReadDirEntry( 06606 This->parentStorage, 06607 This->ownerDirEntry, 06608 &chainEntry); 06609 06610 if (SUCCEEDED(hr)) 06611 { 06612 return chainEntry.startingBlock; 06613 } 06614 06615 } 06616 06617 return BLOCK_END_OF_CHAIN; 06618 } 06619 06620 /****************************************************************************** 06621 * SmallBlockChainStream_GetNextBlockInChain 06622 * 06623 * Returns the index of the next small block in this chain. 06624 * 06625 * Return Values: 06626 * - BLOCK_END_OF_CHAIN: end of this chain 06627 * - BLOCK_UNUSED: small block 'blockIndex' is free 06628 */ 06629 static HRESULT SmallBlockChainStream_GetNextBlockInChain( 06630 SmallBlockChainStream* This, 06631 ULONG blockIndex, 06632 ULONG* nextBlockInChain) 06633 { 06634 ULARGE_INTEGER offsetOfBlockInDepot; 06635 DWORD buffer; 06636 ULONG bytesRead; 06637 HRESULT res; 06638 06639 *nextBlockInChain = BLOCK_END_OF_CHAIN; 06640 06641 offsetOfBlockInDepot.u.HighPart = 0; 06642 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG); 06643 06644 /* 06645 * Read those bytes in the buffer from the small block file. 06646 */ 06647 res = BlockChainStream_ReadAt( 06648 This->parentStorage->smallBlockDepotChain, 06649 offsetOfBlockInDepot, 06650 sizeof(DWORD), 06651 &buffer, 06652 &bytesRead); 06653 06654 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD)) 06655 res = STG_E_READFAULT; 06656 06657 if (SUCCEEDED(res)) 06658 { 06659 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain); 06660 return S_OK; 06661 } 06662 06663 return res; 06664 } 06665 06666 /****************************************************************************** 06667 * SmallBlockChainStream_SetNextBlockInChain 06668 * 06669 * Writes the index of the next block of the specified block in the small 06670 * block depot. 06671 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock. 06672 * To flag a block as free use BLOCK_UNUSED as nextBlock. 06673 */ 06674 static void SmallBlockChainStream_SetNextBlockInChain( 06675 SmallBlockChainStream* This, 06676 ULONG blockIndex, 06677 ULONG nextBlock) 06678 { 06679 ULARGE_INTEGER offsetOfBlockInDepot; 06680 DWORD buffer; 06681 ULONG bytesWritten; 06682 06683 offsetOfBlockInDepot.u.HighPart = 0; 06684 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG); 06685 06686 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock); 06687 06688 /* 06689 * Read those bytes in the buffer from the small block file. 06690 */ 06691 BlockChainStream_WriteAt( 06692 This->parentStorage->smallBlockDepotChain, 06693 offsetOfBlockInDepot, 06694 sizeof(DWORD), 06695 &buffer, 06696 &bytesWritten); 06697 } 06698 06699 /****************************************************************************** 06700 * SmallBlockChainStream_FreeBlock 06701 * 06702 * Flag small block 'blockIndex' as free in the small block depot. 06703 */ 06704 static void SmallBlockChainStream_FreeBlock( 06705 SmallBlockChainStream* This, 06706 ULONG blockIndex) 06707 { 06708 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); 06709 } 06710 06711 /****************************************************************************** 06712 * SmallBlockChainStream_GetNextFreeBlock 06713 * 06714 * Returns the index of a free small block. The small block depot will be 06715 * enlarged if necessary. The small block chain will also be enlarged if 06716 * necessary. 06717 */ 06718 static ULONG SmallBlockChainStream_GetNextFreeBlock( 06719 SmallBlockChainStream* This) 06720 { 06721 ULARGE_INTEGER offsetOfBlockInDepot; 06722 DWORD buffer; 06723 ULONG bytesRead; 06724 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock; 06725 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN; 06726 HRESULT res = S_OK; 06727 ULONG smallBlocksPerBigBlock; 06728 DirEntry rootEntry; 06729 ULONG blocksRequired; 06730 ULARGE_INTEGER old_size, size_required; 06731 06732 offsetOfBlockInDepot.u.HighPart = 0; 06733 06734 /* 06735 * Scan the small block depot for a free block 06736 */ 06737 while (nextBlockIndex != BLOCK_UNUSED) 06738 { 06739 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG); 06740 06741 res = BlockChainStream_ReadAt( 06742 This->parentStorage->smallBlockDepotChain, 06743 offsetOfBlockInDepot, 06744 sizeof(DWORD), 06745 &buffer, 06746 &bytesRead); 06747 06748 /* 06749 * If we run out of space for the small block depot, enlarge it 06750 */ 06751 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD)) 06752 { 06753 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex); 06754 06755 if (nextBlockIndex != BLOCK_UNUSED) 06756 blockIndex++; 06757 } 06758 else 06759 { 06760 ULONG count = 06761 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain); 06762 06763 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE]; 06764 ULARGE_INTEGER newSize, offset; 06765 ULONG bytesWritten; 06766 06767 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize; 06768 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize); 06769 06770 /* 06771 * Initialize all the small blocks to free 06772 */ 06773 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize); 06774 offset.QuadPart = count * This->parentStorage->bigBlockSize; 06775 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain, 06776 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten); 06777 06778 StorageImpl_SaveFileHeader(This->parentStorage); 06779 } 06780 } 06781 06782 This->parentStorage->firstFreeSmallBlock = blockIndex+1; 06783 06784 smallBlocksPerBigBlock = 06785 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize; 06786 06787 /* 06788 * Verify if we have to allocate big blocks to contain small blocks 06789 */ 06790 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; 06791 06792 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize; 06793 06794 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain); 06795 06796 if (size_required.QuadPart > old_size.QuadPart) 06797 { 06798 BlockChainStream_SetSize( 06799 This->parentStorage->smallBlockRootChain, 06800 size_required); 06801 06802 StorageImpl_ReadDirEntry( 06803 This->parentStorage, 06804 This->parentStorage->base.storageDirEntry, 06805 &rootEntry); 06806 06807 rootEntry.size = size_required; 06808 06809 StorageImpl_WriteDirEntry( 06810 This->parentStorage, 06811 This->parentStorage->base.storageDirEntry, 06812 &rootEntry); 06813 } 06814 06815 return blockIndex; 06816 } 06817 06818 /****************************************************************************** 06819 * SmallBlockChainStream_ReadAt 06820 * 06821 * Reads a specified number of bytes from this chain at the specified offset. 06822 * bytesRead may be NULL. 06823 * Failure will be returned if the specified number of bytes has not been read. 06824 */ 06825 HRESULT SmallBlockChainStream_ReadAt( 06826 SmallBlockChainStream* This, 06827 ULARGE_INTEGER offset, 06828 ULONG size, 06829 void* buffer, 06830 ULONG* bytesRead) 06831 { 06832 HRESULT rc = S_OK; 06833 ULARGE_INTEGER offsetInBigBlockFile; 06834 ULONG blockNoInSequence = 06835 offset.u.LowPart / This->parentStorage->smallBlockSize; 06836 06837 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize; 06838 ULONG bytesToReadInBuffer; 06839 ULONG blockIndex; 06840 ULONG bytesReadFromBigBlockFile; 06841 BYTE* bufferWalker; 06842 ULARGE_INTEGER stream_size; 06843 06844 /* 06845 * This should never happen on a small block file. 06846 */ 06847 assert(offset.u.HighPart==0); 06848 06849 *bytesRead = 0; 06850 06851 stream_size = SmallBlockChainStream_GetSize(This); 06852 if (stream_size.QuadPart > offset.QuadPart) 06853 size = min(stream_size.QuadPart - offset.QuadPart, size); 06854 else 06855 return S_OK; 06856 06857 /* 06858 * Find the first block in the stream that contains part of the buffer. 06859 */ 06860 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 06861 06862 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) 06863 { 06864 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); 06865 if(FAILED(rc)) 06866 return rc; 06867 blockNoInSequence--; 06868 } 06869 06870 /* 06871 * Start reading the buffer. 06872 */ 06873 bufferWalker = buffer; 06874 06875 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) 06876 { 06877 /* 06878 * Calculate how many bytes we can copy from this small block. 06879 */ 06880 bytesToReadInBuffer = 06881 min(This->parentStorage->smallBlockSize - offsetInBlock, size); 06882 06883 /* 06884 * Calculate the offset of the small block in the small block file. 06885 */ 06886 offsetInBigBlockFile.u.HighPart = 0; 06887 offsetInBigBlockFile.u.LowPart = 06888 blockIndex * This->parentStorage->smallBlockSize; 06889 06890 offsetInBigBlockFile.u.LowPart += offsetInBlock; 06891 06892 /* 06893 * Read those bytes in the buffer from the small block file. 06894 * The small block has already been identified so it shouldn't fail 06895 * unless the file is corrupt. 06896 */ 06897 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain, 06898 offsetInBigBlockFile, 06899 bytesToReadInBuffer, 06900 bufferWalker, 06901 &bytesReadFromBigBlockFile); 06902 06903 if (FAILED(rc)) 06904 return rc; 06905 06906 if (!bytesReadFromBigBlockFile) 06907 return STG_E_DOCFILECORRUPT; 06908 06909 /* 06910 * Step to the next big block. 06911 */ 06912 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); 06913 if(FAILED(rc)) 06914 return STG_E_DOCFILECORRUPT; 06915 06916 bufferWalker += bytesReadFromBigBlockFile; 06917 size -= bytesReadFromBigBlockFile; 06918 *bytesRead += bytesReadFromBigBlockFile; 06919 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize; 06920 } 06921 06922 return S_OK; 06923 } 06924 06925 /****************************************************************************** 06926 * SmallBlockChainStream_WriteAt 06927 * 06928 * Writes the specified number of bytes to this chain at the specified offset. 06929 * Will fail if not all specified number of bytes have been written. 06930 */ 06931 HRESULT SmallBlockChainStream_WriteAt( 06932 SmallBlockChainStream* This, 06933 ULARGE_INTEGER offset, 06934 ULONG size, 06935 const void* buffer, 06936 ULONG* bytesWritten) 06937 { 06938 ULARGE_INTEGER offsetInBigBlockFile; 06939 ULONG blockNoInSequence = 06940 offset.u.LowPart / This->parentStorage->smallBlockSize; 06941 06942 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize; 06943 ULONG bytesToWriteInBuffer; 06944 ULONG blockIndex; 06945 ULONG bytesWrittenToBigBlockFile; 06946 const BYTE* bufferWalker; 06947 HRESULT res; 06948 06949 /* 06950 * This should never happen on a small block file. 06951 */ 06952 assert(offset.u.HighPart==0); 06953 06954 /* 06955 * Find the first block in the stream that contains part of the buffer. 06956 */ 06957 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 06958 06959 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) 06960 { 06961 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex))) 06962 return STG_E_DOCFILECORRUPT; 06963 blockNoInSequence--; 06964 } 06965 06966 /* 06967 * Start writing the buffer. 06968 */ 06969 *bytesWritten = 0; 06970 bufferWalker = buffer; 06971 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) 06972 { 06973 /* 06974 * Calculate how many bytes we can copy to this small block. 06975 */ 06976 bytesToWriteInBuffer = 06977 min(This->parentStorage->smallBlockSize - offsetInBlock, size); 06978 06979 /* 06980 * Calculate the offset of the small block in the small block file. 06981 */ 06982 offsetInBigBlockFile.u.HighPart = 0; 06983 offsetInBigBlockFile.u.LowPart = 06984 blockIndex * This->parentStorage->smallBlockSize; 06985 06986 offsetInBigBlockFile.u.LowPart += offsetInBlock; 06987 06988 /* 06989 * Write those bytes in the buffer to the small block file. 06990 */ 06991 res = BlockChainStream_WriteAt( 06992 This->parentStorage->smallBlockRootChain, 06993 offsetInBigBlockFile, 06994 bytesToWriteInBuffer, 06995 bufferWalker, 06996 &bytesWrittenToBigBlockFile); 06997 if (FAILED(res)) 06998 return res; 06999 07000 /* 07001 * Step to the next big block. 07002 */ 07003 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, 07004 &blockIndex))) 07005 return FALSE; 07006 bufferWalker += bytesWrittenToBigBlockFile; 07007 size -= bytesWrittenToBigBlockFile; 07008 *bytesWritten += bytesWrittenToBigBlockFile; 07009 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize; 07010 } 07011 07012 return (size == 0) ? S_OK : STG_E_WRITEFAULT; 07013 } 07014 07015 /****************************************************************************** 07016 * SmallBlockChainStream_Shrink 07017 * 07018 * Shrinks this chain in the small block depot. 07019 */ 07020 static BOOL SmallBlockChainStream_Shrink( 07021 SmallBlockChainStream* This, 07022 ULARGE_INTEGER newSize) 07023 { 07024 ULONG blockIndex, extraBlock; 07025 ULONG numBlocks; 07026 ULONG count = 0; 07027 07028 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize; 07029 07030 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0) 07031 numBlocks++; 07032 07033 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 07034 07035 /* 07036 * Go to the new end of chain 07037 */ 07038 while (count < numBlocks) 07039 { 07040 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, 07041 &blockIndex))) 07042 return FALSE; 07043 count++; 07044 } 07045 07046 /* 07047 * If the count is 0, we have a special case, the head of the chain was 07048 * just freed. 07049 */ 07050 if (count == 0) 07051 { 07052 DirEntry chainEntry; 07053 07054 StorageImpl_ReadDirEntry(This->parentStorage, 07055 This->ownerDirEntry, 07056 &chainEntry); 07057 07058 chainEntry.startingBlock = BLOCK_END_OF_CHAIN; 07059 07060 StorageImpl_WriteDirEntry(This->parentStorage, 07061 This->ownerDirEntry, 07062 &chainEntry); 07063 07064 /* 07065 * We start freeing the chain at the head block. 07066 */ 07067 extraBlock = blockIndex; 07068 } 07069 else 07070 { 07071 /* Get the next block before marking the new end */ 07072 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, 07073 &extraBlock))) 07074 return FALSE; 07075 07076 /* Mark the new end of chain */ 07077 SmallBlockChainStream_SetNextBlockInChain( 07078 This, 07079 blockIndex, 07080 BLOCK_END_OF_CHAIN); 07081 } 07082 07083 /* 07084 * Mark the extra blocks as free 07085 */ 07086 while (extraBlock != BLOCK_END_OF_CHAIN) 07087 { 07088 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock, 07089 &blockIndex))) 07090 return FALSE; 07091 SmallBlockChainStream_FreeBlock(This, extraBlock); 07092 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock); 07093 extraBlock = blockIndex; 07094 } 07095 07096 return TRUE; 07097 } 07098 07099 /****************************************************************************** 07100 * SmallBlockChainStream_Enlarge 07101 * 07102 * Grows this chain in the small block depot. 07103 */ 07104 static BOOL SmallBlockChainStream_Enlarge( 07105 SmallBlockChainStream* This, 07106 ULARGE_INTEGER newSize) 07107 { 07108 ULONG blockIndex, currentBlock; 07109 ULONG newNumBlocks; 07110 ULONG oldNumBlocks = 0; 07111 07112 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 07113 07114 /* 07115 * Empty chain. Create the head. 07116 */ 07117 if (blockIndex == BLOCK_END_OF_CHAIN) 07118 { 07119 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); 07120 SmallBlockChainStream_SetNextBlockInChain( 07121 This, 07122 blockIndex, 07123 BLOCK_END_OF_CHAIN); 07124 07125 if (This->headOfStreamPlaceHolder != NULL) 07126 { 07127 *(This->headOfStreamPlaceHolder) = blockIndex; 07128 } 07129 else 07130 { 07131 DirEntry chainEntry; 07132 07133 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry, 07134 &chainEntry); 07135 07136 chainEntry.startingBlock = blockIndex; 07137 07138 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry, 07139 &chainEntry); 07140 } 07141 } 07142 07143 currentBlock = blockIndex; 07144 07145 /* 07146 * Figure out how many blocks are needed to contain this stream 07147 */ 07148 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize; 07149 07150 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0) 07151 newNumBlocks++; 07152 07153 /* 07154 * Go to the current end of chain 07155 */ 07156 while (blockIndex != BLOCK_END_OF_CHAIN) 07157 { 07158 oldNumBlocks++; 07159 currentBlock = blockIndex; 07160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex))) 07161 return FALSE; 07162 } 07163 07164 /* 07165 * Add new blocks to the chain 07166 */ 07167 while (oldNumBlocks < newNumBlocks) 07168 { 07169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); 07170 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex); 07171 07172 SmallBlockChainStream_SetNextBlockInChain( 07173 This, 07174 blockIndex, 07175 BLOCK_END_OF_CHAIN); 07176 07177 currentBlock = blockIndex; 07178 oldNumBlocks++; 07179 } 07180 07181 return TRUE; 07182 } 07183 07184 /****************************************************************************** 07185 * SmallBlockChainStream_SetSize 07186 * 07187 * Sets the size of this stream. 07188 * The file will grow if we grow the chain. 07189 * 07190 * TODO: Free the actual blocks in the file when we shrink the chain. 07191 * Currently, the blocks are still in the file. So the file size 07192 * doesn't shrink even if we shrink streams. 07193 */ 07194 BOOL SmallBlockChainStream_SetSize( 07195 SmallBlockChainStream* This, 07196 ULARGE_INTEGER newSize) 07197 { 07198 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This); 07199 07200 if (newSize.u.LowPart == size.u.LowPart) 07201 return TRUE; 07202 07203 if (newSize.u.LowPart < size.u.LowPart) 07204 { 07205 SmallBlockChainStream_Shrink(This, newSize); 07206 } 07207 else 07208 { 07209 SmallBlockChainStream_Enlarge(This, newSize); 07210 } 07211 07212 return TRUE; 07213 } 07214 07215 /****************************************************************************** 07216 * SmallBlockChainStream_GetCount 07217 * 07218 * Returns the number of small blocks that comprises this chain. 07219 * This is not the size of the stream as the last block may not be full! 07220 * 07221 */ 07222 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This) 07223 { 07224 ULONG blockIndex; 07225 ULONG count = 0; 07226 07227 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 07228 07229 while(blockIndex != BLOCK_END_OF_CHAIN) 07230 { 07231 count++; 07232 07233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, 07234 blockIndex, &blockIndex))) 07235 return 0; 07236 } 07237 07238 return count; 07239 } 07240 07241 /****************************************************************************** 07242 * SmallBlockChainStream_GetSize 07243 * 07244 * Returns the size of this chain. 07245 */ 07246 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This) 07247 { 07248 DirEntry chainEntry; 07249 07250 if(This->headOfStreamPlaceHolder != NULL) 07251 { 07252 ULARGE_INTEGER result; 07253 result.u.HighPart = 0; 07254 07255 result.u.LowPart = SmallBlockChainStream_GetCount(This) * 07256 This->parentStorage->smallBlockSize; 07257 07258 return result; 07259 } 07260 07261 StorageImpl_ReadDirEntry( 07262 This->parentStorage, 07263 This->ownerDirEntry, 07264 &chainEntry); 07265 07266 return chainEntry.size; 07267 } 07268 07269 static HRESULT create_storagefile( 07270 LPCOLESTR pwcsName, 07271 DWORD grfMode, 07272 DWORD grfAttrs, 07273 STGOPTIONS* pStgOptions, 07274 REFIID riid, 07275 void** ppstgOpen) 07276 { 07277 StorageBaseImpl* newStorage = 0; 07278 HANDLE hFile = INVALID_HANDLE_VALUE; 07279 HRESULT hr = STG_E_INVALIDFLAG; 07280 DWORD shareMode; 07281 DWORD accessMode; 07282 DWORD creationMode; 07283 DWORD fileAttributes; 07284 WCHAR tempFileName[MAX_PATH]; 07285 07286 if (ppstgOpen == 0) 07287 return STG_E_INVALIDPOINTER; 07288 07289 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE) 07290 return STG_E_INVALIDPARAMETER; 07291 07292 /* if no share mode given then DENY_NONE is the default */ 07293 if (STGM_SHARE_MODE(grfMode) == 0) 07294 grfMode |= STGM_SHARE_DENY_NONE; 07295 07296 if ( FAILED( validateSTGM(grfMode) )) 07297 goto end; 07298 07299 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */ 07300 switch(STGM_ACCESS_MODE(grfMode)) 07301 { 07302 case STGM_WRITE: 07303 case STGM_READWRITE: 07304 break; 07305 default: 07306 goto end; 07307 } 07308 07309 /* in direct mode, can only use SHARE_EXCLUSIVE */ 07310 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)) 07311 goto end; 07312 07313 /* but in transacted mode, any share mode is valid */ 07314 07315 /* 07316 * Generate a unique name. 07317 */ 07318 if (pwcsName == 0) 07319 { 07320 WCHAR tempPath[MAX_PATH]; 07321 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 }; 07322 07323 memset(tempPath, 0, sizeof(tempPath)); 07324 memset(tempFileName, 0, sizeof(tempFileName)); 07325 07326 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 ) 07327 tempPath[0] = '.'; 07328 07329 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0) 07330 pwcsName = tempFileName; 07331 else 07332 { 07333 hr = STG_E_INSUFFICIENTMEMORY; 07334 goto end; 07335 } 07336 07337 creationMode = TRUNCATE_EXISTING; 07338 } 07339 else 07340 { 07341 creationMode = GetCreationModeFromSTGM(grfMode); 07342 } 07343 07344 /* 07345 * Interpret the STGM value grfMode 07346 */ 07347 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 07348 accessMode = GetAccessModeFromSTGM(grfMode); 07349 07350 if (grfMode & STGM_DELETEONRELEASE) 07351 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE; 07352 else 07353 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; 07354 07355 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE)) 07356 { 07357 static int fixme; 07358 if (!fixme++) 07359 FIXME("Storage share mode not implemented.\n"); 07360 } 07361 07362 *ppstgOpen = 0; 07363 07364 hFile = CreateFileW(pwcsName, 07365 accessMode, 07366 shareMode, 07367 NULL, 07368 creationMode, 07369 fileAttributes, 07370 0); 07371 07372 if (hFile == INVALID_HANDLE_VALUE) 07373 { 07374 if(GetLastError() == ERROR_FILE_EXISTS) 07375 hr = STG_E_FILEALREADYEXISTS; 07376 else 07377 hr = E_FAIL; 07378 goto end; 07379 } 07380 07381 /* 07382 * Allocate and initialize the new IStorage32object. 07383 */ 07384 hr = Storage_Construct( 07385 hFile, 07386 pwcsName, 07387 NULL, 07388 grfMode, 07389 TRUE, 07390 TRUE, 07391 pStgOptions->ulSectorSize, 07392 &newStorage); 07393 07394 if (FAILED(hr)) 07395 { 07396 goto end; 07397 } 07398 07399 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen); 07400 07401 IStorage_Release((IStorage*)newStorage); 07402 07403 end: 07404 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr); 07405 07406 return hr; 07407 } 07408 07409 /****************************************************************************** 07410 * StgCreateDocfile [OLE32.@] 07411 * Creates a new compound file storage object 07412 * 07413 * PARAMS 07414 * pwcsName [ I] Unicode string with filename (can be relative or NULL) 07415 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants) 07416 * reserved [ ?] unused?, usually 0 07417 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject 07418 * 07419 * RETURNS 07420 * S_OK if the file was successfully created 07421 * some STG_E_ value if error 07422 * NOTES 07423 * if pwcsName is NULL, create file with new unique name 07424 * the function can returns 07425 * STG_S_CONVERTED if the specified file was successfully converted to storage format 07426 * (unrealized now) 07427 */ 07428 HRESULT WINAPI StgCreateDocfile( 07429 LPCOLESTR pwcsName, 07430 DWORD grfMode, 07431 DWORD reserved, 07432 IStorage **ppstgOpen) 07433 { 07434 STGOPTIONS stgoptions = {1, 0, 512}; 07435 07436 TRACE("(%s, %x, %d, %p)\n", 07437 debugstr_w(pwcsName), grfMode, 07438 reserved, ppstgOpen); 07439 07440 if (ppstgOpen == 0) 07441 return STG_E_INVALIDPOINTER; 07442 if (reserved != 0) 07443 return STG_E_INVALIDPARAMETER; 07444 07445 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen); 07446 } 07447 07448 /****************************************************************************** 07449 * StgCreateStorageEx [OLE32.@] 07450 */ 07451 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) 07452 { 07453 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName), 07454 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); 07455 07456 if (stgfmt != STGFMT_FILE && grfAttrs != 0) 07457 { 07458 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n"); 07459 return STG_E_INVALIDPARAMETER; 07460 } 07461 07462 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING) 07463 { 07464 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n"); 07465 return STG_E_INVALIDPARAMETER; 07466 } 07467 07468 if (stgfmt == STGFMT_FILE) 07469 { 07470 ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); 07471 return STG_E_INVALIDPARAMETER; 07472 } 07473 07474 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE) 07475 { 07476 STGOPTIONS defaultOptions = {1, 0, 512}; 07477 07478 if (!pStgOptions) pStgOptions = &defaultOptions; 07479 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen); 07480 } 07481 07482 07483 ERR("Invalid stgfmt argument\n"); 07484 return STG_E_INVALIDPARAMETER; 07485 } 07486 07487 /****************************************************************************** 07488 * StgCreatePropSetStg [OLE32.@] 07489 */ 07490 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved, 07491 IPropertySetStorage **ppPropSetStg) 07492 { 07493 HRESULT hr; 07494 07495 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg); 07496 if (reserved) 07497 hr = STG_E_INVALIDPARAMETER; 07498 else 07499 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage, 07500 (void**)ppPropSetStg); 07501 return hr; 07502 } 07503 07504 /****************************************************************************** 07505 * StgOpenStorageEx [OLE32.@] 07506 */ 07507 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) 07508 { 07509 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName), 07510 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); 07511 07512 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0) 07513 { 07514 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n"); 07515 return STG_E_INVALIDPARAMETER; 07516 } 07517 07518 switch (stgfmt) 07519 { 07520 case STGFMT_FILE: 07521 ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); 07522 return STG_E_INVALIDPARAMETER; 07523 07524 case STGFMT_STORAGE: 07525 break; 07526 07527 case STGFMT_DOCFILE: 07528 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING) 07529 { 07530 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n"); 07531 return STG_E_INVALIDPARAMETER; 07532 } 07533 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n"); 07534 break; 07535 07536 case STGFMT_ANY: 07537 WARN("STGFMT_ANY assuming storage\n"); 07538 break; 07539 07540 default: 07541 return STG_E_INVALIDPARAMETER; 07542 } 07543 07544 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen); 07545 } 07546 07547 07548 /****************************************************************************** 07549 * StgOpenStorage [OLE32.@] 07550 */ 07551 HRESULT WINAPI StgOpenStorage( 07552 const OLECHAR *pwcsName, 07553 IStorage *pstgPriority, 07554 DWORD grfMode, 07555 SNB snbExclude, 07556 DWORD reserved, 07557 IStorage **ppstgOpen) 07558 { 07559 StorageBaseImpl* newStorage = 0; 07560 HRESULT hr = S_OK; 07561 HANDLE hFile = 0; 07562 DWORD shareMode; 07563 DWORD accessMode; 07564 07565 TRACE("(%s, %p, %x, %p, %d, %p)\n", 07566 debugstr_w(pwcsName), pstgPriority, grfMode, 07567 snbExclude, reserved, ppstgOpen); 07568 07569 if (pwcsName == 0) 07570 { 07571 hr = STG_E_INVALIDNAME; 07572 goto end; 07573 } 07574 07575 if (ppstgOpen == 0) 07576 { 07577 hr = STG_E_INVALIDPOINTER; 07578 goto end; 07579 } 07580 07581 if (reserved) 07582 { 07583 hr = STG_E_INVALIDPARAMETER; 07584 goto end; 07585 } 07586 07587 if (grfMode & STGM_PRIORITY) 07588 { 07589 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT)) 07590 return STG_E_INVALIDFLAG; 07591 if (grfMode & STGM_DELETEONRELEASE) 07592 return STG_E_INVALIDFUNCTION; 07593 if(STGM_ACCESS_MODE(grfMode) != STGM_READ) 07594 return STG_E_INVALIDFLAG; 07595 grfMode &= ~0xf0; /* remove the existing sharing mode */ 07596 grfMode |= STGM_SHARE_DENY_NONE; 07597 07598 /* STGM_PRIORITY stops other IStorage objects on the same file from 07599 * committing until the STGM_PRIORITY IStorage is closed. it also 07600 * stops non-transacted mode StgOpenStorage calls with write access from 07601 * succeeding. obviously, both of these cannot be achieved through just 07602 * file share flags */ 07603 FIXME("STGM_PRIORITY mode not implemented correctly\n"); 07604 } 07605 07606 /* 07607 * Validate the sharing mode 07608 */ 07609 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY))) 07610 switch(STGM_SHARE_MODE(grfMode)) 07611 { 07612 case STGM_SHARE_EXCLUSIVE: 07613 case STGM_SHARE_DENY_WRITE: 07614 break; 07615 default: 07616 hr = STG_E_INVALIDFLAG; 07617 goto end; 07618 } 07619 07620 if ( FAILED( validateSTGM(grfMode) ) || 07621 (grfMode&STGM_CREATE)) 07622 { 07623 hr = STG_E_INVALIDFLAG; 07624 goto end; 07625 } 07626 07627 /* shared reading requires transacted mode */ 07628 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE && 07629 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE && 07630 !(grfMode&STGM_TRANSACTED) ) 07631 { 07632 hr = STG_E_INVALIDFLAG; 07633 goto end; 07634 } 07635 07636 /* 07637 * Interpret the STGM value grfMode 07638 */ 07639 shareMode = GetShareModeFromSTGM(grfMode); 07640 accessMode = GetAccessModeFromSTGM(grfMode); 07641 07642 *ppstgOpen = 0; 07643 07644 hFile = CreateFileW( pwcsName, 07645 accessMode, 07646 shareMode, 07647 NULL, 07648 OPEN_EXISTING, 07649 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 07650 0); 07651 07652 if (hFile==INVALID_HANDLE_VALUE) 07653 { 07654 DWORD last_error = GetLastError(); 07655 07656 hr = E_FAIL; 07657 07658 switch (last_error) 07659 { 07660 case ERROR_FILE_NOT_FOUND: 07661 hr = STG_E_FILENOTFOUND; 07662 break; 07663 07664 case ERROR_PATH_NOT_FOUND: 07665 hr = STG_E_PATHNOTFOUND; 07666 break; 07667 07668 case ERROR_ACCESS_DENIED: 07669 case ERROR_WRITE_PROTECT: 07670 hr = STG_E_ACCESSDENIED; 07671 break; 07672 07673 case ERROR_SHARING_VIOLATION: 07674 hr = STG_E_SHAREVIOLATION; 07675 break; 07676 07677 default: 07678 hr = E_FAIL; 07679 } 07680 07681 goto end; 07682 } 07683 07684 /* 07685 * Refuse to open the file if it's too small to be a structured storage file 07686 * FIXME: verify the file when reading instead of here 07687 */ 07688 if (GetFileSize(hFile, NULL) < 0x100) 07689 { 07690 CloseHandle(hFile); 07691 hr = STG_E_FILEALREADYEXISTS; 07692 goto end; 07693 } 07694 07695 /* 07696 * Allocate and initialize the new IStorage32object. 07697 */ 07698 hr = Storage_Construct( 07699 hFile, 07700 pwcsName, 07701 NULL, 07702 grfMode, 07703 TRUE, 07704 FALSE, 07705 512, 07706 &newStorage); 07707 07708 if (FAILED(hr)) 07709 { 07710 /* 07711 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS 07712 */ 07713 if(hr == STG_E_INVALIDHEADER) 07714 hr = STG_E_FILEALREADYEXISTS; 07715 goto end; 07716 } 07717 07718 /* 07719 * Get an "out" pointer for the caller. 07720 */ 07721 *ppstgOpen = (IStorage*)newStorage; 07722 07723 end: 07724 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL); 07725 return hr; 07726 } 07727 07728 /****************************************************************************** 07729 * StgCreateDocfileOnILockBytes [OLE32.@] 07730 */ 07731 HRESULT WINAPI StgCreateDocfileOnILockBytes( 07732 ILockBytes *plkbyt, 07733 DWORD grfMode, 07734 DWORD reserved, 07735 IStorage** ppstgOpen) 07736 { 07737 StorageBaseImpl* newStorage = 0; 07738 HRESULT hr = S_OK; 07739 07740 if ((ppstgOpen == 0) || (plkbyt == 0)) 07741 return STG_E_INVALIDPOINTER; 07742 07743 /* 07744 * Allocate and initialize the new IStorage object. 07745 */ 07746 hr = Storage_Construct( 07747 0, 07748 0, 07749 plkbyt, 07750 grfMode, 07751 FALSE, 07752 TRUE, 07753 512, 07754 &newStorage); 07755 07756 if (FAILED(hr)) 07757 { 07758 return hr; 07759 } 07760 07761 /* 07762 * Get an "out" pointer for the caller. 07763 */ 07764 *ppstgOpen = (IStorage*)newStorage; 07765 07766 return hr; 07767 } 07768 07769 /****************************************************************************** 07770 * StgOpenStorageOnILockBytes [OLE32.@] 07771 */ 07772 HRESULT WINAPI StgOpenStorageOnILockBytes( 07773 ILockBytes *plkbyt, 07774 IStorage *pstgPriority, 07775 DWORD grfMode, 07776 SNB snbExclude, 07777 DWORD reserved, 07778 IStorage **ppstgOpen) 07779 { 07780 StorageBaseImpl* newStorage = 0; 07781 HRESULT hr = S_OK; 07782 07783 if ((plkbyt == 0) || (ppstgOpen == 0)) 07784 return STG_E_INVALIDPOINTER; 07785 07786 if ( FAILED( validateSTGM(grfMode) )) 07787 return STG_E_INVALIDFLAG; 07788 07789 *ppstgOpen = 0; 07790 07791 /* 07792 * Allocate and initialize the new IStorage object. 07793 */ 07794 hr = Storage_Construct( 07795 0, 07796 0, 07797 plkbyt, 07798 grfMode, 07799 FALSE, 07800 FALSE, 07801 512, 07802 &newStorage); 07803 07804 if (FAILED(hr)) 07805 { 07806 return hr; 07807 } 07808 07809 /* 07810 * Get an "out" pointer for the caller. 07811 */ 07812 *ppstgOpen = (IStorage*)newStorage; 07813 07814 return hr; 07815 } 07816 07817 /****************************************************************************** 07818 * StgSetTimes [ole32.@] 07819 * StgSetTimes [OLE32.@] 07820 * 07821 * 07822 */ 07823 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime, 07824 FILETIME const *patime, FILETIME const *pmtime) 07825 { 07826 IStorage *stg = NULL; 07827 HRESULT r; 07828 07829 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime); 07830 07831 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE, 07832 0, 0, &stg); 07833 if( SUCCEEDED(r) ) 07834 { 07835 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime); 07836 IStorage_Release(stg); 07837 } 07838 07839 return r; 07840 } 07841 07842 /****************************************************************************** 07843 * StgIsStorageILockBytes [OLE32.@] 07844 * 07845 * Determines if the ILockBytes contains a storage object. 07846 */ 07847 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt) 07848 { 07849 BYTE sig[sizeof(STORAGE_magic)]; 07850 ULARGE_INTEGER offset; 07851 ULONG read = 0; 07852 07853 offset.u.HighPart = 0; 07854 offset.u.LowPart = 0; 07855 07856 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read); 07857 07858 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0) 07859 return S_OK; 07860 07861 return S_FALSE; 07862 } 07863 07864 /****************************************************************************** 07865 * WriteClassStg [OLE32.@] 07866 * 07867 * This method will store the specified CLSID in the specified storage object 07868 */ 07869 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid) 07870 { 07871 HRESULT hRes; 07872 07873 if(!pStg) 07874 return E_INVALIDARG; 07875 07876 if(!rclsid) 07877 return STG_E_INVALIDPOINTER; 07878 07879 hRes = IStorage_SetClass(pStg, rclsid); 07880 07881 return hRes; 07882 } 07883 07884 /*********************************************************************** 07885 * ReadClassStg (OLE32.@) 07886 * 07887 * This method reads the CLSID previously written to a storage object with 07888 * the WriteClassStg. 07889 * 07890 * PARAMS 07891 * pstg [I] IStorage pointer 07892 * pclsid [O] Pointer to where the CLSID is written 07893 * 07894 * RETURNS 07895 * Success: S_OK. 07896 * Failure: HRESULT code. 07897 */ 07898 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){ 07899 07900 STATSTG pstatstg; 07901 HRESULT hRes; 07902 07903 TRACE("(%p, %p)\n", pstg, pclsid); 07904 07905 if(!pstg || !pclsid) 07906 return E_INVALIDARG; 07907 07908 /* 07909 * read a STATSTG structure (contains the clsid) from the storage 07910 */ 07911 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME); 07912 07913 if(SUCCEEDED(hRes)) 07914 *pclsid=pstatstg.clsid; 07915 07916 return hRes; 07917 } 07918 07919 /*********************************************************************** 07920 * OleLoadFromStream (OLE32.@) 07921 * 07922 * This function loads an object from stream 07923 */ 07924 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj) 07925 { 07926 CLSID clsid; 07927 HRESULT res; 07928 LPPERSISTSTREAM xstm; 07929 07930 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj); 07931 07932 res=ReadClassStm(pStm,&clsid); 07933 if (FAILED(res)) 07934 return res; 07935 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj); 07936 if (FAILED(res)) 07937 return res; 07938 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm); 07939 if (FAILED(res)) { 07940 IUnknown_Release((IUnknown*)*ppvObj); 07941 return res; 07942 } 07943 res=IPersistStream_Load(xstm,pStm); 07944 IPersistStream_Release(xstm); 07945 /* FIXME: all refcounts ok at this point? I think they should be: 07946 * pStm : unchanged 07947 * ppvObj : 1 07948 * xstm : 0 (released) 07949 */ 07950 return res; 07951 } 07952 07953 /*********************************************************************** 07954 * OleSaveToStream (OLE32.@) 07955 * 07956 * This function saves an object with the IPersistStream interface on it 07957 * to the specified stream. 07958 */ 07959 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm) 07960 { 07961 07962 CLSID clsid; 07963 HRESULT res; 07964 07965 TRACE("(%p,%p)\n",pPStm,pStm); 07966 07967 res=IPersistStream_GetClassID(pPStm,&clsid); 07968 07969 if (SUCCEEDED(res)){ 07970 07971 res=WriteClassStm(pStm,&clsid); 07972 07973 if (SUCCEEDED(res)) 07974 07975 res=IPersistStream_Save(pPStm,pStm,TRUE); 07976 } 07977 07978 TRACE("Finished Save\n"); 07979 return res; 07980 } 07981 07982 /**************************************************************************** 07983 * This method validate a STGM parameter that can contain the values below 07984 * 07985 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values. 07986 * The stgm values contained in 0xffff0000 are bitmasks. 07987 * 07988 * STGM_DIRECT 0x00000000 07989 * STGM_TRANSACTED 0x00010000 07990 * STGM_SIMPLE 0x08000000 07991 * 07992 * STGM_READ 0x00000000 07993 * STGM_WRITE 0x00000001 07994 * STGM_READWRITE 0x00000002 07995 * 07996 * STGM_SHARE_DENY_NONE 0x00000040 07997 * STGM_SHARE_DENY_READ 0x00000030 07998 * STGM_SHARE_DENY_WRITE 0x00000020 07999 * STGM_SHARE_EXCLUSIVE 0x00000010 08000 * 08001 * STGM_PRIORITY 0x00040000 08002 * STGM_DELETEONRELEASE 0x04000000 08003 * 08004 * STGM_CREATE 0x00001000 08005 * STGM_CONVERT 0x00020000 08006 * STGM_FAILIFTHERE 0x00000000 08007 * 08008 * STGM_NOSCRATCH 0x00100000 08009 * STGM_NOSNAPSHOT 0x00200000 08010 */ 08011 static HRESULT validateSTGM(DWORD stgm) 08012 { 08013 DWORD access = STGM_ACCESS_MODE(stgm); 08014 DWORD share = STGM_SHARE_MODE(stgm); 08015 DWORD create = STGM_CREATE_MODE(stgm); 08016 08017 if (stgm&~STGM_KNOWN_FLAGS) 08018 { 08019 ERR("unknown flags %08x\n", stgm); 08020 return E_FAIL; 08021 } 08022 08023 switch (access) 08024 { 08025 case STGM_READ: 08026 case STGM_WRITE: 08027 case STGM_READWRITE: 08028 break; 08029 default: 08030 return E_FAIL; 08031 } 08032 08033 switch (share) 08034 { 08035 case STGM_SHARE_DENY_NONE: 08036 case STGM_SHARE_DENY_READ: 08037 case STGM_SHARE_DENY_WRITE: 08038 case STGM_SHARE_EXCLUSIVE: 08039 break; 08040 default: 08041 return E_FAIL; 08042 } 08043 08044 switch (create) 08045 { 08046 case STGM_CREATE: 08047 case STGM_FAILIFTHERE: 08048 break; 08049 default: 08050 return E_FAIL; 08051 } 08052 08053 /* 08054 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE 08055 */ 08056 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) ) 08057 return E_FAIL; 08058 08059 /* 08060 * STGM_CREATE | STGM_CONVERT 08061 * if both are false, STGM_FAILIFTHERE is set to TRUE 08062 */ 08063 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) ) 08064 return E_FAIL; 08065 08066 /* 08067 * STGM_NOSCRATCH requires STGM_TRANSACTED 08068 */ 08069 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) ) 08070 return E_FAIL; 08071 08072 /* 08073 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and 08074 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE` 08075 */ 08076 if ( (stgm & STGM_NOSNAPSHOT) && 08077 (!(stgm & STGM_TRANSACTED) || 08078 share == STGM_SHARE_EXCLUSIVE || 08079 share == STGM_SHARE_DENY_WRITE) ) 08080 return E_FAIL; 08081 08082 return S_OK; 08083 } 08084 08085 /**************************************************************************** 08086 * GetShareModeFromSTGM 08087 * 08088 * This method will return a share mode flag from a STGM value. 08089 * The STGM value is assumed valid. 08090 */ 08091 static DWORD GetShareModeFromSTGM(DWORD stgm) 08092 { 08093 switch (STGM_SHARE_MODE(stgm)) 08094 { 08095 case STGM_SHARE_DENY_NONE: 08096 return FILE_SHARE_READ | FILE_SHARE_WRITE; 08097 case STGM_SHARE_DENY_READ: 08098 return FILE_SHARE_WRITE; 08099 case STGM_SHARE_DENY_WRITE: 08100 return FILE_SHARE_READ; 08101 case STGM_SHARE_EXCLUSIVE: 08102 return 0; 08103 } 08104 ERR("Invalid share mode!\n"); 08105 assert(0); 08106 return 0; 08107 } 08108 08109 /**************************************************************************** 08110 * GetAccessModeFromSTGM 08111 * 08112 * This method will return an access mode flag from a STGM value. 08113 * The STGM value is assumed valid. 08114 */ 08115 static DWORD GetAccessModeFromSTGM(DWORD stgm) 08116 { 08117 switch (STGM_ACCESS_MODE(stgm)) 08118 { 08119 case STGM_READ: 08120 return GENERIC_READ; 08121 case STGM_WRITE: 08122 case STGM_READWRITE: 08123 return GENERIC_READ | GENERIC_WRITE; 08124 } 08125 ERR("Invalid access mode!\n"); 08126 assert(0); 08127 return 0; 08128 } 08129 08130 /**************************************************************************** 08131 * GetCreationModeFromSTGM 08132 * 08133 * This method will return a creation mode flag from a STGM value. 08134 * The STGM value is assumed valid. 08135 */ 08136 static DWORD GetCreationModeFromSTGM(DWORD stgm) 08137 { 08138 switch(STGM_CREATE_MODE(stgm)) 08139 { 08140 case STGM_CREATE: 08141 return CREATE_ALWAYS; 08142 case STGM_CONVERT: 08143 FIXME("STGM_CONVERT not implemented!\n"); 08144 return CREATE_NEW; 08145 case STGM_FAILIFTHERE: 08146 return CREATE_NEW; 08147 } 08148 ERR("Invalid create mode!\n"); 08149 assert(0); 08150 return 0; 08151 } 08152 08153 08154 /************************************************************************* 08155 * OLECONVERT_LoadOLE10 [Internal] 08156 * 08157 * Loads the OLE10 STREAM to memory 08158 * 08159 * PARAMS 08160 * pOleStream [I] The OLESTREAM 08161 * pData [I] Data Structure for the OLESTREAM Data 08162 * 08163 * RETURNS 08164 * Success: S_OK 08165 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get 08166 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid 08167 * 08168 * NOTES 08169 * This function is used by OleConvertOLESTREAMToIStorage only. 08170 * 08171 * Memory allocated for pData must be freed by the caller 08172 */ 08173 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1) 08174 { 08175 DWORD dwSize; 08176 HRESULT hRes = S_OK; 08177 int nTryCnt=0; 08178 int max_try = 6; 08179 08180 pData->pData = NULL; 08181 pData->pstrOleObjFileName = NULL; 08182 08183 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++) 08184 { 08185 /* Get the OleID */ 08186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); 08187 if(dwSize != sizeof(pData->dwOleID)) 08188 { 08189 hRes = CONVERT10_E_OLESTREAM_GET; 08190 } 08191 else if(pData->dwOleID != OLESTREAM_ID) 08192 { 08193 hRes = CONVERT10_E_OLESTREAM_FMT; 08194 } 08195 else 08196 { 08197 hRes = S_OK; 08198 break; 08199 } 08200 } 08201 08202 if(hRes == S_OK) 08203 { 08204 /* Get the TypeID... more info needed for this field */ 08205 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); 08206 if(dwSize != sizeof(pData->dwTypeID)) 08207 { 08208 hRes = CONVERT10_E_OLESTREAM_GET; 08209 } 08210 } 08211 if(hRes == S_OK) 08212 { 08213 if(pData->dwTypeID != 0) 08214 { 08215 /* Get the length of the OleTypeName */ 08216 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); 08217 if(dwSize != sizeof(pData->dwOleTypeNameLength)) 08218 { 08219 hRes = CONVERT10_E_OLESTREAM_GET; 08220 } 08221 08222 if(hRes == S_OK) 08223 { 08224 if(pData->dwOleTypeNameLength > 0) 08225 { 08226 /* Get the OleTypeName */ 08227 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); 08228 if(dwSize != pData->dwOleTypeNameLength) 08229 { 08230 hRes = CONVERT10_E_OLESTREAM_GET; 08231 } 08232 } 08233 } 08234 if(bStrem1) 08235 { 08236 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength)); 08237 if(dwSize != sizeof(pData->dwOleObjFileNameLength)) 08238 { 08239 hRes = CONVERT10_E_OLESTREAM_GET; 08240 } 08241 if(hRes == S_OK) 08242 { 08243 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */ 08244 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength); 08245 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength); 08246 if(pData->pstrOleObjFileName) 08247 { 08248 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength); 08249 if(dwSize != pData->dwOleObjFileNameLength) 08250 { 08251 hRes = CONVERT10_E_OLESTREAM_GET; 08252 } 08253 } 08254 else 08255 hRes = CONVERT10_E_OLESTREAM_GET; 08256 } 08257 } 08258 else 08259 { 08260 /* Get the Width of the Metafile */ 08261 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); 08262 if(dwSize != sizeof(pData->dwMetaFileWidth)) 08263 { 08264 hRes = CONVERT10_E_OLESTREAM_GET; 08265 } 08266 if(hRes == S_OK) 08267 { 08268 /* Get the Height of the Metafile */ 08269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); 08270 if(dwSize != sizeof(pData->dwMetaFileHeight)) 08271 { 08272 hRes = CONVERT10_E_OLESTREAM_GET; 08273 } 08274 } 08275 } 08276 if(hRes == S_OK) 08277 { 08278 /* Get the Length of the Data */ 08279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); 08280 if(dwSize != sizeof(pData->dwDataLength)) 08281 { 08282 hRes = CONVERT10_E_OLESTREAM_GET; 08283 } 08284 } 08285 08286 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */ 08287 { 08288 if(!bStrem1) /* if it is a second OLE stream data */ 08289 { 08290 pData->dwDataLength -= 8; 08291 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown)); 08292 if(dwSize != sizeof(pData->strUnknown)) 08293 { 08294 hRes = CONVERT10_E_OLESTREAM_GET; 08295 } 08296 } 08297 } 08298 if(hRes == S_OK) 08299 { 08300 if(pData->dwDataLength > 0) 08301 { 08302 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength); 08303 08304 /* Get Data (ex. IStorage, Metafile, or BMP) */ 08305 if(pData->pData) 08306 { 08307 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength); 08308 if(dwSize != pData->dwDataLength) 08309 { 08310 hRes = CONVERT10_E_OLESTREAM_GET; 08311 } 08312 } 08313 else 08314 { 08315 hRes = CONVERT10_E_OLESTREAM_GET; 08316 } 08317 } 08318 } 08319 } 08320 } 08321 return hRes; 08322 } 08323 08324 /************************************************************************* 08325 * OLECONVERT_SaveOLE10 [Internal] 08326 * 08327 * Saves the OLE10 STREAM From memory 08328 * 08329 * PARAMS 08330 * pData [I] Data Structure for the OLESTREAM Data 08331 * pOleStream [I] The OLESTREAM to save 08332 * 08333 * RETURNS 08334 * Success: S_OK 08335 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put 08336 * 08337 * NOTES 08338 * This function is used by OleConvertIStorageToOLESTREAM only. 08339 * 08340 */ 08341 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream) 08342 { 08343 DWORD dwSize; 08344 HRESULT hRes = S_OK; 08345 08346 08347 /* Set the OleID */ 08348 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); 08349 if(dwSize != sizeof(pData->dwOleID)) 08350 { 08351 hRes = CONVERT10_E_OLESTREAM_PUT; 08352 } 08353 08354 if(hRes == S_OK) 08355 { 08356 /* Set the TypeID */ 08357 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); 08358 if(dwSize != sizeof(pData->dwTypeID)) 08359 { 08360 hRes = CONVERT10_E_OLESTREAM_PUT; 08361 } 08362 } 08363 08364 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK) 08365 { 08366 /* Set the Length of the OleTypeName */ 08367 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); 08368 if(dwSize != sizeof(pData->dwOleTypeNameLength)) 08369 { 08370 hRes = CONVERT10_E_OLESTREAM_PUT; 08371 } 08372 08373 if(hRes == S_OK) 08374 { 08375 if(pData->dwOleTypeNameLength > 0) 08376 { 08377 /* Set the OleTypeName */ 08378 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); 08379 if(dwSize != pData->dwOleTypeNameLength) 08380 { 08381 hRes = CONVERT10_E_OLESTREAM_PUT; 08382 } 08383 } 08384 } 08385 08386 if(hRes == S_OK) 08387 { 08388 /* Set the width of the Metafile */ 08389 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); 08390 if(dwSize != sizeof(pData->dwMetaFileWidth)) 08391 { 08392 hRes = CONVERT10_E_OLESTREAM_PUT; 08393 } 08394 } 08395 08396 if(hRes == S_OK) 08397 { 08398 /* Set the height of the Metafile */ 08399 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); 08400 if(dwSize != sizeof(pData->dwMetaFileHeight)) 08401 { 08402 hRes = CONVERT10_E_OLESTREAM_PUT; 08403 } 08404 } 08405 08406 if(hRes == S_OK) 08407 { 08408 /* Set the length of the Data */ 08409 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); 08410 if(dwSize != sizeof(pData->dwDataLength)) 08411 { 08412 hRes = CONVERT10_E_OLESTREAM_PUT; 08413 } 08414 } 08415 08416 if(hRes == S_OK) 08417 { 08418 if(pData->dwDataLength > 0) 08419 { 08420 /* Set the Data (eg. IStorage, Metafile, Bitmap) */ 08421 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength); 08422 if(dwSize != pData->dwDataLength) 08423 { 08424 hRes = CONVERT10_E_OLESTREAM_PUT; 08425 } 08426 } 08427 } 08428 } 08429 return hRes; 08430 } 08431 08432 /************************************************************************* 08433 * OLECONVERT_GetOLE20FromOLE10[Internal] 08434 * 08435 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk, 08436 * opens it, and copies the content to the dest IStorage for 08437 * OleConvertOLESTREAMToIStorage 08438 * 08439 * 08440 * PARAMS 08441 * pDestStorage [I] The IStorage to copy the data to 08442 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM 08443 * nBufferLength [I] The size of the buffer 08444 * 08445 * RETURNS 08446 * Nothing 08447 * 08448 * NOTES 08449 * 08450 * 08451 */ 08452 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength) 08453 { 08454 HRESULT hRes; 08455 HANDLE hFile; 08456 IStorage *pTempStorage; 08457 DWORD dwNumOfBytesWritten; 08458 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH]; 08459 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0}; 08460 08461 /* Create a temp File */ 08462 GetTempPathW(MAX_PATH, wstrTempDir); 08463 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile); 08464 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 08465 08466 if(hFile != INVALID_HANDLE_VALUE) 08467 { 08468 /* Write IStorage Data to File */ 08469 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL); 08470 CloseHandle(hFile); 08471 08472 /* Open and copy temp storage to the Dest Storage */ 08473 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage); 08474 if(hRes == S_OK) 08475 { 08476 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage); 08477 IStorage_Release(pTempStorage); 08478 } 08479 DeleteFileW(wstrTempFile); 08480 } 08481 } 08482 08483 08484 /************************************************************************* 08485 * OLECONVERT_WriteOLE20ToBuffer [Internal] 08486 * 08487 * Saves the OLE10 STREAM From memory 08488 * 08489 * PARAMS 08490 * pStorage [I] The Src IStorage to copy 08491 * pData [I] The Dest Memory to write to. 08492 * 08493 * RETURNS 08494 * The size in bytes allocated for pData 08495 * 08496 * NOTES 08497 * Memory allocated for pData must be freed by the caller 08498 * 08499 * Used by OleConvertIStorageToOLESTREAM only. 08500 * 08501 */ 08502 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData) 08503 { 08504 HANDLE hFile; 08505 HRESULT hRes; 08506 DWORD nDataLength = 0; 08507 IStorage *pTempStorage; 08508 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH]; 08509 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0}; 08510 08511 *pData = NULL; 08512 08513 /* Create temp Storage */ 08514 GetTempPathW(MAX_PATH, wstrTempDir); 08515 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile); 08516 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage); 08517 08518 if(hRes == S_OK) 08519 { 08520 /* Copy Src Storage to the Temp Storage */ 08521 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage); 08522 IStorage_Release(pTempStorage); 08523 08524 /* Open Temp Storage as a file and copy to memory */ 08525 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 08526 if(hFile != INVALID_HANDLE_VALUE) 08527 { 08528 nDataLength = GetFileSize(hFile, NULL); 08529 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength); 08530 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0); 08531 CloseHandle(hFile); 08532 } 08533 DeleteFileW(wstrTempFile); 08534 } 08535 return nDataLength; 08536 } 08537 08538 /************************************************************************* 08539 * OLECONVERT_CreateOleStream [Internal] 08540 * 08541 * Creates the "\001OLE" stream in the IStorage if necessary. 08542 * 08543 * PARAMS 08544 * pStorage [I] Dest storage to create the stream in 08545 * 08546 * RETURNS 08547 * Nothing 08548 * 08549 * NOTES 08550 * This function is used by OleConvertOLESTREAMToIStorage only. 08551 * 08552 * This stream is still unknown, MS Word seems to have extra data 08553 * but since the data is stored in the OLESTREAM there should be 08554 * no need to recreate the stream. If the stream is manually 08555 * deleted it will create it with this default data. 08556 * 08557 */ 08558 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage) 08559 { 08560 HRESULT hRes; 08561 IStream *pStream; 08562 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0}; 08563 BYTE pOleStreamHeader [] = 08564 { 08565 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 08566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 08567 0x00, 0x00, 0x00, 0x00 08568 }; 08569 08570 /* Create stream if not present */ 08571 hRes = IStorage_CreateStream(pStorage, wstrStreamName, 08572 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 08573 08574 if(hRes == S_OK) 08575 { 08576 /* Write default Data */ 08577 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL); 08578 IStream_Release(pStream); 08579 } 08580 } 08581 08582 /* write a string to a stream, preceded by its length */ 08583 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string ) 08584 { 08585 HRESULT r; 08586 LPSTR str; 08587 DWORD len = 0; 08588 08589 if( string ) 08590 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL); 08591 r = IStream_Write( stm, &len, sizeof(len), NULL); 08592 if( FAILED( r ) ) 08593 return r; 08594 if(len == 0) 08595 return r; 08596 str = CoTaskMemAlloc( len ); 08597 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL); 08598 r = IStream_Write( stm, str, len, NULL); 08599 CoTaskMemFree( str ); 08600 return r; 08601 } 08602 08603 /* read a string preceded by its length from a stream */ 08604 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string ) 08605 { 08606 HRESULT r; 08607 DWORD len, count = 0; 08608 LPSTR str; 08609 LPWSTR wstr; 08610 08611 r = IStream_Read( stm, &len, sizeof(len), &count ); 08612 if( FAILED( r ) ) 08613 return r; 08614 if( count != sizeof(len) ) 08615 return E_OUTOFMEMORY; 08616 08617 TRACE("%d bytes\n",len); 08618 08619 str = CoTaskMemAlloc( len ); 08620 if( !str ) 08621 return E_OUTOFMEMORY; 08622 count = 0; 08623 r = IStream_Read( stm, str, len, &count ); 08624 if( FAILED( r ) ) 08625 return r; 08626 if( count != len ) 08627 { 08628 CoTaskMemFree( str ); 08629 return E_OUTOFMEMORY; 08630 } 08631 08632 TRACE("Read string %s\n",debugstr_an(str,len)); 08633 08634 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 ); 08635 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) ); 08636 if( wstr ) 08637 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len ); 08638 CoTaskMemFree( str ); 08639 08640 *string = wstr; 08641 08642 return r; 08643 } 08644 08645 08646 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, 08647 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) 08648 { 08649 IStream *pstm; 08650 HRESULT r = S_OK; 08651 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 08652 08653 static const BYTE unknown1[12] = 08654 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 08655 0xFF, 0xFF, 0xFF, 0xFF}; 08656 static const BYTE unknown2[16] = 08657 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 08658 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 08659 08660 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid), 08661 debugstr_w(lpszUserType), debugstr_w(szClipName), 08662 debugstr_w(szProgIDName)); 08663 08664 /* Create a CompObj stream */ 08665 r = IStorage_CreateStream(pstg, szwStreamName, 08666 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm ); 08667 if( FAILED (r) ) 08668 return r; 08669 08670 /* Write CompObj Structure to stream */ 08671 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL); 08672 08673 if( SUCCEEDED( r ) ) 08674 r = WriteClassStm( pstm, clsid ); 08675 08676 if( SUCCEEDED( r ) ) 08677 r = STREAM_WriteString( pstm, lpszUserType ); 08678 if( SUCCEEDED( r ) ) 08679 r = STREAM_WriteString( pstm, szClipName ); 08680 if( SUCCEEDED( r ) ) 08681 r = STREAM_WriteString( pstm, szProgIDName ); 08682 if( SUCCEEDED( r ) ) 08683 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL); 08684 08685 IStream_Release( pstm ); 08686 08687 return r; 08688 } 08689 08690 /*********************************************************************** 08691 * WriteFmtUserTypeStg (OLE32.@) 08692 */ 08693 HRESULT WINAPI WriteFmtUserTypeStg( 08694 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType) 08695 { 08696 HRESULT r; 08697 WCHAR szwClipName[0x40]; 08698 CLSID clsid = CLSID_NULL; 08699 LPWSTR wstrProgID = NULL; 08700 DWORD n; 08701 08702 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType)); 08703 08704 /* get the clipboard format name */ 08705 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) ); 08706 szwClipName[n]=0; 08707 08708 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName)); 08709 08710 /* FIXME: There's room to save a CLSID and its ProgID, but 08711 the CLSID is not looked up in the registry and in all the 08712 tests I wrote it was CLSID_NULL. Where does it come from? 08713 */ 08714 08715 /* get the real program ID. This may fail, but that's fine */ 08716 ProgIDFromCLSID(&clsid, &wstrProgID); 08717 08718 TRACE("progid is %s\n",debugstr_w(wstrProgID)); 08719 08720 r = STORAGE_WriteCompObj( pstg, &clsid, 08721 lpszUserType, szwClipName, wstrProgID ); 08722 08723 CoTaskMemFree(wstrProgID); 08724 08725 return r; 08726 } 08727 08728 08729 /****************************************************************************** 08730 * ReadFmtUserTypeStg [OLE32.@] 08731 */ 08732 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) 08733 { 08734 HRESULT r; 08735 IStream *stm = 0; 08736 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 }; 08737 unsigned char unknown1[12]; 08738 unsigned char unknown2[16]; 08739 DWORD count; 08740 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; 08741 CLSID clsid; 08742 08743 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); 08744 08745 r = IStorage_OpenStream( pstg, szCompObj, NULL, 08746 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); 08747 if( FAILED ( r ) ) 08748 { 08749 WARN("Failed to open stream r = %08x\n", r); 08750 return r; 08751 } 08752 08753 /* read the various parts of the structure */ 08754 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count ); 08755 if( FAILED( r ) || ( count != sizeof(unknown1) ) ) 08756 goto end; 08757 r = ReadClassStm( stm, &clsid ); 08758 if( FAILED( r ) ) 08759 goto end; 08760 08761 r = STREAM_ReadString( stm, &szCLSIDName ); 08762 if( FAILED( r ) ) 08763 goto end; 08764 08765 r = STREAM_ReadString( stm, &szOleTypeName ); 08766 if( FAILED( r ) ) 08767 goto end; 08768 08769 r = STREAM_ReadString( stm, &szProgIDName ); 08770 if( FAILED( r ) ) 08771 goto end; 08772 08773 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count ); 08774 if( FAILED( r ) || ( count != sizeof(unknown2) ) ) 08775 goto end; 08776 08777 /* ok, success... now we just need to store what we found */ 08778 if( pcf ) 08779 *pcf = RegisterClipboardFormatW( szOleTypeName ); 08780 CoTaskMemFree( szOleTypeName ); 08781 08782 if( lplpszUserType ) 08783 *lplpszUserType = szCLSIDName; 08784 CoTaskMemFree( szProgIDName ); 08785 08786 end: 08787 IStream_Release( stm ); 08788 08789 return r; 08790 } 08791 08792 08793 /************************************************************************* 08794 * OLECONVERT_CreateCompObjStream [Internal] 08795 * 08796 * Creates a "\001CompObj" is the destination IStorage if necessary. 08797 * 08798 * PARAMS 08799 * pStorage [I] The dest IStorage to create the CompObj Stream 08800 * if necessary. 08801 * strOleTypeName [I] The ProgID 08802 * 08803 * RETURNS 08804 * Success: S_OK 08805 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream 08806 * 08807 * NOTES 08808 * This function is used by OleConvertOLESTREAMToIStorage only. 08809 * 08810 * The stream data is stored in the OLESTREAM and there should be 08811 * no need to recreate the stream. If the stream is manually 08812 * deleted it will attempt to create it by querying the registry. 08813 * 08814 * 08815 */ 08816 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName) 08817 { 08818 IStream *pStream; 08819 HRESULT hStorageRes, hRes = S_OK; 08820 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj; 08821 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 08822 WCHAR bufferW[OLESTREAM_MAX_STR_LEN]; 08823 08824 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; 08825 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71}; 08826 08827 /* Initialize the CompObj structure */ 08828 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj)); 08829 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1)); 08830 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2)); 08831 08832 08833 /* Create a CompObj stream if it doesn't exist */ 08834 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName, 08835 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 08836 if(hStorageRes == S_OK) 08837 { 08838 /* copy the OleTypeName to the compobj struct */ 08839 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1; 08840 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName); 08841 08842 /* copy the OleTypeName to the compobj struct */ 08843 /* Note: in the test made, these were Identical */ 08844 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1; 08845 strcpy(IStorageCompObj.strProgIDName, strOleTypeName); 08846 08847 /* Get the CLSID */ 08848 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1, 08849 bufferW, OLESTREAM_MAX_STR_LEN ); 08850 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid)); 08851 08852 if(hRes == S_OK) 08853 { 08854 HKEY hKey; 08855 LONG hErr; 08856 /* Get the CLSID Default Name from the Registry */ 08857 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey); 08858 if(hErr == ERROR_SUCCESS) 08859 { 08860 char strTemp[OLESTREAM_MAX_STR_LEN]; 08861 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN; 08862 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength)); 08863 if(hErr == ERROR_SUCCESS) 08864 { 08865 strcpy(IStorageCompObj.strCLSIDName, strTemp); 08866 } 08867 RegCloseKey(hKey); 08868 } 08869 } 08870 08871 /* Write CompObj Structure to stream */ 08872 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL); 08873 08874 WriteClassStm(pStream,&(IStorageCompObj.clsid)); 08875 08876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL); 08877 if(IStorageCompObj.dwCLSIDNameLength > 0) 08878 { 08879 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL); 08880 } 08881 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL); 08882 if(IStorageCompObj.dwOleTypeNameLength > 0) 08883 { 08884 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL); 08885 } 08886 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL); 08887 if(IStorageCompObj.dwProgIDNameLength > 0) 08888 { 08889 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL); 08890 } 08891 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL); 08892 IStream_Release(pStream); 08893 } 08894 return hRes; 08895 } 08896 08897 08898 /************************************************************************* 08899 * OLECONVERT_CreateOlePresStream[Internal] 08900 * 08901 * Creates the "\002OlePres000" Stream with the Metafile data 08902 * 08903 * PARAMS 08904 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in. 08905 * dwExtentX [I] Width of the Metafile 08906 * dwExtentY [I] Height of the Metafile 08907 * pData [I] Metafile data 08908 * dwDataLength [I] Size of the Metafile data 08909 * 08910 * RETURNS 08911 * Success: S_OK 08912 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put 08913 * 08914 * NOTES 08915 * This function is used by OleConvertOLESTREAMToIStorage only. 08916 * 08917 */ 08918 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength) 08919 { 08920 HRESULT hRes; 08921 IStream *pStream; 08922 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0}; 08923 BYTE pOlePresStreamHeader [] = 08924 { 08925 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 08926 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 08927 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 08928 0x00, 0x00, 0x00, 0x00 08929 }; 08930 08931 BYTE pOlePresStreamHeaderEmpty [] = 08932 { 08933 0x00, 0x00, 0x00, 0x00, 08934 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 08935 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 08936 0x00, 0x00, 0x00, 0x00 08937 }; 08938 08939 /* Create the OlePres000 Stream */ 08940 hRes = IStorage_CreateStream(pStorage, wstrStreamName, 08941 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 08942 08943 if(hRes == S_OK) 08944 { 08945 DWORD nHeaderSize; 08946 OLECONVERT_ISTORAGE_OLEPRES OlePres; 08947 08948 memset(&OlePres, 0, sizeof(OlePres)); 08949 /* Do we have any metafile data to save */ 08950 if(dwDataLength > 0) 08951 { 08952 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader)); 08953 nHeaderSize = sizeof(pOlePresStreamHeader); 08954 } 08955 else 08956 { 08957 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty)); 08958 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty); 08959 } 08960 /* Set width and height of the metafile */ 08961 OlePres.dwExtentX = dwExtentX; 08962 OlePres.dwExtentY = -dwExtentY; 08963 08964 /* Set Data and Length */ 08965 if(dwDataLength > sizeof(METAFILEPICT16)) 08966 { 08967 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16); 08968 OlePres.pData = &(pData[8]); 08969 } 08970 /* Save OlePres000 Data to Stream */ 08971 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL); 08972 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL); 08973 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL); 08974 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL); 08975 if(OlePres.dwSize > 0) 08976 { 08977 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL); 08978 } 08979 IStream_Release(pStream); 08980 } 08981 } 08982 08983 /************************************************************************* 08984 * OLECONVERT_CreateOle10NativeStream [Internal] 08985 * 08986 * Creates the "\001Ole10Native" Stream (should contain a BMP) 08987 * 08988 * PARAMS 08989 * pStorage [I] Dest storage to create the stream in 08990 * pData [I] Ole10 Native Data (ex. bmp) 08991 * dwDataLength [I] Size of the Ole10 Native Data 08992 * 08993 * RETURNS 08994 * Nothing 08995 * 08996 * NOTES 08997 * This function is used by OleConvertOLESTREAMToIStorage only. 08998 * 08999 * Might need to verify the data and return appropriate error message 09000 * 09001 */ 09002 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength) 09003 { 09004 HRESULT hRes; 09005 IStream *pStream; 09006 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 09007 09008 /* Create the Ole10Native Stream */ 09009 hRes = IStorage_CreateStream(pStorage, wstrStreamName, 09010 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 09011 09012 if(hRes == S_OK) 09013 { 09014 /* Write info to stream */ 09015 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL); 09016 hRes = IStream_Write(pStream, pData, dwDataLength, NULL); 09017 IStream_Release(pStream); 09018 } 09019 09020 } 09021 09022 /************************************************************************* 09023 * OLECONVERT_GetOLE10ProgID [Internal] 09024 * 09025 * Finds the ProgID (or OleTypeID) from the IStorage 09026 * 09027 * PARAMS 09028 * pStorage [I] The Src IStorage to get the ProgID 09029 * strProgID [I] the ProgID string to get 09030 * dwSize [I] the size of the string 09031 * 09032 * RETURNS 09033 * Success: S_OK 09034 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream 09035 * 09036 * NOTES 09037 * This function is used by OleConvertIStorageToOLESTREAM only. 09038 * 09039 * 09040 */ 09041 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize) 09042 { 09043 HRESULT hRes; 09044 IStream *pStream; 09045 LARGE_INTEGER iSeekPos; 09046 OLECONVERT_ISTORAGE_COMPOBJ CompObj; 09047 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 09048 09049 /* Open the CompObj Stream */ 09050 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 09051 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 09052 if(hRes == S_OK) 09053 { 09054 09055 /*Get the OleType from the CompObj Stream */ 09056 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid); 09057 iSeekPos.u.HighPart = 0; 09058 09059 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); 09060 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL); 09061 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength; 09062 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); 09063 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL); 09064 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength; 09065 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); 09066 09067 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL); 09068 if(*dwSize > 0) 09069 { 09070 IStream_Read(pStream, strProgID, *dwSize, NULL); 09071 } 09072 IStream_Release(pStream); 09073 } 09074 else 09075 { 09076 STATSTG stat; 09077 LPOLESTR wstrProgID; 09078 09079 /* Get the OleType from the registry */ 09080 REFCLSID clsid = &(stat.clsid); 09081 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME); 09082 hRes = ProgIDFromCLSID(clsid, &wstrProgID); 09083 if(hRes == S_OK) 09084 { 09085 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE); 09086 } 09087 09088 } 09089 return hRes; 09090 } 09091 09092 /************************************************************************* 09093 * OLECONVERT_GetOle10PresData [Internal] 09094 * 09095 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream 09096 * 09097 * PARAMS 09098 * pStorage [I] Src IStroage 09099 * pOleStream [I] Dest OleStream Mem Struct 09100 * 09101 * RETURNS 09102 * Nothing 09103 * 09104 * NOTES 09105 * This function is used by OleConvertIStorageToOLESTREAM only. 09106 * 09107 * Memory allocated for pData must be freed by the caller 09108 * 09109 * 09110 */ 09111 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) 09112 { 09113 09114 HRESULT hRes; 09115 IStream *pStream; 09116 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 09117 09118 /* Initialize Default data for OLESTREAM */ 09119 pOleStreamData[0].dwOleID = OLESTREAM_ID; 09120 pOleStreamData[0].dwTypeID = 2; 09121 pOleStreamData[1].dwOleID = OLESTREAM_ID; 09122 pOleStreamData[1].dwTypeID = 0; 09123 pOleStreamData[0].dwMetaFileWidth = 0; 09124 pOleStreamData[0].dwMetaFileHeight = 0; 09125 pOleStreamData[0].pData = NULL; 09126 pOleStreamData[1].pData = NULL; 09127 09128 /* Open Ole10Native Stream */ 09129 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 09130 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 09131 if(hRes == S_OK) 09132 { 09133 09134 /* Read Size and Data */ 09135 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL); 09136 if(pOleStreamData->dwDataLength > 0) 09137 { 09138 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength); 09139 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL); 09140 } 09141 IStream_Release(pStream); 09142 } 09143 09144 } 09145 09146 09147 /************************************************************************* 09148 * OLECONVERT_GetOle20PresData[Internal] 09149 * 09150 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream 09151 * 09152 * PARAMS 09153 * pStorage [I] Src IStroage 09154 * pOleStreamData [I] Dest OleStream Mem Struct 09155 * 09156 * RETURNS 09157 * Nothing 09158 * 09159 * NOTES 09160 * This function is used by OleConvertIStorageToOLESTREAM only. 09161 * 09162 * Memory allocated for pData must be freed by the caller 09163 */ 09164 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) 09165 { 09166 HRESULT hRes; 09167 IStream *pStream; 09168 OLECONVERT_ISTORAGE_OLEPRES olePress; 09169 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0}; 09170 09171 /* Initialize Default data for OLESTREAM */ 09172 pOleStreamData[0].dwOleID = OLESTREAM_ID; 09173 pOleStreamData[0].dwTypeID = 2; 09174 pOleStreamData[0].dwMetaFileWidth = 0; 09175 pOleStreamData[0].dwMetaFileHeight = 0; 09176 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData)); 09177 pOleStreamData[1].dwOleID = OLESTREAM_ID; 09178 pOleStreamData[1].dwTypeID = 0; 09179 pOleStreamData[1].dwOleTypeNameLength = 0; 09180 pOleStreamData[1].strOleTypeName[0] = 0; 09181 pOleStreamData[1].dwMetaFileWidth = 0; 09182 pOleStreamData[1].dwMetaFileHeight = 0; 09183 pOleStreamData[1].pData = NULL; 09184 pOleStreamData[1].dwDataLength = 0; 09185 09186 09187 /* Open OlePress000 stream */ 09188 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 09189 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 09190 if(hRes == S_OK) 09191 { 09192 LARGE_INTEGER iSeekPos; 09193 METAFILEPICT16 MetaFilePict; 09194 static const char strMetafilePictName[] = "METAFILEPICT"; 09195 09196 /* Set the TypeID for a Metafile */ 09197 pOleStreamData[1].dwTypeID = 5; 09198 09199 /* Set the OleTypeName to Metafile */ 09200 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1; 09201 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName); 09202 09203 iSeekPos.u.HighPart = 0; 09204 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1); 09205 09206 /* Get Presentation Data */ 09207 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); 09208 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL); 09209 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL); 09210 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL); 09211 09212 /*Set width and Height */ 09213 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX; 09214 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY; 09215 if(olePress.dwSize > 0) 09216 { 09217 /* Set Length */ 09218 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16); 09219 09220 /* Set MetaFilePict struct */ 09221 MetaFilePict.mm = 8; 09222 MetaFilePict.xExt = olePress.dwExtentX; 09223 MetaFilePict.yExt = olePress.dwExtentY; 09224 MetaFilePict.hMF = 0; 09225 09226 /* Get Metafile Data */ 09227 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength); 09228 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict)); 09229 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL); 09230 } 09231 IStream_Release(pStream); 09232 } 09233 } 09234 09235 /************************************************************************* 09236 * OleConvertOLESTREAMToIStorage [OLE32.@] 09237 * 09238 * Read info on MSDN 09239 * 09240 * TODO 09241 * DVTARGETDEVICE parameter is not handled 09242 * Still unsure of some mem fields for OLE 10 Stream 09243 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", 09244 * and "\001OLE" streams 09245 * 09246 */ 09247 HRESULT WINAPI OleConvertOLESTREAMToIStorage ( 09248 LPOLESTREAM pOleStream, 09249 LPSTORAGE pstg, 09250 const DVTARGETDEVICE* ptd) 09251 { 09252 int i; 09253 HRESULT hRes=S_OK; 09254 OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; 09255 09256 TRACE("%p %p %p\n", pOleStream, pstg, ptd); 09257 09258 memset(pOleStreamData, 0, sizeof(pOleStreamData)); 09259 09260 if(ptd != NULL) 09261 { 09262 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n"); 09263 } 09264 09265 if(pstg == NULL || pOleStream == NULL) 09266 { 09267 hRes = E_INVALIDARG; 09268 } 09269 09270 if(hRes == S_OK) 09271 { 09272 /* Load the OLESTREAM to Memory */ 09273 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE); 09274 } 09275 09276 if(hRes == S_OK) 09277 { 09278 /* Load the OLESTREAM to Memory (part 2)*/ 09279 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE); 09280 } 09281 09282 if(hRes == S_OK) 09283 { 09284 09285 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic)) 09286 { 09287 /* Do we have the IStorage Data in the OLESTREAM */ 09288 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0) 09289 { 09290 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 09291 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength); 09292 } 09293 else 09294 { 09295 /* It must be an original OLE 1.0 source */ 09296 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 09297 } 09298 } 09299 else 09300 { 09301 /* It must be an original OLE 1.0 source */ 09302 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 09303 } 09304 09305 /* Create CompObj Stream if necessary */ 09306 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName); 09307 if(hRes == S_OK) 09308 { 09309 /*Create the Ole Stream if necessary */ 09310 OLECONVERT_CreateOleStream(pstg); 09311 } 09312 } 09313 09314 09315 /* Free allocated memory */ 09316 for(i=0; i < 2; i++) 09317 { 09318 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); 09319 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName); 09320 pOleStreamData[i].pstrOleObjFileName = NULL; 09321 } 09322 return hRes; 09323 } 09324 09325 /************************************************************************* 09326 * OleConvertIStorageToOLESTREAM [OLE32.@] 09327 * 09328 * Read info on MSDN 09329 * 09330 * Read info on MSDN 09331 * 09332 * TODO 09333 * Still unsure of some mem fields for OLE 10 Stream 09334 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", 09335 * and "\001OLE" streams. 09336 * 09337 */ 09338 HRESULT WINAPI OleConvertIStorageToOLESTREAM ( 09339 LPSTORAGE pstg, 09340 LPOLESTREAM pOleStream) 09341 { 09342 int i; 09343 HRESULT hRes = S_OK; 09344 IStream *pStream; 09345 OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; 09346 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 09347 09348 TRACE("%p %p\n", pstg, pOleStream); 09349 09350 memset(pOleStreamData, 0, sizeof(pOleStreamData)); 09351 09352 if(pstg == NULL || pOleStream == NULL) 09353 { 09354 hRes = E_INVALIDARG; 09355 } 09356 if(hRes == S_OK) 09357 { 09358 /* Get the ProgID */ 09359 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN; 09360 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength)); 09361 } 09362 if(hRes == S_OK) 09363 { 09364 /* Was it originally Ole10 */ 09365 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream); 09366 if(hRes == S_OK) 09367 { 09368 IStream_Release(pStream); 09369 /* Get Presentation Data for Ole10Native */ 09370 OLECONVERT_GetOle10PresData(pstg, pOleStreamData); 09371 } 09372 else 09373 { 09374 /* Get Presentation Data (OLE20) */ 09375 OLECONVERT_GetOle20PresData(pstg, pOleStreamData); 09376 } 09377 09378 /* Save OLESTREAM */ 09379 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream); 09380 if(hRes == S_OK) 09381 { 09382 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream); 09383 } 09384 09385 } 09386 09387 /* Free allocated memory */ 09388 for(i=0; i < 2; i++) 09389 { 09390 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); 09391 } 09392 09393 return hRes; 09394 } 09395 09396 /*********************************************************************** 09397 * GetConvertStg (OLE32.@) 09398 */ 09399 HRESULT WINAPI GetConvertStg(IStorage *stg) { 09400 FIXME("unimplemented stub!\n"); 09401 return E_FAIL; 09402 } 09403 09404 /****************************************************************************** 09405 * StgIsStorageFile [OLE32.@] 09406 * Verify if the file contains a storage object 09407 * 09408 * PARAMS 09409 * fn [ I] Filename 09410 * 09411 * RETURNS 09412 * S_OK if file has magic bytes as a storage object 09413 * S_FALSE if file is not storage 09414 */ 09415 HRESULT WINAPI 09416 StgIsStorageFile(LPCOLESTR fn) 09417 { 09418 HANDLE hf; 09419 BYTE magic[8]; 09420 DWORD bytes_read; 09421 09422 TRACE("%s\n", debugstr_w(fn)); 09423 hf = CreateFileW(fn, GENERIC_READ, 09424 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 09425 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 09426 09427 if (hf == INVALID_HANDLE_VALUE) 09428 return STG_E_FILENOTFOUND; 09429 09430 if (!ReadFile(hf, magic, 8, &bytes_read, NULL)) 09431 { 09432 WARN(" unable to read file\n"); 09433 CloseHandle(hf); 09434 return S_FALSE; 09435 } 09436 09437 CloseHandle(hf); 09438 09439 if (bytes_read != 8) { 09440 TRACE(" too short\n"); 09441 return S_FALSE; 09442 } 09443 09444 if (!memcmp(magic,STORAGE_magic,8)) { 09445 TRACE(" -> YES\n"); 09446 return S_OK; 09447 } 09448 09449 TRACE(" -> Invalid header.\n"); 09450 return S_FALSE; 09451 } 09452 09453 /*********************************************************************** 09454 * WriteClassStm (OLE32.@) 09455 * 09456 * Writes a CLSID to a stream. 09457 * 09458 * PARAMS 09459 * pStm [I] Stream to write to. 09460 * rclsid [I] CLSID to write. 09461 * 09462 * RETURNS 09463 * Success: S_OK. 09464 * Failure: HRESULT code. 09465 */ 09466 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid) 09467 { 09468 TRACE("(%p,%p)\n",pStm,rclsid); 09469 09470 if (!pStm || !rclsid) 09471 return E_INVALIDARG; 09472 09473 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL); 09474 } 09475 09476 /*********************************************************************** 09477 * ReadClassStm (OLE32.@) 09478 * 09479 * Reads a CLSID from a stream. 09480 * 09481 * PARAMS 09482 * pStm [I] Stream to read from. 09483 * rclsid [O] CLSID to read. 09484 * 09485 * RETURNS 09486 * Success: S_OK. 09487 * Failure: HRESULT code. 09488 */ 09489 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid) 09490 { 09491 ULONG nbByte; 09492 HRESULT res; 09493 09494 TRACE("(%p,%p)\n",pStm,pclsid); 09495 09496 if (!pStm || !pclsid) 09497 return E_INVALIDARG; 09498 09499 /* clear the output args */ 09500 *pclsid = CLSID_NULL; 09501 09502 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte); 09503 09504 if (FAILED(res)) 09505 return res; 09506 09507 if (nbByte != sizeof(CLSID)) 09508 return STG_E_READFAULT; 09509 else 09510 return S_OK; 09511 } Generated on Sat May 26 2012 04:24:14 for ReactOS by
1.7.6.1
|