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

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

Fixed out-of-scope FIXME,TRACE,WARN macros

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