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