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