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