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 /* Can't create a stream on read-only storage */
983 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
984 return STG_E_ACCESSDENIED;
985
986 /*
987 * Check that we're compatible with the parent's storage mode
988 * if not in transacted mode
989 */
990 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
991 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
992 return STG_E_ACCESSDENIED;
993 }
994
995 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
996 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
997
998 /*
999 * Initialize the out parameter
1000 */
1001 *ppstm = 0;
1002
1003 /*
1004 * Create a property enumeration to search the properties
1005 */
1006 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1007 This->rootPropertySetIndex);
1008
1009 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1010 pwcsName,
1011 ¤tProperty);
1012
1013 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1014
1015 if (foundPropertyIndex != PROPERTY_NULL)
1016 {
1017 /*
1018 * An element with this name already exists
1019 */
1020 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1021 {
1022 StgStreamImpl *strm;
1023
1024 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1025 {
1026 if (strm->ownerProperty == foundPropertyIndex)
1027 {
1028 TRACE("Stream deleted %p\n", strm);
1029 strm->parentStorage = NULL;
1030 list_remove(&strm->StrmListEntry);
1031 }
1032 }
1033 IStorage_DestroyElement(iface, pwcsName);
1034 }
1035 else
1036 return STG_E_FILEALREADYEXISTS;
1037 }
1038 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1039 {
1040 WARN("read-only storage\n");
1041 return STG_E_ACCESSDENIED;
1042 }
1043
1044 /*
1045 * memset the empty property
1046 */
1047 memset(&newStreamProperty, 0, sizeof(StgProperty));
1048
1049 newStreamProperty.sizeOfNameString =
1050 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1051
1052 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1053 return STG_E_INVALIDNAME;
1054
1055 strcpyW(newStreamProperty.name, pwcsName);
1056
1057 newStreamProperty.propertyType = PROPTYPE_STREAM;
1058 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1059 newStreamProperty.size.u.LowPart = 0;
1060 newStreamProperty.size.u.HighPart = 0;
1061
1062 newStreamProperty.previousProperty = PROPERTY_NULL;
1063 newStreamProperty.nextProperty = PROPERTY_NULL;
1064 newStreamProperty.dirProperty = PROPERTY_NULL;
1065
1066 /* call CoFileTime to get the current time
1067 newStreamProperty.timeStampS1
1068 newStreamProperty.timeStampD1
1069 newStreamProperty.timeStampS2
1070 newStreamProperty.timeStampD2
1071 */
1072
1073 /* newStreamProperty.propertyUniqueID */
1074
1075 /*
1076 * Get a free property or create a new one
1077 */
1078 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1079
1080 /*
1081 * Save the new property into the new property spot
1082 */
1083 StorageImpl_WriteProperty(
1084 This->ancestorStorage,
1085 newPropertyIndex,
1086 &newStreamProperty);
1087
1088 /*
1089 * Find a spot in the property chain for our newly created property.
1090 */
1091 updatePropertyChain(
1092 (StorageImpl*)This,
1093 newPropertyIndex,
1094 newStreamProperty);
1095
1096 /*
1097 * Open the stream to return it.
1098 */
1099 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1100
1101 if (newStream != 0)
1102 {
1103 *ppstm = (IStream*)newStream;
1104
1105 /*
1106 * Since we are returning a pointer to the interface, we have to nail down
1107 * the reference.
1108 */
1109 IStream_AddRef(*ppstm);
1110 }
1111 else
1112 {
1113 return STG_E_INSUFFICIENTMEMORY;
1114 }
1115
1116 return S_OK;
1117 }
1118
1119 /************************************************************************
1120 * Storage32BaseImpl_SetClass (IStorage)
1121 *
1122 * This method will write the specified CLSID in the property of this
1123 * storage.
1124 *
1125 * See Windows documentation for more details on IStorage methods.
1126 */
1127 static HRESULT WINAPI StorageBaseImpl_SetClass(
1128 IStorage* iface,
1129 REFCLSID clsid) /* [in] */
1130 {
1131 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1132 HRESULT hRes = E_FAIL;
1133 StgProperty curProperty;
1134 BOOL success;
1135
1136 TRACE("(%p, %p)\n", iface, clsid);
1137
1138 success = StorageImpl_ReadProperty(This->ancestorStorage,
1139 This->rootPropertySetIndex,
1140 &curProperty);
1141 if (success)
1142 {
1143 curProperty.propertyUniqueID = *clsid;
1144
1145 success = StorageImpl_WriteProperty(This->ancestorStorage,
1146 This->rootPropertySetIndex,
1147 &curProperty);
1148 if (success)
1149 hRes = S_OK;
1150 }
1151
1152 return hRes;
1153 }
1154
1155 /************************************************************************
1156 ** Storage32Impl implementation
1157 */
1158
1159 /************************************************************************
1160 * Storage32Impl_CreateStorage (IStorage)
1161 *
1162 * This method will create the storage object within the provided storage.
1163 *
1164 * See Windows documentation for more details on IStorage methods.
1165 */
1166 static HRESULT WINAPI StorageImpl_CreateStorage(
1167 IStorage* iface,
1168 const OLECHAR *pwcsName, /* [string][in] */
1169 DWORD grfMode, /* [in] */
1170 DWORD reserved1, /* [in] */
1171 DWORD reserved2, /* [in] */
1172 IStorage **ppstg) /* [out] */
1173 {
1174 StorageImpl* const This=(StorageImpl*)iface;
1175
1176 IEnumSTATSTGImpl *propertyEnumeration;
1177 StgProperty currentProperty;
1178 StgProperty newProperty;
1179 ULONG foundPropertyIndex;
1180 ULONG newPropertyIndex;
1181 HRESULT hr;
1182
1183 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1184 iface, debugstr_w(pwcsName), grfMode,
1185 reserved1, reserved2, ppstg);
1186
1187 /*
1188 * Validate parameters
1189 */
1190 if (ppstg == 0)
1191 return STG_E_INVALIDPOINTER;
1192
1193 if (pwcsName == 0)
1194 return STG_E_INVALIDNAME;
1195
1196 /*
1197 * Initialize the out parameter
1198 */
1199 *ppstg = NULL;
1200
1201 /*
1202 * Validate the STGM flags
1203 */
1204 if ( FAILED( validateSTGM(grfMode) ) ||
1205 (grfMode & STGM_DELETEONRELEASE) )
1206 {
1207 WARN("bad grfMode: 0x%x\n", grfMode);
1208 return STG_E_INVALIDFLAG;
1209 }
1210
1211 /*
1212 * Check that we're compatible with the parent's storage mode
1213 */
1214 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1215 {
1216 WARN("access denied\n");
1217 return STG_E_ACCESSDENIED;
1218 }
1219
1220 /*
1221 * Create a property enumeration and search the properties
1222 */
1223 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1224 This->base.rootPropertySetIndex);
1225
1226 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1227 pwcsName,
1228 ¤tProperty);
1229 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1230
1231 if (foundPropertyIndex != PROPERTY_NULL)
1232 {
1233 /*
1234 * An element with this name already exists
1235 */
1236 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1237 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1238 {
1239 hr = IStorage_DestroyElement(iface, pwcsName);
1240 if (FAILED(hr))
1241 return hr;
1242 }
1243 else
1244 {
1245 WARN("file already exists\n");
1246 return STG_E_FILEALREADYEXISTS;
1247 }
1248 }
1249 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1250 {
1251 WARN("read-only storage\n");
1252 return STG_E_ACCESSDENIED;
1253 }
1254
1255 /*
1256 * memset the empty property
1257 */
1258 memset(&newProperty, 0, sizeof(StgProperty));
1259
1260 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1261
1262 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1263 {
1264 FIXME("name too long\n");
1265 return STG_E_INVALIDNAME;
1266 }
1267
1268 strcpyW(newProperty.name, pwcsName);
1269
1270 newProperty.propertyType = PROPTYPE_STORAGE;
1271 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1272 newProperty.size.u.LowPart = 0;
1273 newProperty.size.u.HighPart = 0;
1274
1275 newProperty.previousProperty = PROPERTY_NULL;
1276 newProperty.nextProperty = PROPERTY_NULL;
1277 newProperty.dirProperty = PROPERTY_NULL;
1278
1279 /* call CoFileTime to get the current time
1280 newProperty.timeStampS1
1281 newProperty.timeStampD1
1282 newProperty.timeStampS2
1283 newProperty.timeStampD2
1284 */
1285
1286 /* newStorageProperty.propertyUniqueID */
1287
1288 /*
1289 * Obtain a free property in the property chain
1290 */
1291 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1292
1293 /*
1294 * Save the new property into the new property spot
1295 */
1296 StorageImpl_WriteProperty(
1297 This->base.ancestorStorage,
1298 newPropertyIndex,
1299 &newProperty);
1300
1301 /*
1302 * Find a spot in the property chain for our newly created property.
1303 */
1304 updatePropertyChain(
1305 This,
1306 newPropertyIndex,
1307 newProperty);
1308
1309 /*
1310 * Open it to get a pointer to return.
1311 */
1312 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1313
1314 if( (hr != S_OK) || (*ppstg == NULL))
1315 {
1316 return hr;
1317 }
1318
1319
1320 return S_OK;
1321 }
1322
1323
1324 /***************************************************************************
1325 *
1326 * Internal Method
1327 *
1328 * Get a free property or create a new one.
1329 */
1330 static ULONG getFreeProperty(
1331 StorageImpl *storage)
1332 {
1333 ULONG currentPropertyIndex = 0;
1334 ULONG newPropertyIndex = PROPERTY_NULL;
1335 BOOL readSuccessful = TRUE;
1336 StgProperty currentProperty;
1337
1338 do
1339 {
1340 /*
1341 * Start by reading the root property
1342 */
1343 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1344 currentPropertyIndex,
1345 ¤tProperty);
1346 if (readSuccessful)
1347 {
1348 if (currentProperty.sizeOfNameString == 0)
1349 {
1350 /*
1351 * The property existis and is available, we found it.
1352 */
1353 newPropertyIndex = currentPropertyIndex;
1354 }
1355 }
1356 else
1357 {
1358 /*
1359 * We exhausted the property list, we will create more space below
1360 */
1361 newPropertyIndex = currentPropertyIndex;
1362 }
1363 currentPropertyIndex++;
1364
1365 } while (newPropertyIndex == PROPERTY_NULL);
1366
1367 /*
1368 * grow the property chain
1369 */
1370 if (! readSuccessful)
1371 {
1372 StgProperty emptyProperty;
1373 ULARGE_INTEGER newSize;
1374 ULONG propertyIndex;
1375 ULONG lastProperty = 0;
1376 ULONG blockCount = 0;
1377
1378 /*
1379 * obtain the new count of property blocks
1380 */
1381 blockCount = BlockChainStream_GetCount(
1382 storage->base.ancestorStorage->rootBlockChain)+1;
1383
1384 /*
1385 * initialize the size used by the property stream
1386 */
1387 newSize.u.HighPart = 0;
1388 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1389
1390 /*
1391 * add a property block to the property chain
1392 */
1393 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1394
1395 /*
1396 * memset the empty property in order to initialize the unused newly
1397 * created property
1398 */
1399 memset(&emptyProperty, 0, sizeof(StgProperty));
1400
1401 /*
1402 * initialize them
1403 */
1404 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1405
1406 for(
1407 propertyIndex = newPropertyIndex;
1408 propertyIndex < lastProperty;
1409 propertyIndex++)
1410 {
1411 StorageImpl_WriteProperty(
1412 storage->base.ancestorStorage,
1413 propertyIndex,
1414 &emptyProperty);
1415 }
1416 }
1417
1418 return newPropertyIndex;
1419 }
1420
1421 /****************************************************************************
1422 *
1423 * Internal Method
1424 *
1425 * Case insensitive comparison of StgProperty.name by first considering
1426 * their size.
1427 *
1428 * Returns <0 when newProperty < currentProperty
1429 * >0 when newProperty > currentProperty
1430 * 0 when newProperty == currentProperty
1431 */
1432 static LONG propertyNameCmp(
1433 const OLECHAR *newProperty,
1434 const OLECHAR *currentProperty)
1435 {
1436 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1437
1438 if (diff == 0)
1439 {
1440 /*
1441 * We compare the string themselves only when they are of the same length
1442 */
1443 diff = lstrcmpiW( newProperty, currentProperty);
1444 }
1445
1446 return diff;
1447 }
1448
1449 /****************************************************************************
1450 *
1451 * Internal Method
1452 *
1453 * Properly link this new element in the property chain.
1454 */
1455 static void updatePropertyChain(
1456 StorageImpl *storage,
1457 ULONG newPropertyIndex,
1458 StgProperty newProperty)
1459 {
1460 StgProperty currentProperty;
1461
1462 /*
1463 * Read the root property
1464 */
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466 storage->base.rootPropertySetIndex,
1467 ¤tProperty);
1468
1469 if (currentProperty.dirProperty != PROPERTY_NULL)
1470 {
1471 /*
1472 * The root storage contains some element, therefore, start the research
1473 * for the appropriate location.
1474 */
1475 BOOL found = 0;
1476 ULONG current, next, previous, currentPropertyId;
1477
1478 /*
1479 * Keep the StgProperty sequence number of the storage first property
1480 */
1481 currentPropertyId = currentProperty.dirProperty;
1482
1483 /*
1484 * Read
1485 */
1486 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1487 currentProperty.dirProperty,
1488 ¤tProperty);
1489
1490 previous = currentProperty.previousProperty;
1491 next = currentProperty.nextProperty;
1492 current = currentPropertyId;
1493
1494 while (found == 0)
1495 {
1496 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1497
1498 if (diff < 0)
1499 {
1500 if (previous != PROPERTY_NULL)
1501 {
1502 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1503 previous,
1504 ¤tProperty);
1505 current = previous;
1506 }
1507 else
1508 {
1509 currentProperty.previousProperty = newPropertyIndex;
1510 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1511 current,
1512 ¤tProperty);
1513 found = 1;
1514 }
1515 }
1516 else if (diff > 0)
1517 {
1518 if (next != PROPERTY_NULL)
1519 {
1520 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1521 next,
1522 ¤tProperty);
1523 current = next;
1524 }
1525 else
1526 {
1527 currentProperty.nextProperty = newPropertyIndex;
1528 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1529 current,
1530 ¤tProperty);
1531 found = 1;
1532 }
1533 }
1534 else
1535 {
1536 /*
1537 * Trying to insert an item with the same name in the
1538 * subtree structure.
1539 */
1540 assert(FALSE);
1541 }
1542
1543 previous = currentProperty.previousProperty;
1544 next = currentProperty.nextProperty;
1545 }
1546 }
1547 else
1548 {
1549 /*
1550 * The root storage is empty, link the new property to its dir property
1551 */
1552 currentProperty.dirProperty = newPropertyIndex;
1553 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1554 storage->base.rootPropertySetIndex,
1555 ¤tProperty);
1556 }
1557 }
1558
1559
1560 /*************************************************************************
1561 * CopyTo (IStorage)
1562 */
1563 static HRESULT WINAPI StorageImpl_CopyTo(
1564 IStorage* iface,
1565 DWORD ciidExclude, /* [in] */
1566 const IID* rgiidExclude, /* [size_is][unique][in] */
1567 SNB snbExclude, /* [unique][in] */
1568 IStorage* pstgDest) /* [unique][in] */
1569 {
1570 IEnumSTATSTG *elements = 0;
1571 STATSTG curElement, strStat;
1572 HRESULT hr;
1573 IStorage *pstgTmp, *pstgChild;
1574 IStream *pstrTmp, *pstrChild;
1575
1576 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1577 FIXME("Exclude option not implemented\n");
1578
1579 TRACE("(%p, %d, %p, %p, %p)\n",
1580 iface, ciidExclude, rgiidExclude,
1581 snbExclude, pstgDest);
1582
1583 /*
1584 * Perform a sanity check
1585 */
1586 if ( pstgDest == 0 )
1587 return STG_E_INVALIDPOINTER;
1588
1589 /*
1590 * Enumerate the elements
1591 */
1592 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1593
1594 if ( hr != S_OK )
1595 return hr;
1596
1597 /*
1598 * set the class ID
1599 */
1600 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1601 IStorage_SetClass( pstgDest, &curElement.clsid );
1602
1603 do
1604 {
1605 /*
1606 * Obtain the next element
1607 */
1608 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1609
1610 if ( hr == S_FALSE )
1611 {
1612 hr = S_OK; /* done, every element has been copied */
1613 break;
1614 }
1615
1616 if (curElement.type == STGTY_STORAGE)
1617 {
1618 /*
1619 * open child source storage
1620 */
1621 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1622 STGM_READ|STGM_SHARE_EXCLUSIVE,
1623 NULL, 0, &pstgChild );
1624
1625 if (hr != S_OK)
1626 break;
1627
1628 /*
1629 * Check if destination storage is not a child of the source
1630 * storage, which will cause an infinite loop
1631 */
1632 if (pstgChild == pstgDest)
1633 {
1634 IEnumSTATSTG_Release(elements);
1635
1636 return STG_E_ACCESSDENIED;
1637 }
1638
1639 /*
1640 * create a new storage in destination storage
1641 */
1642 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1643 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1644 0, 0,
1645 &pstgTmp );
1646 /*
1647 * if it already exist, don't create a new one use this one
1648 */
1649 if (hr == STG_E_FILEALREADYEXISTS)
1650 {
1651 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1652 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1653 NULL, 0, &pstgTmp );
1654 }
1655
1656 if (hr != S_OK)
1657 break;
1658
1659
1660 /*
1661 * do the copy recursively
1662 */
1663 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1664 snbExclude, pstgTmp );
1665
1666 IStorage_Release( pstgTmp );
1667 IStorage_Release( pstgChild );
1668 }
1669 else if (curElement.type == STGTY_STREAM)
1670 {
1671 /*
1672 * create a new stream in destination storage. If the stream already
1673 * exist, it will be deleted and a new one will be created.
1674 */
1675 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1676 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1677 0, 0, &pstrTmp );
1678
1679 if (hr != S_OK)
1680 break;
1681
1682 /*
1683 * open child stream storage
1684 */
1685 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1686 STGM_READ|STGM_SHARE_EXCLUSIVE,
1687 0, &pstrChild );
1688
1689 if (hr != S_OK)
1690 break;
1691
1692 /*
1693 * Get the size of the source stream
1694 */
1695 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1696
1697 /*
1698 * Set the size of the destination stream.
1699 */
1700 IStream_SetSize(pstrTmp, strStat.cbSize);
1701
1702 /*
1703 * do the copy
1704 */
1705 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1706 NULL, NULL );
1707
1708 IStream_Release( pstrTmp );
1709 IStream_Release( pstrChild );
1710 }
1711 else
1712 {
1713 WARN("unknown element type: %d\n", curElement.type);
1714 }
1715
1716 } while (hr == S_OK);
1717
1718 /*
1719 * Clean-up
1720 */
1721 IEnumSTATSTG_Release(elements);
1722
1723 return hr;
1724 }
1725
1726 /*************************************************************************
1727 * MoveElementTo (IStorage)
1728 */
1729 static HRESULT WINAPI StorageImpl_MoveElementTo(
1730 IStorage* iface,
1731 const OLECHAR *pwcsName, /* [string][in] */
1732 IStorage *pstgDest, /* [unique][in] */
1733 const OLECHAR *pwcsNewName,/* [string][in] */
1734 DWORD grfFlags) /* [in] */
1735 {
1736 FIXME("(%p %s %p %s %u): stub\n", iface,
1737 debugstr_w(pwcsName), pstgDest,
1738 debugstr_w(pwcsNewName), grfFlags);
1739 return E_NOTIMPL;
1740 }
1741
1742 /*************************************************************************
1743 * Commit (IStorage)
1744 *
1745 * Ensures that any changes made to a storage object open in transacted mode
1746 * are reflected in the parent storage
1747 *
1748 * NOTES
1749 * Wine doesn't implement transacted mode, which seems to be a basic
1750 * optimization, so we can ignore this stub for now.
1751 */
1752 static HRESULT WINAPI StorageImpl_Commit(
1753 IStorage* iface,
1754 DWORD grfCommitFlags)/* [in] */
1755 {
1756 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1757 return S_OK;
1758 }
1759
1760 /*************************************************************************
1761 * Revert (IStorage)
1762 *
1763 * Discard all changes that have been made since the last commit operation
1764 */
1765 static HRESULT WINAPI StorageImpl_Revert(
1766 IStorage* iface)
1767 {
1768 FIXME("(%p): stub\n", iface);
1769 return E_NOTIMPL;
1770 }
1771
1772 /*************************************************************************
1773 * DestroyElement (IStorage)
1774 *
1775 * Strategy: This implementation is built this way for simplicity not for speed.
1776 * I always delete the topmost element of the enumeration and adjust
1777 * the deleted element pointer all the time. This takes longer to
1778 * do but allow to reinvoke DestroyElement whenever we encounter a
1779 * storage object. The optimisation resides in the usage of another
1780 * enumeration strategy that would give all the leaves of a storage
1781 * first. (postfix order)
1782 */
1783 static HRESULT WINAPI StorageImpl_DestroyElement(
1784 IStorage* iface,
1785 const OLECHAR *pwcsName)/* [string][in] */
1786 {
1787 StorageImpl* const This=(StorageImpl*)iface;
1788
1789 IEnumSTATSTGImpl* propertyEnumeration;
1790 HRESULT hr = S_OK;
1791 BOOL res;
1792 StgProperty propertyToDelete;
1793 StgProperty parentProperty;
1794 ULONG foundPropertyIndexToDelete;
1795 ULONG typeOfRelation;
1796 ULONG parentPropertyId = 0;
1797
1798 TRACE("(%p, %s)\n",
1799 iface, debugstr_w(pwcsName));
1800
1801 /*
1802 * Perform a sanity check on the parameters.
1803 */
1804 if (pwcsName==NULL)
1805 return STG_E_INVALIDPOINTER;
1806
1807 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1808 return STG_E_ACCESSDENIED;
1809
1810 /*
1811 * Create a property enumeration to search the property with the given name
1812 */
1813 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1814 This->base.ancestorStorage,
1815 This->base.rootPropertySetIndex);
1816
1817 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1818 propertyEnumeration,
1819 pwcsName,
1820 &propertyToDelete);
1821
1822 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1823
1824 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1825 {
1826 return STG_E_FILENOTFOUND;
1827 }
1828
1829 /*
1830 * Find the parent property of the property to delete (the one that
1831 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1832 * the parent is This. Otherwise, the parent is one of its sibling...
1833 */
1834
1835 /*
1836 * First, read This's StgProperty..
1837 */
1838 res = StorageImpl_ReadProperty(
1839 This->base.ancestorStorage,
1840 This->base.rootPropertySetIndex,
1841 &parentProperty);
1842
1843 assert(res);
1844
1845 /*
1846 * Second, check to see if by any chance the actual storage (This) is not
1847 * the parent of the property to delete... We never know...
1848 */
1849 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1850 {
1851 /*
1852 * Set data as it would have been done in the else part...
1853 */
1854 typeOfRelation = PROPERTY_RELATION_DIR;
1855 parentPropertyId = This->base.rootPropertySetIndex;
1856 }
1857 else
1858 {
1859 /*
1860 * Create a property enumeration to search the parent properties, and
1861 * delete it once done.
1862 */
1863 IEnumSTATSTGImpl* propertyEnumeration2;
1864
1865 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1866 This->base.ancestorStorage,
1867 This->base.rootPropertySetIndex);
1868
1869 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1870 propertyEnumeration2,
1871 foundPropertyIndexToDelete,
1872 &parentProperty,
1873 &parentPropertyId);
1874
1875 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1876 }
1877
1878 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1879 {
1880 hr = deleteStorageProperty(
1881 This,
1882 foundPropertyIndexToDelete,
1883 propertyToDelete);
1884 }
1885 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1886 {
1887 hr = deleteStreamProperty(
1888 This,
1889 foundPropertyIndexToDelete,
1890 propertyToDelete);
1891 }
1892
1893 if (hr!=S_OK)
1894 return hr;
1895
1896 /*
1897 * Adjust the property chain
1898 */
1899 hr = adjustPropertyChain(
1900 This,
1901 propertyToDelete,
1902 parentProperty,
1903 parentPropertyId,
1904 typeOfRelation);
1905
1906 return hr;
1907 }
1908
1909
1910 /************************************************************************
1911 * StorageImpl_Stat (IStorage)
1912 *
1913 * This method will retrieve information about this storage object.
1914 *
1915 * See Windows documentation for more details on IStorage methods.
1916 */
1917 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1918 STATSTG* pstatstg, /* [out] */
1919 DWORD grfStatFlag) /* [in] */
1920 {
1921 StorageImpl* const This = (StorageImpl*)iface;
1922 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1923
1924 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1925 {
1926 CoTaskMemFree(pstatstg->pwcsName);
1927 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1928 strcpyW(pstatstg->pwcsName, This->pwcsName);
1929 }
1930
1931 return result;
1932 }
1933
1934 /******************************************************************************
1935 * Internal stream list handlers
1936 */
1937
1938 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1939 {
1940 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1941 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1942 }
1943
1944 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1945 {
1946 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1947 list_remove(&(strm->StrmListEntry));
1948 }
1949
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1951 {
1952 struct list *cur, *cur2;
1953 StgStreamImpl *strm=NULL;
1954
1955 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1956 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1957 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1958 strm->parentStorage = NULL;
1959 list_remove(cur);
1960 }
1961 }
1962
1963
1964 /*********************************************************************
1965 *
1966 * Internal Method
1967 *
1968 * Perform the deletion of a complete storage node
1969 *
1970 */
1971 static HRESULT deleteStorageProperty(
1972 StorageImpl *parentStorage,
1973 ULONG indexOfPropertyToDelete,
1974 StgProperty propertyToDelete)
1975 {
1976 IEnumSTATSTG *elements = 0;
1977 IStorage *childStorage = 0;
1978 STATSTG currentElement;
1979 HRESULT hr;
1980 HRESULT destroyHr = S_OK;
1981
1982 /*
1983 * Open the storage and enumerate it
1984 */
1985 hr = StorageBaseImpl_OpenStorage(
1986 (IStorage*)parentStorage,
1987 propertyToDelete.name,
1988 0,
1989 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1990 0,
1991 0,
1992 &childStorage);
1993
1994 if (hr != S_OK)
1995 {
1996 return hr;
1997 }
1998
1999 /*
2000 * Enumerate the elements
2001 */
2002 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2003
2004 do
2005 {
2006 /*
2007 * Obtain the next element
2008 */
2009 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2010 if (hr==S_OK)
2011 {
2012 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2013
2014 CoTaskMemFree(currentElement.pwcsName);
2015 }
2016
2017 /*
2018 * We need to Reset the enumeration every time because we delete elements
2019 * and the enumeration could be invalid
2020 */
2021 IEnumSTATSTG_Reset(elements);
2022
2023 } while ((hr == S_OK) && (destroyHr == S_OK));
2024
2025 /*
2026 * Invalidate the property by zeroing its name member.
2027 */
2028 propertyToDelete.sizeOfNameString = 0;
2029
2030 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2031 indexOfPropertyToDelete,
2032 &propertyToDelete);
2033
2034 IStorage_Release(childStorage);
2035 IEnumSTATSTG_Release(elements);
2036
2037 return destroyHr;
2038 }
2039
2040 /*********************************************************************
2041 *
2042 * Internal Method
2043 *
2044 * Perform the deletion of a stream node
2045 *
2046 */
2047 static HRESULT deleteStreamProperty(
2048 StorageImpl *parentStorage,
2049 ULONG indexOfPropertyToDelete,
2050 StgProperty propertyToDelete)
2051 {
2052 IStream *pis;
2053 HRESULT hr;
2054 ULARGE_INTEGER size;
2055
2056 size.u.HighPart = 0;
2057 size.u.LowPart = 0;
2058
2059 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2060 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2061
2062 if (hr!=S_OK)
2063 {
2064 return(hr);
2065 }
2066
2067 /*
2068 * Zap the stream
2069 */
2070 hr = IStream_SetSize(pis, size);
2071
2072 if(hr != S_OK)
2073 {
2074 return hr;
2075 }
2076
2077 /*
2078 * Release the stream object.
2079 */
2080 IStream_Release(pis);
2081
2082 /*
2083 * Invalidate the property by zeroing its name member.
2084 */
2085 propertyToDelete.sizeOfNameString = 0;
2086
2087 /*
2088 * Here we should re-read the property so we get the updated pointer
2089 * but since we are here to zap it, I don't do it...
2090 */
2091 StorageImpl_WriteProperty(
2092 parentStorage->base.ancestorStorage,
2093 indexOfPropertyToDelete,
2094 &propertyToDelete);
2095
2096 return S_OK;
2097 }
2098
2099 /*********************************************************************
2100 *
2101 * Internal Method
2102 *
2103 * Finds a placeholder for the StgProperty within the Storage
2104 *
2105 */
2106 static HRESULT findPlaceholder(
2107 StorageImpl *storage,
2108 ULONG propertyIndexToStore,
2109 ULONG storePropertyIndex,
2110 INT typeOfRelation)
2111 {
2112 StgProperty storeProperty;
2113 BOOL res = TRUE;
2114
2115 /*
2116 * Read the storage property
2117 */
2118 res = StorageImpl_ReadProperty(
2119 storage->base.ancestorStorage,
2120 storePropertyIndex,
2121 &storeProperty);
2122
2123 if(! res)
2124 {
2125 return E_FAIL;
2126 }
2127
2128 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2129 {
2130 if (storeProperty.previousProperty != PROPERTY_NULL)
2131 {
2132 return findPlaceholder(
2133 storage,
2134 propertyIndexToStore,
2135 storeProperty.previousProperty,
2136 typeOfRelation);
2137 }
2138 else
2139 {
2140 storeProperty.previousProperty = propertyIndexToStore;
2141 }
2142 }
2143 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2144 {
2145 if (storeProperty.nextProperty != PROPERTY_NULL)
2146 {
2147 return findPlaceholder(
2148 storage,
2149 propertyIndexToStore,
2150 storeProperty.nextProperty,
2151 typeOfRelation);
2152 }
2153 else
2154 {
2155 storeProperty.nextProperty = propertyIndexToStore;
2156 }
2157 }
2158 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2159 {
2160 if (storeProperty.dirProperty != PROPERTY_NULL)
2161 {
2162 return findPlaceholder(
2163 storage,
2164 propertyIndexToStore,
2165 storeProperty.dirProperty,
2166 typeOfRelation);
2167 }
2168 else
2169 {
2170 storeProperty.dirProperty = propertyIndexToStore;
2171 }
2172 }
2173
2174 res = StorageImpl_WriteProperty(
2175 storage->base.ancestorStorage,
2176 storePropertyIndex,
2177 &storeProperty);
2178
2179 if(!res)
2180 {
2181 return E_FAIL;
2182 }
2183
2184 return S_OK;
2185 }
2186
2187 /*************************************************************************
2188 *
2189 * Internal Method
2190 *
2191 * This method takes the previous and the next property link of a property
2192 * to be deleted and find them a place in the Storage.
2193 */
2194 static HRESULT adjustPropertyChain(
2195 StorageImpl *This,
2196 StgProperty propertyToDelete,
2197 StgProperty parentProperty,
2198 ULONG parentPropertyId,
2199 INT typeOfRelation)
2200 {
2201 ULONG newLinkProperty = PROPERTY_NULL;
2202 BOOL needToFindAPlaceholder = FALSE;
2203 ULONG storeNode = PROPERTY_NULL;
2204 ULONG toStoreNode = PROPERTY_NULL;
2205 INT relationType = 0;
2206 HRESULT hr = S_OK;
2207 BOOL res = TRUE;
2208
2209 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2210 {
2211 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2212 {
2213 /*
2214 * Set the parent previous to the property to delete previous
2215 */
2216 newLinkProperty = propertyToDelete.previousProperty;
2217
2218 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2219 {
2220 /*
2221 * We also need to find a storage for the other link, setup variables
2222 * to do this at the end...
2223 */
2224 needToFindAPlaceholder = TRUE;
2225 storeNode = propertyToDelete.previousProperty;
2226 toStoreNode = propertyToDelete.nextProperty;
2227 relationType = PROPERTY_RELATION_NEXT;
2228 }
2229 }
2230 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2231 {
2232 /*
2233 * Set the parent previous to the property to delete next
2234 */
2235 newLinkProperty = propertyToDelete.nextProperty;
2236 }
2237
2238 /*
2239 * Link it for real...
2240 */
2241 parentProperty.previousProperty = newLinkProperty;
2242
2243 }
2244 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2245 {
2246 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2247 {
2248 /*
2249 * Set the parent next to the property to delete next previous
2250 */
2251 newLinkProperty = propertyToDelete.previousProperty;
2252
2253 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2254 {
2255 /*
2256 * We also need to find a storage for the other link, setup variables
2257 * to do this at the end...
2258 */
2259 needToFindAPlaceholder = TRUE;
2260 storeNode = propertyToDelete.previousProperty;
2261 toStoreNode = propertyToDelete.nextProperty;
2262 relationType = PROPERTY_RELATION_NEXT;
2263 }
2264 }
2265 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2266 {
2267 /*
2268 * Set the parent next to the property to delete next
2269 */
2270 newLinkProperty = propertyToDelete.nextProperty;
2271 }
2272
2273 /*
2274 * Link it for real...
2275 */
2276 parentProperty.nextProperty = newLinkProperty;
2277 }
2278 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2279 {
2280 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2281 {
2282 /*
2283 * Set the parent dir to the property to delete previous
2284 */
2285 newLinkProperty = propertyToDelete.previousProperty;
2286
2287 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2288 {
2289 /*
2290 * We also need to find a storage for the other link, setup variables
2291 * to do this at the end...
2292 */
2293 needToFindAPlaceholder = TRUE;
2294 storeNode = propertyToDelete.previousProperty;
2295 toStoreNode = propertyToDelete.nextProperty;
2296 relationType = PROPERTY_RELATION_NEXT;
2297 }
2298 }
2299 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2300 {
2301 /*
2302 * Set the parent dir to the property to delete next
2303 */
2304 newLinkProperty = propertyToDelete.nextProperty;
2305 }
2306
2307 /*
2308 * Link it for real...
2309 */
2310 parentProperty.dirProperty = newLinkProperty;
2311 }
2312
2313 /*
2314 * Write back the parent property
2315 */
2316 res = StorageImpl_WriteProperty(
2317 This->base.ancestorStorage,
2318 parentPropertyId,
2319 &parentProperty);
2320 if(! res)
2321 {
2322 return E_FAIL;
2323 }
2324
2325 /*
2326 * If a placeholder is required for the other link, then, find one and
2327 * get out of here...
2328 */
2329 if (needToFindAPlaceholder)
2330 {
2331 hr = findPlaceholder(
2332 This,
2333 toStoreNode,
2334 storeNode,
2335 relationType);
2336 }
2337
2338 return hr;
2339 }
2340
2341
2342 /******************************************************************************
2343 * SetElementTimes (IStorage)
2344 */
2345 static HRESULT WINAPI StorageImpl_SetElementTimes(
2346 IStorage* iface,
2347 const OLECHAR *pwcsName,/* [string][in] */
2348 const FILETIME *pctime, /* [in] */
2349 const FILETIME *patime, /* [in] */
2350 const FILETIME *pmtime) /* [in] */
2351 {
2352 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2353 return S_OK;
2354 }
2355
2356 /******************************************************************************
2357 * SetStateBits (IStorage)
2358 */
2359 static HRESULT WINAPI StorageImpl_SetStateBits(
2360 IStorage* iface,
2361 DWORD grfStateBits,/* [in] */
2362 DWORD grfMask) /* [in] */
2363 {
2364 StorageImpl* const This = (StorageImpl*)iface;
2365 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2366 return S_OK;
2367 }
2368
2369 /*
2370 * Virtual function table for the IStorage32Impl class.
2371 */
2372 static const IStorageVtbl Storage32Impl_Vtbl =
2373 {
2374 StorageBaseImpl_QueryInterface,
2375 StorageBaseImpl_AddRef,
2376 StorageBaseImpl_Release,
2377 StorageBaseImpl_CreateStream,
2378 StorageBaseImpl_OpenStream,
2379 StorageImpl_CreateStorage,
2380 StorageBaseImpl_OpenStorage,
2381 StorageImpl_CopyTo,
2382 StorageImpl_MoveElementTo,
2383 StorageImpl_Commit,
2384 StorageImpl_Revert,
2385 StorageBaseImpl_EnumElements,
2386 StorageImpl_DestroyElement,
2387 StorageBaseImpl_RenameElement,
2388 StorageImpl_SetElementTimes,
2389 StorageBaseImpl_SetClass,
2390 StorageImpl_SetStateBits,
2391 StorageImpl_Stat
2392 };
2393
2394 static HRESULT StorageImpl_Construct(
2395 StorageImpl* This,
2396 HANDLE hFile,
2397 LPCOLESTR pwcsName,
2398 ILockBytes* pLkbyt,
2399 DWORD openFlags,
2400 BOOL fileBased,
2401 BOOL create)
2402 {
2403 HRESULT hr = S_OK;
2404 StgProperty currentProperty;
2405 BOOL readSuccessful;
2406 ULONG currentPropertyIndex;
2407
2408 if ( FAILED( validateSTGM(openFlags) ))
2409 return STG_E_INVALIDFLAG;
2410
2411 memset(This, 0, sizeof(StorageImpl));
2412
2413 list_init(&This->base.strmHead);
2414
2415 This->base.lpVtbl = &Storage32Impl_Vtbl;
2416 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2417 This->base.v_destructor = StorageImpl_Destroy;
2418 This->base.openFlags = (openFlags & ~STGM_CREATE);
2419 This->create = create;
2420
2421 /*
2422 * This is the top-level storage so initialize the ancestor pointer
2423 * to this.
2424 */
2425 This->base.ancestorStorage = This;
2426
2427 This->hFile = hFile;
2428
2429 if(pwcsName) {
2430 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2431 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2432 if (!This->pwcsName)
2433 return STG_E_INSUFFICIENTMEMORY;
2434 strcpyW(This->pwcsName, pwcsName);
2435 }
2436
2437 /*
2438 * Initialize the big block cache.
2439 */
2440 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2441 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2442 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2443 pLkbyt,
2444 openFlags,
2445 This->bigBlockSize,
2446 fileBased);
2447
2448 if (This->bigBlockFile == 0)
2449 return E_FAIL;
2450
2451 if (create)
2452 {
2453 ULARGE_INTEGER size;
2454 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2455
2456 /*
2457 * Initialize all header variables:
2458 * - The big block depot consists of one block and it is at block 0
2459 * - The properties start at block 1
2460 * - There is no small block depot
2461 */
2462 memset( This->bigBlockDepotStart,
2463 BLOCK_UNUSED,
2464 sizeof(This->bigBlockDepotStart));
2465
2466 This->bigBlockDepotCount = 1;
2467 This->bigBlockDepotStart[0] = 0;
2468 This->rootStartBlock = 1;
2469 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2470 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2471 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2472 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2473 This->extBigBlockDepotCount = 0;
2474
2475 StorageImpl_SaveFileHeader(This);
2476
2477 /*
2478 * Add one block for the big block depot and one block for the properties
2479 */
2480 size.u.HighPart = 0;
2481 size.u.LowPart = This->bigBlockSize * 3;
2482 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2483
2484 /*
2485 * Initialize the big block depot
2486 */
2487 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2488 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2489 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2490 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2491 }
2492 else
2493 {
2494 /*
2495 * Load the header for the file.
2496 */
2497 hr = StorageImpl_LoadFileHeader(This);
2498
2499 if (FAILED(hr))
2500 {
2501 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2502
2503 return hr;
2504 }
2505 }
2506
2507 /*
2508 * There is no block depot cached yet.
2509 */
2510 This->indexBlockDepotCached = 0xFFFFFFFF;
2511
2512 /*
2513 * Start searching for free blocks with block 0.
2514 */
2515 This->prevFreeBlock = 0;
2516
2517 /*
2518 * Create the block chain abstractions.
2519 */
2520 if(!(This->rootBlockChain =
2521 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2522 return STG_E_READFAULT;
2523
2524 if(!(This->smallBlockDepotChain =
2525 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2526 PROPERTY_NULL)))
2527 return STG_E_READFAULT;
2528
2529 /*
2530 * Write the root property (memory only)
2531 */
2532 if (create)
2533 {
2534 StgProperty rootProp;
2535 /*
2536 * Initialize the property chain
2537 */
2538 memset(&rootProp, 0, sizeof(rootProp));
2539 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2540 sizeof(rootProp.name)/sizeof(WCHAR) );
2541 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2542 rootProp.propertyType = PROPTYPE_ROOT;
2543 rootProp.previousProperty = PROPERTY_NULL;
2544 rootProp.nextProperty = PROPERTY_NULL;
2545 rootProp.dirProperty = PROPERTY_NULL;
2546 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2547 rootProp.size.u.HighPart = 0;
2548 rootProp.size.u.LowPart = 0;
2549
2550 StorageImpl_WriteProperty(This, 0, &rootProp);
2551 }
2552
2553 /*
2554 * Find the ID of the root in the property sets.
2555 */
2556 currentPropertyIndex = 0;
2557
2558 do
2559 {
2560 readSuccessful = StorageImpl_ReadProperty(
2561 This,
2562 currentPropertyIndex,
2563 ¤tProperty);
2564
2565 if (readSuccessful)
2566 {
2567 if ( (currentProperty.sizeOfNameString != 0 ) &&
2568 (currentProperty.propertyType == PROPTYPE_ROOT) )
2569 {
2570 This->base.rootPropertySetIndex = currentPropertyIndex;
2571 }
2572 }
2573
2574 currentPropertyIndex++;
2575
2576 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2577
2578 if (!readSuccessful)
2579 {
2580 /* TODO CLEANUP */
2581 return STG_E_READFAULT;
2582 }
2583
2584 /*
2585 * Create the block chain abstraction for the small block root chain.
2586 */
2587 if(!(This->smallBlockRootChain =
2588 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2589 return STG_E_READFAULT;
2590
2591 return hr;
2592 }
2593
2594 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2595 {
2596 StorageImpl *This = (StorageImpl*) iface;
2597 TRACE("(%p)\n", This);
2598
2599 StorageBaseImpl_DeleteAll(&This->base);
2600
2601 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2602
2603 BlockChainStream_Destroy(This->smallBlockRootChain);
2604 BlockChainStream_Destroy(This->rootBlockChain);
2605 BlockChainStream_Destroy(This->smallBlockDepotChain);
2606
2607 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2608 HeapFree(GetProcessHeap(), 0, This);
2609 }
2610
2611 /******************************************************************************
2612 * Storage32Impl_GetNextFreeBigBlock
2613 *
2614 * Returns the index of the next free big block.
2615 * If the big block depot is filled, this method will enlarge it.
2616 *
2617 */
2618 static ULONG StorageImpl_GetNextFreeBigBlock(
2619 StorageImpl* This)
2620 {
2621 ULONG depotBlockIndexPos;
2622 BYTE depotBuffer[BIG_BLOCK_SIZE];
2623 BOOL success;
2624 ULONG depotBlockOffset;
2625 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2626 ULONG nextBlockIndex = BLOCK_SPECIAL;
2627 int depotIndex = 0;
2628 ULONG freeBlock = BLOCK_UNUSED;
2629
2630 depotIndex = This->prevFreeBlock / blocksPerDepot;
2631 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2632
2633 /*
2634 * Scan the entire big block depot until we find a block marked free
2635 */
2636 while (nextBlockIndex != BLOCK_UNUSED)
2637 {
2638 if (depotIndex < COUNT_BBDEPOTINHEADER)
2639 {
2640 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2641
2642 /*
2643 * Grow the primary depot.
2644 */
2645 if (depotBlockIndexPos == BLOCK_UNUSED)
2646 {
2647 depotBlockIndexPos = depotIndex*blocksPerDepot;
2648
2649 /*
2650 * Add a block depot.
2651 */
2652 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2653 This->bigBlockDepotCount++;
2654 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2655
2656 /*
2657 * Flag it as a block depot.
2658 */
2659 StorageImpl_SetNextBlockInChain(This,
2660 depotBlockIndexPos,
2661 BLOCK_SPECIAL);
2662
2663 /* Save new header information.
2664 */
2665 StorageImpl_SaveFileHeader(This);
2666 }
2667 }
2668 else
2669 {
2670 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2671
2672 if (depotBlockIndexPos == BLOCK_UNUSED)
2673 {
2674 /*
2675 * Grow the extended depot.
2676 */
2677 ULONG extIndex = BLOCK_UNUSED;
2678 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2679 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2680
2681 if (extBlockOffset == 0)
2682 {
2683 /* We need an extended block.
2684 */
2685 extIndex = Storage32Impl_AddExtBlockDepot(This);
2686 This->extBigBlockDepotCount++;
2687 depotBlockIndexPos = extIndex + 1;
2688 }
2689 else
2690 depotBlockIndexPos = depotIndex * blocksPerDepot;
2691
2692 /*
2693 * Add a block depot and mark it in the extended block.
2694 */
2695 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2696 This->bigBlockDepotCount++;
2697 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2698
2699 /* Flag the block depot.
2700 */
2701 StorageImpl_SetNextBlockInChain(This,
2702 depotBlockIndexPos,
2703 BLOCK_SPECIAL);
2704
2705 /* If necessary, flag the extended depot block.
2706 */
2707 if (extIndex != BLOCK_UNUSED)
2708 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2709
2710 /* Save header information.
2711 */
2712 StorageImpl_SaveFileHeader(This);
2713 }
2714 }
2715
2716 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2717
2718 if (success)
2719 {
2720 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2721 ( nextBlockIndex != BLOCK_UNUSED))
2722 {
2723 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2724
2725 if (nextBlockIndex == BLOCK_UNUSED)
2726 {
2727 freeBlock = (depotIndex * blocksPerDepot) +
2728 (depotBlockOffset/sizeof(ULONG));
2729 }
2730
2731 depotBlockOffset += sizeof(ULONG);
2732 }
2733 }
2734
2735 depotIndex++;
2736 depotBlockOffset = 0;
2737 }
2738
2739 /*
2740 * make sure that the block physically exists before using it
2741 */
2742 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2743
2744 This->prevFreeBlock = freeBlock;
2745
2746 return freeBlock;
2747 }
2748
2749 /******************************************************************************
2750 * Storage32Impl_AddBlockDepot
2751 *
2752 * This will create a depot block, essentially it is a block initialized
2753 * to BLOCK_UNUSEDs.
2754 */
2755 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2756 {
2757 BYTE blockBuffer[BIG_BLOCK_SIZE];
2758
2759 /*
2760 * Initialize blocks as free
2761 */
2762 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2763 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2764 }
2765
2766 /******************************************************************************
2767 * Storage32Impl_GetExtDepotBlock
2768 *
2769 * Returns the index of the block that corresponds to the specified depot
2770 * index. This method is only for depot indexes equal or greater than
2771 * COUNT_BBDEPOTINHEADER.
2772 */
2773 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2774 {
2775 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2776 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2777 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2778 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2779 ULONG blockIndex = BLOCK_UNUSED;
2780 ULONG extBlockIndex = This->extBigBlockDepotStart;
2781
2782 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2783
2784 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2785 return BLOCK_UNUSED;
2786
2787 while (extBlockCount > 0)
2788 {
2789 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2790 extBlockCount--;
2791 }
2792
2793 if (extBlockIndex != BLOCK_UNUSED)
2794 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2795 extBlockOffset * sizeof(ULONG), &blockIndex);
2796
2797 return blockIndex;
2798 }
2799
2800 /******************************************************************************
2801 * Storage32Impl_SetExtDepotBlock
2802 *
2803 * Associates the specified block index to the specified depot index.
2804 * This method is only for depot indexes equal or greater than
2805 * COUNT_BBDEPOTINHEADER.
2806 */
2807 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2808 {
2809 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2810 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2811 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2812 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2813 ULONG extBlockIndex = This->extBigBlockDepotStart;
2814
2815 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2816
2817 while (extBlockCount > 0)
2818 {
2819 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2820 extBlockCount--;
2821 }
2822
2823 if (extBlockIndex != BLOCK_UNUSED)
2824 {
2825 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2826 extBlockOffset * sizeof(ULONG),
2827 blockIndex);
2828 }
2829 }
2830
2831 /******************************************************************************
2832 * Storage32Impl_AddExtBlockDepot
2833 *
2834 * Creates an extended depot block.
2835 */
2836 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2837 {
2838 ULONG numExtBlocks = This->extBigBlockDepotCount;
2839 ULONG nextExtBlock = This->extBigBlockDepotStart;
2840 BYTE depotBuffer[BIG_BLOCK_SIZE];
2841 ULONG index = BLOCK_UNUSED;
2842 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2843 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2844 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2845
2846 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2847 blocksPerDepotBlock;
2848
2849 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2850 {
2851 /*
2852 * The first extended block.
2853 */
2854 This->extBigBlockDepotStart = index;
2855 }
2856 else
2857 {
2858 unsigned int i;
2859 /*
2860 * Follow the chain to the last one.
2861 */
2862 for (i = 0; i < (numExtBlocks - 1); i++)
2863 {
2864 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2865 }
2866
2867 /*
2868 * Add the new extended block to the chain.
2869 */
2870 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2871 index);
2872 }
2873
2874 /*
2875 * Initialize this block.
2876 */
2877 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2878 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2879
2880 return index;
2881 }
2882
2883 /******************************************************************************
2884 * Storage32Impl_FreeBigBlock
2885 *
2886 * This method will flag the specified block as free in the big block depot.
2887 */
2888 static void StorageImpl_FreeBigBlock(
2889 StorageImpl* This,
2890 ULONG blockIndex)
2891 {
2892 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2893
2894 if (blockIndex < This->prevFreeBlock)
2895 This->prevFreeBlock = blockIndex;
2896 }
2897
2898 /************************************************************************
2899 * Storage32Impl_GetNextBlockInChain
2900 *
2901 * This method will retrieve the block index of the next big block in
2902 * in the chain.
2903 *
2904 * Params: This - Pointer to the Storage object.
2905 * blockIndex - Index of the block to retrieve the chain
2906 * for.
2907 * nextBlockIndex - receives the return value.
2908 *
2909 * Returns: This method returns the index of the next block in the chain.
2910 * It will return the constants:
2911 * BLOCK_SPECIAL - If the block given was not part of a
2912 * chain.
2913 * BLOCK_END_OF_CHAIN - If the block given was the last in
2914 * a chain.
2915 * BLOCK_UNUSED - If the block given was not past of a chain
2916 * and is available.
2917 * BLOCK_EXTBBDEPOT - This block is part of the extended
2918 * big block depot.
2919 *
2920 * See Windows documentation for more details on IStorage methods.
2921 */
2922 static HRESULT StorageImpl_GetNextBlockInChain(
2923 StorageImpl* This,
2924 ULONG blockIndex,
2925 ULONG* nextBlockIndex)
2926 {
2927 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2928 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2929 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2930 BYTE depotBuffer[BIG_BLOCK_SIZE];
2931 BOOL success;
2932 ULONG depotBlockIndexPos;
2933 int index;
2934
2935 *nextBlockIndex = BLOCK_SPECIAL;
2936
2937 if(depotBlockCount >= This->bigBlockDepotCount)
2938 {
2939 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2940 This->bigBlockDepotCount);
2941 return STG_E_READFAULT;
2942 }
2943
2944 /*
2945 * Cache the currently accessed depot block.
2946 */
2947 if (depotBlockCount != This->indexBlockDepotCached)
2948 {
2949 This->indexBlockDepotCached = depotBlockCount;
2950
2951 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2952 {
2953 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2954 }
2955 else
2956 {
2957 /*
2958 * We have to look in the extended depot.
2959 */
2960 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2961 }
2962
2963 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2964
2965 if (!success)
2966 return STG_E_READFAULT;
2967
2968 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2969 {
2970 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2971 This->blockDepotCached[index] = *nextBlockIndex;
2972 }
2973 }
2974
2975 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2976
2977 return S_OK;
2978 }
2979
2980 /******************************************************************************
2981 * Storage32Impl_GetNextExtendedBlock
2982 *
2983 * Given an extended block this method will return the next extended block.
2984 *
2985 * NOTES:
2986 * The last ULONG of an extended block is the block index of the next
2987 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2988 * depot.
2989 *
2990 * Return values:
2991 * - The index of the next extended block
2992 * - BLOCK_UNUSED: there is no next extended block.
2993 * - Any other return values denotes failure.
2994 */
2995 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2996 {
2997 ULONG nextBlockIndex = BLOCK_SPECIAL;
2998 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2999
3000 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3001 &nextBlockIndex);
3002
3003 return nextBlockIndex;
3004 }
3005
3006 /******************************************************************************
3007 * Storage32Impl_SetNextBlockInChain
3008 *
3009 * This method will write the index of the specified block's next block
3010 * in the big block depot.
3011 *
3012 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3013 * do the following
3014 *
3015 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3016 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3017 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3018 *
3019 */
3020 static void StorageImpl_SetNextBlockInChain(
3021 StorageImpl* This,
3022 ULONG blockIndex,
3023 ULONG nextBlock)
3024 {
3025 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3026 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3027 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3028 ULONG depotBlockIndexPos;
3029
3030 assert(depotBlockCount < This->bigBlockDepotCount);
3031 assert(blockIndex != nextBlock);
3032
3033 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3034 {
3035 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3036 }
3037 else
3038 {
3039 /*
3040 * We have to look in the extended depot.
3041 */
3042 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3043 }
3044
3045 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3046 nextBlock);
3047 /*
3048 * Update the cached block depot, if necessary.
3049 */
3050 if (depotBlockCount == This->indexBlockDepotCached)
3051 {
3052 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3053 }
3054 }
3055
3056 /******************************************************************************
3057 * Storage32Impl_LoadFileHeader
3058 *
3059 * This method will read in the file header, i.e. big block index -1.
3060 */
3061 static HRESULT StorageImpl_LoadFileHeader(
3062 StorageImpl* This)
3063 {
3064 HRESULT hr = STG_E_FILENOTFOUND;
3065 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3066 BOOL success;
3067 int index;
3068
3069 TRACE("\n");
3070 /*
3071 * Get a pointer to the big block of data containing the header.
3072 */
3073 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3074
3075 /*
3076 * Extract the information from the header.
3077 */
3078 if (success)
3079 {
3080 /*
3081 * Check for the "magic number" signature and return an error if it is not
3082 * found.
3083 */
3084 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3085 {
3086 return STG_E_OLDFORMAT;
3087 }
3088
3089 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3090 {
3091 return STG_E_INVALIDHEADER;
3092 }
3093
3094 StorageUtl_ReadWord(
3095 headerBigBlock,
3096 OFFSET_BIGBLOCKSIZEBITS,
3097 &This->bigBlockSizeBits);
3098
3099 StorageUtl_ReadWord(
3100 headerBigBlock,
3101 OFFSET_SMALLBLOCKSIZEBITS,
3102 &This->smallBlockSizeBits);
3103
3104 StorageUtl_ReadDWord(
3105 headerBigBlock,
3106 OFFSET_BBDEPOTCOUNT,
3107 &This->bigBlockDepotCount);
3108
3109 StorageUtl_ReadDWord(
3110 headerBigBlock,
3111 OFFSET_ROOTSTARTBLOCK,
3112 &This->rootStartBlock);
3113
3114 StorageUtl_ReadDWord(
3115 headerBigBlock,
3116 OFFSET_SBDEPOTSTART,
3117 &This->smallBlockDepotStart);
3118
3119 StorageUtl_ReadDWord(
3120 headerBigBlock,
3121 OFFSET_EXTBBDEPOTSTART,
3122 &This->extBigBlockDepotStart);
3123
3124 StorageUtl_ReadDWord(
3125 headerBigBlock,
3126 OFFSET_EXTBBDEPOTCOUNT,
3127 &This->extBigBlockDepotCount);
3128
3129 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3130 {
3131 StorageUtl_ReadDWord(
3132 headerBigBlock,
3133 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3134 &(This->bigBlockDepotStart[index]));
3135 }
3136
3137 /*
3138 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3139 */
3140 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3141 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3142
3143 /*
3144 * Right now, the code is making some assumptions about the size of the
3145 * blocks, just make sure they are what we're expecting.
3146 */
3147 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3148 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3149 {
3150 WARN("Broken OLE storage file\n");
3151 hr = STG_E_INVALIDHEADER;
3152 }
3153 else
3154 hr = S_OK;
3155 }
3156
3157 return hr;
3158 }
3159
3160 /******************************************************************************
3161 * Storage32Impl_SaveFileHeader
3162 *
3163 * This method will save to the file the header, i.e. big block -1.
3164 */
3165 static void StorageImpl_SaveFileHeader(
3166 StorageImpl* This)
3167 {
3168 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3169 int index;
3170 BOOL success;
3171
3172 /*
3173 * Get a pointer to the big block of data containing the header.
3174 */
3175 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3176
3177 /*
3178 * If the block read failed, the file is probably new.
3179 */
3180 if (!success)
3181 {
3182 /*
3183 * Initialize for all unknown fields.
3184 */
3185 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3186
3187 /*
3188 * Initialize the magic number.
3189 */
3190 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3191
3192 /*
3193 * And a bunch of things we don't know what they mean
3194 */
3195 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3196 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3197 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3198 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3199 }
3200
3201 /*
3202 * Write the information to the header.
3203 */
3204 StorageUtl_WriteWord(
3205 headerBigBlock,
3206 OFFSET_BIGBLOCKSIZEBITS,
3207 This->bigBlockSizeBits);
3208
3209 StorageUtl_WriteWord(
3210 headerBigBlock,
3211 OFFSET_SMALLBLOCKSIZEBITS,
3212 This->smallBlockSizeBits);
3213
3214 StorageUtl_WriteDWord(
3215 headerBigBlock,
3216 OFFSET_BBDEPOTCOUNT,
3217 This->bigBlockDepotCount);
3218
3219 StorageUtl_WriteDWord(
3220 headerBigBlock,
3221 OFFSET_ROOTSTARTBLOCK,
3222 This->rootStartBlock);
3223
3224 StorageUtl_WriteDWord(
3225 headerBigBlock,
3226 OFFSET_SBDEPOTSTART,
3227 This->smallBlockDepotStart);
3228
3229 StorageUtl_WriteDWord(
3230 headerBigBlock,
3231 OFFSET_SBDEPOTCOUNT,
3232 This->smallBlockDepotChain ?
3233 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3234
3235 StorageUtl_WriteDWord(
3236 headerBigBlock,
3237 OFFSET_EXTBBDEPOTSTART,
3238 This->extBigBlockDepotStart);
3239
3240 StorageUtl_WriteDWord(
3241 headerBigBlock,
3242 OFFSET_EXTBBDEPOTCOUNT,
3243 This->extBigBlockDepotCount);
3244
3245 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3246 {
3247 StorageUtl_WriteDWord(
3248 headerBigBlock,
3249 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3250 (This->bigBlockDepotStart[index]));
3251 }
3252
3253 /*
3254 * Write the big block back to the file.
3255 */
3256 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3257 }
3258
3259 /******************************************************************************
3260 * Storage32Impl_ReadProperty
3261 *
3262 * This method will read the specified property from the property chain.
3263 */
3264 BOOL StorageImpl_ReadProperty(
3265 StorageImpl* This,
3266 ULONG index,
3267 StgProperty* buffer)
3268 {
3269 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3270 ULARGE_INTEGER offsetInPropSet;
3271 HRESULT readRes;
3272 ULONG bytesRead;
3273
3274 offsetInPropSet.u.HighPart = 0;
3275 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3276
3277 readRes = BlockChainStream_ReadAt(
3278 This->rootBlockChain,
3279 offsetInPropSet,
3280 PROPSET_BLOCK_SIZE,
3281 currentProperty,
3282 &bytesRead);
3283
3284 if (SUCCEEDED(readRes))
3285 {
3286 /* replace the name of root entry (often "Root Entry") by the file name */
3287 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3288 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3289
3290 memset(buffer->name, 0, sizeof(buffer->name));
3291 memcpy(
3292 buffer->name,
3293 propName,
3294 PROPERTY_NAME_BUFFER_LEN );
3295 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3296
3297 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3298
3299 StorageUtl_ReadWord(
3300 currentProperty,
3301 OFFSET_PS_NAMELENGTH,
3302 &buffer->sizeOfNameString);
3303
3304 StorageUtl_ReadDWord(
3305 currentProperty,
3306 OFFSET_PS_PREVIOUSPROP,
3307 &buffer->previousProperty);
3308
3309 StorageUtl_ReadDWord(
3310 currentProperty,
3311 OFFSET_PS_NEXTPROP,
3312 &buffer->nextProperty);
3313
3314 StorageUtl_ReadDWord(
3315 currentProperty,
3316 OFFSET_PS_DIRPROP,
3317 &buffer->dirProperty);
3318
3319 StorageUtl_ReadGUID(
3320 currentProperty,
3321 OFFSET_PS_GUID,
3322 &buffer->propertyUniqueID);
3323
3324 StorageUtl_ReadDWord(
3325 currentProperty,
3326 OFFSET_PS_TSS1,
3327 &buffer->timeStampS1);
3328
3329 StorageUtl_ReadDWord(
3330 currentProperty,
3331 OFFSET_PS_TSD1,
3332 &buffer->timeStampD1);
3333
3334 StorageUtl_ReadDWord(
3335 currentProperty,
3336 OFFSET_PS_TSS2,
3337 &buffer->timeStampS2);
3338
3339 StorageUtl_ReadDWord(
3340 currentProperty,
3341 OFFSET_PS_TSD2,
3342 &buffer->timeStampD2);
3343
3344 StorageUtl_ReadDWord(
3345 currentProperty,
3346 OFFSET_PS_STARTBLOCK,
3347 &buffer->startingBlock);
3348
3349 StorageUtl_ReadDWord(
3350 currentProperty,
3351 OFFSET_PS_SIZE,
3352 &buffer->size.u.LowPart);
3353
3354 buffer->size.u.HighPart = 0;
3355 }
3356
3357 return SUCCEEDED(readRes) ? TRUE : FALSE;
3358 }
3359
3360 /*********************************************************************
3361 * Write the specified property into the property chain
3362 */
3363 BOOL StorageImpl_WriteProperty(
3364 StorageImpl* This,
3365 ULONG index,
3366 const StgProperty* buffer)
3367 {
3368 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3369 ULARGE_INTEGER offsetInPropSet;
3370 HRESULT writeRes;
3371 ULONG bytesWritten;
3372
3373 offsetInPropSet.u.HighPart = 0;
3374 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3375
3376 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3377
3378 memcpy(
3379 currentProperty + OFFSET_PS_NAME,
3380 buffer->name,
3381 PROPERTY_NAME_BUFFER_LEN );
3382
3383 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3384
3385 StorageUtl_WriteWord(
3386 currentProperty,
3387 OFFSET_PS_NAMELENGTH,
3388 buffer->sizeOfNameString);
3389
3390 StorageUtl_WriteDWord(
3391 currentProperty,
3392 OFFSET_PS_PREVIOUSPROP,
3393 buffer->previousProperty);
3394
3395 StorageUtl_WriteDWord(
3396 currentProperty,
3397 OFFSET_PS_NEXTPROP,
3398 buffer->nextProperty);
3399
3400 StorageUtl_WriteDWord(
3401 currentProperty,
3402 OFFSET_PS_DIRPROP,
3403 buffer->dirProperty);
3404
3405 StorageUtl_WriteGUID(
3406 currentProperty,
3407 OFFSET_PS_GUID,
3408 &buffer->propertyUniqueID);
3409
3410 StorageUtl_WriteDWord(
3411 currentProperty,
3412 OFFSET_PS_TSS1,
3413 buffer->timeStampS1);
3414
3415 StorageUtl_WriteDWord(
3416 currentProperty,
3417 OFFSET_PS_TSD1,
3418 buffer->timeStampD1);
3419
3420 StorageUtl_WriteDWord(
3421 currentProperty,
3422 OFFSET_PS_TSS2,
3423 buffer->timeStampS2);
3424
3425 StorageUtl_WriteDWord(
3426 currentProperty,
3427 OFFSET_PS_TSD2,
3428 buffer->timeStampD2);
3429
3430 StorageUtl_WriteDWord(
3431 currentProperty,
3432 OFFSET_PS_STARTBLOCK,
3433 buffer->startingBlock);
3434
3435 StorageUtl_WriteDWord(
3436 currentProperty,
3437 OFFSET_PS_SIZE,
3438 buffer->size.u.LowPart);
3439
3440 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3441 offsetInPropSet,
3442 PROPSET_BLOCK_SIZE,
3443 currentProperty,
3444 &bytesWritten);
3445 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3446 }
3447
3448 static BOOL StorageImpl_ReadBigBlock(
3449 StorageImpl* This,
3450 ULONG blockIndex,
3451 void* buffer)
3452 {
3453 ULARGE_INTEGER ulOffset;
3454 DWORD read;
3455
3456 ulOffset.u.HighPart = 0;
3457 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3458
3459 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3460 return (read == This->bigBlockSize);
3461 }
3462
3463 static BOOL StorageImpl_ReadDWordFromBigBlock(
3464 StorageImpl* This,
3465 ULONG blockIndex,
3466 ULONG offset,
3467 DWORD* value)
3468 {
3469 ULARGE_INTEGER ulOffset;
3470 DWORD read;
3471 DWORD tmp;
3472
3473 ulOffset.u.HighPart = 0;
3474 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3475 ulOffset.u.LowPart += offset;
3476
3477 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3478 *value = lendian32toh(tmp);
3479 return (read == sizeof(DWORD));
3480 }
3481
3482 static BOOL StorageImpl_WriteBigBlock(
3483 StorageImpl* This,
3484 ULONG blockIndex,
3485 const void* buffer)
3486 {
3487 ULARGE_INTEGER ulOffset;
3488 DWORD wrote;
3489
3490 ulOffset.u.HighPart = 0;
3491 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3492
3493 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3494 return (wrote == This->bigBlockSize);
3495 }
3496
3497 static BOOL StorageImpl_WriteDWordToBigBlock(
3498 StorageImpl* This,
3499 ULONG blockIndex,
3500 ULONG offset,
3501 DWORD value)
3502 {
3503 ULARGE_INTEGER ulOffset;
3504 DWORD wrote;
3505
3506 ulOffset.u.HighPart = 0;
3507 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3508 ulOffset.u.LowPart += offset;
3509
3510 value = htole32(value);
3511 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3512 return (wrote == sizeof(DWORD));
3513 }
3514
3515 /******************************************************************************
3516 * Storage32Impl_SmallBlocksToBigBlocks
3517 *
3518 * This method will convert a small block chain to a big block chain.
3519 * The small block chain will be destroyed.
3520 */
3521 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3522 StorageImpl* This,
3523 SmallBlockChainStream** ppsbChain)
3524 {
3525 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3526 ULARGE_INTEGER size, offset;
3527 ULONG cbRead, cbWritten;
3528 ULARGE_INTEGER cbTotalRead;
3529 ULONG propertyIndex;
3530 HRESULT resWrite = S_OK;
3531 HRESULT resRead;
3532 StgProperty chainProperty;
3533 BYTE *buffer;
3534 BlockChainStream *bbTempChain = NULL;
3535 BlockChainStream *bigBlockChain = NULL;
3536
3537 /*
3538 * Create a temporary big block chain that doesn't have
3539 * an associated property. This temporary chain will be
3540 * used to copy data from small blocks to big blocks.
3541 */
3542 bbTempChain = BlockChainStream_Construct(This,
3543 &bbHeadOfChain,
3544 PROPERTY_NULL);
3545 if(!bbTempChain) return NULL;
3546 /*
3547 * Grow the big block chain.
3548 */
3549 size = SmallBlockChainStream_GetSize(*ppsbChain);
3550 BlockChainStream_SetSize(bbTempChain, size);
3551
3552 /*
3553 * Copy the contents of the small block chain to the big block chain
3554 * by small block size increments.
3555 */
3556 offset.u.LowPart = 0;
3557 offset.u.HighPart = 0;
3558 cbTotalRead.QuadPart = 0;
3559
3560 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3561 do
3562 {
3563 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3564 offset,
3565 This->smallBlockSize,
3566 buffer,
3567 &cbRead);
3568 if (FAILED(resRead))
3569 break;
3570
3571 if (cbRead > 0)
3572 {
3573 cbTotalRead.QuadPart += cbRead;
3574
3575 resWrite = BlockChainStream_WriteAt(bbTempChain,
3576 offset,
3577 cbRead,
3578 buffer,
3579 &cbWritten);
3580
3581 if (FAILED(resWrite))
3582 break;
3583
3584 offset.u.LowPart += This->smallBlockSize;
3585 }
3586 } while (cbTotalRead.QuadPart < size.QuadPart);
3587 HeapFree(GetProcessHeap(),0,buffer);
3588
3589 if (FAILED(resRead) || FAILED(resWrite))
3590 {
3591 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3592 BlockChainStream_Destroy(bbTempChain);
3593 return NULL;
3594 }
3595
3596 /*
3597 * Destroy the small block chain.
3598 */
3599 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3600 size.u.HighPart = 0;
3601 size.u.LowPart = 0;
3602 SmallBlockChainStream_SetSize(*ppsbChain, size);
3603 SmallBlockChainStream_Destroy(*ppsbChain);
3604 *ppsbChain = 0;
3605
3606 /*
3607 * Change the property information. This chain is now a big block chain
3608 * and it doesn't reside in the small blocks chain anymore.
3609 */
3610 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3611
3612 chainProperty.startingBlock = bbHeadOfChain;
3613
3614 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3615
3616 /*
3617 * Destroy the temporary propertyless big block chain.
3618 * Create a new big block chain associated with this property.
3619 */
3620 BlockChainStream_Destroy(bbTempChain);
3621 bigBlockChain = BlockChainStream_Construct(This,
3622 NULL,
3623 propertyIndex);
3624
3625 return bigBlockChain;
3626 }
3627
3628 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3629 {
3630 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3631
3632 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3633 HeapFree(GetProcessHeap(), 0, This);
3634 }
3635
3636 /******************************************************************************
3637 **
3638 ** Storage32InternalImpl_Commit
3639 **
3640 ** The non-root storages cannot be opened in transacted mode thus this function
3641 ** does nothing.
3642 */
3643 static HRESULT WINAPI StorageInternalImpl_Commit(
3644 IStorage* iface,
3645 DWORD grfCommitFlags) /* [in] */
3646 {
3647 return S_OK;
3648 }
3649
3650 /******************************************************************************
3651 **
3652 ** Storage32InternalImpl_Revert
3653 **
3654 ** The non-root storages cannot be opened in transacted mode thus this function
3655 ** does nothing.
3656 */
3657 static HRESULT WINAPI StorageInternalImpl_Revert(
3658 IStorage* iface)
3659 {
3660 return S_OK;
3661 }
3662
3663 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3664 {
3665 IStorage_Release((IStorage*)This->parentStorage);
3666 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3667 HeapFree(GetProcessHeap(), 0, This);
3668 }
3669
3670 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3671 IEnumSTATSTG* iface,
3672 REFIID riid,
3673 void** ppvObject)
3674 {
3675 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3676
3677 /*
3678 * Perform a sanity check on the parameters.
3679 */
3680 if (ppvObject==0)
3681 return E_INVALIDARG;
3682
3683 /*
3684 * Initialize the return parameter.
3685 */
3686 *ppvObject = 0;
3687
3688 /*
3689 * Compare the riid with the interface IDs implemented by this object.
3690 */
3691 if (IsEqualGUID(&IID_IUnknown, riid) ||
3692 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3693 {
3694 *ppvObject = This;
3695 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3696 return S_OK;
3697 }
3698
3699 return E_NOINTERFACE;
3700 }
3701
3702 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3703 IEnumSTATSTG* iface)
3704 {
3705 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3706 return InterlockedIncrement(&This->ref);
3707 }
3708
3709 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3710 IEnumSTATSTG* iface)
3711 {
3712 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3713
3714 ULONG newRef;
3715
3716 newRef = InterlockedDecrement(&This->ref);
3717
3718 /*
3719 * If the reference count goes down to 0, perform suicide.
3720 */