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

Last change on this file since 7335 was 7335, checked in by phaller, 24 years ago

replaced heap alloc by stack alloc

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