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

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

restored old version + wine update

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