source: trunk/src/ole32/storage.cpp@ 1036

Last change on this file since 1036 was 1033, checked in by davidr, 26 years ago

Ported remaining files pertaining to OLE32 from WINE

File size: 149.5 KB
Line 
1/* $Id: storage.cpp,v 1.1 1999-09-24 21:49:44 davidr Exp $ */
2/*
3 * Compound Storage functions.
4 *
5 * 20/9/99
6 *
7 * Copyright 1999 David J. Raison
8 *
9 * Direct port of Wine Implementation
10 * Copyright 1999 Francis Beaudet
11 * Copyright 1999 Sylvain St-Germain
12 * Copyright 1999 Thuy Nguyen
13 * Copyright 1998 Marcus Meissner
14 *
15 * Implemented using the documentation of the LAOLA project at
16 * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
17 * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
18 *
19 */
20
21#include "ole32.h"
22#include "heapstring.h"
23#include "debugtools.h"
24#include <assert.h>
25
26#include "storage.h"
27
28DEFAULT_DEBUG_CHANNEL(storage)
29
30#define FILE_BEGIN 0
31
32static const char rootPropertyName[] = "Root Entry";
33
34/***********************************************************************
35 * Forward declaration of internal functions used by the method DestroyElement
36 */
37static HRESULT deleteStorageProperty(
38 StorageImpl *parentStorage,
39 OLECHAR *propertyToDeleteName);
40
41static HRESULT deleteStreamProperty(
42 StorageImpl *parentStorage,
43 ULONG foundPropertyIndexToDelete,
44 StgProperty propertyToDelete);
45
46static HRESULT findPlaceholder(
47 StorageImpl *storage,
48 ULONG propertyIndexToStore,
49 ULONG storagePropertyIndex,
50 INT typeOfRelation);
51
52static HRESULT adjustPropertyChain(
53 StorageImpl *This,
54 StgProperty propertyToDelete,
55 StgProperty parentProperty,
56 ULONG parentPropertyId,
57 INT typeOfRelation);
58
59/***********************************************************************
60 * Declaration of the functions used to manipulate StgProperty
61 */
62
63static ULONG getFreeProperty(
64 StorageImpl *storage);
65
66static void updatePropertyChain(
67 StorageImpl *storage,
68 ULONG newPropertyIndex,
69 StgProperty newProperty);
70
71static LONG propertyNameCmp(
72 OLECHAR *newProperty,
73 OLECHAR *currentProperty);
74
75
76/***********************************************************************
77 * Declaration of miscellaneous functions...
78 */
79static HRESULT validateSTGM(DWORD stgmValue);
80
81static DWORD GetShareModeFromSTGM(DWORD stgm);
82static DWORD GetAccessModeFromSTGM(DWORD stgm);
83static DWORD GetCreationModeFromSTGM(DWORD stgm);
84
85/*
86 * Virtual function table for the IStorage32Impl class.
87 */
88static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
89{
90 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
91 StorageBaseImpl_QueryInterface,
92 StorageBaseImpl_AddRef,
93 StorageBaseImpl_Release,
94 StorageBaseImpl_CreateStream,
95 StorageBaseImpl_OpenStream,
96 StorageImpl_CreateStorage,
97 StorageBaseImpl_OpenStorage,
98 StorageImpl_CopyTo,
99 StorageImpl_MoveElementTo,
100 StorageImpl_Commit,
101 StorageImpl_Revert,
102 StorageBaseImpl_EnumElements,
103 StorageImpl_DestroyElement,
104 StorageBaseImpl_RenameElement,
105 StorageImpl_SetElementTimes,
106 StorageBaseImpl_SetClass,
107 StorageImpl_SetStateBits,
108 StorageBaseImpl_Stat
109};
110
111/*
112 * Virtual function table for the Storage32InternalImpl class.
113 */
114static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
115 {
116 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
117 StorageBaseImpl_QueryInterface,
118 StorageBaseImpl_AddRef,
119 StorageBaseImpl_Release,
120 StorageBaseImpl_CreateStream,
121 StorageBaseImpl_OpenStream,
122 StorageImpl_CreateStorage,
123 StorageBaseImpl_OpenStorage,
124 StorageImpl_CopyTo,
125 StorageImpl_MoveElementTo,
126 StorageInternalImpl_Commit,
127 StorageInternalImpl_Revert,
128 StorageBaseImpl_EnumElements,
129 StorageImpl_DestroyElement,
130 StorageBaseImpl_RenameElement,
131 StorageImpl_SetElementTimes,
132 StorageBaseImpl_SetClass,
133 StorageImpl_SetStateBits,
134 StorageBaseImpl_Stat
135};
136
137/*
138 * Virtual function table for the IEnumSTATSTGImpl class.
139 */
140static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
141{
142 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
143 IEnumSTATSTGImpl_QueryInterface,
144 IEnumSTATSTGImpl_AddRef,
145 IEnumSTATSTGImpl_Release,
146 IEnumSTATSTGImpl_Next,
147 IEnumSTATSTGImpl_Skip,
148 IEnumSTATSTGImpl_Reset,
149 IEnumSTATSTGImpl_Clone
150};
151
152
153
154
155
156/************************************************************************
157** Storage32BaseImpl implementatiion
158*/
159
160/************************************************************************
161 * Storage32BaseImpl_QueryInterface (IUnknown)
162 *
163 * This method implements the common QueryInterface for all IStorage32
164 * implementations contained in this file.
165 *
166 * See Windows documentation for more details on IUnknown methods.
167 */
168HRESULT WINAPI StorageBaseImpl_QueryInterface(
169 IStorage* iface,
170 REFIID riid,
171 void** ppvObject)
172{
173 ICOM_THIS(StorageBaseImpl,iface);
174 /*
175 * Perform a sanity check on the parameters.
176 */
177 if ( (This==0) || (ppvObject==0) )
178 return E_INVALIDARG;
179
180 /*
181 * Initialize the return parameter.
182 */
183 *ppvObject = 0;
184
185 /*
186 * Compare the riid with the interface IDs implemented by this object.
187 */
188 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
189 {
190 *ppvObject = (IStorage*)This;
191 }
192 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
193 {
194 *ppvObject = (IStorage*)This;
195 }
196
197 /*
198 * Check that we obtained an interface.
199 */
200 if ((*ppvObject)==0)
201 return E_NOINTERFACE;
202
203 /*
204 * Query Interface always increases the reference count by one when it is
205 * successful
206 */
207 StorageBaseImpl_AddRef(iface);
208
209 return S_OK;
210}
211
212/************************************************************************
213 * Storage32BaseImpl_AddRef (IUnknown)
214 *
215 * This method implements the common AddRef for all IStorage32
216 * implementations contained in this file.
217 *
218 * See Windows documentation for more details on IUnknown methods.
219 */
220ULONG WINAPI StorageBaseImpl_AddRef(
221 IStorage* iface)
222{
223 ICOM_THIS(StorageBaseImpl,iface);
224 This->ref++;
225
226 return This->ref;
227}
228
229/************************************************************************
230 * Storage32BaseImpl_Release (IUnknown)
231 *
232 * This method implements the common Release for all IStorage32
233 * implementations contained in this file.
234 *
235 * See Windows documentation for more details on IUnknown methods.
236 */
237ULONG WINAPI StorageBaseImpl_Release(
238 IStorage* iface)
239{
240 ICOM_THIS(StorageBaseImpl,iface);
241 /*
242 * Decrease the reference count on this object.
243 */
244 This->ref--;
245
246 /*
247 * If the reference count goes down to 0, perform suicide.
248 */
249 if (This->ref==0)
250 {
251 /*
252 * Since we are using a system of base-classes, we want to call the
253 * destructor of the appropriate derived class. To do this, we are
254 * using virtual functions to implement the destructor.
255 */
256 This->v_destructor(This);
257
258 return 0;
259 }
260
261 return This->ref;
262}
263
264/************************************************************************
265 * Storage32BaseImpl_OpenStream (IStorage)
266 *
267 * This method will open the specified stream object from the current storage.
268 *
269 * See Windows documentation for more details on IStorage methods.
270 */
271HRESULT WINAPI StorageBaseImpl_OpenStream(
272 IStorage* iface,
273 const OLECHAR* pwcsName, /* [string][in] */
274 void* reserved1, /* [unique][in] */
275 DWORD grfMode, /* [in] */
276 DWORD reserved2, /* [in] */
277 IStream** ppstm) /* [out] */
278{
279 ICOM_THIS(StorageBaseImpl,iface);
280 IEnumSTATSTGImpl* propertyEnumeration;
281 StgStreamImpl* newStream;
282 StgProperty currentProperty;
283 ULONG foundPropertyIndex;
284
285 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
286 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
287
288 /*
289 * Perform a sanity check on the parameters.
290 */
291 if ( (pwcsName==NULL) || (ppstm==0) )
292 return E_INVALIDARG;
293
294 /*
295 * Initialize the out parameter
296 */
297 *ppstm = 0;
298
299 /*
300 * Validate the STGM flags
301 */
302 if ( FAILED( validateSTGM(grfMode) ))
303 return STG_E_INVALIDFLAG;
304
305 /*
306 * As documented.
307 */
308 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
309 (grfMode & STGM_DELETEONRELEASE) ||
310 (grfMode & STGM_TRANSACTED) )
311 return STG_E_INVALIDFUNCTION;
312
313 /*
314 * Create a property enumeration to search the properties
315 */
316 propertyEnumeration = IEnumSTATSTGImpl_Construct(
317 This->ancestorStorage,
318 This->rootPropertySetIndex);
319
320 /*
321 * Search the enumeration for the property with the given name
322 */
323 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
324 propertyEnumeration,
325 pwcsName,
326 &currentProperty);
327
328 /*
329 * Delete the property enumeration since we don't need it anymore
330 */
331 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
332
333 /*
334 * If it was found, construct the stream object and return a pointer to it.
335 */
336 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
337 (currentProperty.propertyType==PROPTYPE_STREAM) )
338 {
339 newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
340
341 if (newStream!=0)
342 {
343 *ppstm = (IStream*)newStream;
344
345 /*
346 * Since we are returning a pointer to the interface, we have to
347 * nail down the reference.
348 */
349 StgStreamImpl_AddRef(*ppstm);
350
351 return S_OK;
352 }
353
354 return E_OUTOFMEMORY;
355 }
356
357 return STG_E_FILENOTFOUND;
358}
359
360/************************************************************************
361 * Storage32BaseImpl_OpenStorage (IStorage)
362 *
363 * This method will open a new storage object from the current storage.
364 *
365 * See Windows documentation for more details on IStorage methods.
366 */
367HRESULT WINAPI StorageBaseImpl_OpenStorage(
368 IStorage* iface,
369 const OLECHAR* pwcsName, /* [string][unique][in] */
370 IStorage* pstgPriority, /* [unique][in] */
371 DWORD grfMode, /* [in] */
372 SNB snbExclude, /* [unique][in] */
373 DWORD reserved, /* [in] */
374 IStorage** ppstg) /* [out] */
375{
376 ICOM_THIS(StorageBaseImpl,iface);
377 StorageInternalImpl* newStorage;
378 IEnumSTATSTGImpl* propertyEnumeration;
379 StgProperty currentProperty;
380 ULONG foundPropertyIndex;
381
382 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
383 iface, debugstr_w(pwcsName), pstgPriority,
384 grfMode, snbExclude, reserved, ppstg);
385
386 /*
387 * Perform a sanity check on the parameters.
388 */
389 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
390 return E_INVALIDARG;
391
392 /*
393 * Validate the STGM flags
394 */
395 if ( FAILED( validateSTGM(grfMode) ))
396 return STG_E_INVALIDFLAG;
397
398 /*
399 * As documented.
400 */
401 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
402 (grfMode & STGM_DELETEONRELEASE) ||
403 (grfMode & STGM_PRIORITY) )
404 return STG_E_INVALIDFUNCTION;
405
406 /*
407 * Initialize the out parameter
408 */
409 *ppstg = 0;
410
411 /*
412 * Create a property enumeration to search the properties
413 */
414 propertyEnumeration = IEnumSTATSTGImpl_Construct(
415 This->ancestorStorage,
416 This->rootPropertySetIndex);
417
418 /*
419 * Search the enumeration for the property with the given name
420 */
421 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
422 propertyEnumeration,
423 pwcsName,
424 &currentProperty);
425
426 /*
427 * Delete the property enumeration since we don't need it anymore
428 */
429 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
430
431 /*
432 * If it was found, construct the stream object and return a pointer to it.
433 */
434 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
435 (currentProperty.propertyType==PROPTYPE_STORAGE) )
436 {
437 /*
438 * Construct a new Storage object
439 */
440 newStorage = StorageInternalImpl_Construct(
441 This->ancestorStorage,
442 foundPropertyIndex);
443
444 if (newStorage != 0)
445 {
446 *ppstg = (IStorage*)newStorage;
447
448 /*
449 * Since we are returning a pointer to the interface,
450 * we have to nail down the reference.
451 */
452 StorageBaseImpl_AddRef(*ppstg);
453
454 return S_OK;
455 }
456
457 return STG_E_INSUFFICIENTMEMORY;
458 }
459
460 return STG_E_FILENOTFOUND;
461}
462
463/************************************************************************
464 * Storage32BaseImpl_EnumElements (IStorage)
465 *
466 * This method will create an enumerator object that can be used to
467 * retrieve informatino about all the properties in the storage object.
468 *
469 * See Windows documentation for more details on IStorage methods.
470 */
471HRESULT WINAPI StorageBaseImpl_EnumElements(
472 IStorage* iface,
473 DWORD reserved1, /* [in] */
474 void* reserved2, /* [size_is][unique][in] */
475 DWORD reserved3, /* [in] */
476 IEnumSTATSTG** ppenum) /* [out] */
477{
478 ICOM_THIS(StorageBaseImpl,iface);
479 IEnumSTATSTGImpl* newEnum;
480
481 TRACE("(%p, %ld, %p, %ld, %p)\n",
482 iface, reserved1, reserved2, reserved3, ppenum);
483
484 /*
485 * Perform a sanity check on the parameters.
486 */
487 if ( (This==0) || (ppenum==0))
488 return E_INVALIDARG;
489
490 /*
491 * Construct the enumerator.
492 */
493 newEnum = IEnumSTATSTGImpl_Construct(
494 This->ancestorStorage,
495 This->rootPropertySetIndex);
496
497 if (newEnum!=0)
498 {
499 *ppenum = (IEnumSTATSTG*)newEnum;
500
501 /*
502 * Don't forget to nail down a reference to the new object before
503 * returning it.
504 */
505 IEnumSTATSTGImpl_AddRef(*ppenum);
506
507 return S_OK;
508 }
509
510 return E_OUTOFMEMORY;
511}
512
513/************************************************************************
514 * Storage32BaseImpl_Stat (IStorage)
515 *
516 * This method will retrieve information about this storage object.
517 *
518 * See Windows documentation for more details on IStorage methods.
519 */
520HRESULT WINAPI StorageBaseImpl_Stat(
521 IStorage* iface,
522 STATSTG* pstatstg, /* [out] */
523 DWORD grfStatFlag) /* [in] */
524{
525 ICOM_THIS(StorageBaseImpl,iface);
526 StgProperty curProperty;
527 BOOL readSucessful;
528
529 TRACE("(%p, %p, %lx)\n",
530 iface, pstatstg, grfStatFlag);
531
532 /*
533 * Perform a sanity check on the parameters.
534 */
535 if ( (This==0) || (pstatstg==0))
536 return E_INVALIDARG;
537
538 /*
539 * Read the information from the property.
540 */
541 readSucessful = StorageImpl_ReadProperty(
542 This->ancestorStorage,
543 This->rootPropertySetIndex,
544 &curProperty);
545
546 if (readSucessful)
547 {
548 StorageUtl_CopyPropertyToSTATSTG(
549 pstatstg,
550 &curProperty,
551 grfStatFlag);
552
553 return S_OK;
554 }
555
556 return E_FAIL;
557}
558
559/************************************************************************
560 * Storage32BaseImpl_RenameElement (IStorage)
561 *
562 * This method will rename the specified element.
563 *
564 * See Windows documentation for more details on IStorage methods.
565 *
566 * Implementation notes: The method used to rename consists of creating a clone
567 * of the deleted StgProperty object setting it with the new name and to
568 * perform a DestroyElement of the old StgProperty.
569 */
570HRESULT WINAPI StorageBaseImpl_RenameElement(
571 IStorage* iface,
572 const OLECHAR* pwcsOldName, /* [in] */
573 const OLECHAR* pwcsNewName) /* [in] */
574{
575 ICOM_THIS(StorageBaseImpl,iface);
576 IEnumSTATSTGImpl* propertyEnumeration;
577 StgProperty currentProperty;
578 ULONG foundPropertyIndex;
579
580 TRACE("(%p, %s, %s)\n",
581 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
582
583 /*
584 * Create a property enumeration to search the properties
585 */
586 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
587 This->rootPropertySetIndex);
588
589 /*
590 * Search the enumeration for the new property name
591 */
592 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
593 pwcsNewName,
594 &currentProperty);
595
596 if (foundPropertyIndex != PROPERTY_NULL)
597 {
598 /*
599 * There is already a property with the new name
600 */
601 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
602 return STG_E_FILEALREADYEXISTS;
603 }
604
605 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
606
607 /*
608 * Search the enumeration for the old property name
609 */
610 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
611 pwcsOldName,
612 &currentProperty);
613
614 /*
615 * Delete the property enumeration since we don't need it anymore
616 */
617 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
618
619 if (foundPropertyIndex != PROPERTY_NULL)
620 {
621 StgProperty renamedProperty;
622 ULONG renamedPropertyIndex;
623
624 /*
625 * Setup a new property for the renamed property
626 */
627 renamedProperty.sizeOfNameString =
628 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
629
630 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
631 return STG_E_INVALIDNAME;
632
633 lstrcpyW(renamedProperty.name, pwcsNewName);
634
635 renamedProperty.propertyType = currentProperty.propertyType;
636 renamedProperty.startingBlock = currentProperty.startingBlock;
637 renamedProperty.size.LowPart = currentProperty.size.LowPart;
638 renamedProperty.size.HighPart = currentProperty.size.HighPart;
639
640 renamedProperty.previousProperty = PROPERTY_NULL;
641 renamedProperty.nextProperty = PROPERTY_NULL;
642
643 /*
644 * Bring the dirProperty link in case it is a storage and in which
645 * case the renamed storage elements don't require to be reorganized.
646 */
647 renamedProperty.dirProperty = currentProperty.dirProperty;
648
649 /* call CoFileTime to get the current time
650 renamedProperty.timeStampS1
651 renamedProperty.timeStampD1
652 renamedProperty.timeStampS2
653 renamedProperty.timeStampD2
654 renamedProperty.propertyUniqueID
655 */
656
657 /*
658 * Obtain a free property in the property chain
659 */
660 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
661
662 /*
663 * Save the new property into the new property spot
664 */
665 StorageImpl_WriteProperty(
666 This->ancestorStorage,
667 renamedPropertyIndex,
668 &renamedProperty);
669
670 /*
671 * Find a spot in the property chain for our newly created property.
672 */
673 updatePropertyChain(
674 (StorageImpl*)This,
675 renamedPropertyIndex,
676 renamedProperty);
677
678 /*
679 * At this point the renamed property has been inserted in the tree,
680 * now, before to Destroy the old property we must zeroed it's dirProperty
681 * otherwise the DestroyProperty below will zap it all and we do not want
682 * this to happen.
683 * Also, we fake that the old property is a storage so the DestroyProperty
684 * will not do a SetSize(0) on the stream data.
685 *
686 * This means that we need to tweek the StgProperty if it is a stream or a
687 * non empty storage.
688 */
689 currentProperty.dirProperty = PROPERTY_NULL;
690 currentProperty.propertyType = PROPTYPE_STORAGE;
691 StorageImpl_WriteProperty(
692 This->ancestorStorage,
693 foundPropertyIndex,
694 &currentProperty);
695
696 /*
697 * Invoke Destroy to get rid of the ole property and automatically redo
698 * the linking of it's previous and next members...
699 */
700 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
701
702 }
703 else
704 {
705 /*
706 * There is no property with the old name
707 */
708 return STG_E_FILENOTFOUND;
709 }
710
711 return S_OK;
712}
713
714/************************************************************************
715 * Storage32BaseImpl_CreateStream (IStorage)
716 *
717 * This method will create a stream object within this storage
718 *
719 * See Windows documentation for more details on IStorage methods.
720 */
721HRESULT WINAPI StorageBaseImpl_CreateStream(
722 IStorage* iface,
723 const OLECHAR* pwcsName, /* [string][in] */
724 DWORD grfMode, /* [in] */
725 DWORD reserved1, /* [in] */
726 DWORD reserved2, /* [in] */
727 IStream** ppstm) /* [out] */
728{
729 ICOM_THIS(StorageBaseImpl,iface);
730 IEnumSTATSTGImpl* propertyEnumeration;
731 StgStreamImpl* newStream;
732 StgProperty currentProperty, newStreamProperty;
733 ULONG foundPropertyIndex, newPropertyIndex;
734
735 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
736 iface, debugstr_w(pwcsName), grfMode,
737 reserved1, reserved2, ppstm);
738
739 /*
740 * Validate parameters
741 */
742 if (ppstm == 0)
743 return STG_E_INVALIDPOINTER;
744
745 if (pwcsName == 0)
746 return STG_E_INVALIDNAME;
747
748 /*
749 * Validate the STGM flags
750 */
751 if ( FAILED( validateSTGM(grfMode) ))
752 return STG_E_INVALIDFLAG;
753
754 /*
755 * As documented.
756 */
757 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
758 (grfMode & STGM_DELETEONRELEASE) ||
759 (grfMode & STGM_TRANSACTED) )
760 return STG_E_INVALIDFUNCTION;
761
762 /*
763 * Initialize the out parameter
764 */
765 *ppstm = 0;
766
767 /*
768 * Create a property enumeration to search the properties
769 */
770 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
771 This->rootPropertySetIndex);
772
773 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
774 pwcsName,
775 &currentProperty);
776
777 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
778
779 if (foundPropertyIndex != PROPERTY_NULL)
780 {
781 /*
782 * An element with this name already exists
783 */
784 if (grfMode & STGM_CREATE)
785 {
786 IStorage_DestroyElement(iface, pwcsName);
787 }
788 else
789 return STG_E_FILEALREADYEXISTS;
790 }
791
792 /*
793 * memset the empty property
794 */
795 memset(&newStreamProperty, 0, sizeof(StgProperty));
796
797 newStreamProperty.sizeOfNameString =
798 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
799
800 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
801 return STG_E_INVALIDNAME;
802
803 lstrcpyW(newStreamProperty.name, pwcsName);
804
805 newStreamProperty.propertyType = PROPTYPE_STREAM;
806 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
807 newStreamProperty.size.LowPart = 0;
808 newStreamProperty.size.HighPart = 0;
809
810 newStreamProperty.previousProperty = PROPERTY_NULL;
811 newStreamProperty.nextProperty = PROPERTY_NULL;
812 newStreamProperty.dirProperty = PROPERTY_NULL;
813
814 /* call CoFileTime to get the current time
815 newStreamProperty.timeStampS1
816 newStreamProperty.timeStampD1
817 newStreamProperty.timeStampS2
818 newStreamProperty.timeStampD2
819 */
820
821 /* newStreamProperty.propertyUniqueID */
822
823 /*
824 * Get a free property or create a new one
825 */
826 newPropertyIndex = getFreeProperty(This->ancestorStorage);
827
828 /*
829 * Save the new property into the new property spot
830 */
831 StorageImpl_WriteProperty(
832 This->ancestorStorage,
833 newPropertyIndex,
834 &newStreamProperty);
835
836 /*
837 * Find a spot in the property chain for our newly created property.
838 */
839 updatePropertyChain(
840 (StorageImpl*)This,
841 newPropertyIndex,
842 newStreamProperty);
843
844 /*
845 * Open the stream to return it.
846 */
847 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
848
849 if (newStream != 0)
850 {
851 *ppstm = (IStream*)newStream;
852
853 /*
854 * Since we are returning a pointer to the interface, we have to nail down
855 * the reference.
856 */
857 StgStreamImpl_AddRef(*ppstm);
858 }
859 else
860 {
861 return STG_E_INSUFFICIENTMEMORY;
862 }
863
864 return S_OK;
865}
866
867/************************************************************************
868 * Storage32BaseImpl_SetClass (IStorage)
869 *
870 * This method will write the specified CLSID in the property of this
871 * storage.
872 *
873 * See Windows documentation for more details on IStorage methods.
874 */
875HRESULT WINAPI StorageBaseImpl_SetClass(
876 IStorage* iface,
877 REFCLSID clsid) /* [in] */
878{
879 ICOM_THIS(StorageBaseImpl,iface);
880 HRESULT hRes = E_FAIL;
881 StgProperty curProperty;
882 BOOL success;
883
884 TRACE("(%p, %p)\n", iface, clsid);
885
886 success = StorageImpl_ReadProperty(This->ancestorStorage,
887 This->rootPropertySetIndex,
888 &curProperty);
889 if (success)
890 {
891 curProperty.propertyUniqueID = *clsid;
892
893 success = StorageImpl_WriteProperty(This->ancestorStorage,
894 This->rootPropertySetIndex,
895 &curProperty);
896 if (success)
897 hRes = S_OK;
898 }
899
900 return hRes;
901}
902
903/************************************************************************
904** Storage32Impl implementation
905*/
906
907/************************************************************************
908 * Storage32Impl_CreateStorage (IStorage)
909 *
910 * This method will create the storage object within the provided storage.
911 *
912 * See Windows documentation for more details on IStorage methods.
913 */
914HRESULT WINAPI StorageImpl_CreateStorage(
915 IStorage* iface,
916 const OLECHAR *pwcsName, /* [string][in] */
917 DWORD grfMode, /* [in] */
918 DWORD reserved1, /* [in] */
919 DWORD reserved2, /* [in] */
920 IStorage **ppstg) /* [out] */
921{
922 StorageImpl* const This=(StorageImpl*)iface;
923
924 IEnumSTATSTGImpl *propertyEnumeration;
925 StgProperty currentProperty;
926 StgProperty newProperty;
927 ULONG foundPropertyIndex;
928 ULONG newPropertyIndex;
929 HRESULT hr;
930
931 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
932 iface, debugstr_w(pwcsName), grfMode,
933 reserved1, reserved2, ppstg);
934
935 /*
936 * Validate parameters
937 */
938 if (ppstg == 0)
939 return STG_E_INVALIDPOINTER;
940
941 if (pwcsName == 0)
942 return STG_E_INVALIDNAME;
943
944 /*
945 * Validate the STGM flags
946 */
947 if ( FAILED( validateSTGM(grfMode) ) ||
948 (grfMode & STGM_DELETEONRELEASE) )
949 return STG_E_INVALIDFLAG;
950
951 /*
952 * Initialize the out parameter
953 */
954 *ppstg = 0;
955
956 /*
957 * Create a property enumeration and search the properties
958 */
959 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
960 This->rootPropertySetIndex);
961
962 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
963 pwcsName,
964 &currentProperty);
965 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
966
967 if (foundPropertyIndex != PROPERTY_NULL)
968 {
969 /*
970 * An element with this name already exists
971 */
972 if (grfMode & STGM_CREATE)
973 IStorage_DestroyElement(iface, pwcsName);
974 else
975 return STG_E_FILEALREADYEXISTS;
976 }
977
978 /*
979 * memset the empty property
980 */
981 memset(&newProperty, 0, sizeof(StgProperty));
982
983 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
984
985 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
986 return STG_E_INVALIDNAME;
987
988 lstrcpyW(newProperty.name, pwcsName);
989
990 newProperty.propertyType = PROPTYPE_STORAGE;
991 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
992 newProperty.size.LowPart = 0;
993 newProperty.size.HighPart = 0;
994
995 newProperty.previousProperty = PROPERTY_NULL;
996 newProperty.nextProperty = PROPERTY_NULL;
997 newProperty.dirProperty = PROPERTY_NULL;
998
999 /* call CoFileTime to get the current time
1000 newProperty.timeStampS1
1001 newProperty.timeStampD1
1002 newProperty.timeStampS2
1003 newProperty.timeStampD2
1004 */
1005
1006 /* newStorageProperty.propertyUniqueID */
1007
1008 /*
1009 * Obtain a free property in the property chain
1010 */
1011 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1012
1013 /*
1014 * Save the new property into the new property spot
1015 */
1016 StorageImpl_WriteProperty(
1017 This->ancestorStorage,
1018 newPropertyIndex,
1019 &newProperty);
1020
1021 /*
1022 * Find a spot in the property chain for our newly created property.
1023 */
1024 updatePropertyChain(
1025 This,
1026 newPropertyIndex,
1027 newProperty);
1028
1029 /*
1030 * Open it to get a pointer to return.
1031 */
1032 hr = IStorage_OpenStorage(
1033 iface,
1034 (OLECHAR*)pwcsName,
1035 0,
1036 grfMode,
1037 0,
1038 0,
1039 ppstg);
1040
1041 if( (hr != S_OK) || (*ppstg == NULL))
1042 {
1043 return hr;
1044 }
1045
1046
1047 return S_OK;
1048}
1049
1050
1051/***************************************************************************
1052 *
1053 * Internal Method
1054 *
1055 * Get a free property or create a new one.
1056 */
1057static ULONG getFreeProperty(
1058 StorageImpl *storage)
1059{
1060 ULONG currentPropertyIndex = 0;
1061 ULONG newPropertyIndex = PROPERTY_NULL;
1062 BOOL readSucessful = TRUE;
1063 StgProperty currentProperty;
1064
1065 do
1066 {
1067 /*
1068 * Start by reading the root property
1069 */
1070 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1071 currentPropertyIndex,
1072 &currentProperty);
1073 if (readSucessful)
1074 {
1075 if (currentProperty.sizeOfNameString == 0)
1076 {
1077 /*
1078 * The property existis and is available, we found it.
1079 */
1080 newPropertyIndex = currentPropertyIndex;
1081 }
1082 }
1083 else
1084 {
1085 /*
1086 * We exhausted the property list, we will create more space below
1087 */
1088 newPropertyIndex = currentPropertyIndex;
1089 }
1090 currentPropertyIndex++;
1091
1092 } while (newPropertyIndex == PROPERTY_NULL);
1093
1094 /*
1095 * grow the property chain
1096 */
1097 if (! readSucessful)
1098 {
1099 StgProperty emptyProperty;
1100 ULARGE_INTEGER newSize;
1101 ULONG propertyIndex;
1102 ULONG lastProperty = 0;
1103 ULONG blockCount = 0;
1104
1105 /*
1106 * obtain the new count of property blocks
1107 */
1108 blockCount = BlockChainStream_GetCount(
1109 storage->ancestorStorage->rootBlockChain)+1;
1110
1111 /*
1112 * initialize the size used by the property stream
1113 */
1114 newSize.HighPart = 0;
1115 newSize.LowPart = storage->bigBlockSize * blockCount;
1116
1117 /*
1118 * add a property block to the property chain
1119 */
1120 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1121
1122 /*
1123 * memset the empty property in order to initialize the unused newly
1124 * created property
1125 */
1126 memset(&emptyProperty, 0, sizeof(StgProperty));
1127
1128 /*
1129 * initialize them
1130 */
1131 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1132
1133 for(
1134 propertyIndex = newPropertyIndex;
1135 propertyIndex < lastProperty;
1136 propertyIndex++)
1137 {
1138 StorageImpl_WriteProperty(
1139 storage->ancestorStorage,
1140 propertyIndex,
1141 &emptyProperty);
1142 }
1143 }
1144
1145 return newPropertyIndex;
1146}
1147
1148/****************************************************************************
1149 *
1150 * Internal Method
1151 *
1152 * Case insensitive comparaison of StgProperty.name by first considering
1153 * their size.
1154 *
1155 * Returns <0 when newPrpoerty < currentProperty
1156 * >0 when newPrpoerty > currentProperty
1157 * 0 when newPrpoerty == currentProperty
1158 */
1159static LONG propertyNameCmp(
1160 OLECHAR *newProperty,
1161 OLECHAR *currentProperty)
1162{
1163 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1164
1165 if (diff == 0)
1166 {
1167 /*
1168 * We compare the string themselves only when they are of the same lenght
1169 */
1170 diff = lstrcmpiW( newProperty, currentProperty);
1171 }
1172
1173 return diff;
1174}
1175
1176/****************************************************************************
1177 *
1178 * Internal Method
1179 *
1180 * Properly link this new element in the property chain.
1181 */
1182static void updatePropertyChain(
1183 StorageImpl *storage,
1184 ULONG newPropertyIndex,
1185 StgProperty newProperty)
1186{
1187 StgProperty currentProperty;
1188
1189 /*
1190 * Read the root property
1191 */
1192 StorageImpl_ReadProperty(storage->ancestorStorage,
1193 storage->rootPropertySetIndex,
1194 &currentProperty);
1195
1196 if (currentProperty.dirProperty != PROPERTY_NULL)
1197 {
1198 /*
1199 * The root storage contains some element, therefore, start the research
1200 * for the appropriate location.
1201 */
1202 BOOL found = 0;
1203 ULONG current, next, previous, currentPropertyId;
1204
1205 /*
1206 * Keep the StgProperty sequence number of the storage first property
1207 */
1208 currentPropertyId = currentProperty.dirProperty;
1209
1210 /*
1211 * Read
1212 */
1213 StorageImpl_ReadProperty(storage->ancestorStorage,
1214 currentProperty.dirProperty,
1215 &currentProperty);
1216
1217 previous = currentProperty.previousProperty;
1218 next = currentProperty.nextProperty;
1219 current = currentPropertyId;
1220
1221 while (found == 0)
1222 {
1223 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1224
1225 if (diff < 0)
1226 {
1227 if (previous != PROPERTY_NULL)
1228 {
1229 StorageImpl_ReadProperty(storage->ancestorStorage,
1230 previous,
1231 &currentProperty);
1232 current = previous;
1233 }
1234 else
1235 {
1236 currentProperty.previousProperty = newPropertyIndex;
1237 StorageImpl_WriteProperty(storage->ancestorStorage,
1238 current,
1239 &currentProperty);
1240 found = 1;
1241 }
1242 }
1243 else if (diff > 0)
1244 {
1245 if (next != PROPERTY_NULL)
1246 {
1247 StorageImpl_ReadProperty(storage->ancestorStorage,
1248 next,
1249 &currentProperty);
1250 current = next;
1251 }
1252 else
1253 {
1254 currentProperty.nextProperty = newPropertyIndex;
1255 StorageImpl_WriteProperty(storage->ancestorStorage,
1256 current,
1257 &currentProperty);
1258 found = 1;
1259 }
1260 }
1261 else
1262 {
1263 /*
1264 * Trying to insert an item with the same name in the
1265 * subtree structure.
1266 */
1267 assert(FALSE);
1268 }
1269
1270 previous = currentProperty.previousProperty;
1271 next = currentProperty.nextProperty;
1272 }
1273 }
1274 else
1275 {
1276 /*
1277 * The root storage is empty, link the new property to it's dir property
1278 */
1279 currentProperty.dirProperty = newPropertyIndex;
1280 StorageImpl_WriteProperty(storage->ancestorStorage,
1281 storage->rootPropertySetIndex,
1282 &currentProperty);
1283 }
1284}
1285
1286
1287/*************************************************************************
1288 * CopyTo (IStorage)
1289 */
1290HRESULT WINAPI StorageImpl_CopyTo(
1291 IStorage* iface,
1292 DWORD ciidExclude, /* [in] */
1293 const IID* rgiidExclude, /* [size_is][unique][in] */
1294 SNB snbExclude, /* [unique][in] */
1295 IStorage* pstgDest) /* [unique][in] */
1296{
1297 IEnumSTATSTG *elements = 0;
1298 STATSTG curElement, strStat;
1299 HRESULT hr;
1300 IStorage *pstgTmp, *pstgChild;
1301 IStream *pstrTmp, *pstrChild;
1302
1303 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1304 FIXME("Exclude option not implemented\n");
1305
1306 TRACE("(%p, %ld, %p, %p, %p)\n",
1307 iface, ciidExclude, rgiidExclude,
1308 snbExclude, pstgDest);
1309
1310 /*
1311 * Perform a sanity check
1312 */
1313 if ( pstgDest == 0 )
1314 return STG_E_INVALIDPOINTER;
1315
1316 /*
1317 * Enumerate the elements
1318 */
1319 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1320
1321 if ( hr != S_OK )
1322 return hr;
1323
1324 /*
1325 * set the class ID
1326 */
1327 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1328 IStorage_SetClass( pstgDest, &curElement.clsid );
1329
1330 do
1331 {
1332 /*
1333 * Obtain the next element
1334 */
1335 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1336
1337 if ( hr == S_FALSE )
1338 {
1339 hr = S_OK; /* done, every element has been copied */
1340 break;
1341 }
1342
1343 if (curElement.type == STGTY_STORAGE)
1344 {
1345 /*
1346 * open child source storage
1347 */
1348 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1349 STGM_READ|STGM_SHARE_EXCLUSIVE,
1350 NULL, 0, &pstgChild );
1351
1352 if (hr != S_OK)
1353 break;
1354
1355 /*
1356 * Check if destination storage is not a child of the source
1357 * storage, which will cause an infinite loop
1358 */
1359 if (pstgChild == pstgDest)
1360 {
1361 IEnumSTATSTG_Release(elements);
1362
1363 return STG_E_ACCESSDENIED;
1364 }
1365
1366 /*
1367 * create a new storage in destination storage
1368 */
1369 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1370 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1371 0, 0,
1372 &pstgTmp );
1373 /*
1374 * if it already exist, don't create a new one use this one
1375 */
1376 if (hr == STG_E_FILEALREADYEXISTS)
1377 {
1378 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1379 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1380 NULL, 0, &pstgTmp );
1381 }
1382
1383 if (hr != S_OK)
1384 break;
1385
1386
1387 /*
1388 * do the copy recursively
1389 */
1390 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1391 snbExclude, pstgTmp );
1392
1393 IStorage_Release( pstgTmp );
1394 IStorage_Release( pstgChild );
1395 }
1396 else if (curElement.type == STGTY_STREAM)
1397 {
1398 /*
1399 * create a new stream in destination storage. If the stream already
1400 * exist, it will be deleted and a new one will be created.
1401 */
1402 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1403 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1404 0, 0, &pstrTmp );
1405
1406 if (hr != S_OK)
1407 break;
1408
1409 /*
1410 * open child stream storage
1411 */
1412 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1413 STGM_READ|STGM_SHARE_EXCLUSIVE,
1414 0, &pstrChild );
1415
1416 if (hr != S_OK)
1417 break;
1418
1419 /*
1420 * Get the size of the source stream
1421 */
1422 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1423
1424 /*
1425 * Set the size of the destination stream.
1426 */
1427 IStream_SetSize(pstrTmp, strStat.cbSize);
1428
1429 /*
1430 * do the copy
1431 */
1432 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1433 NULL, NULL );
1434
1435 IStream_Release( pstrTmp );
1436 IStream_Release( pstrChild );
1437 }
1438 else
1439 {
1440 WARN("unknown element type: %ld\n", curElement.type);
1441 }
1442
1443 } while (hr == S_OK);
1444
1445 /*
1446 * Clean-up
1447 */
1448 IEnumSTATSTG_Release(elements);
1449
1450 return hr;
1451}
1452
1453/*************************************************************************
1454 * MoveElementTo (IStorage)
1455 */
1456HRESULT WINAPI StorageImpl_MoveElementTo(
1457 IStorage* iface,
1458 const OLECHAR *pwcsName, /* [string][in] */
1459 IStorage *pstgDest, /* [unique][in] */
1460 const OLECHAR *pwcsNewName,/* [string][in] */
1461 DWORD grfFlags) /* [in] */
1462{
1463 FIXME("not implemented!\n");
1464 return E_NOTIMPL;
1465}
1466
1467/*************************************************************************
1468 * Commit (IStorage)
1469 */
1470HRESULT WINAPI StorageImpl_Commit(
1471 IStorage* iface,
1472 DWORD grfCommitFlags)/* [in] */
1473{
1474 FIXME("(%ld): stub!\n", grfCommitFlags);
1475 return S_OK;
1476}
1477
1478/*************************************************************************
1479 * Revert (IStorage)
1480 */
1481HRESULT WINAPI StorageImpl_Revert(
1482 IStorage* iface)
1483{
1484 FIXME("not implemented!\n");
1485 return E_NOTIMPL;
1486}
1487
1488/*************************************************************************
1489 * DestroyElement (IStorage)
1490 *
1491 * Stategy: This implementation is build this way for simplicity not for speed.
1492 * I always delete the top most element of the enumeration and adjust
1493 * the deleted element pointer all the time. This takes longer to
1494 * do but allow to reinvoke DestroyElement whenever we encounter a
1495 * storage object. The optimisation reside in the usage of another
1496 * enumeration stategy that would give all the leaves of a storage
1497 * first. (postfix order)
1498 */
1499HRESULT WINAPI StorageImpl_DestroyElement(
1500 IStorage* iface,
1501 const OLECHAR *pwcsName)/* [string][in] */
1502{
1503 StorageImpl* const This=(StorageImpl*)iface;
1504
1505 IEnumSTATSTGImpl* propertyEnumeration;
1506 HRESULT hr = S_OK;
1507 BOOL res;
1508 StgProperty propertyToDelete;
1509 StgProperty parentProperty;
1510 ULONG foundPropertyIndexToDelete;
1511 ULONG typeOfRelation;
1512 ULONG parentPropertyId;
1513
1514 TRACE("(%p, %s)\n",
1515 iface, debugstr_w(pwcsName));
1516
1517 /*
1518 * Perform a sanity check on the parameters.
1519 */
1520 if (pwcsName==NULL)
1521 return STG_E_INVALIDPOINTER;
1522
1523 /*
1524 * Create a property enumeration to search the property with the given name
1525 */
1526 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1527 This->ancestorStorage,
1528 This->rootPropertySetIndex);
1529
1530 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1531 propertyEnumeration,
1532 pwcsName,
1533 &propertyToDelete);
1534
1535 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1536
1537 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1538 {
1539 return STG_E_FILENOTFOUND;
1540 }
1541
1542 /*
1543 * Find the parent property of the property to delete (the one that
1544 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1545 * the parent is This. Otherwise, the parent is one of it's sibling...
1546 */
1547
1548 /*
1549 * First, read This's StgProperty..
1550 */
1551 res = StorageImpl_ReadProperty(
1552 This->ancestorStorage,
1553 This->rootPropertySetIndex,
1554 &parentProperty);
1555
1556 assert(res==TRUE);
1557
1558 /*
1559 * Second, check to see if by any chance the actual storage (This) is not
1560 * the parent of the property to delete... We never know...
1561 */
1562 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1563 {
1564 /*
1565 * Set data as it would have been done in the else part...
1566 */
1567 typeOfRelation = PROPERTY_RELATION_DIR;
1568 parentPropertyId = This->rootPropertySetIndex;
1569 }
1570 else
1571 {
1572 /*
1573 * Create a property enumeration to search the parent properties, and
1574 * delete it once done.
1575 */
1576 IEnumSTATSTGImpl* propertyEnumeration2;
1577
1578 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1579 This->ancestorStorage,
1580 This->rootPropertySetIndex);
1581
1582 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1583 propertyEnumeration2,
1584 foundPropertyIndexToDelete,
1585 &parentProperty,
1586 &parentPropertyId);
1587
1588 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1589 }
1590
1591 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1592 {
1593 hr = deleteStorageProperty(
1594 This,
1595 propertyToDelete.name);
1596 }
1597 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1598 {
1599 hr = deleteStreamProperty(
1600 This,
1601 foundPropertyIndexToDelete,
1602 propertyToDelete);
1603 }
1604
1605 if (hr!=S_OK)
1606 return hr;
1607
1608 /*
1609 * Adjust the property chain
1610 */
1611 hr = adjustPropertyChain(
1612 This,
1613 propertyToDelete,
1614 parentProperty,
1615 parentPropertyId,
1616 typeOfRelation);
1617
1618 return hr;
1619}
1620
1621
1622/*********************************************************************
1623 *
1624 * Internal Method
1625 *
1626 * Perform the deletion of a complete storage node
1627 *
1628 */
1629static HRESULT deleteStorageProperty(
1630 StorageImpl *parentStorage,
1631 OLECHAR *propertyToDeleteName)
1632{
1633 IEnumSTATSTG *elements = 0;
1634 IStorage *childStorage = 0;
1635 STATSTG currentElement;
1636 HRESULT hr;
1637 HRESULT destroyHr = S_OK;
1638
1639 /*
1640 * Open the storage and enumerate it
1641 */
1642 hr = StorageBaseImpl_OpenStorage(
1643 (IStorage*)parentStorage,
1644 propertyToDeleteName,
1645 0,
1646 STGM_SHARE_EXCLUSIVE,
1647 0,
1648 0,
1649 &childStorage);
1650
1651 if (hr != S_OK)
1652 {
1653 return hr;
1654 }
1655
1656 /*
1657 * Enumerate the elements
1658 */
1659 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1660
1661 do
1662 {
1663 /*
1664 * Obtain the next element
1665 */
1666 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1667 if (hr==S_OK)
1668 {
1669 destroyHr = StorageImpl_DestroyElement(
1670 (IStorage*)childStorage,
1671 (OLECHAR*)currentElement.pwcsName);
1672
1673 CoTaskMemFree(currentElement.pwcsName);
1674 }
1675
1676 /*
1677 * We need to Reset the enumeration every time because we delete elements
1678 * and the enumeration could be invalid
1679 */
1680 IEnumSTATSTG_Reset(elements);
1681
1682 } while ((hr == S_OK) && (destroyHr == S_OK));
1683
1684 IStorage_Release(childStorage);
1685 IEnumSTATSTG_Release(elements);
1686
1687 return destroyHr;
1688}
1689
1690/*********************************************************************
1691 *
1692 * Internal Method
1693 *
1694 * Perform the deletion of a stream node
1695 *
1696 */
1697static HRESULT deleteStreamProperty(
1698 StorageImpl *parentStorage,
1699 ULONG indexOfPropertyToDelete,
1700 StgProperty propertyToDelete)
1701{
1702 IStream *pis;
1703 HRESULT hr;
1704 ULARGE_INTEGER size;
1705
1706 size.HighPart = 0;
1707 size.LowPart = 0;
1708
1709 hr = StorageBaseImpl_OpenStream(
1710 (IStorage*)parentStorage,
1711 (OLECHAR*)propertyToDelete.name,
1712 NULL,
1713 STGM_SHARE_EXCLUSIVE,
1714 0,
1715 &pis);
1716
1717 if (hr!=S_OK)
1718 {
1719 return(hr);
1720 }
1721
1722 /*
1723 * Zap the stream
1724 */
1725 hr = IStream_SetSize(pis, size);
1726
1727 if(hr != S_OK)
1728 {
1729 return hr;
1730 }
1731
1732 /*
1733 * Release the stream object.
1734 */
1735 IStream_Release(pis);
1736
1737 /*
1738 * Invalidate the property by zeroing it's name member.
1739 */
1740 propertyToDelete.sizeOfNameString = 0;
1741
1742 /*
1743 * Here we should re-read the property so we get the updated pointer
1744 * but since we are here to zap it, I don't do it...
1745 */
1746 StorageImpl_WriteProperty(
1747 parentStorage->ancestorStorage,
1748 indexOfPropertyToDelete,
1749 &propertyToDelete);
1750
1751 return S_OK;
1752}
1753
1754/*********************************************************************
1755 *
1756 * Internal Method
1757 *
1758 * Finds a placeholder for the StgProperty within the Storage
1759 *
1760 */
1761static HRESULT findPlaceholder(
1762 StorageImpl *storage,
1763 ULONG propertyIndexToStore,
1764 ULONG storePropertyIndex,
1765 INT typeOfRelation)
1766{
1767 StgProperty storeProperty;
1768 HRESULT hr = S_OK;
1769 BOOL res = TRUE;
1770
1771 /*
1772 * Read the storage property
1773 */
1774 res = StorageImpl_ReadProperty(
1775 storage->ancestorStorage,
1776 storePropertyIndex,
1777 &storeProperty);
1778
1779 if(! res)
1780 {
1781 return E_FAIL;
1782 }
1783
1784 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1785 {
1786 if (storeProperty.previousProperty != PROPERTY_NULL)
1787 {
1788 return findPlaceholder(
1789 storage,
1790 propertyIndexToStore,
1791 storeProperty.previousProperty,
1792 typeOfRelation);
1793 }
1794 else
1795 {
1796 storeProperty.previousProperty = propertyIndexToStore;
1797 }
1798 }
1799 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1800 {
1801 if (storeProperty.nextProperty != PROPERTY_NULL)
1802 {
1803 return findPlaceholder(
1804 storage,
1805 propertyIndexToStore,
1806 storeProperty.nextProperty,
1807 typeOfRelation);
1808 }
1809 else
1810 {
1811 storeProperty.nextProperty = propertyIndexToStore;
1812 }
1813 }
1814 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1815 {
1816 if (storeProperty.dirProperty != PROPERTY_NULL)
1817 {
1818 return findPlaceholder(
1819 storage,
1820 propertyIndexToStore,
1821 storeProperty.dirProperty,
1822 typeOfRelation);
1823 }
1824 else
1825 {
1826 storeProperty.dirProperty = propertyIndexToStore;
1827 }
1828 }
1829
1830 hr = StorageImpl_WriteProperty(
1831 storage->ancestorStorage,
1832 storePropertyIndex,
1833 &storeProperty);
1834
1835 if(! hr)
1836 {
1837 return E_FAIL;
1838 }
1839
1840 return S_OK;
1841}
1842
1843/*************************************************************************
1844 *
1845 * Internal Method
1846 *
1847 * This method takes the previous and the next property link of a property
1848 * to be deleted and find them a place in the Storage.
1849 */
1850static HRESULT adjustPropertyChain(
1851 StorageImpl *This,
1852 StgProperty propertyToDelete,
1853 StgProperty parentProperty,
1854 ULONG parentPropertyId,
1855 INT typeOfRelation)
1856{
1857 ULONG newLinkProperty = PROPERTY_NULL;
1858 BOOL needToFindAPlaceholder = FALSE;
1859 ULONG storeNode = PROPERTY_NULL;
1860 ULONG toStoreNode = PROPERTY_NULL;
1861 INT relationType = 0;
1862 HRESULT hr = S_OK;
1863 BOOL res = TRUE;
1864
1865 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1866 {
1867 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1868 {
1869 /*
1870 * Set the parent previous to the property to delete previous
1871 */
1872 newLinkProperty = propertyToDelete.previousProperty;
1873
1874 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1875 {
1876 /*
1877 * We also need to find a storage for the other link, setup variables
1878 * to do this at the end...
1879 */
1880 needToFindAPlaceholder = TRUE;
1881 storeNode = propertyToDelete.previousProperty;
1882 toStoreNode = propertyToDelete.nextProperty;
1883 relationType = PROPERTY_RELATION_NEXT;
1884 }
1885 }
1886 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1887 {
1888 /*
1889 * Set the parent previous to the property to delete next
1890 */
1891 newLinkProperty = propertyToDelete.nextProperty;
1892 }
1893
1894 /*
1895 * Link it for real...
1896 */
1897 parentProperty.previousProperty = newLinkProperty;
1898
1899 }
1900 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1901 {
1902 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1903 {
1904 /*
1905 * Set the parent next to the property to delete next previous
1906 */
1907 newLinkProperty = propertyToDelete.previousProperty;
1908
1909 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1910 {
1911 /*
1912 * We also need to find a storage for the other link, setup variables
1913 * to do this at the end...
1914 */
1915 needToFindAPlaceholder = TRUE;
1916 storeNode = propertyToDelete.previousProperty;
1917 toStoreNode = propertyToDelete.nextProperty;
1918 relationType = PROPERTY_RELATION_NEXT;
1919 }
1920 }
1921 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1922 {
1923 /*
1924 * Set the parent next to the property to delete next
1925 */
1926 newLinkProperty = propertyToDelete.nextProperty;
1927 }
1928
1929 /*
1930 * Link it for real...
1931 */
1932 parentProperty.nextProperty = newLinkProperty;
1933 }
1934 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1935 {
1936 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1937 {
1938 /*
1939 * Set the parent dir to the property to delete previous
1940 */
1941 newLinkProperty = propertyToDelete.previousProperty;
1942
1943 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1944 {
1945 /*
1946 * We also need to find a storage for the other link, setup variables
1947 * to do this at the end...
1948 */
1949 needToFindAPlaceholder = TRUE;
1950 storeNode = propertyToDelete.previousProperty;
1951 toStoreNode = propertyToDelete.nextProperty;
1952 relationType = PROPERTY_RELATION_NEXT;
1953 }
1954 }
1955 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1956 {
1957 /*
1958 * Set the parent dir to the property to delete next
1959 */
1960 newLinkProperty = propertyToDelete.nextProperty;
1961 }
1962
1963 /*
1964 * Link it for real...
1965 */
1966 parentProperty.dirProperty = newLinkProperty;
1967 }
1968
1969 /*
1970 * Write back the parent property
1971 */
1972 res = StorageImpl_WriteProperty(
1973 This->ancestorStorage,
1974 parentPropertyId,
1975 &parentProperty);
1976 if(! res)
1977 {
1978 return E_FAIL;
1979 }
1980
1981 /*
1982 * If a placeholder is required for the other link, then, find one and
1983 * get out of here...
1984 */
1985 if (needToFindAPlaceholder)
1986 {
1987 hr = findPlaceholder(
1988 This,
1989 toStoreNode,
1990 storeNode,
1991 relationType);
1992 }
1993
1994 return hr;
1995}
1996
1997
1998/******************************************************************************
1999 * SetElementTimes (IStorage)
2000 */
2001HRESULT WINAPI StorageImpl_SetElementTimes(
2002 IStorage* iface,
2003 const OLECHAR *pwcsName,/* [string][in] */
2004 const FILETIME *pctime, /* [in] */
2005 const FILETIME *patime, /* [in] */
2006 const FILETIME *pmtime) /* [in] */
2007{
2008 FIXME("not implemented!\n");
2009 return E_NOTIMPL;
2010}
2011
2012/******************************************************************************
2013 * SetStateBits (IStorage)
2014 */
2015HRESULT WINAPI StorageImpl_SetStateBits(
2016 IStorage* iface,
2017 DWORD grfStateBits,/* [in] */
2018 DWORD grfMask) /* [in] */
2019{
2020 FIXME("not implemented!\n");
2021 return E_NOTIMPL;
2022}
2023
2024HRESULT StorageImpl_Construct(
2025 StorageImpl* This,
2026 HANDLE hFile,
2027 ILockBytes* pLkbyt,
2028 DWORD openFlags,
2029 BOOL fileBased)
2030{
2031 HRESULT hr = S_OK;
2032 StgProperty currentProperty;
2033 BOOL readSucessful;
2034 ULONG currentPropertyIndex;
2035
2036 if ( FAILED( validateSTGM(openFlags) ))
2037 return STG_E_INVALIDFLAG;
2038
2039 memset(This, 0, sizeof(StorageImpl));
2040
2041 /*
2042 * Initialize the virtual fgunction table.
2043 */
2044 This->lpvtbl = &Storage32Impl_Vtbl;
2045 This->v_destructor = &StorageImpl_Destroy;
2046
2047 /*
2048 * This is the top-level storage so initialize the ancester pointer
2049 * to this.
2050 */
2051 This->ancestorStorage = This;
2052
2053 /*
2054 * Initialize the physical support of the storage.
2055 */
2056 This->hFile = hFile;
2057
2058 /*
2059 * Initialize the big block cache.
2060 */
2061 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2062 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2063 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2064 pLkbyt,
2065 openFlags,
2066 This->bigBlockSize,
2067 fileBased);
2068
2069 if (This->bigBlockFile == 0)
2070 return E_FAIL;
2071
2072 if (openFlags & STGM_CREATE)
2073 {
2074 ULARGE_INTEGER size;
2075 BYTE* bigBlockBuffer;
2076
2077 /*
2078 * Initialize all header variables:
2079 * - The big block depot consists of one block and it is at block 0
2080 * - The properties start at block 1
2081 * - There is no small block depot
2082 */
2083 memset( This->bigBlockDepotStart,
2084 BLOCK_UNUSED,
2085 sizeof(This->bigBlockDepotStart));
2086
2087 This->bigBlockDepotCount = 1;
2088 This->bigBlockDepotStart[0] = 0;
2089 This->rootStartBlock = 1;
2090 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2091 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2092 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2093 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2094 This->extBigBlockDepotCount = 0;
2095
2096 StorageImpl_SaveFileHeader(This);
2097
2098 /*
2099 * Add one block for the big block depot and one block for the properties
2100 */
2101 size.HighPart = 0;
2102 size.LowPart = This->bigBlockSize * 3;
2103 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2104
2105 /*
2106 * Initialize the big block depot
2107 */
2108 bigBlockBuffer = (BYTE *)StorageImpl_GetBigBlock(This, 0);
2109 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2110 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2111 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2112 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2113 }
2114 else
2115 {
2116 /*
2117 * Load the header for the file.
2118 */
2119 hr = StorageImpl_LoadFileHeader(This);
2120
2121 if (FAILED(hr))
2122 {
2123 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2124
2125 return hr;
2126 }
2127 }
2128
2129 /*
2130 * There is no block depot cached yet.
2131 */
2132 This->indexBlockDepotCached = 0xFFFFFFFF;
2133
2134 /*
2135 * Start searching for free blocks with block 0.
2136 */
2137 This->prevFreeBlock = 0;
2138
2139 /*
2140 * Create the block chain abstractions.
2141 */
2142 This->rootBlockChain =
2143 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2144
2145 This->smallBlockDepotChain = BlockChainStream_Construct(
2146 This,
2147 &This->smallBlockDepotStart,
2148 PROPERTY_NULL);
2149
2150 /*
2151 * Write the root property
2152 */
2153 if (openFlags & STGM_CREATE)
2154 {
2155 StgProperty rootProp;
2156 /*
2157 * Initialize the property chain
2158 */
2159 memset(&rootProp, 0, sizeof(rootProp));
2160 lstrcpyAtoW(rootProp.name, (LPSTR)rootPropertyName);
2161
2162 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2163 rootProp.propertyType = PROPTYPE_ROOT;
2164 rootProp.previousProperty = PROPERTY_NULL;
2165 rootProp.nextProperty = PROPERTY_NULL;
2166 rootProp.dirProperty = PROPERTY_NULL;
2167 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2168 rootProp.size.HighPart = 0;
2169 rootProp.size.LowPart = 0;
2170
2171 StorageImpl_WriteProperty(This, 0, &rootProp);
2172 }
2173
2174 /*
2175 * Find the ID of the root int he property sets.
2176 */
2177 currentPropertyIndex = 0;
2178
2179 do
2180 {
2181 readSucessful = StorageImpl_ReadProperty(
2182 This,
2183 currentPropertyIndex,
2184 &currentProperty);
2185
2186 if (readSucessful)
2187 {
2188 if ( (currentProperty.sizeOfNameString != 0 ) &&
2189 (currentProperty.propertyType == PROPTYPE_ROOT) )
2190 {
2191 This->rootPropertySetIndex = currentPropertyIndex;
2192 }
2193 }
2194
2195 currentPropertyIndex++;
2196
2197 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2198
2199 if (!readSucessful)
2200 {
2201 /* TODO CLEANUP */
2202 return E_FAIL;
2203 }
2204
2205 /*
2206 * Create the block chain abstraction for the small block root chain.
2207 */
2208 This->smallBlockRootChain = BlockChainStream_Construct(
2209 This,
2210 NULL,
2211 This->rootPropertySetIndex);
2212
2213 return hr;
2214}
2215
2216void StorageImpl_Destroy(
2217 StorageImpl* This)
2218{
2219 TRACE("(%p)\n", This);
2220
2221 BlockChainStream_Destroy(This->smallBlockRootChain);
2222 BlockChainStream_Destroy(This->rootBlockChain);
2223 BlockChainStream_Destroy(This->smallBlockDepotChain);
2224
2225 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2226 return;
2227}
2228
2229/******************************************************************************
2230 * Storage32Impl_GetNextFreeBigBlock
2231 *
2232 * Returns the index of the next free big block.
2233 * If the big block depot is filled, this method will enlarge it.
2234 *
2235 */
2236ULONG StorageImpl_GetNextFreeBigBlock(
2237 StorageImpl* This)
2238{
2239 ULONG depotBlockIndexPos;
2240 void *depotBuffer;
2241 ULONG depotBlockOffset;
2242 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2243 ULONG nextBlockIndex = BLOCK_SPECIAL;
2244 int depotIndex = 0;
2245 ULONG freeBlock = BLOCK_UNUSED;
2246
2247 depotIndex = This->prevFreeBlock / blocksPerDepot;
2248 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2249
2250 /*
2251 * Scan the entire big block depot until we find a block marked free
2252 */
2253 while (nextBlockIndex != BLOCK_UNUSED)
2254 {
2255 if (depotIndex < COUNT_BBDEPOTINHEADER)
2256 {
2257 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2258
2259 /*
2260 * Grow the primary depot.
2261 */
2262 if (depotBlockIndexPos == BLOCK_UNUSED)
2263 {
2264 depotBlockIndexPos = depotIndex*blocksPerDepot;
2265
2266 /*
2267 * Add a block depot.
2268 */
2269 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2270 This->bigBlockDepotCount++;
2271 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2272
2273 /*
2274 * Flag it as a block depot.
2275 */
2276 StorageImpl_SetNextBlockInChain(This,
2277 depotBlockIndexPos,
2278 BLOCK_SPECIAL);
2279
2280 /* Save new header information.
2281 */
2282 StorageImpl_SaveFileHeader(This);
2283 }
2284 }
2285 else
2286 {
2287 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2288
2289 if (depotBlockIndexPos == BLOCK_UNUSED)
2290 {
2291 /*
2292 * Grow the extended depot.
2293 */
2294 ULONG extIndex = BLOCK_UNUSED;
2295 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2296 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2297
2298 if (extBlockOffset == 0)
2299 {
2300 /* We need an extended block.
2301 */
2302 extIndex = Storage32Impl_AddExtBlockDepot(This);
2303 This->extBigBlockDepotCount++;
2304 depotBlockIndexPos = extIndex + 1;
2305 }
2306 else
2307 depotBlockIndexPos = depotIndex * blocksPerDepot;
2308
2309 /*
2310 * Add a block depot and mark it in the extended block.
2311 */
2312 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2313 This->bigBlockDepotCount++;
2314 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2315
2316 /* Flag the block depot.
2317 */
2318 StorageImpl_SetNextBlockInChain(This,
2319 depotBlockIndexPos,
2320 BLOCK_SPECIAL);
2321
2322 /* If necessary, flag the extended depot block.
2323 */
2324 if (extIndex != BLOCK_UNUSED)
2325 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2326
2327 /* Save header information.
2328 */
2329 StorageImpl_SaveFileHeader(This);
2330 }
2331 }
2332
2333 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2334
2335 if (depotBuffer != 0)
2336 {
2337 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2338 ( nextBlockIndex != BLOCK_UNUSED))
2339 {
2340 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2341
2342 if (nextBlockIndex == BLOCK_UNUSED)
2343 {
2344 freeBlock = (depotIndex * blocksPerDepot) +
2345 (depotBlockOffset/sizeof(ULONG));
2346 }
2347
2348 depotBlockOffset += sizeof(ULONG);
2349 }
2350
2351 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2352 }
2353
2354 depotIndex++;
2355 depotBlockOffset = 0;
2356 }
2357
2358 This->prevFreeBlock = freeBlock;
2359
2360 return freeBlock;
2361}
2362
2363/******************************************************************************
2364 * Storage32Impl_AddBlockDepot
2365 *
2366 * This will create a depot block, essentially it is a block initialized
2367 * to BLOCK_UNUSEDs.
2368 */
2369void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2370{
2371 BYTE* blockBuffer;
2372
2373 blockBuffer = (BYTE *)StorageImpl_GetBigBlock(This, blockIndex);
2374
2375 /*
2376 * Initialize blocks as free
2377 */
2378 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2379
2380 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2381}
2382
2383/******************************************************************************
2384 * Storage32Impl_GetExtDepotBlock
2385 *
2386 * Returns the index of the block that corresponds to the specified depot
2387 * index. This method is only for depot indexes equal or greater than
2388 * COUNT_BBDEPOTINHEADER.
2389 */
2390ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2391{
2392 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2393 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2394 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2395 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2396 ULONG blockIndex = BLOCK_UNUSED;
2397 ULONG extBlockIndex = This->extBigBlockDepotStart;
2398
2399 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2400
2401 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2402 return BLOCK_UNUSED;
2403
2404 while (extBlockCount > 0)
2405 {
2406 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2407 extBlockCount--;
2408 }
2409
2410 if (extBlockIndex != BLOCK_UNUSED)
2411 {
2412 BYTE* depotBuffer;
2413
2414 depotBuffer = (BYTE *)StorageImpl_GetROBigBlock(This, extBlockIndex);
2415
2416 if (depotBuffer != 0)
2417 {
2418 StorageUtl_ReadDWord(depotBuffer,
2419 extBlockOffset * sizeof(ULONG),
2420 &blockIndex);
2421
2422 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2423 }
2424 }
2425
2426 return blockIndex;
2427}
2428
2429/******************************************************************************
2430 * Storage32Impl_SetExtDepotBlock
2431 *
2432 * Associates the specified block index to the specified depot index.
2433 * This method is only for depot indexes equal or greater than
2434 * COUNT_BBDEPOTINHEADER.
2435 */
2436void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2437 ULONG depotIndex,
2438 ULONG blockIndex)
2439{
2440 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2441 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2442 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2443 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2444 ULONG extBlockIndex = This->extBigBlockDepotStart;
2445
2446 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2447
2448 while (extBlockCount > 0)
2449 {
2450 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2451 extBlockCount--;
2452 }
2453
2454 if (extBlockIndex != BLOCK_UNUSED)
2455 {
2456 BYTE* depotBuffer;
2457
2458 depotBuffer = (BYTE *)StorageImpl_GetBigBlock(This, extBlockIndex);
2459
2460 if (depotBuffer != 0)
2461 {
2462 StorageUtl_WriteDWord(depotBuffer,
2463 extBlockOffset * sizeof(ULONG),
2464 blockIndex);
2465
2466 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2467 }
2468 }
2469}
2470
2471/******************************************************************************
2472 * Storage32Impl_AddExtBlockDepot
2473 *
2474 * Creates an extended depot block.
2475 */
2476ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2477{
2478 ULONG numExtBlocks = This->extBigBlockDepotCount;
2479 ULONG nextExtBlock = This->extBigBlockDepotStart;
2480 BYTE* depotBuffer = NULL;
2481 ULONG index = BLOCK_UNUSED;
2482 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2483 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2484 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2485
2486 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2487 blocksPerDepotBlock;
2488
2489 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2490 {
2491 /*
2492 * The first extended block.
2493 */
2494 This->extBigBlockDepotStart = index;
2495 }
2496 else
2497 {
2498 int i;
2499 /*
2500 * Follow the chain to the last one.
2501 */
2502 for (i = 0; i < (numExtBlocks - 1); i++)
2503 {
2504 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2505 }
2506
2507 /*
2508 * Add the new extended block to the chain.
2509 */
2510 depotBuffer = (BYTE *)StorageImpl_GetBigBlock(This, nextExtBlock);
2511 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2512 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2513 }
2514
2515 /*
2516 * Initialize this block.
2517 */
2518 depotBuffer = (BYTE *)StorageImpl_GetBigBlock(This, index);
2519 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2520 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2521
2522 return index;
2523}
2524
2525/******************************************************************************
2526 * Storage32Impl_FreeBigBlock
2527 *
2528 * This method will flag the specified block as free in the big block depot.
2529 */
2530void StorageImpl_FreeBigBlock(
2531 StorageImpl* This,
2532 ULONG blockIndex)
2533{
2534 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2535
2536 if (blockIndex < This->prevFreeBlock)
2537 This->prevFreeBlock = blockIndex;
2538}
2539
2540/************************************************************************
2541 * Storage32Impl_GetNextBlockInChain
2542 *
2543 * This method will retrieve the block index of the next big block in
2544 * in the chain.
2545 *
2546 * Params: This - Pointer to the Storage object.
2547 * blockIndex - Index of the block to retrieve the chain
2548 * for.
2549 *
2550 * Returns: This method returns the index of the next block in the chain.
2551 * It will return the constants:
2552 * BLOCK_SPECIAL - If the block given was not part of a
2553 * chain.
2554 * BLOCK_END_OF_CHAIN - If the block given was the last in
2555 * a chain.
2556 * BLOCK_UNUSED - If the block given was not past of a chain
2557 * and is available.
2558 * BLOCK_EXTBBDEPOT - This block is part of the extended
2559 * big block depot.
2560 *
2561 * See Windows documentation for more details on IStorage methods.
2562 */
2563ULONG StorageImpl_GetNextBlockInChain(
2564 StorageImpl* This,
2565 ULONG blockIndex)
2566{
2567 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2568 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2569 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2570 ULONG nextBlockIndex = BLOCK_SPECIAL;
2571 void* depotBuffer;
2572 ULONG depotBlockIndexPos;
2573
2574 assert(depotBlockCount < This->bigBlockDepotCount);
2575
2576 /*
2577 * Cache the currently accessed depot block.
2578 */
2579 if (depotBlockCount != This->indexBlockDepotCached)
2580 {
2581 This->indexBlockDepotCached = depotBlockCount;
2582
2583 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2584 {
2585 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2586 }
2587 else
2588 {
2589 /*
2590 * We have to look in the extended depot.
2591 */
2592 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2593 }
2594
2595 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2596
2597 if (depotBuffer!=0)
2598 {
2599 int index;
2600
2601 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2602 {
2603 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2604 This->blockDepotCached[index] = nextBlockIndex;
2605 }
2606
2607 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2608 }
2609 }
2610
2611 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2612
2613 return nextBlockIndex;
2614}
2615
2616/******************************************************************************
2617 * Storage32Impl_GetNextExtendedBlock
2618 *
2619 * Given an extended block this method will return the next extended block.
2620 *
2621 * NOTES:
2622 * The last ULONG of an extended block is the block index of the next
2623 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2624 * depot.
2625 *
2626 * Return values:
2627 * - The index of the next extended block
2628 * - BLOCK_UNUSED: there is no next extended block.
2629 * - Any other return values denotes failure.
2630 */
2631ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2632{
2633 ULONG nextBlockIndex = BLOCK_SPECIAL;
2634 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2635 void* depotBuffer;
2636
2637 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2638
2639 if (depotBuffer!=0)
2640 {
2641 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2642
2643 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2644 }
2645
2646 return nextBlockIndex;
2647}
2648
2649/******************************************************************************
2650 * Storage32Impl_SetNextBlockInChain
2651 *
2652 * This method will write the index of the specified block's next block
2653 * in the big block depot.
2654 *
2655 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2656 * do the following
2657 *
2658 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2659 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2660 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2661 *
2662 */
2663void StorageImpl_SetNextBlockInChain(
2664 StorageImpl* This,
2665 ULONG blockIndex,
2666 ULONG nextBlock)
2667{
2668 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2669 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2670 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2671 ULONG depotBlockIndexPos;
2672 void* depotBuffer;
2673
2674 assert(depotBlockCount < This->bigBlockDepotCount);
2675 assert(blockIndex != nextBlock);
2676
2677 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2678 {
2679 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2680 }
2681 else
2682 {
2683 /*
2684 * We have to look in the extended depot.
2685 */
2686 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2687 }
2688
2689 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2690
2691 if (depotBuffer!=0)
2692 {
2693 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2694 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2695 }
2696
2697 /*
2698 * Update the cached block depot, if necessary.
2699 */
2700 if (depotBlockCount == This->indexBlockDepotCached)
2701 {
2702 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2703 }
2704}
2705
2706/******************************************************************************
2707 * Storage32Impl_LoadFileHeader
2708 *
2709 * This method will read in the file header, i.e. big block index -1.
2710 */
2711HRESULT StorageImpl_LoadFileHeader(
2712 StorageImpl* This)
2713{
2714 HRESULT hr = STG_E_FILENOTFOUND;
2715 void* headerBigBlock = NULL;
2716 int index;
2717
2718 /*
2719 * Get a pointer to the big block of data containing the header.
2720 */
2721 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2722
2723 /*
2724 * Extract the information from the header.
2725 */
2726 if (headerBigBlock!=0)
2727 {
2728 /*
2729 * Check for the "magic number" signature and return an error if it is not
2730 * found.
2731 */
2732 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2733 {
2734 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2735 return STG_E_OLDFORMAT;
2736 }
2737
2738 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2739 {
2740 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2741 return STG_E_INVALIDHEADER;
2742 }
2743
2744 StorageUtl_ReadWord(
2745 headerBigBlock,
2746 OFFSET_BIGBLOCKSIZEBITS,
2747 &This->bigBlockSizeBits);
2748
2749 StorageUtl_ReadWord(
2750 headerBigBlock,
2751 OFFSET_SMALLBLOCKSIZEBITS,
2752 &This->smallBlockSizeBits);
2753
2754 StorageUtl_ReadDWord(
2755 headerBigBlock,
2756 OFFSET_BBDEPOTCOUNT,
2757 &This->bigBlockDepotCount);
2758
2759 StorageUtl_ReadDWord(
2760 headerBigBlock,
2761 OFFSET_ROOTSTARTBLOCK,
2762 &This->rootStartBlock);
2763
2764 StorageUtl_ReadDWord(
2765 headerBigBlock,
2766 OFFSET_SBDEPOTSTART,
2767 &This->smallBlockDepotStart);
2768
2769 StorageUtl_ReadDWord(
2770 headerBigBlock,
2771 OFFSET_EXTBBDEPOTSTART,
2772 &This->extBigBlockDepotStart);
2773
2774 StorageUtl_ReadDWord(
2775 headerBigBlock,
2776 OFFSET_EXTBBDEPOTCOUNT,
2777 &This->extBigBlockDepotCount);
2778
2779 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2780 {
2781 StorageUtl_ReadDWord(
2782 headerBigBlock,
2783 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2784 &(This->bigBlockDepotStart[index]));
2785 }
2786
2787 /*
2788 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2789 */
2790 if ((1 << 2) == 4)
2791 {
2792 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2793 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2794 }
2795 else
2796 {
2797 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2798 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2799 }
2800
2801 /*
2802 * Right now, the code is making some assumptions about the size of the
2803 * blocks, just make sure they are what we're expecting.
2804 */
2805 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2806 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2807
2808 /*
2809 * Release the block.
2810 */
2811 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2812
2813 hr = S_OK;
2814 }
2815
2816 return hr;
2817}
2818
2819/******************************************************************************
2820 * Storage32Impl_SaveFileHeader
2821 *
2822 * This method will save to the file the header, i.e. big block -1.
2823 */
2824void StorageImpl_SaveFileHeader(
2825 StorageImpl* This)
2826{
2827 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2828 int index;
2829 BOOL success;
2830
2831 /*
2832 * Get a pointer to the big block of data containing the header.
2833 */
2834 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2835
2836 /*
2837 * If the block read failed, the file is probably new.
2838 */
2839 if (!success)
2840 {
2841 /*
2842 * Initialize for all unknown fields.
2843 */
2844 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2845
2846 /*
2847 * Initialize the magic number.
2848 */
2849 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2850
2851 /*
2852 * And a bunch of things we don't know what they mean
2853 */
2854 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2855 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2856 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2857 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2858 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2859 }
2860
2861 /*
2862 * Write the information to the header.
2863 */
2864 if (headerBigBlock!=0)
2865 {
2866 StorageUtl_WriteWord(
2867 headerBigBlock,
2868 OFFSET_BIGBLOCKSIZEBITS,
2869 This->bigBlockSizeBits);
2870
2871 StorageUtl_WriteWord(
2872 headerBigBlock,
2873 OFFSET_SMALLBLOCKSIZEBITS,
2874 This->smallBlockSizeBits);
2875
2876 StorageUtl_WriteDWord(
2877 headerBigBlock,
2878 OFFSET_BBDEPOTCOUNT,
2879 This->bigBlockDepotCount);
2880
2881 StorageUtl_WriteDWord(
2882 headerBigBlock,
2883 OFFSET_ROOTSTARTBLOCK,
2884 This->rootStartBlock);
2885
2886 StorageUtl_WriteDWord(
2887 headerBigBlock,
2888 OFFSET_SBDEPOTSTART,
2889 This->smallBlockDepotStart);
2890
2891 StorageUtl_WriteDWord(
2892 headerBigBlock,
2893 OFFSET_EXTBBDEPOTSTART,
2894 This->extBigBlockDepotStart);
2895
2896 StorageUtl_WriteDWord(
2897 headerBigBlock,
2898 OFFSET_EXTBBDEPOTCOUNT,
2899 This->extBigBlockDepotCount);
2900
2901 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2902 {
2903 StorageUtl_WriteDWord(
2904 headerBigBlock,
2905 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2906 (This->bigBlockDepotStart[index]));
2907 }
2908 }
2909
2910 /*
2911 * Write the big block back to the file.
2912 */
2913 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2914}
2915
2916/******************************************************************************
2917 * Storage32Impl_ReadProperty
2918 *
2919 * This method will read the specified property from the property chain.
2920 */
2921BOOL StorageImpl_ReadProperty(
2922 StorageImpl* This,
2923 ULONG index,
2924 StgProperty* buffer)
2925{
2926 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2927 ULARGE_INTEGER offsetInPropSet;
2928 BOOL readSucessful;
2929 ULONG bytesRead;
2930
2931 offsetInPropSet.HighPart = 0;
2932 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2933
2934 readSucessful = BlockChainStream_ReadAt(
2935 This->rootBlockChain,
2936 offsetInPropSet,
2937 PROPSET_BLOCK_SIZE,
2938 currentProperty,
2939 &bytesRead);
2940
2941 if (readSucessful)
2942 {
2943 memset(buffer->name, 0, sizeof(buffer->name));
2944 memcpy(
2945 buffer->name,
2946 currentProperty+OFFSET_PS_NAME,
2947 PROPERTY_NAME_BUFFER_LEN );
2948
2949 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2950
2951 StorageUtl_ReadWord(
2952 currentProperty,
2953 OFFSET_PS_NAMELENGTH,
2954 &buffer->sizeOfNameString);
2955
2956 StorageUtl_ReadDWord(
2957 currentProperty,
2958 OFFSET_PS_PREVIOUSPROP,
2959 &buffer->previousProperty);
2960
2961 StorageUtl_ReadDWord(
2962 currentProperty,
2963 OFFSET_PS_NEXTPROP,
2964 &buffer->nextProperty);
2965
2966 StorageUtl_ReadDWord(
2967 currentProperty,
2968 OFFSET_PS_DIRPROP,
2969 &buffer->dirProperty);
2970
2971 StorageUtl_ReadGUID(
2972 currentProperty,
2973 OFFSET_PS_GUID,
2974 &buffer->propertyUniqueID);
2975
2976 StorageUtl_ReadDWord(
2977 currentProperty,
2978 OFFSET_PS_TSS1,
2979 &buffer->timeStampS1);
2980
2981 StorageUtl_ReadDWord(
2982 currentProperty,
2983 OFFSET_PS_TSD1,
2984 &buffer->timeStampD1);
2985
2986 StorageUtl_ReadDWord(
2987 currentProperty,
2988 OFFSET_PS_TSS2,
2989 &buffer->timeStampS2);
2990
2991 StorageUtl_ReadDWord(
2992 currentProperty,
2993 OFFSET_PS_TSD2,
2994 &buffer->timeStampD2);
2995
2996 StorageUtl_ReadDWord(
2997 currentProperty,
2998 OFFSET_PS_STARTBLOCK,
2999 &buffer->startingBlock);
3000
3001 StorageUtl_ReadDWord(
3002 currentProperty,
3003 OFFSET_PS_SIZE,
3004 &buffer->size.LowPart);
3005
3006 buffer->size.HighPart = 0;
3007 }
3008
3009 return readSucessful;
3010}
3011
3012/*********************************************************************
3013 * Write the specified property into the property chain
3014 */
3015BOOL StorageImpl_WriteProperty(
3016 StorageImpl* This,
3017 ULONG index,
3018 StgProperty* buffer)
3019{
3020 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3021 ULARGE_INTEGER offsetInPropSet;
3022 BOOL writeSucessful;
3023 ULONG bytesWritten;
3024
3025 offsetInPropSet.HighPart = 0;
3026 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
3027
3028 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3029
3030 memcpy(
3031 currentProperty + OFFSET_PS_NAME,
3032 buffer->name,
3033 PROPERTY_NAME_BUFFER_LEN );
3034
3035 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3036
3037 /*
3038 * Reassign the size in case of mistake....
3039 */
3040 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3041
3042 StorageUtl_WriteWord(
3043 currentProperty,
3044 OFFSET_PS_NAMELENGTH,
3045 buffer->sizeOfNameString);
3046
3047 StorageUtl_WriteDWord(
3048 currentProperty,
3049 OFFSET_PS_PREVIOUSPROP,
3050 buffer->previousProperty);
3051
3052 StorageUtl_WriteDWord(
3053 currentProperty,
3054 OFFSET_PS_NEXTPROP,
3055 buffer->nextProperty);
3056
3057 StorageUtl_WriteDWord(
3058 currentProperty,
3059 OFFSET_PS_DIRPROP,
3060 buffer->dirProperty);
3061
3062 StorageUtl_WriteGUID(
3063 currentProperty,
3064 OFFSET_PS_GUID,
3065 &buffer->propertyUniqueID);
3066
3067 StorageUtl_WriteDWord(
3068 currentProperty,
3069 OFFSET_PS_TSS1,
3070 buffer->timeStampS1);
3071
3072 StorageUtl_WriteDWord(
3073 currentProperty,
3074 OFFSET_PS_TSD1,
3075 buffer->timeStampD1);
3076
3077 StorageUtl_WriteDWord(
3078 currentProperty,
3079 OFFSET_PS_TSS2,
3080 buffer->timeStampS2);
3081
3082 StorageUtl_WriteDWord(
3083 currentProperty,
3084 OFFSET_PS_TSD2,
3085 buffer->timeStampD2);
3086
3087 StorageUtl_WriteDWord(
3088 currentProperty,
3089 OFFSET_PS_STARTBLOCK,
3090 buffer->startingBlock);
3091
3092 StorageUtl_WriteDWord(
3093 currentProperty,
3094 OFFSET_PS_SIZE,
3095 buffer->size.LowPart);
3096
3097 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3098 offsetInPropSet,
3099 PROPSET_BLOCK_SIZE,
3100 currentProperty,
3101 &bytesWritten);
3102 return writeSucessful;
3103}
3104
3105BOOL StorageImpl_ReadBigBlock(
3106 StorageImpl* This,
3107 ULONG blockIndex,
3108 void* buffer)
3109{
3110 void* bigBlockBuffer;
3111
3112 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3113
3114 if (bigBlockBuffer!=0)
3115 {
3116 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3117
3118 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3119
3120 return TRUE;
3121 }
3122
3123 return FALSE;
3124}
3125
3126BOOL StorageImpl_WriteBigBlock(
3127 StorageImpl* This,
3128 ULONG blockIndex,
3129 void* buffer)
3130{
3131 void* bigBlockBuffer;
3132
3133 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3134
3135 if (bigBlockBuffer!=0)
3136 {
3137 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3138
3139 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3140
3141 return TRUE;
3142 }
3143
3144 return FALSE;
3145}
3146
3147void* StorageImpl_GetROBigBlock(
3148 StorageImpl* This,
3149 ULONG blockIndex)
3150{
3151 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3152}
3153
3154void* StorageImpl_GetBigBlock(
3155 StorageImpl* This,
3156 ULONG blockIndex)
3157{
3158 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3159}
3160
3161void StorageImpl_ReleaseBigBlock(
3162 StorageImpl* This,
3163 void* pBigBlock)
3164{
3165 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3166}
3167
3168/******************************************************************************
3169 * Storage32Impl_SmallBlocksToBigBlocks
3170 *
3171 * This method will convert a small block chain to a big block chain.
3172 * The small block chain will be destroyed.
3173 */
3174BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3175 StorageImpl* This,
3176 SmallBlockChainStream** ppsbChain)
3177{
3178 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3179 ULARGE_INTEGER size, offset;
3180 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3181 ULONG propertyIndex;
3182 BOOL successRead, successWrite;
3183 StgProperty chainProperty;
3184 BYTE *buffer;
3185 BlockChainStream *bbTempChain = NULL;
3186 BlockChainStream *bigBlockChain = NULL;
3187
3188 /*
3189 * Create a temporary big block chain that doesn't have
3190 * an associated property. This temporary chain will be
3191 * used to copy data from small blocks to big blocks.
3192 */
3193 bbTempChain = BlockChainStream_Construct(This,
3194 &bbHeadOfChain,
3195 PROPERTY_NULL);
3196
3197 /*
3198 * Grow the big block chain.
3199 */
3200 size = SmallBlockChainStream_GetSize(*ppsbChain);
3201 BlockChainStream_SetSize(bbTempChain, size);
3202
3203 /*
3204 * Copy the contents of the small block chain to the big block chain
3205 * by small block size increments.
3206 */
3207 offset.LowPart = 0;
3208 offset.HighPart = 0;
3209 cbTotalRead = 0;
3210 cbTotalWritten = 0;
3211
3212 buffer = (BYTE *) malloc(DEF_SMALL_BLOCK_SIZE);
3213 do
3214 {
3215 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3216 offset,
3217 DEF_SMALL_BLOCK_SIZE,
3218 buffer,
3219 &cbRead);
3220 cbTotalRead += cbRead;
3221
3222 successWrite = BlockChainStream_WriteAt(bbTempChain,
3223 offset,
3224 cbRead,
3225 buffer,
3226 &cbWritten);
3227 cbTotalWritten += cbWritten;
3228
3229 offset.LowPart += This->smallBlockSize;
3230
3231 } while (successRead && successWrite);
3232 free(buffer);
3233
3234 assert(cbTotalRead == cbTotalWritten);
3235
3236 /*
3237 * Destroy the small block chain.
3238 */
3239 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3240 size.HighPart = 0;
3241 size.LowPart = 0;
3242 SmallBlockChainStream_SetSize(*ppsbChain, size);
3243 SmallBlockChainStream_Destroy(*ppsbChain);
3244 *ppsbChain = 0;
3245
3246 /*
3247 * Change the property information. This chain is now a big block chain
3248 * and it doesn't reside in the small blocks chain anymore.
3249 */
3250 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3251
3252 chainProperty.startingBlock = bbHeadOfChain;
3253
3254 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3255
3256 /*
3257 * Destroy the temporary propertyless big block chain.
3258 * Create a new big block chain associated with this property.
3259 */
3260 BlockChainStream_Destroy(bbTempChain);
3261 bigBlockChain = BlockChainStream_Construct(This,
3262 NULL,
3263 propertyIndex);
3264
3265 return bigBlockChain;
3266}
3267
3268/******************************************************************************
3269** Storage32InternalImpl implementation
3270*/
3271
3272StorageInternalImpl* StorageInternalImpl_Construct(
3273 StorageImpl* ancestorStorage,
3274 ULONG rootPropertyIndex)
3275{
3276 StorageInternalImpl* newStorage;
3277
3278 /*
3279 * Allocate space for the new storage object
3280 */
3281 newStorage = (StorageInternalImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3282
3283 if (newStorage!=0)
3284 {
3285 memset(newStorage, 0, sizeof(StorageInternalImpl));
3286
3287 /*
3288 * Initialize the virtual function table.
3289 */
3290 newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
3291 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3292
3293 /*
3294 * Keep the ancestor storage pointer and nail a reference to it.
3295 */
3296 newStorage->ancestorStorage = ancestorStorage;
3297 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3298
3299 /*
3300 * Keep the index of the root property set for this storage,
3301 */
3302 newStorage->rootPropertySetIndex = rootPropertyIndex;
3303
3304 return newStorage;
3305 }
3306
3307 return 0;
3308}
3309
3310void StorageInternalImpl_Destroy(
3311 StorageInternalImpl* This)
3312{
3313 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3314 HeapFree(GetProcessHeap(), 0, This);
3315}
3316
3317/******************************************************************************
3318**
3319** Storage32InternalImpl_Commit
3320**
3321** The non-root storages cannot be opened in transacted mode thus this function
3322** does nothing.
3323*/
3324HRESULT WINAPI StorageInternalImpl_Commit(
3325 IStorage* iface,
3326 DWORD grfCommitFlags) /* [in] */
3327{
3328 return S_OK;
3329}
3330
3331/******************************************************************************
3332**
3333** Storage32InternalImpl_Revert
3334**
3335** The non-root storages cannot be opened in transacted mode thus this function
3336** does nothing.
3337*/
3338HRESULT WINAPI StorageInternalImpl_Revert(
3339 IStorage* iface)
3340{
3341 return S_OK;
3342}
3343
3344/******************************************************************************
3345** IEnumSTATSTGImpl implementation
3346*/
3347
3348IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3349 StorageImpl* parentStorage,
3350 ULONG firstPropertyNode)
3351{
3352 IEnumSTATSTGImpl* newEnumeration;
3353
3354 newEnumeration = (IEnumSTATSTGImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3355
3356 if (newEnumeration!=0)
3357 {
3358 /*
3359 * Set-up the virtual function table and reference count.
3360 */
3361 newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
3362 newEnumeration->ref = 0;
3363
3364 /*
3365 * We want to nail-down the reference to the storage in case the
3366 * enumeration out-lives the storage in the client application.
3367 */
3368 newEnumeration->parentStorage = parentStorage;
3369 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3370
3371 newEnumeration->firstPropertyNode = firstPropertyNode;
3372
3373 /*
3374 * Initialize the search stack
3375 */
3376 newEnumeration->stackSize = 0;
3377 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3378 newEnumeration->stackToVisit =
3379 (ULONG *)HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3380
3381 /*
3382 * Make sure the current node of the iterator is the first one.
3383 */
3384 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3385 }
3386
3387 return newEnumeration;
3388}
3389
3390void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3391{
3392 IStorage_Release((IStorage*)This->parentStorage);
3393 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3394 HeapFree(GetProcessHeap(), 0, This);
3395}
3396
3397HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3398 IEnumSTATSTG* iface,
3399 REFIID riid,
3400 void** ppvObject)
3401{
3402 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3403
3404 /*
3405 * Perform a sanity check on the parameters.
3406 */
3407 if (ppvObject==0)
3408 return E_INVALIDARG;
3409
3410 /*
3411 * Initialize the return parameter.
3412 */
3413 *ppvObject = 0;
3414
3415 /*
3416 * Compare the riid with the interface IDs implemented by this object.
3417 */
3418 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3419 {
3420 *ppvObject = (IEnumSTATSTG*)This;
3421 }
3422 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3423 {
3424 *ppvObject = (IEnumSTATSTG*)This;
3425 }
3426
3427 /*
3428 * Check that we obtained an interface.
3429 */
3430 if ((*ppvObject)==0)
3431 return E_NOINTERFACE;
3432
3433 /*
3434 * Query Interface always increases the reference count by one when it is
3435 * successful
3436 */
3437 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3438
3439 return S_OK;
3440}
3441
3442ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3443 IEnumSTATSTG* iface)
3444{
3445 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3446
3447 This->ref++;
3448 return This->ref;
3449}
3450
3451ULONG WINAPI IEnumSTATSTGImpl_Release(
3452 IEnumSTATSTG* iface)
3453{
3454 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3455
3456 ULONG newRef;
3457
3458 This->ref--;
3459 newRef = This->ref;
3460
3461 /*
3462 * If the reference count goes down to 0, perform suicide.
3463 */
3464 if (newRef==0)
3465 {
3466 IEnumSTATSTGImpl_Destroy(This);
3467 }
3468
3469 return newRef;;
3470}
3471
3472HRESULT WINAPI IEnumSTATSTGImpl_Next(
3473 IEnumSTATSTG* iface,
3474 ULONG celt,
3475 STATSTG* rgelt,
3476 ULONG* pceltFetched)
3477{
3478 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3479
3480 StgProperty currentProperty;
3481 STATSTG* currentReturnStruct = rgelt;
3482 ULONG objectFetched = 0;
3483 ULONG currentSearchNode;
3484
3485 /*
3486 * Perform a sanity check on the parameters.
3487 */
3488 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3489 return E_INVALIDARG;
3490
3491 /*
3492 * To avoid the special case, get another pointer to a ULONG value if
3493 * the caller didn't supply one.
3494 */
3495 if (pceltFetched==0)
3496 pceltFetched = &objectFetched;
3497
3498 /*
3499 * Start the iteration, we will iterate until we hit the end of the
3500 * linked list or until we hit the number of items to iterate through
3501 */
3502 *pceltFetched = 0;
3503
3504 /*
3505 * Start with the node at the top of the stack.
3506 */
3507 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3508
3509 while ( ( *pceltFetched < celt) &&
3510 ( currentSearchNode!=PROPERTY_NULL) )
3511 {
3512 /*
3513 * Remove the top node from the stack
3514 */
3515 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3516
3517 /*
3518 * Read the property from the storage.
3519 */
3520 StorageImpl_ReadProperty(This->parentStorage,
3521 currentSearchNode,
3522 &currentProperty);
3523
3524 /*
3525 * Copy the information to the return buffer.
3526 */
3527 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3528 &currentProperty,
3529 STATFLAG_DEFAULT);
3530
3531 /*
3532 * Step to the next item in the iteration
3533 */
3534 (*pceltFetched)++;
3535 currentReturnStruct++;
3536
3537 /*
3538 * Push the next search node in the search stack.
3539 */
3540 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3541
3542 /*
3543 * continue the iteration.
3544 */
3545 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3546 }
3547
3548 if (*pceltFetched == celt)
3549 return S_OK;
3550
3551 return S_FALSE;
3552}
3553
3554
3555HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3556 IEnumSTATSTG* iface,
3557 ULONG celt)
3558{
3559 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3560
3561 StgProperty currentProperty;
3562 ULONG objectFetched = 0;
3563 ULONG currentSearchNode;
3564
3565 /*
3566 * Start with the node at the top of the stack.
3567 */
3568 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3569
3570 while ( (objectFetched < celt) &&
3571 (currentSearchNode!=PROPERTY_NULL) )
3572 {
3573 /*
3574 * Remove the top node from the stack
3575 */
3576 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3577
3578 /*
3579 * Read the property from the storage.
3580 */
3581 StorageImpl_ReadProperty(This->parentStorage,
3582 currentSearchNode,
3583 &currentProperty);
3584
3585 /*
3586 * Step to the next item in the iteration
3587 */
3588 objectFetched++;
3589
3590 /*
3591 * Push the next search node in the search stack.
3592 */
3593 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3594
3595 /*
3596 * continue the iteration.
3597 */
3598 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3599 }
3600
3601 if (objectFetched == celt)
3602 return S_OK;
3603
3604 return S_FALSE;
3605}
3606
3607HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3608 IEnumSTATSTG* iface)
3609{
3610 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3611
3612 StgProperty rootProperty;
3613 BOOL readSucessful;
3614
3615 /*
3616 * Re-initialize the search stack to an empty stack
3617 */
3618 This->stackSize = 0;
3619
3620 /*
3621 * Read the root property from the storage.
3622 */
3623 readSucessful = StorageImpl_ReadProperty(
3624 This->parentStorage,
3625 This->firstPropertyNode,
3626 &rootProperty);
3627
3628 if (readSucessful)
3629 {
3630 assert(rootProperty.sizeOfNameString!=0);
3631
3632 /*
3633 * Push the search node in the search stack.
3634 */
3635 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3636 }
3637
3638 return S_OK;
3639}
3640
3641HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3642 IEnumSTATSTG* iface,
3643 IEnumSTATSTG** ppenum)
3644{
3645 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3646
3647 IEnumSTATSTGImpl* newClone;
3648
3649 /*
3650 * Perform a sanity check on the parameters.
3651 */
3652 if (ppenum==0)
3653 return E_INVALIDARG;
3654
3655 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3656 This->firstPropertyNode);
3657
3658
3659 /*
3660 * The new clone enumeration must point to the same current node as
3661 * the ole one.
3662 */
3663 newClone->stackSize = This->stackSize ;
3664 newClone->stackMaxSize = This->stackMaxSize ;
3665 newClone->stackToVisit =
3666 (ULONG *)HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3667
3668 memcpy(
3669 newClone->stackToVisit,
3670 This->stackToVisit,
3671 sizeof(ULONG) * newClone->stackSize);
3672
3673 *ppenum = (IEnumSTATSTG*)newClone;
3674
3675 /*
3676 * Don't forget to nail down a reference to the clone before
3677 * returning it.
3678 */
3679 IEnumSTATSTGImpl_AddRef(*ppenum);
3680
3681 return S_OK;
3682}
3683
3684INT IEnumSTATSTGImpl_FindParentProperty(
3685 IEnumSTATSTGImpl *This,
3686 ULONG childProperty,
3687 StgProperty *currentProperty,
3688 ULONG *thisNodeId)
3689{
3690 ULONG currentSearchNode;
3691 ULONG foundNode;
3692
3693 /*
3694 * To avoid the special case, get another pointer to a ULONG value if
3695 * the caller didn't supply one.
3696 */
3697 if (thisNodeId==0)
3698 thisNodeId = &foundNode;
3699
3700 /*
3701 * Start with the node at the top of the stack.
3702 */
3703 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3704
3705
3706 while (currentSearchNode!=PROPERTY_NULL)
3707 {
3708 /*
3709 * Store the current node in the returned parameters
3710 */
3711 *thisNodeId = currentSearchNode;
3712
3713 /*
3714 * Remove the top node from the stack
3715 */
3716 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3717
3718 /*
3719 * Read the property from the storage.
3720 */
3721 StorageImpl_ReadProperty(
3722 This->parentStorage,
3723 currentSearchNode,
3724 currentProperty);
3725
3726 if (currentProperty->previousProperty == childProperty)
3727 return PROPERTY_RELATION_PREVIOUS;
3728
3729 else if (currentProperty->nextProperty == childProperty)
3730 return PROPERTY_RELATION_NEXT;
3731
3732 else if (currentProperty->dirProperty == childProperty)
3733 return PROPERTY_RELATION_DIR;
3734
3735 /*
3736 * Push the next search node in the search stack.
3737 */
3738 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3739
3740 /*
3741 * continue the iteration.
3742 */
3743 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3744 }
3745
3746 return PROPERTY_NULL;
3747}
3748
3749ULONG IEnumSTATSTGImpl_FindProperty(
3750 IEnumSTATSTGImpl* This,
3751 const OLECHAR* lpszPropName,
3752 StgProperty* currentProperty)
3753{
3754 ULONG currentSearchNode;
3755
3756 /*
3757 * Start with the node at the top of the stack.
3758 */
3759 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3760
3761 while (currentSearchNode!=PROPERTY_NULL)
3762 {
3763 /*
3764 * Remove the top node from the stack
3765 */
3766 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3767
3768 /*
3769 * Read the property from the storage.
3770 */
3771 StorageImpl_ReadProperty(This->parentStorage,
3772 currentSearchNode,
3773 currentProperty);
3774
3775 if ( propertyNameCmp(
3776 (OLECHAR*)currentProperty->name,
3777 (OLECHAR*)lpszPropName) == 0)
3778 return currentSearchNode;
3779
3780 /*
3781 * Push the next search node in the search stack.
3782 */
3783 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3784
3785 /*
3786 * continue the iteration.
3787 */
3788 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3789 }
3790
3791 return PROPERTY_NULL;
3792}
3793
3794void IEnumSTATSTGImpl_PushSearchNode(
3795 IEnumSTATSTGImpl* This,
3796 ULONG nodeToPush)
3797{
3798 StgProperty rootProperty;
3799 BOOL readSucessful;
3800
3801 /*
3802 * First, make sure we're not trying to push an unexisting node.
3803 */
3804 if (nodeToPush==PROPERTY_NULL)
3805 return;
3806
3807 /*
3808 * First push the node to the stack
3809 */
3810 if (This->stackSize == This->stackMaxSize)
3811 {
3812 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3813
3814 This->stackToVisit = (ULONG *)HeapReAlloc(
3815 GetProcessHeap(),
3816 0,
3817 This->stackToVisit,
3818 sizeof(ULONG) * This->stackMaxSize);
3819 }
3820
3821 This->stackToVisit[This->stackSize] = nodeToPush;
3822 This->stackSize++;
3823
3824 /*
3825 * Read the root property from the storage.
3826 */
3827 readSucessful = StorageImpl_ReadProperty(
3828 This->parentStorage,
3829 nodeToPush,
3830 &rootProperty);
3831
3832 if (readSucessful)
3833 {
3834 assert(rootProperty.sizeOfNameString!=0);
3835
3836 /*
3837 * Push the previous search node in the search stack.
3838 */
3839 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3840 }
3841}
3842
3843ULONG IEnumSTATSTGImpl_PopSearchNode(
3844 IEnumSTATSTGImpl* This,
3845 BOOL remove)
3846{
3847 ULONG topNode;
3848
3849 if (This->stackSize == 0)
3850 return PROPERTY_NULL;
3851
3852 topNode = This->stackToVisit[This->stackSize-1];
3853
3854 if (remove)
3855 This->stackSize--;
3856
3857 return topNode;
3858}
3859
3860/******************************************************************************
3861** StorageUtl implementation
3862*/
3863
3864void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3865{
3866 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3867}
3868
3869void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3870{
3871 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3872}
3873
3874void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3875{
3876 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3877}
3878
3879void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3880{
3881 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3882}
3883
3884void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3885{
3886 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3887 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3888 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3889
3890 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3891}
3892
3893void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3894{
3895 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3896 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3897 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3898
3899 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3900}
3901
3902void StorageUtl_CopyPropertyToSTATSTG(
3903 STATSTG* destination,
3904 StgProperty* source,
3905 int statFlags)
3906{
3907 /*
3908 * The copy of the string occurs only when the flag is not set
3909 */
3910 if ((statFlags & STATFLAG_NONAME) != 0)
3911 {
3912 destination->pwcsName = 0;
3913 }
3914 else
3915 {
3916 destination->pwcsName =
3917 (WCHAR *)CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3918
3919 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3920 }
3921
3922 switch (source->propertyType)
3923 {
3924 case PROPTYPE_STORAGE:
3925 case PROPTYPE_ROOT:
3926 destination->type = STGTY_STORAGE;
3927 break;
3928 case PROPTYPE_STREAM:
3929 destination->type = STGTY_STREAM;
3930 break;
3931 default:
3932 destination->type = STGTY_STREAM;
3933 break;
3934 }
3935
3936 destination->cbSize = source->size;
3937/*
3938 currentReturnStruct->mtime = {0}; TODO
3939 currentReturnStruct->ctime = {0};
3940 currentReturnStruct->atime = {0};
3941*/
3942 destination->grfMode = 0;
3943 destination->grfLocksSupported = 0;
3944 destination->clsid = source->propertyUniqueID;
3945 destination->grfStateBits = 0;
3946 destination->reserved = 0;
3947}
3948
3949/******************************************************************************
3950** BlockChainStream implementation
3951*/
3952
3953BlockChainStream* BlockChainStream_Construct(
3954 StorageImpl* parentStorage,
3955 ULONG* headOfStreamPlaceHolder,
3956 ULONG propertyIndex)
3957{
3958 BlockChainStream* newStream;
3959 ULONG blockIndex;
3960
3961 newStream = (BlockChainStream *)HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3962
3963 newStream->parentStorage = parentStorage;
3964 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3965 newStream->ownerPropertyIndex = propertyIndex;
3966 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3967 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3968 newStream->numBlocks = 0;
3969
3970 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3971
3972 while (blockIndex != BLOCK_END_OF_CHAIN)
3973 {
3974 newStream->numBlocks++;
3975 newStream->tailIndex = blockIndex;
3976
3977 blockIndex = StorageImpl_GetNextBlockInChain(
3978 parentStorage,
3979 blockIndex);
3980 }
3981
3982 return newStream;
3983}
3984
3985void BlockChainStream_Destroy(BlockChainStream* This)
3986{
3987 HeapFree(GetProcessHeap(), 0, This);
3988}
3989
3990/******************************************************************************
3991 * BlockChainStream_GetHeadOfChain
3992 *
3993 * Returns the head of this stream chain.
3994 * Some special chains don't have properties, their heads are kept in
3995 * This->headOfStreamPlaceHolder.
3996 *
3997 */
3998ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3999{
4000 StgProperty chainProperty;
4001 BOOL readSucessful;
4002
4003 if (This->headOfStreamPlaceHolder != 0)
4004 return *(This->headOfStreamPlaceHolder);
4005
4006 if (This->ownerPropertyIndex != PROPERTY_NULL)
4007 {
4008 readSucessful = StorageImpl_ReadProperty(
4009 This->parentStorage,
4010 This->ownerPropertyIndex,
4011 &chainProperty);
4012
4013 if (readSucessful)
4014 {
4015 return chainProperty.startingBlock;
4016 }
4017 }
4018
4019 return BLOCK_END_OF_CHAIN;
4020}
4021
4022/******************************************************************************
4023 * BlockChainStream_GetCount
4024 *
4025 * Returns the number of blocks that comprises this chain.
4026 * This is not the size of the stream as the last block may not be full!
4027 *
4028 */
4029ULONG BlockChainStream_GetCount(BlockChainStream* This)
4030{
4031 ULONG blockIndex;
4032 ULONG count = 0;
4033
4034 blockIndex = BlockChainStream_GetHeadOfChain(This);
4035
4036 while (blockIndex != BLOCK_END_OF_CHAIN)
4037 {
4038 count++;
4039
4040 blockIndex = StorageImpl_GetNextBlockInChain(
4041 This->parentStorage,
4042 blockIndex);
4043 }
4044
4045 return count;
4046}
4047
4048/******************************************************************************
4049 * BlockChainStream_ReadAt
4050 *
4051 * Reads a specified number of bytes from this chain at the specified offset.
4052 * bytesRead may be NULL.
4053 * Failure will be returned if the specified number of bytes has not been read.
4054 */
4055BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4056 ULARGE_INTEGER offset,
4057 ULONG size,
4058 void* buffer,
4059 ULONG* bytesRead)
4060{
4061 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4062 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4063 ULONG bytesToReadInBuffer;
4064 ULONG blockIndex;
4065 BYTE* bufferWalker;
4066 BYTE* bigBlockBuffer;
4067
4068 /*
4069 * Find the first block in the stream that contains part of the buffer.
4070 */
4071 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4072 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4073 (blockNoInSequence < This->lastBlockNoInSequence) )
4074 {
4075 blockIndex = BlockChainStream_GetHeadOfChain(This);
4076 This->lastBlockNoInSequence = blockNoInSequence;
4077 }
4078 else
4079 {
4080 ULONG temp = blockNoInSequence;
4081
4082 blockIndex = This->lastBlockNoInSequenceIndex;
4083 blockNoInSequence -= This->lastBlockNoInSequence;
4084 This->lastBlockNoInSequence = temp;
4085 }
4086
4087 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4088 {
4089 blockIndex =
4090 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4091
4092 blockNoInSequence--;
4093 }
4094
4095 This->lastBlockNoInSequenceIndex = blockIndex;
4096
4097 /*
4098 * Start reading the buffer.
4099 */
4100 *bytesRead = 0;
4101 bufferWalker = (BYTE *)buffer;
4102
4103 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4104 {
4105 /*
4106 * Calculate how many bytes we can copy from this big block.
4107 */
4108 bytesToReadInBuffer =
4109 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4110
4111 /*
4112 * Copy those bytes to the buffer
4113 */
4114 bigBlockBuffer =
4115 (BYTE *)StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4116
4117 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4118
4119 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4120
4121 /*
4122 * Step to the next big block.
4123 */
4124 blockIndex =
4125 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4126
4127 bufferWalker += bytesToReadInBuffer;
4128 size -= bytesToReadInBuffer;
4129 *bytesRead += bytesToReadInBuffer;
4130 offsetInBlock = 0; /* There is no offset on the next block */
4131
4132 }
4133
4134 return (size == 0);
4135}
4136
4137/******************************************************************************
4138 * BlockChainStream_WriteAt
4139 *
4140 * Writes the specified number of bytes to this chain at the specified offset.
4141 * bytesWritten may be NULL.
4142 * Will fail if not all specified number of bytes have been written.
4143 */
4144BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4145 ULARGE_INTEGER offset,
4146 ULONG size,
4147 const void* buffer,
4148 ULONG* bytesWritten)
4149{
4150 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4151 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4152 ULONG bytesToWrite;
4153 ULONG blockIndex;
4154 BYTE* bufferWalker;
4155 BYTE* bigBlockBuffer;
4156
4157 /*
4158 * Find the first block in the stream that contains part of the buffer.
4159 */
4160 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4161 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4162 (blockNoInSequence < This->lastBlockNoInSequence) )
4163 {
4164 blockIndex = BlockChainStream_GetHeadOfChain(This);
4165 This->lastBlockNoInSequence = blockNoInSequence;
4166 }
4167 else
4168 {
4169 ULONG temp = blockNoInSequence;
4170
4171 blockIndex = This->lastBlockNoInSequenceIndex;
4172 blockNoInSequence -= This->lastBlockNoInSequence;
4173 This->lastBlockNoInSequence = temp;
4174 }
4175
4176 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4177 {
4178 blockIndex =
4179 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4180
4181 blockNoInSequence--;
4182 }
4183
4184 This->lastBlockNoInSequenceIndex = blockIndex;
4185
4186 /*
4187 * Here, I'm casting away the constness on the buffer variable
4188 * This is OK since we don't intend to modify that buffer.
4189 */
4190 *bytesWritten = 0;
4191 bufferWalker = (BYTE*)buffer;
4192
4193 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4194 {
4195 /*
4196 * Calculate how many bytes we can copy from this big block.
4197 */
4198 bytesToWrite =
4199 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4200
4201 /*
4202 * Copy those bytes to the buffer
4203 */
4204 bigBlockBuffer = (BYTE *)StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4205
4206 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4207
4208 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4209
4210 /*
4211 * Step to the next big block.
4212 */
4213 blockIndex =
4214 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4215
4216 bufferWalker += bytesToWrite;
4217 size -= bytesToWrite;
4218 *bytesWritten += bytesToWrite;
4219 offsetInBlock = 0; /* There is no offset on the next block */
4220 }
4221
4222 return (size == 0);
4223}
4224
4225/******************************************************************************
4226 * BlockChainStream_Shrink
4227 *
4228 * Shrinks this chain in the big block depot.
4229 */
4230BOOL BlockChainStream_Shrink(BlockChainStream* This,
4231 ULARGE_INTEGER newSize)
4232{
4233 ULONG blockIndex, extraBlock;
4234 ULONG numBlocks;
4235 ULONG count = 1;
4236
4237 /*
4238 * Reset the last accessed block cache.
4239 */
4240 This->lastBlockNoInSequence = 0xFFFFFFFF;
4241 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4242
4243 /*
4244 * Figure out how many blocks are needed to contain the new size
4245 */
4246 numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4247
4248 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4249 numBlocks++;
4250
4251 blockIndex = BlockChainStream_GetHeadOfChain(This);
4252
4253 /*
4254 * Go to the new end of chain
4255 */
4256 while (count < numBlocks)
4257 {
4258 blockIndex =
4259 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4260
4261 count++;
4262 }
4263
4264 /* Get the next block before marking the new end */
4265 extraBlock =
4266 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4267
4268 /* Mark the new end of chain */
4269 StorageImpl_SetNextBlockInChain(
4270 This->parentStorage,
4271 blockIndex,
4272 BLOCK_END_OF_CHAIN);
4273
4274 This->tailIndex = blockIndex;
4275 This->numBlocks = numBlocks;
4276
4277 /*
4278 * Mark the extra blocks as free
4279 */
4280 while (extraBlock != BLOCK_END_OF_CHAIN)
4281 {
4282 blockIndex =
4283 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4284
4285 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4286 extraBlock = blockIndex;
4287 }
4288
4289 return TRUE;
4290}
4291
4292/******************************************************************************
4293 * BlockChainStream_Enlarge
4294 *
4295 * Grows this chain in the big block depot.
4296 */
4297BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4298 ULARGE_INTEGER newSize)
4299{
4300 ULONG blockIndex, currentBlock;
4301 ULONG newNumBlocks;
4302 ULONG oldNumBlocks = 0;
4303
4304 blockIndex = BlockChainStream_GetHeadOfChain(This);
4305
4306 /*
4307 * Empty chain. Create the head.
4308 */
4309 if (blockIndex == BLOCK_END_OF_CHAIN)
4310 {
4311 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4312 StorageImpl_SetNextBlockInChain(This->parentStorage,
4313 blockIndex,
4314 BLOCK_END_OF_CHAIN);
4315
4316 if (This->headOfStreamPlaceHolder != 0)
4317 {
4318 *(This->headOfStreamPlaceHolder) = blockIndex;
4319 }
4320 else
4321 {
4322 StgProperty chainProp;
4323 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4324
4325 StorageImpl_ReadProperty(
4326 This->parentStorage,
4327 This->ownerPropertyIndex,
4328 &chainProp);
4329
4330 chainProp.startingBlock = blockIndex;
4331
4332 StorageImpl_WriteProperty(
4333 This->parentStorage,
4334 This->ownerPropertyIndex,
4335 &chainProp);
4336 }
4337
4338 This->tailIndex = blockIndex;
4339 This->numBlocks = 1;
4340 }
4341
4342 /*
4343 * Figure out how many blocks are needed to contain this stream
4344 */
4345 newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4346
4347 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4348 newNumBlocks++;
4349
4350 /*
4351 * Go to the current end of chain
4352 */
4353 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4354 {
4355 currentBlock = blockIndex;
4356
4357 while (blockIndex != BLOCK_END_OF_CHAIN)
4358 {
4359 This->numBlocks++;
4360 currentBlock = blockIndex;
4361
4362 blockIndex =
4363 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4364 }
4365
4366 This->tailIndex = currentBlock;
4367 }
4368
4369 currentBlock = This->tailIndex;
4370 oldNumBlocks = This->numBlocks;
4371
4372 /*
4373 * Add new blocks to the chain
4374 */
4375 if (oldNumBlocks < newNumBlocks)
4376 {
4377 while (oldNumBlocks < newNumBlocks)
4378 {
4379 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4380
4381 StorageImpl_SetNextBlockInChain(
4382 This->parentStorage,
4383 currentBlock,
4384 blockIndex);
4385
4386 StorageImpl_SetNextBlockInChain(
4387 This->parentStorage,
4388 blockIndex,
4389 BLOCK_END_OF_CHAIN);
4390
4391 currentBlock = blockIndex;
4392 oldNumBlocks++;
4393 }
4394
4395 This->tailIndex = blockIndex;
4396 This->numBlocks = newNumBlocks;
4397 }
4398
4399 return TRUE;
4400}
4401
4402/******************************************************************************
4403 * BlockChainStream_SetSize
4404 *
4405 * Sets the size of this stream. The big block depot will be updated.
4406 * The file will grow if we grow the chain.
4407 *
4408 * TODO: Free the actual blocks in the file when we shrink the chain.
4409 * Currently, the blocks are still in the file. So the file size
4410 * doesn't shrink even if we shrink streams.
4411 */
4412BOOL BlockChainStream_SetSize(
4413 BlockChainStream* This,
4414 ULARGE_INTEGER newSize)
4415{
4416 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4417
4418 if (newSize.LowPart == size.LowPart)
4419 return TRUE;
4420
4421 if (newSize.LowPart < size.LowPart)
4422 {
4423 BlockChainStream_Shrink(This, newSize);
4424 }
4425 else
4426 {
4427 ULARGE_INTEGER fileSize =
4428 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4429
4430 ULONG diff = newSize.LowPart - size.LowPart;
4431
4432 /*
4433 * Make sure the file stays a multiple of blocksize
4434 */
4435 if ((diff % This->parentStorage->bigBlockSize) != 0)
4436 diff += (This->parentStorage->bigBlockSize -
4437 (diff % This->parentStorage->bigBlockSize) );
4438
4439 fileSize.LowPart += diff;
4440 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4441
4442 BlockChainStream_Enlarge(This, newSize);
4443 }
4444
4445 return TRUE;
4446}
4447
4448/******************************************************************************
4449 * BlockChainStream_GetSize
4450 *
4451 * Returns the size of this chain.
4452 * Will return the block count if this chain doesn't have a property.
4453 */
4454ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4455{
4456 StgProperty chainProperty;
4457
4458 if(This->headOfStreamPlaceHolder == NULL)
4459 {
4460 /*
4461 * This chain is a data stream read the property and return
4462 * the appropriate size
4463 */
4464 StorageImpl_ReadProperty(
4465 This->parentStorage,
4466 This->ownerPropertyIndex,
4467 &chainProperty);
4468
4469 return chainProperty.size;
4470 }
4471 else
4472 {
4473 /*
4474 * this chain is a chain that does not have a property, figure out the
4475 * size by making the product number of used blocks times the
4476 * size of them
4477 */
4478 ULARGE_INTEGER result;
4479 result.HighPart = 0;
4480
4481 result.LowPart =
4482 BlockChainStream_GetCount(This) *
4483 This->parentStorage->bigBlockSize;
4484
4485 return result;
4486 }
4487}
4488
4489/******************************************************************************
4490** SmallBlockChainStream implementation
4491*/
4492
4493SmallBlockChainStream* SmallBlockChainStream_Construct(
4494 StorageImpl* parentStorage,
4495 ULONG propertyIndex)
4496{
4497 SmallBlockChainStream* newStream;
4498
4499 newStream =
4500 (SmallBlockChainStream *)HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4501
4502 newStream->parentStorage = parentStorage;
4503 newStream->ownerPropertyIndex = propertyIndex;
4504
4505 return newStream;
4506}
4507
4508void SmallBlockChainStream_Destroy(
4509 SmallBlockChainStream* This)
4510{
4511 HeapFree(GetProcessHeap(), 0, This);
4512}
4513
4514/******************************************************************************
4515 * SmallBlockChainStream_GetHeadOfChain
4516 *
4517 * Returns the head of this chain of small blocks.
4518 */
4519ULONG SmallBlockChainStream_GetHeadOfChain(
4520 SmallBlockChainStream* This)
4521{
4522 StgProperty chainProperty;
4523 BOOL readSucessful;
4524
4525 if (This->ownerPropertyIndex)
4526 {
4527 readSucessful = StorageImpl_ReadProperty(
4528 This->parentStorage,
4529 This->ownerPropertyIndex,
4530 &chainProperty);
4531
4532 if (readSucessful)
4533 {
4534 return chainProperty.startingBlock;
4535 }
4536
4537 }
4538
4539 return BLOCK_END_OF_CHAIN;
4540}
4541
4542/******************************************************************************
4543 * SmallBlockChainStream_GetNextBlockInChain
4544 *
4545 * Returns the index of the next small block in this chain.
4546 *
4547 * Return Values:
4548 * - BLOCK_END_OF_CHAIN: end of this chain
4549 * - BLOCK_UNUSED: small block 'blockIndex' is free
4550 */
4551ULONG SmallBlockChainStream_GetNextBlockInChain(
4552 SmallBlockChainStream* This,
4553 ULONG blockIndex)
4554{
4555 ULARGE_INTEGER offsetOfBlockInDepot;
4556 DWORD buffer;
4557 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4558 ULONG bytesRead;
4559 BOOL success;
4560
4561 offsetOfBlockInDepot.HighPart = 0;
4562 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4563
4564 /*
4565 * Read those bytes in the buffer from the small block file.
4566 */
4567 success = BlockChainStream_ReadAt(
4568 This->parentStorage->smallBlockDepotChain,
4569 offsetOfBlockInDepot,
4570 sizeof(DWORD),
4571 &buffer,
4572 &bytesRead);
4573
4574 if (success)
4575 {
4576 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4577 }
4578
4579 return nextBlockInChain;
4580}
4581
4582/******************************************************************************
4583 * SmallBlockChainStream_SetNextBlockInChain
4584 *
4585 * Writes the index of the next block of the specified block in the small
4586 * block depot.
4587 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4588 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4589 */
4590void SmallBlockChainStream_SetNextBlockInChain(
4591 SmallBlockChainStream* This,
4592 ULONG blockIndex,
4593 ULONG nextBlock)
4594{
4595 ULARGE_INTEGER offsetOfBlockInDepot;
4596 DWORD buffer;
4597 ULONG bytesWritten;
4598
4599 offsetOfBlockInDepot.HighPart = 0;
4600 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4601
4602 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4603
4604 /*
4605 * Read those bytes in the buffer from the small block file.
4606 */
4607 BlockChainStream_WriteAt(
4608 This->parentStorage->smallBlockDepotChain,
4609 offsetOfBlockInDepot,
4610 sizeof(DWORD),
4611 &buffer,
4612 &bytesWritten);
4613}
4614
4615/******************************************************************************
4616 * SmallBlockChainStream_FreeBlock
4617 *
4618 * Flag small block 'blockIndex' as free in the small block depot.
4619 */
4620void SmallBlockChainStream_FreeBlock(
4621 SmallBlockChainStream* This,
4622 ULONG blockIndex)
4623{
4624 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4625}
4626
4627/******************************************************************************
4628 * SmallBlockChainStream_GetNextFreeBlock
4629 *
4630 * Returns the index of a free small block. The small block depot will be
4631 * enlarged if necessary. The small block chain will also be enlarged if
4632 * necessary.
4633 */
4634ULONG SmallBlockChainStream_GetNextFreeBlock(
4635 SmallBlockChainStream* This)
4636{
4637 ULARGE_INTEGER offsetOfBlockInDepot;
4638 DWORD buffer;
4639 ULONG bytesRead;
4640 ULONG blockIndex = 0;
4641 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4642 BOOL success = TRUE;
4643 ULONG smallBlocksPerBigBlock;
4644
4645 offsetOfBlockInDepot.HighPart = 0;
4646
4647 /*
4648 * Scan the small block depot for a free block
4649 */
4650 while (nextBlockIndex != BLOCK_UNUSED)
4651 {
4652 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4653
4654 success = BlockChainStream_ReadAt(
4655 This->parentStorage->smallBlockDepotChain,
4656 offsetOfBlockInDepot,
4657 sizeof(DWORD),
4658 &buffer,
4659 &bytesRead);
4660
4661 /*
4662 * If we run out of space for the small block depot, enlarge it
4663 */
4664 if (success)
4665 {
4666 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4667
4668 if (nextBlockIndex != BLOCK_UNUSED)
4669 blockIndex++;
4670 }
4671 else
4672 {
4673 ULONG count =
4674 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4675
4676 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4677 ULONG nextBlock, newsbdIndex;
4678 BYTE* smallBlockDepot;
4679
4680 nextBlock = sbdIndex;
4681 while (nextBlock != BLOCK_END_OF_CHAIN)
4682 {
4683 sbdIndex = nextBlock;
4684 nextBlock =
4685 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4686 }
4687
4688 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4689 if (sbdIndex != BLOCK_END_OF_CHAIN)
4690 StorageImpl_SetNextBlockInChain(
4691 This->parentStorage,
4692 sbdIndex,
4693 newsbdIndex);
4694
4695 StorageImpl_SetNextBlockInChain(
4696 This->parentStorage,
4697 newsbdIndex,
4698 BLOCK_END_OF_CHAIN);
4699
4700 /*
4701 * Initialize all the small blocks to free
4702 */
4703 smallBlockDepot =
4704 (BYTE *)StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4705
4706 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4707 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4708
4709 if (count == 0)
4710 {
4711 /*
4712 * We have just created the small block depot.
4713 */
4714 StgProperty rootProp;
4715 ULONG sbStartIndex;
4716
4717 /*
4718 * Save it in the header
4719 */
4720 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4721 StorageImpl_SaveFileHeader(This->parentStorage);
4722
4723 /*
4724 * And allocate the first big block that will contain small blocks
4725 */
4726 sbStartIndex =
4727 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4728
4729 StorageImpl_SetNextBlockInChain(
4730 This->parentStorage,
4731 sbStartIndex,
4732 BLOCK_END_OF_CHAIN);
4733
4734 StorageImpl_ReadProperty(
4735 This->parentStorage,
4736 This->parentStorage->rootPropertySetIndex,
4737 &rootProp);
4738
4739 rootProp.startingBlock = sbStartIndex;
4740 rootProp.size.HighPart = 0;
4741 rootProp.size.LowPart = This->parentStorage->bigBlockSize;
4742
4743 StorageImpl_WriteProperty(
4744 This->parentStorage,
4745 This->parentStorage->rootPropertySetIndex,
4746 &rootProp);
4747 }
4748 }
4749 }
4750
4751 smallBlocksPerBigBlock =
4752 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4753
4754 /*
4755 * Verify if we have to allocate big blocks to contain small blocks
4756 */
4757 if (blockIndex % smallBlocksPerBigBlock == 0)
4758 {
4759 StgProperty rootProp;
4760 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4761
4762 StorageImpl_ReadProperty(
4763 This->parentStorage,
4764 This->parentStorage->rootPropertySetIndex,
4765 &rootProp);
4766
4767 if (rootProp.size.LowPart <
4768 (blocksRequired * This->parentStorage->bigBlockSize))
4769 {
4770 rootProp.size.LowPart += This->parentStorage->bigBlockSize;
4771
4772 BlockChainStream_SetSize(
4773 This->parentStorage->smallBlockRootChain,
4774 rootProp.size);
4775
4776 StorageImpl_WriteProperty(
4777 This->parentStorage,
4778 This->parentStorage->rootPropertySetIndex,
4779 &rootProp);
4780 }
4781 }
4782
4783 return blockIndex;
4784}
4785
4786/******************************************************************************
4787 * SmallBlockChainStream_ReadAt
4788 *
4789 * Reads a specified number of bytes from this chain at the specified offset.
4790 * bytesRead may be NULL.
4791 * Failure will be returned if the specified number of bytes has not been read.
4792 */
4793BOOL SmallBlockChainStream_ReadAt(
4794 SmallBlockChainStream* This,
4795 ULARGE_INTEGER offset,
4796 ULONG size,
4797 void* buffer,
4798 ULONG* bytesRead)
4799{
4800 ULARGE_INTEGER offsetInBigBlockFile;
4801 ULONG blockNoInSequence =
4802 offset.LowPart / This->parentStorage->smallBlockSize;
4803
4804 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4805 ULONG bytesToReadInBuffer;
4806 ULONG blockIndex;
4807 ULONG bytesReadFromBigBlockFile;
4808 BYTE* bufferWalker;
4809
4810 /*
4811 * This should never happen on a small block file.
4812 */
4813 assert(offset.HighPart==0);
4814
4815 /*
4816 * Find the first block in the stream that contains part of the buffer.
4817 */
4818 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4819
4820 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4821 {
4822 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4823
4824 blockNoInSequence--;
4825 }
4826
4827 /*
4828 * Start reading the buffer.
4829 */
4830 *bytesRead = 0;
4831 bufferWalker = (BYTE *)buffer;
4832
4833 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4834 {
4835 /*
4836 * Calculate how many bytes we can copy from this small block.
4837 */
4838 bytesToReadInBuffer =
4839 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4840
4841 /*
4842 * Calculate the offset of the small block in the small block file.
4843 */
4844 offsetInBigBlockFile.HighPart = 0;
4845 offsetInBigBlockFile.LowPart =
4846 blockIndex * This->parentStorage->smallBlockSize;
4847
4848 offsetInBigBlockFile.LowPart += offsetInBlock;
4849
4850 /*
4851 * Read those bytes in the buffer from the small block file.
4852 */
4853 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4854 offsetInBigBlockFile,
4855 bytesToReadInBuffer,
4856 bufferWalker,
4857 &bytesReadFromBigBlockFile);
4858
4859 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4860
4861 /*
4862 * Step to the next big block.
4863 */
4864 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4865 bufferWalker += bytesToReadInBuffer;
4866 size -= bytesToReadInBuffer;
4867 *bytesRead += bytesToReadInBuffer;
4868 offsetInBlock = 0; /* There is no offset on the next block */
4869 }
4870
4871 return (size == 0);
4872}
4873
4874/******************************************************************************
4875 * SmallBlockChainStream_WriteAt
4876 *
4877 * Writes the specified number of bytes to this chain at the specified offset.
4878 * bytesWritten may be NULL.
4879 * Will fail if not all specified number of bytes have been written.
4880 */
4881BOOL SmallBlockChainStream_WriteAt(
4882 SmallBlockChainStream* This,
4883 ULARGE_INTEGER offset,
4884 ULONG size,
4885 const void* buffer,
4886 ULONG* bytesWritten)
4887{
4888 ULARGE_INTEGER offsetInBigBlockFile;
4889 ULONG blockNoInSequence =
4890 offset.LowPart / This->parentStorage->smallBlockSize;
4891
4892 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4893 ULONG bytesToWriteInBuffer;
4894 ULONG blockIndex;
4895 ULONG bytesWrittenFromBigBlockFile;
4896 BYTE* bufferWalker;
4897
4898 /*
4899 * This should never happen on a small block file.
4900 */
4901 assert(offset.HighPart==0);
4902
4903 /*
4904 * Find the first block in the stream that contains part of the buffer.
4905 */
4906 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4907
4908 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4909 {
4910 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4911
4912 blockNoInSequence--;
4913 }
4914
4915 /*
4916 * Start writing the buffer.
4917 *
4918 * Here, I'm casting away the constness on the buffer variable
4919 * This is OK since we don't intend to modify that buffer.
4920 */
4921 *bytesWritten = 0;
4922 bufferWalker = (BYTE*)buffer;
4923 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4924 {
4925 /*
4926 * Calculate how many bytes we can copy to this small block.
4927 */
4928 bytesToWriteInBuffer =
4929 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4930
4931 /*
4932 * Calculate the offset of the small block in the small block file.
4933 */
4934 offsetInBigBlockFile.HighPart = 0;
4935 offsetInBigBlockFile.LowPart =
4936 blockIndex * This->parentStorage->smallBlockSize;
4937
4938 offsetInBigBlockFile.LowPart += offsetInBlock;
4939
4940 /*
4941 * Write those bytes in the buffer to the small block file.
4942 */
4943 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4944 offsetInBigBlockFile,
4945 bytesToWriteInBuffer,
4946 bufferWalker,
4947 &bytesWrittenFromBigBlockFile);
4948
4949 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4950
4951 /*
4952 * Step to the next big block.
4953 */
4954 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4955 bufferWalker += bytesToWriteInBuffer;
4956 size -= bytesToWriteInBuffer;
4957 *bytesWritten += bytesToWriteInBuffer;
4958 offsetInBlock = 0; /* There is no offset on the next block */
4959 }
4960
4961 return (size == 0);
4962}
4963
4964/******************************************************************************
4965 * SmallBlockChainStream_Shrink
4966 *
4967 * Shrinks this chain in the small block depot.
4968 */
4969BOOL SmallBlockChainStream_Shrink(
4970 SmallBlockChainStream* This,
4971 ULARGE_INTEGER newSize)
4972{
4973 ULONG blockIndex, extraBlock;
4974 ULONG numBlocks;
4975 ULONG count = 0;
4976
4977 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4978
4979 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4980 numBlocks++;
4981
4982 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4983
4984 /*
4985 * Go to the new end of chain
4986 */
4987 while (count < numBlocks)
4988 {
4989 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4990 count++;
4991 }
4992
4993 /*
4994 * If the count is 0, we have a special case, the head of the chain was
4995 * just freed.
4996 */
4997 if (count == 0)
4998 {
4999 StgProperty chainProp;
5000
5001 StorageImpl_ReadProperty(This->parentStorage,
5002 This->ownerPropertyIndex,
5003 &chainProp);
5004
5005 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5006
5007 StorageImpl_WriteProperty(This->parentStorage,
5008 This->ownerPropertyIndex,
5009 &chainProp);
5010
5011 /*
5012 * We start freeing the chain at the head block.
5013 */
5014 extraBlock = blockIndex;
5015 }
5016 else
5017 {
5018 /* Get the next block before marking the new end */
5019 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5020
5021 /* Mark the new end of chain */
5022 SmallBlockChainStream_SetNextBlockInChain(
5023 This,
5024 blockIndex,
5025 BLOCK_END_OF_CHAIN);
5026 }
5027
5028 /*
5029 * Mark the extra blocks as free
5030 */
5031 while (extraBlock != BLOCK_END_OF_CHAIN)
5032 {
5033 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5034 SmallBlockChainStream_FreeBlock(This, extraBlock);
5035 extraBlock = blockIndex;
5036 }
5037
5038 return TRUE;
5039}
5040
5041/******************************************************************************
5042 * SmallBlockChainStream_Enlarge
5043 *
5044 * Grows this chain in the small block depot.
5045 */
5046BOOL SmallBlockChainStream_Enlarge(
5047 SmallBlockChainStream* This,
5048 ULARGE_INTEGER newSize)
5049{
5050 ULONG blockIndex, currentBlock;
5051 ULONG newNumBlocks;
5052 ULONG oldNumBlocks = 0;
5053
5054 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5055
5056 /*
5057 * Empty chain
5058 */
5059 if (blockIndex == BLOCK_END_OF_CHAIN)
5060 {
5061
5062 StgProperty chainProp;
5063
5064 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5065 &chainProp);
5066
5067 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5068
5069 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5070 &chainProp);
5071
5072 blockIndex = chainProp.startingBlock;
5073 SmallBlockChainStream_SetNextBlockInChain(
5074 This,
5075 blockIndex,
5076 BLOCK_END_OF_CHAIN);
5077 }
5078
5079 currentBlock = blockIndex;
5080
5081 /*
5082 * Figure out how many blocks are needed to contain this stream
5083 */
5084 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
5085
5086 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
5087 newNumBlocks++;
5088
5089 /*
5090 * Go to the current end of chain
5091 */
5092 while (blockIndex != BLOCK_END_OF_CHAIN)
5093 {
5094 oldNumBlocks++;
5095 currentBlock = blockIndex;
5096 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5097 }
5098
5099 /*
5100 * Add new blocks to the chain
5101 */
5102 while (oldNumBlocks < newNumBlocks)
5103 {
5104 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5105 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5106
5107 SmallBlockChainStream_SetNextBlockInChain(
5108 This,
5109 blockIndex,
5110 BLOCK_END_OF_CHAIN);
5111
5112 currentBlock = blockIndex;
5113 oldNumBlocks++;
5114 }
5115
5116 return TRUE;
5117}
5118
5119/******************************************************************************
5120 * SmallBlockChainStream_GetCount
5121 *
5122 * Returns the number of blocks that comprises this chain.
5123 * This is not the size of this chain as the last block may not be full!
5124 */
5125ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5126{
5127 ULONG blockIndex;
5128 ULONG count = 0;
5129
5130 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5131
5132 while (blockIndex != BLOCK_END_OF_CHAIN)
5133 {
5134 count++;
5135
5136 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5137 }
5138
5139 return count;
5140}
5141
5142/******************************************************************************
5143 * SmallBlockChainStream_SetSize
5144 *
5145 * Sets the size of this stream.
5146 * The file will grow if we grow the chain.
5147 *
5148 * TODO: Free the actual blocks in the file when we shrink the chain.
5149 * Currently, the blocks are still in the file. So the file size
5150 * doesn't shrink even if we shrink streams.
5151 */
5152BOOL SmallBlockChainStream_SetSize(
5153 SmallBlockChainStream* This,
5154 ULARGE_INTEGER newSize)
5155{
5156 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5157
5158 if (newSize.LowPart == size.LowPart)
5159 return TRUE;
5160
5161 if (newSize.LowPart < size.LowPart)
5162 {
5163 SmallBlockChainStream_Shrink(This, newSize);
5164 }
5165 else
5166 {
5167 SmallBlockChainStream_Enlarge(This, newSize);
5168 }
5169
5170 return TRUE;
5171}
5172
5173/******************************************************************************
5174 * SmallBlockChainStream_GetSize
5175 *
5176 * Returns the size of this chain.
5177 */
5178ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5179{
5180 StgProperty chainProperty;
5181
5182 StorageImpl_ReadProperty(
5183 This->parentStorage,
5184 This->ownerPropertyIndex,
5185 &chainProperty);
5186
5187 return chainProperty.size;
5188}
5189
5190/******************************************************************************
5191 * StgCreateDocfile32 [OLE32.144]
5192 */
5193HRESULT WINAPI StgCreateDocfile(
5194 LPCOLESTR pwcsName,
5195 DWORD grfMode,
5196 DWORD reserved,
5197 IStorage **ppstgOpen)
5198{
5199 StorageImpl* newStorage = 0;
5200 HANDLE hFile = INVALID_HANDLE_VALUE;
5201 HRESULT hr = S_OK;
5202 DWORD shareMode;
5203 DWORD accessMode;
5204 DWORD creationMode;
5205 DWORD fileAttributes;
5206 WCHAR tempFileName[MAX_PATH];
5207
5208 TRACE("(%s, %lx, %ld, %p)\n",
5209 debugstr_w(pwcsName), grfMode,
5210 reserved, ppstgOpen);
5211
5212 /*
5213 * Validate the parameters
5214 */
5215 if (ppstgOpen == 0)
5216 return STG_E_INVALIDPOINTER;
5217
5218 /*
5219 * Validate the STGM flags
5220 */
5221 if ( FAILED( validateSTGM(grfMode) ))
5222 return STG_E_INVALIDFLAG;
5223
5224 /*
5225 * Generate a unique name.
5226 */
5227 if (pwcsName == 0)
5228 {
5229 WCHAR tempPath[MAX_PATH];
5230 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5231
5232 memset(tempPath, 0, sizeof(tempPath));
5233 memset(tempFileName, 0, sizeof(tempFileName));
5234
5235 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5236 tempPath[0] = '.';
5237
5238 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5239 pwcsName = tempFileName;
5240 else
5241 return STG_E_INSUFFICIENTMEMORY;
5242 }
5243
5244 /*
5245 * Interpret the STGM value grfMode
5246 */
5247 shareMode = GetShareModeFromSTGM(grfMode);
5248 accessMode = GetAccessModeFromSTGM(grfMode);
5249 creationMode = GetCreationModeFromSTGM(grfMode);
5250
5251 if (grfMode & STGM_DELETEONRELEASE)
5252 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5253 else
5254 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5255
5256 if (grfMode & STGM_TRANSACTED)
5257 FIXME("Transacted mode not implemented.\n");
5258
5259 /*
5260 * Initialize the "out" parameter.
5261 */
5262 *ppstgOpen = 0;
5263
5264 hFile = CreateFileW(pwcsName,
5265 accessMode,
5266 shareMode,
5267 NULL,
5268 creationMode,
5269 fileAttributes,
5270 0);
5271
5272 if (hFile == INVALID_HANDLE_VALUE)
5273 {
5274 return E_FAIL;
5275 }
5276
5277 /*
5278 * Allocate and initialize the new IStorage32object.
5279 */
5280 newStorage = (StorageImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5281
5282 if (newStorage == 0)
5283 return STG_E_INSUFFICIENTMEMORY;
5284
5285 hr = StorageImpl_Construct(
5286 newStorage,
5287 hFile,
5288 NULL,
5289 grfMode,
5290 TRUE);
5291
5292 if (FAILED(hr))
5293 {
5294 HeapFree(GetProcessHeap(), 0, newStorage);
5295 return hr;
5296 }
5297
5298 /*
5299 * Get an "out" pointer for the caller.
5300 */
5301 hr = StorageBaseImpl_QueryInterface(
5302 (IStorage*)newStorage,
5303 (REFIID)&IID_IStorage,
5304 (void**)ppstgOpen);
5305
5306 return hr;
5307}
5308
5309/******************************************************************************
5310 * StgOpenStorage32 [OLE32.148]
5311 */
5312HRESULT WINAPI StgOpenStorage(
5313 const OLECHAR *pwcsName,
5314 IStorage *pstgPriority,
5315 DWORD grfMode,
5316 SNB snbExclude,
5317 DWORD reserved,
5318 IStorage **ppstgOpen)
5319{
5320 StorageImpl* newStorage = 0;
5321 HRESULT hr = S_OK;
5322 HANDLE hFile = 0;
5323 DWORD shareMode;
5324 DWORD accessMode;
5325
5326 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5327 debugstr_w(pwcsName), pstgPriority, grfMode,
5328 snbExclude, reserved, ppstgOpen);
5329
5330 /*
5331 * Perform a sanity check
5332 */
5333 if (( pwcsName == 0) || (ppstgOpen == 0) )
5334 return STG_E_INVALIDPOINTER;
5335
5336 /*
5337 * Validate the STGM flags
5338 */
5339 if ( FAILED( validateSTGM(grfMode) ))
5340 return STG_E_INVALIDFLAG;
5341
5342 /*
5343 * Interpret the STGM value grfMode
5344 */
5345 shareMode = GetShareModeFromSTGM(grfMode);
5346 accessMode = GetAccessModeFromSTGM(grfMode);
5347
5348 /*
5349 * Initialize the "out" parameter.
5350 */
5351 *ppstgOpen = 0;
5352
5353 hFile = CreateFileW( pwcsName,
5354 accessMode,
5355 shareMode,
5356 NULL,
5357 OPEN_EXISTING,
5358 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5359 0);
5360
5361
5362 if (hFile==INVALID_HANDLE_VALUE)
5363 {
5364 return E_FAIL;
5365 }
5366
5367 /*
5368 * Allocate and initialize the new IStorage32object.
5369 */
5370 newStorage = (StorageImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5371
5372 if (newStorage == 0)
5373 return STG_E_INSUFFICIENTMEMORY;
5374
5375 hr = StorageImpl_Construct(
5376 newStorage,
5377 hFile,
5378 NULL,
5379 grfMode,
5380 TRUE);
5381
5382 if (FAILED(hr))
5383 {
5384 HeapFree(GetProcessHeap(), 0, newStorage);
5385 return hr;
5386 }
5387
5388 /*
5389 * Get an "out" pointer for the caller.
5390 */
5391 hr = StorageBaseImpl_QueryInterface(
5392 (IStorage*)newStorage,
5393 (REFIID)&IID_IStorage,
5394 (void**)ppstgOpen);
5395
5396 return hr;
5397}
5398
5399/******************************************************************************
5400 * StgCreateDocfileOnILockBytes [OLE32.145]
5401 */
5402HRESULT WINAPI StgCreateDocfileOnILockBytes(
5403 ILockBytes *plkbyt,
5404 DWORD grfMode,
5405 DWORD reserved,
5406 IStorage** ppstgOpen)
5407{
5408 StorageImpl* newStorage = 0;
5409 HRESULT hr = S_OK;
5410
5411 /*
5412 * Validate the parameters
5413 */
5414 if ((ppstgOpen == 0) || (plkbyt == 0))
5415 return STG_E_INVALIDPOINTER;
5416
5417 /*
5418 * Allocate and initialize the new IStorage object.
5419 */
5420 newStorage = (StorageImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5421
5422 if (newStorage == 0)
5423 return STG_E_INSUFFICIENTMEMORY;
5424
5425 hr = StorageImpl_Construct(
5426 newStorage,
5427 0,
5428 plkbyt,
5429 grfMode,
5430 FALSE);
5431
5432 if (FAILED(hr))
5433 {
5434 HeapFree(GetProcessHeap(), 0, newStorage);
5435 return hr;
5436 }
5437
5438 /*
5439 * Get an "out" pointer for the caller.
5440 */
5441 hr = StorageBaseImpl_QueryInterface(
5442 (IStorage*)newStorage,
5443 (REFIID)&IID_IStorage,
5444 (void**)ppstgOpen);
5445
5446 return hr;
5447}
5448
5449/******************************************************************************
5450 * StgOpenStorageOnILockBytes [OLE32.149]
5451 */
5452HRESULT WINAPI StgOpenStorageOnILockBytes(
5453 ILockBytes *plkbyt,
5454 IStorage *pstgPriority,
5455 DWORD grfMode,
5456 SNB snbExclude,
5457 DWORD reserved,
5458 IStorage **ppstgOpen)
5459{
5460 StorageImpl* newStorage = 0;
5461 HRESULT hr = S_OK;
5462
5463 /*
5464 * Perform a sanity check
5465 */
5466 if ((plkbyt == 0) || (ppstgOpen == 0))
5467 return STG_E_INVALIDPOINTER;
5468
5469 /*
5470 * Validate the STGM flags
5471 */
5472 if ( FAILED( validateSTGM(grfMode) ))
5473 return STG_E_INVALIDFLAG;
5474
5475 /*
5476 * Initialize the "out" parameter.
5477 */
5478 *ppstgOpen = 0;
5479
5480 /*
5481 * Allocate and initialize the new IStorage object.
5482 */
5483 newStorage = (StorageImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5484
5485 if (newStorage == 0)
5486 return STG_E_INSUFFICIENTMEMORY;
5487
5488 hr = StorageImpl_Construct(
5489 newStorage,
5490 0,
5491 plkbyt,
5492 grfMode,
5493 FALSE);
5494
5495 if (FAILED(hr))
5496 {
5497 HeapFree(GetProcessHeap(), 0, newStorage);
5498 return hr;
5499 }
5500
5501 /*
5502 * Get an "out" pointer for the caller.
5503 */
5504 hr = StorageBaseImpl_QueryInterface(
5505 (IStorage*)newStorage,
5506 (REFIID)&IID_IStorage,
5507 (void**)ppstgOpen);
5508
5509 return hr;
5510}
5511
5512/******************************************************************************
5513 * StgIsStorageILockBytes [OLE32.147]
5514 *
5515 * Determines if the ILockBytes contains a storage object.
5516 */
5517HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5518{
5519 BYTE sig[8];
5520 ULARGE_INTEGER offset;
5521
5522 offset.HighPart = 0;
5523 offset.LowPart = 0;
5524
5525 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5526
5527 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5528 return S_OK;
5529
5530 return S_FALSE;
5531}
5532
5533/******************************************************************************
5534 * WriteClassStg32 [OLE32.158]
5535 *
5536 * This method will store the specified CLSID in the specified storage object
5537 */
5538HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5539{
5540 HRESULT hRes;
5541
5542 assert(pStg != 0);
5543
5544 hRes = IStorage_SetClass(pStg, rclsid);
5545
5546 return hRes;
5547}
5548
5549/*******************************************************************************************
5550 * ReadClassStg
5551 *
5552 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5553 */
5554HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5555
5556 STATSTG pstatstg;
5557 HRESULT hRes;
5558
5559 TRACE("()\n");
5560
5561 if(pclsid==NULL)
5562 return E_POINTER;
5563 /*
5564 * read a STATSTG structure (contains the clsid) from the storage
5565 */
5566 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5567
5568 if(SUCCEEDED(hRes))
5569 *pclsid=pstatstg.clsid;
5570
5571 return hRes;
5572}
5573
5574/*************************************************************************************
5575 * OleLoadFromStream
5576 *
5577 * This function loads an object from stream
5578 */
5579HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5580{
5581 CLSID clsid;
5582 HRESULT res;
5583
5584 FIXME("(),stub!\n");
5585
5586 res=ReadClassStm(pStm,&clsid);
5587
5588 if (SUCCEEDED(res)){
5589
5590 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5591
5592 if (SUCCEEDED(res))
5593
5594 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5595 }
5596
5597 return res;
5598}
5599
5600/************************************************************************************************
5601 * OleSaveToStream
5602 *
5603 * This function saves an object with the IPersistStream interface on it to the specified stream
5604 */
5605HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5606{
5607
5608 CLSID clsid;
5609 HRESULT res;
5610
5611 TRACE("(%p,%p)\n",pPStm,pStm);
5612
5613 res=IPersistStream_GetClassID(pPStm,&clsid);
5614
5615 if (SUCCEEDED(res)){
5616
5617 res=WriteClassStm(pStm,&clsid);
5618
5619 if (SUCCEEDED(res))
5620
5621 res=IPersistStream_Save(pPStm,pStm,FALSE);
5622 }
5623
5624 return res;
5625}
5626
5627/****************************************************************************
5628 * This method validate a STGM parameter that can contain the values below
5629 *
5630 * STGM_DIRECT 0x00000000
5631 * STGM_TRANSACTED 0x00010000
5632 * STGM_SIMPLE 0x08000000
5633 *
5634 * STGM_READ 0x00000000
5635 * STGM_WRITE 0x00000001
5636 * STGM_READWRITE 0x00000002
5637 *
5638 * STGM_SHARE_DENY_NONE 0x00000040
5639 * STGM_SHARE_DENY_READ 0x00000030
5640 * STGM_SHARE_DENY_WRITE 0x00000020
5641 * STGM_SHARE_EXCLUSIVE 0x00000010
5642 *
5643 * STGM_PRIORITY 0x00040000
5644 * STGM_DELETEONRELEASE 0x04000000
5645 *
5646 * STGM_CREATE 0x00001000
5647 * STGM_CONVERT 0x00020000
5648 * STGM_FAILIFTHERE 0x00000000
5649 *
5650 * STGM_NOSCRATCH 0x00100000
5651 * STGM_NOSNAPSHOT 0x00200000
5652 */
5653static HRESULT validateSTGM(DWORD stgm)
5654{
5655 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5656 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5657 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5658
5659 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5660 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5661 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5662
5663 BOOL bSTGM_SHARE_DENY_NONE =
5664 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5665
5666 BOOL bSTGM_SHARE_DENY_READ =
5667 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5668
5669 BOOL bSTGM_SHARE_DENY_WRITE =
5670 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5671
5672 BOOL bSTGM_SHARE_EXCLUSIVE =
5673 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5674
5675 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5676 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5677
5678 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5679 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5680
5681 /*
5682 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5683 */
5684 if ( ! bSTGM_DIRECT )
5685 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5686 return E_FAIL;
5687
5688 /*
5689 * STGM_WRITE | STGM_READWRITE | STGM_READ
5690 */
5691 if ( ! bSTGM_READ )
5692 if( bSTGM_WRITE && bSTGM_READWRITE )
5693 return E_FAIL;
5694
5695 /*
5696 * STGM_SHARE_DENY_NONE | others
5697 * (I assume here that DENY_READ implies DENY_WRITE)
5698 */
5699 if ( bSTGM_SHARE_DENY_NONE )
5700 if ( bSTGM_SHARE_DENY_READ ||
5701 bSTGM_SHARE_DENY_WRITE ||
5702 bSTGM_SHARE_EXCLUSIVE)
5703 return E_FAIL;
5704
5705 /*
5706 * STGM_CREATE | STGM_CONVERT
5707 * if both are false, STGM_FAILIFTHERE is set to TRUE
5708 */
5709 if ( bSTGM_CREATE && bSTGM_CONVERT )
5710 return E_FAIL;
5711
5712 /*
5713 * STGM_NOSCRATCH requires STGM_TRANSACTED
5714 */
5715 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5716 return E_FAIL;
5717
5718 /*
5719 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5720 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5721 */
5722 if (bSTGM_NOSNAPSHOT)
5723 {
5724 if ( ! ( bSTGM_TRANSACTED &&
5725 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5726 return E_FAIL;
5727 }
5728
5729 return S_OK;
5730}
5731
5732/****************************************************************************
5733 * GetShareModeFromSTGM
5734 *
5735 * This method will return a share mode flag from a STGM value.
5736 * The STGM value is assumed valid.
5737 */
5738static DWORD GetShareModeFromSTGM(DWORD stgm)
5739{
5740 DWORD dwShareMode = 0;
5741 BOOL bSTGM_SHARE_DENY_NONE =
5742 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5743
5744 BOOL bSTGM_SHARE_DENY_READ =
5745 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5746
5747 BOOL bSTGM_SHARE_DENY_WRITE =
5748 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5749
5750 BOOL bSTGM_SHARE_EXCLUSIVE =
5751 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5752
5753 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5754 dwShareMode = 0;
5755
5756 if (bSTGM_SHARE_DENY_NONE)
5757 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5758
5759 if (bSTGM_SHARE_DENY_WRITE)
5760 dwShareMode = FILE_SHARE_READ;
5761
5762 return dwShareMode;
5763}
5764
5765/****************************************************************************
5766 * GetAccessModeFromSTGM
5767 *
5768 * This method will return an access mode flag from a STGM value.
5769 * The STGM value is assumed valid.
5770 */
5771static DWORD GetAccessModeFromSTGM(DWORD stgm)
5772{
5773 DWORD dwDesiredAccess = GENERIC_READ;
5774 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5775 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5776 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5777
5778 if (bSTGM_READ)
5779 dwDesiredAccess = GENERIC_READ;
5780
5781 if (bSTGM_WRITE)
5782 dwDesiredAccess |= GENERIC_WRITE;
5783
5784 if (bSTGM_READWRITE)
5785 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5786
5787 return dwDesiredAccess;
5788}
5789
5790/****************************************************************************
5791 * GetCreationModeFromSTGM
5792 *
5793 * This method will return a creation mode flag from a STGM value.
5794 * The STGM value is assumed valid.
5795 */
5796static DWORD GetCreationModeFromSTGM(DWORD stgm)
5797{
5798 if ( stgm & STGM_CREATE)
5799 return CREATE_ALWAYS;
5800 if (stgm & STGM_CONVERT) {
5801 FIXME("STGM_CONVERT not implemented!\n");
5802 return CREATE_NEW;
5803 }
5804 /* All other cases */
5805 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5806 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5807 return CREATE_NEW;
5808}
5809
5810
5811
5812/******************************************************************************
5813 * StgIsStorageFile16 [STORAGE.5]
5814 */
5815HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
5816 HFILE hf;
5817 OFSTRUCT ofs;
5818 BYTE magic[24];
5819
5820 TRACE_(ole)("(\'%s\')\n",fn);
5821 hf = OpenFile(fn,&ofs,OF_SHARE_DENY_NONE);
5822 if (hf==HFILE_ERROR)
5823 return STG_E_FILENOTFOUND;
5824 if (24!=_lread(hf,magic,24)) {
5825 WARN_(ole)(" too short\n");
5826 _lclose(hf);
5827 return S_FALSE;
5828 }
5829 if (!memcmp(magic,STORAGE_magic,8)) {
5830 WARN_(ole)(" -> YES\n");
5831 _lclose(hf);
5832 return S_OK;
5833 }
5834 if (!memcmp(magic,STORAGE_notmagic,8)) {
5835 WARN_(ole)(" -> NO\n");
5836 _lclose(hf);
5837 return S_FALSE;
5838 }
5839 if (!memcmp(magic,STORAGE_oldmagic,8)) {
5840 WARN_(ole)(" -> old format\n");
5841 _lclose(hf);
5842 return STG_E_OLDFORMAT;
5843 }
5844 WARN_(ole)(" -> Invalid header.\n");
5845 _lclose(hf);
5846 return STG_E_INVALIDHEADER;
5847}
5848
5849HRESULT WINAPI
5850StgIsStorageFile(LPCOLESTR fn)
5851{
5852 LPOLESTR16 xfn = HEAP_strdupWtoA(GetProcessHeap(),0,fn);
5853 HRESULT ret = StgIsStorageFile16(xfn);
5854
5855 HeapFree(GetProcessHeap(),0,xfn);
5856 return ret;
5857}
5858
Note: See TracBrowser for help on using the repository browser.