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