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