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 StorageBaseImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 DirEntry propertyToDelete);
166
167 static HRESULT deleteStreamProperty(
168 StorageBaseImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 DirEntry propertyToDelete);
171
172 static HRESULT removeFromTree(
173 StorageImpl *This,
174 ULONG parentStorageIndex,
175 ULONG deletedIndex);
176
177 /***********************************************************************
178 * Declaration of the functions used to manipulate DirEntry
179 */
180
181 static HRESULT createDirEntry(
182 StorageImpl *storage,
183 const DirEntry *newData,
184 ULONG *index);
185
186 static HRESULT destroyDirEntry(
187 StorageImpl *storage,
188 ULONG index);
189
190 static HRESULT insertIntoTree(
191 StorageImpl *This,
192 ULONG parentStorageIndex,
193 ULONG newPropertyIndex);
194
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
198
199 static ULONG findElement(
200 StorageImpl *storage,
201 ULONG storageEntry,
202 const OLECHAR *name,
203 DirEntry *data);
204
205 static HRESULT findTreeParent(
206 StorageImpl *storage,
207 ULONG storageEntry,
208 const OLECHAR *childName,
209 DirEntry *parentData,
210 ULONG *parentEntry,
211 ULONG *relation);
212
213 /***********************************************************************
214 * Declaration of miscellaneous functions...
215 */
216 static HRESULT validateSTGM(DWORD stgmValue);
217
218 static DWORD GetShareModeFromSTGM(DWORD stgm);
219 static DWORD GetAccessModeFromSTGM(DWORD stgm);
220 static DWORD GetCreationModeFromSTGM(DWORD stgm);
221
222 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
223
224
225 /****************************************************************************
226 * IEnumSTATSTGImpl definitions.
227 *
228 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
229 * This class allows iterating through the content of a storage and to find
230 * specific items inside it.
231 */
232 struct IEnumSTATSTGImpl
233 {
234 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
235 * since we want to cast this in an IEnumSTATSTG pointer */
236
237 LONG ref; /* Reference count */
238 StorageImpl* parentStorage; /* Reference to the parent storage */
239 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
240
241 /*
242 * The current implementation of the IEnumSTATSTGImpl class uses a stack
243 * to walk the property sets to get the content of a storage. This stack
244 * is implemented by the following 3 data members
245 */
246 ULONG stackSize;
247 ULONG stackMaxSize;
248 ULONG* stackToVisit;
249
250 #define ENUMSTATSGT_SIZE_INCREMENT 10
251 };
252
253
254 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
255 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
256 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
257 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
258
259 /************************************************************************
260 ** Block Functions
261 */
262
263 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
264 {
265 if (index == 0xffffffff)
266 index = 0;
267 else
268 index ++;
269
270 return index * BIG_BLOCK_SIZE;
271 }
272
273 /************************************************************************
274 ** Storage32BaseImpl implementation
275 */
276 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
277 ULARGE_INTEGER offset,
278 void* buffer,
279 ULONG size,
280 ULONG* bytesRead)
281 {
282 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
283 }
284
285 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
287 const void* buffer,
288 const ULONG size,
289 ULONG* bytesWritten)
290 {
291 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
292 }
293
294 /************************************************************************
295 * Storage32BaseImpl_QueryInterface (IUnknown)
296 *
297 * This method implements the common QueryInterface for all IStorage32
298 * implementations contained in this file.
299 *
300 * See Windows documentation for more details on IUnknown methods.
301 */
302 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
303 IStorage* iface,
304 REFIID riid,
305 void** ppvObject)
306 {
307 StorageBaseImpl *This = (StorageBaseImpl *)iface;
308
309 if ( (This==0) || (ppvObject==0) )
310 return E_INVALIDARG;
311
312 *ppvObject = 0;
313
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
316 {
317 *ppvObject = This;
318 }
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
320 {
321 *ppvObject = &This->pssVtbl;
322 }
323
324 if ((*ppvObject)==0)
325 return E_NOINTERFACE;
326
327 IStorage_AddRef(iface);
328
329 return S_OK;
330 }
331
332 /************************************************************************
333 * Storage32BaseImpl_AddRef (IUnknown)
334 *
335 * This method implements the common AddRef for all IStorage32
336 * implementations contained in this file.
337 *
338 * See Windows documentation for more details on IUnknown methods.
339 */
340 static ULONG WINAPI StorageBaseImpl_AddRef(
341 IStorage* iface)
342 {
343 StorageBaseImpl *This = (StorageBaseImpl *)iface;
344 ULONG ref = InterlockedIncrement(&This->ref);
345
346 TRACE("(%p) AddRef to %d\n", This, ref);
347
348 return ref;
349 }
350
351 /************************************************************************
352 * Storage32BaseImpl_Release (IUnknown)
353 *
354 * This method implements the common Release for all IStorage32
355 * implementations contained in this file.
356 *
357 * See Windows documentation for more details on IUnknown methods.
358 */
359 static ULONG WINAPI StorageBaseImpl_Release(
360 IStorage* iface)
361 {
362 StorageBaseImpl *This = (StorageBaseImpl *)iface;
363
364 ULONG ref = InterlockedDecrement(&This->ref);
365
366 TRACE("(%p) ReleaseRef to %d\n", This, ref);
367
368 if (ref == 0)
369 {
370 /*
371 * Since we are using a system of base-classes, we want to call the
372 * destructor of the appropriate derived class. To do this, we are
373 * using virtual functions to implement the destructor.
374 */
375 This->v_destructor(This);
376 }
377
378 return ref;
379 }
380
381 /************************************************************************
382 * Storage32BaseImpl_OpenStream (IStorage)
383 *
384 * This method will open the specified stream object from the current storage.
385 *
386 * See Windows documentation for more details on IStorage methods.
387 */
388 static HRESULT WINAPI StorageBaseImpl_OpenStream(
389 IStorage* iface,
390 const OLECHAR* pwcsName, /* [string][in] */
391 void* reserved1, /* [unique][in] */
392 DWORD grfMode, /* [in] */
393 DWORD reserved2, /* [in] */
394 IStream** ppstm) /* [out] */
395 {
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 StgStreamImpl* newStream;
398 DirEntry currentProperty;
399 ULONG foundPropertyIndex;
400 HRESULT res = STG_E_UNKNOWN;
401
402 TRACE("(%p, %s, %p, %x, %d, %p)\n",
403 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
404
405 if ( (pwcsName==NULL) || (ppstm==0) )
406 {
407 res = E_INVALIDARG;
408 goto end;
409 }
410
411 *ppstm = NULL;
412
413 if ( FAILED( validateSTGM(grfMode) ) ||
414 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
415 {
416 res = STG_E_INVALIDFLAG;
417 goto end;
418 }
419
420 /*
421 * As documented.
422 */
423 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
424 {
425 res = STG_E_INVALIDFUNCTION;
426 goto end;
427 }
428
429 /*
430 * Check that we're compatible with the parent's storage mode, but
431 * only if we are not in transacted mode
432 */
433 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
434 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
435 {
436 res = STG_E_ACCESSDENIED;
437 goto end;
438 }
439 }
440
441 /*
442 * Search for the element with the given name
443 */
444 foundPropertyIndex = findElement(
445 This->ancestorStorage,
446 This->rootPropertySetIndex,
447 pwcsName,
448 ¤tProperty);
449
450 /*
451 * If it was found, construct the stream object and return a pointer to it.
452 */
453 if ( (foundPropertyIndex!=DIRENTRY_NULL) &&
454 (currentProperty.propertyType==STGTY_STREAM) )
455 {
456 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
457
458 if (newStream!=0)
459 {
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
462
463 IStream_AddRef(*ppstm);
464
465 res = S_OK;
466 goto end;
467 }
468
469 res = E_OUTOFMEMORY;
470 goto end;
471 }
472
473 res = STG_E_FILENOTFOUND;
474
475 end:
476 if (res == S_OK)
477 TRACE("<-- IStream %p\n", *ppstm);
478 TRACE("<-- %08x\n", res);
479 return res;
480 }
481
482 /************************************************************************
483 * Storage32BaseImpl_OpenStorage (IStorage)
484 *
485 * This method will open a new storage object from the current storage.
486 *
487 * See Windows documentation for more details on IStorage methods.
488 */
489 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
490 IStorage* iface,
491 const OLECHAR* pwcsName, /* [string][unique][in] */
492 IStorage* pstgPriority, /* [unique][in] */
493 DWORD grfMode, /* [in] */
494 SNB snbExclude, /* [unique][in] */
495 DWORD reserved, /* [in] */
496 IStorage** ppstg) /* [out] */
497 {
498 StorageBaseImpl *This = (StorageBaseImpl *)iface;
499 StorageInternalImpl* newStorage;
500 DirEntry currentProperty;
501 ULONG foundPropertyIndex;
502 HRESULT res = STG_E_UNKNOWN;
503
504 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
505 iface, debugstr_w(pwcsName), pstgPriority,
506 grfMode, snbExclude, reserved, ppstg);
507
508 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
509 {
510 res = E_INVALIDARG;
511 goto end;
512 }
513
514 /* as documented */
515 if (snbExclude != NULL)
516 {
517 res = STG_E_INVALIDPARAMETER;
518 goto end;
519 }
520
521 if ( FAILED( validateSTGM(grfMode) ))
522 {
523 res = STG_E_INVALIDFLAG;
524 goto end;
525 }
526
527 /*
528 * As documented.
529 */
530 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
531 (grfMode & STGM_DELETEONRELEASE) ||
532 (grfMode & STGM_PRIORITY) )
533 {
534 res = STG_E_INVALIDFUNCTION;
535 goto end;
536 }
537
538 /*
539 * Check that we're compatible with the parent's storage mode,
540 * but only if we are not transacted
541 */
542 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
543 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
544 {
545 res = STG_E_ACCESSDENIED;
546 goto end;
547 }
548 }
549
550 *ppstg = NULL;
551
552 foundPropertyIndex = findElement(
553 This->ancestorStorage,
554 This->rootPropertySetIndex,
555 pwcsName,
556 ¤tProperty);
557
558 if ( (foundPropertyIndex!=DIRENTRY_NULL) &&
559 (currentProperty.propertyType==STGTY_STORAGE) )
560 {
561 newStorage = StorageInternalImpl_Construct(
562 This->ancestorStorage,
563 grfMode,
564 foundPropertyIndex);
565
566 if (newStorage != 0)
567 {
568 *ppstg = (IStorage*)newStorage;
569
570 StorageBaseImpl_AddRef(*ppstg);
571
572 res = S_OK;
573 goto end;
574 }
575
576 res = STG_E_INSUFFICIENTMEMORY;
577 goto end;
578 }
579
580 res = STG_E_FILENOTFOUND;
581
582 end:
583 TRACE("<-- %08x\n", res);
584 return res;
585 }
586
587 /************************************************************************
588 * Storage32BaseImpl_EnumElements (IStorage)
589 *
590 * This method will create an enumerator object that can be used to
591 * retrieve information about all the properties in the storage object.
592 *
593 * See Windows documentation for more details on IStorage methods.
594 */
595 static HRESULT WINAPI StorageBaseImpl_EnumElements(
596 IStorage* iface,
597 DWORD reserved1, /* [in] */
598 void* reserved2, /* [size_is][unique][in] */
599 DWORD reserved3, /* [in] */
600 IEnumSTATSTG** ppenum) /* [out] */
601 {
602 StorageBaseImpl *This = (StorageBaseImpl *)iface;
603 IEnumSTATSTGImpl* newEnum;
604
605 TRACE("(%p, %d, %p, %d, %p)\n",
606 iface, reserved1, reserved2, reserved3, ppenum);
607
608 if ( (This==0) || (ppenum==0))
609 return E_INVALIDARG;
610
611 newEnum = IEnumSTATSTGImpl_Construct(
612 This->ancestorStorage,
613 This->rootPropertySetIndex);
614
615 if (newEnum!=0)
616 {
617 *ppenum = (IEnumSTATSTG*)newEnum;
618
619 IEnumSTATSTG_AddRef(*ppenum);
620
621 return S_OK;
622 }
623
624 return E_OUTOFMEMORY;
625 }
626
627 /************************************************************************
628 * Storage32BaseImpl_Stat (IStorage)
629 *
630 * This method will retrieve information about this storage object.
631 *
632 * See Windows documentation for more details on IStorage methods.
633 */
634 static HRESULT WINAPI StorageBaseImpl_Stat(
635 IStorage* iface,
636 STATSTG* pstatstg, /* [out] */
637 DWORD grfStatFlag) /* [in] */
638 {
639 StorageBaseImpl *This = (StorageBaseImpl *)iface;
640 DirEntry curProperty;
641 BOOL readSuccessful;
642 HRESULT res = STG_E_UNKNOWN;
643
644 TRACE("(%p, %p, %x)\n",
645 iface, pstatstg, grfStatFlag);
646
647 if ( (This==0) || (pstatstg==0))
648 {
649 res = E_INVALIDARG;
650 goto end;
651 }
652
653 readSuccessful = StorageImpl_ReadDirEntry(
654 This->ancestorStorage,
655 This->rootPropertySetIndex,
656 &curProperty);
657
658 if (readSuccessful)
659 {
660 StorageUtl_CopyDirEntryToSTATSTG(
661 pstatstg,
662 &curProperty,
663 grfStatFlag);
664
665 pstatstg->grfMode = This->openFlags;
666 pstatstg->grfStateBits = This->stateBits;
667
668 res = S_OK;
669 goto end;
670 }
671
672 res = E_FAIL;
673
674 end:
675 if (res == S_OK)
676 {
677 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);
678 }
679 TRACE("<-- %08x\n", res);
680 return res;
681 }
682
683 /************************************************************************
684 * Storage32BaseImpl_RenameElement (IStorage)
685 *
686 * This method will rename the specified element.
687 *
688 * See Windows documentation for more details on IStorage methods.
689 */
690 static HRESULT WINAPI StorageBaseImpl_RenameElement(
691 IStorage* iface,
692 const OLECHAR* pwcsOldName, /* [in] */
693 const OLECHAR* pwcsNewName) /* [in] */
694 {
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 DirEntry currentProperty;
697 ULONG foundPropertyIndex;
698
699 TRACE("(%p, %s, %s)\n",
700 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
701
702 foundPropertyIndex = findElement(This->ancestorStorage,
703 This->rootPropertySetIndex,
704 pwcsNewName,
705 ¤tProperty);
706
707 if (foundPropertyIndex != DIRENTRY_NULL)
708 {
709 /*
710 * There is already a property with the new name
711 */
712 return STG_E_FILEALREADYEXISTS;
713 }
714
715 /*
716 * Search for the old element name
717 */
718 foundPropertyIndex = findElement(This->ancestorStorage,
719 This->rootPropertySetIndex,
720 pwcsOldName,
721 ¤tProperty);
722
723 if (foundPropertyIndex != DIRENTRY_NULL)
724 {
725 /* Remove the element from its current position in the tree */
726 removeFromTree(This->ancestorStorage, This->rootPropertySetIndex,
727 foundPropertyIndex);
728
729 /* Change the name of the element */
730 strcpyW(currentProperty.name, pwcsNewName);
731
732 StorageImpl_WriteDirEntry(This->ancestorStorage, foundPropertyIndex,
733 ¤tProperty);
734
735 /* Insert the element in a new position in the tree */
736 insertIntoTree(This->ancestorStorage, This->rootPropertySetIndex,
737 foundPropertyIndex);
738 }
739 else
740 {
741 /*
742 * There is no property with the old name
743 */
744 return STG_E_FILENOTFOUND;
745 }
746
747 return S_OK;
748 }
749
750 /************************************************************************
751 * Storage32BaseImpl_CreateStream (IStorage)
752 *
753 * This method will create a stream object within this storage
754 *
755 * See Windows documentation for more details on IStorage methods.
756 */
757 static HRESULT WINAPI StorageBaseImpl_CreateStream(
758 IStorage* iface,
759 const OLECHAR* pwcsName, /* [string][in] */
760 DWORD grfMode, /* [in] */
761 DWORD reserved1, /* [in] */
762 DWORD reserved2, /* [in] */
763 IStream** ppstm) /* [out] */
764 {
765 StorageBaseImpl *This = (StorageBaseImpl *)iface;
766 StgStreamImpl* newStream;
767 DirEntry currentProperty, newStreamProperty;
768 ULONG foundPropertyIndex, newPropertyIndex;
769
770 TRACE("(%p, %s, %x, %d, %d, %p)\n",
771 iface, debugstr_w(pwcsName), grfMode,
772 reserved1, reserved2, ppstm);
773
774 if (ppstm == 0)
775 return STG_E_INVALIDPOINTER;
776
777 if (pwcsName == 0)
778 return STG_E_INVALIDNAME;
779
780 if (reserved1 || reserved2)
781 return STG_E_INVALIDPARAMETER;
782
783 if ( FAILED( validateSTGM(grfMode) ))
784 return STG_E_INVALIDFLAG;
785
786 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
787 return STG_E_INVALIDFLAG;
788
789 /*
790 * As documented.
791 */
792 if ((grfMode & STGM_DELETEONRELEASE) ||
793 (grfMode & STGM_TRANSACTED))
794 return STG_E_INVALIDFUNCTION;
795
796 /* Can't create a stream on read-only storage */
797 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
798 return STG_E_ACCESSDENIED;
799
800 /*
801 * Check that we're compatible with the parent's storage mode
802 * if not in transacted mode
803 */
804 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
805 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
806 return STG_E_ACCESSDENIED;
807 }
808
809 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
810 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
811
812 *ppstm = 0;
813
814 foundPropertyIndex = findElement(This->ancestorStorage,
815 This->rootPropertySetIndex,
816 pwcsName,
817 ¤tProperty);
818
819 if (foundPropertyIndex != DIRENTRY_NULL)
820 {
821 /*
822 * An element with this name already exists
823 */
824 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
825 {
826 StgStreamImpl *strm;
827
828 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
829 {
830 if (strm->ownerProperty == foundPropertyIndex)
831 {
832 TRACE("Stream deleted %p\n", strm);
833 strm->parentStorage = NULL;
834 list_remove(&strm->StrmListEntry);
835 }
836 }
837 IStorage_DestroyElement(iface, pwcsName);
838 }
839 else
840 return STG_E_FILEALREADYEXISTS;
841 }
842 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
843 {
844 WARN("read-only storage\n");
845 return STG_E_ACCESSDENIED;
846 }
847
848 /*
849 * memset the empty property
850 */
851 memset(&newStreamProperty, 0, sizeof(DirEntry));
852
853 newStreamProperty.sizeOfNameString =
854 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
855
856 if (newStreamProperty.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
857 return STG_E_INVALIDNAME;
858
859 strcpyW(newStreamProperty.name, pwcsName);
860
861 newStreamProperty.propertyType = STGTY_STREAM;
862 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
863 newStreamProperty.size.u.LowPart = 0;
864 newStreamProperty.size.u.HighPart = 0;
865
866 newStreamProperty.leftChild = DIRENTRY_NULL;
867 newStreamProperty.rightChild = DIRENTRY_NULL;
868 newStreamProperty.dirRootEntry = DIRENTRY_NULL;
869
870 /* call CoFileTime to get the current time
871 newStreamProperty.ctime
872 newStreamProperty.mtime
873 */
874
875 /* newStreamProperty.propertyUniqueID */
876
877 /*
878 * Save the new property into a new property spot
879 */
880 createDirEntry(This->ancestorStorage, &newStreamProperty, &newPropertyIndex);
881
882 /*
883 * Find a spot in the property chain for our newly created property.
884 */
885 insertIntoTree(
886 This->ancestorStorage,
887 This->rootPropertySetIndex,
888 newPropertyIndex);
889
890 /*
891 * Open the stream to return it.
892 */
893 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
894
895 if (newStream != 0)
896 {
897 *ppstm = (IStream*)newStream;
898
899 IStream_AddRef(*ppstm);
900 }
901 else
902 {
903 return STG_E_INSUFFICIENTMEMORY;
904 }
905
906 return S_OK;
907 }
908
909 /************************************************************************
910 * Storage32BaseImpl_SetClass (IStorage)
911 *
912 * This method will write the specified CLSID in the property of this
913 * storage.
914 *
915 * See Windows documentation for more details on IStorage methods.
916 */
917 static HRESULT WINAPI StorageBaseImpl_SetClass(
918 IStorage* iface,
919 REFCLSID clsid) /* [in] */
920 {
921 StorageBaseImpl *This = (StorageBaseImpl *)iface;
922 HRESULT hRes = E_FAIL;
923 DirEntry curProperty;
924 BOOL success;
925
926 TRACE("(%p, %p)\n", iface, clsid);
927
928 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
929 This->rootPropertySetIndex,
930 &curProperty);
931 if (success)
932 {
933 curProperty.propertyUniqueID = *clsid;
934
935 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
936 This->rootPropertySetIndex,
937 &curProperty);
938 if (success)
939 hRes = S_OK;
940 }
941
942 return hRes;
943 }
944
945 /************************************************************************
946 ** Storage32Impl implementation
947 */
948
949 /************************************************************************
950 * Storage32BaseImpl_CreateStorage (IStorage)
951 *
952 * This method will create the storage object within the provided storage.
953 *
954 * See Windows documentation for more details on IStorage methods.
955 */
956 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
957 IStorage* iface,
958 const OLECHAR *pwcsName, /* [string][in] */
959 DWORD grfMode, /* [in] */
960 DWORD reserved1, /* [in] */
961 DWORD reserved2, /* [in] */
962 IStorage **ppstg) /* [out] */
963 {
964 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
965
966 DirEntry currentProperty;
967 DirEntry newProperty;
968 ULONG foundPropertyIndex;
969 ULONG newPropertyIndex;
970 HRESULT hr;
971
972 TRACE("(%p, %s, %x, %d, %d, %p)\n",
973 iface, debugstr_w(pwcsName), grfMode,
974 reserved1, reserved2, ppstg);
975
976 if (ppstg == 0)
977 return STG_E_INVALIDPOINTER;
978
979 if (pwcsName == 0)
980 return STG_E_INVALIDNAME;
981
982 *ppstg = NULL;
983
984 if ( FAILED( validateSTGM(grfMode) ) ||
985 (grfMode & STGM_DELETEONRELEASE) )
986 {
987 WARN("bad grfMode: 0x%x\n", grfMode);
988 return STG_E_INVALIDFLAG;
989 }
990
991 /*
992 * Check that we're compatible with the parent's storage mode
993 */
994 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
995 {
996 WARN("access denied\n");
997 return STG_E_ACCESSDENIED;
998 }
999
1000 foundPropertyIndex = findElement(This->ancestorStorage,
1001 This->rootPropertySetIndex,
1002 pwcsName,
1003 ¤tProperty);
1004
1005 if (foundPropertyIndex != DIRENTRY_NULL)
1006 {
1007 /*
1008 * An element with this name already exists
1009 */
1010 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1011 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1012 {
1013 hr = IStorage_DestroyElement(iface, pwcsName);
1014 if (FAILED(hr))
1015 return hr;
1016 }
1017 else
1018 {
1019 WARN("file already exists\n");
1020 return STG_E_FILEALREADYEXISTS;
1021 }
1022 }
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1024 {
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1027 }
1028
1029 /*
1030 * memset the empty property
1031 */
1032 memset(&newProperty, 0, sizeof(DirEntry));
1033
1034 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1035
1036 if (newProperty.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1037 {
1038 FIXME("name too long\n");
1039 return STG_E_INVALIDNAME;
1040 }
1041
1042 strcpyW(newProperty.name, pwcsName);
1043
1044 newProperty.propertyType = STGTY_STORAGE;
1045 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1046 newProperty.size.u.LowPart = 0;
1047 newProperty.size.u.HighPart = 0;
1048
1049 newProperty.leftChild = DIRENTRY_NULL;
1050 newProperty.rightChild = DIRENTRY_NULL;
1051 newProperty.dirRootEntry = DIRENTRY_NULL;
1052
1053 /* call CoFileTime to get the current time
1054 newProperty.ctime
1055 newProperty.mtime
1056 */
1057
1058 /* newStorageProperty.propertyUniqueID */
1059
1060 /*
1061 * Save the new property into a new property spot
1062 */
1063 createDirEntry(This->ancestorStorage, &newProperty, &newPropertyIndex);
1064
1065 /*
1066 * Find a spot in the property chain for our newly created property.
1067 */
1068 insertIntoTree(
1069 This->ancestorStorage,
1070 This->rootPropertySetIndex,
1071 newPropertyIndex);
1072
1073 /*
1074 * Open it to get a pointer to return.
1075 */
1076 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1077
1078 if( (hr != S_OK) || (*ppstg == NULL))
1079 {
1080 return hr;
1081 }
1082
1083
1084 return S_OK;
1085 }
1086
1087
1088 /***************************************************************************
1089 *
1090 * Internal Method
1091 *
1092 * Reserve a directory entry in the file and initialize it.
1093 */
1094 static HRESULT createDirEntry(
1095 StorageImpl *storage,
1096 const DirEntry *newData,
1097 ULONG *index)
1098 {
1099 ULONG currentPropertyIndex = 0;
1100 ULONG newPropertyIndex = DIRENTRY_NULL;
1101 HRESULT hr = S_OK;
1102 BYTE currentData[RAW_DIRENTRY_SIZE];
1103 WORD sizeOfNameString;
1104
1105 do
1106 {
1107 hr = StorageImpl_ReadRawDirEntry(storage,
1108 currentPropertyIndex,
1109 currentData);
1110
1111 if (SUCCEEDED(hr))
1112 {
1113 StorageUtl_ReadWord(
1114 currentData,
1115 OFFSET_PS_NAMELENGTH,
1116 &sizeOfNameString);
1117
1118 if (sizeOfNameString == 0)
1119 {
1120 /*
1121 * The property existis and is available, we found it.
1122 */
1123 newPropertyIndex = currentPropertyIndex;
1124 }
1125 }
1126 else
1127 {
1128 /*
1129 * We exhausted the property list, we will create more space below
1130 */
1131 newPropertyIndex = currentPropertyIndex;
1132 }
1133 currentPropertyIndex++;
1134
1135 } while (newPropertyIndex == DIRENTRY_NULL);
1136
1137 /*
1138 * grow the property chain
1139 */
1140 if (FAILED(hr))
1141 {
1142 BYTE emptyData[RAW_DIRENTRY_SIZE];
1143 ULARGE_INTEGER newSize;
1144 ULONG propertyIndex;
1145 ULONG lastProperty = 0;
1146 ULONG blockCount = 0;
1147
1148 /*
1149 * obtain the new count of property blocks
1150 */
1151 blockCount = BlockChainStream_GetCount(
1152 storage->rootBlockChain)+1;
1153
1154 /*
1155 * initialize the size used by the property stream
1156 */
1157 newSize.u.HighPart = 0;
1158 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1159
1160 /*
1161 * add a property block to the property chain
1162 */
1163 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1164
1165 /*
1166 * memset the empty property in order to initialize the unused newly
1167 * created property
1168 */
1169 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1170
1171 /*
1172 * initialize them
1173 */
1174 lastProperty = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1175
1176 for(
1177 propertyIndex = newPropertyIndex + 1;
1178 propertyIndex < lastProperty;
1179 propertyIndex++)
1180 {
1181 StorageImpl_WriteRawDirEntry(
1182 storage,
1183 propertyIndex,
1184 emptyData);
1185 }
1186 }
1187
1188 UpdateRawDirEntry(currentData, newData);
1189
1190 hr = StorageImpl_WriteRawDirEntry(storage, newPropertyIndex, currentData);
1191
1192 if (SUCCEEDED(hr))
1193 *index = newPropertyIndex;
1194
1195 return hr;
1196 }
1197
1198 /***************************************************************************
1199 *
1200 * Internal Method
1201 *
1202 * Mark a directory entry in the file as free.
1203 */
1204 static HRESULT destroyDirEntry(
1205 StorageImpl *storage,
1206 ULONG index)
1207 {
1208 HRESULT hr;
1209 BYTE emptyData[RAW_DIRENTRY_SIZE];
1210
1211 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1212
1213 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1214
1215 return hr;
1216 }
1217
1218
1219 /****************************************************************************
1220 *
1221 * Internal Method
1222 *
1223 * Case insensitive comparison of DirEntry.name by first considering
1224 * their size.
1225 *
1226 * Returns <0 when newProperty < currentProperty
1227 * >0 when newProperty > currentProperty
1228 * 0 when newProperty == currentProperty
1229 */
1230 static LONG propertyNameCmp(
1231 const OLECHAR *newProperty,
1232 const OLECHAR *currentProperty)
1233 {
1234 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1235
1236 if (diff == 0)
1237 {
1238 /*
1239 * We compare the string themselves only when they are of the same length
1240 */
1241 diff = lstrcmpiW( newProperty, currentProperty);
1242 }
1243
1244 return diff;
1245 }
1246
1247 /****************************************************************************
1248 *
1249 * Internal Method
1250 *
1251 * Properly link this new element in the property chain.
1252 */
1253 static HRESULT insertIntoTree(
1254 StorageImpl *This,
1255 ULONG parentStorageIndex,
1256 ULONG newPropertyIndex)
1257 {
1258 DirEntry currentProperty;
1259 DirEntry newProperty;
1260
1261 /*
1262 * Read the inserted property
1263 */
1264 StorageImpl_ReadDirEntry(This,
1265 newPropertyIndex,
1266 &newProperty);
1267
1268 /*
1269 * Read the root property
1270 */
1271 StorageImpl_ReadDirEntry(This,
1272 parentStorageIndex,
1273 ¤tProperty);
1274
1275 if (currentProperty.dirRootEntry != DIRENTRY_NULL)
1276 {
1277 /*
1278 * The root storage contains some element, therefore, start the research
1279 * for the appropriate location.
1280 */
1281 BOOL found = 0;
1282 ULONG current, next, previous, currentPropertyId;
1283
1284 /*
1285 * Keep the DirEntry sequence number of the storage first property
1286 */
1287 currentPropertyId = currentProperty.dirRootEntry;
1288
1289 /*
1290 * Read
1291 */
1292 StorageImpl_ReadDirEntry(This,
1293 currentProperty.dirRootEntry,
1294 ¤tProperty);
1295
1296 previous = currentProperty.leftChild;
1297 next = currentProperty.rightChild;
1298 current = currentPropertyId;
1299
1300 while (found == 0)
1301 {
1302 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1303
1304 if (diff < 0)
1305 {
1306 if (previous != DIRENTRY_NULL)
1307 {
1308 StorageImpl_ReadDirEntry(This,
1309 previous,
1310 ¤tProperty);
1311 current = previous;
1312 }
1313 else
1314 {
1315 currentProperty.leftChild = newPropertyIndex;
1316 StorageImpl_WriteDirEntry(This,
1317 current,
1318 ¤tProperty);
1319 found = 1;
1320 }
1321 }
1322 else if (diff > 0)
1323 {
1324 if (next != DIRENTRY_NULL)
1325 {
1326 StorageImpl_ReadDirEntry(This,
1327 next,
1328 ¤tProperty);
1329 current = next;
1330 }
1331 else
1332 {
1333 currentProperty.rightChild = newPropertyIndex;
1334 StorageImpl_WriteDirEntry(This,
1335 current,
1336 ¤tProperty);
1337 found = 1;
1338 }
1339 }
1340 else
1341 {
1342 /*
1343 * Trying to insert an item with the same name in the
1344 * subtree structure.
1345 */
1346 return STG_E_FILEALREADYEXISTS;
1347 }
1348
1349 previous = currentProperty.leftChild;
1350 next = currentProperty.rightChild;
1351 }
1352 }
1353 else
1354 {
1355 /*
1356 * The root storage is empty, link the new property to its dir property
1357 */
1358 currentProperty.dirRootEntry = newPropertyIndex;
1359 StorageImpl_WriteDirEntry(This,
1360 parentStorageIndex,
1361 ¤tProperty);
1362 }
1363
1364 return S_OK;
1365 }
1366
1367 /****************************************************************************
1368 *
1369 * Internal Method
1370 *
1371 * Find and read the element of a storage with the given name.
1372 */
1373 static ULONG findElement(StorageImpl *storage, ULONG storageEntry,
1374 const OLECHAR *name, DirEntry *data)
1375 {
1376 ULONG currentEntry;
1377
1378 /* Read the storage entry to find the root of the tree. */
1379 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1380
1381 currentEntry = data->dirRootEntry;
1382
1383 while (currentEntry != DIRENTRY_NULL)
1384 {
1385 LONG cmp;
1386
1387 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1388
1389 cmp = propertyNameCmp(name, data->name);
1390
1391 if (cmp == 0)
1392 /* found it */
1393 break;
1394
1395 else if (cmp < 0)
1396 currentEntry = data->leftChild;
1397
1398 else if (cmp > 0)
1399 currentEntry = data->rightChild;
1400 }
1401
1402 return currentEntry;
1403 }
1404
1405 /****************************************************************************
1406 *
1407 * Internal Method
1408 *
1409 * Find and read the binary tree parent of the element with the given name.
1410 *
1411 * If there is no such element, find a place where it could be inserted and
1412 * return STG_E_FILENOTFOUND.
1413 */
1414 static HRESULT findTreeParent(StorageImpl *storage, ULONG storageEntry,
1415 const OLECHAR *childName, DirEntry *parentData, ULONG *parentEntry,
1416 ULONG *relation)
1417 {
1418 ULONG childEntry;
1419 DirEntry childData;
1420
1421 /* Read the storage entry to find the root of the tree. */
1422 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1423
1424 *parentEntry = storageEntry;
1425 *relation = DIRENTRY_RELATION_DIR;
1426
1427 childEntry = parentData->dirRootEntry;
1428
1429 while (childEntry != DIRENTRY_NULL)
1430 {
1431 LONG cmp;
1432
1433 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1434
1435 cmp = propertyNameCmp(childName, childData.name);
1436
1437 if (cmp == 0)
1438 /* found it */
1439 break;
1440
1441 else if (cmp < 0)
1442 {
1443 *parentData = childData;
1444 *parentEntry = childEntry;
1445 *relation = DIRENTRY_RELATION_PREVIOUS;
1446
1447 childEntry = parentData->leftChild;
1448 }
1449
1450 else if (cmp > 0)
1451 {
1452 *parentData = childData;
1453 *parentEntry = childEntry;
1454 *relation = DIRENTRY_RELATION_NEXT;
1455
1456 childEntry = parentData->rightChild;
1457 }
1458 }
1459
1460 if (childEntry == DIRENTRY_NULL)
1461 return STG_E_FILENOTFOUND;
1462 else
1463 return S_OK;
1464 }
1465
1466
1467 /*************************************************************************
1468 * CopyTo (IStorage)
1469 */
1470 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1471 IStorage* iface,
1472 DWORD ciidExclude, /* [in] */
1473 const IID* rgiidExclude, /* [size_is][unique][in] */
1474 SNB snbExclude, /* [unique][in] */
1475 IStorage* pstgDest) /* [unique][in] */
1476 {
1477 IEnumSTATSTG *elements = 0;
1478 STATSTG curElement, strStat;
1479 HRESULT hr;
1480 IStorage *pstgTmp, *pstgChild;
1481 IStream *pstrTmp, *pstrChild;
1482 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1483 int i;
1484
1485 TRACE("(%p, %d, %p, %p, %p)\n",
1486 iface, ciidExclude, rgiidExclude,
1487 snbExclude, pstgDest);
1488
1489 if ( pstgDest == 0 )
1490 return STG_E_INVALIDPOINTER;
1491
1492 /*
1493 * Enumerate the elements
1494 */
1495 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1496
1497 if ( hr != S_OK )
1498 return hr;
1499
1500 /*
1501 * set the class ID
1502 */
1503 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1504 IStorage_SetClass( pstgDest, &curElement.clsid );
1505
1506 for(i = 0; i < ciidExclude; ++i)
1507 {
1508 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1509 skip_storage = TRUE;
1510 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1511 skip_stream = TRUE;
1512 else
1513 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1514 }
1515
1516 do
1517 {
1518 /*
1519 * Obtain the next element
1520 */
1521 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1522
1523 if ( hr == S_FALSE )
1524 {
1525 hr = S_OK; /* done, every element has been copied */
1526 break;
1527 }
1528
1529 if ( snbExclude )
1530 {
1531 WCHAR **snb = snbExclude;
1532 skip = FALSE;
1533 while ( *snb != NULL && !skip )
1534 {
1535 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1536 skip = TRUE;
1537 ++snb;
1538 }
1539 }
1540
1541 if ( skip )
1542 continue;
1543
1544 if (curElement.type == STGTY_STORAGE)
1545 {
1546 if(skip_storage)
1547 continue;
1548
1549 /*
1550 * open child source storage
1551 */
1552 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1553 STGM_READ|STGM_SHARE_EXCLUSIVE,
1554 NULL, 0, &pstgChild );
1555
1556 if (hr != S_OK)
1557 break;
1558
1559 /*
1560 * Check if destination storage is not a child of the source
1561 * storage, which will cause an infinite loop
1562 */
1563 if (pstgChild == pstgDest)
1564 {
1565 IEnumSTATSTG_Release(elements);
1566
1567 return STG_E_ACCESSDENIED;
1568 }
1569
1570 /*
1571 * create a new storage in destination storage
1572 */
1573 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1574 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1575 0, 0,
1576 &pstgTmp );
1577 /*
1578 * if it already exist, don't create a new one use this one
1579 */
1580 if (hr == STG_E_FILEALREADYEXISTS)
1581 {
1582 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1583 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1584 NULL, 0, &pstgTmp );
1585 }
1586
1587 if (hr != S_OK)
1588 break;
1589
1590
1591 /*
1592 * do the copy recursively
1593 */
1594 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1595 NULL, pstgTmp );
1596
1597 IStorage_Release( pstgTmp );
1598 IStorage_Release( pstgChild );
1599 }
1600 else if (curElement.type == STGTY_STREAM)
1601 {
1602 if(skip_stream)
1603 continue;
1604
1605 /*
1606 * create a new stream in destination storage. If the stream already
1607 * exist, it will be deleted and a new one will be created.
1608 */
1609 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1610 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1611 0, 0, &pstrTmp );
1612
1613 if (hr != S_OK)
1614 break;
1615
1616 /*
1617 * open child stream storage
1618 */
1619 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1620 STGM_READ|STGM_SHARE_EXCLUSIVE,
1621 0, &pstrChild );
1622
1623 if (hr != S_OK)
1624 break;
1625
1626 /*
1627 * Get the size of the source stream
1628 */
1629 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1630
1631 /*
1632 * Set the size of the destination stream.
1633 */
1634 IStream_SetSize(pstrTmp, strStat.cbSize);
1635
1636 /*
1637 * do the copy
1638 */
1639 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1640 NULL, NULL );
1641
1642 IStream_Release( pstrTmp );
1643 IStream_Release( pstrChild );
1644 }
1645 else
1646 {
1647 WARN("unknown element type: %d\n", curElement.type);
1648 }
1649
1650 } while (hr == S_OK);
1651
1652 /*
1653 * Clean-up
1654 */
1655 IEnumSTATSTG_Release(elements);
1656
1657 return hr;
1658 }
1659
1660 /*************************************************************************
1661 * MoveElementTo (IStorage)
1662 */
1663 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1664 IStorage* iface,
1665 const OLECHAR *pwcsName, /* [string][in] */
1666 IStorage *pstgDest, /* [unique][in] */
1667 const OLECHAR *pwcsNewName,/* [string][in] */
1668 DWORD grfFlags) /* [in] */
1669 {
1670 FIXME("(%p %s %p %s %u): stub\n", iface,
1671 debugstr_w(pwcsName), pstgDest,
1672 debugstr_w(pwcsNewName), grfFlags);
1673 return E_NOTIMPL;
1674 }
1675
1676 /*************************************************************************
1677 * Commit (IStorage)
1678 *
1679 * Ensures that any changes made to a storage object open in transacted mode
1680 * are reflected in the parent storage
1681 *
1682 * NOTES
1683 * Wine doesn't implement transacted mode, which seems to be a basic
1684 * optimization, so we can ignore this stub for now.
1685 */
1686 static HRESULT WINAPI StorageImpl_Commit(
1687 IStorage* iface,
1688 DWORD grfCommitFlags)/* [in] */
1689 {
1690 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1691 return S_OK;
1692 }
1693
1694 /*************************************************************************
1695 * Revert (IStorage)
1696 *
1697 * Discard all changes that have been made since the last commit operation
1698 */
1699 static HRESULT WINAPI StorageImpl_Revert(
1700 IStorage* iface)
1701 {
1702 FIXME("(%p): stub\n", iface);
1703 return E_NOTIMPL;
1704 }
1705
1706 /*************************************************************************
1707 * DestroyElement (IStorage)
1708 *
1709 * Strategy: This implementation is built this way for simplicity not for speed.
1710 * I always delete the topmost element of the enumeration and adjust
1711 * the deleted element pointer all the time. This takes longer to
1712 * do but allow to reinvoke DestroyElement whenever we encounter a
1713 * storage object. The optimisation resides in the usage of another
1714 * enumeration strategy that would give all the leaves of a storage
1715 * first. (postfix order)
1716 */
1717 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1718 IStorage* iface,
1719 const OLECHAR *pwcsName)/* [string][in] */
1720 {
1721 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1722
1723 HRESULT hr = S_OK;
1724 DirEntry propertyToDelete;
1725 ULONG foundPropertyIndexToDelete;
1726
1727 TRACE("(%p, %s)\n",
1728 iface, debugstr_w(pwcsName));
1729
1730 if (pwcsName==NULL)
1731 return STG_E_INVALIDPOINTER;
1732
1733 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1734 return STG_E_ACCESSDENIED;
1735
1736 foundPropertyIndexToDelete = findElement(
1737 This->ancestorStorage,
1738 This->rootPropertySetIndex,
1739 pwcsName,
1740 &propertyToDelete);
1741
1742 if ( foundPropertyIndexToDelete == DIRENTRY_NULL )
1743 {
1744 return STG_E_FILENOTFOUND;
1745 }
1746
1747 if ( propertyToDelete.propertyType == STGTY_STORAGE )
1748 {
1749 hr = deleteStorageProperty(
1750 This,
1751 foundPropertyIndexToDelete,
1752 propertyToDelete);
1753 }
1754 else if ( propertyToDelete.propertyType == STGTY_STREAM )
1755 {
1756 hr = deleteStreamProperty(
1757 This,
1758 foundPropertyIndexToDelete,
1759 propertyToDelete);
1760 }
1761
1762 if (hr!=S_OK)
1763 return hr;
1764
1765 /*
1766 * Adjust the property chain
1767 */
1768 hr = removeFromTree(
1769 This->ancestorStorage,
1770 This->rootPropertySetIndex,
1771 foundPropertyIndexToDelete);
1772
1773 /*
1774 * Invalidate the property
1775 */
1776 if (SUCCEEDED(hr))
1777 destroyDirEntry(This->ancestorStorage,
1778 foundPropertyIndexToDelete);
1779
1780 return hr;
1781 }
1782
1783
1784 /************************************************************************
1785 * StorageImpl_Stat (IStorage)
1786 *
1787 * This method will retrieve information about this storage object.
1788 *
1789 * See Windows documentation for more details on IStorage methods.
1790 */
1791 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1792 STATSTG* pstatstg, /* [out] */
1793 DWORD grfStatFlag) /* [in] */
1794 {
1795 StorageImpl* const This = (StorageImpl*)iface;
1796 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1797
1798 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1799 {
1800 CoTaskMemFree(pstatstg->pwcsName);
1801 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1802 strcpyW(pstatstg->pwcsName, This->pwcsName);
1803 }
1804
1805 return result;
1806 }
1807
1808 /******************************************************************************
1809 * Internal stream list handlers
1810 */
1811
1812 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1813 {
1814 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1815 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1816 }
1817
1818 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1819 {
1820 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1821 list_remove(&(strm->StrmListEntry));
1822 }
1823
1824 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1825 {
1826 struct list *cur, *cur2;
1827 StgStreamImpl *strm=NULL;
1828
1829 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1830 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1831 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1832 strm->parentStorage = NULL;
1833 list_remove(cur);
1834 }
1835 }
1836
1837
1838 /*********************************************************************
1839 *
1840 * Internal Method
1841 *
1842 * Perform the deletion of a complete storage node
1843 *
1844 */
1845 static HRESULT deleteStorageProperty(
1846 StorageBaseImpl *parentStorage,
1847 ULONG indexOfPropertyToDelete,
1848 DirEntry propertyToDelete)
1849 {
1850 IEnumSTATSTG *elements = 0;
1851 IStorage *childStorage = 0;
1852 STATSTG currentElement;
1853 HRESULT hr;
1854 HRESULT destroyHr = S_OK;
1855
1856 /*
1857 * Open the storage and enumerate it
1858 */
1859 hr = StorageBaseImpl_OpenStorage(
1860 (IStorage*)parentStorage,
1861 propertyToDelete.name,
1862 0,
1863 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1864 0,
1865 0,
1866 &childStorage);
1867
1868 if (hr != S_OK)
1869 {
1870 return hr;
1871 }
1872
1873 /*
1874 * Enumerate the elements
1875 */
1876 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1877
1878 do
1879 {
1880 /*
1881 * Obtain the next element
1882 */
1883 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1884 if (hr==S_OK)
1885 {
1886 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1887
1888 CoTaskMemFree(currentElement.pwcsName);
1889 }
1890
1891 /*
1892 * We need to Reset the enumeration every time because we delete elements
1893 * and the enumeration could be invalid
1894 */
1895 IEnumSTATSTG_Reset(elements);
1896
1897 } while ((hr == S_OK) && (destroyHr == S_OK));
1898
1899 IStorage_Release(childStorage);
1900 IEnumSTATSTG_Release(elements);
1901
1902 return destroyHr;
1903 }
1904
1905 /*********************************************************************
1906 *
1907 * Internal Method
1908 *
1909 * Perform the deletion of a stream node
1910 *
1911 */
1912 static HRESULT deleteStreamProperty(
1913 StorageBaseImpl *parentStorage,
1914 ULONG indexOfPropertyToDelete,
1915 DirEntry propertyToDelete)
1916 {
1917 IStream *pis;
1918 HRESULT hr;
1919 ULARGE_INTEGER size;
1920
1921 size.u.HighPart = 0;
1922 size.u.LowPart = 0;
1923
1924 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1925 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1926
1927 if (hr!=S_OK)
1928 {
1929 return(hr);
1930 }
1931
1932 /*
1933 * Zap the stream
1934 */
1935 hr = IStream_SetSize(pis, size);
1936
1937 if(hr != S_OK)
1938 {
1939 return hr;
1940 }
1941
1942 /*
1943 * Release the stream object.
1944 */
1945 IStream_Release(pis);
1946
1947 return S_OK;
1948 }
1949
1950 static void setPropertyLink(DirEntry *property, ULONG relation, ULONG new_target)
1951 {
1952 switch (relation)
1953 {
1954 case DIRENTRY_RELATION_PREVIOUS:
1955 property->leftChild = new_target;
1956 break;
1957 case DIRENTRY_RELATION_NEXT:
1958 property->rightChild = new_target;
1959 break;
1960 case DIRENTRY_RELATION_DIR:
1961 property->dirRootEntry = new_target;
1962 break;
1963 default:
1964 assert(0);
1965 }
1966 }
1967
1968 /*************************************************************************
1969 *
1970 * Internal Method
1971 *
1972 * This method removes a directory entry from its parent storage tree without
1973 * freeing any resources attached to it.
1974 */
1975 static HRESULT removeFromTree(
1976 StorageImpl *This,
1977 ULONG parentStorageIndex,
1978 ULONG deletedIndex)
1979 {
1980 HRESULT hr = S_OK;
1981 BOOL res = TRUE;
1982 DirEntry propertyToDelete;
1983 DirEntry parentProperty;
1984 ULONG parentPropertyId;
1985 ULONG typeOfRelation;
1986
1987 res = StorageImpl_ReadDirEntry(This, deletedIndex, &propertyToDelete);
1988
1989 /*
1990 * Find the property that links to the one we want to delete.
1991 */
1992 hr = findTreeParent(This, parentStorageIndex, propertyToDelete.name,
1993 &parentProperty, &parentPropertyId, &typeOfRelation);
1994
1995 if (hr != S_OK)
1996 return hr;
1997
1998 if (propertyToDelete.leftChild != DIRENTRY_NULL)
1999 {
2000 /*
2001 * Replace the deleted entry with its left child
2002 */
2003 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.leftChild);
2004
2005 res = StorageImpl_WriteDirEntry(
2006 This,
2007 parentPropertyId,
2008 &parentProperty);
2009 if(!res)
2010 {
2011 return E_FAIL;
2012 }
2013
2014 if (propertyToDelete.rightChild != DIRENTRY_NULL)
2015 {
2016 /*
2017 * We need to reinsert the right child somewhere. We already know it and
2018 * its children are greater than everything in the left tree, so we
2019 * insert it at the rightmost point in the left tree.
2020 */
2021 ULONG newRightChildParent = propertyToDelete.leftChild;
2022 DirEntry newRightChildParentProperty;
2023
2024 do
2025 {
2026 res = StorageImpl_ReadDirEntry(
2027 This,
2028 newRightChildParent,
2029 &newRightChildParentProperty);
2030 if (!res)
2031 {
2032 return E_FAIL;
2033 }
2034
2035 if (newRightChildParentProperty.rightChild != DIRENTRY_NULL)
2036 newRightChildParent = newRightChildParentProperty.rightChild;
2037 } while (newRightChildParentProperty.rightChild != DIRENTRY_NULL);
2038
2039 newRightChildParentProperty.rightChild = propertyToDelete.rightChild;
2040
2041 res = StorageImpl_WriteDirEntry(
2042 This,
2043 newRightChildParent,
2044 &newRightChildParentProperty);
2045 if (!res)
2046 {
2047 return E_FAIL;
2048 }
2049 }
2050 }
2051 else
2052 {
2053 /*
2054 * Replace the deleted entry with its right child
2055 */
2056 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.rightChild);
2057
2058 res = StorageImpl_WriteDirEntry(
2059 This,
2060 parentPropertyId,
2061 &parentProperty);
2062 if(!res)
2063 {
2064 return E_FAIL;
2065 }
2066 }
2067
2068 return hr;
2069 }
2070
2071
2072 /******************************************************************************
2073 * SetElementTimes (IStorage)
2074 */
2075 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2076 IStorage* iface,
2077 const OLECHAR *pwcsName,/* [string][in] */
2078 const FILETIME *pctime, /* [in] */
2079 const FILETIME *patime, /* [in] */
2080 const FILETIME *pmtime) /* [in] */
2081 {
2082 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2083 return S_OK;
2084 }
2085
2086 /******************************************************************************
2087 * SetStateBits (IStorage)
2088 */
2089 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2090 IStorage* iface,
2091 DWORD grfStateBits,/* [in] */
2092 DWORD grfMask) /* [in] */
2093 {
2094 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2095 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2096 return S_OK;
2097 }
2098
2099 /*
2100 * Virtual function table for the IStorage32Impl class.
2101 */
2102 static const IStorageVtbl Storage32Impl_Vtbl =
2103 {
2104 StorageBaseImpl_QueryInterface,
2105 StorageBaseImpl_AddRef,
2106 StorageBaseImpl_Release,
2107 StorageBaseImpl_CreateStream,
2108 StorageBaseImpl_OpenStream,
2109 StorageBaseImpl_CreateStorage,
2110 StorageBaseImpl_OpenStorage,
2111 StorageBaseImpl_CopyTo,
2112 StorageBaseImpl_MoveElementTo,
2113 StorageImpl_Commit,
2114 StorageImpl_Revert,
2115 StorageBaseImpl_EnumElements,
2116 StorageBaseImpl_DestroyElement,
2117 StorageBaseImpl_RenameElement,
2118 StorageBaseImpl_SetElementTimes,
2119 StorageBaseImpl_SetClass,
2120 StorageBaseImpl_SetStateBits,
2121 StorageImpl_Stat
2122 };
2123
2124 static HRESULT StorageImpl_Construct(
2125 StorageImpl* This,
2126 HANDLE hFile,
2127 LPCOLESTR pwcsName,
2128 ILockBytes* pLkbyt,
2129 DWORD openFlags,
2130 BOOL fileBased,
2131 BOOL create)
2132 {
2133 HRESULT hr = S_OK;
2134 DirEntry currentProperty;
2135 BOOL readSuccessful;
2136 ULONG currentPropertyIndex;
2137
2138 if ( FAILED( validateSTGM(openFlags) ))
2139 return STG_E_INVALIDFLAG;
2140
2141 memset(This, 0, sizeof(StorageImpl));
2142
2143 list_init(&This->base.strmHead);
2144
2145 This->base.lpVtbl = &Storage32Impl_Vtbl;
2146 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2147 This->base.v_destructor = StorageImpl_Destroy;
2148 This->base.openFlags = (openFlags & ~STGM_CREATE);
2149 This->create = create;
2150
2151 /*
2152 * This is the top-level storage so initialize the ancestor pointer
2153 * to this.
2154 */
2155 This->base.ancestorStorage = This;
2156
2157 This->hFile = hFile;
2158
2159 if(pwcsName) {
2160 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2161 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2162 if (!This->pwcsName)
2163 return STG_E_INSUFFICIENTMEMORY;
2164 strcpyW(This->pwcsName, pwcsName);
2165 }
2166
2167 /*
2168 * Initialize the big block cache.
2169 */
2170 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2171 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2172 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2173 pLkbyt,
2174 openFlags,
2175 This->bigBlockSize,
2176 fileBased);
2177
2178 if (This->bigBlockFile == 0)
2179 return E_FAIL;
2180
2181 if (create)
2182 {
2183 ULARGE_INTEGER size;
2184 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2185
2186 /*
2187 * Initialize all header variables:
2188 * - The big block depot consists of one block and it is at block 0
2189 * - The properties start at block 1
2190 * - There is no small block depot
2191 */
2192 memset( This->bigBlockDepotStart,
2193 BLOCK_UNUSED,
2194 sizeof(This->bigBlockDepotStart));
2195
2196 This->bigBlockDepotCount = 1;
2197 This->bigBlockDepotStart[0] = 0;
2198 This->rootStartBlock = 1;
2199 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2200 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2201 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2202 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2203 This->extBigBlockDepotCount = 0;
2204
2205 StorageImpl_SaveFileHeader(This);
2206
2207 /*
2208 * Add one block for the big block depot and one block for the properties
2209 */
2210 size.u.HighPart = 0;
2211 size.u.LowPart = This->bigBlockSize * 3;
2212 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2213
2214 /*
2215 * Initialize the big block depot
2216 */
2217 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2218 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2219 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2220 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2221 }
2222 else
2223 {
2224 /*
2225 * Load the header for the file.
2226 */
2227 hr = StorageImpl_LoadFileHeader(This);
2228
2229 if (FAILED(hr))
2230 {
2231 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2232
2233 return hr;
2234 }
2235 }
2236
2237 /*
2238 * There is no block depot cached yet.
2239 */
2240 This->indexBlockDepotCached = 0xFFFFFFFF;
2241
2242 /*
2243 * Start searching for free blocks with block 0.
2244 */
2245 This->prevFreeBlock = 0;
2246
2247 /*
2248 * Create the block chain abstractions.
2249 */
2250 if(!(This->rootBlockChain =
2251 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2252 return STG_E_READFAULT;
2253
2254 if(!(This->smallBlockDepotChain =
2255 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2256 DIRENTRY_NULL)))
2257 return STG_E_READFAULT;
2258
2259 /*
2260 * Write the root property (memory only)
2261 */
2262 if (create)
2263 {
2264 DirEntry rootProp;
2265 /*
2266 * Initialize the property chain
2267 */
2268 memset(&rootProp, 0, sizeof(rootProp));
2269 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2270 sizeof(rootProp.name)/sizeof(WCHAR) );
2271 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2272 rootProp.propertyType = STGTY_ROOT;
2273 rootProp.leftChild = DIRENTRY_NULL;
2274 rootProp.rightChild = DIRENTRY_NULL;
2275 rootProp.dirRootEntry = DIRENTRY_NULL;
2276 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2277 rootProp.size.u.HighPart = 0;
2278 rootProp.size.u.LowPart = 0;
2279
2280 StorageImpl_WriteDirEntry(This, 0, &rootProp);
2281 }
2282
2283 /*
2284 * Find the ID of the root in the property sets.
2285 */
2286 currentPropertyIndex = 0;
2287
2288 do
2289 {
2290 readSuccessful = StorageImpl_ReadDirEntry(
2291 This,
2292 currentPropertyIndex,
2293 ¤tProperty);
2294
2295 if (readSuccessful)
2296 {
2297 if ( (currentProperty.sizeOfNameString != 0 ) &&
2298 (currentProperty.propertyType == STGTY_ROOT) )
2299 {
2300 This->base.rootPropertySetIndex = currentPropertyIndex;
2301 }
2302 }
2303
2304 currentPropertyIndex++;
2305
2306 } while (readSuccessful && (This->base.rootPropertySetIndex == DIRENTRY_NULL) );
2307
2308 if (!readSuccessful)
2309 {
2310 /* TODO CLEANUP */
2311 return STG_E_READFAULT;
2312 }
2313
2314 /*
2315 * Create the block chain abstraction for the small block root chain.
2316 */
2317 if(!(This->smallBlockRootChain =
2318 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2319 return STG_E_READFAULT;
2320
2321 return hr;
2322 }
2323
2324 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2325 {
2326 StorageImpl *This = (StorageImpl*) iface;
2327 TRACE("(%p)\n", This);
2328
2329 StorageBaseImpl_DeleteAll(&This->base);
2330
2331 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2332
2333 BlockChainStream_Destroy(This->smallBlockRootChain);
2334 BlockChainStream_Destroy(This->rootBlockChain);
2335 BlockChainStream_Destroy(This->smallBlockDepotChain);
2336
2337 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2338 HeapFree(GetProcessHeap(), 0, This);
2339 }
2340
2341 /******************************************************************************
2342 * Storage32Impl_GetNextFreeBigBlock
2343 *
2344 * Returns the index of the next free big block.
2345 * If the big block depot is filled, this method will enlarge it.
2346 *
2347 */
2348 static ULONG StorageImpl_GetNextFreeBigBlock(
2349 StorageImpl* This)
2350 {
2351 ULONG depotBlockIndexPos;
2352 BYTE depotBuffer[BIG_BLOCK_SIZE];
2353 BOOL success;
2354 ULONG depotBlockOffset;
2355 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2356 ULONG nextBlockIndex = BLOCK_SPECIAL;
2357 int depotIndex = 0;
2358 ULONG freeBlock = BLOCK_UNUSED;
2359
2360 depotIndex = This->prevFreeBlock / blocksPerDepot;
2361 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2362
2363 /*
2364 * Scan the entire big block depot until we find a block marked free
2365 */
2366 while (nextBlockIndex != BLOCK_UNUSED)
2367 {
2368 if (depotIndex < COUNT_BBDEPOTINHEADER)
2369 {
2370 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2371
2372 /*
2373 * Grow the primary depot.
2374 */
2375 if (depotBlockIndexPos == BLOCK_UNUSED)
2376 {
2377 depotBlockIndexPos = depotIndex*blocksPerDepot;
2378
2379 /*
2380 * Add a block depot.
2381 */
2382 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2383 This->bigBlockDepotCount++;
2384 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2385
2386 /*
2387 * Flag it as a block depot.
2388 */
2389 StorageImpl_SetNextBlockInChain(This,
2390 depotBlockIndexPos,
2391 BLOCK_SPECIAL);
2392
2393 /* Save new header information.
2394 */
2395 StorageImpl_SaveFileHeader(This);
2396 }
2397 }
2398 else
2399 {
2400 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2401
2402 if (depotBlockIndexPos == BLOCK_UNUSED)
2403 {
2404 /*
2405 * Grow the extended depot.
2406 */
2407 ULONG extIndex = BLOCK_UNUSED;
2408 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2409 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2410
2411 if (extBlockOffset == 0)
2412 {
2413 /* We need an extended block.
2414 */
2415 extIndex = Storage32Impl_AddExtBlockDepot(This);
2416 This->extBigBlockDepotCount++;
2417 depotBlockIndexPos = extIndex + 1;
2418 }
2419 else
2420 depotBlockIndexPos = depotIndex * blocksPerDepot;
2421
2422 /*
2423 * Add a block depot and mark it in the extended block.
2424 */
2425 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2426 This->bigBlockDepotCount++;
2427 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2428
2429 /* Flag the block depot.
2430 */
2431 StorageImpl_SetNextBlockInChain(This,
2432 depotBlockIndexPos,
2433 BLOCK_SPECIAL);
2434
2435 /* If necessary, flag the extended depot block.
2436 */
2437 if (extIndex != BLOCK_UNUSED)
2438 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2439
2440 /* Save header information.
2441 */
2442 StorageImpl_SaveFileHeader(This);
2443 }
2444 }
2445
2446 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2447
2448 if (success)
2449 {
2450 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2451 ( nextBlockIndex != BLOCK_UNUSED))
2452 {
2453 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2454
2455 if (nextBlockIndex == BLOCK_UNUSED)
2456 {
2457 freeBlock = (depotIndex * blocksPerDepot) +
2458 (depotBlockOffset/sizeof(ULONG));
2459 }
2460
2461 depotBlockOffset += sizeof(ULONG);
2462 }
2463 }
2464
2465 depotIndex++;
2466 depotBlockOffset = 0;
2467 }
2468
2469 /*
2470 * make sure that the block physically exists before using it
2471 */
2472 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2473
2474 This->prevFreeBlock = freeBlock;
2475
2476 return freeBlock;
2477 }
2478
2479 /******************************************************************************
2480 * Storage32Impl_AddBlockDepot
2481 *
2482 * This will create a depot block, essentially it is a block initialized
2483 * to BLOCK_UNUSEDs.
2484 */
2485 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2486 {
2487 BYTE blockBuffer[BIG_BLOCK_SIZE];
2488
2489 /*
2490 * Initialize blocks as free
2491 */
2492 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2493 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2494 }
2495
2496 /******************************************************************************
2497 * Storage32Impl_GetExtDepotBlock
2498 *
2499 * Returns the index of the block that corresponds to the specified depot
2500 * index. This method is only for depot indexes equal or greater than
2501 * COUNT_BBDEPOTINHEADER.
2502 */
2503 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2504 {
2505 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2506 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2507 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2508 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2509 ULONG blockIndex = BLOCK_UNUSED;
2510 ULONG extBlockIndex = This->extBigBlockDepotStart;
2511
2512 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2513
2514 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2515 return BLOCK_UNUSED;
2516
2517 while (extBlockCount > 0)
2518 {
2519 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2520 extBlockCount--;
2521 }
2522
2523 if (extBlockIndex != BLOCK_UNUSED)
2524 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2525 extBlockOffset * sizeof(ULONG), &blockIndex);
2526
2527 return blockIndex;
2528 }
2529
2530 /******************************************************************************
2531 * Storage32Impl_SetExtDepotBlock
2532 *
2533 * Associates the specified block index to the specified depot index.
2534 * This method is only for depot indexes equal or greater than
2535 * COUNT_BBDEPOTINHEADER.
2536 */
2537 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2538 {
2539 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2540 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2541 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2542 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2543 ULONG extBlockIndex = This->extBigBlockDepotStart;
2544
2545 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2546
2547 while (extBlockCount > 0)
2548 {
2549 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2550 extBlockCount--;
2551 }
2552
2553 if (extBlockIndex != BLOCK_UNUSED)
2554 {
2555 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2556 extBlockOffset * sizeof(ULONG),
2557 blockIndex);
2558 }
2559 }
2560
2561 /******************************************************************************
2562 * Storage32Impl_AddExtBlockDepot
2563 *
2564 * Creates an extended depot block.
2565 */
2566 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2567 {
2568 ULONG numExtBlocks = This->extBigBlockDepotCount;
2569 ULONG nextExtBlock = This->extBigBlockDepotStart;
2570 BYTE depotBuffer[BIG_BLOCK_SIZE];
2571 ULONG index = BLOCK_UNUSED;
2572 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2573 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2574 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2575
2576 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2577 blocksPerDepotBlock;
2578
2579 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2580 {
2581 /*
2582 * The first extended block.
2583 */
2584 This->extBigBlockDepotStart = index;
2585 }
2586 else
2587 {
2588 unsigned int i;
2589 /*
2590 * Follow the chain to the last one.
2591 */
2592 for (i = 0; i < (numExtBlocks - 1); i++)
2593 {
2594 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2595 }
2596
2597 /*
2598 * Add the new extended block to the chain.
2599 */
2600 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2601 index);
2602 }
2603
2604 /*
2605 * Initialize this block.
2606 */
2607 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2608 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2609
2610 return index;
2611 }
2612
2613 /******************************************************************************
2614 * Storage32Impl_FreeBigBlock
2615 *
2616 * This method will flag the specified block as free in the big block depot.
2617 */
2618 static void StorageImpl_FreeBigBlock(
2619 StorageImpl* This,
2620 ULONG blockIndex)
2621 {
2622 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2623
2624 if (blockIndex < This->prevFreeBlock)
2625 This->prevFreeBlock = blockIndex;
2626 }
2627
2628 /************************************************************************
2629 * Storage32Impl_GetNextBlockInChain
2630 *
2631 * This method will retrieve the block index of the next big block in
2632 * in the chain.
2633 *
2634 * Params: This - Pointer to the Storage object.
2635 * blockIndex - Index of the block to retrieve the chain
2636 * for.
2637 * nextBlockIndex - receives the return value.
2638 *
2639 * Returns: This method returns the index of the next block in the chain.
2640 * It will return the constants:
2641 * BLOCK_SPECIAL - If the block given was not part of a
2642 * chain.
2643 * BLOCK_END_OF_CHAIN - If the block given was the last in
2644 * a chain.
2645 * BLOCK_UNUSED - If the block given was not past of a chain
2646 * and is available.
2647 * BLOCK_EXTBBDEPOT - This block is part of the extended
2648 * big block depot.
2649 *
2650 * See Windows documentation for more details on IStorage methods.
2651 */
2652 static HRESULT StorageImpl_GetNextBlockInChain(
2653 StorageImpl* This,
2654 ULONG blockIndex,
2655 ULONG* nextBlockIndex)
2656 {
2657 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2658 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2659 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2660 BYTE depotBuffer[BIG_BLOCK_SIZE];
2661 BOOL success;
2662 ULONG depotBlockIndexPos;
2663 int index;
2664
2665 *nextBlockIndex = BLOCK_SPECIAL;
2666
2667 if(depotBlockCount >= This->bigBlockDepotCount)
2668 {
2669 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2670 This->bigBlockDepotCount);
2671 return STG_E_READFAULT;
2672 }
2673
2674 /*
2675 * Cache the currently accessed depot block.
2676 */
2677 if (depotBlockCount != This->indexBlockDepotCached)
2678 {
2679 This->indexBlockDepotCached = depotBlockCount;
2680
2681 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2682 {
2683 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2684 }
2685 else
2686 {
2687 /*
2688 * We have to look in the extended depot.
2689 */
2690 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2691 }
2692
2693 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2694
2695 if (!success)
2696 return STG_E_READFAULT;
2697
2698 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2699 {
2700 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2701 This->blockDepotCached[index] = *nextBlockIndex;
2702 }
2703 }
2704
2705 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2706
2707 return S_OK;
2708 }
2709
2710 /******************************************************************************
2711 * Storage32Impl_GetNextExtendedBlock
2712 *
2713 * Given an extended block this method will return the next extended block.
2714 *
2715 * NOTES:
2716 * The last ULONG of an extended block is the block index of the next
2717 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2718 * depot.
2719 *
2720 * Return values:
2721 * - The index of the next extended block
2722 * - BLOCK_UNUSED: there is no next extended block.
2723 * - Any other return values denotes failure.
2724 */
2725 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2726 {
2727 ULONG nextBlockIndex = BLOCK_SPECIAL;
2728 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2729
2730 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2731 &nextBlockIndex);
2732
2733 return nextBlockIndex;
2734 }
2735
2736 /******************************************************************************
2737 * Storage32Impl_SetNextBlockInChain
2738 *
2739 * This method will write the index of the specified block's next block
2740 * in the big block depot.
2741 *
2742 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2743 * do the following
2744 *
2745 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2746 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2747 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2748 *
2749 */
2750 static void StorageImpl_SetNextBlockInChain(
2751 StorageImpl* This,
2752 ULONG blockIndex,
2753 ULONG nextBlock)
2754 {
2755 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2756 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2757 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2758 ULONG depotBlockIndexPos;
2759
2760 assert(depotBlockCount < This->bigBlockDepotCount);
2761 assert(blockIndex != nextBlock);
2762
2763 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2764 {
2765 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2766 }
2767 else
2768 {
2769 /*
2770 * We have to look in the extended depot.
2771 */
2772 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2773 }
2774
2775 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2776 nextBlock);
2777 /*
2778 * Update the cached block depot, if necessary.
2779 */
2780 if (depotBlockCount == This->indexBlockDepotCached)
2781 {
2782 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2783 }
2784 }
2785
2786 /******************************************************************************
2787 * Storage32Impl_LoadFileHeader
2788 *
2789 * This method will read in the file header, i.e. big block index -1.
2790 */
2791 static HRESULT StorageImpl_LoadFileHeader(
2792 StorageImpl* This)
2793 {
2794 HRESULT hr = STG_E_FILENOTFOUND;
2795 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2796 BOOL success;
2797 int index;
2798
2799 TRACE("\n");
2800 /*
2801 * Get a pointer to the big block of data containing the header.
2802 */
2803 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2804
2805 /*
2806 * Extract the information from the header.
2807 */
2808 if (success)
2809 {
2810 /*
2811 * Check for the "magic number" signature and return an error if it is not
2812 * found.
2813 */
2814 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2815 {
2816 return STG_E_OLDFORMAT;
2817 }
2818
2819 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2820 {
2821 return STG_E_INVALIDHEADER;
2822 }
2823
2824 StorageUtl_ReadWord(
2825 headerBigBlock,
2826 OFFSET_BIGBLOCKSIZEBITS,
2827 &This->bigBlockSizeBits);
2828
2829 StorageUtl_ReadWord(
2830 headerBigBlock,
2831 OFFSET_SMALLBLOCKSIZEBITS,
2832 &This->smallBlockSizeBits);
2833
2834 StorageUtl_ReadDWord(
2835 headerBigBlock,
2836 OFFSET_BBDEPOTCOUNT,
2837 &This->bigBlockDepotCount);
2838
2839 StorageUtl_ReadDWord(
2840 headerBigBlock,
2841 OFFSET_ROOTSTARTBLOCK,
2842 &This->rootStartBlock);
2843
2844 StorageUtl_ReadDWord(
2845 headerBigBlock,
2846 OFFSET_SBDEPOTSTART,
2847 &This->smallBlockDepotStart);
2848
2849 StorageUtl_ReadDWord(
2850 headerBigBlock,
2851 OFFSET_EXTBBDEPOTSTART,
2852 &This->extBigBlockDepotStart);
2853
2854 StorageUtl_ReadDWord(
2855 headerBigBlock,
2856 OFFSET_EXTBBDEPOTCOUNT,
2857 &This->extBigBlockDepotCount);
2858
2859 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2860 {
2861 StorageUtl_ReadDWord(
2862 headerBigBlock,
2863 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2864 &(This->bigBlockDepotStart[index]));
2865 }
2866
2867 /*
2868 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2869 */
2870 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2871 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2872
2873 /*
2874 * Right now, the code is making some assumptions about the size of the
2875 * blocks, just make sure they are what we're expecting.
2876 */
2877 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2878 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2879 {
2880 WARN("Broken OLE storage file\n");
2881 hr = STG_E_INVALIDHEADER;
2882 }
2883 else
2884 hr = S_OK;
2885 }
2886
2887 return hr;
2888 }
2889
2890 /******************************************************************************
2891 * Storage32Impl_SaveFileHeader
2892 *
2893 * This method will save to the file the header, i.e. big block -1.
2894 */
2895 static void StorageImpl_SaveFileHeader(
2896 StorageImpl* This)
2897 {
2898 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2899 int index;
2900 BOOL success;
2901
2902 /*
2903 * Get a pointer to the big block of data containing the header.
2904 */
2905 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2906
2907 /*
2908 * If the block read failed, the file is probably new.
2909 */
2910 if (!success)
2911 {
2912 /*
2913 * Initialize for all unknown fields.
2914 */
2915 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2916
2917 /*
2918 * Initialize the magic number.
2919 */
2920 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2921
2922 /*
2923 * And a bunch of things we don't know what they mean
2924 */
2925 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2926 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2927 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2928 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2929 }
2930
2931 /*
2932 * Write the information to the header.
2933 */
2934 StorageUtl_WriteWord(
2935 headerBigBlock,
2936 OFFSET_BIGBLOCKSIZEBITS,
2937 This->bigBlockSizeBits);
2938
2939 StorageUtl_WriteWord(
2940 headerBigBlock,
2941 OFFSET_SMALLBLOCKSIZEBITS,
2942 This->smallBlockSizeBits);
2943
2944 StorageUtl_WriteDWord(
2945 headerBigBlock,
2946 OFFSET_BBDEPOTCOUNT,
2947 This->bigBlockDepotCount);
2948
2949 StorageUtl_WriteDWord(
2950 headerBigBlock,
2951 OFFSET_ROOTSTARTBLOCK,
2952 This->rootStartBlock);
2953
2954 StorageUtl_WriteDWord(
2955 headerBigBlock,
2956 OFFSET_SBDEPOTSTART,
2957 This->smallBlockDepotStart);
2958
2959 StorageUtl_WriteDWord(
2960 headerBigBlock,
2961 OFFSET_SBDEPOTCOUNT,
2962 This->smallBlockDepotChain ?
2963 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
2964
2965 StorageUtl_WriteDWord(
2966 headerBigBlock,
2967 OFFSET_EXTBBDEPOTSTART,
2968 This->extBigBlockDepotStart);
2969
2970 StorageUtl_WriteDWord(
2971 headerBigBlock,
2972 OFFSET_EXTBBDEPOTCOUNT,
2973 This->extBigBlockDepotCount);
2974
2975 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2976 {
2977 StorageUtl_WriteDWord(
2978 headerBigBlock,
2979 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2980 (This->bigBlockDepotStart[index]));
2981 }
2982
2983 /*
2984 * Write the big block back to the file.
2985 */
2986 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2987 }
2988
2989 /******************************************************************************
2990 * StorageImpl_ReadRawDirEntry
2991 *
2992 * This method will read the raw data from a directory entry in the file.
2993 *
2994 * buffer must be PROPSET_BLOCK_SIZE bytes long.
2995 */
2996 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
2997 {
2998 ULARGE_INTEGER offset;
2999 HRESULT hr;
3000 ULONG bytesRead;
3001
3002 offset.u.HighPart = 0;
3003 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3004
3005 hr = BlockChainStream_ReadAt(
3006 This->rootBlockChain,
3007 offset,
3008 RAW_DIRENTRY_SIZE,
3009 buffer,
3010 &bytesRead);
3011
3012 return hr;
3013 }
3014
3015 /******************************************************************************
3016 * StorageImpl_WriteRawDirEntry
3017 *
3018 * This method will write the raw data from a directory entry in the file.
3019 *
3020 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3021 */
3022 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3023 {
3024 ULARGE_INTEGER offset;
3025 HRESULT hr;
3026 ULONG bytesRead;
3027
3028 offset.u.HighPart = 0;
3029 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3030
3031 hr = BlockChainStream_WriteAt(
3032 This->rootBlockChain,
3033 offset,
3034 RAW_DIRENTRY_SIZE,
3035 buffer,
3036 &bytesRead);
3037
3038 return hr;
3039 }
3040
3041 /******************************************************************************
3042 * UpdateRawDirEntry
3043 *
3044 * Update raw directory entry data from the fields in newData.
3045 *
3046 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3047 */
3048 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3049 {
3050 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3051
3052 memcpy(
3053 buffer + OFFSET_PS_NAME,
3054 newData->name,
3055 DIRENTRY_NAME_BUFFER_LEN );
3056
3057 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->propertyType, 1);
3058
3059 StorageUtl_WriteWord(
3060 buffer,
3061 OFFSET_PS_NAMELENGTH,
3062 newData->sizeOfNameString);
3063
3064 StorageUtl_WriteDWord(
3065 buffer,
3066 OFFSET_PS_LEFTCHILD,
3067 newData->leftChild);
3068
3069 StorageUtl_WriteDWord(
3070 buffer,
3071 OFFSET_PS_RIGHTCHILD,
3072 newData->rightChild);
3073
3074 StorageUtl_WriteDWord(
3075 buffer,
3076 OFFSET_PS_DIRROOT,
3077 newData->dirRootEntry);
3078
3079 StorageUtl_WriteGUID(
3080 buffer,
3081 OFFSET_PS_GUID,
3082 &newData->propertyUniqueID);
3083
3084 StorageUtl_WriteDWord(
3085 buffer,
3086 OFFSET_PS_CTIMELOW,
3087 newData->ctime.dwLowDateTime);
3088
3089 StorageUtl_WriteDWord(
3090 buffer,
3091 OFFSET_PS_CTIMEHIGH,
3092 newData->ctime.dwHighDateTime);
3093
3094 StorageUtl_WriteDWord(
3095 buffer,
3096 OFFSET_PS_MTIMELOW,
3097 newData->mtime.dwLowDateTime);
3098
3099 StorageUtl_WriteDWord(
3100 buffer,
3101 OFFSET_PS_MTIMEHIGH,
3102 newData->ctime.dwHighDateTime);
3103
3104 StorageUtl_WriteDWord(
3105 buffer,
3106 OFFSET_PS_STARTBLOCK,
3107 newData->startingBlock);
3108
3109 StorageUtl_WriteDWord(
3110 buffer,
3111 OFFSET_PS_SIZE,
3112 newData->size.u.LowPart);
3113 }
3114
3115 /******************************************************************************
3116 * Storage32Impl_ReadProperty
3117 *
3118 * This method will read the specified property from the property chain.
3119 */
3120 BOOL StorageImpl_ReadDirEntry(
3121 StorageImpl* This,
3122 ULONG index,
3123 DirEntry* buffer)
3124 {
3125 BYTE currentProperty[RAW_DIRENTRY_SIZE];
3126 HRESULT readRes;
3127
3128 readRes = StorageImpl_ReadRawDirEntry(This, index, currentProperty);
3129
3130 if (SUCCEEDED(readRes))
3131 {
3132 /* replace the name of root entry (often "Root Entry") by the file name */
3133 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3134 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3135
3136 memset(buffer->name, 0, sizeof(buffer->name));
3137 memcpy(
3138 buffer->name,
3139 propName,
3140 DIRENTRY_NAME_BUFFER_LEN );
3141 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3142
3143 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_STGTYPE, 1);
3144
3145 StorageUtl_ReadWord(
3146 currentProperty,
3147 OFFSET_PS_NAMELENGTH,
3148 &buffer->sizeOfNameString);
3149
3150 StorageUtl_ReadDWord(
3151 currentProperty,
3152 OFFSET_PS_LEFTCHILD,
3153 &buffer->leftChild);
3154
3155 StorageUtl_ReadDWord(
3156 currentProperty,
3157 OFFSET_PS_RIGHTCHILD,
3158 &buffer->rightChild);
3159
3160 StorageUtl_ReadDWord(
3161 currentProperty,
3162 OFFSET_PS_DIRROOT,
3163 &buffer->dirRootEntry);
3164
3165 StorageUtl_ReadGUID(
3166 currentProperty,
3167 OFFSET_PS_GUID,
3168 &buffer->propertyUniqueID);
3169
3170 StorageUtl_ReadDWord(
3171 currentProperty,
3172 OFFSET_PS_CTIMELOW,
3173 &buffer->ctime.dwLowDateTime);
3174
3175 StorageUtl_ReadDWord(
3176 currentProperty,
3177 OFFSET_PS_CTIMEHIGH,
3178 &buffer->ctime.dwHighDateTime);
3179
3180 StorageUtl_ReadDWord(
3181 currentProperty,
3182 OFFSET_PS_MTIMELOW,
3183 &buffer->mtime.dwLowDateTime);
3184
3185 StorageUtl_ReadDWord(
3186 currentProperty,
3187 OFFSET_PS_MTIMEHIGH,
3188 &buffer->mtime.dwHighDateTime);
3189
3190 StorageUtl_ReadDWord(
3191 currentProperty,
3192 OFFSET_PS_STARTBLOCK,
3193 &buffer->startingBlock);
3194
3195 StorageUtl_ReadDWord(
3196 currentProperty,
3197 OFFSET_PS_SIZE,
3198 &buffer->size.u.LowPart);
3199
3200 buffer->size.u.HighPart = 0;
3201 }
3202
3203 return SUCCEEDED(readRes) ? TRUE : FALSE;
3204 }
3205
3206 /*********************************************************************
3207 * Write the specified property into the property chain
3208 */
3209 BOOL StorageImpl_WriteDirEntry(
3210 StorageImpl* This,
3211 ULONG index,
3212 const DirEntry* buffer)
3213 {
3214 BYTE currentProperty[RAW_DIRENTRY_SIZE];
3215 HRESULT writeRes;
3216
3217 UpdateRawDirEntry(currentProperty, buffer);
3218
3219 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentProperty);
3220 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3221 }
3222
3223 static BOOL StorageImpl_ReadBigBlock(
3224 StorageImpl* This,
3225 ULONG blockIndex,
3226 void* buffer)
3227 {
3228 ULARGE_INTEGER ulOffset;
3229 DWORD read;
3230
3231 ulOffset.u.HighPart = 0;
3232 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3233
3234 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3235 return (read == This->bigBlockSize);
3236 }
3237
3238 static BOOL StorageImpl_ReadDWordFromBigBlock(
3239 StorageImpl* This,
3240 ULONG blockIndex,
3241 ULONG offset,
3242 DWORD* value)
3243 {
3244 ULARGE_INTEGER ulOffset;
3245 DWORD read;
3246 DWORD tmp;
3247
3248 ulOffset.u.HighPart = 0;
3249 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3250 ulOffset.u.LowPart += offset;
3251
3252 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3253 *value = lendian32toh(tmp);
3254 return (read == sizeof(DWORD));
3255 }
3256
3257 static BOOL StorageImpl_WriteBigBlock(
3258 StorageImpl* This,
3259 ULONG blockIndex,
3260 const void* buffer)
3261 {
3262 ULARGE_INTEGER ulOffset;
3263 DWORD wrote;
3264
3265 ulOffset.u.HighPart = 0;
3266 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3267
3268 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3269 return (wrote == This->bigBlockSize);
3270 }
3271
3272 static BOOL StorageImpl_WriteDWordToBigBlock(
3273 StorageImpl* This,
3274 ULONG blockIndex,
3275 ULONG offset,
3276 DWORD value)
3277 {
3278 ULARGE_INTEGER ulOffset;
3279 DWORD wrote;
3280
3281 ulOffset.u.HighPart = 0;
3282 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3283 ulOffset.u.LowPart += offset;
3284
3285 value = htole32(value);
3286 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3287 return (wrote == sizeof(DWORD));
3288 }
3289
3290 /******************************************************************************
3291 * Storage32Impl_SmallBlocksToBigBlocks
3292 *
3293 * This method will convert a small block chain to a big block chain.
3294 * The small block chain will be destroyed.
3295 */
3296 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3297 StorageImpl* This,
3298 SmallBlockChainStream** ppsbChain)
3299 {
3300 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3301 ULARGE_INTEGER size, offset;
3302 ULONG cbRead, cbWritten;
3303 ULARGE_INTEGER cbTotalRead;
3304 ULONG propertyIndex;
3305 HRESULT resWrite = S_OK;
3306 HRESULT resRead;
3307 DirEntry chainProperty;
3308 BYTE *buffer;
3309 BlockChainStream *bbTempChain = NULL;
3310 BlockChainStream *bigBlockChain = NULL;
3311
3312 /*
3313 * Create a temporary big block chain that doesn't have
3314 * an associated property. This temporary chain will be
3315 * used to copy data from small blocks to big blocks.
3316 */
3317 bbTempChain = BlockChainStream_Construct(This,
3318 &bbHeadOfChain,
3319 DIRENTRY_NULL);
3320 if(!bbTempChain) return NULL;
3321 /*
3322 * Grow the big block chain.
3323 */
3324 size = SmallBlockChainStream_GetSize(*ppsbChain);
3325 BlockChainStream_SetSize(bbTempChain, size);
3326
3327 /*
3328 * Copy the contents of the small block chain to the big block chain
3329 * by small block size increments.
3330 */
3331 offset.u.LowPart = 0;
3332 offset.u.HighPart = 0;
3333 cbTotalRead.QuadPart = 0;
3334
3335 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3336 do
3337 {
3338 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3339 offset,
3340 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3341 buffer,
3342 &cbRead);
3343 if (FAILED(resRead))
3344 break;
3345
3346 if (cbRead > 0)
3347 {
3348 cbTotalRead.QuadPart += cbRead;
3349
3350 resWrite = BlockChainStream_WriteAt(bbTempChain,
3351 offset,
3352 cbRead,
3353 buffer,
3354 &cbWritten);
3355
3356 if (FAILED(resWrite))
3357 break;
3358
3359 offset.u.LowPart += cbRead;
3360 }
3361 } while (cbTotalRead.QuadPart < size.QuadPart);
3362 HeapFree(GetProcessHeap(),0,buffer);
3363
3364 size.u.HighPart = 0;
3365 size.u.LowPart = 0;
3366
3367 if (FAILED(resRead) || FAILED(resWrite))
3368 {
3369 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3370 BlockChainStream_SetSize(bbTempChain, size);
3371 BlockChainStream_Destroy(bbTempChain);
3372 return NULL;
3373 }
3374
3375 /*
3376 * Destroy the small block chain.
3377 */
3378 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3379 SmallBlockChainStream_SetSize(*ppsbChain, size);
3380 SmallBlockChainStream_Destroy(*ppsbChain);
3381 *ppsbChain = 0;
3382
3383 /*
3384 * Change the property information. This chain is now a big block chain
3385 * and it doesn't reside in the small blocks chain anymore.
3386 */
3387 StorageImpl_ReadDirEntry(This, propertyIndex, &chainProperty);
3388
3389 chainProperty.startingBlock = bbHeadOfChain;
3390
3391 StorageImpl_WriteDirEntry(This, propertyIndex, &chainProperty);
3392
3393 /*
3394 * Destroy the temporary propertyless big block chain.
3395 * Create a new big block chain associated with this property.
3396 */
3397 BlockChainStream_Destroy(bbTempChain);
3398 bigBlockChain = BlockChainStream_Construct(This,
3399 NULL,
3400 propertyIndex);
3401
3402 return bigBlockChain;
3403 }
3404
3405 /******************************************************************************
3406 * Storage32Impl_BigBlocksToSmallBlocks
3407 *
3408 * This method will convert a big block chain to a small block chain.
3409 * The big block chain will be destroyed on success.
3410 */
3411 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3412 StorageImpl* This,
3413 BlockChainStream** ppbbChain)
3414 {
3415 ULARGE_INTEGER size, offset, cbTotalRead;
3416 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3417 HRESULT resWrite = S_OK, resRead;
3418 DirEntry chainProperty;
3419 BYTE* buffer;
3420 SmallBlockChainStream* sbTempChain;
3421
3422 TRACE("%p %p\n", This, ppbbChain);
3423
3424 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3425 DIRENTRY_NULL);
3426
3427 if(!sbTempChain)
3428 return NULL;
3429
3430 size = BlockChainStream_GetSize(*ppbbChain);
3431 SmallBlockChainStream_SetSize(sbTempChain, size);
3432
3433 offset.u.HighPart = 0;
3434 offset.u.LowPart = 0;
3435 cbTotalRead.QuadPart = 0;
3436 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3437 do
3438 {
3439 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3440 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3441 buffer, &cbRead);
3442
3443 if(FAILED(resRead))
3444 break;
3445
3446 if(cbRead > 0)
3447 {
3448 cbTotalRead.QuadPart += cbRead;
3449
3450 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3451 cbRead, buffer, &cbWritten);
3452
3453 if(FAILED(resWrite))
3454 break;
3455
3456 offset.u.LowPart += cbRead;
3457 }
3458 }while(cbTotalRead.QuadPart < size.QuadPart);
3459 HeapFree(GetProcessHeap(), 0, buffer);
3460
3461 size.u.HighPart = 0;
3462 size.u.LowPart = 0;
3463
3464 if(FAILED(resRead) || FAILED(resWrite))
3465 {
3466 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3467 SmallBlockChainStream_SetSize(sbTempChain, size);
3468 SmallBlockChainStream_Destroy(sbTempChain);
3469 return NULL;
3470 }
3471
3472 /* destroy the original big block chain */
3473 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3474 BlockChainStream_SetSize(*ppbbChain, size);
3475 BlockChainStream_Destroy(*ppbbChain);
3476 *ppbbChain = NULL;
3477
3478 StorageImpl_ReadDirEntry(This, propertyIndex, &chainProperty);
3479 chainProperty.startingBlock = sbHeadOfChain;
3480 StorageImpl_WriteDirEntry(This, propertyIndex, &chainProperty);
3481
3482 SmallBlockChainStream_Destroy(sbTempChain);
3483 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3484 }
3485
3486 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3487 {
3488 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3489
3490 HeapFree(GetProcessHeap(), 0, This);
3491 }
3492
3493 /******************************************************************************
3494 **
3495 ** Storage32InternalImpl_Commit
3496 **
3497 */
3498 static HRESULT WINAPI StorageInternalImpl_Commit(
3499 IStorage* iface,
3500 DWORD grfCommitFlags) /* [in] */
3501 {
3502 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3503 return S_OK;
3504 }
3505
3506 /******************************************************************************
3507 **
3508 ** Storage32InternalImpl_Revert
3509 **
3510 */
3511 static HRESULT WINAPI StorageInternalImpl_Revert(
3512 IStorage* iface)
3513 {
3514 FIXME("(%p): stub\n", iface);
3515 return S_OK;
3516 }
3517
3518 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3519 {
3520 IStorage_Release((IStorage*)This->parentStorage);
3521 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3522 HeapFree(GetProcessHeap(), 0, This);
3523 }
3524
3525 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3526 IEnumSTATSTG* iface,
3527 REFIID riid,
3528 void** ppvObject)
3529 {
3530 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3531
3532 if (ppvObject==0)
3533 return E_INVALIDARG;
3534
3535 *ppvObject = 0;
3536
3537 if (IsEqualGUID(&IID_IUnknown, riid) ||
3538 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3539 {
3540 *ppvObject = This;
3541 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3542 return S_OK;
3543 }
3544
3545 return E_NOINTERFACE;
3546 }
3547
3548 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3549 IEnumSTATSTG* iface)
3550 {
3551 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3552 return InterlockedIncrement(&This->ref);
3553 }
3554
3555 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3556 IEnumSTATSTG* iface)
3557 {
3558 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3559
3560 ULONG newRef;
3561
3562 newRef = InterlockedDecrement(&This->ref);
3563
3564 if (newRef==0)
3565 {
3566 IEnumSTATSTGImpl_Destroy(This);
3567 }
3568
3569 return newRef;
3570 }
3571
3572 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3573 IEnumSTATSTG* iface,
3574 ULONG celt,
3575 STATSTG* rgelt,
3576 ULONG* pceltFetched)
3577 {
3578 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3579
3580 DirEntry currentProperty;
3581 STATSTG* currentReturnStruct = rgelt;
3582 ULONG objectFetched = 0;
3583 ULONG currentSearchNode;
3584
3585 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3586 return E_INVALIDARG;
3587
3588 /*
3589 * To avoid the special case, get another pointer to a ULONG value if
3590 * the caller didn't supply one.
3591 */
3592 if (pceltFetched==0)
3593 pceltFetched = &objectFetched;
3594
3595 /*
3596 * Start the iteration, we will iterate until we hit the end of the
3597 * linked list or until we hit the number of items to iterate through
3598 */
3599 *pceltFetched = 0;
3600
3601 /*
3602 * Start with the node at the top of the stack.
3603 */
3604 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3605
3606 while ( ( *pceltFetched < celt) &&
3607 ( currentSearchNode!=DIRENTRY_NULL) )
3608 {
3609 /*
3610 * Remove the top node from the stack
3611 */
3612 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3613
3614 /*
3615 * Read the property from the storage.
3616 */
3617 StorageImpl_ReadDirEntry(This->parentStorage,
3618 currentSearchNode,
3619 ¤tProperty);
3620
3621 /*
3622 * Copy the information to the return buffer.
3623 */
3624 StorageUtl_CopyDirEntryToSTATSTG(currentReturnStruct,
3625 ¤tProperty,
3626 STATFLAG_DEFAULT);
3627
3628 /*
3629 * Step to the next item in the iteration
3630 */
3631 (*pceltFetched)++;
3632 currentReturnStruct++;
3633
3634 /*
3635 * Push the next search node in the search stack.
3636 */
3637 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3638
3639 /*
3640 * continue the iteration.
3641 */
3642 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3643 }
3644
3645 if (*pceltFetched == celt)
3646 return S_OK;
3647
3648 return S_FALSE;
3649 }
3650
3651
3652 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3653 IEnumSTATSTG* iface,
3654 ULONG celt)
3655 {
3656 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3657
3658 DirEntry currentProperty;
3659 ULONG objectFetched = 0;
3660 ULONG currentSearchNode;
3661
3662 /*
3663 * Start with the node at the top of the stack.
3664 */
3665 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3666
3667 while ( (objectFetched < celt) &&
3668 (currentSearchNode!=DIRENTRY_NULL) )
3669 {
3670 /*
3671 * Remove the top node from the stack
3672 */
3673 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3674
3675 /*
3676 * Read the property from the storage.
3677 */
3678 StorageImpl_ReadDirEntry(This->parentStorage,
3679 currentSearchNode,
3680 ¤tProperty);
3681
3682 /*
3683 * Step to the next item in the iteration
3684 */
3685 objectFetched++;
3686
3687 /*
3688 * Push the next search node in the search stack.
3689 */
3690 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3691
3692 /*
3693 * continue the iteration.
3694 */
3695 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3696 }
3697
3698 if (objectFetched == celt)
3699 return S_OK;
3700
3701 return S_FALSE;
3702 }
3703
3704 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3705 IEnumSTATSTG* iface)
3706 {
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3708
3709 DirEntry rootProperty;
3710 BOOL readSuccessful;
3711
3712 /*
3713 * Re-initialize the search stack to an empty stack
3714 */
3715 This->stackSize = 0;
3716
3717 /*
3718 * Read the root property from the storage.
3719 */
3720 readSuccessful = StorageImpl_ReadDirEntry(
3721 This->parentStorage,
3722 This->firstPropertyNode,
3723 &rootProperty);
3724
3725 if (readSuccessful)
3726 {
3727 assert(rootProperty.sizeOfNameString!=0);
3728
3729 /*
3730 * Push the search node in the search stack.
3731 */
3732 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirRootEntry);
3733 }
3734
3735 return S_OK;
3736 }
3737
3738 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3739 IEnumSTATSTG* iface,
3740 IEnumSTATSTG** ppenum)
3741 {
3742 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3743
3744 IEnumSTATSTGImpl* newClone;
3745
3746 /*
3747 * Perform a sanity check on the parameters.
3748 */
3749 if (ppenum==0)
3750 return E_INVALIDARG;
3751
3752 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3753 This->firstPropertyNode);
3754
3755
3756 /*
3757 * The new clone enumeration must point to the same current node as
3758 * the ole one.
3759 */
3760 newClone->stackSize = This->stackSize ;
3761 newClone->stackMaxSize = This->stackMaxSize ;
3762 newClone->stackToVisit =
3763 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3764
3765 memcpy(
3766 newClone->stackToVisit,
3767 This->stackToVisit,
3768 sizeof(ULONG) * newClone->stackSize);
3769
3770 *ppenum = (IEnumSTATSTG*)newClone;
3771
3772 /*
3773 * Don't forget to nail down a reference to the clone before
3774 * returning it.
3775 */
3776 IEnumSTATSTGImpl_AddRef(*ppenum);
3777
3778 return S_OK;
3779 }
3780
3781 static void IEnumSTATSTGImpl_PushSearchNode(
3782 IEnumSTATSTGImpl* This,
3783 ULONG nodeToPush)
3784 {
3785 DirEntry rootProperty;
3786 BOOL readSuccessful;
3787
3788 /*
3789 * First, make sure we're not trying to push an unexisting node.
3790 */
3791 if (nodeToPush==DIRENTRY_NULL)
3792 return;
3793
3794 /*
3795 * First push the node to the stack
3796 */
3797 if (This->stackSize == This->stackMaxSize)
3798 {
3799 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3800
3801 This->stackToVisit = HeapReAlloc(
3802 GetProcessHeap(),
3803 0,
3804 This->stackToVisit,
3805 sizeof(ULONG) * This->stackMaxSize);
3806 }
3807
3808 This->stackToVisit[This->stackSize] = nodeToPush;
3809 This->stackSize++;
3810
3811 /*
3812 * Read the root property from the storage.
3813 */
3814 readSuccessful = StorageImpl_ReadDirEntry(
3815 This->parentStorage,
3816 nodeToPush,
3817 &rootProperty);
3818
3819 if (readSuccessful)
3820 {
3821 assert(rootProperty.sizeOfNameString!=0);
3822
3823 /*
3824 * Push the previous search node in the search stack.
3825 */
3826 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.leftChild);
3827 }
3828 }
3829
3830 static ULONG IEnumSTATSTGImpl_PopSearchNode(
3831 IEnumSTATSTGImpl* This,
3832 BOOL remove)
3833 {
3834 ULONG topNode;
3835
3836 if (This->stackSize == 0)
3837 return DIRENTRY_NULL;
3838
3839 topNode = This->stackToVisit[This->stackSize-1];
3840
3841 if (remove)
3842 This->stackSize--;
3843
3844 return topNode;
3845 }
3846
3847 /*
3848 * Virtual function table for the IEnumSTATSTGImpl class.
3849 */
3850 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3851 {
3852 IEnumSTATSTGImpl_QueryInterface,
3853 IEnumSTATSTGImpl_AddRef,
3854 IEnumSTATSTGImpl_Release,
3855 IEnumSTATSTGImpl_Next,
3856 IEnumSTATSTGImpl_Skip,
3857 IEnumSTATSTGImpl_Reset,
3858 IEnumSTATSTGImpl_Clone
3859 };
3860
3861 /******************************************************************************
3862 ** IEnumSTATSTGImpl implementation
3863 */
3864
3865 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3866 StorageImpl* parentStorage,
3867 ULONG firstPropertyNode)
3868 {
3869 IEnumSTATSTGImpl* newEnumeration;
3870
3871 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3872
3873 if (newEnumeration!=0)
3874 {
3875 /*
3876 * Set-up the virtual function table and reference count.
3877 */
3878 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3879 newEnumeration->ref = 0;
3880
3881 /*
3882 * We want to nail-down the reference to the storage in case the
3883 * enumeration out-lives the storage in the client application.
3884 */
3885 newEnumeration->parentStorage = parentStorage;
3886 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3887
3888 newEnumeration->firstPropertyNode = firstPropertyNode;
3889
3890 /*
3891 * Initialize the search stack
3892 */
3893 newEnumeration->stackSize = 0;
3894 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3895 newEnumeration->stackToVisit =
3896 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3897
3898 /*
3899 * Make sure the current node of the iterator is the first one.
3900 */
3901 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3902 }
3903
3904 return newEnumeration;
3905 }
3906
3907 /*
3908 * Virtual function table for the Storage32InternalImpl class.
3909 */
3910 static const IStorageVtbl Storage32InternalImpl_Vtbl =
3911 {
3912 StorageBaseImpl_QueryInterface,
3913 StorageBaseImpl_AddRef,
3914 StorageBaseImpl_Release,
3915 StorageBaseImpl_CreateStream,
3916 StorageBaseImpl_OpenStream,
3917 StorageBaseImpl_CreateStorage,
3918 StorageBaseImpl_OpenStorage,
3919 StorageBaseImpl_CopyTo,
3920 StorageBaseImpl_MoveElementTo,
3921 StorageInternalImpl_Commit,
3922 StorageInternalImpl_Revert,
3923 StorageBaseImpl_EnumElements,
3924 StorageBaseImpl_DestroyElement,
3925 StorageBaseImpl_RenameElement,
3926 StorageBaseImpl_SetElementTimes,
3927 StorageBaseImpl_SetClass,
3928 StorageBaseImpl_SetStateBits,
3929 StorageBaseImpl_Stat
3930 };
3931
3932 /******************************************************************************
3933 ** Storage32InternalImpl implementation
3934 */
3935
3936 static StorageInternalImpl* StorageInternalImpl_Construct(
3937 StorageImpl* ancestorStorage,
3938 DWORD openFlags,
3939 ULONG rootPropertyIndex)
3940 {
3941 StorageInternalImpl* newStorage;
3942
3943 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
3944
3945 if (newStorage!=0)
3946 {
3947 /*
3948 * Initialize the stream list
3949 */
3950 list_init(&newStorage->base.strmHead);
3951
3952 /*
3953 * Initialize the virtual function table.
3954 */
3955 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
3956 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
3957 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
3958
3959 /*
3960 * Keep the ancestor storage pointer but do not nail a reference to it.
3961 */
3962 newStorage->base.ancestorStorage = ancestorStorage;
3963
3964 /*
3965 * Keep the index of the root property set for this storage,
3966 */
3967 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
3968
3969 return newStorage;
3970 }
3971
3972 return 0;
3973 }
3974
3975 /******************************************************************************
3976 ** StorageUtl implementation
3977 */
3978
3979 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
3980 {
3981 WORD tmp;
3982
3983 memcpy(&tmp, buffer+offset, sizeof(WORD));
3984 *value = lendian16toh(tmp);
3985 }
3986
3987 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
3988 {
3989 value = htole16(value);
3990 memcpy(buffer+offset, &value, sizeof(WORD));
3991 }
3992
3993 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
3994 {
3995 DWORD tmp;
3996
3997 memcpy(&tmp, buffer+offset, sizeof(DWORD));
3998 *value = lendian32toh(tmp);
3999 }
4000
4001 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4002 {
4003 value = htole32(value);
4004 memcpy(buffer+offset, &value, sizeof(DWORD));
4005 }
4006
4007 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4008 ULARGE_INTEGER* value)
4009 {
4010 #ifdef WORDS_BIGENDIAN
4011 ULARGE_INTEGER tmp;
4012
4013 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4014 value->u.LowPart = htole32(tmp.u.HighPart);
4015 value->u.HighPart = htole32(tmp.u.LowPart);
4016 #else
4017 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4018 #endif
4019 }
4020
4021 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4022 const ULARGE_INTEGER *value)
4023 {
4024 #ifdef WORDS_BIGENDIAN
4025 ULARGE_INTEGER tmp;
4026
4027 tmp.u.LowPart = htole32(value->u.HighPart);
4028 tmp.u.HighPart = htole32(value->u.LowPart);
4029 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4030 #else
4031 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4032 #endif
4033 }
4034
4035 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4036 {
4037 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4038 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4039 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4040
4041 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4042 }
4043
4044 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4045 {
4046 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4047 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4048 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4049
4050 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4051 }
4052
4053 void StorageUtl_CopyDirEntryToSTATSTG(
4054 STATSTG* destination,
4055 const DirEntry* source,
4056 int statFlags)
4057 {
4058 /*
4059 * The copy of the string occurs only when the flag is not set
4060 */
4061 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4062 (source->name == NULL) ||
4063 (source->name[0] == 0) )
4064 {
4065 destination->pwcsName = 0;
4066 }
4067 else
4068 {
4069 destination->pwcsName =
4070 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4071
4072 strcpyW(destination->pwcsName, source->name);
4073 }
4074
4075 switch (source->propertyType)
4076 {
4077 case STGTY_STORAGE:
4078 case STGTY_ROOT:
4079 destination->type = STGTY_STORAGE;
4080 break;
4081 case STGTY_STREAM:
4082 destination->type = STGTY_STREAM;
4083 break;
4084 default:
4085 destination->type = STGTY_STREAM;
4086 break;
4087 }
4088
4089 destination->cbSize = source->size;
4090 /*
4091 currentReturnStruct->mtime = {0}; TODO
4092 currentReturnStruct->ctime = {0};
4093 currentReturnStruct->atime = {0};
4094 */
4095 destination->grfMode = 0;
4096 destination->grfLocksSupported = 0;
4097 destination->clsid = source->propertyUniqueID;
4098 destination->grfStateBits = 0;
4099 destination->reserved = 0;
4100 }
4101
4102 /******************************************************************************
4103 ** BlockChainStream implementation
4104 */
4105
4106 BlockChainStream* BlockChainStream_Construct(
4107 StorageImpl* parentStorage,
4108 ULONG* headOfStreamPlaceHolder,
4109 ULONG propertyIndex)
4110 {
4111 BlockChainStream* newStream;
4112 ULONG blockIndex;
4113
4114 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4115
4116 newStream->parentStorage = parentStorage;
4117 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4118 newStream->ownerPropertyIndex = propertyIndex;
4119 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4120 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4121 newStream->numBlocks = 0;
4122
4123 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4124
4125 while (blockIndex != BLOCK_END_OF_CHAIN)
4126 {
4127 newStream->numBlocks++;
4128 newStream->tailIndex = blockIndex;
4129
4130 if(FAILED(StorageImpl_GetNextBlockInChain(
4131 parentStorage,
4132 blockIndex,
4133 &blockIndex)))
4134 {
4135 HeapFree(GetProcessHeap(), 0, newStream);
4136 return NULL;
4137 }
4138 }
4139
4140 return newStream;
4141 }
4142
4143 void BlockChainStream_Destroy(BlockChainStream* This)
4144 {
4145 HeapFree(GetProcessHeap(), 0, This);
4146 }
4147
4148 /******************************************************************************
4149 * BlockChainStream_GetHeadOfChain
4150 *
4151 * Returns the head of this stream chain.
4152 * Some special chains don't have properties, their heads are kept in
4153 * This->headOfStreamPlaceHolder.
4154 *
4155 */
4156 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4157 {
4158 DirEntry chainProperty;
4159 BOOL readSuccessful;
4160
4161 if (This->headOfStreamPlaceHolder != 0)
4162 return *(This->headOfStreamPlaceHolder);
4163
4164 if (This->ownerPropertyIndex != DIRENTRY_NULL)
4165 {
4166 readSuccessful = StorageImpl_ReadDirEntry(
4167 This->parentStorage,
4168 This->ownerPropertyIndex,
4169 &chainProperty);
4170
4171 if (readSuccessful)
4172 {
4173 return chainProperty.startingBlock;
4174 }
4175 }
4176
4177 return BLOCK_END_OF_CHAIN;
4178 }
4179
4180 /******************************************************************************
4181 * BlockChainStream_GetCount
4182 *
4183 * Returns the number of blocks that comprises this chain.
4184 * This is not the size of the stream as the last block may not be full!
4185 *
4186 */
4187 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4188 {
4189 ULONG blockIndex;
4190 ULONG count = 0;
4191
4192 blockIndex = BlockChainStream_GetHeadOfChain(This);
4193
4194 while (blockIndex != BLOCK_END_OF_CHAIN)
4195 {
4196 count++;
4197
4198 if(FAILED(StorageImpl_GetNextBlockInChain(
4199 This->parentStorage,
4200 blockIndex,
4201 &blockIndex)))
4202 return 0;
4203 }
4204
4205 return count;
4206 }
4207
4208 /******************************************************************************
4209 * BlockChainStream_ReadAt
4210 *
4211 * Reads a specified number of bytes from this chain at the specified offset.
4212 * bytesRead may be NULL.
4213 * Failure will be returned if the specified number of bytes has not been read.
4214 */
4215 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4216 ULARGE_INTEGER offset,
4217 ULONG size,
4218 void* buffer,
4219 ULONG* bytesRead)
4220 {
4221 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4222 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4223 ULONG bytesToReadInBuffer;
4224 ULONG blockIndex;
4225 BYTE* bufferWalker;
4226
4227 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4228
4229 /*
4230 * Find the first block in the stream that contains part of the buffer.
4231 */
4232 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4233 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4234 (blockNoInSequence < This->lastBlockNoInSequence) )
4235 {
4236 blockIndex = BlockChainStream_GetHeadOfChain(This);
4237 This->lastBlockNoInSequence = blockNoInSequence;
4238 }
4239 else
4240 {
4241 ULONG temp = blockNoInSequence;
4242
4243 blockIndex = This->lastBlockNoInSequenceIndex;
4244 blockNoInSequence -= This->lastBlockNoInSequence;
4245 This->lastBlockNoInSequence = temp;
4246 }
4247
4248 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4249 {
4250 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4251 return STG_E_DOCFILECORRUPT;
4252 blockNoInSequence--;
4253 }
4254
4255 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4256 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4257
4258 This->lastBlockNoInSequenceIndex = blockIndex;
4259
4260 /*
4261 * Start reading the buffer.
4262 */
4263 *bytesRead = 0;
4264 bufferWalker = buffer;
4265
4266 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4267 {
4268 ULARGE_INTEGER ulOffset;
4269 DWORD bytesReadAt;
4270 /*
4271 * Calculate how many bytes we can copy from this big block.
4272 */
4273 bytesToReadInBuffer =
4274 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4275
4276 TRACE("block %i\n",blockIndex);
4277 ulOffset.u.HighPart = 0;
4278 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4279 offsetInBlock;
4280
4281 StorageImpl_ReadAt(This->parentStorage,
4282 ulOffset,
4283 bufferWalker,
4284 bytesToReadInBuffer,
4285 &bytesReadAt);
4286 /*
4287 * Step to the next big block.
4288 */
4289 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4290 return STG_E_DOCFILECORRUPT;
4291
4292 bufferWalker += bytesReadAt;
4293 size -= bytesReadAt;
4294 *bytesRead += bytesReadAt;
4295 offsetInBlock = 0; /* There is no offset on the next block */
4296
4297 if (bytesToReadInBuffer != bytesReadAt)
4298 break;
4299 }
4300
4301 return (size == 0) ? S_OK : STG_E_READFAULT;
4302 }
4303
4304 /******************************************************************************
4305 * BlockChainStream_WriteAt
4306 *
4307 * Writes the specified number of bytes to this chain at the specified offset.
4308 * Will fail if not all specified number of bytes have been written.
4309 */
4310 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4311 ULARGE_INTEGER offset,
4312 ULONG size,
4313 const void* buffer,
4314 ULONG* bytesWritten)
4315 {
4316 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4317 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4318 ULONG bytesToWrite;
4319 ULONG blockIndex;
4320 const BYTE* bufferWalker;
4321
4322 /*
4323 * Find the first block in the stream that contains part of the buffer.
4324 */
4325 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4326 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4327 (blockNoInSequence < This->lastBlockNoInSequence) )
4328 {
4329 blockIndex = BlockChainStream_GetHeadOfChain(This);
4330 This->lastBlockNoInSequence = blockNoInSequence;
4331 }
4332 else
4333 {
4334 ULONG temp = blockNoInSequence;
4335
4336 blockIndex = This->lastBlockNoInSequenceIndex;
4337 blockNoInSequence -= This->lastBlockNoInSequence;
4338 This->lastBlockNoInSequence = temp;
4339 }
4340
4341 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4342 {
4343 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4344 &blockIndex)))
4345 return STG_E_DOCFILECORRUPT;
4346 blockNoInSequence--;
4347 }
4348
4349 This->lastBlockNoInSequenceIndex = blockIndex;
4350
4351 /* BlockChainStream_SetSize should have already been called to ensure we have
4352 * enough blocks in the chain to write into */
4353 if (blockIndex == BLOCK_END_OF_CHAIN)
4354 {
4355 ERR("not enough blocks in chain to write data\n");
4356 return STG_E_DOCFILECORRUPT;
4357 }
4358
4359 *bytesWritten = 0;
4360 bufferWalker = buffer;
4361
4362 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4363 {
4364 ULARGE_INTEGER ulOffset;
4365 DWORD bytesWrittenAt;
4366 /*
4367 * Calculate how many bytes we can copy from this big block.
4368 */
4369 bytesToWrite =
4370 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4371
4372 TRACE("block %i\n",blockIndex);
4373 ulOffset.u.HighPart = 0;
4374 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4375 offsetInBlock;
4376
4377 StorageImpl_WriteAt(This->parentStorage,
4378 ulOffset,
4379 bufferWalker,