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