source: trunk/src/ole32/storage32.c@ 6502

Last change on this file since 6502 was 5602, checked in by sandervl, 24 years ago

resync with Wine 20010418

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