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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
904
905 currentProperty.dirProperty = PROPERTY_NULL;
906 currentProperty.propertyType = PROPTYPE_STORAGE;
907 StorageImpl_WriteProperty(
908 This->ancestorStorage,
909 foundPropertyIndex,
910 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
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 ¤tProperty);
1506 current = previous;
1507 }
1508 else
1509 {
1510 currentProperty.previousProperty = newPropertyIndex;
1511 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1512 current,
1513 ¤tProperty);
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 ¤tProperty);
1524 current = next;
1525 }
1526 else
1527 {
1528 currentProperty.nextProperty = newPropertyIndex;
1529 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1530 current,
1531 ¤tProperty);
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 ¤tProperty);
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, ¤tElement, 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 ¤tProperty);
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;