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

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

Wine resync

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