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

Last change on this file since 8441 was 8441, checked in by sandervl, 23 years ago

Wine resync

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