1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
4 *
5 * This file contains the compound file implementation
6 * of the storage interface.
7 *
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 *
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
31 */
32
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
49
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
52
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
57
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
61
62 /*
63 * These are signatures to detect the type of Document file.
64 */
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
67
68 static const char rootPropertyName[] = "Root Entry";
69
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
72 *
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
76 */
77 struct StorageInternalImpl
78 {
79 struct StorageBaseImpl base;
80 /*
81 * There is no specific data for this class.
82 */
83 };
84 typedef struct StorageInternalImpl StorageInternalImpl;
85
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
95
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
101
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
105
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
112
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
115 typedef struct
116 {
117 DWORD dwOleID;
118 DWORD dwTypeID;
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
126 DWORD dwDataLength;
127 BYTE *pData;
128 }OLECONVERT_OLESTREAM_DATA;
129
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
132 typedef struct
133 {
134 BYTE byUnknown1[12];
135 CLSID clsid;
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
142 BYTE byUnknown2[16];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
144
145
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 typedef struct
149 {
150 BYTE byUnknown1[28];
151 DWORD dwExtentX;
152 DWORD dwExtentY;
153 DWORD dwSize;
154 BYTE *pData;
155 }OLECONVERT_ISTORAGE_OLEPRES;
156
157
158
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
161 */
162 static HRESULT deleteStorageProperty(
163 StorageImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
166
167 static HRESULT deleteStreamProperty(
168 StorageImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
171
172 static HRESULT findPlaceholder(
173 StorageImpl *storage,
174 ULONG propertyIndexToStore,
175 ULONG storagePropertyIndex,
176 INT typeOfRelation);
177
178 static HRESULT adjustPropertyChain(
179 StorageImpl *This,
180 StgProperty propertyToDelete,
181 StgProperty parentProperty,
182 ULONG parentPropertyId,
183 INT typeOfRelation);
184
185 /***********************************************************************
186 * Declaration of the functions used to manipulate StgProperty
187 */
188
189 static ULONG getFreeProperty(
190 StorageImpl *storage);
191
192 static void updatePropertyChain(
193 StorageImpl *storage,
194 ULONG newPropertyIndex,
195 StgProperty newProperty);
196
197 static LONG propertyNameCmp(
198 const OLECHAR *newProperty,
199 const OLECHAR *currentProperty);
200
201
202 /***********************************************************************
203 * Declaration of miscellaneous functions...
204 */
205 static HRESULT validateSTGM(DWORD stgmValue);
206
207 static DWORD GetShareModeFromSTGM(DWORD stgm);
208 static DWORD GetAccessModeFromSTGM(DWORD stgm);
209 static DWORD GetCreationModeFromSTGM(DWORD stgm);
210
211 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
212
213
214 /****************************************************************************
215 * IEnumSTATSTGImpl definitions.
216 *
217 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
218 * This class allows iterating through the content of a storage and to find
219 * specific items inside it.
220 */
221 struct IEnumSTATSTGImpl
222 {
223 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
224 * since we want to cast this in an IEnumSTATSTG pointer */
225
226 LONG ref; /* Reference count */
227 StorageImpl* parentStorage; /* Reference to the parent storage */
228 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
229
230 /*
231 * The current implementation of the IEnumSTATSTGImpl class uses a stack
232 * to walk the property sets to get the content of a storage. This stack
233 * is implemented by the following 3 data members
234 */
235 ULONG stackSize;
236 ULONG stackMaxSize;
237 ULONG* stackToVisit;
238
239 #define ENUMSTATSGT_SIZE_INCREMENT 10
240 };
241
242
243 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
244 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
245 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
246 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
247 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
248 StgProperty* buffer);
249 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
250 StgProperty *currentProperty, ULONG *propertyId);
251
252 /************************************************************************
253 ** Block Functions
254 */
255
256 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
257 {
258 if (index == 0xffffffff)
259 index = 0;
260 else
261 index ++;
262
263 return index * BIG_BLOCK_SIZE;
264 }
265
266 /************************************************************************
267 ** Storage32BaseImpl implementation
268 */
269 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
270 ULARGE_INTEGER offset,
271 void* buffer,
272 ULONG size,
273 ULONG* bytesRead)
274 {
275 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
276 }
277
278 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
279 ULARGE_INTEGER offset,
280 const void* buffer,
281 const ULONG size,
282 ULONG* bytesWritten)
283 {
284 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
285 }
286
287 /************************************************************************
288 * Storage32BaseImpl_QueryInterface (IUnknown)
289 *
290 * This method implements the common QueryInterface for all IStorage32
291 * implementations contained in this file.
292 *
293 * See Windows documentation for more details on IUnknown methods.
294 */
295 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
296 IStorage* iface,
297 REFIID riid,
298 void** ppvObject)
299 {
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
301
302 if ( (This==0) || (ppvObject==0) )
303 return E_INVALIDARG;
304
305 *ppvObject = 0;
306
307 if (IsEqualGUID(&IID_IUnknown, riid) ||
308 IsEqualGUID(&IID_IStorage, riid))
309 {
310 *ppvObject = This;
311 }
312 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
313 {
314 *ppvObject = &This->pssVtbl;
315 }
316
317 if ((*ppvObject)==0)
318 return E_NOINTERFACE;
319
320 IStorage_AddRef(iface);
321
322 return S_OK;
323 }
324
325 /************************************************************************
326 * Storage32BaseImpl_AddRef (IUnknown)
327 *
328 * This method implements the common AddRef for all IStorage32
329 * implementations contained in this file.
330 *
331 * See Windows documentation for more details on IUnknown methods.
332 */
333 static ULONG WINAPI StorageBaseImpl_AddRef(
334 IStorage* iface)
335 {
336 StorageBaseImpl *This = (StorageBaseImpl *)iface;
337 ULONG ref = InterlockedIncrement(&This->ref);
338
339 TRACE("(%p) AddRef to %d\n", This, ref);
340
341 return ref;
342 }
343
344 /************************************************************************
345 * Storage32BaseImpl_Release (IUnknown)
346 *
347 * This method implements the common Release for all IStorage32
348 * implementations contained in this file.
349 *
350 * See Windows documentation for more details on IUnknown methods.
351 */
352 static ULONG WINAPI StorageBaseImpl_Release(
353 IStorage* iface)
354 {
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
356
357 ULONG ref = InterlockedDecrement(&This->ref);
358
359 TRACE("(%p) ReleaseRef to %d\n", This, ref);
360
361 if (ref == 0)
362 {
363 /*
364 * Since we are using a system of base-classes, we want to call the
365 * destructor of the appropriate derived class. To do this, we are
366 * using virtual functions to implement the destructor.
367 */
368 This->v_destructor(This);
369 }
370
371 return ref;
372 }
373
374 /************************************************************************
375 * Storage32BaseImpl_OpenStream (IStorage)
376 *
377 * This method will open the specified stream object from the current storage.
378 *
379 * See Windows documentation for more details on IStorage methods.
380 */
381 static HRESULT WINAPI StorageBaseImpl_OpenStream(
382 IStorage* iface,
383 const OLECHAR* pwcsName, /* [string][in] */
384 void* reserved1, /* [unique][in] */
385 DWORD grfMode, /* [in] */
386 DWORD reserved2, /* [in] */
387 IStream** ppstm) /* [out] */
388 {
389 StorageBaseImpl *This = (StorageBaseImpl *)iface;
390 IEnumSTATSTGImpl* propertyEnumeration;
391 StgStreamImpl* newStream;
392 StgProperty currentProperty;
393 ULONG foundPropertyIndex;
394 HRESULT res = STG_E_UNKNOWN;
395
396 TRACE("(%p, %s, %p, %x, %d, %p)\n",
397 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
398
399 if ( (pwcsName==NULL) || (ppstm==0) )
400 {
401 res = E_INVALIDARG;
402 goto end;
403 }
404
405 *ppstm = NULL;
406
407 if ( FAILED( validateSTGM(grfMode) ) ||
408 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
409 {
410 res = STG_E_INVALIDFLAG;
411 goto end;
412 }
413
414 /*
415 * As documented.
416 */
417 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
418 {
419 res = STG_E_INVALIDFUNCTION;
420 goto end;
421 }
422
423 /*
424 * Check that we're compatible with the parent's storage mode, but
425 * only if we are not in transacted mode
426 */
427 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
428 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
429 {
430 res = STG_E_ACCESSDENIED;
431 goto end;
432 }
433 }
434
435 /*
436 * Create a property enumeration to search the properties
437 */
438 propertyEnumeration = IEnumSTATSTGImpl_Construct(
439 This->ancestorStorage,
440 This->rootPropertySetIndex);
441
442 /*
443 * Search the enumeration for the property with the given name
444 */
445 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
446 propertyEnumeration,
447 pwcsName,
448 ¤tProperty);
449
450 /*
451 * Delete the property enumeration since we don't need it anymore
452 */
453 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
454
455 /*
456 * If it was found, construct the stream object and return a pointer to it.
457 */
458 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
459 (currentProperty.propertyType==PROPTYPE_STREAM) )
460 {
461 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
462
463 if (newStream!=0)
464 {
465 newStream->grfMode = grfMode;
466 *ppstm = (IStream*)newStream;
467
468 IStream_AddRef(*ppstm);
469
470 res = S_OK;
471 goto end;
472 }
473
474 res = E_OUTOFMEMORY;
475 goto end;
476 }
477
478 res = STG_E_FILENOTFOUND;
479
480 end:
481 if (res == S_OK)
482 TRACE("<-- IStream %p\n", *ppstm);
483 TRACE("<-- %08x\n", res);
484 return res;
485 }
486
487 /************************************************************************
488 * Storage32BaseImpl_OpenStorage (IStorage)
489 *
490 * This method will open a new storage object from the current storage.
491 *
492 * See Windows documentation for more details on IStorage methods.
493 */
494 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
495 IStorage* iface,
496 const OLECHAR* pwcsName, /* [string][unique][in] */
497 IStorage* pstgPriority, /* [unique][in] */
498 DWORD grfMode, /* [in] */
499 SNB snbExclude, /* [unique][in] */
500 DWORD reserved, /* [in] */
501 IStorage** ppstg) /* [out] */
502 {
503 StorageBaseImpl *This = (StorageBaseImpl *)iface;
504 StorageInternalImpl* newStorage;
505 IEnumSTATSTGImpl* propertyEnumeration;
506 StgProperty currentProperty;
507 ULONG foundPropertyIndex;
508 HRESULT res = STG_E_UNKNOWN;
509
510 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
511 iface, debugstr_w(pwcsName), pstgPriority,
512 grfMode, snbExclude, reserved, ppstg);
513
514 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
515 {
516 res = E_INVALIDARG;
517 goto end;
518 }
519
520 /* as documented */
521 if (snbExclude != NULL)
522 {
523 res = STG_E_INVALIDPARAMETER;
524 goto end;
525 }
526
527 if ( FAILED( validateSTGM(grfMode) ))
528 {
529 res = STG_E_INVALIDFLAG;
530 goto end;
531 }
532
533 /*
534 * As documented.
535 */
536 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
537 (grfMode & STGM_DELETEONRELEASE) ||
538 (grfMode & STGM_PRIORITY) )
539 {
540 res = STG_E_INVALIDFUNCTION;
541 goto end;
542 }
543
544 /*
545 * Check that we're compatible with the parent's storage mode,
546 * but only if we are not transacted
547 */
548 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
549 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
550 {
551 res = STG_E_ACCESSDENIED;
552 goto end;
553 }
554 }
555
556 *ppstg = NULL;
557
558 propertyEnumeration = IEnumSTATSTGImpl_Construct(
559 This->ancestorStorage,
560 This->rootPropertySetIndex);
561
562 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
563 propertyEnumeration,
564 pwcsName,
565 ¤tProperty);
566
567 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
568
569 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
570 (currentProperty.propertyType==PROPTYPE_STORAGE) )
571 {
572 newStorage = StorageInternalImpl_Construct(
573 This->ancestorStorage,
574 grfMode,
575 foundPropertyIndex);
576
577 if (newStorage != 0)
578 {
579 *ppstg = (IStorage*)newStorage;
580
581 StorageBaseImpl_AddRef(*ppstg);
582
583 res = S_OK;
584 goto end;
585 }
586
587 res = STG_E_INSUFFICIENTMEMORY;
588 goto end;
589 }
590
591 res = STG_E_FILENOTFOUND;
592
593 end:
594 TRACE("<-- %08x\n", res);
595 return res;
596 }
597
598 /************************************************************************
599 * Storage32BaseImpl_EnumElements (IStorage)
600 *
601 * This method will create an enumerator object that can be used to
602 * retrieve information about all the properties in the storage object.
603 *
604 * See Windows documentation for more details on IStorage methods.
605 */
606 static HRESULT WINAPI StorageBaseImpl_EnumElements(
607 IStorage* iface,
608 DWORD reserved1, /* [in] */
609 void* reserved2, /* [size_is][unique][in] */
610 DWORD reserved3, /* [in] */
611 IEnumSTATSTG** ppenum) /* [out] */
612 {
613 StorageBaseImpl *This = (StorageBaseImpl *)iface;
614 IEnumSTATSTGImpl* newEnum;
615
616 TRACE("(%p, %d, %p, %d, %p)\n",
617 iface, reserved1, reserved2, reserved3, ppenum);
618
619 if ( (This==0) || (ppenum==0))
620 return E_INVALIDARG;
621
622 newEnum = IEnumSTATSTGImpl_Construct(
623 This->ancestorStorage,
624 This->rootPropertySetIndex);
625
626 if (newEnum!=0)
627 {
628 *ppenum = (IEnumSTATSTG*)newEnum;
629
630 IEnumSTATSTG_AddRef(*ppenum);
631
632 return S_OK;
633 }
634
635 return E_OUTOFMEMORY;
636 }
637
638 /************************************************************************
639 * Storage32BaseImpl_Stat (IStorage)
640 *
641 * This method will retrieve information about this storage object.
642 *
643 * See Windows documentation for more details on IStorage methods.
644 */
645 static HRESULT WINAPI StorageBaseImpl_Stat(
646 IStorage* iface,
647 STATSTG* pstatstg, /* [out] */
648 DWORD grfStatFlag) /* [in] */
649 {
650 StorageBaseImpl *This = (StorageBaseImpl *)iface;
651 StgProperty curProperty;
652 BOOL readSuccessful;
653 HRESULT res = STG_E_UNKNOWN;
654
655 TRACE("(%p, %p, %x)\n",
656 iface, pstatstg, grfStatFlag);
657
658 if ( (This==0) || (pstatstg==0))
659 {
660 res = E_INVALIDARG;
661 goto end;
662 }
663
664 readSuccessful = StorageImpl_ReadProperty(
665 This->ancestorStorage,
666 This->rootPropertySetIndex,
667 &curProperty);
668
669 if (readSuccessful)
670 {
671 StorageUtl_CopyPropertyToSTATSTG(
672 pstatstg,
673 &curProperty,
674 grfStatFlag);
675
676 pstatstg->grfMode = This->openFlags;
677 pstatstg->grfStateBits = This->stateBits;
678
679 res = S_OK;
680 goto end;
681 }
682
683 res = E_FAIL;
684
685 end:
686 if (res == S_OK)
687 {
688 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);
689 }
690 TRACE("<-- %08x\n", res);
691 return res;
692 }
693
694 /************************************************************************
695 * Storage32BaseImpl_RenameElement (IStorage)
696 *
697 * This method will rename the specified element.
698 *
699 * See Windows documentation for more details on IStorage methods.
700 *
701 * Implementation notes: The method used to rename consists of creating a clone
702 * of the deleted StgProperty object setting it with the new name and to
703 * perform a DestroyElement of the old StgProperty.
704 */
705 static HRESULT WINAPI StorageBaseImpl_RenameElement(
706 IStorage* iface,
707 const OLECHAR* pwcsOldName, /* [in] */
708 const OLECHAR* pwcsNewName) /* [in] */
709 {
710 StorageBaseImpl *This = (StorageBaseImpl *)iface;
711 IEnumSTATSTGImpl* propertyEnumeration;
712 StgProperty currentProperty;
713 ULONG foundPropertyIndex;
714
715 TRACE("(%p, %s, %s)\n",
716 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
717
718 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
719 This->rootPropertySetIndex);
720
721 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
722 pwcsNewName,
723 ¤tProperty);
724
725 if (foundPropertyIndex != PROPERTY_NULL)
726 {
727 /*
728 * There is already a property with the new name
729 */
730 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
731 return STG_E_FILEALREADYEXISTS;
732 }
733
734 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
735
736 /*
737 * Search the enumeration for the old property name
738 */
739 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
740 pwcsOldName,
741 ¤tProperty);
742
743 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
744
745 if (foundPropertyIndex != PROPERTY_NULL)
746 {
747 StgProperty renamedProperty;
748 ULONG renamedPropertyIndex;
749
750 /*
751 * Setup a new property for the renamed property
752 */
753 renamedProperty.sizeOfNameString =
754 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
755
756 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
757 return STG_E_INVALIDNAME;
758
759 strcpyW(renamedProperty.name, pwcsNewName);
760
761 renamedProperty.propertyType = currentProperty.propertyType;
762 renamedProperty.startingBlock = currentProperty.startingBlock;
763 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
764 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
765
766 renamedProperty.previousProperty = PROPERTY_NULL;
767 renamedProperty.nextProperty = PROPERTY_NULL;
768
769 /*
770 * Bring the dirProperty link in case it is a storage and in which
771 * case the renamed storage elements don't require to be reorganized.
772 */
773 renamedProperty.dirProperty = currentProperty.dirProperty;
774
775 /* call CoFileTime to get the current time
776 renamedProperty.timeStampS1
777 renamedProperty.timeStampD1
778 renamedProperty.timeStampS2
779 renamedProperty.timeStampD2
780 renamedProperty.propertyUniqueID
781 */
782
783 /*
784 * Obtain a free property in the property chain
785 */
786 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
787
788 /*
789 * Save the new property into the new property spot
790 */
791 StorageImpl_WriteProperty(
792 This->ancestorStorage,
793 renamedPropertyIndex,
794 &renamedProperty);
795
796 /*
797 * Find a spot in the property chain for our newly created property.
798 */
799 updatePropertyChain(
800 (StorageImpl*)This,
801 renamedPropertyIndex,
802 renamedProperty);
803
804 /*
805 * At this point the renamed property has been inserted in the tree,
806 * now, before Destroying the old property we must zero its dirProperty
807 * otherwise the DestroyProperty below will zap it all and we do not want
808 * this to happen.
809 * Also, we fake that the old property is a storage so the DestroyProperty
810 * will not do a SetSize(0) on the stream data.
811 *
812 * This means that we need to tweak the StgProperty if it is a stream or a
813 * non empty storage.
814 */
815 StorageImpl_ReadProperty(This->ancestorStorage,
816 foundPropertyIndex,
817 ¤tProperty);
818
819 currentProperty.dirProperty = PROPERTY_NULL;
820 currentProperty.propertyType = PROPTYPE_STORAGE;
821 StorageImpl_WriteProperty(
822 This->ancestorStorage,
823 foundPropertyIndex,
824 ¤tProperty);
825
826 /*
827 * Invoke Destroy to get rid of the ole property and automatically redo
828 * the linking of its previous and next members...
829 */
830 IStorage_DestroyElement(iface, pwcsOldName);
831
832 }
833 else
834 {
835 /*
836 * There is no property with the old name
837 */
838 return STG_E_FILENOTFOUND;
839 }
840
841 return S_OK;
842 }
843
844 /************************************************************************
845 * Storage32BaseImpl_CreateStream (IStorage)
846 *
847 * This method will create a stream object within this storage
848 *
849 * See Windows documentation for more details on IStorage methods.
850 */
851 static HRESULT WINAPI StorageBaseImpl_CreateStream(
852 IStorage* iface,
853 const OLECHAR* pwcsName, /* [string][in] */
854 DWORD grfMode, /* [in] */
855 DWORD reserved1, /* [in] */
856 DWORD reserved2, /* [in] */
857 IStream** ppstm) /* [out] */
858 {
859 StorageBaseImpl *This = (StorageBaseImpl *)iface;
860 IEnumSTATSTGImpl* propertyEnumeration;
861 StgStreamImpl* newStream;
862 StgProperty currentProperty, newStreamProperty;
863 ULONG foundPropertyIndex, newPropertyIndex;
864
865 TRACE("(%p, %s, %x, %d, %d, %p)\n",
866 iface, debugstr_w(pwcsName), grfMode,
867 reserved1, reserved2, ppstm);
868
869 if (ppstm == 0)
870 return STG_E_INVALIDPOINTER;
871
872 if (pwcsName == 0)
873 return STG_E_INVALIDNAME;
874
875 if (reserved1 || reserved2)
876 return STG_E_INVALIDPARAMETER;
877
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
880
881 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
882 return STG_E_INVALIDFLAG;
883
884 /*
885 * As documented.
886 */
887 if ((grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED))
889 return STG_E_INVALIDFUNCTION;
890
891 /* Can't create a stream on read-only storage */
892 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
893 return STG_E_ACCESSDENIED;
894
895 /*
896 * Check that we're compatible with the parent's storage mode
897 * if not in transacted mode
898 */
899 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
900 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
901 return STG_E_ACCESSDENIED;
902 }
903
904 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
905 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
906
907 *ppstm = 0;
908
909 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
910 This->rootPropertySetIndex);
911
912 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
913 pwcsName,
914 ¤tProperty);
915
916 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
917
918 if (foundPropertyIndex != PROPERTY_NULL)
919 {
920 /*
921 * An element with this name already exists
922 */
923 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
924 {
925 StgStreamImpl *strm;
926
927 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
928 {
929 if (strm->ownerProperty == foundPropertyIndex)
930 {
931 TRACE("Stream deleted %p\n", strm);
932 strm->parentStorage = NULL;
933 list_remove(&strm->StrmListEntry);
934 }
935 }
936 IStorage_DestroyElement(iface, pwcsName);
937 }
938 else
939 return STG_E_FILEALREADYEXISTS;
940 }
941 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
942 {
943 WARN("read-only storage\n");
944 return STG_E_ACCESSDENIED;
945 }
946
947 /*
948 * memset the empty property
949 */
950 memset(&newStreamProperty, 0, sizeof(StgProperty));
951
952 newStreamProperty.sizeOfNameString =
953 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
954
955 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
956 return STG_E_INVALIDNAME;
957
958 strcpyW(newStreamProperty.name, pwcsName);
959
960 newStreamProperty.propertyType = PROPTYPE_STREAM;
961 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
962 newStreamProperty.size.u.LowPart = 0;
963 newStreamProperty.size.u.HighPart = 0;
964
965 newStreamProperty.previousProperty = PROPERTY_NULL;
966 newStreamProperty.nextProperty = PROPERTY_NULL;
967 newStreamProperty.dirProperty = PROPERTY_NULL;
968
969 /* call CoFileTime to get the current time
970 newStreamProperty.timeStampS1
971 newStreamProperty.timeStampD1
972 newStreamProperty.timeStampS2
973 newStreamProperty.timeStampD2
974 */
975
976 /* newStreamProperty.propertyUniqueID */
977
978 /*
979 * Get a free property or create a new one
980 */
981 newPropertyIndex = getFreeProperty(This->ancestorStorage);
982
983 /*
984 * Save the new property into the new property spot
985 */
986 StorageImpl_WriteProperty(
987 This->ancestorStorage,
988 newPropertyIndex,
989 &newStreamProperty);
990
991 /*
992 * Find a spot in the property chain for our newly created property.
993 */
994 updatePropertyChain(
995 (StorageImpl*)This,
996 newPropertyIndex,
997 newStreamProperty);
998
999 /*
1000 * Open the stream to return it.
1001 */
1002 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1003
1004 if (newStream != 0)
1005 {
1006 *ppstm = (IStream*)newStream;
1007
1008 IStream_AddRef(*ppstm);
1009 }
1010 else
1011 {
1012 return STG_E_INSUFFICIENTMEMORY;
1013 }
1014
1015 return S_OK;
1016 }
1017
1018 /************************************************************************
1019 * Storage32BaseImpl_SetClass (IStorage)
1020 *
1021 * This method will write the specified CLSID in the property of this
1022 * storage.
1023 *
1024 * See Windows documentation for more details on IStorage methods.
1025 */
1026 static HRESULT WINAPI StorageBaseImpl_SetClass(
1027 IStorage* iface,
1028 REFCLSID clsid) /* [in] */
1029 {
1030 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1031 HRESULT hRes = E_FAIL;
1032 StgProperty curProperty;
1033 BOOL success;
1034
1035 TRACE("(%p, %p)\n", iface, clsid);
1036
1037 success = StorageImpl_ReadProperty(This->ancestorStorage,
1038 This->rootPropertySetIndex,
1039 &curProperty);
1040 if (success)
1041 {
1042 curProperty.propertyUniqueID = *clsid;
1043
1044 success = StorageImpl_WriteProperty(This->ancestorStorage,
1045 This->rootPropertySetIndex,
1046 &curProperty);
1047 if (success)
1048 hRes = S_OK;
1049 }
1050
1051 return hRes;
1052 }
1053
1054 /************************************************************************
1055 ** Storage32Impl implementation
1056 */
1057
1058 /************************************************************************
1059 * Storage32Impl_CreateStorage (IStorage)
1060 *
1061 * This method will create the storage object within the provided storage.
1062 *
1063 * See Windows documentation for more details on IStorage methods.
1064 */
1065 static HRESULT WINAPI StorageImpl_CreateStorage(
1066 IStorage* iface,
1067 const OLECHAR *pwcsName, /* [string][in] */
1068 DWORD grfMode, /* [in] */
1069 DWORD reserved1, /* [in] */
1070 DWORD reserved2, /* [in] */
1071 IStorage **ppstg) /* [out] */
1072 {
1073 StorageImpl* const This=(StorageImpl*)iface;
1074
1075 IEnumSTATSTGImpl *propertyEnumeration;
1076 StgProperty currentProperty;
1077 StgProperty newProperty;
1078 ULONG foundPropertyIndex;
1079 ULONG newPropertyIndex;
1080 HRESULT hr;
1081
1082 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1083 iface, debugstr_w(pwcsName), grfMode,
1084 reserved1, reserved2, ppstg);
1085
1086 if (ppstg == 0)
1087 return STG_E_INVALIDPOINTER;
1088
1089 if (pwcsName == 0)
1090 return STG_E_INVALIDNAME;
1091
1092 *ppstg = NULL;
1093
1094 if ( FAILED( validateSTGM(grfMode) ) ||
1095 (grfMode & STGM_DELETEONRELEASE) )
1096 {
1097 WARN("bad grfMode: 0x%x\n", grfMode);
1098 return STG_E_INVALIDFLAG;
1099 }
1100
1101 /*
1102 * Check that we're compatible with the parent's storage mode
1103 */
1104 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1105 {
1106 WARN("access denied\n");
1107 return STG_E_ACCESSDENIED;
1108 }
1109
1110 /*
1111 * Create a property enumeration and search the properties
1112 */
1113 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1114 This->base.rootPropertySetIndex);
1115
1116 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1117 pwcsName,
1118 ¤tProperty);
1119 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1120
1121 if (foundPropertyIndex != PROPERTY_NULL)
1122 {
1123 /*
1124 * An element with this name already exists
1125 */
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1128 {
1129 hr = IStorage_DestroyElement(iface, pwcsName);
1130 if (FAILED(hr))
1131 return hr;
1132 }
1133 else
1134 {
1135 WARN("file already exists\n");
1136 return STG_E_FILEALREADYEXISTS;
1137 }
1138 }
1139 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1140 {
1141 WARN("read-only storage\n");
1142 return STG_E_ACCESSDENIED;
1143 }
1144
1145 /*
1146 * memset the empty property
1147 */
1148 memset(&newProperty, 0, sizeof(StgProperty));
1149
1150 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1151
1152 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1153 {
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME;
1156 }
1157
1158 strcpyW(newProperty.name, pwcsName);
1159
1160 newProperty.propertyType = PROPTYPE_STORAGE;
1161 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1162 newProperty.size.u.LowPart = 0;
1163 newProperty.size.u.HighPart = 0;
1164
1165 newProperty.previousProperty = PROPERTY_NULL;
1166 newProperty.nextProperty = PROPERTY_NULL;
1167 newProperty.dirProperty = PROPERTY_NULL;
1168
1169 /* call CoFileTime to get the current time
1170 newProperty.timeStampS1
1171 newProperty.timeStampD1
1172 newProperty.timeStampS2
1173 newProperty.timeStampD2
1174 */
1175
1176 /* newStorageProperty.propertyUniqueID */
1177
1178 /*
1179 * Obtain a free property in the property chain
1180 */
1181 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1182
1183 /*
1184 * Save the new property into the new property spot
1185 */
1186 StorageImpl_WriteProperty(
1187 This->base.ancestorStorage,
1188 newPropertyIndex,
1189 &newProperty);
1190
1191 /*
1192 * Find a spot in the property chain for our newly created property.
1193 */
1194 updatePropertyChain(
1195 This,
1196 newPropertyIndex,
1197 newProperty);
1198
1199 /*
1200 * Open it to get a pointer to return.
1201 */
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1203
1204 if( (hr != S_OK) || (*ppstg == NULL))
1205 {
1206 return hr;
1207 }
1208
1209
1210 return S_OK;
1211 }
1212
1213
1214 /***************************************************************************
1215 *
1216 * Internal Method
1217 *
1218 * Get a free property or create a new one.
1219 */
1220 static ULONG getFreeProperty(
1221 StorageImpl *storage)
1222 {
1223 ULONG currentPropertyIndex = 0;
1224 ULONG newPropertyIndex = PROPERTY_NULL;
1225 BOOL readSuccessful = TRUE;
1226 StgProperty currentProperty;
1227
1228 do
1229 {
1230 /*
1231 * Start by reading the root property
1232 */
1233 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1234 currentPropertyIndex,
1235 ¤tProperty);
1236 if (readSuccessful)
1237 {
1238 if (currentProperty.sizeOfNameString == 0)
1239 {
1240 /*
1241 * The property existis and is available, we found it.
1242 */
1243 newPropertyIndex = currentPropertyIndex;
1244 }
1245 }
1246 else
1247 {
1248 /*
1249 * We exhausted the property list, we will create more space below
1250 */
1251 newPropertyIndex = currentPropertyIndex;
1252 }
1253 currentPropertyIndex++;
1254
1255 } while (newPropertyIndex == PROPERTY_NULL);
1256
1257 /*
1258 * grow the property chain
1259 */
1260 if (! readSuccessful)
1261 {
1262 StgProperty emptyProperty;
1263 ULARGE_INTEGER newSize;
1264 ULONG propertyIndex;
1265 ULONG lastProperty = 0;
1266 ULONG blockCount = 0;
1267
1268 /*
1269 * obtain the new count of property blocks
1270 */
1271 blockCount = BlockChainStream_GetCount(
1272 storage->base.ancestorStorage->rootBlockChain)+1;
1273
1274 /*
1275 * initialize the size used by the property stream
1276 */
1277 newSize.u.HighPart = 0;
1278 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1279
1280 /*
1281 * add a property block to the property chain
1282 */
1283 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1284
1285 /*
1286 * memset the empty property in order to initialize the unused newly
1287 * created property
1288 */
1289 memset(&emptyProperty, 0, sizeof(StgProperty));
1290
1291 /*
1292 * initialize them
1293 */
1294 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1295
1296 for(
1297 propertyIndex = newPropertyIndex;
1298 propertyIndex < lastProperty;
1299 propertyIndex++)
1300 {
1301 StorageImpl_WriteProperty(
1302 storage->base.ancestorStorage,
1303 propertyIndex,
1304 &emptyProperty);
1305 }
1306 }
1307
1308 return newPropertyIndex;
1309 }
1310
1311 /****************************************************************************
1312 *
1313 * Internal Method
1314 *
1315 * Case insensitive comparison of StgProperty.name by first considering
1316 * their size.
1317 *
1318 * Returns <0 when newProperty < currentProperty
1319 * >0 when newProperty > currentProperty
1320 * 0 when newProperty == currentProperty
1321 */
1322 static LONG propertyNameCmp(
1323 const OLECHAR *newProperty,
1324 const OLECHAR *currentProperty)
1325 {
1326 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1327
1328 if (diff == 0)
1329 {
1330 /*
1331 * We compare the string themselves only when they are of the same length
1332 */
1333 diff = lstrcmpiW( newProperty, currentProperty);
1334 }
1335
1336 return diff;
1337 }
1338
1339 /****************************************************************************
1340 *
1341 * Internal Method
1342 *
1343 * Properly link this new element in the property chain.
1344 */
1345 static void updatePropertyChain(
1346 StorageImpl *storage,
1347 ULONG newPropertyIndex,
1348 StgProperty newProperty)
1349 {
1350 StgProperty currentProperty;
1351
1352 /*
1353 * Read the root property
1354 */
1355 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1356 storage->base.rootPropertySetIndex,
1357 ¤tProperty);
1358
1359 if (currentProperty.dirProperty != PROPERTY_NULL)
1360 {
1361 /*
1362 * The root storage contains some element, therefore, start the research
1363 * for the appropriate location.
1364 */
1365 BOOL found = 0;
1366 ULONG current, next, previous, currentPropertyId;
1367
1368 /*
1369 * Keep the StgProperty sequence number of the storage first property
1370 */
1371 currentPropertyId = currentProperty.dirProperty;
1372
1373 /*
1374 * Read
1375 */
1376 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1377 currentProperty.dirProperty,
1378 ¤tProperty);
1379
1380 previous = currentProperty.previousProperty;
1381 next = currentProperty.nextProperty;
1382 current = currentPropertyId;
1383
1384 while (found == 0)
1385 {
1386 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1387
1388 if (diff < 0)
1389 {
1390 if (previous != PROPERTY_NULL)
1391 {
1392 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1393 previous,
1394 ¤tProperty);
1395 current = previous;
1396 }
1397 else
1398 {
1399 currentProperty.previousProperty = newPropertyIndex;
1400 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1401 current,
1402 ¤tProperty);
1403 found = 1;
1404 }
1405 }
1406 else if (diff > 0)
1407 {
1408 if (next != PROPERTY_NULL)
1409 {
1410 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1411 next,
1412 ¤tProperty);
1413 current = next;
1414 }
1415 else
1416 {
1417 currentProperty.nextProperty = newPropertyIndex;
1418 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1419 current,
1420 ¤tProperty);
1421 found = 1;
1422 }
1423 }
1424 else
1425 {
1426 /*
1427 * Trying to insert an item with the same name in the
1428 * subtree structure.
1429 */
1430 assert(FALSE);
1431 }
1432
1433 previous = currentProperty.previousProperty;
1434 next = currentProperty.nextProperty;
1435 }
1436 }
1437 else
1438 {
1439 /*
1440 * The root storage is empty, link the new property to its dir property
1441 */
1442 currentProperty.dirProperty = newPropertyIndex;
1443 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1444 storage->base.rootPropertySetIndex,
1445 ¤tProperty);
1446 }
1447 }
1448
1449
1450 /*************************************************************************
1451 * CopyTo (IStorage)
1452 */
1453 static HRESULT WINAPI StorageImpl_CopyTo(
1454 IStorage* iface,
1455 DWORD ciidExclude, /* [in] */
1456 const IID* rgiidExclude, /* [size_is][unique][in] */
1457 SNB snbExclude, /* [unique][in] */
1458 IStorage* pstgDest) /* [unique][in] */
1459 {
1460 IEnumSTATSTG *elements = 0;
1461 STATSTG curElement, strStat;
1462 HRESULT hr;
1463 IStorage *pstgTmp, *pstgChild;
1464 IStream *pstrTmp, *pstrChild;
1465 BOOL skip = FALSE;
1466
1467 if ((ciidExclude != 0) || (rgiidExclude != NULL))
1468 FIXME("Exclude option not implemented\n");
1469
1470 TRACE("(%p, %d, %p, %p, %p)\n",
1471 iface, ciidExclude, rgiidExclude,
1472 snbExclude, pstgDest);
1473
1474 if ( pstgDest == 0 )
1475 return STG_E_INVALIDPOINTER;
1476
1477 /*
1478 * Enumerate the elements
1479 */
1480 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1481
1482 if ( hr != S_OK )
1483 return hr;
1484
1485 /*
1486 * set the class ID
1487 */
1488 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1489 IStorage_SetClass( pstgDest, &curElement.clsid );
1490
1491 do
1492 {
1493 /*
1494 * Obtain the next element
1495 */
1496 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1497
1498 if ( hr == S_FALSE )
1499 {
1500 hr = S_OK; /* done, every element has been copied */
1501 break;
1502 }
1503
1504 if ( snbExclude )
1505 {
1506 WCHAR **snb = snbExclude;
1507 skip = FALSE;
1508 while ( *snb != NULL && !skip )
1509 {
1510 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1511 skip = TRUE;
1512 ++snb;
1513 }
1514 }
1515
1516 if ( skip )
1517 continue;
1518
1519 if (curElement.type == STGTY_STORAGE)
1520 {
1521 /*
1522 * open child source storage
1523 */
1524 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1525 STGM_READ|STGM_SHARE_EXCLUSIVE,
1526 NULL, 0, &pstgChild );
1527
1528 if (hr != S_OK)
1529 break;
1530
1531 /*
1532 * Check if destination storage is not a child of the source
1533 * storage, which will cause an infinite loop
1534 */
1535 if (pstgChild == pstgDest)
1536 {
1537 IEnumSTATSTG_Release(elements);
1538
1539 return STG_E_ACCESSDENIED;
1540 }
1541
1542 /*
1543 * create a new storage in destination storage
1544 */
1545 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1546 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1547 0, 0,
1548 &pstgTmp );
1549 /*
1550 * if it already exist, don't create a new one use this one
1551 */
1552 if (hr == STG_E_FILEALREADYEXISTS)
1553 {
1554 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1555 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1556 NULL, 0, &pstgTmp );
1557 }
1558
1559 if (hr != S_OK)
1560 break;
1561
1562
1563 /*
1564 * do the copy recursively
1565 */
1566 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1567 NULL, pstgTmp );
1568
1569 IStorage_Release( pstgTmp );
1570 IStorage_Release( pstgChild );
1571 }
1572 else if (curElement.type == STGTY_STREAM)
1573 {
1574 /*
1575 * create a new stream in destination storage. If the stream already
1576 * exist, it will be deleted and a new one will be created.
1577 */
1578 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1579 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1580 0, 0, &pstrTmp );
1581
1582 if (hr != S_OK)
1583 break;
1584
1585 /*
1586 * open child stream storage
1587 */
1588 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1589 STGM_READ|STGM_SHARE_EXCLUSIVE,
1590 0, &pstrChild );
1591
1592 if (hr != S_OK)
1593 break;
1594
1595 /*
1596 * Get the size of the source stream
1597 */
1598 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1599
1600 /*
1601 * Set the size of the destination stream.
1602 */
1603 IStream_SetSize(pstrTmp, strStat.cbSize);
1604
1605 /*
1606 * do the copy
1607 */
1608 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1609 NULL, NULL );
1610
1611 IStream_Release( pstrTmp );
1612 IStream_Release( pstrChild );
1613 }
1614 else
1615 {
1616 WARN("unknown element type: %d\n", curElement.type);
1617 }
1618
1619 } while (hr == S_OK);
1620
1621 /*
1622 * Clean-up
1623 */
1624 IEnumSTATSTG_Release(elements);
1625
1626 return hr;
1627 }
1628
1629 /*************************************************************************
1630 * MoveElementTo (IStorage)
1631 */
1632 static HRESULT WINAPI StorageImpl_MoveElementTo(
1633 IStorage* iface,
1634 const OLECHAR *pwcsName, /* [string][in] */
1635 IStorage *pstgDest, /* [unique][in] */
1636 const OLECHAR *pwcsNewName,/* [string][in] */
1637 DWORD grfFlags) /* [in] */
1638 {
1639 FIXME("(%p %s %p %s %u): stub\n", iface,
1640 debugstr_w(pwcsName), pstgDest,
1641 debugstr_w(pwcsNewName), grfFlags);
1642 return E_NOTIMPL;
1643 }
1644
1645 /*************************************************************************
1646 * Commit (IStorage)
1647 *
1648 * Ensures that any changes made to a storage object open in transacted mode
1649 * are reflected in the parent storage
1650 *
1651 * NOTES
1652 * Wine doesn't implement transacted mode, which seems to be a basic
1653 * optimization, so we can ignore this stub for now.
1654 */
1655 static HRESULT WINAPI StorageImpl_Commit(
1656 IStorage* iface,
1657 DWORD grfCommitFlags)/* [in] */
1658 {
1659 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1660 return S_OK;
1661 }
1662
1663 /*************************************************************************
1664 * Revert (IStorage)
1665 *
1666 * Discard all changes that have been made since the last commit operation
1667 */
1668 static HRESULT WINAPI StorageImpl_Revert(
1669 IStorage* iface)
1670 {
1671 FIXME("(%p): stub\n", iface);
1672 return E_NOTIMPL;
1673 }
1674
1675 /*************************************************************************
1676 * DestroyElement (IStorage)
1677 *
1678 * Strategy: This implementation is built this way for simplicity not for speed.
1679 * I always delete the topmost element of the enumeration and adjust
1680 * the deleted element pointer all the time. This takes longer to
1681 * do but allow to reinvoke DestroyElement whenever we encounter a
1682 * storage object. The optimisation resides in the usage of another
1683 * enumeration strategy that would give all the leaves of a storage
1684 * first. (postfix order)
1685 */
1686 static HRESULT WINAPI StorageImpl_DestroyElement(
1687 IStorage* iface,
1688 const OLECHAR *pwcsName)/* [string][in] */
1689 {
1690 StorageImpl* const This=(StorageImpl*)iface;
1691
1692 IEnumSTATSTGImpl* propertyEnumeration;
1693 HRESULT hr = S_OK;
1694 BOOL res;
1695 StgProperty propertyToDelete;
1696 StgProperty parentProperty;
1697 ULONG foundPropertyIndexToDelete;
1698 ULONG typeOfRelation;
1699 ULONG parentPropertyId = 0;
1700
1701 TRACE("(%p, %s)\n",
1702 iface, debugstr_w(pwcsName));
1703
1704 if (pwcsName==NULL)
1705 return STG_E_INVALIDPOINTER;
1706
1707 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1708 return STG_E_ACCESSDENIED;
1709
1710 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1711 This->base.ancestorStorage,
1712 This->base.rootPropertySetIndex);
1713
1714 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1715 propertyEnumeration,
1716 pwcsName,
1717 &propertyToDelete);
1718
1719 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1720
1721 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1722 {
1723 return STG_E_FILENOTFOUND;
1724 }
1725
1726 /*
1727 * Find the parent property of the property to delete (the one that
1728 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1729 * the parent is This. Otherwise, the parent is one of its sibling...
1730 */
1731
1732 /*
1733 * First, read This's StgProperty..
1734 */
1735 res = StorageImpl_ReadProperty(
1736 This->base.ancestorStorage,
1737 This->base.rootPropertySetIndex,
1738 &parentProperty);
1739
1740 assert(res);
1741
1742 /*
1743 * Second, check to see if by any chance the actual storage (This) is not
1744 * the parent of the property to delete... We never know...
1745 */
1746 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1747 {
1748 /*
1749 * Set data as it would have been done in the else part...
1750 */
1751 typeOfRelation = PROPERTY_RELATION_DIR;
1752 parentPropertyId = This->base.rootPropertySetIndex;
1753 }
1754 else
1755 {
1756 /*
1757 * Create a property enumeration to search the parent properties, and
1758 * delete it once done.
1759 */
1760 IEnumSTATSTGImpl* propertyEnumeration2;
1761
1762 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1763 This->base.ancestorStorage,
1764 This->base.rootPropertySetIndex);
1765
1766 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1767 propertyEnumeration2,
1768 foundPropertyIndexToDelete,
1769 &parentProperty,
1770 &parentPropertyId);
1771
1772 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1773 }
1774
1775 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1776 {
1777 hr = deleteStorageProperty(
1778 This,
1779 foundPropertyIndexToDelete,
1780 propertyToDelete);
1781 }
1782 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1783 {
1784 hr = deleteStreamProperty(
1785 This,
1786 foundPropertyIndexToDelete,
1787 propertyToDelete);
1788 }
1789
1790 if (hr!=S_OK)
1791 return hr;
1792
1793 /*
1794 * Adjust the property chain
1795 */
1796 hr = adjustPropertyChain(
1797 This,
1798 propertyToDelete,
1799 parentProperty,
1800 parentPropertyId,
1801 typeOfRelation);
1802
1803 return hr;
1804 }
1805
1806
1807 /************************************************************************
1808 * StorageImpl_Stat (IStorage)
1809 *
1810 * This method will retrieve information about this storage object.
1811 *
1812 * See Windows documentation for more details on IStorage methods.
1813 */
1814 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1815 STATSTG* pstatstg, /* [out] */
1816 DWORD grfStatFlag) /* [in] */
1817 {
1818 StorageImpl* const This = (StorageImpl*)iface;
1819 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1820
1821 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1822 {
1823 CoTaskMemFree(pstatstg->pwcsName);
1824 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1825 strcpyW(pstatstg->pwcsName, This->pwcsName);
1826 }
1827
1828 return result;
1829 }
1830
1831 /******************************************************************************
1832 * Internal stream list handlers
1833 */
1834
1835 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1836 {
1837 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1838 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1839 }
1840
1841 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1842 {
1843 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1844 list_remove(&(strm->StrmListEntry));
1845 }
1846
1847 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1848 {
1849 struct list *cur, *cur2;
1850 StgStreamImpl *strm=NULL;
1851
1852 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1853 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1854 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1855 strm->parentStorage = NULL;
1856 list_remove(cur);
1857 }
1858 }
1859
1860
1861 /*********************************************************************
1862 *
1863 * Internal Method
1864 *
1865 * Perform the deletion of a complete storage node
1866 *
1867 */
1868 static HRESULT deleteStorageProperty(
1869 StorageImpl *parentStorage,
1870 ULONG indexOfPropertyToDelete,
1871 StgProperty propertyToDelete)
1872 {
1873 IEnumSTATSTG *elements = 0;
1874 IStorage *childStorage = 0;
1875 STATSTG currentElement;
1876 HRESULT hr;
1877 HRESULT destroyHr = S_OK;
1878
1879 /*
1880 * Open the storage and enumerate it
1881 */
1882 hr = StorageBaseImpl_OpenStorage(
1883 (IStorage*)parentStorage,
1884 propertyToDelete.name,
1885 0,
1886 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1887 0,
1888 0,
1889 &childStorage);
1890
1891 if (hr != S_OK)
1892 {
1893 return hr;
1894 }
1895
1896 /*
1897 * Enumerate the elements
1898 */
1899 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1900
1901 do
1902 {
1903 /*
1904 * Obtain the next element
1905 */
1906 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1907 if (hr==S_OK)
1908 {
1909 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1910
1911 CoTaskMemFree(currentElement.pwcsName);
1912 }
1913
1914 /*
1915 * We need to Reset the enumeration every time because we delete elements
1916 * and the enumeration could be invalid
1917 */
1918 IEnumSTATSTG_Reset(elements);
1919
1920 } while ((hr == S_OK) && (destroyHr == S_OK));
1921
1922 /*
1923 * Invalidate the property by zeroing its name member.
1924 */
1925 propertyToDelete.sizeOfNameString = 0;
1926
1927 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1928 indexOfPropertyToDelete,
1929 &propertyToDelete);
1930
1931 IStorage_Release(childStorage);
1932 IEnumSTATSTG_Release(elements);
1933
1934 return destroyHr;
1935 }
1936
1937 /*********************************************************************
1938 *
1939 * Internal Method
1940 *
1941 * Perform the deletion of a stream node
1942 *
1943 */
1944 static HRESULT deleteStreamProperty(
1945 StorageImpl *parentStorage,
1946 ULONG indexOfPropertyToDelete,
1947 StgProperty propertyToDelete)
1948 {
1949 IStream *pis;
1950 HRESULT hr;
1951 ULARGE_INTEGER size;
1952
1953 size.u.HighPart = 0;
1954 size.u.LowPart = 0;
1955
1956 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1957 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1958
1959 if (hr!=S_OK)
1960 {
1961 return(hr);
1962 }
1963
1964 /*
1965 * Zap the stream
1966 */
1967 hr = IStream_SetSize(pis, size);
1968
1969 if(hr != S_OK)
1970 {
1971 return hr;
1972 }
1973
1974 /*
1975 * Release the stream object.
1976 */
1977 IStream_Release(pis);
1978
1979 /*
1980 * Invalidate the property by zeroing its name member.
1981 */
1982 propertyToDelete.sizeOfNameString = 0;
1983
1984 /*
1985 * Here we should re-read the property so we get the updated pointer
1986 * but since we are here to zap it, I don't do it...
1987 */
1988 StorageImpl_WriteProperty(
1989 parentStorage->base.ancestorStorage,
1990 indexOfPropertyToDelete,
1991 &propertyToDelete);
1992
1993 return S_OK;
1994 }
1995
1996 /*********************************************************************
1997 *
1998 * Internal Method
1999 *
2000 * Finds a placeholder for the StgProperty within the Storage
2001 *
2002 */
2003 static HRESULT findPlaceholder(
2004 StorageImpl *storage,
2005 ULONG propertyIndexToStore,
2006 ULONG storePropertyIndex,
2007 INT typeOfRelation)
2008 {
2009 StgProperty storeProperty;
2010 BOOL res = TRUE;
2011
2012 /*
2013 * Read the storage property
2014 */
2015 res = StorageImpl_ReadProperty(
2016 storage->base.ancestorStorage,
2017 storePropertyIndex,
2018 &storeProperty);
2019
2020 if(! res)
2021 {
2022 return E_FAIL;
2023 }
2024
2025 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2026 {
2027 if (storeProperty.previousProperty != PROPERTY_NULL)
2028 {
2029 return findPlaceholder(
2030 storage,
2031 propertyIndexToStore,
2032 storeProperty.previousProperty,
2033 typeOfRelation);
2034 }
2035 else
2036 {
2037 storeProperty.previousProperty = propertyIndexToStore;
2038 }
2039 }
2040 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2041 {
2042 if (storeProperty.nextProperty != PROPERTY_NULL)
2043 {
2044 return findPlaceholder(
2045 storage,
2046 propertyIndexToStore,
2047 storeProperty.nextProperty,
2048 typeOfRelation);
2049 }
2050 else
2051 {
2052 storeProperty.nextProperty = propertyIndexToStore;
2053 }
2054 }
2055 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2056 {
2057 if (storeProperty.dirProperty != PROPERTY_NULL)
2058 {
2059 return findPlaceholder(
2060 storage,
2061 propertyIndexToStore,
2062 storeProperty.dirProperty,
2063 typeOfRelation);
2064 }
2065 else
2066 {
2067 storeProperty.dirProperty = propertyIndexToStore;
2068 }
2069 }
2070
2071 res = StorageImpl_WriteProperty(
2072 storage->base.ancestorStorage,
2073 storePropertyIndex,
2074 &storeProperty);
2075
2076 if(!res)
2077 {
2078 return E_FAIL;
2079 }
2080
2081 return S_OK;
2082 }
2083
2084 /*************************************************************************
2085 *
2086 * Internal Method
2087 *
2088 * This method takes the previous and the next property link of a property
2089 * to be deleted and find them a place in the Storage.
2090 */
2091 static HRESULT adjustPropertyChain(
2092 StorageImpl *This,
2093 StgProperty propertyToDelete,
2094 StgProperty parentProperty,
2095 ULONG parentPropertyId,
2096 INT typeOfRelation)
2097 {
2098 ULONG newLinkProperty = PROPERTY_NULL;
2099 BOOL needToFindAPlaceholder = FALSE;
2100 ULONG storeNode = PROPERTY_NULL;
2101 ULONG toStoreNode = PROPERTY_NULL;
2102 INT relationType = 0;
2103 HRESULT hr = S_OK;
2104 BOOL res = TRUE;
2105
2106 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2107 {
2108 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2109 {
2110 /*
2111 * Set the parent previous to the property to delete previous
2112 */
2113 newLinkProperty = propertyToDelete.previousProperty;
2114
2115 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2116 {
2117 /*
2118 * We also need to find a storage for the other link, setup variables
2119 * to do this at the end...
2120 */
2121 needToFindAPlaceholder = TRUE;
2122 storeNode = propertyToDelete.previousProperty;
2123 toStoreNode = propertyToDelete.nextProperty;
2124 relationType = PROPERTY_RELATION_NEXT;
2125 }
2126 }
2127 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2128 {
2129 /*
2130 * Set the parent previous to the property to delete next
2131 */
2132 newLinkProperty = propertyToDelete.nextProperty;
2133 }
2134
2135 /*
2136 * Link it for real...
2137 */
2138 parentProperty.previousProperty = newLinkProperty;
2139
2140 }
2141 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2142 {
2143 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2144 {
2145 /*
2146 * Set the parent next to the property to delete next previous
2147 */
2148 newLinkProperty = propertyToDelete.previousProperty;
2149
2150 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2151 {
2152 /*
2153 * We also need to find a storage for the other link, setup variables
2154 * to do this at the end...
2155 */
2156 needToFindAPlaceholder = TRUE;
2157 storeNode = propertyToDelete.previousProperty;
2158 toStoreNode = propertyToDelete.nextProperty;
2159 relationType = PROPERTY_RELATION_NEXT;
2160 }
2161 }
2162 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2163 {
2164 /*
2165 * Set the parent next to the property to delete next
2166 */
2167 newLinkProperty = propertyToDelete.nextProperty;
2168 }
2169
2170 /*
2171 * Link it for real...
2172 */
2173 parentProperty.nextProperty = newLinkProperty;
2174 }
2175 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2176 {
2177 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2178 {
2179 /*
2180 * Set the parent dir to the property to delete previous
2181 */
2182 newLinkProperty = propertyToDelete.previousProperty;
2183
2184 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2185 {
2186 /*
2187 * We also need to find a storage for the other link, setup variables
2188 * to do this at the end...
2189 */
2190 needToFindAPlaceholder = TRUE;
2191 storeNode = propertyToDelete.previousProperty;
2192 toStoreNode = propertyToDelete.nextProperty;
2193 relationType = PROPERTY_RELATION_NEXT;
2194 }
2195 }
2196 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2197 {
2198 /*
2199 * Set the parent dir to the property to delete next
2200 */
2201 newLinkProperty = propertyToDelete.nextProperty;
2202 }
2203
2204 /*
2205 * Link it for real...
2206 */
2207 parentProperty.dirProperty = newLinkProperty;
2208 }
2209
2210 /*
2211 * Write back the parent property
2212 */
2213 res = StorageImpl_WriteProperty(
2214 This->base.ancestorStorage,
2215 parentPropertyId,
2216 &parentProperty);
2217 if(! res)
2218 {
2219 return E_FAIL;
2220 }
2221
2222 /*
2223 * If a placeholder is required for the other link, then, find one and
2224 * get out of here...
2225 */
2226 if (needToFindAPlaceholder)
2227 {
2228 hr = findPlaceholder(
2229 This,
2230 toStoreNode,
2231 storeNode,
2232 relationType);
2233 }
2234
2235 return hr;
2236 }
2237
2238
2239 /******************************************************************************
2240 * SetElementTimes (IStorage)
2241 */
2242 static HRESULT WINAPI StorageImpl_SetElementTimes(
2243 IStorage* iface,
2244 const OLECHAR *pwcsName,/* [string][in] */
2245 const FILETIME *pctime, /* [in] */
2246 const FILETIME *patime, /* [in] */
2247 const FILETIME *pmtime) /* [in] */
2248 {
2249 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2250 return S_OK;
2251 }
2252
2253 /******************************************************************************
2254 * SetStateBits (IStorage)
2255 */
2256 static HRESULT WINAPI StorageImpl_SetStateBits(
2257 IStorage* iface,
2258 DWORD grfStateBits,/* [in] */
2259 DWORD grfMask) /* [in] */
2260 {
2261 StorageImpl* const This = (StorageImpl*)iface;
2262 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2263 return S_OK;
2264 }
2265
2266 /*
2267 * Virtual function table for the IStorage32Impl class.
2268 */
2269 static const IStorageVtbl Storage32Impl_Vtbl =
2270 {
2271 StorageBaseImpl_QueryInterface,
2272 StorageBaseImpl_AddRef,
2273 StorageBaseImpl_Release,
2274 StorageBaseImpl_CreateStream,
2275 StorageBaseImpl_OpenStream,
2276 StorageImpl_CreateStorage,
2277 StorageBaseImpl_OpenStorage,
2278 StorageImpl_CopyTo,
2279 StorageImpl_MoveElementTo,
2280 StorageImpl_Commit,
2281 StorageImpl_Revert,
2282 StorageBaseImpl_EnumElements,
2283 StorageImpl_DestroyElement,
2284 StorageBaseImpl_RenameElement,
2285 StorageImpl_SetElementTimes,
2286 StorageBaseImpl_SetClass,
2287 StorageImpl_SetStateBits,
2288 StorageImpl_Stat
2289 };
2290
2291 static HRESULT StorageImpl_Construct(
2292 StorageImpl* This,
2293 HANDLE hFile,
2294 LPCOLESTR pwcsName,
2295 ILockBytes* pLkbyt,
2296 DWORD openFlags,
2297 BOOL fileBased,
2298 BOOL create)
2299 {
2300 HRESULT hr = S_OK;
2301 StgProperty currentProperty;
2302 BOOL readSuccessful;
2303 ULONG currentPropertyIndex;
2304
2305 if ( FAILED( validateSTGM(openFlags) ))
2306 return STG_E_INVALIDFLAG;
2307
2308 memset(This, 0, sizeof(StorageImpl));
2309
2310 list_init(&This->base.strmHead);
2311
2312 This->base.lpVtbl = &Storage32Impl_Vtbl;
2313 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2314 This->base.v_destructor = StorageImpl_Destroy;
2315 This->base.openFlags = (openFlags & ~STGM_CREATE);
2316 This->create = create;
2317
2318 /*
2319 * This is the top-level storage so initialize the ancestor pointer
2320 * to this.
2321 */
2322 This->base.ancestorStorage = This;
2323
2324 This->hFile = hFile;
2325
2326 if(pwcsName) {
2327 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2328 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2329 if (!This->pwcsName)
2330 return STG_E_INSUFFICIENTMEMORY;
2331 strcpyW(This->pwcsName, pwcsName);
2332 }
2333
2334 /*
2335 * Initialize the big block cache.
2336 */
2337 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2338 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2339 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2340 pLkbyt,
2341 openFlags,
2342 This->bigBlockSize,
2343 fileBased);
2344
2345 if (This->bigBlockFile == 0)
2346 return E_FAIL;
2347
2348 if (create)
2349 {
2350 ULARGE_INTEGER size;
2351 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2352
2353 /*
2354 * Initialize all header variables:
2355 * - The big block depot consists of one block and it is at block 0
2356 * - The properties start at block 1
2357 * - There is no small block depot
2358 */
2359 memset( This->bigBlockDepotStart,
2360 BLOCK_UNUSED,
2361 sizeof(This->bigBlockDepotStart));
2362
2363 This->bigBlockDepotCount = 1;
2364 This->bigBlockDepotStart[0] = 0;
2365 This->rootStartBlock = 1;
2366 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2367 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2368 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2369 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2370 This->extBigBlockDepotCount = 0;
2371
2372 StorageImpl_SaveFileHeader(This);
2373
2374 /*
2375 * Add one block for the big block depot and one block for the properties
2376 */
2377 size.u.HighPart = 0;
2378 size.u.LowPart = This->bigBlockSize * 3;
2379 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2380
2381 /*
2382 * Initialize the big block depot
2383 */
2384 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2385 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2386 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2387 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2388 }
2389 else
2390 {
2391 /*
2392 * Load the header for the file.
2393 */
2394 hr = StorageImpl_LoadFileHeader(This);
2395
2396 if (FAILED(hr))
2397 {
2398 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2399
2400 return hr;
2401 }
2402 }
2403
2404 /*
2405 * There is no block depot cached yet.
2406 */
2407 This->indexBlockDepotCached = 0xFFFFFFFF;
2408
2409 /*
2410 * Start searching for free blocks with block 0.
2411 */
2412 This->prevFreeBlock = 0;
2413
2414 /*
2415 * Create the block chain abstractions.
2416 */
2417 if(!(This->rootBlockChain =
2418 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2419 return STG_E_READFAULT;
2420
2421 if(!(This->smallBlockDepotChain =
2422 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2423 PROPERTY_NULL)))
2424 return STG_E_READFAULT;
2425
2426 /*
2427 * Write the root property (memory only)
2428 */
2429 if (create)
2430 {
2431 StgProperty rootProp;
2432 /*
2433 * Initialize the property chain
2434 */
2435 memset(&rootProp, 0, sizeof(rootProp));
2436 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2437 sizeof(rootProp.name)/sizeof(WCHAR) );
2438 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2439 rootProp.propertyType = PROPTYPE_ROOT;
2440 rootProp.previousProperty = PROPERTY_NULL;
2441 rootProp.nextProperty = PROPERTY_NULL;
2442 rootProp.dirProperty = PROPERTY_NULL;
2443 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2444 rootProp.size.u.HighPart = 0;
2445 rootProp.size.u.LowPart = 0;
2446
2447 StorageImpl_WriteProperty(This, 0, &rootProp);
2448 }
2449
2450 /*
2451 * Find the ID of the root in the property sets.
2452 */
2453 currentPropertyIndex = 0;
2454
2455 do
2456 {
2457 readSuccessful = StorageImpl_ReadProperty(
2458 This,
2459 currentPropertyIndex,
2460 ¤tProperty);
2461
2462 if (readSuccessful)
2463 {
2464 if ( (currentProperty.sizeOfNameString != 0 ) &&
2465 (currentProperty.propertyType == PROPTYPE_ROOT) )
2466 {
2467 This->base.rootPropertySetIndex = currentPropertyIndex;
2468 }
2469 }
2470
2471 currentPropertyIndex++;
2472
2473 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2474
2475 if (!readSuccessful)
2476 {
2477 /* TODO CLEANUP */
2478 return STG_E_READFAULT;
2479 }
2480
2481 /*
2482 * Create the block chain abstraction for the small block root chain.
2483 */
2484 if(!(This->smallBlockRootChain =
2485 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2486 return STG_E_READFAULT;
2487
2488 return hr;
2489 }
2490
2491 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2492 {
2493 StorageImpl *This = (StorageImpl*) iface;
2494 TRACE("(%p)\n", This);
2495
2496 StorageBaseImpl_DeleteAll(&This->base);
2497
2498 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2499
2500 BlockChainStream_Destroy(This->smallBlockRootChain);
2501 BlockChainStream_Destroy(This->rootBlockChain);
2502 BlockChainStream_Destroy(This->smallBlockDepotChain);
2503
2504 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2505 HeapFree(GetProcessHeap(), 0, This);
2506 }
2507
2508 /******************************************************************************
2509 * Storage32Impl_GetNextFreeBigBlock
2510 *
2511 * Returns the index of the next free big block.
2512 * If the big block depot is filled, this method will enlarge it.
2513 *
2514 */
2515 static ULONG StorageImpl_GetNextFreeBigBlock(
2516 StorageImpl* This)
2517 {
2518 ULONG depotBlockIndexPos;
2519 BYTE depotBuffer[BIG_BLOCK_SIZE];
2520 BOOL success;
2521 ULONG depotBlockOffset;
2522 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2523 ULONG nextBlockIndex = BLOCK_SPECIAL;
2524 int depotIndex = 0;
2525 ULONG freeBlock = BLOCK_UNUSED;
2526
2527 depotIndex = This->prevFreeBlock / blocksPerDepot;
2528 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2529
2530 /*
2531 * Scan the entire big block depot until we find a block marked free
2532 */
2533 while (nextBlockIndex != BLOCK_UNUSED)
2534 {
2535 if (depotIndex < COUNT_BBDEPOTINHEADER)
2536 {
2537 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2538
2539 /*
2540 * Grow the primary depot.
2541 */
2542 if (depotBlockIndexPos == BLOCK_UNUSED)
2543 {
2544 depotBlockIndexPos = depotIndex*blocksPerDepot;
2545
2546 /*
2547 * Add a block depot.
2548 */
2549 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2550 This->bigBlockDepotCount++;
2551 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2552
2553 /*
2554 * Flag it as a block depot.
2555 */
2556 StorageImpl_SetNextBlockInChain(This,
2557 depotBlockIndexPos,
2558 BLOCK_SPECIAL);
2559
2560 /* Save new header information.
2561 */
2562 StorageImpl_SaveFileHeader(This);
2563 }
2564 }
2565 else
2566 {
2567 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2568
2569 if (depotBlockIndexPos == BLOCK_UNUSED)
2570 {
2571 /*
2572 * Grow the extended depot.
2573 */
2574 ULONG extIndex = BLOCK_UNUSED;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2577
2578 if (extBlockOffset == 0)
2579 {
2580 /* We need an extended block.
2581 */
2582 extIndex = Storage32Impl_AddExtBlockDepot(This);
2583 This->extBigBlockDepotCount++;
2584 depotBlockIndexPos = extIndex + 1;
2585 }
2586 else
2587 depotBlockIndexPos = depotIndex * blocksPerDepot;
2588
2589 /*
2590 * Add a block depot and mark it in the extended block.
2591 */
2592 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2593 This->bigBlockDepotCount++;
2594 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2595
2596 /* Flag the block depot.
2597 */
2598 StorageImpl_SetNextBlockInChain(This,
2599 depotBlockIndexPos,
2600 BLOCK_SPECIAL);
2601
2602 /* If necessary, flag the extended depot block.
2603 */
2604 if (extIndex != BLOCK_UNUSED)
2605 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2606
2607 /* Save header information.
2608 */
2609 StorageImpl_SaveFileHeader(This);
2610 }
2611 }
2612
2613 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2614
2615 if (success)
2616 {
2617 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2618 ( nextBlockIndex != BLOCK_UNUSED))
2619 {
2620 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2621
2622 if (nextBlockIndex == BLOCK_UNUSED)
2623 {
2624 freeBlock = (depotIndex * blocksPerDepot) +
2625 (depotBlockOffset/sizeof(ULONG));
2626 }
2627
2628 depotBlockOffset += sizeof(ULONG);
2629 }
2630 }
2631
2632 depotIndex++;
2633 depotBlockOffset = 0;
2634 }
2635
2636 /*
2637 * make sure that the block physically exists before using it
2638 */
2639 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2640
2641 This->prevFreeBlock = freeBlock;
2642
2643 return freeBlock;
2644 }
2645
2646 /******************************************************************************
2647 * Storage32Impl_AddBlockDepot
2648 *
2649 * This will create a depot block, essentially it is a block initialized
2650 * to BLOCK_UNUSEDs.
2651 */
2652 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2653 {
2654 BYTE blockBuffer[BIG_BLOCK_SIZE];
2655
2656 /*
2657 * Initialize blocks as free
2658 */
2659 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2660 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2661 }
2662
2663 /******************************************************************************
2664 * Storage32Impl_GetExtDepotBlock
2665 *
2666 * Returns the index of the block that corresponds to the specified depot
2667 * index. This method is only for depot indexes equal or greater than
2668 * COUNT_BBDEPOTINHEADER.
2669 */
2670 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2671 {
2672 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2673 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2674 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2675 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2676 ULONG blockIndex = BLOCK_UNUSED;
2677 ULONG extBlockIndex = This->extBigBlockDepotStart;
2678
2679 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2680
2681 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2682 return BLOCK_UNUSED;
2683
2684 while (extBlockCount > 0)
2685 {
2686 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2687 extBlockCount--;
2688 }
2689
2690 if (extBlockIndex != BLOCK_UNUSED)
2691 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2692 extBlockOffset * sizeof(ULONG), &blockIndex);
2693
2694 return blockIndex;
2695 }
2696
2697 /******************************************************************************
2698 * Storage32Impl_SetExtDepotBlock
2699 *
2700 * Associates the specified block index to the specified depot index.
2701 * This method is only for depot indexes equal or greater than
2702 * COUNT_BBDEPOTINHEADER.
2703 */
2704 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2705 {
2706 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2707 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2708 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2709 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2710 ULONG extBlockIndex = This->extBigBlockDepotStart;
2711
2712 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2713
2714 while (extBlockCount > 0)
2715 {
2716 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2717 extBlockCount--;
2718 }
2719
2720 if (extBlockIndex != BLOCK_UNUSED)
2721 {
2722 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2723 extBlockOffset * sizeof(ULONG),
2724 blockIndex);
2725 }
2726 }
2727
2728 /******************************************************************************
2729 * Storage32Impl_AddExtBlockDepot
2730 *
2731 * Creates an extended depot block.
2732 */
2733 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2734 {
2735 ULONG numExtBlocks = This->extBigBlockDepotCount;
2736 ULONG nextExtBlock = This->extBigBlockDepotStart;
2737 BYTE depotBuffer[BIG_BLOCK_SIZE];
2738 ULONG index = BLOCK_UNUSED;
2739 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2740 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2741 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2742
2743 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2744 blocksPerDepotBlock;
2745
2746 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2747 {
2748 /*
2749 * The first extended block.
2750 */
2751 This->extBigBlockDepotStart = index;
2752 }
2753 else
2754 {
2755 unsigned int i;
2756 /*
2757 * Follow the chain to the last one.
2758 */
2759 for (i = 0; i < (numExtBlocks - 1); i++)
2760 {
2761 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2762 }
2763
2764 /*
2765 * Add the new extended block to the chain.
2766 */
2767 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2768 index);
2769 }
2770
2771 /*
2772 * Initialize this block.
2773 */
2774 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2775 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2776
2777 return index;
2778 }
2779
2780 /******************************************************************************
2781 * Storage32Impl_FreeBigBlock
2782 *
2783 * This method will flag the specified block as free in the big block depot.
2784 */
2785 static void StorageImpl_FreeBigBlock(
2786 StorageImpl* This,
2787 ULONG blockIndex)
2788 {
2789 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2790
2791 if (blockIndex < This->prevFreeBlock)
2792 This->prevFreeBlock = blockIndex;
2793 }
2794
2795 /************************************************************************
2796 * Storage32Impl_GetNextBlockInChain
2797 *
2798 * This method will retrieve the block index of the next big block in
2799 * in the chain.
2800 *
2801 * Params: This - Pointer to the Storage object.
2802 * blockIndex - Index of the block to retrieve the chain
2803 * for.
2804 * nextBlockIndex - receives the return value.
2805 *
2806 * Returns: This method returns the index of the next block in the chain.
2807 * It will return the constants:
2808 * BLOCK_SPECIAL - If the block given was not part of a
2809 * chain.
2810 * BLOCK_END_OF_CHAIN - If the block given was the last in
2811 * a chain.
2812 * BLOCK_UNUSED - If the block given was not past of a chain
2813 * and is available.
2814 * BLOCK_EXTBBDEPOT - This block is part of the extended
2815 * big block depot.
2816 *
2817 * See Windows documentation for more details on IStorage methods.
2818 */
2819 static HRESULT StorageImpl_GetNextBlockInChain(
2820 StorageImpl* This,
2821 ULONG blockIndex,
2822 ULONG* nextBlockIndex)
2823 {
2824 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2825 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2826 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2827 BYTE depotBuffer[BIG_BLOCK_SIZE];
2828 BOOL success;
2829 ULONG depotBlockIndexPos;
2830 int index;
2831
2832 *nextBlockIndex = BLOCK_SPECIAL;
2833
2834 if(depotBlockCount >= This->bigBlockDepotCount)
2835 {
2836 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2837 This->bigBlockDepotCount);
2838 return STG_E_READFAULT;
2839 }
2840
2841 /*
2842 * Cache the currently accessed depot block.
2843 */
2844 if (depotBlockCount != This->indexBlockDepotCached)
2845 {
2846 This->indexBlockDepotCached = depotBlockCount;
2847
2848 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2849 {
2850 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2851 }
2852 else
2853 {
2854 /*
2855 * We have to look in the extended depot.
2856 */
2857 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2858 }
2859
2860 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2861
2862 if (!success)
2863 return STG_E_READFAULT;
2864
2865 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2866 {
2867 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2868 This->blockDepotCached[index] = *nextBlockIndex;
2869 }
2870 }
2871
2872 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2873
2874 return S_OK;
2875 }
2876
2877 /******************************************************************************
2878 * Storage32Impl_GetNextExtendedBlock
2879 *
2880 * Given an extended block this method will return the next extended block.
2881 *
2882 * NOTES:
2883 * The last ULONG of an extended block is the block index of the next
2884 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2885 * depot.
2886 *
2887 * Return values:
2888 * - The index of the next extended block
2889 * - BLOCK_UNUSED: there is no next extended block.
2890 * - Any other return values denotes failure.
2891 */
2892 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2893 {
2894 ULONG nextBlockIndex = BLOCK_SPECIAL;
2895 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2896
2897 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2898 &nextBlockIndex);
2899
2900 return nextBlockIndex;
2901 }
2902
2903 /******************************************************************************
2904 * Storage32Impl_SetNextBlockInChain
2905 *
2906 * This method will write the index of the specified block's next block
2907 * in the big block depot.
2908 *
2909 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2910 * do the following
2911 *
2912 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2913 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2914 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2915 *
2916 */
2917 static void StorageImpl_SetNextBlockInChain(
2918 StorageImpl* This,
2919 ULONG blockIndex,
2920 ULONG nextBlock)
2921 {
2922 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2923 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2924 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2925 ULONG depotBlockIndexPos;
2926
2927 assert(depotBlockCount < This->bigBlockDepotCount);
2928 assert(blockIndex != nextBlock);
2929
2930 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2931 {
2932 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2933 }
2934 else
2935 {
2936 /*
2937 * We have to look in the extended depot.
2938 */
2939 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2940 }
2941
2942 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2943 nextBlock);
2944 /*
2945 * Update the cached block depot, if necessary.
2946 */
2947 if (depotBlockCount == This->indexBlockDepotCached)
2948 {
2949 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2950 }
2951 }
2952
2953 /******************************************************************************
2954 * Storage32Impl_LoadFileHeader
2955 *
2956 * This method will read in the file header, i.e. big block index -1.
2957 */
2958 static HRESULT StorageImpl_LoadFileHeader(
2959 StorageImpl* This)
2960 {
2961 HRESULT hr = STG_E_FILENOTFOUND;
2962 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2963 BOOL success;
2964 int index;
2965
2966 TRACE("\n");
2967 /*
2968 * Get a pointer to the big block of data containing the header.
2969 */
2970 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2971
2972 /*
2973 * Extract the information from the header.
2974 */
2975 if (success)
2976 {
2977 /*
2978 * Check for the "magic number" signature and return an error if it is not
2979 * found.
2980 */
2981 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2982 {
2983 return STG_E_OLDFORMAT;
2984 }
2985
2986 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2987 {
2988 return STG_E_INVALIDHEADER;
2989 }
2990
2991 StorageUtl_ReadWord(
2992 headerBigBlock,
2993 OFFSET_BIGBLOCKSIZEBITS,
2994 &This->bigBlockSizeBits);
2995
2996 StorageUtl_ReadWord(
2997 headerBigBlock,
2998 OFFSET_SMALLBLOCKSIZEBITS,
2999 &This->smallBlockSizeBits);
3000
3001 StorageUtl_ReadDWord(
3002 headerBigBlock,
3003 OFFSET_BBDEPOTCOUNT,
3004 &This->bigBlockDepotCount);
3005
3006 StorageUtl_ReadDWord(
3007 headerBigBlock,
3008 OFFSET_ROOTSTARTBLOCK,
3009 &This->rootStartBlock);
3010
3011 StorageUtl_ReadDWord(
3012 headerBigBlock,
3013 OFFSET_SBDEPOTSTART,
3014 &This->smallBlockDepotStart);
3015
3016 StorageUtl_ReadDWord(
3017 headerBigBlock,
3018 OFFSET_EXTBBDEPOTSTART,
3019 &This->extBigBlockDepotStart);
3020
3021 StorageUtl_ReadDWord(
3022 headerBigBlock,
3023 OFFSET_EXTBBDEPOTCOUNT,
3024 &This->extBigBlockDepotCount);
3025
3026 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3027 {
3028 StorageUtl_ReadDWord(
3029 headerBigBlock,
3030 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3031 &(This->bigBlockDepotStart[index]));
3032 }
3033
3034 /*
3035 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3036 */
3037 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3038 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3039
3040 /*
3041 * Right now, the code is making some assumptions about the size of the
3042 * blocks, just make sure they are what we're expecting.
3043 */
3044 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3045 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3046 {
3047 WARN("Broken OLE storage file\n");
3048 hr = STG_E_INVALIDHEADER;
3049 }
3050 else
3051 hr = S_OK;
3052 }
3053
3054 return hr;
3055 }
3056
3057 /******************************************************************************
3058 * Storage32Impl_SaveFileHeader
3059 *
3060 * This method will save to the file the header, i.e. big block -1.
3061 */
3062 static void StorageImpl_SaveFileHeader(
3063 StorageImpl* This)
3064 {
3065 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3066 int index;
3067 BOOL success;
3068
3069 /*
3070 * Get a pointer to the big block of data containing the header.
3071 */
3072 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3073
3074 /*
3075 * If the block read failed, the file is probably new.
3076 */
3077 if (!success)
3078 {
3079 /*
3080 * Initialize for all unknown fields.
3081 */
3082 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3083
3084 /*
3085 * Initialize the magic number.
3086 */
3087 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3088
3089 /*
3090 * And a bunch of things we don't know what they mean
3091 */
3092 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3093 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3094 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3095 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3096 }
3097
3098 /*
3099 * Write the information to the header.
3100 */
3101 StorageUtl_WriteWord(
3102 headerBigBlock,
3103 OFFSET_BIGBLOCKSIZEBITS,
3104 This->bigBlockSizeBits);
3105
3106 StorageUtl_WriteWord(
3107 headerBigBlock,
3108 OFFSET_SMALLBLOCKSIZEBITS,
3109 This->smallBlockSizeBits);
3110
3111 StorageUtl_WriteDWord(
3112 headerBigBlock,
3113 OFFSET_BBDEPOTCOUNT,
3114 This->bigBlockDepotCount);
3115
3116 StorageUtl_WriteDWord(
3117 headerBigBlock,
3118 OFFSET_ROOTSTARTBLOCK,
3119 This->rootStartBlock);
3120
3121 StorageUtl_WriteDWord(
3122 headerBigBlock,
3123 OFFSET_SBDEPOTSTART,
3124 This->smallBlockDepotStart);
3125
3126 StorageUtl_WriteDWord(
3127 headerBigBlock,
3128 OFFSET_SBDEPOTCOUNT,
3129 This->smallBlockDepotChain ?
3130 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3131
3132 StorageUtl_WriteDWord(
3133 headerBigBlock,
3134 OFFSET_EXTBBDEPOTSTART,
3135 This->extBigBlockDepotStart);
3136
3137 StorageUtl_WriteDWord(
3138 headerBigBlock,
3139 OFFSET_EXTBBDEPOTCOUNT,
3140 This->extBigBlockDepotCount);
3141
3142 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3143 {
3144 StorageUtl_WriteDWord(
3145 headerBigBlock,
3146 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3147 (This->bigBlockDepotStart[index]));
3148 }
3149
3150 /*
3151 * Write the big block back to the file.
3152 */
3153 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3154 }
3155
3156 /******************************************************************************
3157 * Storage32Impl_ReadProperty
3158 *
3159 * This method will read the specified property from the property chain.
3160 */
3161 BOOL StorageImpl_ReadProperty(
3162 StorageImpl* This,
3163 ULONG index,
3164 StgProperty* buffer)
3165 {
3166 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3167 ULARGE_INTEGER offsetInPropSet;
3168 HRESULT readRes;
3169 ULONG bytesRead;
3170
3171 offsetInPropSet.u.HighPart = 0;
3172 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3173
3174 readRes = BlockChainStream_ReadAt(
3175 This->rootBlockChain,
3176 offsetInPropSet,
3177 PROPSET_BLOCK_SIZE,
3178 currentProperty,
3179 &bytesRead);
3180
3181 if (SUCCEEDED(readRes))
3182 {
3183 /* replace the name of root entry (often "Root Entry") by the file name */
3184 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3185 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3186
3187 memset(buffer->name, 0, sizeof(buffer->name));
3188 memcpy(
3189 buffer->name,
3190 propName,
3191 PROPERTY_NAME_BUFFER_LEN );
3192 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3193
3194 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3195
3196 StorageUtl_ReadWord(
3197 currentProperty,
3198 OFFSET_PS_NAMELENGTH,
3199 &buffer->sizeOfNameString);
3200
3201 StorageUtl_ReadDWord(
3202 currentProperty,
3203 OFFSET_PS_PREVIOUSPROP,
3204 &buffer->previousProperty);
3205
3206 StorageUtl_ReadDWord(
3207 currentProperty,
3208 OFFSET_PS_NEXTPROP,
3209 &buffer->nextProperty);
3210
3211 StorageUtl_ReadDWord(
3212 currentProperty,
3213 OFFSET_PS_DIRPROP,
3214 &buffer->dirProperty);
3215
3216 StorageUtl_ReadGUID(
3217 currentProperty,
3218 OFFSET_PS_GUID,
3219 &buffer->propertyUniqueID);
3220
3221 StorageUtl_ReadDWord(
3222 currentProperty,
3223 OFFSET_PS_TSS1,
3224 &buffer->timeStampS1);
3225
3226 StorageUtl_ReadDWord(
3227 currentProperty,
3228 OFFSET_PS_TSD1,
3229 &buffer->timeStampD1);
3230
3231 StorageUtl_ReadDWord(
3232 currentProperty,
3233 OFFSET_PS_TSS2,
3234 &buffer->timeStampS2);
3235
3236 StorageUtl_ReadDWord(
3237 currentProperty,
3238 OFFSET_PS_TSD2,
3239 &buffer->timeStampD2);
3240
3241 StorageUtl_ReadDWord(
3242 currentProperty,
3243 OFFSET_PS_STARTBLOCK,
3244 &buffer->startingBlock);
3245
3246 StorageUtl_ReadDWord(
3247 currentProperty,
3248 OFFSET_PS_SIZE,
3249 &buffer->size.u.LowPart);
3250
3251 buffer->size.u.HighPart = 0;
3252 }
3253
3254 return SUCCEEDED(readRes) ? TRUE : FALSE;
3255 }
3256
3257 /*********************************************************************
3258 * Write the specified property into the property chain
3259 */
3260 BOOL StorageImpl_WriteProperty(
3261 StorageImpl* This,
3262 ULONG index,
3263 const StgProperty* buffer)
3264 {
3265 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3266 ULARGE_INTEGER offsetInPropSet;
3267 HRESULT writeRes;
3268 ULONG bytesWritten;
3269
3270 offsetInPropSet.u.HighPart = 0;
3271 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3272
3273 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3274
3275 memcpy(
3276 currentProperty + OFFSET_PS_NAME,
3277 buffer->name,
3278 PROPERTY_NAME_BUFFER_LEN );
3279
3280 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3281
3282 StorageUtl_WriteWord(
3283 currentProperty,
3284 OFFSET_PS_NAMELENGTH,
3285 buffer->sizeOfNameString);
3286
3287 StorageUtl_WriteDWord(
3288 currentProperty,
3289 OFFSET_PS_PREVIOUSPROP,
3290 buffer->previousProperty);
3291
3292 StorageUtl_WriteDWord(
3293 currentProperty,
3294 OFFSET_PS_NEXTPROP,
3295 buffer->nextProperty);
3296
3297 StorageUtl_WriteDWord(
3298 currentProperty,
3299 OFFSET_PS_DIRPROP,
3300 buffer->dirProperty);
3301
3302 StorageUtl_WriteGUID(
3303 currentProperty,
3304 OFFSET_PS_GUID,
3305 &buffer->propertyUniqueID);
3306
3307 StorageUtl_WriteDWord(
3308 currentProperty,
3309 OFFSET_PS_TSS1,
3310 buffer->timeStampS1);
3311
3312 StorageUtl_WriteDWord(
3313 currentProperty,
3314 OFFSET_PS_TSD1,
3315 buffer->timeStampD1);
3316
3317 StorageUtl_WriteDWord(
3318 currentProperty,
3319 OFFSET_PS_TSS2,
3320 buffer->timeStampS2);
3321
3322 StorageUtl_WriteDWord(
3323 currentProperty,
3324 OFFSET_PS_TSD2,
3325 buffer->timeStampD2);
3326
3327 StorageUtl_WriteDWord(
3328 currentProperty,
3329 OFFSET_PS_STARTBLOCK,
3330 buffer->startingBlock);
3331
3332 StorageUtl_WriteDWord(
3333 currentProperty,
3334 OFFSET_PS_SIZE,
3335 buffer->size.u.LowPart);
3336
3337 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3338 offsetInPropSet,
3339 PROPSET_BLOCK_SIZE,
3340 currentProperty,
3341 &bytesWritten);
3342 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3343 }
3344
3345 static BOOL StorageImpl_ReadBigBlock(
3346 StorageImpl* This,
3347 ULONG blockIndex,
3348 void* buffer)
3349 {
3350 ULARGE_INTEGER ulOffset;
3351 DWORD read;
3352
3353 ulOffset.u.HighPart = 0;
3354 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3355
3356 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3357 return (read == This->bigBlockSize);
3358 }
3359
3360 static BOOL StorageImpl_ReadDWordFromBigBlock(
3361 StorageImpl* This,
3362 ULONG blockIndex,
3363 ULONG offset,
3364 DWORD* value)
3365 {
3366 ULARGE_INTEGER ulOffset;
3367 DWORD read;
3368 DWORD tmp;
3369
3370 ulOffset.u.HighPart = 0;
3371 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3372 ulOffset.u.LowPart += offset;
3373
3374 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3375 *value = lendian32toh(tmp);
3376 return (read == sizeof(DWORD));
3377 }
3378
3379 static BOOL StorageImpl_WriteBigBlock(
3380 StorageImpl* This,
3381 ULONG blockIndex,
3382 const void* buffer)
3383 {
3384 ULARGE_INTEGER ulOffset;
3385 DWORD wrote;
3386
3387 ulOffset.u.HighPart = 0;
3388 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3389
3390 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3391 return (wrote == This->bigBlockSize);
3392 }
3393
3394 static BOOL StorageImpl_WriteDWordToBigBlock(
3395 StorageImpl* This,
3396 ULONG blockIndex,
3397 ULONG offset,
3398 DWORD value)
3399 {
3400 ULARGE_INTEGER ulOffset;
3401 DWORD wrote;
3402
3403 ulOffset.u.HighPart = 0;
3404 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3405 ulOffset.u.LowPart += offset;
3406
3407 value = htole32(value);
3408 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3409 return (wrote == sizeof(DWORD));
3410 }
3411
3412 /******************************************************************************
3413 * Storage32Impl_SmallBlocksToBigBlocks
3414 *
3415 * This method will convert a small block chain to a big block chain.
3416 * The small block chain will be destroyed.
3417 */
3418 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3419 StorageImpl* This,
3420 SmallBlockChainStream** ppsbChain)
3421 {
3422 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3423 ULARGE_INTEGER size, offset;
3424 ULONG cbRead, cbWritten;
3425 ULARGE_INTEGER cbTotalRead;
3426 ULONG propertyIndex;
3427 HRESULT resWrite = S_OK;
3428 HRESULT resRead;
3429 StgProperty chainProperty;
3430 BYTE *buffer;
3431 BlockChainStream *bbTempChain = NULL;
3432 BlockChainStream *bigBlockChain = NULL;
3433
3434 /*
3435 * Create a temporary big block chain that doesn't have
3436 * an associated property. This temporary chain will be
3437 * used to copy data from small blocks to big blocks.
3438 */
3439 bbTempChain = BlockChainStream_Construct(This,
3440 &bbHeadOfChain,
3441 PROPERTY_NULL);
3442 if(!bbTempChain) return NULL;
3443 /*
3444 * Grow the big block chain.
3445 */
3446 size = SmallBlockChainStream_GetSize(*ppsbChain);
3447 BlockChainStream_SetSize(bbTempChain, size);
3448
3449 /*
3450 * Copy the contents of the small block chain to the big block chain
3451 * by small block size increments.
3452 */
3453 offset.u.LowPart = 0;
3454 offset.u.HighPart = 0;
3455 cbTotalRead.QuadPart = 0;
3456
3457 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3458 do
3459 {
3460 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3461 offset,
3462 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3463 buffer,
3464 &cbRead);
3465 if (FAILED(resRead))
3466 break;
3467
3468 if (cbRead > 0)
3469 {
3470 cbTotalRead.QuadPart += cbRead;
3471
3472 resWrite = BlockChainStream_WriteAt(bbTempChain,
3473 offset,
3474 cbRead,
3475 buffer,
3476 &cbWritten);
3477
3478 if (FAILED(resWrite))
3479 break;
3480
3481 offset.u.LowPart += cbRead;
3482 }
3483 } while (cbTotalRead.QuadPart < size.QuadPart);
3484 HeapFree(GetProcessHeap(),0,buffer);
3485
3486 size.u.HighPart = 0;
3487 size.u.LowPart = 0;
3488
3489 if (FAILED(resRead) || FAILED(resWrite))
3490 {
3491 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3492 BlockChainStream_SetSize(bbTempChain, size);
3493 BlockChainStream_Destroy(bbTempChain);
3494 return NULL;
3495 }
3496
3497 /*
3498 * Destroy the small block chain.
3499 */
3500 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3501 SmallBlockChainStream_SetSize(*ppsbChain, size);
3502 SmallBlockChainStream_Destroy(*ppsbChain);
3503 *ppsbChain = 0;
3504
3505 /*
3506 * Change the property information. This chain is now a big block chain
3507 * and it doesn't reside in the small blocks chain anymore.
3508 */
3509 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3510
3511 chainProperty.startingBlock = bbHeadOfChain;
3512
3513 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3514
3515 /*
3516 * Destroy the temporary propertyless big block chain.
3517 * Create a new big block chain associated with this property.
3518 */
3519 BlockChainStream_Destroy(bbTempChain);
3520 bigBlockChain = BlockChainStream_Construct(This,
3521 NULL,
3522 propertyIndex);
3523
3524 return bigBlockChain;
3525 }
3526
3527 /******************************************************************************
3528 * Storage32Impl_BigBlocksToSmallBlocks
3529 *
3530 * This method will convert a big block chain to a small block chain.
3531 * The big block chain will be destroyed on success.
3532 */
3533 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3534 StorageImpl* This,
3535 BlockChainStream** ppbbChain)
3536 {
3537 ULARGE_INTEGER size, offset, cbTotalRead;
3538 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3539 HRESULT resWrite = S_OK, resRead;
3540 StgProperty chainProperty;
3541 BYTE* buffer;
3542 SmallBlockChainStream* sbTempChain;
3543
3544 TRACE("%p %p\n", This, ppbbChain);
3545
3546 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3547 PROPERTY_NULL);
3548
3549 if(!sbTempChain)
3550 return NULL;
3551
3552 size = BlockChainStream_GetSize(*ppbbChain);
3553 SmallBlockChainStream_SetSize(sbTempChain, size);
3554
3555 offset.u.HighPart = 0;
3556 offset.u.LowPart = 0;
3557 cbTotalRead.QuadPart = 0;
3558 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3559 do
3560 {
3561 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3562 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3563 buffer, &cbRead);
3564
3565 if(FAILED(resRead))
3566 break;
3567
3568 if(cbRead > 0)
3569 {
3570 cbTotalRead.QuadPart += cbRead;
3571
3572 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3573 cbRead, buffer, &cbWritten);
3574
3575 if(FAILED(resWrite))
3576 break;
3577
3578 offset.u.LowPart += cbRead;
3579 }
3580 }while(cbTotalRead.QuadPart < size.QuadPart);
3581 HeapFree(GetProcessHeap(), 0, buffer);
3582
3583 size.u.HighPart = 0;
3584 size.u.LowPart = 0;
3585
3586 if(FAILED(resRead) || FAILED(resWrite))
3587 {
3588 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3589 SmallBlockChainStream_SetSize(sbTempChain, size);
3590 SmallBlockChainStream_Destroy(sbTempChain);
3591 return NULL;
3592 }
3593
3594 /* destroy the original big block chain */
3595 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3596 BlockChainStream_SetSize(*ppbbChain, size);
3597 BlockChainStream_Destroy(*ppbbChain);
3598 *ppbbChain = NULL;
3599
3600 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3601 chainProperty.startingBlock = sbHeadOfChain;
3602 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3603
3604 SmallBlockChainStream_Destroy(sbTempChain);
3605 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3606 }
3607
3608 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3609 {
3610 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3611
3612 HeapFree(GetProcessHeap(), 0, This);
3613 }
3614
3615 /******************************************************************************
3616 **
3617 ** Storage32InternalImpl_Commit
3618 **
3619 */
3620 static HRESULT WINAPI StorageInternalImpl_Commit(
3621 IStorage* iface,
3622 DWORD grfCommitFlags) /* [in] */
3623 {
3624 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3625 return S_OK;
3626 }
3627
3628 /******************************************************************************
3629 **
3630 ** Storage32InternalImpl_Revert
3631 **
3632 */
3633 static HRESULT WINAPI StorageInternalImpl_Revert(
3634 IStorage* iface)
3635 {
3636 FIXME("(%p): stub\n", iface);
3637 return S_OK;
3638 }
3639
3640 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3641 {
3642 IStorage_Release((IStorage*)This->parentStorage);
3643 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3644 HeapFree(GetProcessHeap(), 0, This);
3645 }
3646
3647 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3648 IEnumSTATSTG* iface,
3649 REFIID riid,
3650 void** ppvObject)
3651 {
3652 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3653
3654 if (ppvObject==0)
3655 return E_INVALIDARG;
3656
3657 *ppvObject = 0;
3658
3659 if (IsEqualGUID(&IID_IUnknown, riid) ||
3660 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3661 {
3662 *ppvObject = This;
3663 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3664 return S_OK;
3665 }
3666
3667 return E_NOINTERFACE;
3668 }
3669
3670 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3671 IEnumSTATSTG* iface)