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 ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1910 {
1911 CoTaskMemFree(pstatstg->pwcsName);
1912 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1913 strcpyW(pstatstg->pwcsName, This->pwcsName);
1914 }
1915
1916 return result;
1917 }
1918
1919 /******************************************************************************
1920 * Internal stream list handlers
1921 */
1922
1923 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1924 {
1925 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1926 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1927 }
1928
1929 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1930 {
1931 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1932 list_remove(&(strm->StrmListEntry));
1933 }
1934
1935 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1936 {
1937 struct list *cur, *cur2;
1938 StgStreamImpl *strm=NULL;
1939
1940 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1941 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1942 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1943 strm->parentStorage = NULL;
1944 list_remove(cur);
1945 }
1946 }
1947
1948
1949 /*********************************************************************
1950 *
1951 * Internal Method
1952 *
1953 * Perform the deletion of a complete storage node
1954 *
1955 */
1956 static HRESULT deleteStorageProperty(
1957 StorageImpl *parentStorage,
1958 ULONG indexOfPropertyToDelete,
1959 StgProperty propertyToDelete)
1960 {
1961 IEnumSTATSTG *elements = 0;
1962 IStorage *childStorage = 0;
1963 STATSTG currentElement;
1964 HRESULT hr;
1965 HRESULT destroyHr = S_OK;
1966
1967 /*
1968 * Open the storage and enumerate it
1969 */
1970 hr = StorageBaseImpl_OpenStorage(
1971 (IStorage*)parentStorage,
1972 propertyToDelete.name,
1973 0,
1974 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1975 0,
1976 0,
1977 &childStorage);
1978
1979 if (hr != S_OK)
1980 {
1981 return hr;
1982 }
1983
1984 /*
1985 * Enumerate the elements
1986 */
1987 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1988
1989 do
1990 {
1991 /*
1992 * Obtain the next element
1993 */
1994 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1995 if (hr==S_OK)
1996 {
1997 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1998
1999 CoTaskMemFree(currentElement.pwcsName);
2000 }
2001
2002 /*
2003 * We need to Reset the enumeration every time because we delete elements
2004 * and the enumeration could be invalid
2005 */
2006 IEnumSTATSTG_Reset(elements);
2007
2008 } while ((hr == S_OK) && (destroyHr == S_OK));
2009
2010 /*
2011 * Invalidate the property by zeroing its name member.
2012 */
2013 propertyToDelete.sizeOfNameString = 0;
2014
2015 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2016 indexOfPropertyToDelete,
2017 &propertyToDelete);
2018
2019 IStorage_Release(childStorage);
2020 IEnumSTATSTG_Release(elements);
2021
2022 return destroyHr;
2023 }
2024
2025 /*********************************************************************
2026 *
2027 * Internal Method
2028 *
2029 * Perform the deletion of a stream node
2030 *
2031 */
2032 static HRESULT deleteStreamProperty(
2033 StorageImpl *parentStorage,
2034 ULONG indexOfPropertyToDelete,
2035 StgProperty propertyToDelete)
2036 {
2037 IStream *pis;
2038 HRESULT hr;
2039 ULARGE_INTEGER size;
2040
2041 size.u.HighPart = 0;
2042 size.u.LowPart = 0;
2043
2044 hr = StorageBaseImpl_OpenStream(
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 BOOL res = TRUE;
2104
2105 /*
2106 * Read the storage property
2107 */
2108 res = StorageImpl_ReadProperty(
2109 storage->base.ancestorStorage,
2110 storePropertyIndex,
2111 &storeProperty);
2112
2113 if(! res)
2114 {
2115 return E_FAIL;
2116 }
2117
2118 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2119 {
2120 if (storeProperty.previousProperty != PROPERTY_NULL)
2121 {
2122 return findPlaceholder(
2123 storage,
2124 propertyIndexToStore,
2125 storeProperty.previousProperty,
2126 typeOfRelation);
2127 }
2128 else
2129 {
2130 storeProperty.previousProperty = propertyIndexToStore;
2131 }
2132 }
2133 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2134 {
2135 if (storeProperty.nextProperty != PROPERTY_NULL)
2136 {
2137 return findPlaceholder(
2138 storage,
2139 propertyIndexToStore,
2140 storeProperty.nextProperty,
2141 typeOfRelation);
2142 }
2143 else
2144 {
2145 storeProperty.nextProperty = propertyIndexToStore;
2146 }
2147 }
2148 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2149 {
2150 if (storeProperty.dirProperty != PROPERTY_NULL)
2151 {
2152 return findPlaceholder(
2153 storage,
2154 propertyIndexToStore,
2155 storeProperty.dirProperty,
2156 typeOfRelation);
2157 }
2158 else
2159 {
2160 storeProperty.dirProperty = propertyIndexToStore;
2161 }
2162 }
2163
2164 res = StorageImpl_WriteProperty(
2165 storage->base.ancestorStorage,
2166 storePropertyIndex,
2167 &storeProperty);
2168
2169 if(!res)
2170 {
2171 return E_FAIL;
2172 }
2173
2174 return S_OK;
2175 }
2176
2177 /*************************************************************************
2178 *
2179 * Internal Method
2180 *
2181 * This method takes the previous and the next property link of a property
2182 * to be deleted and find them a place in the Storage.
2183 */
2184 static HRESULT adjustPropertyChain(
2185 StorageImpl *This,
2186 StgProperty propertyToDelete,
2187 StgProperty parentProperty,
2188 ULONG parentPropertyId,
2189 INT typeOfRelation)
2190 {
2191 ULONG newLinkProperty = PROPERTY_NULL;
2192 BOOL needToFindAPlaceholder = FALSE;
2193 ULONG storeNode = PROPERTY_NULL;
2194 ULONG toStoreNode = PROPERTY_NULL;
2195 INT relationType = 0;
2196 HRESULT hr = S_OK;
2197 BOOL res = TRUE;
2198
2199 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2200 {
2201 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2202 {
2203 /*
2204 * Set the parent previous to the property to delete previous
2205 */
2206 newLinkProperty = propertyToDelete.previousProperty;
2207
2208 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2209 {
2210 /*
2211 * We also need to find a storage for the other link, setup variables
2212 * to do this at the end...
2213 */
2214 needToFindAPlaceholder = TRUE;
2215 storeNode = propertyToDelete.previousProperty;
2216 toStoreNode = propertyToDelete.nextProperty;
2217 relationType = PROPERTY_RELATION_NEXT;
2218 }
2219 }
2220 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2221 {
2222 /*
2223 * Set the parent previous to the property to delete next
2224 */
2225 newLinkProperty = propertyToDelete.nextProperty;
2226 }
2227
2228 /*
2229 * Link it for real...
2230 */
2231 parentProperty.previousProperty = newLinkProperty;
2232
2233 }
2234 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2235 {
2236 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2237 {
2238 /*
2239 * Set the parent next to the property to delete next previous
2240 */
2241 newLinkProperty = propertyToDelete.previousProperty;
2242
2243 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2244 {
2245 /*
2246 * We also need to find a storage for the other link, setup variables
2247 * to do this at the end...
2248 */
2249 needToFindAPlaceholder = TRUE;
2250 storeNode = propertyToDelete.previousProperty;
2251 toStoreNode = propertyToDelete.nextProperty;
2252 relationType = PROPERTY_RELATION_NEXT;
2253 }
2254 }
2255 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2256 {
2257 /*
2258 * Set the parent next to the property to delete next
2259 */
2260 newLinkProperty = propertyToDelete.nextProperty;
2261 }
2262
2263 /*
2264 * Link it for real...
2265 */
2266 parentProperty.nextProperty = newLinkProperty;
2267 }
2268 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2269 {
2270 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2271 {
2272 /*
2273 * Set the parent dir to the property to delete previous
2274 */
2275 newLinkProperty = propertyToDelete.previousProperty;
2276
2277 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2278 {
2279 /*
2280 * We also need to find a storage for the other link, setup variables
2281 * to do this at the end...
2282 */
2283 needToFindAPlaceholder = TRUE;
2284 storeNode = propertyToDelete.previousProperty;
2285 toStoreNode = propertyToDelete.nextProperty;
2286 relationType = PROPERTY_RELATION_NEXT;
2287 }
2288 }
2289 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2290 {
2291 /*
2292 * Set the parent dir to the property to delete next
2293 */
2294 newLinkProperty = propertyToDelete.nextProperty;
2295 }
2296
2297 /*
2298 * Link it for real...
2299 */
2300 parentProperty.dirProperty = newLinkProperty;
2301 }
2302
2303 /*
2304 * Write back the parent property
2305 */
2306 res = StorageImpl_WriteProperty(
2307 This->base.ancestorStorage,
2308 parentPropertyId,
2309 &parentProperty);
2310 if(! res)
2311 {
2312 return E_FAIL;
2313 }
2314
2315 /*
2316 * If a placeholder is required for the other link, then, find one and
2317 * get out of here...
2318 */
2319 if (needToFindAPlaceholder)
2320 {
2321 hr = findPlaceholder(
2322 This,
2323 toStoreNode,
2324 storeNode,
2325 relationType);
2326 }
2327
2328 return hr;
2329 }
2330
2331
2332 /******************************************************************************
2333 * SetElementTimes (IStorage)
2334 */
2335 static HRESULT WINAPI StorageImpl_SetElementTimes(
2336 IStorage* iface,
2337 const OLECHAR *pwcsName,/* [string][in] */
2338 const FILETIME *pctime, /* [in] */
2339 const FILETIME *patime, /* [in] */
2340 const FILETIME *pmtime) /* [in] */
2341 {
2342 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2343 return S_OK;
2344 }
2345
2346 /******************************************************************************
2347 * SetStateBits (IStorage)
2348 */
2349 static HRESULT WINAPI StorageImpl_SetStateBits(
2350 IStorage* iface,
2351 DWORD grfStateBits,/* [in] */
2352 DWORD grfMask) /* [in] */
2353 {
2354 StorageImpl* const This = (StorageImpl*)iface;
2355 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2356 return S_OK;
2357 }
2358
2359 /*
2360 * Virtual function table for the IStorage32Impl class.
2361 */
2362 static const IStorageVtbl Storage32Impl_Vtbl =
2363 {
2364 StorageBaseImpl_QueryInterface,
2365 StorageBaseImpl_AddRef,
2366 StorageBaseImpl_Release,
2367 StorageBaseImpl_CreateStream,
2368 StorageBaseImpl_OpenStream,
2369 StorageImpl_CreateStorage,
2370 StorageBaseImpl_OpenStorage,
2371 StorageImpl_CopyTo,
2372 StorageImpl_MoveElementTo,
2373 StorageImpl_Commit,
2374 StorageImpl_Revert,
2375 StorageBaseImpl_EnumElements,
2376 StorageImpl_DestroyElement,
2377 StorageBaseImpl_RenameElement,
2378 StorageImpl_SetElementTimes,
2379 StorageBaseImpl_SetClass,
2380 StorageImpl_SetStateBits,
2381 StorageImpl_Stat
2382 };
2383
2384 static HRESULT StorageImpl_Construct(
2385 StorageImpl* This,
2386 HANDLE hFile,
2387 LPCOLESTR pwcsName,
2388 ILockBytes* pLkbyt,
2389 DWORD openFlags,
2390 BOOL fileBased,
2391 BOOL fileCreate)
2392 {
2393 HRESULT hr = S_OK;
2394 StgProperty currentProperty;
2395 BOOL readSuccessful;
2396 ULONG currentPropertyIndex;
2397
2398 if ( FAILED( validateSTGM(openFlags) ))
2399 return STG_E_INVALIDFLAG;
2400
2401 memset(This, 0, sizeof(StorageImpl));
2402
2403 /*
2404 * Initialize stream list
2405 */
2406
2407 list_init(&This->base.strmHead);
2408
2409 /*
2410 * Initialize the virtual function table.
2411 */
2412 This->base.lpVtbl = &Storage32Impl_Vtbl;
2413 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2414 This->base.v_destructor = StorageImpl_Destroy;
2415 This->base.openFlags = (openFlags & ~STGM_CREATE);
2416
2417 /*
2418 * This is the top-level storage so initialize the ancestor pointer
2419 * to this.
2420 */
2421 This->base.ancestorStorage = This;
2422
2423 /*
2424 * Initialize the physical support of the storage.
2425 */
2426 This->hFile = hFile;
2427
2428 /*
2429 * Store copy of file path.
2430 */
2431 if(pwcsName) {
2432 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2433 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2434 if (!This->pwcsName)
2435 return STG_E_INSUFFICIENTMEMORY;
2436 strcpyW(This->pwcsName, pwcsName);
2437 }
2438
2439 /*
2440 * Initialize the big block cache.
2441 */
2442 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2443 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2444 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2445 pLkbyt,
2446 openFlags,
2447 This->bigBlockSize,
2448 fileBased);
2449
2450 if (This->bigBlockFile == 0)
2451 return E_FAIL;
2452
2453 if (fileCreate)
2454 {
2455 ULARGE_INTEGER size;
2456 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2457
2458 /*
2459 * Initialize all header variables:
2460 * - The big block depot consists of one block and it is at block 0
2461 * - The properties start at block 1
2462 * - There is no small block depot
2463 */
2464 memset( This->bigBlockDepotStart,
2465 BLOCK_UNUSED,
2466 sizeof(This->bigBlockDepotStart));
2467
2468 This->bigBlockDepotCount = 1;
2469 This->bigBlockDepotStart[0] = 0;
2470 This->rootStartBlock = 1;
2471 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2472 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2473 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2474 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2475 This->extBigBlockDepotCount = 0;
2476
2477 StorageImpl_SaveFileHeader(This);
2478
2479 /*
2480 * Add one block for the big block depot and one block for the properties
2481 */
2482 size.u.HighPart = 0;
2483 size.u.LowPart = This->bigBlockSize * 3;
2484 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2485
2486 /*
2487 * Initialize the big block depot
2488 */
2489 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2490 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2491 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2492 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2493 }
2494 else
2495 {
2496 /*
2497 * Load the header for the file.
2498 */
2499 hr = StorageImpl_LoadFileHeader(This);
2500
2501 if (FAILED(hr))
2502 {
2503 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2504
2505 return hr;
2506 }
2507 }
2508
2509 /*
2510 * There is no block depot cached yet.
2511 */
2512 This->indexBlockDepotCached = 0xFFFFFFFF;
2513
2514 /*
2515 * Start searching for free blocks with block 0.
2516 */
2517 This->prevFreeBlock = 0;
2518
2519 /*
2520 * Create the block chain abstractions.
2521 */
2522 if(!(This->rootBlockChain =
2523 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2524 return STG_E_READFAULT;
2525
2526 if(!(This->smallBlockDepotChain =
2527 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2528 PROPERTY_NULL)))
2529 return STG_E_READFAULT;
2530
2531 /*
2532 * Write the root property (memory only)
2533 */
2534 if (fileCreate)
2535 {
2536 StgProperty rootProp;
2537 /*
2538 * Initialize the property chain
2539 */
2540 memset(&rootProp, 0, sizeof(rootProp));
2541 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2542 sizeof(rootProp.name)/sizeof(WCHAR) );
2543 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2544 rootProp.propertyType = PROPTYPE_ROOT;
2545 rootProp.previousProperty = PROPERTY_NULL;
2546 rootProp.nextProperty = PROPERTY_NULL;
2547 rootProp.dirProperty = PROPERTY_NULL;
2548 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2549 rootProp.size.u.HighPart = 0;
2550 rootProp.size.u.LowPart = 0;
2551
2552 StorageImpl_WriteProperty(This, 0, &rootProp);
2553 }
2554
2555 /*
2556 * Find the ID of the root in the property sets.
2557 */
2558 currentPropertyIndex = 0;
2559
2560 do
2561 {
2562 readSuccessful = StorageImpl_ReadProperty(
2563 This,
2564 currentPropertyIndex,
2565 ¤tProperty);
2566
2567 if (readSuccessful)
2568 {
2569 if ( (currentProperty.sizeOfNameString != 0 ) &&
2570 (currentProperty.propertyType == PROPTYPE_ROOT) )
2571 {
2572 This->base.rootPropertySetIndex = currentPropertyIndex;
2573 }
2574 }
2575
2576 currentPropertyIndex++;
2577
2578 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2579
2580 if (!readSuccessful)
2581 {
2582 /* TODO CLEANUP */
2583 return STG_E_READFAULT;
2584 }
2585
2586 /*
2587 * Create the block chain abstraction for the small block root chain.
2588 */
2589 if(!(This->smallBlockRootChain =
2590 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2591 return STG_E_READFAULT;
2592
2593 return hr;
2594 }
2595
2596 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2597 {
2598 StorageImpl *This = (StorageImpl*) iface;
2599 TRACE("(%p)\n", This);
2600
2601 StorageBaseImpl_DeleteAll(&This->base);
2602
2603 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2604
2605 BlockChainStream_Destroy(This->smallBlockRootChain);
2606 BlockChainStream_Destroy(This->rootBlockChain);
2607 BlockChainStream_Destroy(This->smallBlockDepotChain);
2608
2609 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2610 HeapFree(GetProcessHeap(), 0, This);
2611 }
2612
2613 /******************************************************************************
2614 * Storage32Impl_GetNextFreeBigBlock
2615 *
2616 * Returns the index of the next free big block.
2617 * If the big block depot is filled, this method will enlarge it.
2618 *
2619 */
2620 static ULONG StorageImpl_GetNextFreeBigBlock(
2621 StorageImpl* This)
2622 {
2623 ULONG depotBlockIndexPos;
2624 BYTE depotBuffer[BIG_BLOCK_SIZE];
2625 BOOL success;
2626 ULONG depotBlockOffset;
2627 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2628 ULONG nextBlockIndex = BLOCK_SPECIAL;
2629 int depotIndex = 0;
2630 ULONG freeBlock = BLOCK_UNUSED;
2631
2632 depotIndex = This->prevFreeBlock / blocksPerDepot;
2633 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2634
2635 /*
2636 * Scan the entire big block depot until we find a block marked free
2637 */
2638 while (nextBlockIndex != BLOCK_UNUSED)
2639 {
2640 if (depotIndex < COUNT_BBDEPOTINHEADER)
2641 {
2642 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2643
2644 /*
2645 * Grow the primary depot.
2646 */
2647 if (depotBlockIndexPos == BLOCK_UNUSED)
2648 {
2649 depotBlockIndexPos = depotIndex*blocksPerDepot;
2650
2651 /*
2652 * Add a block depot.
2653 */
2654 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2655 This->bigBlockDepotCount++;
2656 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2657
2658 /*
2659 * Flag it as a block depot.
2660 */
2661 StorageImpl_SetNextBlockInChain(This,
2662 depotBlockIndexPos,
2663 BLOCK_SPECIAL);
2664
2665 /* Save new header information.
2666 */
2667 StorageImpl_SaveFileHeader(This);
2668 }
2669 }
2670 else
2671 {
2672 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2673
2674 if (depotBlockIndexPos == BLOCK_UNUSED)
2675 {
2676 /*
2677 * Grow the extended depot.
2678 */
2679 ULONG extIndex = BLOCK_UNUSED;
2680 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2681 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2682
2683 if (extBlockOffset == 0)
2684 {
2685 /* We need an extended block.
2686 */
2687 extIndex = Storage32Impl_AddExtBlockDepot(This);
2688 This->extBigBlockDepotCount++;
2689 depotBlockIndexPos = extIndex + 1;
2690 }
2691 else
2692 depotBlockIndexPos = depotIndex * blocksPerDepot;
2693
2694 /*
2695 * Add a block depot and mark it in the extended block.
2696 */
2697 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2698 This->bigBlockDepotCount++;
2699 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2700
2701 /* Flag the block depot.
2702 */
2703 StorageImpl_SetNextBlockInChain(This,
2704 depotBlockIndexPos,
2705 BLOCK_SPECIAL);
2706
2707 /* If necessary, flag the extended depot block.
2708 */
2709 if (extIndex != BLOCK_UNUSED)
2710 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2711
2712 /* Save header information.
2713 */
2714 StorageImpl_SaveFileHeader(This);
2715 }
2716 }
2717
2718 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2719
2720 if (success)
2721 {
2722 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2723 ( nextBlockIndex != BLOCK_UNUSED))
2724 {
2725 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2726
2727 if (nextBlockIndex == BLOCK_UNUSED)
2728 {
2729 freeBlock = (depotIndex * blocksPerDepot) +
2730 (depotBlockOffset/sizeof(ULONG));
2731 }
2732
2733 depotBlockOffset += sizeof(ULONG);
2734 }
2735 }
2736
2737 depotIndex++;
2738 depotBlockOffset = 0;
2739 }
2740
2741 /*
2742 * make sure that the block physically exists before using it
2743 */
2744 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2745
2746 This->prevFreeBlock = freeBlock;
2747
2748 return freeBlock;
2749 }
2750
2751 /******************************************************************************
2752 * Storage32Impl_AddBlockDepot
2753 *
2754 * This will create a depot block, essentially it is a block initialized
2755 * to BLOCK_UNUSEDs.
2756 */
2757 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2758 {
2759 BYTE blockBuffer[BIG_BLOCK_SIZE];
2760
2761 /*
2762 * Initialize blocks as free
2763 */
2764 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2765 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2766 }
2767
2768 /******************************************************************************
2769 * Storage32Impl_GetExtDepotBlock
2770 *
2771 * Returns the index of the block that corresponds to the specified depot
2772 * index. This method is only for depot indexes equal or greater than
2773 * COUNT_BBDEPOTINHEADER.
2774 */
2775 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2776 {
2777 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2778 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2779 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2780 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2781 ULONG blockIndex = BLOCK_UNUSED;
2782 ULONG extBlockIndex = This->extBigBlockDepotStart;
2783
2784 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2785
2786 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2787 return BLOCK_UNUSED;
2788
2789 while (extBlockCount > 0)
2790 {
2791 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2792 extBlockCount--;
2793 }
2794
2795 if (extBlockIndex != BLOCK_UNUSED)
2796 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2797 extBlockOffset * sizeof(ULONG), &blockIndex);
2798
2799 return blockIndex;
2800 }
2801
2802 /******************************************************************************
2803 * Storage32Impl_SetExtDepotBlock
2804 *
2805 * Associates the specified block index to the specified depot index.
2806 * This method is only for depot indexes equal or greater than
2807 * COUNT_BBDEPOTINHEADER.
2808 */
2809 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2810 {
2811 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2812 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2813 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2814 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2815 ULONG extBlockIndex = This->extBigBlockDepotStart;
2816
2817 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2818
2819 while (extBlockCount > 0)
2820 {
2821 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2822 extBlockCount--;
2823 }
2824
2825 if (extBlockIndex != BLOCK_UNUSED)
2826 {
2827 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2828 extBlockOffset * sizeof(ULONG),
2829 blockIndex);
2830 }
2831 }
2832
2833 /******************************************************************************
2834 * Storage32Impl_AddExtBlockDepot
2835 *
2836 * Creates an extended depot block.
2837 */
2838 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2839 {
2840 ULONG numExtBlocks = This->extBigBlockDepotCount;
2841 ULONG nextExtBlock = This->extBigBlockDepotStart;
2842 BYTE depotBuffer[BIG_BLOCK_SIZE];
2843 ULONG index = BLOCK_UNUSED;
2844 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2845 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2846 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2847
2848 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2849 blocksPerDepotBlock;
2850
2851 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2852 {
2853 /*
2854 * The first extended block.
2855 */
2856 This->extBigBlockDepotStart = index;
2857 }
2858 else
2859 {
2860 unsigned int i;
2861 /*
2862 * Follow the chain to the last one.
2863 */
2864 for (i = 0; i < (numExtBlocks - 1); i++)
2865 {
2866 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2867 }
2868
2869 /*
2870 * Add the new extended block to the chain.
2871 */
2872 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2873 index);
2874 }
2875
2876 /*
2877 * Initialize this block.
2878 */
2879 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2880 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2881
2882 return index;
2883 }
2884
2885 /******************************************************************************
2886 * Storage32Impl_FreeBigBlock
2887 *
2888 * This method will flag the specified block as free in the big block depot.
2889 */
2890 static void StorageImpl_FreeBigBlock(
2891 StorageImpl* This,
2892 ULONG blockIndex)
2893 {
2894 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2895
2896 if (blockIndex < This->prevFreeBlock)
2897 This->prevFreeBlock = blockIndex;
2898 }
2899
2900 /************************************************************************
2901 * Storage32Impl_GetNextBlockInChain
2902 *
2903 * This method will retrieve the block index of the next big block in
2904 * in the chain.
2905 *
2906 * Params: This - Pointer to the Storage object.
2907 * blockIndex - Index of the block to retrieve the chain
2908 * for.
2909 * nextBlockIndex - receives the return value.
2910 *
2911 * Returns: This method returns the index of the next block in the chain.
2912 * It will return the constants:
2913 * BLOCK_SPECIAL - If the block given was not part of a
2914 * chain.
2915 * BLOCK_END_OF_CHAIN - If the block given was the last in
2916 * a chain.
2917 * BLOCK_UNUSED - If the block given was not past of a chain
2918 * and is available.
2919 * BLOCK_EXTBBDEPOT - This block is part of the extended
2920 * big block depot.
2921 *
2922 * See Windows documentation for more details on IStorage methods.
2923 */
2924 static HRESULT StorageImpl_GetNextBlockInChain(
2925 StorageImpl* This,
2926 ULONG blockIndex,
2927 ULONG* nextBlockIndex)
2928 {
2929 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2930 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2931 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2932 BYTE depotBuffer[BIG_BLOCK_SIZE];
2933 BOOL success;
2934 ULONG depotBlockIndexPos;
2935 int index;
2936
2937 *nextBlockIndex = BLOCK_SPECIAL;
2938
2939 if(depotBlockCount >= This->bigBlockDepotCount)
2940 {
2941 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2942 This->bigBlockDepotCount);
2943 return STG_E_READFAULT;
2944 }
2945
2946 /*
2947 * Cache the currently accessed depot block.
2948 */
2949 if (depotBlockCount != This->indexBlockDepotCached)
2950 {
2951 This->indexBlockDepotCached = depotBlockCount;
2952
2953 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2954 {
2955 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2956 }
2957 else
2958 {
2959 /*
2960 * We have to look in the extended depot.
2961 */
2962 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2963 }
2964
2965 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2966
2967 if (!success)
2968 return STG_E_READFAULT;
2969
2970 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2971 {
2972 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2973 This->blockDepotCached[index] = *nextBlockIndex;
2974 }
2975 }
2976
2977 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2978
2979 return S_OK;
2980 }
2981
2982 /******************************************************************************
2983 * Storage32Impl_GetNextExtendedBlock
2984 *
2985 * Given an extended block this method will return the next extended block.
2986 *
2987 * NOTES:
2988 * The last ULONG of an extended block is the block index of the next
2989 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2990 * depot.
2991 *
2992 * Return values:
2993 * - The index of the next extended block
2994 * - BLOCK_UNUSED: there is no next extended block.
2995 * - Any other return values denotes failure.
2996 */
2997 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2998 {
2999 ULONG nextBlockIndex = BLOCK_SPECIAL;
3000 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3001
3002 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3003 &nextBlockIndex);
3004
3005 return nextBlockIndex;
3006 }
3007
3008 /******************************************************************************
3009 * Storage32Impl_SetNextBlockInChain
3010 *
3011 * This method will write the index of the specified block's next block
3012 * in the big block depot.
3013 *
3014 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3015 * do the following
3016 *
3017 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3018 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3019 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3020 *
3021 */
3022 static void StorageImpl_SetNextBlockInChain(
3023 StorageImpl* This,
3024 ULONG blockIndex,
3025 ULONG nextBlock)
3026 {
3027 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3028 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3029 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3030 ULONG depotBlockIndexPos;
3031
3032 assert(depotBlockCount < This->bigBlockDepotCount);
3033 assert(blockIndex != nextBlock);
3034
3035 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3036 {
3037 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3038 }
3039 else
3040 {
3041 /*
3042 * We have to look in the extended depot.
3043 */
3044 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3045 }
3046
3047 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3048 nextBlock);
3049 /*
3050 * Update the cached block depot, if necessary.
3051 */
3052 if (depotBlockCount == This->indexBlockDepotCached)
3053 {
3054 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3055 }
3056 }
3057
3058 /******************************************************************************
3059 * Storage32Impl_LoadFileHeader
3060 *
3061 * This method will read in the file header, i.e. big block index -1.
3062 */
3063 static HRESULT StorageImpl_LoadFileHeader(
3064 StorageImpl* This)
3065 {
3066 HRESULT hr = STG_E_FILENOTFOUND;
3067 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3068 BOOL success;
3069 int index;
3070
3071 TRACE("\n");
3072 /*
3073 * Get a pointer to the big block of data containing the header.
3074 */
3075 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3076
3077 /*
3078 * Extract the information from the header.
3079 */
3080 if (success)
3081 {
3082 /*
3083 * Check for the "magic number" signature and return an error if it is not
3084 * found.
3085 */
3086 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3087 {
3088 return STG_E_OLDFORMAT;
3089 }
3090
3091 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3092 {
3093 return STG_E_INVALIDHEADER;
3094 }
3095
3096 StorageUtl_ReadWord(
3097 headerBigBlock,
3098 OFFSET_BIGBLOCKSIZEBITS,
3099 &This->bigBlockSizeBits);
3100
3101 StorageUtl_ReadWord(
3102 headerBigBlock,
3103 OFFSET_SMALLBLOCKSIZEBITS,
3104 &This->smallBlockSizeBits);
3105
3106 StorageUtl_ReadDWord(
3107 headerBigBlock,
3108 OFFSET_BBDEPOTCOUNT,
3109 &This->bigBlockDepotCount);
3110
3111 StorageUtl_ReadDWord(
3112 headerBigBlock,
3113 OFFSET_ROOTSTARTBLOCK,
3114 &This->rootStartBlock);
3115
3116 StorageUtl_ReadDWord(
3117 headerBigBlock,
3118 OFFSET_SBDEPOTSTART,
3119 &This->smallBlockDepotStart);
3120
3121 StorageUtl_ReadDWord(
3122 headerBigBlock,
3123 OFFSET_EXTBBDEPOTSTART,
3124 &This->extBigBlockDepotStart);
3125
3126 StorageUtl_ReadDWord(
3127 headerBigBlock,
3128 OFFSET_EXTBBDEPOTCOUNT,
3129 &This->extBigBlockDepotCount);
3130
3131 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3132 {
3133 StorageUtl_ReadDWord(
3134 headerBigBlock,
3135 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3136 &(This->bigBlockDepotStart[index]));
3137 }
3138
3139 /*
3140 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3141 */
3142 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3143 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3144
3145 /*
3146 * Right now, the code is making some assumptions about the size of the
3147 * blocks, just make sure they are what we're expecting.
3148 */
3149 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3150 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3151 {
3152 WARN("Broken OLE storage file\n");
3153 hr = STG_E_INVALIDHEADER;
3154 }
3155 else
3156 hr = S_OK;
3157 }
3158
3159 return hr;
3160 }
3161
3162 /******************************************************************************
3163 * Storage32Impl_SaveFileHeader
3164 *
3165 * This method will save to the file the header, i.e. big block -1.
3166 */
3167 static void StorageImpl_SaveFileHeader(
3168 StorageImpl* This)
3169 {
3170 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3171 int index;
3172 BOOL success;
3173
3174 /*
3175 * Get a pointer to the big block of data containing the header.
3176 */
3177 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3178
3179 /*
3180 * If the block read failed, the file is probably new.
3181 */
3182 if (!success)
3183 {
3184 /*
3185 * Initialize for all unknown fields.
3186 */
3187 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3188
3189 /*
3190 * Initialize the magic number.
3191 */
3192 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3193
3194 /*
3195 * And a bunch of things we don't know what they mean
3196 */
3197 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3198 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3199 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3200 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3201 }
3202
3203 /*
3204 * Write the information to the header.
3205 */
3206 StorageUtl_WriteWord(
3207 headerBigBlock,
3208 OFFSET_BIGBLOCKSIZEBITS,
3209 This->bigBlockSizeBits);
3210
3211 StorageUtl_WriteWord(
3212 headerBigBlock,
3213 OFFSET_SMALLBLOCKSIZEBITS,
3214 This->smallBlockSizeBits);
3215
3216 StorageUtl_WriteDWord(
3217 headerBigBlock,
3218 OFFSET_BBDEPOTCOUNT,
3219 This->bigBlockDepotCount);
3220
3221 StorageUtl_WriteDWord(
3222 headerBigBlock,
3223 OFFSET_ROOTSTARTBLOCK,
3224 This->rootStartBlock);
3225
3226 StorageUtl_WriteDWord(
3227 headerBigBlock,
3228 OFFSET_SBDEPOTSTART,
3229 This->smallBlockDepotStart);
3230
3231 StorageUtl_WriteDWord(
3232 headerBigBlock,
3233 OFFSET_SBDEPOTCOUNT,
3234 This->smallBlockDepotChain ?
3235 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3236
3237 StorageUtl_WriteDWord(
3238 headerBigBlock,
3239 OFFSET_EXTBBDEPOTSTART,
3240 This->extBigBlockDepotStart);
3241
3242 StorageUtl_WriteDWord(
3243 headerBigBlock,
3244 OFFSET_EXTBBDEPOTCOUNT,
3245 This->extBigBlockDepotCount);
3246
3247 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3248 {
3249 StorageUtl_WriteDWord(
3250 headerBigBlock,
3251 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3252 (This->bigBlockDepotStart[index]));
3253 }
3254
3255 /*
3256 * Write the big block back to the file.
3257 */
3258 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3259 }
3260
3261 /******************************************************************************
3262 * Storage32Impl_ReadProperty
3263 *
3264 * This method will read the specified property from the property chain.
3265 */
3266 BOOL StorageImpl_ReadProperty(
3267 StorageImpl* This,
3268 ULONG index,
3269 StgProperty* buffer)
3270 {
3271 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3272 ULARGE_INTEGER offsetInPropSet;
3273 HRESULT readRes;
3274 ULONG bytesRead;
3275
3276 offsetInPropSet.u.HighPart = 0;
3277 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3278
3279 readRes = BlockChainStream_ReadAt(
3280 This->rootBlockChain,
3281 offsetInPropSet,
3282 PROPSET_BLOCK_SIZE,
3283 currentProperty,
3284 &bytesRead);
3285
3286 if (SUCCEEDED(readRes))
3287 {
3288 /* replace the name of root entry (often "Root Entry") by the file name */
3289 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3290 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3291
3292 memset(buffer->name, 0, sizeof(buffer->name));
3293 memcpy(
3294 buffer->name,
3295 propName,
3296 PROPERTY_NAME_BUFFER_LEN );
3297 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3298
3299 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3300
3301 StorageUtl_ReadWord(
3302 currentProperty,
3303 OFFSET_PS_NAMELENGTH,
3304 &buffer->sizeOfNameString);
3305
3306 StorageUtl_ReadDWord(
3307 currentProperty,
3308 OFFSET_PS_PREVIOUSPROP,
3309 &buffer->previousProperty);
3310
3311 StorageUtl_ReadDWord(
3312 currentProperty,
3313 OFFSET_PS_NEXTPROP,
3314 &buffer->nextProperty);
3315
3316 StorageUtl_ReadDWord(
3317 currentProperty,
3318 OFFSET_PS_DIRPROP,
3319 &buffer->dirProperty);
3320
3321 StorageUtl_ReadGUID(
3322 currentProperty,
3323 OFFSET_PS_GUID,
3324 &buffer->propertyUniqueID);
3325
3326 StorageUtl_ReadDWord(
3327 currentProperty,
3328 OFFSET_PS_TSS1,
3329 &buffer->timeStampS1);
3330
3331 StorageUtl_ReadDWord(
3332 currentProperty,
3333 OFFSET_PS_TSD1,
3334 &buffer->timeStampD1);
3335
3336 StorageUtl_ReadDWord(
3337 currentProperty,
3338 OFFSET_PS_TSS2,
3339 &buffer->timeStampS2);
3340
3341 StorageUtl_ReadDWord(
3342 currentProperty,
3343 OFFSET_PS_TSD2,
3344 &buffer->timeStampD2);
3345
3346 StorageUtl_ReadDWord(
3347 currentProperty,
3348 OFFSET_PS_STARTBLOCK,
3349 &buffer->startingBlock);
3350
3351 StorageUtl_ReadDWord(
3352 currentProperty,
3353 OFFSET_PS_SIZE,
3354 &buffer->size.u.LowPart);
3355
3356 buffer->size.u.HighPart = 0;
3357 }
3358
3359 return SUCCEEDED(readRes) ? TRUE : FALSE;
3360 }
3361
3362 /*********************************************************************
3363 * Write the specified property into the property chain
3364 */
3365 BOOL StorageImpl_WriteProperty(
3366 StorageImpl* This,
3367 ULONG index,
3368 const StgProperty* buffer)
3369 {
3370 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3371 ULARGE_INTEGER offsetInPropSet;
3372 HRESULT writeRes;
3373 ULONG bytesWritten;
3374
3375 offsetInPropSet.u.HighPart = 0;
3376 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3377
3378 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3379
3380 memcpy(
3381 currentProperty + OFFSET_PS_NAME,
3382 buffer->name,
3383 PROPERTY_NAME_BUFFER_LEN );
3384
3385 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3386
3387 StorageUtl_WriteWord(
3388 currentProperty,
3389 OFFSET_PS_NAMELENGTH,
3390 buffer->sizeOfNameString);
3391
3392 StorageUtl_WriteDWord(
3393 currentProperty,
3394 OFFSET_PS_PREVIOUSPROP,
3395 buffer->previousProperty);
3396
3397 StorageUtl_WriteDWord(
3398 currentProperty,
3399 OFFSET_PS_NEXTPROP,
3400 buffer->nextProperty);
3401
3402 StorageUtl_WriteDWord(
3403 currentProperty,
3404 OFFSET_PS_DIRPROP,
3405 buffer->dirProperty);
3406
3407 StorageUtl_WriteGUID(
3408 currentProperty,
3409 OFFSET_PS_GUID,
3410 &buffer->propertyUniqueID);
3411
3412 StorageUtl_WriteDWord(
3413 currentProperty,
3414 OFFSET_PS_TSS1,
3415 buffer->timeStampS1);
3416
3417 StorageUtl_WriteDWord(
3418 currentProperty,
3419 OFFSET_PS_TSD1,
3420 buffer->timeStampD1);
3421
3422 StorageUtl_WriteDWord(
3423 currentProperty,
3424 OFFSET_PS_TSS2,
3425 buffer->timeStampS2);
3426
3427 StorageUtl_WriteDWord(
3428 currentProperty,
3429 OFFSET_PS_TSD2,
3430 buffer->timeStampD2);
3431
3432 StorageUtl_WriteDWord(
3433 currentProperty,
3434 OFFSET_PS_STARTBLOCK,
3435 buffer->startingBlock);
3436
3437 StorageUtl_WriteDWord(
3438 currentProperty,
3439 OFFSET_PS_SIZE,
3440 buffer->size.u.LowPart);
3441
3442 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3443 offsetInPropSet,
3444 PROPSET_BLOCK_SIZE,
3445 currentProperty,
3446 &bytesWritten);
3447 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3448 }
3449
3450 static BOOL StorageImpl_ReadBigBlock(
3451 StorageImpl* This,
3452 ULONG blockIndex,
3453 void* buffer)
3454 {
3455 ULARGE_INTEGER ulOffset;
3456 DWORD read;
3457
3458 ulOffset.u.HighPart = 0;
3459 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3460
3461 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3462 return (read == This->bigBlockSize);
3463 }
3464
3465 static BOOL StorageImpl_ReadDWordFromBigBlock(
3466 StorageImpl* This,
3467 ULONG blockIndex,
3468 ULONG offset,
3469 DWORD* value)
3470 {
3471 ULARGE_INTEGER ulOffset;
3472 DWORD read;
3473 DWORD tmp;
3474
3475 ulOffset.u.HighPart = 0;
3476 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3477 ulOffset.u.LowPart += offset;
3478
3479 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3480 *value = le32toh(tmp);
3481 return (read == sizeof(DWORD));
3482 }
3483
3484 static BOOL StorageImpl_WriteBigBlock(
3485 StorageImpl* This,
3486 ULONG blockIndex,
3487 const void* buffer)
3488 {
3489 ULARGE_INTEGER ulOffset;
3490 DWORD wrote;
3491
3492 ulOffset.u.HighPart = 0;
3493 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3494
3495 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3496 return (wrote == This->bigBlockSize);
3497 }
3498
3499 static BOOL StorageImpl_WriteDWordToBigBlock(
3500 StorageImpl* This,
3501 ULONG blockIndex,
3502 ULONG offset,
3503 DWORD value)
3504 {
3505 ULARGE_INTEGER ulOffset;
3506 DWORD wrote;
3507
3508 ulOffset.u.HighPart = 0;
3509 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3510 ulOffset.u.LowPart += offset;
3511
3512 value = htole32(value);
3513 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3514 return (wrote == sizeof(DWORD));
3515 }
3516
3517 /******************************************************************************
3518 * Storage32Impl_SmallBlocksToBigBlocks
3519 *
3520 * This method will convert a small block chain to a big block chain.
3521 * The small block chain will be destroyed.
3522 */
3523 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3524 StorageImpl* This,
3525 SmallBlockChainStream** ppsbChain)
3526 {
3527 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3528 ULARGE_INTEGER size, offset;
3529 ULONG cbRead, cbWritten;
3530 ULARGE_INTEGER cbTotalRead;
3531 ULONG propertyIndex;
3532 HRESULT resWrite = S_OK;
3533 HRESULT resRead;
3534 StgProperty chainProperty;
3535 BYTE *buffer;
3536 BlockChainStream *bbTempChain = NULL;
3537 BlockChainStream *bigBlockChain = NULL;
3538
3539 /*
3540 * Create a temporary big block chain that doesn't have
3541 * an associated property. This temporary chain will be
3542 * used to copy data from small blocks to big blocks.
3543 */
3544 bbTempChain = BlockChainStream_Construct(This,
3545 &bbHeadOfChain,
3546 PROPERTY_NULL);
3547 if(!bbTempChain) return NULL;
3548 /*
3549 * Grow the big block chain.
3550 */
3551 size = SmallBlockChainStream_GetSize(*ppsbChain);
3552 BlockChainStream_SetSize(bbTempChain, size);
3553
3554 /*
3555 * Copy the contents of the small block chain to the big block chain
3556 * by small block size increments.
3557 */
3558 offset.u.LowPart = 0;
3559 offset.u.HighPart = 0;
3560 cbTotalRead.QuadPart = 0;
3561
3562 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3563 do
3564 {
3565 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3566 offset,
3567 This->smallBlockSize,
3568 buffer,
3569 &cbRead);
3570 if (FAILED(resRead))
3571 break;
3572
3573 if (cbRead > 0)
3574 {
3575 cbTotalRead.QuadPart += cbRead;
3576
3577 resWrite = BlockChainStream_WriteAt(bbTempChain,
3578 offset,
3579 cbRead,
3580 buffer,
3581 &cbWritten);
3582
3583 if (FAILED(resWrite))
3584 break;
3585
3586 offset.u.LowPart += This->smallBlockSize;
3587 }
3588 } while (cbTotalRead.QuadPart < size.QuadPart);
3589 HeapFree(GetProcessHeap(),0,buffer);
3590
3591 if (FAILED(resRead) || FAILED(resWrite))
3592 {
3593 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3594 BlockChainStream_Destroy(bbTempChain);
3595 return NULL;
3596 }
3597
3598 /*
3599 * Destroy the small block chain.
3600 */
3601 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3602 size.u.HighPart = 0;
3603 size.u.LowPart = 0;
3604 SmallBlockChainStream_SetSize(*ppsbChain, size);
3605 SmallBlockChainStream_Destroy(*ppsbChain);
3606 *ppsbChain = 0;
3607
3608 /*
3609 * Change the property information. This chain is now a big block chain
3610 * and it doesn't reside in the small blocks chain anymore.
3611 */
3612 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3613
3614 chainProperty.startingBlock = bbHeadOfChain;
3615
3616 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3617
3618 /*
3619 * Destroy the temporary propertyless big block chain.
3620 * Create a new big block chain associated with this property.
3621 */
3622 BlockChainStream_Destroy(bbTempChain);
3623 bigBlockChain = BlockChainStream_Construct(This,
3624 NULL,
3625 propertyIndex);
3626
3627 return bigBlockChain;
3628 }
3629
3630 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3631 {
3632 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3633
3634 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3635 HeapFree(GetProcessHeap(), 0, This);
3636 }
3637
3638 /******************************************************************************
3639 **
3640 ** Storage32InternalImpl_Commit
3641 **
3642 ** The non-root storages cannot be opened in transacted mode thus this function
3643 ** does nothing.
3644 */
3645 static HRESULT WINAPI StorageInternalImpl_Commit(
3646 IStorage* iface,
3647 DWORD grfCommitFlags) /* [in] */
3648 {
3649 return S_OK;
3650 }
3651
3652 /******************************************************************************
3653 **
3654 ** Storage32InternalImpl_Revert
3655 **
3656 ** The non-root storages cannot be opened in transacted mode thus this function
3657 ** does nothing.
3658 */
3659 static HRESULT WINAPI StorageInternalImpl_Revert(
3660 IStorage* iface)
3661 {
3662 return S_OK;
3663 }
3664
3665 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3666 {
3667 IStorage_Release((IStorage*)This->parentStorage);
3668 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3669 HeapFree(GetProcessHeap(), 0, This);
3670 }
3671
3672 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3673 IEnumSTATSTG* iface,
3674 REFIID riid,
3675 void** ppvObject)
3676 {
3677 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678
3679 /*
3680 * Perform a sanity check on the parameters.
3681 */
3682 if (ppvObject==0)
3683 return E_INVALIDARG;
3684
3685 /*
3686 * Initialize the return parameter.
3687 */
3688 *ppvObject = 0;
3689
3690 /*
3691 * Compare the riid with the interface IDs implemented by this object.
3692 */
3693 if (IsEqualGUID(&IID_IUnknown, riid) ||
3694 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3695 {
3696 *ppvObject = (IEnumSTATSTG*)This;
3697 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3698 return S_OK;
3699 }
3700
3701 return E_NOINTERFACE;
3702 }
3703
3704 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3705 IEnumSTATSTG* iface)
3706 {
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3708 return InterlockedIncrement(&This->ref);
3709 }
3710
3711 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3712 IEnumSTATSTG* iface)
3713 {
3714 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3715
3716 ULONG newRef;
3717
3718 newRef = InterlockedDecrement(&This->ref);
3719
3720 /*
3721 * If the reference count goes down to 0, perform suicide.
3722 */
3723 if (newRef==0)
3724 {
3725 IEnumSTATSTGImpl_Destroy(This);
3726 }
3727
3728 return newRef;
3729 }
3730
3731 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3732 IEnumSTATSTG* iface,
3733 ULONG celt,
3734 STATSTG* rgelt,
3735 ULONG* pceltFetched)
3736 {
3737 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3738
3739 StgProperty currentProperty;
3740 STATSTG* currentReturnStruct = rgelt;
3741 ULONG objectFetched = 0;
3742 ULONG currentSearchNode;
3743
3744 /*
3745 * Perform a sanity check on the parameters.
3746 */
3747 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3748 return E_INVALIDARG;
3749
3750 /*
3751 * To avoid the special case, get another pointer to a ULONG value if
3752 * the caller didn't supply one.
3753 */
3754 if (pceltFetched==0)
3755 pceltFetched = &objectFetched;
3756
3757 /*
3758 * Start the iteration, we will iterate until we hit the end of the
3759 * linked list or until we hit the number of items to iterate through
3760 */
3761 *pceltFetched = 0;
3762
3763 /*
3764 * Start with the node at the top of the stack.
3765 */
3766 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3767
3768 while ( ( *pceltFetched < celt) &&
3769 ( currentSearchNode!=PROPERTY_NULL) )
3770 {
3771 /*
3772 * Remove the top node from the stack
3773 */
3774 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3775
3776 /*
3777 * Read the property from the storage.
3778 */
3779 StorageImpl_ReadProperty(This->parentStorage,
3780 currentSearchNode,
3781 ¤tProperty);
3782