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

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

removed TRACE/WARN macro redefinition

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