9b2f0cd7e9b7261aad70800e6242d9287e5eaf11
[reactos.git] / reactos / dll / win32 / ole32 / storage32.c
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
4 *
5 * This file contains the compound file implementation
6 * of the storage interface.
7 *
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 *
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
31 */
32
33 #include "precomp.h"
34 #include "storage32.h"
35
36 #include <wine/wingdi16.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(storage);
39
40
41 /*
42 * These are signatures to detect the type of Document file.
43 */
44 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
45 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
46
47 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
48
49
50 /****************************************************************************
51 * StorageInternalImpl definitions.
52 *
53 * Definition of the implementation structure for the IStorage interface.
54 * This one implements the IStorage interface for storage that are
55 * inside another storage.
56 */
57 typedef struct StorageInternalImpl
58 {
59 struct StorageBaseImpl base;
60
61 /*
62 * Entry in the parent's stream tracking list
63 */
64 struct list ParentListEntry;
65
66 StorageBaseImpl *parentStorage;
67 } StorageInternalImpl;
68
69 static const IStorageVtbl StorageInternalImpl_Vtbl;
70 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
71
72 typedef struct TransactedDirEntry
73 {
74 /* If applicable, a reference to the original DirEntry in the transacted
75 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
76 DirRef transactedParentEntry;
77
78 /* True if this entry is being used. */
79 BOOL inuse;
80
81 /* True if data is up to date. */
82 BOOL read;
83
84 /* True if this entry has been modified. */
85 BOOL dirty;
86
87 /* True if this entry's stream has been modified. */
88 BOOL stream_dirty;
89
90 /* True if this entry has been deleted in the transacted storage, but the
91 * delete has not yet been committed. */
92 BOOL deleted;
93
94 /* If this entry's stream has been modified, a reference to where the stream
95 * is stored in the snapshot file. */
96 DirRef stream_entry;
97
98 /* This directory entry's data, including any changes that have been made. */
99 DirEntry data;
100
101 /* A reference to the parent of this node. This is only valid while we are
102 * committing changes. */
103 DirRef parent;
104
105 /* A reference to a newly-created entry in the transacted parent. This is
106 * always equal to transactedParentEntry except when committing changes. */
107 DirRef newTransactedParentEntry;
108 } TransactedDirEntry;
109
110
111 /****************************************************************************
112 * Transacted storage object.
113 */
114 typedef struct TransactedSnapshotImpl
115 {
116 struct StorageBaseImpl base;
117
118 /*
119 * Modified streams are temporarily saved to the scratch file.
120 */
121 StorageBaseImpl *scratch;
122
123 /* The directory structure is kept here, so that we can track how these
124 * entries relate to those in the parent storage. */
125 TransactedDirEntry *entries;
126 ULONG entries_size;
127 ULONG firstFreeEntry;
128
129 /*
130 * Changes are committed to the transacted parent.
131 */
132 StorageBaseImpl *transactedParent;
133
134 /* The transaction signature from when we last committed */
135 ULONG lastTransactionSig;
136 } TransactedSnapshotImpl;
137
138 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
139 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
140
141 typedef struct TransactedSharedImpl
142 {
143 struct StorageBaseImpl base;
144
145 /*
146 * Snapshot and uncommitted changes go here.
147 */
148 TransactedSnapshotImpl *scratch;
149
150 /*
151 * Changes are committed to the transacted parent.
152 */
153 StorageBaseImpl *transactedParent;
154
155 /* The transaction signature from when we last committed */
156 ULONG lastTransactionSig;
157 } TransactedSharedImpl;
158
159
160 /****************************************************************************
161 * BlockChainStream definitions.
162 *
163 * The BlockChainStream class is a utility class that is used to create an
164 * abstraction of the big block chains in the storage file.
165 */
166
167 struct BlockChainRun
168 {
169 /* This represents a range of blocks that happen reside in consecutive sectors. */
170 ULONG firstSector;
171 ULONG firstOffset;
172 ULONG lastOffset;
173 };
174
175 typedef struct BlockChainBlock
176 {
177 ULONG index;
178 ULONG sector;
179 BOOL read;
180 BOOL dirty;
181 BYTE data[MAX_BIG_BLOCK_SIZE];
182 } BlockChainBlock;
183
184 struct BlockChainStream
185 {
186 StorageImpl* parentStorage;
187 ULONG* headOfStreamPlaceHolder;
188 DirRef ownerDirEntry;
189 struct BlockChainRun* indexCache;
190 ULONG indexCacheLen;
191 ULONG indexCacheSize;
192 BlockChainBlock cachedBlocks[2];
193 ULONG blockToEvict;
194 ULONG tailIndex;
195 ULONG numBlocks;
196 };
197
198 /* Returns the number of blocks that comprises this chain.
199 * This is not the size of the stream as the last block may not be full!
200 */
201 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
202 {
203 return This->numBlocks;
204 }
205
206 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
207 static void BlockChainStream_Destroy(BlockChainStream*);
208 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
209 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
210 static HRESULT BlockChainStream_Flush(BlockChainStream*);
211 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
212 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
213
214
215 /****************************************************************************
216 * SmallBlockChainStream definitions.
217 *
218 * The SmallBlockChainStream class is a utility class that is used to create an
219 * abstraction of the small block chains in the storage file.
220 */
221
222 struct SmallBlockChainStream
223 {
224 StorageImpl* parentStorage;
225 DirRef ownerDirEntry;
226 ULONG* headOfStreamPlaceHolder;
227 };
228
229 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
230 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
231 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
232 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
233 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
234 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
235
236
237 /************************************************************************
238 * STGM Functions
239 ***********************************************************************/
240
241 /************************************************************************
242 * This method validates an STGM parameter that can contain the values below
243 *
244 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
245 * The stgm values contained in 0xffff0000 are bitmasks.
246 *
247 * STGM_DIRECT 0x00000000
248 * STGM_TRANSACTED 0x00010000
249 * STGM_SIMPLE 0x08000000
250 *
251 * STGM_READ 0x00000000
252 * STGM_WRITE 0x00000001
253 * STGM_READWRITE 0x00000002
254 *
255 * STGM_SHARE_DENY_NONE 0x00000040
256 * STGM_SHARE_DENY_READ 0x00000030
257 * STGM_SHARE_DENY_WRITE 0x00000020
258 * STGM_SHARE_EXCLUSIVE 0x00000010
259 *
260 * STGM_PRIORITY 0x00040000
261 * STGM_DELETEONRELEASE 0x04000000
262 *
263 * STGM_CREATE 0x00001000
264 * STGM_CONVERT 0x00020000
265 * STGM_FAILIFTHERE 0x00000000
266 *
267 * STGM_NOSCRATCH 0x00100000
268 * STGM_NOSNAPSHOT 0x00200000
269 */
270 static HRESULT validateSTGM(DWORD stgm)
271 {
272 DWORD access = STGM_ACCESS_MODE(stgm);
273 DWORD share = STGM_SHARE_MODE(stgm);
274 DWORD create = STGM_CREATE_MODE(stgm);
275
276 if (stgm&~STGM_KNOWN_FLAGS)
277 {
278 ERR("unknown flags %08x\n", stgm);
279 return E_FAIL;
280 }
281
282 switch (access)
283 {
284 case STGM_READ:
285 case STGM_WRITE:
286 case STGM_READWRITE:
287 break;
288 default:
289 return E_FAIL;
290 }
291
292 switch (share)
293 {
294 case STGM_SHARE_DENY_NONE:
295 case STGM_SHARE_DENY_READ:
296 case STGM_SHARE_DENY_WRITE:
297 case STGM_SHARE_EXCLUSIVE:
298 break;
299 case 0:
300 if (!(stgm & STGM_TRANSACTED))
301 return E_FAIL;
302 break;
303 default:
304 return E_FAIL;
305 }
306
307 switch (create)
308 {
309 case STGM_CREATE:
310 case STGM_FAILIFTHERE:
311 break;
312 default:
313 return E_FAIL;
314 }
315
316 /*
317 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
318 */
319 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
320 return E_FAIL;
321
322 /*
323 * STGM_CREATE | STGM_CONVERT
324 * if both are false, STGM_FAILIFTHERE is set to TRUE
325 */
326 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
327 return E_FAIL;
328
329 /*
330 * STGM_NOSCRATCH requires STGM_TRANSACTED
331 */
332 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
333 return E_FAIL;
334
335 /*
336 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
337 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
338 */
339 if ( (stgm & STGM_NOSNAPSHOT) &&
340 (!(stgm & STGM_TRANSACTED) ||
341 share == STGM_SHARE_EXCLUSIVE ||
342 share == STGM_SHARE_DENY_WRITE) )
343 return E_FAIL;
344
345 return S_OK;
346 }
347
348 /************************************************************************
349 * GetShareModeFromSTGM
350 *
351 * This method will return a share mode flag from a STGM value.
352 * The STGM value is assumed valid.
353 */
354 static DWORD GetShareModeFromSTGM(DWORD stgm)
355 {
356 switch (STGM_SHARE_MODE(stgm))
357 {
358 case 0:
359 assert(stgm & STGM_TRANSACTED);
360 /* fall-through */
361 case STGM_SHARE_DENY_NONE:
362 return FILE_SHARE_READ | FILE_SHARE_WRITE;
363 case STGM_SHARE_DENY_READ:
364 return FILE_SHARE_WRITE;
365 case STGM_SHARE_DENY_WRITE:
366 case STGM_SHARE_EXCLUSIVE:
367 return FILE_SHARE_READ;
368 }
369 ERR("Invalid share mode!\n");
370 assert(0);
371 return 0;
372 }
373
374 /************************************************************************
375 * GetAccessModeFromSTGM
376 *
377 * This method will return an access mode flag from a STGM value.
378 * The STGM value is assumed valid.
379 */
380 static DWORD GetAccessModeFromSTGM(DWORD stgm)
381 {
382 switch (STGM_ACCESS_MODE(stgm))
383 {
384 case STGM_READ:
385 return GENERIC_READ;
386 case STGM_WRITE:
387 case STGM_READWRITE:
388 return GENERIC_READ | GENERIC_WRITE;
389 }
390 ERR("Invalid access mode!\n");
391 assert(0);
392 return 0;
393 }
394
395 /************************************************************************
396 * GetCreationModeFromSTGM
397 *
398 * This method will return a creation mode flag from a STGM value.
399 * The STGM value is assumed valid.
400 */
401 static DWORD GetCreationModeFromSTGM(DWORD stgm)
402 {
403 switch(STGM_CREATE_MODE(stgm))
404 {
405 case STGM_CREATE:
406 return CREATE_ALWAYS;
407 case STGM_CONVERT:
408 FIXME("STGM_CONVERT not implemented!\n");
409 return CREATE_NEW;
410 case STGM_FAILIFTHERE:
411 return CREATE_NEW;
412 }
413 ERR("Invalid create mode!\n");
414 assert(0);
415 return 0;
416 }
417
418
419 /************************************************************************
420 * IDirectWriterLock implementation
421 ***********************************************************************/
422
423 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
424 {
425 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
426 }
427
428 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
429 {
430 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
431 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
432 }
433
434 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
435 {
436 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
437 return IStorage_AddRef(&This->IStorage_iface);
438 }
439
440 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
441 {
442 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
443 return IStorage_Release(&This->IStorage_iface);
444 }
445
446 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
447 {
448 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
449 FIXME("(%p)->(%d): stub\n", This, timeout);
450 return E_NOTIMPL;
451 }
452
453 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
454 {
455 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
456 FIXME("(%p): stub\n", This);
457 return E_NOTIMPL;
458 }
459
460 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
461 {
462 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
463 FIXME("(%p): stub\n", This);
464 return E_NOTIMPL;
465 }
466
467 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
468 {
469 directwriterlock_QueryInterface,
470 directwriterlock_AddRef,
471 directwriterlock_Release,
472 directwriterlock_WaitForWriteAccess,
473 directwriterlock_ReleaseWriteAccess,
474 directwriterlock_HaveWriteAccess
475 };
476
477
478 /************************************************************************
479 * StorageBaseImpl implementation : Tree helper functions
480 ***********************************************************************/
481
482 /****************************************************************************
483 *
484 * Internal Method
485 *
486 * Case insensitive comparison of DirEntry.name by first considering
487 * their size.
488 *
489 * Returns <0 when name1 < name2
490 * >0 when name1 > name2
491 * 0 when name1 == name2
492 */
493 static LONG entryNameCmp(
494 const OLECHAR *name1,
495 const OLECHAR *name2)
496 {
497 LONG diff = lstrlenW(name1) - lstrlenW(name2);
498
499 while (diff == 0 && *name1 != 0)
500 {
501 /*
502 * We compare the string themselves only when they are of the same length
503 */
504 diff = toupperW(*name1++) - toupperW(*name2++);
505 }
506
507 return diff;
508 }
509
510 /****************************************************************************
511 *
512 * Internal Method
513 *
514 * Find and read the element of a storage with the given name.
515 */
516 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
517 const OLECHAR *name, DirEntry *data)
518 {
519 DirRef currentEntry;
520
521 /* Read the storage entry to find the root of the tree. */
522 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
523
524 currentEntry = data->dirRootEntry;
525
526 while (currentEntry != DIRENTRY_NULL)
527 {
528 LONG cmp;
529
530 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
531
532 cmp = entryNameCmp(name, data->name);
533
534 if (cmp == 0)
535 /* found it */
536 break;
537
538 else if (cmp < 0)
539 currentEntry = data->leftChild;
540
541 else if (cmp > 0)
542 currentEntry = data->rightChild;
543 }
544
545 return currentEntry;
546 }
547
548 /****************************************************************************
549 *
550 * Internal Method
551 *
552 * Find and read the binary tree parent of the element with the given name.
553 *
554 * If there is no such element, find a place where it could be inserted and
555 * return STG_E_FILENOTFOUND.
556 */
557 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
558 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
559 ULONG *relation)
560 {
561 DirRef childEntry;
562 DirEntry childData;
563
564 /* Read the storage entry to find the root of the tree. */
565 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
566
567 *parentEntry = storageEntry;
568 *relation = DIRENTRY_RELATION_DIR;
569
570 childEntry = parentData->dirRootEntry;
571
572 while (childEntry != DIRENTRY_NULL)
573 {
574 LONG cmp;
575
576 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
577
578 cmp = entryNameCmp(childName, childData.name);
579
580 if (cmp == 0)
581 /* found it */
582 break;
583
584 else if (cmp < 0)
585 {
586 *parentData = childData;
587 *parentEntry = childEntry;
588 *relation = DIRENTRY_RELATION_PREVIOUS;
589
590 childEntry = parentData->leftChild;
591 }
592
593 else if (cmp > 0)
594 {
595 *parentData = childData;
596 *parentEntry = childEntry;
597 *relation = DIRENTRY_RELATION_NEXT;
598
599 childEntry = parentData->rightChild;
600 }
601 }
602
603 if (childEntry == DIRENTRY_NULL)
604 return STG_E_FILENOTFOUND;
605 else
606 return S_OK;
607 }
608
609 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
610 {
611 switch (relation)
612 {
613 case DIRENTRY_RELATION_PREVIOUS:
614 entry->leftChild = new_target;
615 break;
616 case DIRENTRY_RELATION_NEXT:
617 entry->rightChild = new_target;
618 break;
619 case DIRENTRY_RELATION_DIR:
620 entry->dirRootEntry = new_target;
621 break;
622 default:
623 assert(0);
624 }
625 }
626
627 /****************************************************************************
628 *
629 * Internal Method
630 *
631 * Add a directory entry to a storage
632 */
633 static HRESULT insertIntoTree(
634 StorageBaseImpl *This,
635 DirRef parentStorageIndex,
636 DirRef newEntryIndex)
637 {
638 DirEntry currentEntry;
639 DirEntry newEntry;
640
641 /*
642 * Read the inserted entry
643 */
644 StorageBaseImpl_ReadDirEntry(This,
645 newEntryIndex,
646 &newEntry);
647
648 /*
649 * Read the storage entry
650 */
651 StorageBaseImpl_ReadDirEntry(This,
652 parentStorageIndex,
653 &currentEntry);
654
655 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
656 {
657 /*
658 * The root storage contains some element, therefore, start the research
659 * for the appropriate location.
660 */
661 BOOL found = FALSE;
662 DirRef current, next, previous, currentEntryId;
663
664 /*
665 * Keep a reference to the root of the storage's element tree
666 */
667 currentEntryId = currentEntry.dirRootEntry;
668
669 /*
670 * Read
671 */
672 StorageBaseImpl_ReadDirEntry(This,
673 currentEntry.dirRootEntry,
674 &currentEntry);
675
676 previous = currentEntry.leftChild;
677 next = currentEntry.rightChild;
678 current = currentEntryId;
679
680 while (!found)
681 {
682 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
683
684 if (diff < 0)
685 {
686 if (previous != DIRENTRY_NULL)
687 {
688 StorageBaseImpl_ReadDirEntry(This,
689 previous,
690 &currentEntry);
691 current = previous;
692 }
693 else
694 {
695 currentEntry.leftChild = newEntryIndex;
696 StorageBaseImpl_WriteDirEntry(This,
697 current,
698 &currentEntry);
699 found = TRUE;
700 }
701 }
702 else if (diff > 0)
703 {
704 if (next != DIRENTRY_NULL)
705 {
706 StorageBaseImpl_ReadDirEntry(This,
707 next,
708 &currentEntry);
709 current = next;
710 }
711 else
712 {
713 currentEntry.rightChild = newEntryIndex;
714 StorageBaseImpl_WriteDirEntry(This,
715 current,
716 &currentEntry);
717 found = TRUE;
718 }
719 }
720 else
721 {
722 /*
723 * Trying to insert an item with the same name in the
724 * subtree structure.
725 */
726 return STG_E_FILEALREADYEXISTS;
727 }
728
729 previous = currentEntry.leftChild;
730 next = currentEntry.rightChild;
731 }
732 }
733 else
734 {
735 /*
736 * The storage is empty, make the new entry the root of its element tree
737 */
738 currentEntry.dirRootEntry = newEntryIndex;
739 StorageBaseImpl_WriteDirEntry(This,
740 parentStorageIndex,
741 &currentEntry);
742 }
743
744 return S_OK;
745 }
746
747 /*************************************************************************
748 *
749 * Internal Method
750 *
751 * This method removes a directory entry from its parent storage tree without
752 * freeing any resources attached to it.
753 */
754 static HRESULT removeFromTree(
755 StorageBaseImpl *This,
756 DirRef parentStorageIndex,
757 DirRef deletedIndex)
758 {
759 DirEntry entryToDelete;
760 DirEntry parentEntry;
761 DirRef parentEntryRef;
762 ULONG typeOfRelation;
763 HRESULT hr;
764
765 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
766
767 if (hr != S_OK)
768 return hr;
769
770 /*
771 * Find the element that links to the one we want to delete.
772 */
773 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
774 &parentEntry, &parentEntryRef, &typeOfRelation);
775
776 if (hr != S_OK)
777 return hr;
778
779 if (entryToDelete.leftChild != DIRENTRY_NULL)
780 {
781 /*
782 * Replace the deleted entry with its left child
783 */
784 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
785
786 hr = StorageBaseImpl_WriteDirEntry(
787 This,
788 parentEntryRef,
789 &parentEntry);
790 if(FAILED(hr))
791 {
792 return hr;
793 }
794
795 if (entryToDelete.rightChild != DIRENTRY_NULL)
796 {
797 /*
798 * We need to reinsert the right child somewhere. We already know it and
799 * its children are greater than everything in the left tree, so we
800 * insert it at the rightmost point in the left tree.
801 */
802 DirRef newRightChildParent = entryToDelete.leftChild;
803 DirEntry newRightChildParentEntry;
804
805 do
806 {
807 hr = StorageBaseImpl_ReadDirEntry(
808 This,
809 newRightChildParent,
810 &newRightChildParentEntry);
811 if (FAILED(hr))
812 {
813 return hr;
814 }
815
816 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
817 newRightChildParent = newRightChildParentEntry.rightChild;
818 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
819
820 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
821
822 hr = StorageBaseImpl_WriteDirEntry(
823 This,
824 newRightChildParent,
825 &newRightChildParentEntry);
826 if (FAILED(hr))
827 {
828 return hr;
829 }
830 }
831 }
832 else
833 {
834 /*
835 * Replace the deleted entry with its right child
836 */
837 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
838
839 hr = StorageBaseImpl_WriteDirEntry(
840 This,
841 parentEntryRef,
842 &parentEntry);
843 if(FAILED(hr))
844 {
845 return hr;
846 }
847 }
848
849 return hr;
850 }
851
852
853 /************************************************************************
854 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
855 ***********************************************************************/
856
857 /*
858 * IEnumSTATSTGImpl definitions.
859 *
860 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
861 * This class allows iterating through the content of a storage and to find
862 * specific items inside it.
863 */
864 struct IEnumSTATSTGImpl
865 {
866 IEnumSTATSTG IEnumSTATSTG_iface;
867
868 LONG ref; /* Reference count */
869 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
870 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
871
872 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
873 };
874
875 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
876 {
877 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
878 }
879
880 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
881 {
882 IStorage_Release(&This->parentStorage->IStorage_iface);
883 HeapFree(GetProcessHeap(), 0, This);
884 }
885
886 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
887 IEnumSTATSTG* iface,
888 REFIID riid,
889 void** ppvObject)
890 {
891 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
892
893 if (ppvObject==0)
894 return E_INVALIDARG;
895
896 *ppvObject = 0;
897
898 if (IsEqualGUID(&IID_IUnknown, riid) ||
899 IsEqualGUID(&IID_IEnumSTATSTG, riid))
900 {
901 *ppvObject = &This->IEnumSTATSTG_iface;
902 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
903 return S_OK;
904 }
905
906 return E_NOINTERFACE;
907 }
908
909 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
910 IEnumSTATSTG* iface)
911 {
912 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
913 return InterlockedIncrement(&This->ref);
914 }
915
916 static ULONG WINAPI IEnumSTATSTGImpl_Release(
917 IEnumSTATSTG* iface)
918 {
919 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
920
921 ULONG newRef;
922
923 newRef = InterlockedDecrement(&This->ref);
924
925 if (newRef==0)
926 {
927 IEnumSTATSTGImpl_Destroy(This);
928 }
929
930 return newRef;
931 }
932
933 static HRESULT IEnumSTATSTGImpl_GetNextRef(
934 IEnumSTATSTGImpl* This,
935 DirRef *ref)
936 {
937 DirRef result = DIRENTRY_NULL;
938 DirRef searchNode;
939 DirEntry entry;
940 HRESULT hr;
941 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
942
943 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
944 This->parentStorage->storageDirEntry, &entry);
945 searchNode = entry.dirRootEntry;
946
947 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
948 {
949 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
950
951 if (SUCCEEDED(hr))
952 {
953 LONG diff = entryNameCmp( entry.name, This->name);
954
955 if (diff <= 0)
956 {
957 searchNode = entry.rightChild;
958 }
959 else
960 {
961 result = searchNode;
962 memcpy(result_name, entry.name, sizeof(result_name));
963 searchNode = entry.leftChild;
964 }
965 }
966 }
967
968 if (SUCCEEDED(hr))
969 {
970 *ref = result;
971 if (result != DIRENTRY_NULL)
972 memcpy(This->name, result_name, sizeof(result_name));
973 }
974
975 return hr;
976 }
977
978 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
979 IEnumSTATSTG* iface,
980 ULONG celt,
981 STATSTG* rgelt,
982 ULONG* pceltFetched)
983 {
984 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
985
986 DirEntry currentEntry;
987 STATSTG* currentReturnStruct = rgelt;
988 ULONG objectFetched = 0;
989 DirRef currentSearchNode;
990 HRESULT hr=S_OK;
991
992 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
993 return E_INVALIDARG;
994
995 if (This->parentStorage->reverted)
996 return STG_E_REVERTED;
997
998 /*
999 * To avoid the special case, get another pointer to a ULONG value if
1000 * the caller didn't supply one.
1001 */
1002 if (pceltFetched==0)
1003 pceltFetched = &objectFetched;
1004
1005 /*
1006 * Start the iteration, we will iterate until we hit the end of the
1007 * linked list or until we hit the number of items to iterate through
1008 */
1009 *pceltFetched = 0;
1010
1011 while ( *pceltFetched < celt )
1012 {
1013 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1014
1015 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1016 break;
1017
1018 /*
1019 * Read the entry from the storage.
1020 */
1021 StorageBaseImpl_ReadDirEntry(This->parentStorage,
1022 currentSearchNode,
1023 &currentEntry);
1024
1025 /*
1026 * Copy the information to the return buffer.
1027 */
1028 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1029 currentReturnStruct,
1030 &currentEntry,
1031 STATFLAG_DEFAULT);
1032
1033 /*
1034 * Step to the next item in the iteration
1035 */
1036 (*pceltFetched)++;
1037 currentReturnStruct++;
1038 }
1039
1040 if (SUCCEEDED(hr) && *pceltFetched != celt)
1041 hr = S_FALSE;
1042
1043 return hr;
1044 }
1045
1046
1047 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1048 IEnumSTATSTG* iface,
1049 ULONG celt)
1050 {
1051 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1052
1053 ULONG objectFetched = 0;
1054 DirRef currentSearchNode;
1055 HRESULT hr=S_OK;
1056
1057 if (This->parentStorage->reverted)
1058 return STG_E_REVERTED;
1059
1060 while ( (objectFetched < celt) )
1061 {
1062 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1063
1064 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1065 break;
1066
1067 objectFetched++;
1068 }
1069
1070 if (SUCCEEDED(hr) && objectFetched != celt)
1071 return S_FALSE;
1072
1073 return hr;
1074 }
1075
1076 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
1077 IEnumSTATSTG* iface)
1078 {
1079 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1080
1081 if (This->parentStorage->reverted)
1082 return STG_E_REVERTED;
1083
1084 This->name[0] = 0;
1085
1086 return S_OK;
1087 }
1088
1089 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1090
1091 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
1092 IEnumSTATSTG* iface,
1093 IEnumSTATSTG** ppenum)
1094 {
1095 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1096 IEnumSTATSTGImpl* newClone;
1097
1098 if (This->parentStorage->reverted)
1099 return STG_E_REVERTED;
1100
1101 if (ppenum==0)
1102 return E_INVALIDARG;
1103
1104 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1105 This->storageDirEntry);
1106 if (!newClone)
1107 {
1108 *ppenum = NULL;
1109 return E_OUTOFMEMORY;
1110 }
1111
1112 /*
1113 * The new clone enumeration must point to the same current node as
1114 * the old one.
1115 */
1116 memcpy(newClone->name, This->name, sizeof(newClone->name));
1117
1118 *ppenum = &newClone->IEnumSTATSTG_iface;
1119
1120 return S_OK;
1121 }
1122
1123 /*
1124 * Virtual function table for the IEnumSTATSTGImpl class.
1125 */
1126 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1127 {
1128 IEnumSTATSTGImpl_QueryInterface,
1129 IEnumSTATSTGImpl_AddRef,
1130 IEnumSTATSTGImpl_Release,
1131 IEnumSTATSTGImpl_Next,
1132 IEnumSTATSTGImpl_Skip,
1133 IEnumSTATSTGImpl_Reset,
1134 IEnumSTATSTGImpl_Clone
1135 };
1136
1137 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
1138 StorageBaseImpl* parentStorage,
1139 DirRef storageDirEntry)
1140 {
1141 IEnumSTATSTGImpl* newEnumeration;
1142
1143 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1144
1145 if (newEnumeration)
1146 {
1147 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1148 newEnumeration->ref = 1;
1149 newEnumeration->name[0] = 0;
1150
1151 /*
1152 * We want to nail-down the reference to the storage in case the
1153 * enumeration out-lives the storage in the client application.
1154 */
1155 newEnumeration->parentStorage = parentStorage;
1156 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1157
1158 newEnumeration->storageDirEntry = storageDirEntry;
1159 }
1160
1161 return newEnumeration;
1162 }
1163
1164
1165 /************************************************************************
1166 * StorageBaseImpl implementation
1167 ***********************************************************************/
1168
1169 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
1170 {
1171 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1172 }
1173
1174 /************************************************************************
1175 * StorageBaseImpl_QueryInterface (IUnknown)
1176 *
1177 * This method implements the common QueryInterface for all IStorage
1178 * implementations contained in this file.
1179 *
1180 * See Windows documentation for more details on IUnknown methods.
1181 */
1182 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
1183 IStorage* iface,
1184 REFIID riid,
1185 void** ppvObject)
1186 {
1187 StorageBaseImpl *This = impl_from_IStorage(iface);
1188
1189 if (!ppvObject)
1190 return E_INVALIDARG;
1191
1192 *ppvObject = 0;
1193
1194 if (IsEqualGUID(&IID_IUnknown, riid) ||
1195 IsEqualGUID(&IID_IStorage, riid))
1196 {
1197 *ppvObject = &This->IStorage_iface;
1198 }
1199 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1200 {
1201 *ppvObject = &This->IPropertySetStorage_iface;
1202 }
1203 /* locking interface is reported for writer only */
1204 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1205 {
1206 *ppvObject = &This->IDirectWriterLock_iface;
1207 }
1208 else
1209 return E_NOINTERFACE;
1210
1211 IStorage_AddRef(iface);
1212
1213 return S_OK;
1214 }
1215
1216 /************************************************************************
1217 * StorageBaseImpl_AddRef (IUnknown)
1218 *
1219 * This method implements the common AddRef for all IStorage
1220 * implementations contained in this file.
1221 *
1222 * See Windows documentation for more details on IUnknown methods.
1223 */
1224 static ULONG WINAPI StorageBaseImpl_AddRef(
1225 IStorage* iface)
1226 {
1227 StorageBaseImpl *This = impl_from_IStorage(iface);
1228 ULONG ref = InterlockedIncrement(&This->ref);
1229
1230 TRACE("(%p) AddRef to %d\n", This, ref);
1231
1232 return ref;
1233 }
1234
1235 /************************************************************************
1236 * StorageBaseImpl_Release (IUnknown)
1237 *
1238 * This method implements the common Release for all IStorage
1239 * implementations contained in this file.
1240 *
1241 * See Windows documentation for more details on IUnknown methods.
1242 */
1243 static ULONG WINAPI StorageBaseImpl_Release(
1244 IStorage* iface)
1245 {
1246 StorageBaseImpl *This = impl_from_IStorage(iface);
1247
1248 ULONG ref = InterlockedDecrement(&This->ref);
1249
1250 TRACE("(%p) ReleaseRef to %d\n", This, ref);
1251
1252 if (ref == 0)
1253 {
1254 /*
1255 * Since we are using a system of base-classes, we want to call the
1256 * destructor of the appropriate derived class. To do this, we are
1257 * using virtual functions to implement the destructor.
1258 */
1259 StorageBaseImpl_Destroy(This);
1260 }
1261
1262 return ref;
1263 }
1264
1265 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1266 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1267 SNB snbExclude, IStorage *pstgDest);
1268
1269 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1270 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1271 SNB snbExclude, IStorage *pstgDest)
1272 {
1273 DirEntry data;
1274 HRESULT hr;
1275 BOOL skip = FALSE;
1276 IStorage *pstgTmp;
1277 IStream *pstrChild, *pstrTmp;
1278 STATSTG strStat;
1279
1280 if (srcEntry == DIRENTRY_NULL)
1281 return S_OK;
1282
1283 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1284
1285 if (FAILED(hr))
1286 return hr;
1287
1288 if ( snbExclude )
1289 {
1290 WCHAR **snb = snbExclude;
1291
1292 while ( *snb != NULL && !skip )
1293 {
1294 if ( lstrcmpW(data.name, *snb) == 0 )
1295 skip = TRUE;
1296 ++snb;
1297 }
1298 }
1299
1300 if (!skip)
1301 {
1302 if (data.stgType == STGTY_STORAGE && !skip_storage)
1303 {
1304 /*
1305 * create a new storage in destination storage
1306 */
1307 hr = IStorage_CreateStorage( pstgDest, data.name,
1308 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1309 0, 0,
1310 &pstgTmp );
1311
1312 /*
1313 * if it already exist, don't create a new one use this one
1314 */
1315 if (hr == STG_E_FILEALREADYEXISTS)
1316 {
1317 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1318 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1319 NULL, 0, &pstgTmp );
1320 }
1321
1322 if (SUCCEEDED(hr))
1323 {
1324 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1325 skip_stream, NULL, pstgTmp );
1326
1327 IStorage_Release(pstgTmp);
1328 }
1329 }
1330 else if (data.stgType == STGTY_STREAM && !skip_stream)
1331 {
1332 /*
1333 * create a new stream in destination storage. If the stream already
1334 * exist, it will be deleted and a new one will be created.
1335 */
1336 hr = IStorage_CreateStream( pstgDest, data.name,
1337 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1338 0, 0, &pstrTmp );
1339
1340 /*
1341 * open child stream storage. This operation must succeed even if the
1342 * stream is already open, so we use internal functions to do it.
1343 */
1344 if (hr == S_OK)
1345 {
1346 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1347
1348 if (streamimpl)
1349 {
1350 pstrChild = &streamimpl->IStream_iface;
1351 if (pstrChild)
1352 IStream_AddRef(pstrChild);
1353 }
1354 else
1355 {
1356 pstrChild = NULL;
1357 hr = E_OUTOFMEMORY;
1358 }
1359 }
1360
1361 if (hr == S_OK)
1362 {
1363 /*
1364 * Get the size of the source stream
1365 */
1366 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1367
1368 /*
1369 * Set the size of the destination stream.
1370 */
1371 IStream_SetSize(pstrTmp, strStat.cbSize);
1372
1373 /*
1374 * do the copy
1375 */
1376 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1377 NULL, NULL );
1378
1379 IStream_Release( pstrChild );
1380 }
1381
1382 IStream_Release( pstrTmp );
1383 }
1384 }
1385
1386 /* copy siblings */
1387 if (SUCCEEDED(hr))
1388 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1389 skip_stream, snbExclude, pstgDest );
1390
1391 if (SUCCEEDED(hr))
1392 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1393 skip_stream, snbExclude, pstgDest );
1394
1395 return hr;
1396 }
1397
1398 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1399 {
1400 StgStreamImpl *strm;
1401
1402 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1403 {
1404 if (strm->dirEntry == streamEntry)
1405 {
1406 return TRUE;
1407 }
1408 }
1409
1410 return FALSE;
1411 }
1412
1413 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1414 {
1415 StorageInternalImpl *childstg;
1416
1417 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1418 {
1419 if (childstg->base.storageDirEntry == storageEntry)
1420 {
1421 return TRUE;
1422 }
1423 }
1424
1425 return FALSE;
1426 }
1427
1428 /************************************************************************
1429 * StorageBaseImpl_OpenStream (IStorage)
1430 *
1431 * This method will open the specified stream object from the current storage.
1432 *
1433 * See Windows documentation for more details on IStorage methods.
1434 */
1435 static HRESULT WINAPI StorageBaseImpl_OpenStream(
1436 IStorage* iface,
1437 const OLECHAR* pwcsName, /* [string][in] */
1438 void* reserved1, /* [unique][in] */
1439 DWORD grfMode, /* [in] */
1440 DWORD reserved2, /* [in] */
1441 IStream** ppstm) /* [out] */
1442 {
1443 StorageBaseImpl *This = impl_from_IStorage(iface);
1444 StgStreamImpl* newStream;
1445 DirEntry currentEntry;
1446 DirRef streamEntryRef;
1447 HRESULT res = STG_E_UNKNOWN;
1448
1449 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1450 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1451
1452 if ( (pwcsName==NULL) || (ppstm==0) )
1453 {
1454 res = E_INVALIDARG;
1455 goto end;
1456 }
1457
1458 *ppstm = NULL;
1459
1460 if ( FAILED( validateSTGM(grfMode) ) ||
1461 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1462 {
1463 res = STG_E_INVALIDFLAG;
1464 goto end;
1465 }
1466
1467 /*
1468 * As documented.
1469 */
1470 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1471 {
1472 res = STG_E_INVALIDFUNCTION;
1473 goto end;
1474 }
1475
1476 if (This->reverted)
1477 {
1478 res = STG_E_REVERTED;
1479 goto end;
1480 }
1481
1482 /*
1483 * Check that we're compatible with the parent's storage mode, but
1484 * only if we are not in transacted mode
1485 */
1486 if(!(This->openFlags & STGM_TRANSACTED)) {
1487 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1488 {
1489 res = STG_E_INVALIDFLAG;
1490 goto end;
1491 }
1492 }
1493
1494 /*
1495 * Search for the element with the given name
1496 */
1497 streamEntryRef = findElement(
1498 This,
1499 This->storageDirEntry,
1500 pwcsName,
1501 &currentEntry);
1502
1503 /*
1504 * If it was found, construct the stream object and return a pointer to it.
1505 */
1506 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1507 (currentEntry.stgType==STGTY_STREAM) )
1508 {
1509 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1510 {
1511 /* A single stream cannot be opened a second time. */
1512 res = STG_E_ACCESSDENIED;
1513 goto end;
1514 }
1515
1516 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1517
1518 if (newStream)
1519 {
1520 newStream->grfMode = grfMode;
1521 *ppstm = &newStream->IStream_iface;
1522
1523 IStream_AddRef(*ppstm);
1524
1525 res = S_OK;
1526 goto end;
1527 }
1528
1529 res = E_OUTOFMEMORY;
1530 goto end;
1531 }
1532
1533 res = STG_E_FILENOTFOUND;
1534
1535 end:
1536 if (res == S_OK)
1537 TRACE("<-- IStream %p\n", *ppstm);
1538 TRACE("<-- %08x\n", res);
1539 return res;
1540 }
1541
1542 /************************************************************************
1543 * StorageBaseImpl_OpenStorage (IStorage)
1544 *
1545 * This method will open a new storage object from the current storage.
1546 *
1547 * See Windows documentation for more details on IStorage methods.
1548 */
1549 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
1550 IStorage* iface,
1551 const OLECHAR* pwcsName, /* [string][unique][in] */
1552 IStorage* pstgPriority, /* [unique][in] */
1553 DWORD grfMode, /* [in] */
1554 SNB snbExclude, /* [unique][in] */
1555 DWORD reserved, /* [in] */
1556 IStorage** ppstg) /* [out] */
1557 {
1558 StorageBaseImpl *This = impl_from_IStorage(iface);
1559 StorageInternalImpl* newStorage;
1560 StorageBaseImpl* newTransactedStorage;
1561 DirEntry currentEntry;
1562 DirRef storageEntryRef;
1563 HRESULT res = STG_E_UNKNOWN;
1564
1565 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1566 iface, debugstr_w(pwcsName), pstgPriority,
1567 grfMode, snbExclude, reserved, ppstg);
1568
1569 if ((pwcsName==NULL) || (ppstg==0) )
1570 {
1571 res = E_INVALIDARG;
1572 goto end;
1573 }
1574
1575 if (This->openFlags & STGM_SIMPLE)
1576 {
1577 res = STG_E_INVALIDFUNCTION;
1578 goto end;
1579 }
1580
1581 /* as documented */
1582 if (snbExclude != NULL)
1583 {
1584 res = STG_E_INVALIDPARAMETER;
1585 goto end;
1586 }
1587
1588 if ( FAILED( validateSTGM(grfMode) ))
1589 {
1590 res = STG_E_INVALIDFLAG;
1591 goto end;
1592 }
1593
1594 /*
1595 * As documented.
1596 */
1597 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1598 (grfMode & STGM_DELETEONRELEASE) ||
1599 (grfMode & STGM_PRIORITY) )
1600 {
1601 res = STG_E_INVALIDFUNCTION;
1602 goto end;
1603 }
1604
1605 if (This->reverted)
1606 return STG_E_REVERTED;
1607
1608 /*
1609 * Check that we're compatible with the parent's storage mode,
1610 * but only if we are not transacted
1611 */
1612 if(!(This->openFlags & STGM_TRANSACTED)) {
1613 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1614 {
1615 res = STG_E_ACCESSDENIED;
1616 goto end;
1617 }
1618 }
1619
1620 *ppstg = NULL;
1621
1622 storageEntryRef = findElement(
1623 This,
1624 This->storageDirEntry,
1625 pwcsName,
1626 &currentEntry);
1627
1628 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1629 (currentEntry.stgType==STGTY_STORAGE) )
1630 {
1631 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1632 {
1633 /* A single storage cannot be opened a second time. */
1634 res = STG_E_ACCESSDENIED;
1635 goto end;
1636 }
1637
1638 newStorage = StorageInternalImpl_Construct(
1639 This,
1640 grfMode,
1641 storageEntryRef);
1642
1643 if (newStorage != 0)
1644 {
1645 if (grfMode & STGM_TRANSACTED)
1646 {
1647 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1648
1649 if (FAILED(res))
1650 {
1651 HeapFree(GetProcessHeap(), 0, newStorage);
1652 goto end;
1653 }
1654
1655 *ppstg = &newTransactedStorage->IStorage_iface;
1656 }
1657 else
1658 {
1659 *ppstg = &newStorage->base.IStorage_iface;
1660 }
1661
1662 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1663
1664 res = S_OK;
1665 goto end;
1666 }
1667
1668 res = STG_E_INSUFFICIENTMEMORY;
1669 goto end;
1670 }
1671
1672 res = STG_E_FILENOTFOUND;
1673
1674 end:
1675 TRACE("<-- %08x\n", res);
1676 return res;
1677 }
1678
1679 /************************************************************************
1680 * StorageBaseImpl_EnumElements (IStorage)
1681 *
1682 * This method will create an enumerator object that can be used to
1683 * retrieve information about all the elements in the storage object.
1684 *
1685 * See Windows documentation for more details on IStorage methods.
1686 */
1687 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1688 IStorage* iface,
1689 DWORD reserved1, /* [in] */
1690 void* reserved2, /* [size_is][unique][in] */
1691 DWORD reserved3, /* [in] */
1692 IEnumSTATSTG** ppenum) /* [out] */
1693 {
1694 StorageBaseImpl *This = impl_from_IStorage(iface);
1695 IEnumSTATSTGImpl* newEnum;
1696
1697 TRACE("(%p, %d, %p, %d, %p)\n",
1698 iface, reserved1, reserved2, reserved3, ppenum);
1699
1700 if (!ppenum)
1701 return E_INVALIDARG;
1702
1703 if (This->reverted)
1704 return STG_E_REVERTED;
1705
1706 newEnum = IEnumSTATSTGImpl_Construct(
1707 This,
1708 This->storageDirEntry);
1709
1710 if (newEnum)
1711 {
1712 *ppenum = &newEnum->IEnumSTATSTG_iface;
1713 return S_OK;
1714 }
1715
1716 return E_OUTOFMEMORY;
1717 }
1718
1719 /************************************************************************
1720 * StorageBaseImpl_Stat (IStorage)
1721 *
1722 * This method will retrieve information about this storage object.
1723 *
1724 * See Windows documentation for more details on IStorage methods.
1725 */
1726 static HRESULT WINAPI StorageBaseImpl_Stat(
1727 IStorage* iface,
1728 STATSTG* pstatstg, /* [out] */
1729 DWORD grfStatFlag) /* [in] */
1730 {
1731 StorageBaseImpl *This = impl_from_IStorage(iface);
1732 DirEntry currentEntry;
1733 HRESULT res = STG_E_UNKNOWN;
1734
1735 TRACE("(%p, %p, %x)\n",
1736 iface, pstatstg, grfStatFlag);
1737
1738 if (!pstatstg)
1739 {
1740 res = E_INVALIDARG;
1741 goto end;
1742 }
1743
1744 if (This->reverted)
1745 {
1746 res = STG_E_REVERTED;
1747 goto end;
1748 }
1749
1750 res = StorageBaseImpl_ReadDirEntry(
1751 This,
1752 This->storageDirEntry,
1753 &currentEntry);
1754
1755 if (SUCCEEDED(res))
1756 {
1757 StorageUtl_CopyDirEntryToSTATSTG(
1758 This,
1759 pstatstg,
1760 &currentEntry,
1761 grfStatFlag);
1762
1763 pstatstg->grfMode = This->openFlags;
1764 pstatstg->grfStateBits = This->stateBits;
1765 }
1766
1767 end:
1768 if (res == S_OK)
1769 {
1770 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);
1771 }
1772 TRACE("<-- %08x\n", res);
1773 return res;
1774 }
1775
1776 /************************************************************************
1777 * StorageBaseImpl_RenameElement (IStorage)
1778 *
1779 * This method will rename the specified element.
1780 *
1781 * See Windows documentation for more details on IStorage methods.
1782 */
1783 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1784 IStorage* iface,
1785 const OLECHAR* pwcsOldName, /* [in] */
1786 const OLECHAR* pwcsNewName) /* [in] */
1787 {
1788 StorageBaseImpl *This = impl_from_IStorage(iface);
1789 DirEntry currentEntry;
1790 DirRef currentEntryRef;
1791
1792 TRACE("(%p, %s, %s)\n",
1793 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1794
1795 if (This->reverted)
1796 return STG_E_REVERTED;
1797
1798 currentEntryRef = findElement(This,
1799 This->storageDirEntry,
1800 pwcsNewName,
1801 &currentEntry);
1802
1803 if (currentEntryRef != DIRENTRY_NULL)
1804 {
1805 /*
1806 * There is already an element with the new name
1807 */
1808 return STG_E_FILEALREADYEXISTS;
1809 }
1810
1811 /*
1812 * Search for the old element name
1813 */
1814 currentEntryRef = findElement(This,
1815 This->storageDirEntry,
1816 pwcsOldName,
1817 &currentEntry);
1818
1819 if (currentEntryRef != DIRENTRY_NULL)
1820 {
1821 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1822 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1823 {
1824 WARN("Element is already open; cannot rename.\n");
1825 return STG_E_ACCESSDENIED;
1826 }
1827
1828 /* Remove the element from its current position in the tree */
1829 removeFromTree(This, This->storageDirEntry,
1830 currentEntryRef);
1831
1832 /* Change the name of the element */
1833 strcpyW(currentEntry.name, pwcsNewName);
1834
1835 /* Delete any sibling links */
1836 currentEntry.leftChild = DIRENTRY_NULL;
1837 currentEntry.rightChild = DIRENTRY_NULL;
1838
1839 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1840 &currentEntry);
1841
1842 /* Insert the element in a new position in the tree */
1843 insertIntoTree(This, This->storageDirEntry,
1844 currentEntryRef);
1845 }
1846 else
1847 {
1848 /*
1849 * There is no element with the old name
1850 */
1851 return STG_E_FILENOTFOUND;
1852 }
1853
1854 return StorageBaseImpl_Flush(This);
1855 }
1856
1857 /************************************************************************
1858 * StorageBaseImpl_CreateStream (IStorage)
1859 *
1860 * This method will create a stream object within this storage
1861 *
1862 * See Windows documentation for more details on IStorage methods.
1863 */
1864 static HRESULT WINAPI StorageBaseImpl_CreateStream(
1865 IStorage* iface,
1866 const OLECHAR* pwcsName, /* [string][in] */
1867 DWORD grfMode, /* [in] */
1868 DWORD reserved1, /* [in] */
1869 DWORD reserved2, /* [in] */
1870 IStream** ppstm) /* [out] */
1871 {
1872 StorageBaseImpl *This = impl_from_IStorage(iface);
1873 StgStreamImpl* newStream;
1874 DirEntry currentEntry, newStreamEntry;
1875 DirRef currentEntryRef, newStreamEntryRef;
1876 HRESULT hr;
1877
1878 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1879 iface, debugstr_w(pwcsName), grfMode,
1880 reserved1, reserved2, ppstm);
1881
1882 if (ppstm == 0)
1883 return STG_E_INVALIDPOINTER;
1884
1885 if (pwcsName == 0)
1886 return STG_E_INVALIDNAME;
1887
1888 if (reserved1 || reserved2)
1889 return STG_E_INVALIDPARAMETER;
1890
1891 if ( FAILED( validateSTGM(grfMode) ))
1892 return STG_E_INVALIDFLAG;
1893
1894 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1895 return STG_E_INVALIDFLAG;
1896
1897 if (This->reverted)
1898 return STG_E_REVERTED;
1899
1900 /*
1901 * As documented.
1902 */
1903 if ((grfMode & STGM_DELETEONRELEASE) ||
1904 (grfMode & STGM_TRANSACTED))
1905 return STG_E_INVALIDFUNCTION;
1906
1907 /*
1908 * Don't worry about permissions in transacted mode, as we can always write
1909 * changes; we just can't always commit them.
1910 */
1911 if(!(This->openFlags & STGM_TRANSACTED)) {
1912 /* Can't create a stream on read-only storage */
1913 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1914 return STG_E_ACCESSDENIED;
1915
1916 /* Can't create a stream with greater access than the parent. */
1917 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1918 return STG_E_ACCESSDENIED;
1919 }
1920
1921 if(This->openFlags & STGM_SIMPLE)
1922 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1923
1924 *ppstm = 0;
1925
1926 currentEntryRef = findElement(This,
1927 This->storageDirEntry,
1928 pwcsName,
1929 &currentEntry);
1930
1931 if (currentEntryRef != DIRENTRY_NULL)
1932 {
1933 /*
1934 * An element with this name already exists
1935 */
1936 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1937 {
1938 IStorage_DestroyElement(iface, pwcsName);
1939 }
1940 else
1941 return STG_E_FILEALREADYEXISTS;
1942 }
1943
1944 /*
1945 * memset the empty entry
1946 */
1947 memset(&newStreamEntry, 0, sizeof(DirEntry));
1948
1949 newStreamEntry.sizeOfNameString =
1950 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1951
1952 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1953 return STG_E_INVALIDNAME;
1954
1955 strcpyW(newStreamEntry.name, pwcsName);
1956
1957 newStreamEntry.stgType = STGTY_STREAM;
1958 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
1959 newStreamEntry.size.u.LowPart = 0;
1960 newStreamEntry.size.u.HighPart = 0;
1961
1962 newStreamEntry.leftChild = DIRENTRY_NULL;
1963 newStreamEntry.rightChild = DIRENTRY_NULL;
1964 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
1965
1966 /* call CoFileTime to get the current time
1967 newStreamEntry.ctime
1968 newStreamEntry.mtime
1969 */
1970
1971 /* newStreamEntry.clsid */
1972
1973 /*
1974 * Create an entry with the new data
1975 */
1976 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
1977 if (FAILED(hr))
1978 return hr;
1979
1980 /*
1981 * Insert the new entry in the parent storage's tree.
1982 */
1983 hr = insertIntoTree(
1984 This,
1985 This->storageDirEntry,
1986 newStreamEntryRef);
1987 if (FAILED(hr))
1988 {
1989 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1990 return hr;
1991 }
1992
1993 /*
1994 * Open the stream to return it.
1995 */
1996 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1997
1998 if (newStream)
1999 {
2000 *ppstm = &newStream->IStream_iface;
2001 IStream_AddRef(*ppstm);
2002 }
2003 else
2004 {
2005 return STG_E_INSUFFICIENTMEMORY;
2006 }
2007
2008 return StorageBaseImpl_Flush(This);
2009 }
2010
2011 /************************************************************************
2012 * StorageBaseImpl_SetClass (IStorage)
2013 *
2014 * This method will write the specified CLSID in the directory entry of this
2015 * storage.
2016 *
2017 * See Windows documentation for more details on IStorage methods.
2018 */
2019 static HRESULT WINAPI StorageBaseImpl_SetClass(
2020 IStorage* iface,
2021 REFCLSID clsid) /* [in] */
2022 {
2023 StorageBaseImpl *This = impl_from_IStorage(iface);
2024 HRESULT hRes;
2025 DirEntry currentEntry;
2026
2027 TRACE("(%p, %p)\n", iface, clsid);
2028
2029 if (This->reverted)
2030 return STG_E_REVERTED;
2031
2032 hRes = StorageBaseImpl_ReadDirEntry(This,
2033 This->storageDirEntry,
2034 &currentEntry);
2035 if (SUCCEEDED(hRes))
2036 {
2037 currentEntry.clsid = *clsid;
2038
2039 hRes = StorageBaseImpl_WriteDirEntry(This,
2040 This->storageDirEntry,
2041 &currentEntry);
2042 }
2043
2044 if (SUCCEEDED(hRes))
2045 hRes = StorageBaseImpl_Flush(This);
2046
2047 return hRes;
2048 }
2049
2050 /************************************************************************
2051 * StorageBaseImpl_CreateStorage (IStorage)
2052 *
2053 * This method will create the storage object within the provided storage.
2054 *
2055 * See Windows documentation for more details on IStorage methods.
2056 */
2057 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
2058 IStorage* iface,
2059 const OLECHAR *pwcsName, /* [string][in] */
2060 DWORD grfMode, /* [in] */
2061 DWORD reserved1, /* [in] */
2062 DWORD reserved2, /* [in] */
2063 IStorage **ppstg) /* [out] */
2064 {
2065 StorageBaseImpl* This = impl_from_IStorage(iface);
2066
2067 DirEntry currentEntry;
2068 DirEntry newEntry;
2069 DirRef currentEntryRef;
2070 DirRef newEntryRef;
2071 HRESULT hr;
2072
2073 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2074 iface, debugstr_w(pwcsName), grfMode,
2075 reserved1, reserved2, ppstg);
2076
2077 if (ppstg == 0)
2078 return STG_E_INVALIDPOINTER;
2079
2080 if (This->openFlags & STGM_SIMPLE)
2081 {
2082 return STG_E_INVALIDFUNCTION;
2083 }
2084
2085 if (pwcsName == 0)
2086 return STG_E_INVALIDNAME;
2087
2088 *ppstg = NULL;
2089
2090 if ( FAILED( validateSTGM(grfMode) ) ||
2091 (grfMode & STGM_DELETEONRELEASE) )
2092 {
2093 WARN("bad grfMode: 0x%x\n", grfMode);
2094 return STG_E_INVALIDFLAG;
2095 }
2096
2097 if (This->reverted)
2098 return STG_E_REVERTED;
2099
2100 /*
2101 * Check that we're compatible with the parent's storage mode
2102 */
2103 if ( !(This->openFlags & STGM_TRANSACTED) &&
2104 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2105 {
2106 WARN("access denied\n");
2107 return STG_E_ACCESSDENIED;
2108 }
2109
2110 currentEntryRef = findElement(This,
2111 This->storageDirEntry,
2112 pwcsName,
2113 &currentEntry);
2114
2115 if (currentEntryRef != DIRENTRY_NULL)
2116 {
2117 /*
2118 * An element with this name already exists
2119 */
2120 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2121 ((This->openFlags & STGM_TRANSACTED) ||
2122 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2123 {
2124 hr = IStorage_DestroyElement(iface, pwcsName);
2125 if (FAILED(hr))
2126 return hr;
2127 }
2128 else
2129 {
2130 WARN("file already exists\n");
2131 return STG_E_FILEALREADYEXISTS;
2132 }
2133 }
2134 else if (!(This->openFlags & STGM_TRANSACTED) &&
2135 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2136 {
2137 WARN("read-only storage\n");
2138 return STG_E_ACCESSDENIED;
2139 }
2140
2141 memset(&newEntry, 0, sizeof(DirEntry));
2142
2143 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2144
2145 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2146 {
2147 FIXME("name too long\n");
2148 return STG_E_INVALIDNAME;
2149 }
2150
2151 strcpyW(newEntry.name, pwcsName);
2152
2153 newEntry.stgType = STGTY_STORAGE;
2154 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2155 newEntry.size.u.LowPart = 0;
2156 newEntry.size.u.HighPart = 0;
2157
2158 newEntry.leftChild = DIRENTRY_NULL;
2159 newEntry.rightChild = DIRENTRY_NULL;
2160 newEntry.dirRootEntry = DIRENTRY_NULL;
2161
2162 /* call CoFileTime to get the current time
2163 newEntry.ctime
2164 newEntry.mtime
2165 */
2166
2167 /* newEntry.clsid */
2168
2169 /*
2170 * Create a new directory entry for the storage
2171 */
2172 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2173 if (FAILED(hr))
2174 return hr;
2175
2176 /*
2177 * Insert the new directory entry into the parent storage's tree
2178 */
2179 hr = insertIntoTree(
2180 This,
2181 This->storageDirEntry,
2182 newEntryRef);
2183 if (FAILED(hr))
2184 {
2185 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2186 return hr;
2187 }
2188
2189 /*
2190 * Open it to get a pointer to return.
2191 */
2192 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2193
2194 if( (hr != S_OK) || (*ppstg == NULL))
2195 {
2196 return hr;
2197 }
2198
2199 if (SUCCEEDED(hr))
2200 hr = StorageBaseImpl_Flush(This);
2201
2202 return S_OK;
2203 }
2204
2205 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
2206 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2207 SNB snbExclude, IStorage *pstgDest)
2208 {
2209 DirEntry data;
2210 HRESULT hr;
2211
2212 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2213
2214 if (SUCCEEDED(hr))
2215 hr = IStorage_SetClass( pstgDest, &data.clsid );
2216
2217 if (SUCCEEDED(hr))
2218 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2219 skip_stream, snbExclude, pstgDest );
2220
2221 return hr;
2222 }
2223
2224 /*************************************************************************
2225 * CopyTo (IStorage)
2226 */
2227 static HRESULT WINAPI StorageBaseImpl_CopyTo(
2228 IStorage* iface,
2229 DWORD ciidExclude, /* [in] */
2230 const IID* rgiidExclude, /* [size_is][unique][in] */
2231 SNB snbExclude, /* [unique][in] */
2232 IStorage* pstgDest) /* [unique][in] */
2233 {
2234 StorageBaseImpl *This = impl_from_IStorage(iface);
2235
2236 BOOL skip_storage = FALSE, skip_stream = FALSE;
2237 DWORD i;
2238
2239 TRACE("(%p, %d, %p, %p, %p)\n",
2240 iface, ciidExclude, rgiidExclude,
2241 snbExclude, pstgDest);
2242
2243 if ( pstgDest == 0 )
2244 return STG_E_INVALIDPOINTER;
2245
2246 for(i = 0; i < ciidExclude; ++i)
2247 {
2248 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2249 skip_storage = TRUE;
2250 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2251 skip_stream = TRUE;
2252 else
2253 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2254 }
2255
2256 if (!skip_storage)
2257 {
2258 /* Give up early if it looks like this would be infinitely recursive.
2259 * Oddly enough, this includes some cases that aren't really recursive, like
2260 * copying to a transacted child. */
2261 IStorage *pstgDestAncestor = pstgDest;
2262 IStorage *pstgDestAncestorChild = NULL;
2263
2264 /* Go up the chain from the destination until we find the source storage. */
2265 while (pstgDestAncestor != iface) {
2266 pstgDestAncestorChild = pstgDest;
2267
2268 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2269 {
2270 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2271
2272 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2273 }
2274 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2275 {
2276 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2277
2278 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2279 }
2280 else
2281 break;
2282 }
2283
2284 if (pstgDestAncestor == iface)
2285 {
2286 BOOL fail = TRUE;
2287
2288 if (pstgDestAncestorChild && snbExclude)
2289 {
2290 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2291 DirEntry data;
2292 WCHAR **snb = snbExclude;
2293
2294 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2295
2296 while ( *snb != NULL && fail )
2297 {
2298 if ( lstrcmpW(data.name, *snb) == 0 )
2299 fail = FALSE;
2300 ++snb;
2301 }
2302 }
2303
2304 if (fail)
2305 return STG_E_ACCESSDENIED;
2306 }
2307 }
2308
2309 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2310 skip_storage, skip_stream, snbExclude, pstgDest );
2311 }
2312
2313 /*************************************************************************
2314 * MoveElementTo (IStorage)
2315 */
2316 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2317 IStorage* iface,
2318 const OLECHAR *pwcsName, /* [string][in] */
2319 IStorage *pstgDest, /* [unique][in] */
2320 const OLECHAR *pwcsNewName,/* [string][in] */
2321 DWORD grfFlags) /* [in] */
2322 {
2323 FIXME("(%p %s %p %s %u): stub\n", iface,
2324 debugstr_w(pwcsName), pstgDest,
2325 debugstr_w(pwcsNewName), grfFlags);
2326 return E_NOTIMPL;
2327 }
2328
2329 /*************************************************************************
2330 * Commit (IStorage)
2331 *
2332 * Ensures that any changes made to a storage object open in transacted mode
2333 * are reflected in the parent storage
2334 *
2335 * In a non-transacted mode, this ensures all cached writes are completed.
2336 */
2337 static HRESULT WINAPI StorageBaseImpl_Commit(
2338 IStorage* iface,
2339 DWORD grfCommitFlags)/* [in] */
2340 {
2341 StorageBaseImpl* This = impl_from_IStorage(iface);
2342 TRACE("(%p %d)\n", iface, grfCommitFlags);
2343 return StorageBaseImpl_Flush(This);
2344 }
2345
2346 /*************************************************************************
2347 * Revert (IStorage)
2348 *
2349 * Discard all changes that have been made since the last commit operation
2350 */
2351 static HRESULT WINAPI StorageBaseImpl_Revert(
2352 IStorage* iface)
2353 {
2354 TRACE("(%p)\n", iface);
2355 return S_OK;
2356 }
2357
2358 /*********************************************************************
2359 *
2360 * Internal helper function for StorageBaseImpl_DestroyElement()
2361 *
2362 * Delete the contents of a storage entry.
2363 *
2364 */
2365 static HRESULT deleteStorageContents(
2366 StorageBaseImpl *parentStorage,
2367 DirRef indexToDelete,
2368 DirEntry entryDataToDelete)
2369 {
2370 IEnumSTATSTG *elements = 0;
2371 IStorage *childStorage = 0;
2372 STATSTG currentElement;
2373 HRESULT hr;
2374 HRESULT destroyHr = S_OK;
2375 StorageInternalImpl *stg, *stg2;
2376
2377 /* Invalidate any open storage objects. */
2378 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2379 {
2380 if (stg->base.storageDirEntry == indexToDelete)
2381 {
2382 StorageBaseImpl_Invalidate(&stg->base);
2383 }
2384 }
2385
2386 /*
2387 * Open the storage and enumerate it
2388 */
2389 hr = IStorage_OpenStorage(
2390 &parentStorage->IStorage_iface,
2391 entryDataToDelete.name,
2392 0,
2393 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2394 0,
2395 0,
2396 &childStorage);
2397
2398 if (hr != S_OK)
2399 {
2400 return hr;
2401 }
2402
2403 /*
2404 * Enumerate the elements
2405 */
2406 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2407 if (FAILED(hr))
2408 {
2409 IStorage_Release(childStorage);
2410 return hr;
2411 }
2412
2413 do
2414 {
2415 /*
2416 * Obtain the next element
2417 */
2418 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2419 if (hr==S_OK)
2420 {
2421 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2422
2423 CoTaskMemFree(currentElement.pwcsName);
2424 }
2425
2426 /*
2427 * We need to Reset the enumeration every time because we delete elements
2428 * and the enumeration could be invalid
2429 */
2430 IEnumSTATSTG_Reset(elements);
2431
2432 } while ((hr == S_OK) && (destroyHr == S_OK));
2433
2434 IStorage_Release(childStorage);
2435 IEnumSTATSTG_Release(elements);
2436
2437 return destroyHr;
2438 }
2439
2440 /*********************************************************************
2441 *
2442 * Internal helper function for StorageBaseImpl_DestroyElement()
2443 *
2444 * Perform the deletion of a stream's data
2445 *
2446 */
2447 static HRESULT deleteStreamContents(
2448 StorageBaseImpl *parentStorage,
2449 DirRef indexToDelete,
2450 DirEntry entryDataToDelete)
2451 {
2452 IStream *pis;
2453 HRESULT hr;
2454 ULARGE_INTEGER size;
2455 StgStreamImpl *strm, *strm2;
2456
2457 /* Invalidate any open stream objects. */
2458 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2459 {
2460 if (strm->dirEntry == indexToDelete)
2461 {
2462 TRACE("Stream deleted %p\n", strm);
2463 strm->parentStorage = NULL;
2464 list_remove(&strm->StrmListEntry);
2465 }
2466 }
2467
2468 size.u.HighPart = 0;
2469 size.u.LowPart = 0;
2470
2471 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2472 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2473
2474 if (hr!=S_OK)
2475 {
2476 return(hr);
2477 }
2478
2479 /*
2480 * Zap the stream
2481 */
2482 hr = IStream_SetSize(pis, size);
2483
2484 if(hr != S_OK)
2485 {
2486 return hr;
2487 }
2488
2489 /*
2490 * Release the stream object.
2491 */
2492 IStream_Release(pis);
2493
2494 return S_OK;
2495 }
2496
2497 /*************************************************************************
2498 * DestroyElement (IStorage)
2499 *
2500 * Strategy: This implementation is built this way for simplicity not for speed.
2501 * I always delete the topmost element of the enumeration and adjust
2502 * the deleted element pointer all the time. This takes longer to
2503 * do but allow to reinvoke DestroyElement whenever we encounter a
2504 * storage object. The optimisation resides in the usage of another
2505 * enumeration strategy that would give all the leaves of a storage
2506 * first. (postfix order)
2507 */
2508 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2509 IStorage* iface,
2510 const OLECHAR *pwcsName)/* [string][in] */
2511 {
2512 StorageBaseImpl *This = impl_from_IStorage(iface);
2513
2514 HRESULT hr = S_OK;
2515 DirEntry entryToDelete;
2516 DirRef entryToDeleteRef;
2517
2518 TRACE("(%p, %s)\n",
2519 iface, debugstr_w(pwcsName));
2520
2521 if (pwcsName==NULL)
2522 return STG_E_INVALIDPOINTER;
2523
2524 if (This->reverted)
2525 return STG_E_REVERTED;
2526
2527 if ( !(This->openFlags & STGM_TRANSACTED) &&
2528 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2529 return STG_E_ACCESSDENIED;
2530
2531 entryToDeleteRef = findElement(
2532 This,
2533 This->storageDirEntry,
2534 pwcsName,
2535 &entryToDelete);
2536
2537 if ( entryToDeleteRef == DIRENTRY_NULL )
2538 {
2539 return STG_E_FILENOTFOUND;
2540 }
2541
2542 if ( entryToDelete.stgType == STGTY_STORAGE )
2543 {
2544 hr = deleteStorageContents(
2545 This,
2546 entryToDeleteRef,
2547 entryToDelete);
2548 }
2549 else if ( entryToDelete.stgType == STGTY_STREAM )
2550 {
2551 hr = deleteStreamContents(
2552 This,
2553 entryToDeleteRef,
2554 entryToDelete);
2555 }
2556
2557 if (hr!=S_OK)
2558 return hr;
2559
2560 /*
2561 * Remove the entry from its parent storage
2562 */
2563 hr = removeFromTree(
2564 This,
2565 This->storageDirEntry,
2566 entryToDeleteRef);
2567
2568 /*
2569 * Invalidate the entry
2570 */
2571 if (SUCCEEDED(hr))
2572 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2573
2574 if (SUCCEEDED(hr))
2575 hr = StorageBaseImpl_Flush(This);
2576
2577 return hr;
2578 }
2579
2580 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2581 {
2582 struct list *cur, *cur2;
2583 StgStreamImpl *strm=NULL;
2584 StorageInternalImpl *childstg=NULL;
2585
2586 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2587 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2588 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2589 strm->parentStorage = NULL;
2590 list_remove(cur);
2591 }
2592
2593 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2594 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2595 StorageBaseImpl_Invalidate( &childstg->base );
2596 }
2597
2598 if (stg->transactedChild)
2599 {
2600 StorageBaseImpl_Invalidate(stg->transactedChild);
2601
2602 stg->transactedChild = NULL;
2603 }
2604 }
2605
2606 /******************************************************************************
2607 * SetElementTimes (IStorage)
2608 */
2609 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2610 IStorage* iface,
2611 const OLECHAR *pwcsName,/* [string][in] */
2612 const FILETIME *pctime, /* [in] */
2613 const FILETIME *patime, /* [in] */
2614 const FILETIME *pmtime) /* [in] */
2615 {
2616 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2617 return S_OK;
2618 }
2619
2620 /******************************************************************************
2621 * SetStateBits (IStorage)
2622 */
2623 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2624 IStorage* iface,
2625 DWORD grfStateBits,/* [in] */
2626 DWORD grfMask) /* [in] */
2627 {
2628 StorageBaseImpl *This = impl_from_IStorage(iface);
2629
2630 if (This->reverted)
2631 return STG_E_REVERTED;
2632
2633 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2634 return S_OK;
2635 }
2636
2637 /******************************************************************************
2638 * Internal stream list handlers
2639 */
2640
2641 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2642 {
2643 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2644 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2645 }
2646
2647 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2648 {
2649 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2650 list_remove(&(strm->StrmListEntry));
2651 }
2652
2653 static HRESULT StorageBaseImpl_CopyStream(
2654 StorageBaseImpl *dst, DirRef dst_entry,
2655 StorageBaseImpl *src, DirRef src_entry)
2656 {
2657 HRESULT hr;
2658 BYTE data[4096];
2659 DirEntry srcdata;
2660 ULARGE_INTEGER bytes_copied;
2661 ULONG bytestocopy, bytesread, byteswritten;
2662
2663 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2664
2665 if (SUCCEEDED(hr))
2666 {
2667 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2668
2669 bytes_copied.QuadPart = 0;
2670 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2671 {
2672 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2673
2674 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2675 data, &bytesread);
2676 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2677
2678 if (SUCCEEDED(hr))
2679 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2680 data, &byteswritten);
2681 if (SUCCEEDED(hr))
2682 {
2683 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2684 bytes_copied.QuadPart += byteswritten;
2685 }
2686 }
2687 }
2688
2689 return hr;
2690 }
2691
2692 static HRESULT StorageBaseImpl_DupStorageTree(
2693 StorageBaseImpl *dst, DirRef *dst_entry,
2694 StorageBaseImpl *src, DirRef src_entry)
2695 {
2696 HRESULT hr;
2697 DirEntry data;
2698 BOOL has_stream=FALSE;
2699
2700 if (src_entry == DIRENTRY_NULL)
2701 {
2702 *dst_entry = DIRENTRY_NULL;
2703 return S_OK;
2704 }
2705
2706 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2707 if (SUCCEEDED(hr))
2708 {
2709 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2710 data.startingBlock = BLOCK_END_OF_CHAIN;
2711 data.size.QuadPart = 0;
2712
2713 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2714 }
2715
2716 if (SUCCEEDED(hr))
2717 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2718
2719 if (SUCCEEDED(hr))
2720 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2721
2722 if (SUCCEEDED(hr))
2723 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2724
2725 if (SUCCEEDED(hr) && has_stream)
2726 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2727
2728 return hr;
2729 }
2730
2731 static HRESULT StorageBaseImpl_CopyStorageTree(
2732 StorageBaseImpl *dst, DirRef dst_entry,
2733 StorageBaseImpl *src, DirRef src_entry)
2734 {
2735 HRESULT hr;
2736 DirEntry src_data, dst_data;
2737 DirRef new_root_entry;
2738
2739 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2740
2741 if (SUCCEEDED(hr))
2742 {
2743 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2744 }
2745
2746 if (SUCCEEDED(hr))
2747 {
2748 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
2749 dst_data.clsid = src_data.clsid;
2750 dst_data.ctime = src_data.ctime;
2751 dst_data.mtime = src_data.mtime;
2752 dst_data.dirRootEntry = new_root_entry;
2753 }
2754
2755 if (SUCCEEDED(hr))
2756 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2757
2758 return hr;
2759 }
2760
2761 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2762 {
2763 HRESULT hr;
2764 DirEntry data;
2765 ULARGE_INTEGER zero;
2766
2767 if (entry == DIRENTRY_NULL)
2768 return S_OK;
2769
2770 zero.QuadPart = 0;
2771
2772 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2773
2774 if (SUCCEEDED(hr) && include_siblings)
2775 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2776
2777 if (SUCCEEDED(hr) && include_siblings)
2778 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2779
2780 if (SUCCEEDED(hr))
2781 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2782
2783 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2784 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2785
2786 if (SUCCEEDED(hr))
2787 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2788
2789 return hr;
2790 }
2791
2792
2793 /************************************************************************
2794 * StorageImpl implementation
2795 ***********************************************************************/
2796
2797 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2798 ULARGE_INTEGER offset,
2799 void* buffer,
2800 ULONG size,
2801 ULONG* bytesRead)
2802 {
2803 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2804 }
2805
2806 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2807 ULARGE_INTEGER offset,
2808 const void* buffer,
2809 const ULONG size,
2810 ULONG* bytesWritten)
2811 {
2812 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2813 }
2814
2815 /******************************************************************************
2816 * StorageImpl_LoadFileHeader
2817 *
2818 * This method will read in the file header
2819 */
2820 static HRESULT StorageImpl_LoadFileHeader(
2821 StorageImpl* This)
2822 {
2823 HRESULT hr;
2824 BYTE headerBigBlock[HEADER_SIZE];
2825 int index;
2826 ULARGE_INTEGER offset;
2827 DWORD bytes_read;
2828
2829 TRACE("\n");
2830 /*
2831 * Get a pointer to the big block of data containing the header.
2832 */
2833 offset.u.HighPart = 0;
2834 offset.u.LowPart = 0;
2835 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2836 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2837 hr = STG_E_FILENOTFOUND;
2838
2839 /*
2840 * Extract the information from the header.
2841 */
2842 if (SUCCEEDED(hr))
2843 {
2844 /*
2845 * Check for the "magic number" signature and return an error if it is not
2846 * found.
2847 */
2848 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2849 {
2850 return STG_E_OLDFORMAT;
2851 }
2852
2853 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2854 {
2855 return STG_E_INVALIDHEADER;
2856 }
2857
2858 StorageUtl_ReadWord(
2859 headerBigBlock,
2860 OFFSET_BIGBLOCKSIZEBITS,
2861 &This->bigBlockSizeBits);
2862
2863 StorageUtl_ReadWord(
2864 headerBigBlock,
2865 OFFSET_SMALLBLOCKSIZEBITS,
2866 &This->smallBlockSizeBits);
2867
2868 StorageUtl_ReadDWord(
2869 headerBigBlock,
2870 OFFSET_BBDEPOTCOUNT,
2871 &This->bigBlockDepotCount);
2872
2873 StorageUtl_ReadDWord(
2874 headerBigBlock,
2875 OFFSET_ROOTSTARTBLOCK,
2876 &This->rootStartBlock);
2877
2878 StorageUtl_ReadDWord(
2879 headerBigBlock,
2880 OFFSET_TRANSACTIONSIG,
2881 &This->transactionSig);
2882
2883 StorageUtl_ReadDWord(
2884 headerBigBlock,
2885 OFFSET_SMALLBLOCKLIMIT,
2886 &This->smallBlockLimit);
2887
2888 StorageUtl_ReadDWord(
2889 headerBigBlock,
2890 OFFSET_SBDEPOTSTART,
2891 &This->smallBlockDepotStart);
2892
2893 StorageUtl_ReadDWord(
2894 headerBigBlock,
2895 OFFSET_EXTBBDEPOTSTART,
2896 &This->extBigBlockDepotStart);
2897
2898 StorageUtl_ReadDWord(
2899 headerBigBlock,
2900 OFFSET_EXTBBDEPOTCOUNT,
2901 &This->extBigBlockDepotCount);
2902
2903 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2904 {
2905 StorageUtl_ReadDWord(
2906 headerBigBlock,
2907 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2908 &(This->bigBlockDepotStart[index]));
2909 }
2910
2911 /*
2912 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2913 */
2914 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2915 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2916
2917 /*
2918 * Right now, the code is making some assumptions about the size of the
2919 * blocks, just make sure they are what we're expecting.
2920 */
2921 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2922 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2923 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2924 {
2925 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2926 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
2927 hr = STG_E_INVALIDHEADER;
2928 }
2929 else
2930 hr = S_OK;
2931 }
2932
2933 return hr;
2934 }
2935
2936 /******************************************************************************
2937 * StorageImpl_SaveFileHeader
2938 *
2939 * This method will save to the file the header
2940 */
2941 static void StorageImpl_SaveFileHeader(
2942 StorageImpl* This)
2943 {
2944 BYTE headerBigBlock[HEADER_SIZE];
2945 int index;
2946 HRESULT hr;
2947 ULARGE_INTEGER offset;
2948 DWORD bytes_read, bytes_written;
2949 DWORD major_version, dirsectorcount;
2950
2951 /*
2952 * Get a pointer to the big block of data containing the header.
2953 */
2954 offset.u.HighPart = 0;
2955 offset.u.LowPart = 0;
2956 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2957 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2958 hr = STG_E_FILENOTFOUND;
2959
2960 if (This->bigBlockSizeBits == 0x9)
2961 major_version = 3;
2962 else if (This->bigBlockSizeBits == 0xc)
2963 major_version = 4;
2964 else
2965 {
2966 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
2967 major_version = 4;
2968 }
2969
2970 /*
2971 * If the block read failed, the file is probably new.
2972 */
2973 if (FAILED(hr))
2974 {
2975 /*
2976 * Initialize for all unknown fields.
2977 */
2978 memset(headerBigBlock, 0, HEADER_SIZE);
2979
2980 /*
2981 * Initialize the magic number.
2982 */
2983 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2984 }
2985
2986 /*
2987 * Write the information to the header.
2988 */
2989 StorageUtl_WriteWord(
2990 headerBigBlock,
2991 OFFSET_MINORVERSION,
2992 0x3e);
2993
2994 StorageUtl_WriteWord(
2995 headerBigBlock,
2996 OFFSET_MAJORVERSION,
2997 major_version);
2998
2999 StorageUtl_WriteWord(
3000 headerBigBlock,
3001 OFFSET_BYTEORDERMARKER,
3002 (WORD)-2);
3003
3004 StorageUtl_WriteWord(
3005 headerBigBlock,
3006 OFFSET_BIGBLOCKSIZEBITS,
3007 This->bigBlockSizeBits);
3008
3009 StorageUtl_WriteWord(
3010 headerBigBlock,
3011 OFFSET_SMALLBLOCKSIZEBITS,
3012 This->smallBlockSizeBits);
3013
3014 if (major_version >= 4)
3015 {
3016 if (This->rootBlockChain)
3017 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3018 else
3019 /* This file is being created, and it will start out with one block. */
3020 dirsectorcount = 1;
3021 }
3022 else
3023 /* This field must be 0 in versions older than 4 */
3024 dirsectorcount = 0;
3025
3026 StorageUtl_WriteDWord(
3027 headerBigBlock,
3028 OFFSET_DIRSECTORCOUNT,
3029 dirsectorcount);
3030
3031 StorageUtl_WriteDWord(
3032 headerBigBlock,
3033 OFFSET_BBDEPOTCOUNT,
3034 This->bigBlockDepotCount);
3035
3036 StorageUtl_WriteDWord(
3037 headerBigBlock,
3038 OFFSET_ROOTSTARTBLOCK,
3039 This->rootStartBlock);
3040
3041 StorageUtl_WriteDWord(
3042 headerBigBlock,
3043 OFFSET_TRANSACTIONSIG,
3044 This->transactionSig);
3045
3046 StorageUtl_WriteDWord(
3047 headerBigBlock,
3048 OFFSET_SMALLBLOCKLIMIT,
3049 This->smallBlockLimit);
3050
3051 StorageUtl_WriteDWord(
3052 headerBigBlock,
3053 OFFSET_SBDEPOTSTART,
3054 This->smallBlockDepotStart);
3055
3056 StorageUtl_WriteDWord(
3057 headerBigBlock,
3058 OFFSET_SBDEPOTCOUNT,
3059 This->smallBlockDepotChain ?
3060 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3061
3062 StorageUtl_WriteDWord(
3063 headerBigBlock,
3064 OFFSET_EXTBBDEPOTSTART,
3065 This->extBigBlockDepotStart);
3066
3067 StorageUtl_WriteDWord(
3068 headerBigBlock,
3069 OFFSET_EXTBBDEPOTCOUNT,
3070 This->extBigBlockDepotCount);
3071
3072 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3073 {
3074 StorageUtl_WriteDWord(
3075 headerBigBlock,
3076 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3077 (This->bigBlockDepotStart[index]));
3078 }
3079
3080 /*
3081 * Write the big block back to the file.
3082 */
3083 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3084 }
3085
3086
3087 /************************************************************************
3088 * StorageImpl implementation : DirEntry methods
3089 ***********************************************************************/
3090
3091 /******************************************************************************
3092 * StorageImpl_ReadRawDirEntry
3093 *
3094 * This method will read the raw data from a directory entry in the file.
3095 *
3096 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3097 */
3098 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3099 {
3100 ULARGE_INTEGER offset;
3101 HRESULT hr;
3102 ULONG bytesRead;
3103
3104 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3105
3106 hr = BlockChainStream_ReadAt(
3107 This->rootBlockChain,
3108 offset,
3109 RAW_DIRENTRY_SIZE,
3110 buffer,
3111 &bytesRead);
3112
3113 if (bytesRead != RAW_DIRENTRY_SIZE)
3114 return STG_E_READFAULT;
3115
3116 return hr;
3117 }
3118
3119 /******************************************************************************
3120 * StorageImpl_WriteRawDirEntry
3121 *
3122 * This method will write the raw data from a directory entry in the file.
3123 *
3124 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3125 */
3126 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3127 {
3128 ULARGE_INTEGER offset;
3129 ULONG bytesRead;
3130
3131 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3132
3133 return BlockChainStream_WriteAt(
3134 This->rootBlockChain,
3135 offset,
3136 RAW_DIRENTRY_SIZE,
3137 buffer,
3138 &bytesRead);
3139 }
3140
3141 /***************************************************************************
3142 *
3143 * Internal Method
3144 *
3145 * Mark a directory entry in the file as free.
3146 */
3147 static HRESULT StorageImpl_DestroyDirEntry(
3148 StorageBaseImpl *base,
3149 DirRef index)
3150 {
3151 BYTE emptyData[RAW_DIRENTRY_SIZE];
3152 StorageImpl *storage = (StorageImpl*)base;
3153
3154 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3155
3156 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3157 }
3158
3159 /******************************************************************************
3160 * UpdateRawDirEntry
3161 *
3162 * Update raw directory entry data from the fields in newData.
3163 *
3164 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3165 */
3166 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3167 {
3168 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3169
3170 memcpy(
3171 buffer + OFFSET_PS_NAME,
3172 newData->name,
3173 DIRENTRY_NAME_BUFFER_LEN );
3174
3175 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3176
3177 StorageUtl_WriteWord(
3178 buffer,
3179 OFFSET_PS_NAMELENGTH,
3180 newData->sizeOfNameString);
3181
3182 StorageUtl_WriteDWord(
3183 buffer,
3184 OFFSET_PS_LEFTCHILD,
3185 newData->leftChild);
3186
3187 StorageUtl_WriteDWord(
3188 buffer,
3189 OFFSET_PS_RIGHTCHILD,
3190 newData->rightChild);
3191
3192 StorageUtl_WriteDWord(
3193 buffer,
3194 OFFSET_PS_DIRROOT,
3195 newData->dirRootEntry);
3196
3197 StorageUtl_WriteGUID(
3198 buffer,
3199 OFFSET_PS_GUID,
3200 &newData->clsid);
3201
3202 StorageUtl_WriteDWord(
3203 buffer,
3204 OFFSET_PS_CTIMELOW,
3205 newData->ctime.dwLowDateTime);
3206
3207 StorageUtl_WriteDWord(
3208 buffer,
3209 OFFSET_PS_CTIMEHIGH,
3210 newData->ctime.dwHighDateTime);
3211
3212 StorageUtl_WriteDWord(
3213 buffer,
3214 OFFSET_PS_MTIMELOW,
3215 newData->mtime.dwLowDateTime);
3216
3217 StorageUtl_WriteDWord(
3218 buffer,
3219 OFFSET_PS_MTIMEHIGH,
3220 newData->ctime.dwHighDateTime);
3221
3222 StorageUtl_WriteDWord(
3223 buffer,
3224 OFFSET_PS_STARTBLOCK,
3225 newData->startingBlock);
3226
3227 StorageUtl_WriteDWord(
3228 buffer,
3229 OFFSET_PS_SIZE,
3230 newData->size.u.LowPart);
3231
3232 StorageUtl_WriteDWord(
3233 buffer,
3234 OFFSET_PS_SIZE_HIGH,
3235 newData->size.u.HighPart);
3236 }
3237
3238 /***************************************************************************
3239 *
3240 * Internal Method
3241 *
3242 * Reserve a directory entry in the file and initialize it.
3243 */
3244 static HRESULT StorageImpl_CreateDirEntry(
3245 StorageBaseImpl *base,
3246 const DirEntry *newData,
3247 DirRef *index)
3248 {
3249 StorageImpl *storage = (StorageImpl*)base;
3250 ULONG currentEntryIndex = 0;
3251 ULONG newEntryIndex = DIRENTRY_NULL;
3252 HRESULT hr = S_OK;
3253 BYTE currentData[RAW_DIRENTRY_SIZE];
3254 WORD sizeOfNameString;
3255
3256 do
3257 {
3258 hr = StorageImpl_ReadRawDirEntry(storage,
3259 currentEntryIndex,
3260 currentData);
3261
3262 if (SUCCEEDED(hr))
3263 {
3264 StorageUtl_ReadWord(
3265 currentData,
3266 OFFSET_PS_NAMELENGTH,
3267 &sizeOfNameString);
3268
3269 if (sizeOfNameString == 0)
3270 {
3271 /*
3272 * The entry exists and is available, we found it.
3273 */
3274 newEntryIndex = currentEntryIndex;
3275 }
3276 }
3277 else
3278 {
3279 /*
3280 * We exhausted the directory entries, we will create more space below
3281 */
3282 newEntryIndex = currentEntryIndex;
3283 }
3284 currentEntryIndex++;
3285
3286 } while (newEntryIndex == DIRENTRY_NULL);
3287
3288 /*
3289 * grow the directory stream
3290 */
3291 if (FAILED(hr))
3292 {
3293 BYTE emptyData[RAW_DIRENTRY_SIZE];
3294 ULARGE_INTEGER newSize;
3295 ULONG entryIndex;
3296 ULONG lastEntry = 0;
3297 ULONG blockCount = 0;
3298
3299 /*
3300 * obtain the new count of blocks in the directory stream
3301 */
3302 blockCount = BlockChainStream_GetCount(
3303 storage->rootBlockChain)+1;
3304
3305 /*
3306 * initialize the size used by the directory stream
3307 */
3308 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3309
3310 /*
3311 * add a block to the directory stream
3312 */
3313 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3314
3315 /*
3316 * memset the empty entry in order to initialize the unused newly
3317 * created entries
3318 */
3319 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3320
3321 /*
3322 * initialize them
3323 */
3324 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3325
3326 for(
3327 entryIndex = newEntryIndex + 1;
3328 entryIndex < lastEntry;
3329 entryIndex++)
3330 {
3331 StorageImpl_WriteRawDirEntry(
3332 storage,
3333 entryIndex,
3334 emptyData);
3335 }
3336
3337 StorageImpl_SaveFileHeader(storage);
3338 }
3339
3340 UpdateRawDirEntry(currentData, newData);
3341
3342 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3343
3344 if (SUCCEEDED(hr))
3345 *index = newEntryIndex;
3346
3347 return hr;
3348 }
3349
3350 /******************************************************************************
3351 * StorageImpl_ReadDirEntry
3352 *
3353 * This method will read the specified directory entry.
3354 */
3355 static HRESULT StorageImpl_ReadDirEntry(
3356 StorageImpl* This,
3357 DirRef index,
3358 DirEntry* buffer)
3359 {
3360 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3361 HRESULT readRes;
3362
3363 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3364
3365 if (SUCCEEDED(readRes))
3366 {
3367 memset(buffer->name, 0, sizeof(buffer->name));
3368 memcpy(
3369 buffer->name,
3370 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3371 DIRENTRY_NAME_BUFFER_LEN );
3372 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3373
3374 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3375
3376 StorageUtl_ReadWord(
3377 currentEntry,
3378 OFFSET_PS_NAMELENGTH,
3379 &buffer->sizeOfNameString);
3380
3381 StorageUtl_ReadDWord(
3382 currentEntry,
3383 OFFSET_PS_LEFTCHILD,
3384 &buffer->leftChild);
3385
3386 StorageUtl_ReadDWord(
3387 currentEntry,
3388 OFFSET_PS_RIGHTCHILD,
3389 &buffer->rightChild);
3390
3391 StorageUtl_ReadDWord(
3392 currentEntry,
3393 OFFSET_PS_DIRROOT,
3394 &buffer->dirRootEntry);
3395
3396 StorageUtl_ReadGUID(
3397 currentEntry,
3398 OFFSET_PS_GUID,
3399 &buffer->clsid);
3400
3401 StorageUtl_ReadDWord(
3402 currentEntry,
3403 OFFSET_PS_CTIMELOW,
3404 &buffer->ctime.dwLowDateTime);
3405
3406 StorageUtl_ReadDWord(
3407 currentEntry,
3408 OFFSET_PS_CTIMEHIGH,
3409 &buffer->ctime.dwHighDateTime);
3410
3411 StorageUtl_ReadDWord(
3412 currentEntry,
3413 OFFSET_PS_MTIMELOW,
3414 &buffer->mtime.dwLowDateTime);
3415
3416 StorageUtl_ReadDWord(
3417 currentEntry,
3418 OFFSET_PS_MTIMEHIGH,
3419 &buffer->mtime.dwHighDateTime);
3420
3421 StorageUtl_ReadDWord(
3422 currentEntry,
3423 OFFSET_PS_STARTBLOCK,
3424 &buffer->startingBlock);
3425
3426 StorageUtl_ReadDWord(
3427 currentEntry,
3428 OFFSET_PS_SIZE,
3429 &buffer->size.u.LowPart);
3430
3431 StorageUtl_ReadDWord(
3432 currentEntry,
3433 OFFSET_PS_SIZE_HIGH,
3434 &buffer->size.u.HighPart);
3435 }
3436
3437 return readRes;
3438 }
3439
3440 /*********************************************************************
3441 * Write the specified directory entry to the file
3442 */
3443 static HRESULT StorageImpl_WriteDirEntry(
3444 StorageImpl* This,
3445 DirRef index,
3446 const DirEntry* buffer)
3447 {
3448 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3449
3450 UpdateRawDirEntry(currentEntry, buffer);
3451
3452 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3453 }
3454
3455
3456 /************************************************************************
3457 * StorageImpl implementation : Block methods
3458 ***********************************************************************/
3459
3460 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3461 {
3462 return (ULONGLONG)(index+1) * This->bigBlockSize;
3463 }
3464
3465 static HRESULT StorageImpl_ReadBigBlock(
3466 StorageImpl* This,
3467 ULONG blockIndex,
3468 void* buffer,
3469 ULONG* out_read)
3470 {
3471 ULARGE_INTEGER ulOffset;
3472 DWORD read=0;
3473 HRESULT hr;
3474
3475 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3476
3477 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3478
3479 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3480 {
3481 /* File ends during this block; fill the rest with 0's. */
3482 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3483 }
3484
3485 if (out_read) *out_read = read;
3486
3487 return hr;
3488 }
3489
3490 static BOOL StorageImpl_ReadDWordFromBigBlock(
3491 StorageImpl* This,
3492 ULONG blockIndex,
3493 ULONG offset,
3494 DWORD* value)
3495 {
3496 ULARGE_INTEGER ulOffset;
3497 DWORD read;
3498 DWORD tmp;
3499
3500 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3501 ulOffset.QuadPart += offset;
3502
3503 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3504 *value = lendian32toh(tmp);
3505 return (read == sizeof(DWORD));
3506 }
3507
3508 static BOOL StorageImpl_WriteBigBlock(
3509 StorageImpl* This,
3510 ULONG blockIndex,
3511 const void* buffer)
3512 {
3513 ULARGE_INTEGER ulOffset;
3514 DWORD wrote;
3515
3516 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3517
3518 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3519 return (wrote == This->bigBlockSize);
3520 }
3521
3522 static BOOL StorageImpl_WriteDWordToBigBlock(
3523 StorageImpl* This,
3524 ULONG blockIndex,
3525 ULONG offset,
3526 DWORD value)
3527 {
3528 ULARGE_INTEGER ulOffset;
3529 DWORD wrote;
3530
3531 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3532 ulOffset.QuadPart += offset;
3533
3534 value = htole32(value);
3535 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3536 return (wrote == sizeof(DWORD));
3537 }
3538
3539 /******************************************************************************
3540 * Storage32Impl_SmallBlocksToBigBlocks
3541 *
3542 * This method will convert a small block chain to a big block chain.
3543 * The small block chain will be destroyed.
3544 */
3545 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3546 StorageImpl* This,
3547 SmallBlockChainStream** ppsbChain)
3548 {
3549 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3550 ULARGE_INTEGER size, offset;
3551 ULONG cbRead, cbWritten;
3552 ULARGE_INTEGER cbTotalRead;
3553 DirRef streamEntryRef;
3554 HRESULT resWrite = S_OK;
3555 HRESULT resRead;
3556 DirEntry streamEntry;
3557 BYTE *buffer;
3558 BlockChainStream *bbTempChain = NULL;
3559 BlockChainStream *bigBlockChain = NULL;
3560
3561 /*
3562 * Create a temporary big block chain that doesn't have
3563 * an associated directory entry. This temporary chain will be
3564 * used to copy data from small blocks to big blocks.
3565 */
3566 bbTempChain = BlockChainStream_Construct(This,
3567 &bbHeadOfChain,
3568 DIRENTRY_NULL);
3569 if(!bbTempChain) return NULL;
3570 /*
3571 * Grow the big block chain.
3572 */
3573 size = SmallBlockChainStream_GetSize(*ppsbChain);
3574 BlockChainStream_SetSize(bbTempChain, size);
3575
3576 /*
3577 * Copy the contents of the small block chain to the big block chain
3578 * by small block size increments.
3579 */
3580 offset.u.LowPart = 0;
3581 offset.u.HighPart = 0;
3582 cbTotalRead.QuadPart = 0;
3583
3584 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3585 do
3586 {
3587 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3588 offset,
3589 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3590 buffer,
3591 &cbRead);
3592 if (FAILED(resRead))
3593 break;
3594
3595 if (cbRead > 0)
3596 {
3597 cbTotalRead.QuadPart += cbRead;
3598
3599 resWrite = BlockChainStream_WriteAt(bbTempChain,
3600 offset,
3601 cbRead,
3602 buffer,
3603 &cbWritten);
3604
3605 if (FAILED(resWrite))
3606 break;
3607
3608 offset.u.LowPart += cbRead;
3609 }
3610 else
3611 {
3612 resRead = STG_E_READFAULT;
3613 break;
3614 }
3615 } while (cbTotalRead.QuadPart < size.QuadPart);
3616 HeapFree(GetProcessHeap(),0,buffer);
3617
3618 size.u.HighPart = 0;
3619 size.u.LowPart = 0;
3620
3621 if (FAILED(resRead) || FAILED(resWrite))
3622 {
3623 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3624 BlockChainStream_SetSize(bbTempChain, size);
3625 BlockChainStream_Destroy(bbTempChain);
3626 return NULL;
3627 }
3628
3629 /*
3630 * Destroy the small block chain.
3631 */
3632 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3633 SmallBlockChainStream_SetSize(*ppsbChain, size);
3634 SmallBlockChainStream_Destroy(*ppsbChain);
3635 *ppsbChain = 0;
3636
3637 /*
3638 * Change the directory entry. This chain is now a big block chain
3639 * and it doesn't reside in the small blocks chain anymore.
3640 */
3641 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3642
3643 streamEntry.startingBlock = bbHeadOfChain;
3644
3645 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3646
3647 /*
3648 * Destroy the temporary entryless big block chain.
3649 * Create a new big block chain associated with this entry.
3650 */
3651 BlockChainStream_Destroy(bbTempChain);
3652 bigBlockChain = BlockChainStream_Construct(This,
3653 NULL,
3654 streamEntryRef);
3655
3656 return bigBlockChain;
3657 }
3658
3659 /******************************************************************************
3660 * Storage32Impl_BigBlocksToSmallBlocks
3661 *
3662 * This method will convert a big block chain to a small block chain.
3663 * The big block chain will be destroyed on success.
3664 */
3665 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3666 StorageImpl* This,
3667 BlockChainStream** ppbbChain,
3668 ULARGE_INTEGER newSize)
3669 {
3670 ULARGE_INTEGER size, offset, cbTotalRead;
3671 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3672 DirRef streamEntryRef;
3673 HRESULT resWrite = S_OK, resRead = S_OK;
3674 DirEntry streamEntry;
3675 BYTE* buffer;
3676 SmallBlockChainStream* sbTempChain;
3677
3678 TRACE("%p %p\n", This, ppbbChain);
3679
3680 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3681 DIRENTRY_NULL);
3682
3683 if(!sbTempChain)
3684 return NULL;
3685
3686 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3687 size = BlockChainStream_GetSize(*ppbbChain);
3688 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3689
3690 offset.u.HighPart = 0;
3691 offset.u.LowPart = 0;
3692 cbTotalRead.QuadPart = 0;
3693 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3694 while(cbTotalRead.QuadPart < size.QuadPart)
3695 {
3696 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3697 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3698 buffer, &cbRead);
3699
3700 if(FAILED(resRead))
3701 break;
3702
3703 if(cbRead > 0)
3704 {
3705 cbTotalRead.QuadPart += cbRead;
3706
3707 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3708 cbRead, buffer, &cbWritten);
3709
3710 if(FAILED(resWrite))
3711 break;
3712
3713 offset.u.LowPart += cbRead;
3714 }
3715 else
3716 {
3717 resRead = STG_E_READFAULT;
3718 break;
3719 }
3720 }
3721 HeapFree(GetProcessHeap(), 0, buffer);
3722
3723 size.u.HighPart = 0;
3724 size.u.LowPart = 0;
3725
3726 if(FAILED(resRead) || FAILED(resWrite))
3727 {
3728 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3729 SmallBlockChainStream_SetSize(sbTempChain, size);
3730 SmallBlockChainStream_Destroy(sbTempChain);
3731 return NULL;
3732 }
3733
3734 /* destroy the original big block chain */
3735 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3736 BlockChainStream_SetSize(*ppbbChain, size);
3737 BlockChainStream_Destroy(*ppbbChain);
3738 *ppbbChain = NULL;
3739
3740 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3741 streamEntry.startingBlock = sbHeadOfChain;
3742 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3743
3744 SmallBlockChainStream_Destroy(sbTempChain);
3745 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3746 }
3747
3748 /******************************************************************************
3749 * Storage32Impl_AddBlockDepot
3750 *
3751 * This will create a depot block, essentially it is a block initialized
3752 * to BLOCK_UNUSEDs.
3753 */
3754 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3755 {
3756 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3757 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3758 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3759 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3760
3761 /*
3762 * Initialize blocks as free
3763 */
3764 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3765
3766 /* Reserve the range lock sector */
3767 if (depotIndex == rangeLockDepot)
3768 {
3769 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3770 }
3771
3772 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3773 }
3774
3775 /******************************************************************************
3776 * Storage32Impl_GetExtDepotBlock
3777 *
3778 * Returns the index of the block that corresponds to the specified depot
3779 * index. This method is only for depot indexes equal or greater than
3780 * COUNT_BBDEPOTINHEADER.
3781 */
3782 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3783 {
3784 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3785 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3786 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3787 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3788 ULONG blockIndex = BLOCK_UNUSED;
3789 ULONG extBlockIndex;
3790 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3791 int index, num_blocks;
3792
3793 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3794
3795 if (extBlockCount >= This->extBigBlockDepotCount)
3796 return BLOCK_UNUSED;
3797
3798 if (This->indexExtBlockDepotCached != extBlockCount)
3799 {
3800 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3801
3802 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3803
3804 num_blocks = This->bigBlockSize / 4;
3805
3806 for (index = 0; index < num_blocks; index++)
3807 {
3808 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3809 This->extBlockDepotCached[index] = blockIndex;
3810 }
3811
3812 This->indexExtBlockDepotCached = extBlockCount;
3813 }
3814
3815 blockIndex = This->extBlockDepotCached[extBlockOffset];
3816
3817 return blockIndex;
3818 }
3819
3820 /******************************************************************************
3821 * Storage32Impl_SetExtDepotBlock
3822 *
3823 * Associates the specified block index to the specified depot index.
3824 * This method is only for depot indexes equal or greater than
3825 * COUNT_BBDEPOTINHEADER.
3826 */
3827 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3828 {
3829 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3830 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3831 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3832 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3833 ULONG extBlockIndex;
3834
3835 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3836
3837 assert(extBlockCount < This->extBigBlockDepotCount);
3838
3839 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3840
3841 if (extBlockIndex != BLOCK_UNUSED)
3842 {
3843 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3844 extBlockOffset * sizeof(ULONG),
3845 blockIndex);
3846 }
3847
3848 if (This->indexExtBlockDepotCached == extBlockCount)
3849 {
3850 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3851 }
3852 }
3853
3854 /******************************************************************************
3855 * Storage32Impl_AddExtBlockDepot
3856 *
3857 * Creates an extended depot block.
3858 */
3859 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3860 {
3861 ULONG numExtBlocks = This->extBigBlockDepotCount;
3862 ULONG nextExtBlock = This->extBigBlockDepotStart;
3863 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3864 ULONG index = BLOCK_UNUSED;
3865 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3866 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3867 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3868
3869 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3870 blocksPerDepotBlock;
3871
3872 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3873 {
3874 /*
3875 * The first extended block.
3876 */
3877 This->extBigBlockDepotStart = index;
3878 }
3879 else
3880 {
3881 /*
3882 * Find the last existing extended block.
3883 */
3884 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3885
3886 /*
3887 * Add the new extended block to the chain.
3888 */
3889 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3890 index);
3891 }
3892
3893 /*
3894 * Initialize this block.
3895 */
3896 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3897 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3898
3899 /* Add the block to our cache. */
3900 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3901 {
3902 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3903 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3904
3905 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3906 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3907
3908 This->extBigBlockDepotLocations = new_cache;
3909 This->extBigBlockDepotLocationsSize = new_cache_size;
3910 }
3911 This->extBigBlockDepotLocations[numExtBlocks] = index;
3912
3913 return index;
3914 }
3915
3916 /************************************************************************
3917 * StorageImpl_GetNextBlockInChain
3918 *
3919 * This method will retrieve the block index of the next big block in
3920 * in the chain.
3921 *
3922 * Params: This - Pointer to the Storage object.
3923 * blockIndex - Index of the block to retrieve the chain
3924 * for.
3925 * nextBlockIndex - receives the return value.
3926 *
3927 * Returns: This method returns the index of the next block in the chain.
3928 * It will return the constants:
3929 * BLOCK_SPECIAL - If the block given was not part of a
3930 * chain.
3931 * BLOCK_END_OF_CHAIN - If the block given was the last in
3932 * a chain.
3933 * BLOCK_UNUSED - If the block given was not past of a chain
3934 * and is available.
3935 * BLOCK_EXTBBDEPOT - This block is part of the extended
3936 * big block depot.
3937 *
3938 * See Windows documentation for more details on IStorage methods.
3939 */
3940 static HRESULT StorageImpl_GetNextBlockInChain(
3941 StorageImpl* This,
3942 ULONG blockIndex,
3943 ULONG* nextBlockIndex)
3944 {
3945 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3946 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3947 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3948 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3949 ULONG read;
3950 ULONG depotBlockIndexPos;
3951 int index, num_blocks;
3952
3953 *nextBlockIndex = BLOCK_SPECIAL;
3954
3955 if(depotBlockCount >= This->bigBlockDepotCount)
3956 {
3957 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3958 This->bigBlockDepotCount);
3959 return STG_E_READFAULT;
3960 }
3961
3962 /*
3963 * Cache the currently accessed depot block.
3964 */
3965 if (depotBlockCount != This->indexBlockDepotCached)
3966 {
3967 This->indexBlockDepotCached = depotBlockCount;
3968
3969 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3970 {
3971 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3972 }
3973 else
3974 {
3975 /*
3976 * We have to look in the extended depot.
3977 */
3978 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3979 }
3980
3981 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3982
3983 if (!read)
3984 return STG_E_READFAULT;
3985
3986 num_blocks = This->bigBlockSize / 4;
3987
3988 for (index = 0; index < num_blocks; index++)
3989 {
3990 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3991 This->blockDepotCached[index] = *nextBlockIndex;
3992 }
3993 }
3994
3995 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3996
3997 return S_OK;
3998 }
3999
4000 /******************************************************************************
4001 * Storage32Impl_GetNextExtendedBlock
4002 *
4003 * Given an extended block this method will return the next extended block.
4004 *
4005 * NOTES:
4006 * The last ULONG of an extended block is the block index of the next
4007 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4008 * depot.
4009 *
4010 * Return values:
4011 * - The index of the next extended block
4012 * - BLOCK_UNUSED: there is no next extended block.
4013 * - Any other return values denotes failure.
4014 */
4015 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4016 {
4017 ULONG nextBlockIndex = BLOCK_SPECIAL;
4018 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4019
4020 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4021 &nextBlockIndex);
4022
4023 return nextBlockIndex;
4024 }
4025
4026 /******************************************************************************
4027 * StorageImpl_SetNextBlockInChain
4028 *
4029 * This method will write the index of the specified block's next block
4030 * in the big block depot.
4031 *
4032 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4033 * do the following
4034 *
4035 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4036 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4037 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4038 *
4039 */
4040 static void StorageImpl_SetNextBlockInChain(
4041 StorageImpl* This,
4042 ULONG blockIndex,
4043 ULONG nextBlock)
4044 {
4045 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4046 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4047 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4048 ULONG depotBlockIndexPos;
4049
4050 assert(depotBlockCount < This->bigBlockDepotCount);
4051 assert(blockIndex != nextBlock);
4052
4053 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4054 /* This should never happen (storage file format spec forbids it), but
4055 * older versions of Wine may have generated broken files. We don't want to
4056 * assert and potentially lose data, but we do want to know if this ever
4057 * happens in a newly-created file. */
4058 ERR("Using range lock page\n");
4059
4060 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4061 {
4062 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4063 }
4064 else
4065 {
4066 /*
4067 * We have to look in the extended depot.
4068 */
4069 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4070 }
4071
4072 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4073 nextBlock);
4074 /*
4075 * Update the cached block depot, if necessary.
4076 */
4077 if (depotBlockCount == This->indexBlockDepotCached)
4078 {
4079 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4080 }
4081 }
4082
4083 /******************************************************************************
4084 * StorageImpl_GetNextFreeBigBlock
4085 *
4086 * Returns the index of the next free big block.
4087 * If the big block depot is filled, this method will enlarge it.
4088 *
4089 */
4090 static ULONG StorageImpl_GetNextFreeBigBlock(
4091 StorageImpl* This)
4092 {
4093 ULONG depotBlockIndexPos;
4094 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4095 ULONG depotBlockOffset;
4096 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4097 ULONG nextBlockIndex = BLOCK_SPECIAL;
4098 int depotIndex = 0;
4099 ULONG freeBlock = BLOCK_UNUSED;
4100 ULONG read;
4101 ULARGE_INTEGER neededSize;
4102 STATSTG statstg;
4103
4104 depotIndex = This->prevFreeBlock / blocksPerDepot;
4105 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4106
4107 /*
4108 * Scan the entire big block depot until we find a block marked free
4109 */
4110 while (nextBlockIndex != BLOCK_UNUSED)
4111 {
4112 if (depotIndex < COUNT_BBDEPOTINHEADER)
4113 {
4114 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4115
4116 /*
4117 * Grow the primary depot.
4118 */
4119 if (depotBlockIndexPos == BLOCK_UNUSED)
4120 {
4121 depotBlockIndexPos = depotIndex*blocksPerDepot;
4122
4123 /*
4124 * Add a block depot.
4125 */
4126 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4127 This->bigBlockDepotCount++;
4128 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4129
4130 /*
4131 * Flag it as a block depot.
4132 */
4133 StorageImpl_SetNextBlockInChain(This,
4134 depotBlockIndexPos,
4135 BLOCK_SPECIAL);
4136
4137 /* Save new header information.
4138 */
4139 StorageImpl_SaveFileHeader(This);
4140 }
4141 }
4142 else
4143 {
4144 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4145
4146 if (depotBlockIndexPos == BLOCK_UNUSED)
4147 {
4148 /*
4149 * Grow the extended depot.
4150 */
4151 ULONG extIndex = BLOCK_UNUSED;
4152 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4153 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4154
4155 if (extBlockOffset == 0)
4156 {
4157 /* We need an extended block.
4158 */
4159 extIndex = Storage32Impl_AddExtBlockDepot(This);
4160 This->extBigBlockDepotCount++;
4161 depotBlockIndexPos = extIndex + 1;
4162 }
4163 else
4164 depotBlockIndexPos = depotIndex * blocksPerDepot;
4165
4166 /*
4167 * Add a block depot and mark it in the extended block.
4168 */
4169 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4170 This->bigBlockDepotCount++;
4171 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4172
4173 /* Flag the block depot.
4174 */
4175 StorageImpl_SetNextBlockInChain(This,
4176 depotBlockIndexPos,
4177 BLOCK_SPECIAL);
4178
4179 /* If necessary, flag the extended depot block.
4180 */
4181 if (extIndex != BLOCK_UNUSED)
4182 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4183
4184 /* Save header information.
4185 */
4186 StorageImpl_SaveFileHeader(This);
4187 }
4188 }
4189
4190 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4191
4192 if (read)
4193 {
4194 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4195 ( nextBlockIndex != BLOCK_UNUSED))
4196 {
4197 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4198
4199 if (nextBlockIndex == BLOCK_UNUSED)
4200 {
4201 freeBlock = (depotIndex * blocksPerDepot) +
4202 (depotBlockOffset/sizeof(ULONG));
4203 }
4204
4205 depotBlockOffset += sizeof(ULONG);
4206 }
4207 }
4208
4209 depotIndex++;
4210 depotBlockOffset = 0;
4211 }
4212
4213 /*
4214 * make sure that the block physically exists before using it
4215 */
4216 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4217
4218 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4219
4220 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4221 ILockBytes_SetSize(This->lockBytes, neededSize);
4222
4223 This->prevFreeBlock = freeBlock;
4224
4225 return freeBlock;
4226 }
4227
4228 /******************************************************************************
4229 * StorageImpl_FreeBigBlock
4230 *
4231 * This method will flag the specified block as free in the big block depot.
4232 */
4233 static void StorageImpl_FreeBigBlock(
4234 StorageImpl* This,
4235 ULONG blockIndex)
4236 {
4237 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4238
4239 if (blockIndex < This->prevFreeBlock)
4240 This->prevFreeBlock = blockIndex;
4241 }
4242
4243
4244 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4245 DirRef index, const DirEntry *data)
4246 {
4247 StorageImpl *This = (StorageImpl*)base;
4248 return StorageImpl_WriteDirEntry(This, index, data);
4249 }
4250
4251 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4252 DirRef index, DirEntry *data)
4253 {
4254 StorageImpl *This = (StorageImpl*)base;
4255 return StorageImpl_ReadDirEntry(This, index, data);
4256 }
4257
4258 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
4259 {
4260 int i;
4261
4262 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4263 {
4264 if (!This->blockChainCache[i])
4265 {
4266 return &This->blockChainCache[i];
4267 }
4268 }
4269
4270 i = This->blockChainToEvict;
4271
4272 BlockChainStream_Destroy(This->blockChainCache[i]);
4273 This->blockChainCache[i] = NULL;
4274
4275 This->blockChainToEvict++;
4276 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4277 This->blockChainToEvict = 0;
4278
4279 return &This->blockChainCache[i];
4280 }
4281
4282 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
4283 DirRef index)
4284 {
4285 int i, free_index=-1;
4286
4287 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4288 {
4289 if (!This->blockChainCache[i])
4290 {
4291 if (free_index == -1) free_index = i;
4292 }
4293 else if (This->blockChainCache[i]->ownerDirEntry == index)
4294 {
4295 return &This->blockChainCache[i];
4296 }
4297 }
4298
4299 if (free_index == -1)
4300 {
4301 free_index = This->blockChainToEvict;
4302
4303 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4304 This->blockChainCache[free_index] = NULL;
4305
4306 This->blockChainToEvict++;
4307 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4308 This->blockChainToEvict = 0;
4309 }
4310
4311 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4312 return &This->blockChainCache[free_index];
4313 }
4314
4315 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4316 {
4317 int i;
4318
4319 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4320 {
4321 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4322 {
4323 BlockChainStream_Destroy(This->blockChainCache[i]);
4324 This->blockChainCache[i] = NULL;
4325 return;
4326 }
4327 }
4328 }
4329
4330 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4331 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4332 {
4333 StorageImpl *This = (StorageImpl*)base;
4334 DirEntry data;
4335 HRESULT hr;
4336 ULONG bytesToRead;
4337
4338 hr = StorageImpl_ReadDirEntry(This, index, &data);
4339 if (FAILED(hr)) return hr;
4340
4341 if (data.size.QuadPart == 0)
4342 {
4343 *bytesRead = 0;
4344 return S_OK;
4345 }
4346
4347 if (offset.QuadPart + size > data.size.QuadPart)
4348 {
4349 bytesToRead = data.size.QuadPart - offset.QuadPart;
4350 }
4351 else
4352 {
4353 bytesToRead = size;
4354 }
4355
4356 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4357 {
4358 SmallBlockChainStream *stream;
4359
4360 stream = SmallBlockChainStream_Construct(This, NULL, index);
4361 if (!stream) return E_OUTOFMEMORY;
4362
4363 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4364
4365 SmallBlockChainStream_Destroy(stream);
4366
4367 return hr;
4368 }
4369 else
4370 {
4371 BlockChainStream *stream = NULL;
4372
4373 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4374 if (!stream) return E_OUTOFMEMORY;
4375
4376 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4377
4378 return hr;
4379 }
4380 }
4381
4382 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4383 ULARGE_INTEGER newsize)
4384 {
4385 StorageImpl *This = (StorageImpl*)base;
4386 DirEntry data;
4387 HRESULT hr;
4388 SmallBlockChainStream *smallblock=NULL;
4389 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4390
4391 hr = StorageImpl_ReadDirEntry(This, index, &data);
4392 if (FAILED(hr)) return hr;
4393
4394 /* In simple mode keep the stream size above the small block limit */
4395 if (This->base.openFlags & STGM_SIMPLE)
4396 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4397
4398 if (data.size.QuadPart == newsize.QuadPart)
4399 return S_OK;
4400
4401 /* Create a block chain object of the appropriate type */
4402 if (data.size.QuadPart == 0)
4403 {
4404 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4405 {
4406 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4407 if (!smallblock) return E_OUTOFMEMORY;
4408 }
4409 else
4410 {
4411 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4412 bigblock = *pbigblock;
4413 if (!bigblock) return E_OUTOFMEMORY;
4414 }
4415 }
4416 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4417 {
4418 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4419 if (!smallblock) return E_OUTOFMEMORY;
4420 }
4421 else
4422 {
4423 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4424 bigblock = *pbigblock;
4425 if (!bigblock) return E_OUTOFMEMORY;
4426 }
4427
4428 /* Change the block chain type if necessary. */
4429 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4430 {
4431 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4432 if (!bigblock)
4433 {
4434 SmallBlockChainStream_Destroy(smallblock);
4435 return E_FAIL;
4436 }
4437
4438 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
4439 *pbigblock = bigblock;
4440 }
4441 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4442 {
4443 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4444 if (!smallblock)
4445 return E_FAIL;
4446 }
4447
4448 /* Set the size of the block chain. */
4449 if (smallblock)
4450 {
4451 SmallBlockChainStream_SetSize(smallblock, newsize);
4452 SmallBlockChainStream_Destroy(smallblock);
4453 }
4454 else
4455 {
4456 BlockChainStream_SetSize(bigblock, newsize);
4457 }
4458
4459 /* Set the size in the directory entry. */
4460 hr = StorageImpl_ReadDirEntry(This, index, &data);
4461 if (SUCCEEDED(hr))
4462 {
4463 data.size = newsize;
4464
4465 hr = StorageImpl_WriteDirEntry(This, index, &data);
4466 }
4467 return hr;
4468 }
4469
4470 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4471 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4472 {
4473 StorageImpl *This = (StorageImpl*)base;
4474 DirEntry data;
4475 HRESULT hr;
4476 ULARGE_INTEGER newSize;
4477
4478 hr = StorageImpl_ReadDirEntry(This, index, &data);
4479 if (FAILED(hr)) return hr;
4480
4481 /* Grow the stream if necessary */
4482 newSize.QuadPart = offset.QuadPart + size;
4483
4484 if (newSize.QuadPart > data.size.QuadPart)
4485 {
4486 hr = StorageImpl_StreamSetSize(base, index, newSize);
4487 if (FAILED(hr))
4488 return hr;
4489
4490 hr = StorageImpl_ReadDirEntry(This, index, &data);
4491 if (FAILED(hr)) return hr;
4492 }
4493
4494 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4495 {
4496 SmallBlockChainStream *stream;
4497
4498 stream = SmallBlockChainStream_Construct(This, NULL, index);
4499 if (!stream) return E_OUTOFMEMORY;
4500
4501 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4502
4503 SmallBlockChainStream_Destroy(stream);
4504
4505 return hr;
4506 }
4507 else
4508 {
4509 BlockChainStream *stream;
4510
4511 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4512 if (!stream) return E_OUTOFMEMORY;
4513
4514 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4515 }
4516 }
4517
4518 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4519 DirRef src)
4520 {
4521 StorageImpl *This = (StorageImpl*)base;
4522 DirEntry dst_data, src_data;
4523 HRESULT hr;
4524
4525 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4526
4527 if (SUCCEEDED(hr))
4528 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4529
4530 if (SUCCEEDED(hr))
4531 {
4532 StorageImpl_DeleteCachedBlockChainStream(This, src);
4533 dst_data.startingBlock = src_data.startingBlock;
4534 dst_data.size = src_data.size;
4535
4536 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4537 }
4538
4539 return hr;
4540 }
4541
4542 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4543 {
4544 HRESULT hr=S_OK;
4545 DirEntry currentEntry;
4546 DirRef currentEntryRef;
4547 BlockChainStream *blockChainStream;
4548
4549 if (create)
4550 {
4551 ULARGE_INTEGER size;
4552 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4553
4554 /* Discard any existing data. */
4555 size.QuadPart = 0;
4556 ILockBytes_SetSize(This->lockBytes, size);
4557
4558 /*
4559 * Initialize all header variables:
4560 * - The big block depot consists of one block and it is at block 0
4561 * - The directory table starts at block 1
4562 * - There is no small block depot
4563 */
4564 memset( This->bigBlockDepotStart,
4565 BLOCK_UNUSED,
4566 sizeof(This->bigBlockDepotStart));
4567
4568 This->bigBlockDepotCount = 1;
4569 This->bigBlockDepotStart[0] = 0;
4570 This->rootStartBlock = 1;
4571 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4572 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4573 if (This->bigBlockSize == 4096)
4574 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4575 else
4576 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4577 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4578 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4579 This->extBigBlockDepotCount = 0;
4580
4581 StorageImpl_SaveFileHeader(This);
4582
4583 /*
4584 * Add one block for the big block depot and one block for the directory table
4585 */
4586 size.u.HighPart = 0;
4587 size.u.LowPart = This->bigBlockSize * 3;
4588 ILockBytes_SetSize(This->lockBytes, size);
4589
4590 /*
4591 * Initialize the big block depot
4592 */
4593 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4594 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4595 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4596 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4597 }
4598 else
4599 {
4600 /*
4601 * Load the header for the file.
4602 */
4603 hr = StorageImpl_LoadFileHeader(This);
4604
4605 if (FAILED(hr))
4606 {
4607 return hr;
4608 }
4609 }
4610
4611 /*
4612 * There is no block depot cached yet.
4613 */
4614 This->indexBlockDepotCached = 0xFFFFFFFF;
4615 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4616
4617 /*
4618 * Start searching for free blocks with block 0.
4619 */
4620 This->prevFreeBlock = 0;
4621
4622 This->firstFreeSmallBlock = 0;
4623
4624 /* Read the extended big block depot locations. */
4625 if (This->extBigBlockDepotCount != 0)
4626 {
4627 ULONG current_block = This->extBigBlockDepotStart;
4628 ULONG cache_size = This->extBigBlockDepotCount * 2;
4629 ULONG i;
4630
4631 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4632 if (!This->extBigBlockDepotLocations)
4633 {
4634 return E_OUTOFMEMORY;
4635 }
4636
4637 This->extBigBlockDepotLocationsSize = cache_size;
4638
4639 for (i=0; i<This->extBigBlockDepotCount; i++)
4640 {
4641 if (current_block == BLOCK_END_OF_CHAIN)
4642 {
4643 WARN("File has too few extended big block depot blocks.\n");
4644 return STG_E_DOCFILECORRUPT;
4645 }
4646 This->extBigBlockDepotLocations[i] = current_block;
4647 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4648 }
4649 }
4650 else
4651 {
4652 This->extBigBlockDepotLocations = NULL;
4653 This->extBigBlockDepotLocationsSize = 0;
4654 }
4655
4656 /*
4657 * Create the block chain abstractions.
4658 */
4659 if(!(blockChainStream =
4660 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4661 {
4662 return STG_E_READFAULT;
4663 }
4664 if (!new_object)
4665 BlockChainStream_Destroy(This->rootBlockChain);
4666 This->rootBlockChain = blockChainStream;
4667
4668 if(!(blockChainStream =
4669 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4670 DIRENTRY_NULL)))
4671 {
4672 return STG_E_READFAULT;
4673 }
4674 if (!new_object)
4675 BlockChainStream_Destroy(This->smallBlockDepotChain);
4676 This->smallBlockDepotChain = blockChainStream;
4677
4678 /*
4679 * Write the root storage entry (memory only)
4680 */
4681 if (create)
4682 {
4683 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4684 DirEntry rootEntry;
4685 /*
4686 * Initialize the directory table
4687 */
4688 memset(&rootEntry, 0, sizeof(rootEntry));
4689 strcpyW(rootEntry.name, rootentryW);
4690 rootEntry.sizeOfNameString = sizeof(rootentryW);
4691 rootEntry.stgType = STGTY_ROOT;
4692 rootEntry.leftChild = DIRENTRY_NULL;
4693 rootEntry.rightChild = DIRENTRY_NULL;
4694 rootEntry.dirRootEntry = DIRENTRY_NULL;
4695 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4696 rootEntry.size.u.HighPart = 0;
4697 rootEntry.size.u.LowPart = 0;
4698
4699 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4700 }
4701
4702 /*
4703 * Find the ID of the root storage.
4704 */
4705 currentEntryRef = 0;
4706
4707 do
4708 {
4709 hr = StorageImpl_ReadDirEntry(
4710 This,
4711 currentEntryRef,
4712 &currentEntry);
4713
4714 if (SUCCEEDED(hr))
4715 {
4716 if ( (currentEntry.sizeOfNameString != 0 ) &&
4717 (currentEntry.stgType == STGTY_ROOT) )
4718 {
4719 This->base.storageDirEntry = currentEntryRef;
4720 }
4721 }
4722
4723 currentEntryRef++;
4724
4725 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4726
4727 if (FAILED(hr))
4728 {
4729 return STG_E_READFAULT;
4730 }
4731
4732 /*
4733 * Create the block chain abstraction for the small block root chain.
4734 */
4735 if(!(blockChainStream =
4736 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4737 {
4738 return STG_E_READFAULT;
4739 }
4740 if (!new_object)
4741 BlockChainStream_Destroy(This->smallBlockRootChain);
4742 This->smallBlockRootChain = blockChainStream;
4743
4744 if (!new_object)
4745 {
4746 int i;
4747 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4748 {
4749 BlockChainStream_Destroy(This->blockChainCache[i]);
4750 This->blockChainCache[i] = NULL;
4751 }
4752 }
4753
4754 return hr;
4755 }
4756
4757 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4758 ULONG* result, BOOL refresh)
4759 {
4760 StorageImpl *This = (StorageImpl*)base;
4761 HRESULT hr=S_OK;
4762 DWORD oldTransactionSig = This->transactionSig;
4763
4764 if (refresh)
4765 {
4766 ULARGE_INTEGER offset;
4767 ULONG bytes_read;
4768 BYTE data[4];
4769
4770 offset.u.HighPart = 0;
4771 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4772 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4773
4774 if (SUCCEEDED(hr))
4775 {
4776 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4777
4778 if (oldTransactionSig != This->transactionSig)
4779 {
4780 /* Someone else wrote to this, so toss all cached information. */
4781 TRACE("signature changed\n");
4782
4783 hr = StorageImpl_Refresh(This, FALSE, FALSE);
4784 }
4785
4786 if (FAILED(hr))
4787 This->transactionSig = oldTransactionSig;
4788 }
4789 }
4790
4791 *result = This->transactionSig;
4792
4793 return hr;
4794 }
4795
4796 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4797 ULONG value)
4798 {
4799 StorageImpl *This = (StorageImpl*)base;
4800
4801 This->transactionSig = value;
4802 StorageImpl_SaveFileHeader(This);
4803
4804 return S_OK;
4805 }
4806
4807 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4808 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4809 {
4810 if ((dwLockType & This->locks_supported) == 0)
4811 {
4812 if (supported) *supported = FALSE;
4813 return S_OK;
4814 }
4815
4816 if (supported) *supported = TRUE;
4817 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4818 }
4819
4820 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
4821 ULARGE_INTEGER cb, DWORD dwLockType)
4822 {
4823 if ((dwLockType & This->locks_supported) == 0)
4824 return S_OK;
4825
4826 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4827 }
4828
4829 /* Internal function */
4830 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4831 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4832 {
4833 HRESULT hr;
4834 int delay = 0;
4835 DWORD start_time = GetTickCount();
4836 DWORD last_sanity_check = start_time;
4837 ULARGE_INTEGER sanity_offset, sanity_cb;
4838
4839 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4840 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4841
4842 do
4843 {
4844 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4845
4846 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4847 {
4848 DWORD current_time = GetTickCount();
4849 if (current_time - start_time >= 20000)
4850 {
4851 /* timeout */
4852 break;
4853 }
4854 if (current_time - last_sanity_check >= 500)
4855 {
4856 /* Any storage implementation with the file open in a
4857 * shared mode should not lock these bytes for writing. However,
4858 * some programs (LibreOffice Writer) will keep ALL bytes locked
4859 * when opening in exclusive mode. We can use a read lock to
4860 * detect this case early, and not hang a full 20 seconds.
4861 *
4862 * This can collide with another attempt to open the file in
4863 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4864 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4865 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4866 break;
4867 if (SUCCEEDED(hr))
4868 {
4869 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4870 hr = STG_E_ACCESSDENIED;
4871 }
4872
4873 last_sanity_check = current_time;
4874 }
4875 Sleep(delay);
4876 if (delay < 150) delay++;
4877 }
4878 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4879
4880 return hr;
4881 }
4882
4883 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4884 {
4885 StorageImpl *This = (StorageImpl*)base;
4886 HRESULT hr;
4887 ULARGE_INTEGER offset, cb;
4888
4889 if (write)
4890 {
4891 /* Synchronous grab of second priority range, the commit lock, and the
4892 * lock-checking lock. */
4893 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4894 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4895 }
4896 else
4897 {
4898 offset.QuadPart = RANGELOCK_COMMIT;
4899 cb.QuadPart = 1;
4900 }
4901
4902 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4903
4904 return hr;
4905 }
4906
4907 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4908 {
4909 StorageImpl *This = (StorageImpl*)base;
4910 HRESULT hr;
4911 ULARGE_INTEGER offset, cb;
4912
4913 if (write)
4914 {
4915 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4916 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4917 }
4918 else
4919 {
4920 offset.QuadPart = RANGELOCK_COMMIT;
4921 cb.QuadPart = 1;
4922 }
4923
4924 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4925
4926 return hr;
4927 }
4928
4929 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4930 {
4931 StorageImpl *This = (StorageImpl*) iface;
4932 STATSTG statstg;
4933 HRESULT hr;
4934
4935 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4936
4937 *result = statstg.pwcsName;
4938
4939 return hr;
4940 }
4941
4942 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
4943 ULONG end, HRESULT fail_hr)
4944 {
4945 HRESULT hr;
4946 ULARGE_INTEGER offset, cb;
4947
4948 offset.QuadPart = start;
4949 cb.QuadPart = 1 + end - start;
4950
4951 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4952 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4953
4954 if (FAILED(hr))
4955 return fail_hr;
4956 else
4957 return S_OK;
4958 }
4959
4960 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
4961 {
4962 HRESULT hr=S_OK;
4963 int i, j;
4964 ULARGE_INTEGER offset, cb;
4965
4966 cb.QuadPart = 1;
4967
4968 for (i=start; i<=end; i++)
4969 {
4970 offset.QuadPart = i;
4971 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4972 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
4973 break;
4974 }
4975
4976 if (SUCCEEDED(hr))
4977 {
4978 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
4979 {
4980 if (This->locked_bytes[j] == 0)
4981 {
4982 This->locked_bytes[j] = i;
4983 break;
4984 }
4985 }
4986 }
4987
4988 return hr;
4989 }
4990
4991 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
4992 {
4993 HRESULT hr;
4994 ULARGE_INTEGER offset;
4995 ULARGE_INTEGER cb;
4996 DWORD share_mode = STGM_SHARE_MODE(openFlags);
4997 BOOL supported;
4998
4999 if (openFlags & STGM_NOSNAPSHOT)
5000 {
5001 /* STGM_NOSNAPSHOT implies deny write */
5002 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5003 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5004 }
5005
5006 /* Wrap all other locking inside a single lock so we can check ranges safely */
5007 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5008 cb.QuadPart = 1;
5009 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5010
5011 /* If the ILockBytes doesn't support locking that's ok. */
5012 if (!supported) return S_OK;
5013 else if (FAILED(hr)) return hr;
5014
5015 hr = S_OK;
5016
5017 /* First check for any conflicting locks. */
5018 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5019 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5020
5021 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5022 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5023
5024 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5025 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5026
5027 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5028 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5029
5030 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5031 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5032
5033 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5034 {
5035 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5036
5037 if (SUCCEEDED(hr))
5038 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5039 }
5040
5041 /* Then grab our locks. */
5042 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5043 {
5044 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5045 if (SUCCEEDED(hr))
5046 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5047 }
5048
5049 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5050 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5051
5052 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5053 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5054
5055 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5056 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5057
5058 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5059 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5060
5061 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5062 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5063
5064 offset.QuadPart = RANGELOCK_CHECKLOCKS;
5065 cb.QuadPart = 1;
5066 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5067
5068 return hr;
5069 }
5070
5071 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5072 {
5073 StorageImpl *This = (StorageImpl*)storage;
5074 int i;
5075 HRESULT hr;
5076 TRACE("(%p)\n", This);
5077
5078 hr = BlockChainStream_Flush(This->smallBlockRootChain);
5079
5080 if (SUCCEEDED(hr))
5081 hr = BlockChainStream_Flush(This->rootBlockChain);
5082
5083 if (SUCCEEDED(hr))
5084 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5085
5086 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5087 if (This->blockChainCache[i])
5088 hr = BlockChainStream_Flush(This->blockChainCache[i]);
5089
5090 if (SUCCEEDED(hr))
5091 hr = ILockBytes_Flush(This->lockBytes);
5092
5093 return hr;
5094 }
5095
5096 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5097 {
5098 StorageImpl *This = (StorageImpl*) iface;
5099
5100 StorageBaseImpl_DeleteAll(&This->base);
5101
5102 This->base.reverted = TRUE;
5103 }
5104
5105 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5106 {
5107 StorageImpl *This = (StorageImpl*) iface;
5108 int i;
5109 TRACE("(%p)\n", This);
5110
5111 StorageImpl_Flush(iface);
5112
5113 StorageImpl_Invalidate(iface);
5114
5115 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5116
5117 BlockChainStream_Destroy(This->smallBlockRootChain);
5118 BlockChainStream_Destroy(This->rootBlockChain);
5119 BlockChainStream_Destroy(This->smallBlockDepotChain);
5120
5121 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
5122 BlockChainStream_Destroy(This->blockChainCache[i]);
5123
5124 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
5125 {
5126 ULARGE_INTEGER offset, cb;
5127 cb.QuadPart = 1;
5128 if (This->locked_bytes[i] != 0)
5129 {
5130 offset.QuadPart = This->locked_bytes[i];
5131 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5132 }
5133 }
5134
5135 if (This->lockBytes)
5136 ILockBytes_Release(This->lockBytes);
5137 HeapFree(GetProcessHeap(), 0, This);
5138 }
5139
5140
5141 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5142 {
5143 StorageImpl_Destroy,
5144 StorageImpl_Invalidate,
5145 StorageImpl_Flush,
5146 StorageImpl_GetFilename,
5147 StorageImpl_CreateDirEntry,
5148 StorageImpl_BaseWriteDirEntry,
5149 StorageImpl_BaseReadDirEntry,
5150 StorageImpl_DestroyDirEntry,
5151 StorageImpl_StreamReadAt,
5152 StorageImpl_StreamWriteAt,
5153 StorageImpl_StreamSetSize,
5154 StorageImpl_StreamLink,
5155 StorageImpl_GetTransactionSig,
5156 StorageImpl_SetTransactionSig,
5157 StorageImpl_LockTransaction,
5158 StorageImpl_UnlockTransaction
5159 };
5160
5161
5162 /*
5163 * Virtual function table for the IStorageBaseImpl class.
5164 */
5165 static const IStorageVtbl StorageImpl_Vtbl =
5166 {
5167 StorageBaseImpl_QueryInterface,
5168 StorageBaseImpl_AddRef,
5169 StorageBaseImpl_Release,
5170 StorageBaseImpl_CreateStream,
5171 StorageBaseImpl_OpenStream,
5172 StorageBaseImpl_CreateStorage,
5173 StorageBaseImpl_OpenStorage,
5174 StorageBaseImpl_CopyTo,
5175 StorageBaseImpl_MoveElementTo,
5176 StorageBaseImpl_Commit,
5177 StorageBaseImpl_Revert,
5178 StorageBaseImpl_EnumElements,
5179 StorageBaseImpl_DestroyElement,
5180 StorageBaseImpl_RenameElement,
5181 StorageBaseImpl_SetElementTimes,
5182 StorageBaseImpl_SetClass,
5183 StorageBaseImpl_SetStateBits,
5184 StorageBaseImpl_Stat
5185 };
5186
5187 static HRESULT StorageImpl_Construct(
5188 HANDLE hFile,
5189 LPCOLESTR pwcsName,
5190 ILockBytes* pLkbyt,
5191 DWORD openFlags,
5192 BOOL fileBased,
5193 BOOL create,
5194 ULONG sector_size,
5195 StorageImpl** result)
5196 {
5197 StorageImpl* This;
5198 HRESULT hr = S_OK;
5199 STATSTG stat;
5200
5201 if ( FAILED( validateSTGM(openFlags) ))
5202 return STG_E_INVALIDFLAG;
5203
5204 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5205 if (!This)
5206 return E_OUTOFMEMORY;
5207
5208 memset(This, 0, sizeof(StorageImpl));
5209
5210 list_init(&This->base.strmHead);
5211
5212 list_init(&This->base.storageHead);
5213
5214 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5215 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5216 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5217 This->base.baseVtbl = &StorageImpl_BaseVtbl;
5218 This->base.openFlags = (openFlags & ~STGM_CREATE);
5219 This->base.ref = 1;
5220 This->base.create = create;
5221
5222 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5223 This->base.lockingrole = SWMR_Writer;
5224 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5225 This->base.lockingrole = SWMR_Reader;
5226 else
5227 This->base.lockingrole = SWMR_None;
5228
5229 This->base.reverted = FALSE;
5230
5231 /*
5232 * Initialize the big block cache.
5233 */
5234 This->bigBlockSize = sector_size;
5235 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5236 if (hFile)
5237 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5238 else
5239 {
5240 This->lockBytes = pLkbyt;
5241 ILockBytes_AddRef(pLkbyt);
5242 }
5243
5244 if (SUCCEEDED(hr))
5245 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5246
5247 if (SUCCEEDED(hr))
5248 {
5249 This->locks_supported = stat.grfLocksSupported;
5250 if (!hFile)
5251 /* Don't try to use wine-internal locking flag with custom ILockBytes */
5252 This->locks_supported &= ~WINE_LOCK_READ;
5253
5254 hr = StorageImpl_GrabLocks(This, openFlags);
5255 }
5256
5257 if (SUCCEEDED(hr))
5258 hr = StorageImpl_Refresh(This, TRUE, create);
5259
5260 if (FAILED(hr))
5261 {
5262 IStorage_Release(&This->base.IStorage_iface);
5263 *result = NULL;
5264 }
5265 else
5266 {
5267 StorageImpl_Flush(&This->base);
5268 *result = This;
5269 }
5270
5271 return hr;
5272 }
5273
5274
5275 /************************************************************************
5276 * StorageInternalImpl implementation
5277 ***********************************************************************/
5278
5279 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5280 {
5281 StorageInternalImpl* This = (StorageInternalImpl*) base;
5282
5283 if (!This->base.reverted)
5284 {
5285 TRACE("Storage invalidated (stg=%p)\n", This);
5286
5287 This->base.reverted = TRUE;
5288
5289 This->parentStorage = NULL;
5290
5291 StorageBaseImpl_DeleteAll(&This->base);
5292
5293 list_remove(&This->ParentListEntry);
5294 }
5295 }
5296
5297 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5298 {
5299 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5300
5301 StorageInternalImpl_Invalidate(&This->base);
5302
5303 HeapFree(GetProcessHeap(), 0, This);
5304 }
5305
5306 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5307 {
5308 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5309
5310 return StorageBaseImpl_Flush(This->parentStorage);
5311 }
5312
5313 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5314 {
5315 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5316
5317 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5318 }
5319
5320 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5321 const DirEntry *newData, DirRef *index)
5322 {
5323 StorageInternalImpl* This = (StorageInternalImpl*) base;
5324
5325 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5326 newData, index);
5327 }
5328
5329 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5330 DirRef index, const DirEntry *data)
5331 {
5332 StorageInternalImpl* This = (StorageInternalImpl*) base;
5333
5334 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5335 index, data);
5336 }
5337
5338 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5339 DirRef index, DirEntry *data)
5340 {
5341 StorageInternalImpl* This = (StorageInternalImpl*) base;
5342
5343 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5344 index, data);
5345 }
5346
5347 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5348 DirRef index)
5349 {
5350 StorageInternalImpl* This = (StorageInternalImpl*) base;
5351
5352 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5353 index);
5354 }
5355
5356 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5357 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5358 {
5359 StorageInternalImpl* This = (StorageInternalImpl*) base;
5360
5361 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5362 index, offset, size, buffer, bytesRead);
5363 }
5364
5365 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5366 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5367 {
5368 StorageInternalImpl* This = (StorageInternalImpl*) base;
5369
5370 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5371 index, offset, size, buffer, bytesWritten);
5372 }
5373
5374 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5375 DirRef index, ULARGE_INTEGER newsize)
5376 {
5377 StorageInternalImpl* This = (StorageInternalImpl*) base;
5378
5379 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5380 index, newsize);
5381 }
5382
5383 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5384 DirRef dst, DirRef src)
5385 {
5386 StorageInternalImpl* This = (StorageInternalImpl*) base;
5387
5388 return StorageBaseImpl_StreamLink(This->parentStorage,
5389 dst, src);
5390 }
5391
5392 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5393 ULONG* result, BOOL refresh)
5394 {
5395 return E_NOTIMPL;
5396 }
5397
5398 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5399 ULONG value)
5400 {
5401 return E_NOTIMPL;
5402 }
5403
5404 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5405 {
5406 return E_NOTIMPL;
5407 }
5408
5409 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5410 {
5411 return E_NOTIMPL;
5412 }
5413
5414 /******************************************************************************
5415 **
5416 ** StorageInternalImpl_Commit
5417 **
5418 */
5419 static HRESULT WINAPI StorageInternalImpl_Commit(
5420 IStorage* iface,
5421 DWORD grfCommitFlags) /* [in] */
5422 {
5423 StorageBaseImpl* This = impl_from_IStorage(iface);
5424 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5425 return StorageBaseImpl_Flush(This);
5426 }
5427
5428 /******************************************************************************
5429 **
5430 ** StorageInternalImpl_Revert
5431 **
5432 */
5433 static HRESULT WINAPI StorageInternalImpl_Revert(
5434 IStorage* iface)
5435 {
5436 FIXME("(%p): stub\n", iface);
5437 return S_OK;
5438 }
5439
5440 /*
5441 * Virtual function table for the StorageInternalImpl class.
5442 */
5443 static const IStorageVtbl StorageInternalImpl_Vtbl =
5444 {
5445 StorageBaseImpl_QueryInterface,
5446 StorageBaseImpl_AddRef,
5447 StorageBaseImpl_Release,
5448 StorageBaseImpl_CreateStream,
5449 StorageBaseImpl_OpenStream,
5450 StorageBaseImpl_CreateStorage,
5451 StorageBaseImpl_OpenStorage,
5452 StorageBaseImpl_CopyTo,
5453 StorageBaseImpl_MoveElementTo,
5454 StorageInternalImpl_Commit,
5455 StorageInternalImpl_Revert,
5456 StorageBaseImpl_EnumElements,
5457 StorageBaseImpl_DestroyElement,
5458 StorageBaseImpl_RenameElement,
5459 StorageBaseImpl_SetElementTimes,
5460 StorageBaseImpl_SetClass,
5461 StorageBaseImpl_SetStateBits,
5462 StorageBaseImpl_Stat
5463 };
5464
5465 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5466 {
5467 StorageInternalImpl_Destroy,
5468 StorageInternalImpl_Invalidate,
5469 StorageInternalImpl_Flush,
5470 StorageInternalImpl_GetFilename,
5471 StorageInternalImpl_CreateDirEntry,
5472 StorageInternalImpl_WriteDirEntry,
5473 StorageInternalImpl_ReadDirEntry,
5474 StorageInternalImpl_DestroyDirEntry,
5475 StorageInternalImpl_StreamReadAt,
5476 StorageInternalImpl_StreamWriteAt,
5477 StorageInternalImpl_StreamSetSize,
5478 StorageInternalImpl_StreamLink,
5479 StorageInternalImpl_GetTransactionSig,
5480 StorageInternalImpl_SetTransactionSig,
5481 StorageInternalImpl_LockTransaction,
5482 StorageInternalImpl_UnlockTransaction
5483 };
5484
5485 static StorageInternalImpl* StorageInternalImpl_Construct(
5486 StorageBaseImpl* parentStorage,
5487 DWORD openFlags,
5488 DirRef storageDirEntry)
5489 {
5490 StorageInternalImpl* newStorage;
5491
5492 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5493
5494 if (newStorage!=0)
5495 {
5496 list_init(&newStorage->base.strmHead);
5497
5498 list_init(&newStorage->base.storageHead);
5499
5500 /*
5501 * Initialize the virtual function table.
5502 */
5503 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5504 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5505 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5506 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5507
5508 newStorage->base.reverted = FALSE;
5509
5510 newStorage->base.ref = 1;
5511
5512 newStorage->parentStorage = parentStorage;
5513
5514 /*
5515 * Keep a reference to the directory entry of this storage
5516 */
5517 newStorage->base.storageDirEntry = storageDirEntry;
5518
5519 newStorage->base.create = FALSE;
5520
5521 return newStorage;
5522 }
5523
5524 return 0;
5525 }
5526
5527
5528 /************************************************************************
5529 * TransactedSnapshotImpl implementation
5530 ***********************************************************************/
5531
5532 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5533 {
5534 DirRef result=This->firstFreeEntry;
5535
5536 while (result < This->entries_size && This->entries[result].inuse)
5537 result++;
5538
5539 if (result == This->entries_size)
5540 {
5541 ULONG new_size = This->entries_size * 2;
5542 TransactedDirEntry *new_entries;
5543
5544 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5545 if (!new_entries) return DIRENTRY_NULL;
5546
5547 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5548 HeapFree(GetProcessHeap(), 0, This->entries);
5549
5550 This->entries = new_entries;
5551 This->entries_size = new_size;
5552 }
5553
5554 This->entries[result].inuse = TRUE;
5555
5556 This->firstFreeEntry = result+1;
5557
5558 return result;
5559 }
5560
5561 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5562 TransactedSnapshotImpl *This, DirRef parentEntryRef)
5563 {
5564 DirRef stubEntryRef;
5565 TransactedDirEntry *entry;
5566
5567 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5568
5569 if (stubEntryRef != DIRENTRY_NULL)
5570 {
5571 entry = &This->entries[stubEntryRef];
5572
5573 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5574
5575 entry->read = FALSE;
5576 }
5577
5578 return stubEntryRef;
5579 }
5580
5581 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5582 TransactedSnapshotImpl *This, DirRef entry)
5583 {
5584 HRESULT hr=S_OK;
5585 DirEntry data;
5586
5587 if (!This->entries[entry].read)
5588 {
5589 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5590 This->entries[entry].transactedParentEntry,
5591 &data);
5592
5593 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5594 {
5595 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5596
5597 if (data.leftChild == DIRENTRY_NULL)
5598 hr = E_OUTOFMEMORY;
5599 }
5600
5601 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5602 {
5603 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5604
5605 if (data.rightChild == DIRENTRY_NULL)
5606 hr = E_OUTOFMEMORY;
5607 }
5608
5609 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5610 {
5611 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5612
5613 if (data.dirRootEntry == DIRENTRY_NULL)
5614 hr = E_OUTOFMEMORY;
5615 }
5616
5617 if (SUCCEEDED(hr))
5618 {
5619 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5620 This->entries[entry].read = TRUE;
5621 }
5622 }
5623
5624 return hr;
5625 }
5626
5627 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5628 TransactedSnapshotImpl *This, DirRef entry)
5629 {
5630 HRESULT hr = S_OK;
5631
5632 if (!This->entries[entry].stream_dirty)
5633 {
5634 DirEntry new_entrydata;
5635
5636 memset(&new_entrydata, 0, sizeof(DirEntry));
5637 new_entrydata.name[0] = 'S';
5638 new_entrydata.sizeOfNameString = 1;
5639 new_entrydata.stgType = STGTY_STREAM;
5640 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5641 new_entrydata.leftChild = DIRENTRY_NULL;
5642 new_entrydata.rightChild = DIRENTRY_NULL;
5643 new_entrydata.dirRootEntry = DIRENTRY_NULL;
5644
5645 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5646 &This->entries[entry].stream_entry);
5647
5648 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5649 {
5650 hr = StorageBaseImpl_CopyStream(
5651 This->scratch, This->entries[entry].stream_entry,
5652 This->transactedParent, This->entries[entry].transactedParentEntry);
5653
5654 if (FAILED(hr))
5655 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5656 }
5657
5658 if (SUCCEEDED(hr))
5659 This->entries[entry].stream_dirty = TRUE;
5660
5661 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5662 {
5663 /* Since this entry is modified, and we aren't using its stream data, we
5664 * no longer care about the original entry. */
5665 DirRef delete_ref;
5666 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5667
5668 if (delete_ref != DIRENTRY_NULL)
5669 This->entries[delete_ref].deleted = TRUE;
5670
5671 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5672 }
5673 }
5674
5675 return hr;
5676 }
5677
5678 /* Find the first entry in a depth-first traversal. */
5679 static DirRef TransactedSnapshotImpl_FindFirstChild(
5680 TransactedSnapshotImpl* This, DirRef parent)
5681 {
5682 DirRef cursor, prev;
5683 TransactedDirEntry *entry;
5684
5685 cursor = parent;
5686 entry = &This->entries[cursor];
5687 while (entry->read)
5688 {
5689 if (entry->data.leftChild != DIRENTRY_NULL)
5690 {
5691 prev = cursor;
5692 cursor = entry->data.leftChild;
5693 entry = &This->entries[cursor];
5694 entry->parent = prev;
5695 }
5696 else if (entry->data.rightChild != DIRENTRY_NULL)
5697 {
5698 prev = cursor;
5699 cursor = entry->data.rightChild;
5700 entry = &This->entries[cursor];
5701 entry->parent = prev;
5702 }
5703 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5704 {
5705 prev = cursor;
5706 cursor = entry->data.dirRootEntry;
5707 entry = &This->entries[cursor];
5708 entry->parent = prev;
5709 }
5710 else
5711 break;
5712 }
5713
5714 return cursor;
5715 }
5716
5717 /* Find the next entry in a depth-first traversal. */
5718 static DirRef TransactedSnapshotImpl_FindNextChild(
5719 TransactedSnapshotImpl* This, DirRef current)
5720 {
5721 DirRef parent;
5722 TransactedDirEntry *parent_entry;
5723
5724 parent = This->entries[current].parent;
5725 parent_entry = &This->entries[parent];
5726
5727 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5728 {
5729 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5730 {
5731 This->entries[parent_entry->data.rightChild].parent = parent;
5732 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5733 }
5734
5735 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5736 {
5737 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5738 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5739 }
5740 }
5741
5742 return parent;
5743 }
5744
5745 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5746 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5747 TransactedSnapshotImpl* This, DirRef entry)
5748 {
5749 return entry != DIRENTRY_NULL &&
5750 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5751 }
5752
5753 /* Destroy the entries created by CopyTree. */
5754 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5755 TransactedSnapshotImpl* This, DirRef stop)
5756 {
5757 DirRef cursor;
5758 TransactedDirEntry *entry;
5759 ULARGE_INTEGER zero;
5760
5761 zero.QuadPart = 0;
5762
5763 if (!This->entries[This->base.storageDirEntry].read)
5764 return;
5765
5766 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5767
5768 if (cursor == DIRENTRY_NULL)
5769 return;
5770
5771 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5772
5773 while (cursor != DIRENTRY_NULL && cursor != stop)
5774 {
5775 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5776 {
5777 entry = &This->entries[cursor];
5778
5779 if (entry->stream_dirty)
5780 StorageBaseImpl_StreamSetSize(This->transactedParent,
5781 entry->newTransactedParentEntry, zero);
5782
5783 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5784 entry->newTransactedParentEntry);
5785
5786 entry->newTransactedParentEntry = entry->transactedParentEntry;
5787 }
5788
5789 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5790 }
5791 }
5792
5793 /* Make a copy of our edited tree that we can use in the parent. */
5794 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5795 {
5796 DirRef cursor;
5797 TransactedDirEntry *entry;
5798 HRESULT hr = S_OK;
5799
5800 cursor = This->base.storageDirEntry;
5801 entry = &This->entries[cursor];
5802 entry->parent = DIRENTRY_NULL;
5803 entry->newTransactedParentEntry = entry->transactedParentEntry;
5804
5805 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5806 return S_OK;
5807
5808 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5809
5810 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5811 entry = &This->entries[cursor];
5812
5813 while (cursor != DIRENTRY_NULL)
5814 {
5815 /* Make a copy of this entry in the transacted parent. */
5816 if (!entry->read ||
5817 (!entry->dirty && !entry->stream_dirty &&
5818 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5819 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5820 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5821 entry->newTransactedParentEntry = entry->transactedParentEntry;
5822 else
5823 {
5824 DirEntry newData;
5825
5826 memcpy(&newData, &entry->data, sizeof(DirEntry));
5827
5828 newData.size.QuadPart = 0;
5829 newData.startingBlock = BLOCK_END_OF_CHAIN;
5830
5831 if (newData.leftChild != DIRENTRY_NULL)
5832 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5833
5834 if (newData.rightChild != DIRENTRY_NULL)
5835 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5836
5837 if (newData.dirRootEntry != DIRENTRY_NULL)
5838 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5839
5840 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5841 &entry->newTransactedParentEntry);
5842 if (FAILED(hr))
5843 {
5844 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5845 return hr;
5846 }
5847
5848 if (entry->stream_dirty)
5849 {
5850 hr = StorageBaseImpl_CopyStream(
5851 This->transactedParent, entry->newTransactedParentEntry,
5852 This->scratch, entry->stream_entry);
5853 }
5854 else if (entry->data.size.QuadPart)
5855 {
5856 hr = StorageBaseImpl_StreamLink(
5857 This->transactedParent, entry->newTransactedParentEntry,
5858 entry->transactedParentEntry);
5859 }
5860
5861 if (FAILED(hr))
5862 {
5863 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5864 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5865 return hr;
5866 }
5867 }
5868
5869 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5870 entry = &This->entries[cursor];
5871 }
5872
5873 return hr;
5874 }
5875
5876 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5877 IStorage* iface,
5878 DWORD grfCommitFlags) /* [in] */
5879 {
5880 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5881 TransactedDirEntry *root_entry;
5882 DirRef i, dir_root_ref;
5883 DirEntry data;
5884 ULARGE_INTEGER zero;
5885 HRESULT hr;
5886 ULONG transactionSig;
5887
5888 zero.QuadPart = 0;
5889
5890 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5891
5892 /* Cannot commit a read-only transacted storage */
5893 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5894 return STG_E_ACCESSDENIED;
5895
5896 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5897 if (hr == E_NOTIMPL) hr = S_OK;
5898 if (SUCCEEDED(hr))
5899 {
5900 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5901 if (SUCCEEDED(hr))
5902 {
5903 if (transactionSig != This->lastTransactionSig)
5904 {
5905 ERR("file was externally modified\n");
5906 hr = STG_E_NOTCURRENT;
5907 }
5908
5909 if (SUCCEEDED(hr))
5910 {
5911 This->lastTransactionSig = transactionSig+1;
5912 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5913 }
5914 }
5915 else if (hr == E_NOTIMPL)
5916 hr = S_OK;
5917
5918 if (FAILED(hr)) goto end;
5919
5920 /* To prevent data loss, we create the new structure in the file before we
5921 * delete the old one, so that in case of errors the old data is intact. We
5922 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5923 * needed in the rare situation where we have just enough free disk space to
5924 * overwrite the existing data. */
5925
5926 root_entry = &This->entries[This->base.storageDirEntry];
5927
5928 if (!root_entry->read)
5929 goto end;
5930
5931 hr = TransactedSnapshotImpl_CopyTree(This);
5932 if (FAILED(hr)) goto end;
5933
5934 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5935 dir_root_ref = DIRENTRY_NULL;
5936 else
5937 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5938
5939 hr = StorageBaseImpl_Flush(This->transactedParent);
5940
5941 /* Update the storage to use the new data in one step. */
5942 if (SUCCEEDED(hr))
5943 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5944 root_entry->transactedParentEntry, &data);
5945
5946 if (SUCCEEDED(hr))
5947 {
5948 data.dirRootEntry = dir_root_ref;
5949 data.clsid = root_entry->data.clsid;
5950 data.ctime = root_entry->data.ctime;
5951 data.mtime = root_entry->data.mtime;
5952
5953 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5954 root_entry->transactedParentEntry, &data);
5955 }
5956
5957 /* Try to flush after updating the root storage, but if the flush fails, keep
5958 * going, on the theory that it'll either succeed later or the subsequent
5959 * writes will fail. */
5960 StorageBaseImpl_Flush(This->transactedParent);
5961
5962 if (SUCCEEDED(hr))
5963 {
5964 /* Destroy the old now-orphaned data. */
5965 for (i=0; i<This->entries_size; i++)
5966 {
5967 TransactedDirEntry *entry = &This->entries[i];
5968 if (entry->inuse)
5969 {
5970 if (entry->deleted)
5971 {
5972 StorageBaseImpl_StreamSetSize(This->transactedParent,
5973 entry->transactedParentEntry, zero);
5974 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5975 entry->transactedParentEntry);
5976 memset(entry, 0, sizeof(TransactedDirEntry));
5977 This->firstFreeEntry = min(i, This->firstFreeEntry);
5978 }
5979 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
5980 {
5981 if (entry->transactedParentEntry != DIRENTRY_NULL)
5982 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5983 entry->transactedParentEntry);
5984 if (entry->stream_dirty)
5985 {
5986 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
5987 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
5988 entry->stream_dirty = FALSE;
5989 }
5990 entry->dirty = FALSE;
5991 entry->transactedParentEntry = entry->newTransactedParentEntry;
5992 }
5993 }
5994 }
5995 }
5996 else
5997 {
5998 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
5999 }
6000
6001 if (SUCCEEDED(hr))
6002 hr = StorageBaseImpl_Flush(This->transactedParent);
6003 end:
6004 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6005 }
6006
6007 return hr;
6008 }
6009
6010 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6011 IStorage* iface)
6012 {
6013 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6014 ULARGE_INTEGER zero;
6015 ULONG i;
6016
6017 TRACE("(%p)\n", iface);
6018
6019 /* Destroy the open objects. */
6020 StorageBaseImpl_DeleteAll(&This->base);
6021
6022 /* Clear out the scratch file. */
6023 zero.QuadPart = 0;
6024 for (i=0; i<This->entries_size; i++)
6025 {
6026 if (This->entries[i].stream_dirty)
6027 {
6028 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6029 zero);
6030
6031 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6032 }
6033 }
6034
6035 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6036
6037 This->firstFreeEntry = 0;
6038 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6039
6040 return S_OK;
6041 }
6042
6043 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6044 {
6045 if (!This->reverted)
6046 {
6047 TRACE("Storage invalidated (stg=%p)\n", This);
6048
6049 This->reverted = TRUE;
6050
6051 StorageBaseImpl_DeleteAll(This);
6052 }
6053 }
6054
6055 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6056 {
6057 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6058
6059 IStorage_Revert(&This->base.IStorage_iface);
6060 IStorage_Release(&This->transactedParent->IStorage_iface);
6061 IStorage_Release(&This->scratch->IStorage_iface);
6062 HeapFree(GetProcessHeap(), 0, This->entries);
6063 HeapFree(GetProcessHeap(), 0, This);
6064 }
6065
6066 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6067 {
6068 /* We only need to flush when committing. */
6069 return S_OK;
6070 }
6071
6072 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6073 {
6074 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6075
6076 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6077 }
6078
6079 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6080 const DirEntry *newData, DirRef *index)
6081 {
6082 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6083 DirRef new_ref;
6084 TransactedDirEntry *new_entry;
6085
6086 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6087 if (new_ref == DIRENTRY_NULL)
6088 return E_OUTOFMEMORY;
6089
6090 new_entry = &This->entries[new_ref];
6091
6092 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6093 new_entry->read = TRUE;
6094 new_entry->dirty = TRUE;
6095 memcpy(&new_entry->data, newData, sizeof(DirEntry));
6096
6097 *index = new_ref;
6098
6099 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6100
6101 return S_OK;
6102 }
6103
6104 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6105 DirRef index, const DirEntry *data)
6106 {
6107 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6108 HRESULT hr;
6109
6110 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6111
6112 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6113 if (FAILED(hr)) return hr;
6114
6115 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6116
6117 if (index != This->base.storageDirEntry)
6118 {
6119 This->entries[index].dirty = TRUE;
6120
6121 if (data->size.QuadPart == 0 &&
6122 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6123 {
6124 /* Since this entry is modified, and we aren't using its stream data, we
6125 * no longer care about the original entry. */
6126 DirRef delete_ref;
6127 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6128
6129 if (delete_ref != DIRENTRY_NULL)
6130 This->entries[delete_ref].deleted = TRUE;
6131
6132 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6133 }
6134 }
6135
6136 return S_OK;
6137 }
6138
6139 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6140 DirRef index, DirEntry *data)
6141 {
6142 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6143 HRESULT hr;
6144
6145 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6146 if (FAILED(hr)) return hr;
6147
6148 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6149
6150 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6151
6152 return S_OK;
6153 }
6154
6155 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6156 DirRef index)
6157 {
6158 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6159
6160 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6161 This->entries[index].data.size.QuadPart != 0)
6162 {
6163 /* If we deleted this entry while it has stream data. We must have left the
6164 * data because some other entry is using it, and we need to leave the
6165 * original entry alone. */
6166 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6167 This->firstFreeEntry = min(index, This->firstFreeEntry);
6168 }
6169 else
6170 {
6171 This->entries[index].deleted = TRUE;
6172 }
6173
6174 return S_OK;
6175 }
6176
6177 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6178 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6179 {
6180 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6181
6182 if (This->entries[index].stream_dirty)
6183 {
6184 return StorageBaseImpl_StreamReadAt(This->scratch,
6185 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6186 }
6187 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6188 {
6189 /* This stream doesn't live in the parent, and we haven't allocated storage
6190 * for it yet */
6191 *bytesRead = 0;
6192 return S_OK;
6193 }
6194 else
6195 {
6196 return StorageBaseImpl_StreamReadAt(This->transactedParent,
6197 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6198 }
6199 }
6200
6201 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6202 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6203 {
6204 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6205 HRESULT hr;
6206
6207 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6208 if (FAILED(hr)) return hr;
6209
6210 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6211 if (FAILED(hr)) return hr;
6212
6213 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6214 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6215
6216 if (SUCCEEDED(hr) && size != 0)
6217 This->entries[index].data.size.QuadPart = max(
6218 This->entries[index].data.size.QuadPart,
6219 offset.QuadPart + size);
6220
6221 return hr;
6222 }
6223
6224 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6225 DirRef index, ULARGE_INTEGER newsize)
6226 {
6227 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6228 HRESULT hr;
6229
6230 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6231 if (FAILED(hr)) return hr;
6232
6233 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6234 return S_OK;
6235
6236 if (newsize.QuadPart == 0)
6237 {
6238 /* Destroy any parent references or entries in the scratch file. */
6239 if (This->entries[index].stream_dirty)
6240 {
6241 ULARGE_INTEGER zero;
6242 zero.QuadPart = 0;
6243 StorageBaseImpl_StreamSetSize(This->scratch,
6244 This->entries[index].stream_entry, zero);
6245 StorageBaseImpl_DestroyDirEntry(This->scratch,
6246 This->entries[index].stream_entry);
6247 This->entries[index].stream_dirty = FALSE;
6248 }
6249 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6250 {
6251 DirRef delete_ref;
6252 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6253
6254 if (delete_ref != DIRENTRY_NULL)
6255 This->entries[delete_ref].deleted = TRUE;
6256
6257 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6258 }
6259 }
6260 else
6261 {
6262 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6263 if (FAILED(hr)) return hr;
6264
6265 hr = StorageBaseImpl_StreamSetSize(This->scratch,
6266 This->entries[index].stream_entry, newsize);
6267 }
6268
6269 if (SUCCEEDED(hr))
6270 This->entries[index].data.size = newsize;
6271
6272 return hr;
6273 }
6274
6275 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6276 DirRef dst, DirRef src)
6277 {
6278 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6279 HRESULT hr;
6280 TransactedDirEntry *dst_entry, *src_entry;
6281
6282 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6283 if (FAILED(hr)) return hr;
6284
6285 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6286 if (FAILED(hr)) return hr;
6287
6288 dst_entry = &This->entries[dst];
6289 src_entry = &This->entries[src];
6290
6291 dst_entry->stream_dirty = src_entry->stream_dirty;
6292 dst_entry->stream_entry = src_entry->stream_entry;
6293 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6294 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6295 dst_entry->data.size = src_entry->data.size;
6296
6297 return S_OK;
6298 }
6299
6300 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6301 ULONG* result, BOOL refresh)
6302 {
6303 return E_NOTIMPL;
6304 }
6305
6306 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6307 ULONG value)
6308 {
6309 return E_NOTIMPL;
6310 }
6311
6312 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6313 {
6314 return E_NOTIMPL;
6315 }
6316
6317 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6318 {
6319 return E_NOTIMPL;
6320 }
6321
6322 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6323 {
6324 StorageBaseImpl_QueryInterface,
6325 StorageBaseImpl_AddRef,
6326 StorageBaseImpl_Release,
6327 StorageBaseImpl_CreateStream,
6328 StorageBaseImpl_OpenStream,
6329 StorageBaseImpl_CreateStorage,
6330 StorageBaseImpl_OpenStorage,
6331 StorageBaseImpl_CopyTo,
6332 StorageBaseImpl_MoveElementTo,
6333 TransactedSnapshotImpl_Commit,
6334 TransactedSnapshotImpl_Revert,
6335 StorageBaseImpl_EnumElements,
6336 StorageBaseImpl_DestroyElement,
6337 StorageBaseImpl_RenameElement,
6338 StorageBaseImpl_SetElementTimes,
6339 StorageBaseImpl_SetClass,
6340 StorageBaseImpl_SetStateBits,
6341 StorageBaseImpl_Stat
6342 };
6343
6344 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6345 {
6346 TransactedSnapshotImpl_Destroy,
6347 TransactedSnapshotImpl_Invalidate,
6348 TransactedSnapshotImpl_Flush,
6349 TransactedSnapshotImpl_GetFilename,
6350 TransactedSnapshotImpl_CreateDirEntry,
6351 TransactedSnapshotImpl_WriteDirEntry,
6352 TransactedSnapshotImpl_ReadDirEntry,
6353 TransactedSnapshotImpl_DestroyDirEntry,
6354 TransactedSnapshotImpl_StreamReadAt,
6355 TransactedSnapshotImpl_StreamWriteAt,
6356 TransactedSnapshotImpl_StreamSetSize,
6357 TransactedSnapshotImpl_StreamLink,
6358 TransactedSnapshotImpl_GetTransactionSig,
6359 TransactedSnapshotImpl_SetTransactionSig,
6360 TransactedSnapshotImpl_LockTransaction,
6361 TransactedSnapshotImpl_UnlockTransaction
6362 };
6363
6364 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6365 TransactedSnapshotImpl** result)
6366 {
6367 HRESULT hr;
6368
6369 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6370 if (*result)
6371 {
6372 IStorage *scratch;
6373
6374 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6375
6376 /* This is OK because the property set storage functions use the IStorage functions. */
6377 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6378 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6379
6380 list_init(&(*result)->base.strmHead);
6381
6382 list_init(&(*result)->base.storageHead);
6383
6384 (*result)->base.ref = 1;
6385
6386 (*result)->base.openFlags = parentStorage->openFlags;
6387
6388 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6389 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6390
6391 /* Create a new temporary storage to act as the scratch file. */
6392 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6393 0, &scratch);
6394 (*result)->scratch = impl_from_IStorage(scratch);
6395
6396 if (SUCCEEDED(hr))
6397 {
6398 ULONG num_entries = 20;
6399
6400 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6401 (*result)->entries_size = num_entries;
6402 (*result)->firstFreeEntry = 0;
6403
6404 if ((*result)->entries)
6405 {
6406 /* parentStorage already has 1 reference, which we take over here. */
6407 (*result)->transactedParent = parentStorage;
6408
6409 parentStorage->transactedChild = &(*result)->base;
6410
6411 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6412 }
6413 else
6414 {
6415 IStorage_Release(scratch);
6416
6417 hr = E_OUTOFMEMORY;
6418 }
6419 }
6420
6421 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6422
6423 return hr;
6424 }
6425 else
6426 return E_OUTOFMEMORY;
6427 }
6428
6429
6430 /************************************************************************
6431 * TransactedSharedImpl implementation
6432 ***********************************************************************/
6433
6434 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6435 {
6436 if (!This->reverted)
6437 {
6438 TRACE("Storage invalidated (stg=%p)\n", This);
6439
6440 This->reverted = TRUE;
6441
6442 StorageBaseImpl_DeleteAll(This);
6443 }
6444 }
6445
6446 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6447 {
6448 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6449
6450 TransactedSharedImpl_Invalidate(&This->base);
6451 IStorage_Release(&This->transactedParent->IStorage_iface);
6452 IStorage_Release(&This->scratch->base.IStorage_iface);
6453 HeapFree(GetProcessHeap(), 0, This);
6454 }
6455
6456 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6457 {
6458 /* We only need to flush when committing. */
6459 return S_OK;
6460 }
6461
6462 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6463 {
6464 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6465
6466 return StorageBaseImpl_GetFilename(This->transactedParent, result);
6467 }
6468
6469 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6470 const DirEntry *newData, DirRef *index)
6471 {
6472 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6473
6474 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6475 newData, index);
6476 }
6477
6478 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6479 DirRef index, const DirEntry *data)
6480 {
6481 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6482
6483 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6484 index, data);
6485 }
6486
6487 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6488 DirRef index, DirEntry *data)
6489 {
6490 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6491
6492 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6493 index, data);
6494 }
6495
6496 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6497 DirRef index)
6498 {
6499 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6500
6501 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6502 index);
6503 }
6504
6505 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6506 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6507 {
6508 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6509
6510 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6511 index, offset, size, buffer, bytesRead);
6512 }
6513
6514 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6515 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6516 {
6517 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6518
6519 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6520 index, offset, size, buffer, bytesWritten);
6521 }
6522
6523 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6524 DirRef index, ULARGE_INTEGER newsize)
6525 {
6526 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6527
6528 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6529 index, newsize);
6530 }
6531
6532 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6533 DirRef dst, DirRef src)
6534 {
6535 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6536
6537 return StorageBaseImpl_StreamLink(&This->scratch->base,
6538 dst, src);
6539 }
6540
6541 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6542 ULONG* result, BOOL refresh)
6543 {
6544 return E_NOTIMPL;
6545 }
6546
6547 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6548 ULONG value)
6549 {
6550 return E_NOTIMPL;
6551 }
6552
6553 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6554 {
6555 return E_NOTIMPL;
6556 }
6557
6558 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6559 {
6560 return E_NOTIMPL;
6561 }
6562
6563 static HRESULT WINAPI TransactedSharedImpl_Commit(
6564 IStorage* iface,
6565 DWORD grfCommitFlags) /* [in] */
6566 {
6567 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6568 DirRef new_storage_ref, prev_storage_ref;
6569 DirEntry src_data, dst_data;
6570 HRESULT hr;
6571 ULONG transactionSig;
6572
6573 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6574
6575 /* Cannot commit a read-only transacted storage */
6576 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6577 return STG_E_ACCESSDENIED;
6578
6579 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6580 if (hr == E_NOTIMPL) hr = S_OK;
6581 if (SUCCEEDED(hr))
6582 {
6583 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6584 if (SUCCEEDED(hr))
6585 {
6586 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6587 hr = STG_E_NOTCURRENT;
6588
6589 if (SUCCEEDED(hr))
6590 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6591 }
6592 else if (hr == E_NOTIMPL)
6593 hr = S_OK;
6594
6595 if (SUCCEEDED(hr))
6596 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6597
6598 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6599 if (SUCCEEDED(hr))
6600 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6601
6602 if (SUCCEEDED(hr))
6603 hr = StorageBaseImpl_Flush(This->transactedParent);
6604
6605 if (SUCCEEDED(hr))
6606 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6607
6608 if (SUCCEEDED(hr))
6609 {
6610 prev_storage_ref = dst_data.dirRootEntry;
6611 dst_data.dirRootEntry = new_storage_ref;
6612 dst_data.clsid = src_data.clsid;
6613 dst_data.ctime = src_data.ctime;
6614 dst_data.mtime = src_data.mtime;
6615 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6616 }
6617
6618 if (SUCCEEDED(hr))
6619 {
6620 /* Try to flush after updating the root storage, but if the flush fails, keep
6621 * going, on the theory that it'll either succeed later or the subsequent
6622 * writes will fail. */
6623 StorageBaseImpl_Flush(This->transactedParent);
6624
6625 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6626 }
6627
6628 if (SUCCEEDED(hr))
6629 hr = StorageBaseImpl_Flush(This->transactedParent);
6630
6631 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6632
6633 if (SUCCEEDED(hr))
6634 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6635
6636 if (SUCCEEDED(hr))
6637 {
6638 This->lastTransactionSig = transactionSig+1;
6639 }
6640 }
6641
6642 return hr;
6643 }
6644
6645 static HRESULT WINAPI TransactedSharedImpl_Revert(
6646 IStorage* iface)
6647 {
6648 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6649
6650 TRACE("(%p)\n", iface);
6651
6652 /* Destroy the open objects. */
6653 StorageBaseImpl_DeleteAll(&This->base);
6654
6655 return IStorage_Revert(&This->scratch->base.IStorage_iface);
6656 }
6657
6658 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6659 {
6660 StorageBaseImpl_QueryInterface,
6661 StorageBaseImpl_AddRef,
6662 StorageBaseImpl_Release,
6663 StorageBaseImpl_CreateStream,
6664 StorageBaseImpl_OpenStream,
6665 StorageBaseImpl_CreateStorage,
6666 StorageBaseImpl_OpenStorage,
6667 StorageBaseImpl_CopyTo,
6668 StorageBaseImpl_MoveElementTo,
6669 TransactedSharedImpl_Commit,
6670 TransactedSharedImpl_Revert,
6671 StorageBaseImpl_EnumElements,
6672 StorageBaseImpl_DestroyElement,
6673 StorageBaseImpl_RenameElement,
6674 StorageBaseImpl_SetElementTimes,
6675 StorageBaseImpl_SetClass,
6676 StorageBaseImpl_SetStateBits,
6677 StorageBaseImpl_Stat
6678 };
6679
6680 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6681 {
6682 TransactedSharedImpl_Destroy,
6683 TransactedSharedImpl_Invalidate,
6684 TransactedSharedImpl_Flush,
6685 TransactedSharedImpl_GetFilename,
6686 TransactedSharedImpl_CreateDirEntry,
6687 TransactedSharedImpl_WriteDirEntry,
6688 TransactedSharedImpl_ReadDirEntry,
6689 TransactedSharedImpl_DestroyDirEntry,
6690 TransactedSharedImpl_StreamReadAt,
6691 TransactedSharedImpl_StreamWriteAt,
6692 TransactedSharedImpl_StreamSetSize,
6693 TransactedSharedImpl_StreamLink,
6694 TransactedSharedImpl_GetTransactionSig,
6695 TransactedSharedImpl_SetTransactionSig,
6696 TransactedSharedImpl_LockTransaction,
6697 TransactedSharedImpl_UnlockTransaction
6698 };
6699
6700 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6701 TransactedSharedImpl** result)
6702 {
6703 HRESULT hr;
6704
6705 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6706 if (*result)
6707 {
6708 IStorage *scratch;
6709
6710 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6711
6712 /* This is OK because the property set storage functions use the IStorage functions. */
6713 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6714 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6715
6716 list_init(&(*result)->base.strmHead);
6717
6718 list_init(&(*result)->base.storageHead);
6719
6720 (*result)->base.ref = 1;
6721
6722 (*result)->base.openFlags = parentStorage->openFlags;
6723
6724 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6725
6726 if (SUCCEEDED(hr))
6727 {
6728 STGOPTIONS stgo;
6729
6730 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6731 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6732
6733 stgo.usVersion = 1;
6734 stgo.reserved = 0;
6735 stgo.ulSectorSize = 4096;
6736 stgo.pwcsTemplateFile = NULL;
6737
6738 /* Create a new temporary storage to act as the scratch file. */
6739 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6740 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6741 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6742
6743 if (SUCCEEDED(hr))
6744 {
6745 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6746 parentStorage, parentStorage->storageDirEntry);
6747
6748 if (SUCCEEDED(hr))
6749 {
6750 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6751
6752 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6753 (*result)->transactedParent = parentStorage;
6754 }
6755
6756 if (FAILED(hr))
6757 IStorage_Release(scratch);
6758 }
6759
6760 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6761 }
6762
6763 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6764
6765 return hr;
6766 }
6767 else
6768 return E_OUTOFMEMORY;
6769 }
6770
6771 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6772 BOOL toplevel, StorageBaseImpl** result)
6773 {
6774 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6775
6776 if (parentStorage->openFlags & fixme_flags)
6777 {
6778 fixme_flags &= ~parentStorage->openFlags;
6779 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6780 }
6781
6782 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6783 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6784 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6785 {
6786 /* Need to create a temp file for the snapshot */
6787 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6788 }
6789
6790 return TransactedSnapshotImpl_Construct(parentStorage,
6791 (TransactedSnapshotImpl**)result);
6792 }
6793
6794 static HRESULT Storage_Construct(
6795 HANDLE hFile,
6796 LPCOLESTR pwcsName,
6797 ILockBytes* pLkbyt,
6798 DWORD openFlags,
6799 BOOL fileBased,
6800 BOOL create,
6801 ULONG sector_size,
6802 StorageBaseImpl** result)
6803 {
6804 StorageImpl *newStorage;
6805 StorageBaseImpl *newTransactedStorage;
6806 HRESULT hr;
6807
6808 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6809 if (FAILED(hr)) goto end;
6810
6811 if (openFlags & STGM_TRANSACTED)
6812 {
6813 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6814 if (FAILED(hr))
6815 IStorage_Release(&newStorage->base.IStorage_iface);
6816 else
6817 *result = newTransactedStorage;
6818 }
6819 else
6820 *result = &newStorage->base;
6821
6822 end:
6823 return hr;
6824 }
6825
6826
6827 /************************************************************************
6828 * StorageUtl helper functions
6829 ***********************************************************************/
6830
6831 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6832 {
6833 WORD tmp;
6834
6835 memcpy(&tmp, buffer+offset, sizeof(WORD));
6836 *value = lendian16toh(tmp);
6837 }
6838
6839 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6840 {
6841 value = htole16(value);
6842 memcpy(buffer+offset, &value, sizeof(WORD));
6843 }
6844
6845 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6846 {
6847 DWORD tmp;
6848
6849 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6850 *value = lendian32toh(tmp);
6851 }
6852
6853 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6854 {
6855 value = htole32(value);
6856 memcpy(buffer+offset, &value, sizeof(DWORD));
6857 }
6858
6859 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6860 ULARGE_INTEGER* value)
6861 {
6862 #ifdef WORDS_BIGENDIAN
6863 ULARGE_INTEGER tmp;
6864
6865 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6866 value->u.LowPart = htole32(tmp.u.HighPart);
6867 value->u.HighPart = htole32(tmp.u.LowPart);
6868 #else
6869 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6870 #endif
6871 }
6872
6873 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6874 const ULARGE_INTEGER *value)
6875 {
6876 #ifdef WORDS_BIGENDIAN
6877 ULARGE_INTEGER tmp;
6878
6879 tmp.u.LowPart = htole32(value->u.HighPart);
6880 tmp.u.HighPart = htole32(value->u.LowPart);
6881 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6882 #else
6883 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6884 #endif
6885 }
6886
6887 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6888 {
6889 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6890 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6891 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6892
6893 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6894 }
6895
6896 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6897 {
6898 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6899 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6900 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6901
6902 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6903 }
6904
6905 void StorageUtl_CopyDirEntryToSTATSTG(
6906 StorageBaseImpl* storage,
6907 STATSTG* destination,
6908 const DirEntry* source,
6909 int statFlags)
6910 {
6911 /*
6912 * The copy of the string occurs only when the flag is not set
6913 */
6914 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6915 {
6916 /* Use the filename for the root storage. */
6917 destination->pwcsName = 0;
6918 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6919 }
6920 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6921 (source->name[0] == 0) )
6922 {
6923 destination->pwcsName = 0;
6924 }
6925 else
6926 {
6927 destination->pwcsName =
6928 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6929
6930 strcpyW(destination->pwcsName, source->name);
6931 }
6932
6933 switch (source->stgType)
6934 {
6935 case STGTY_STORAGE:
6936 case STGTY_ROOT:
6937 destination->type = STGTY_STORAGE;
6938 break;
6939 case STGTY_STREAM:
6940 destination->type = STGTY_STREAM;
6941 break;
6942 default:
6943 destination->type = STGTY_STREAM;
6944 break;
6945 }
6946
6947 destination->cbSize = source->size;
6948 /*
6949 currentReturnStruct->mtime = {0}; TODO
6950 currentReturnStruct->ctime = {0};
6951 currentReturnStruct->atime = {0};
6952 */
6953 destination->grfMode = 0;
6954 destination->grfLocksSupported = 0;
6955 destination->clsid = source->clsid;
6956 destination->grfStateBits = 0;
6957 destination->reserved = 0;
6958 }
6959
6960
6961 /************************************************************************
6962 * BlockChainStream implementation
6963 ***********************************************************************/
6964
6965 /******************************************************************************
6966 * BlockChainStream_GetHeadOfChain
6967 *
6968 * Returns the head of this stream chain.
6969 * Some special chains don't have directory entries, their heads are kept in
6970 * This->headOfStreamPlaceHolder.
6971 *
6972 */
6973 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6974 {
6975 DirEntry chainEntry;
6976 HRESULT hr;
6977
6978 if (This->headOfStreamPlaceHolder != 0)
6979 return *(This->headOfStreamPlaceHolder);
6980
6981 if (This->ownerDirEntry != DIRENTRY_NULL)
6982 {
6983 hr = StorageImpl_ReadDirEntry(
6984 This->parentStorage,
6985 This->ownerDirEntry,
6986 &chainEntry);
6987
6988 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
6989 return chainEntry.startingBlock;
6990 }
6991
6992 return BLOCK_END_OF_CHAIN;
6993 }
6994
6995 /* Read and save the index of all blocks in this stream. */
6996 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6997 {
6998 ULONG next_sector, next_offset;
6999 HRESULT hr;
7000 struct BlockChainRun *last_run;
7001
7002 if (This->indexCacheLen == 0)
7003 {
7004 last_run = NULL;
7005 next_offset = 0;
7006 next_sector = BlockChainStream_GetHeadOfChain(This);
7007 }
7008 else
7009 {
7010 last_run = &This->indexCache[This->indexCacheLen-1];
7011 next_offset = last_run->lastOffset+1;
7012 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7013 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7014 &next_sector);
7015 if (FAILED(hr)) return hr;
7016 }
7017
7018 while (next_sector != BLOCK_END_OF_CHAIN)
7019 {
7020 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7021 {
7022 /* Add the current block to the cache. */
7023 if (This->indexCacheSize == 0)
7024 {
7025 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7026 if (!This->indexCache) return E_OUTOFMEMORY;
7027 This->indexCacheSize = 16;
7028 }
7029 else if (This->indexCacheSize == This->indexCacheLen)
7030 {
7031 struct BlockChainRun *new_cache;
7032 ULONG new_size;
7033
7034 new_size = This->indexCacheSize * 2;
7035 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7036 if (!new_cache) return E_OUTOFMEMORY;
7037 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7038
7039 HeapFree(GetProcessHeap(), 0, This->indexCache);
7040 This->indexCache = new_cache;
7041 This->indexCacheSize = new_size;
7042 }
7043
7044 This->indexCacheLen++;
7045 last_run = &This->indexCache[This->indexCacheLen-1];
7046 last_run->firstSector = next_sector;
7047 last_run->firstOffset = next_offset;
7048 }
7049
7050 last_run->lastOffset = next_offset;
7051
7052 /* Find the next block. */
7053 next_offset++;
7054 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7055 if (FAILED(hr)) return hr;
7056 }
7057
7058 if (This->indexCacheLen)
7059 {
7060 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7061 This->numBlocks = last_run->lastOffset+1;
7062 }
7063 else
7064 {
7065 This->tailIndex = BLOCK_END_OF_CHAIN;
7066 This->numBlocks = 0;
7067 }
7068
7069 return S_OK;
7070 }
7071
7072 /* Locate the nth block in this stream. */
7073 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7074 {
7075 ULONG min_offset = 0, max_offset = This->numBlocks-1;
7076 ULONG min_run = 0, max_run = This->indexCacheLen-1;
7077
7078 if (offset >= This->numBlocks)
7079 return BLOCK_END_OF_CHAIN;
7080
7081 while (min_run < max_run)
7082 {
7083 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7084 if (offset < This->indexCache[run_to_check].firstOffset)
7085 {
7086 max_offset = This->indexCache[run_to_check].firstOffset-1;
7087 max_run = run_to_check-1;
7088 }
7089 else if (offset > This->indexCache[run_to_check].lastOffset)
7090 {
7091 min_offset = This->indexCache[run_to_check].lastOffset+1;
7092 min_run = run_to_check+1;
7093 }
7094 else
7095 /* Block is in this run. */
7096 min_run = max_run = run_to_check;
7097 }
7098
7099 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7100 }
7101
7102 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7103 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7104 {
7105 BlockChainBlock *result=NULL;
7106 int i;
7107
7108 for (i=0; i<2; i++)
7109 if (This->cachedBlocks[i].index == index)
7110 {
7111 *sector = This->cachedBlocks[i].sector;
7112 *block = &This->cachedBlocks[i];
7113 return S_OK;
7114 }
7115
7116 *sector = BlockChainStream_GetSectorOfOffset(This, index);
7117 if (*sector == BLOCK_END_OF_CHAIN)
7118 return STG_E_DOCFILECORRUPT;
7119
7120 if (create)
7121 {
7122 if (This->cachedBlocks[0].index == 0xffffffff)
7123 result = &This->cachedBlocks[0];
7124 else if (This->cachedBlocks[1].index == 0xffffffff)
7125 result = &This->cachedBlocks[1];
7126 else
7127 {
7128 result = &This->cachedBlocks[This->blockToEvict++];
7129 if (This->blockToEvict == 2)
7130 This->blockToEvict = 0;
7131 }
7132
7133 if (result->dirty)
7134 {
7135 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7136 return STG_E_WRITEFAULT;
7137 result->dirty = FALSE;
7138 }
7139
7140 result->read = FALSE;
7141 result->index = index;
7142 result->sector = *sector;
7143 }
7144
7145 *block = result;
7146 return S_OK;
7147 }
7148
7149 BlockChainStream* BlockChainStream_Construct(
7150 StorageImpl* parentStorage,
7151 ULONG* headOfStreamPlaceHolder,
7152 DirRef dirEntry)
7153 {
7154 BlockChainStream* newStream;
7155
7156 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7157
7158 newStream->parentStorage = parentStorage;
7159 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7160 newStream->ownerDirEntry = dirEntry;
7161 newStream->indexCache = NULL;
7162 newStream->indexCacheLen = 0;
7163 newStream->indexCacheSize = 0;
7164 newStream->cachedBlocks[0].index = 0xffffffff;
7165 newStream->cachedBlocks[0].dirty = FALSE;
7166 newStream->cachedBlocks[1].index = 0xffffffff;
7167 newStream->cachedBlocks[1].dirty = FALSE;
7168 newStream->blockToEvict = 0;
7169
7170 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7171 {
7172 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7173 HeapFree(GetProcessHeap(), 0, newStream);
7174 return NULL;
7175 }
7176
7177 return newStream;
7178 }
7179
7180 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7181 {
7182 int i;
7183 if (!This) return S_OK;
7184 for (i=0; i<2; i++)
7185 {
7186 if (This->cachedBlocks[i].dirty)
7187 {
7188 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7189 This->cachedBlocks[i].dirty = FALSE;
7190 else
7191 return STG_E_WRITEFAULT;
7192 }
7193 }
7194 return S_OK;
7195 }
7196
7197 void BlockChainStream_Destroy(BlockChainStream* This)
7198 {
7199 if (This)
7200 {
7201 BlockChainStream_Flush(This);
7202 HeapFree(GetProcessHeap(), 0, This->indexCache);
7203 }
7204 HeapFree(GetProcessHeap(), 0, This);
7205 }
7206
7207 /******************************************************************************
7208 * BlockChainStream_Shrink
7209 *
7210 * Shrinks this chain in the big block depot.
7211 */
7212 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7213 ULARGE_INTEGER newSize)
7214 {
7215 ULONG blockIndex;
7216 ULONG numBlocks;
7217 int i;
7218
7219 /*
7220 * Figure out how many blocks are needed to contain the new size
7221 */
7222 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7223
7224 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7225 numBlocks++;
7226
7227 if (numBlocks)
7228 {
7229 /*
7230 * Go to the new end of chain
7231 */
7232 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7233
7234 /* Mark the new end of chain */
7235 StorageImpl_SetNextBlockInChain(
7236 This->parentStorage,
7237 blockIndex,
7238 BLOCK_END_OF_CHAIN);
7239
7240 This->tailIndex = blockIndex;
7241 }
7242 else
7243 {
7244 if (This->headOfStreamPlaceHolder != 0)
7245 {
7246 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7247 }
7248 else
7249 {
7250 DirEntry chainEntry;
7251 assert(This->ownerDirEntry != DIRENTRY_NULL);
7252
7253 StorageImpl_ReadDirEntry(
7254 This->parentStorage,
7255 This->ownerDirEntry,
7256 &chainEntry);
7257
7258 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7259
7260 StorageImpl_WriteDirEntry(
7261 This->parentStorage,
7262 This->ownerDirEntry,
7263 &chainEntry);
7264 }
7265
7266 This->tailIndex = BLOCK_END_OF_CHAIN;
7267 }
7268
7269 This->numBlocks = numBlocks;
7270
7271 /*
7272 * Mark the extra blocks as free
7273 */
7274 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7275 {
7276 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7277 StorageImpl_FreeBigBlock(This->parentStorage,
7278 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7279 if (last_run->lastOffset == last_run->firstOffset)
7280 This->indexCacheLen--;
7281 else
7282 last_run->lastOffset--;
7283 }
7284
7285 /*
7286 * Reset the last accessed block cache.
7287 */
7288 for (i=0; i<2; i++)
7289 {
7290 if (This->cachedBlocks[i].index >= numBlocks)
7291 {
7292 This->cachedBlocks[i].index = 0xffffffff;
7293 This->cachedBlocks[i].dirty = FALSE;
7294 }
7295 }
7296
7297 return TRUE;
7298 }
7299
7300 /******************************************************************************
7301 * BlockChainStream_Enlarge
7302 *
7303 * Grows this chain in the big block depot.
7304 */
7305 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7306 ULARGE_INTEGER newSize)
7307 {
7308 ULONG blockIndex, currentBlock;
7309 ULONG newNumBlocks;
7310 ULONG oldNumBlocks = 0;
7311
7312 blockIndex = BlockChainStream_GetHeadOfChain(This);
7313
7314 /*
7315 * Empty chain. Create the head.
7316 */
7317 if (blockIndex == BLOCK_END_OF_CHAIN)
7318 {
7319 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7320 StorageImpl_SetNextBlockInChain(This->parentStorage,
7321 blockIndex,
7322 BLOCK_END_OF_CHAIN);
7323
7324 if (This->headOfStreamPlaceHolder != 0)
7325 {
7326 *(This->headOfStreamPlaceHolder) = blockIndex;
7327 }
7328 else
7329 {
7330 DirEntry chainEntry;
7331 assert(This->ownerDirEntry != DIRENTRY_NULL);
7332
7333 StorageImpl_ReadDirEntry(
7334 This->parentStorage,
7335 This->ownerDirEntry,
7336 &chainEntry);
7337
7338 chainEntry.startingBlock = blockIndex;
7339
7340 StorageImpl_WriteDirEntry(
7341 This->parentStorage,
7342 This->ownerDirEntry,
7343 &chainEntry);
7344 }
7345
7346 This->tailIndex = blockIndex;
7347 This->numBlocks = 1;
7348 }
7349
7350 /*
7351 * Figure out how many blocks are needed to contain this stream
7352 */
7353 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7354
7355 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7356 newNumBlocks++;
7357
7358 /*
7359 * Go to the current end of chain
7360 */
7361 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7362 {
7363 currentBlock = blockIndex;
7364
7365 while (blockIndex != BLOCK_END_OF_CHAIN)
7366 {
7367 This->numBlocks++;
7368 currentBlock = blockIndex;
7369
7370 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7371 &blockIndex)))
7372 return FALSE;
7373 }
7374
7375 This->tailIndex = currentBlock;
7376 }
7377
7378 currentBlock = This->tailIndex;
7379 oldNumBlocks = This->numBlocks;
7380
7381 /*
7382 * Add new blocks to the chain
7383 */
7384 if (oldNumBlocks < newNumBlocks)
7385 {
7386 while (oldNumBlocks < newNumBlocks)
7387 {
7388 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7389
7390 StorageImpl_SetNextBlockInChain(
7391 This->parentStorage,
7392 currentBlock,
7393 blockIndex);
7394
7395 StorageImpl_SetNextBlockInChain(
7396 This->parentStorage,
7397 blockIndex,
7398 BLOCK_END_OF_CHAIN);
7399
7400 currentBlock = blockIndex;
7401 oldNumBlocks++;
7402 }
7403
7404 This->tailIndex = blockIndex;
7405 This->numBlocks = newNumBlocks;
7406 }
7407
7408 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7409 return FALSE;
7410
7411 return TRUE;
7412 }
7413
7414
7415 /******************************************************************************
7416 * BlockChainStream_GetSize
7417 *
7418 * Returns the size of this chain.
7419 * Will return the block count if this chain doesn't have a directory entry.
7420 */
7421 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7422 {
7423 DirEntry chainEntry;
7424
7425 if(This->headOfStreamPlaceHolder == NULL)
7426 {
7427 /*
7428 * This chain has a directory entry so use the size value from there.
7429 */
7430 StorageImpl_ReadDirEntry(
7431 This->parentStorage,
7432 This->ownerDirEntry,
7433 &chainEntry);
7434
7435 return chainEntry.size;
7436 }
7437 else
7438 {
7439 /*
7440 * this chain is a chain that does not have a directory entry, figure out the
7441 * size by making the product number of used blocks times the
7442 * size of them
7443 */
7444 ULARGE_INTEGER result;
7445 result.QuadPart =
7446 (ULONGLONG)BlockChainStream_GetCount(This) *
7447 This->parentStorage->bigBlockSize;
7448
7449 return result;
7450 }
7451 }
7452
7453 /******************************************************************************
7454 * BlockChainStream_SetSize
7455 *
7456 * Sets the size of this stream. The big block depot will be updated.
7457 * The file will grow if we grow the chain.
7458 *
7459 * TODO: Free the actual blocks in the file when we shrink the chain.
7460 * Currently, the blocks are still in the file. So the file size
7461 * doesn't shrink even if we shrink streams.
7462 */
7463 BOOL BlockChainStream_SetSize(
7464 BlockChainStream* This,
7465 ULARGE_INTEGER newSize)
7466 {
7467 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7468
7469 if (newSize.QuadPart == size.QuadPart)
7470 return TRUE;
7471
7472 if (newSize.QuadPart < size.QuadPart)
7473 {
7474 BlockChainStream_Shrink(This, newSize);
7475 }
7476 else
7477 {
7478 BlockChainStream_Enlarge(This, newSize);
7479 }
7480
7481 return TRUE;
7482 }
7483
7484 /******************************************************************************
7485 * BlockChainStream_ReadAt
7486 *
7487 * Reads a specified number of bytes from this chain at the specified offset.
7488 * bytesRead may be NULL.
7489 * Failure will be returned if the specified number of bytes has not been read.
7490 */
7491 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7492 ULARGE_INTEGER offset,
7493 ULONG size,
7494 void* buffer,
7495 ULONG* bytesRead)
7496 {
7497 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7498 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7499 ULONG bytesToReadInBuffer;
7500 ULONG blockIndex;
7501 BYTE* bufferWalker;
7502 ULARGE_INTEGER stream_size;
7503 HRESULT hr;
7504 BlockChainBlock *cachedBlock;
7505
7506 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7507
7508 /*
7509 * Find the first block in the stream that contains part of the buffer.
7510 */
7511 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7512
7513 *bytesRead = 0;
7514
7515 stream_size = BlockChainStream_GetSize(This);
7516 if (stream_size.QuadPart > offset.QuadPart)
7517 size = min(stream_size.QuadPart - offset.QuadPart, size);
7518 else
7519 return S_OK;
7520
7521 /*
7522 * Start reading the buffer.
7523 */
7524 bufferWalker = buffer;
7525
7526 while (size > 0)
7527 {
7528 ULARGE_INTEGER ulOffset;
7529 DWORD bytesReadAt;
7530
7531 /*
7532 * Calculate how many bytes we can copy from this big block.
7533 */
7534 bytesToReadInBuffer =
7535 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7536
7537 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7538
7539 if (FAILED(hr))
7540 return hr;
7541
7542 if (!cachedBlock)
7543 {
7544 /* Not in cache, and we're going to read past the end of the block. */
7545 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7546 offsetInBlock;
7547
7548 StorageImpl_ReadAt(This->parentStorage,
7549 ulOffset,
7550 bufferWalker,
7551 bytesToReadInBuffer,
7552 &bytesReadAt);
7553 }
7554 else
7555 {
7556 if (!cachedBlock->read)
7557 {
7558 ULONG read;
7559 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7560 return STG_E_READFAULT;
7561
7562 cachedBlock->read = TRUE;
7563 }
7564
7565 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7566 bytesReadAt = bytesToReadInBuffer;
7567 }
7568
7569 blockNoInSequence++;
7570 bufferWalker += bytesReadAt;
7571 size -= bytesReadAt;
7572 *bytesRead += bytesReadAt;
7573 offsetInBlock = 0; /* There is no offset on the next block */
7574
7575 if (bytesToReadInBuffer != bytesReadAt)
7576 break;
7577 }
7578
7579 return S_OK;
7580 }
7581
7582 /******************************************************************************
7583 * BlockChainStream_WriteAt
7584 *
7585 * Writes the specified number of bytes to this chain at the specified offset.
7586 * Will fail if not all specified number of bytes have been written.
7587 */
7588 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7589 ULARGE_INTEGER offset,
7590 ULONG size,
7591 const void* buffer,
7592 ULONG* bytesWritten)
7593 {
7594 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7595 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7596 ULONG bytesToWrite;
7597 ULONG blockIndex;
7598 const BYTE* bufferWalker;
7599 HRESULT hr;
7600 BlockChainBlock *cachedBlock;
7601
7602 *bytesWritten = 0;
7603 bufferWalker = buffer;
7604
7605 while (size > 0)
7606 {
7607 ULARGE_INTEGER ulOffset;
7608 DWORD bytesWrittenAt;
7609
7610 /*
7611 * Calculate how many bytes we can copy to this big block.
7612 */
7613 bytesToWrite =
7614 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7615
7616 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7617
7618 /* BlockChainStream_SetSize should have already been called to ensure we have
7619 * enough blocks in the chain to write into */
7620 if (FAILED(hr))
7621 {
7622 ERR("not enough blocks in chain to write data\n");
7623 return hr;
7624 }
7625
7626 if (!cachedBlock)
7627 {
7628 /* Not in cache, and we're going to write past the end of the block. */
7629 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7630 offsetInBlock;
7631
7632 StorageImpl_WriteAt(This->parentStorage,
7633 ulOffset,
7634 bufferWalker,
7635 bytesToWrite,
7636 &bytesWrittenAt);
7637 }
7638 else
7639 {
7640 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7641 {
7642 ULONG read;
7643 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7644 return STG_E_READFAULT;
7645 }
7646
7647 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7648 bytesWrittenAt = bytesToWrite;
7649 cachedBlock->read = TRUE;
7650 cachedBlock->dirty = TRUE;
7651 }
7652
7653 blockNoInSequence++;
7654 bufferWalker += bytesWrittenAt;
7655 size -= bytesWrittenAt;
7656 *bytesWritten += bytesWrittenAt;
7657 offsetInBlock = 0; /* There is no offset on the next block */
7658
7659 if (bytesWrittenAt != bytesToWrite)
7660 break;
7661 }
7662
7663 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7664 }
7665
7666
7667 /************************************************************************
7668 * SmallBlockChainStream implementation
7669 ***********************************************************************/
7670
7671 SmallBlockChainStream* SmallBlockChainStream_Construct(
7672 StorageImpl* parentStorage,
7673 ULONG* headOfStreamPlaceHolder,
7674 DirRef dirEntry)
7675 {
7676 SmallBlockChainStream* newStream;
7677
7678 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7679
7680 newStream->parentStorage = parentStorage;
7681 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7682 newStream->ownerDirEntry = dirEntry;
7683
7684 return newStream;
7685 }
7686
7687 void SmallBlockChainStream_Destroy(
7688 SmallBlockChainStream* This)
7689 {
7690 HeapFree(GetProcessHeap(), 0, This);
7691 }
7692
7693 /******************************************************************************
7694 * SmallBlockChainStream_GetHeadOfChain
7695 *
7696 * Returns the head of this chain of small blocks.
7697 */
7698 static ULONG SmallBlockChainStream_GetHeadOfChain(
7699 SmallBlockChainStream* This)
7700 {
7701 DirEntry chainEntry;
7702 HRESULT hr;
7703
7704 if (This->headOfStreamPlaceHolder != NULL)
7705 return *(This->headOfStreamPlaceHolder);
7706
7707 if (This->ownerDirEntry)
7708 {
7709 hr = StorageImpl_ReadDirEntry(
7710 This->parentStorage,
7711 This->ownerDirEntry,
7712 &chainEntry);
7713
7714 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7715 return chainEntry.startingBlock;
7716 }
7717
7718 return BLOCK_END_OF_CHAIN;
7719 }
7720
7721 /******************************************************************************
7722 * SmallBlockChainStream_GetNextBlockInChain
7723 *
7724 * Returns the index of the next small block in this chain.
7725 *
7726 * Return Values:
7727 * - BLOCK_END_OF_CHAIN: end of this chain
7728 * - BLOCK_UNUSED: small block 'blockIndex' is free
7729 */
7730 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7731 SmallBlockChainStream* This,
7732 ULONG blockIndex,
7733 ULONG* nextBlockInChain)
7734 {
7735 ULARGE_INTEGER offsetOfBlockInDepot;
7736 DWORD buffer;
7737 ULONG bytesRead;
7738 HRESULT res;
7739
7740 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7741
7742 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7743
7744 /*
7745 * Read those bytes in the buffer from the small block file.
7746 */
7747 res = BlockChainStream_ReadAt(
7748 This->parentStorage->smallBlockDepotChain,
7749 offsetOfBlockInDepot,
7750 sizeof(DWORD),
7751 &buffer,
7752 &bytesRead);
7753
7754 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7755 res = STG_E_READFAULT;
7756
7757 if (SUCCEEDED(res))
7758 {
7759 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7760 return S_OK;
7761 }
7762
7763 return res;
7764 }
7765
7766 /******************************************************************************
7767 * SmallBlockChainStream_SetNextBlockInChain
7768 *
7769 * Writes the index of the next block of the specified block in the small
7770 * block depot.
7771 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7772 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7773 */
7774 static void SmallBlockChainStream_SetNextBlockInChain(
7775 SmallBlockChainStream* This,
7776 ULONG blockIndex,
7777 ULONG nextBlock)
7778 {
7779 ULARGE_INTEGER offsetOfBlockInDepot;
7780 DWORD buffer;
7781 ULONG bytesWritten;
7782
7783 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7784
7785 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7786
7787 /*
7788 * Read those bytes in the buffer from the small block file.
7789 */
7790 BlockChainStream_WriteAt(
7791 This->parentStorage->smallBlockDepotChain,
7792 offsetOfBlockInDepot,
7793 sizeof(DWORD),
7794 &buffer,
7795 &bytesWritten);
7796 }
7797
7798 /******************************************************************************
7799 * SmallBlockChainStream_FreeBlock
7800 *
7801 * Flag small block 'blockIndex' as free in the small block depot.
7802 */
7803 static void SmallBlockChainStream_FreeBlock(
7804 SmallBlockChainStream* This,
7805 ULONG blockIndex)
7806 {
7807 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7808 }
7809
7810 /******************************************************************************
7811 * SmallBlockChainStream_GetNextFreeBlock
7812 *
7813 * Returns the index of a free small block. The small block depot will be
7814 * enlarged if necessary. The small block chain will also be enlarged if
7815 * necessary.
7816 */
7817 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7818 SmallBlockChainStream* This)
7819 {
7820 ULARGE_INTEGER offsetOfBlockInDepot;
7821 DWORD buffer;
7822 ULONG bytesRead;
7823 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7824 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7825 HRESULT res = S_OK;
7826 ULONG smallBlocksPerBigBlock;
7827 DirEntry rootEntry;
7828 ULONG blocksRequired;
7829 ULARGE_INTEGER old_size, size_required;
7830
7831 offsetOfBlockInDepot.u.HighPart = 0;
7832
7833 /*
7834 * Scan the small block depot for a free block
7835 */
7836 while (nextBlockIndex != BLOCK_UNUSED)
7837 {
7838 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7839
7840 res = BlockChainStream_ReadAt(
7841 This->parentStorage->smallBlockDepotChain,
7842 offsetOfBlockInDepot,
7843 sizeof(DWORD),
7844 &buffer,
7845 &bytesRead);
7846
7847 /*
7848 * If we run out of space for the small block depot, enlarge it
7849 */
7850 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7851 {
7852 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7853
7854 if (nextBlockIndex != BLOCK_UNUSED)
7855 blockIndex++;
7856 }
7857 else
7858 {
7859 ULONG count =
7860 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7861
7862 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7863 ULARGE_INTEGER newSize, offset;
7864 ULONG bytesWritten;
7865
7866 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7867 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7868
7869 /*
7870 * Initialize all the small blocks to free
7871 */
7872 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7873 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7874 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7875 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7876
7877 StorageImpl_SaveFileHeader(This->parentStorage);
7878 }
7879 }
7880
7881 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7882
7883 smallBlocksPerBigBlock =
7884 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7885
7886 /*
7887 * Verify if we have to allocate big blocks to contain small blocks
7888 */
7889 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7890
7891 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7892
7893 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7894
7895 if (size_required.QuadPart > old_size.QuadPart)
7896 {
7897 BlockChainStream_SetSize(
7898 This->parentStorage->smallBlockRootChain,
7899 size_required);
7900
7901 StorageImpl_ReadDirEntry(
7902 This->parentStorage,
7903 This->parentStorage->base.storageDirEntry,
7904 &rootEntry);
7905
7906 rootEntry.size = size_required;
7907
7908 StorageImpl_WriteDirEntry(
7909 This->parentStorage,
7910 This->parentStorage->base.storageDirEntry,
7911 &rootEntry);
7912 }
7913
7914 return blockIndex;
7915 }
7916
7917 /******************************************************************************
7918 * SmallBlockChainStream_ReadAt
7919 *
7920 * Reads a specified number of bytes from this chain at the specified offset.
7921 * bytesRead may be NULL.
7922 * Failure will be returned if the specified number of bytes has not been read.
7923 */
7924 HRESULT SmallBlockChainStream_ReadAt(
7925 SmallBlockChainStream* This,
7926 ULARGE_INTEGER offset,
7927 ULONG size,
7928 void* buffer,
7929 ULONG* bytesRead)
7930 {
7931 HRESULT rc = S_OK;
7932 ULARGE_INTEGER offsetInBigBlockFile;
7933 ULONG blockNoInSequence =
7934 offset.u.LowPart / This->parentStorage->smallBlockSize;
7935
7936 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7937 ULONG bytesToReadInBuffer;
7938 ULONG blockIndex;
7939 ULONG bytesReadFromBigBlockFile;
7940 BYTE* bufferWalker;
7941 ULARGE_INTEGER stream_size;
7942
7943 /*
7944 * This should never happen on a small block file.
7945 */
7946 assert(offset.u.HighPart==0);
7947
7948 *bytesRead = 0;
7949
7950 stream_size = SmallBlockChainStream_GetSize(This);
7951 if (stream_size.QuadPart > offset.QuadPart)
7952 size = min(stream_size.QuadPart - offset.QuadPart, size);
7953 else
7954 return S_OK;
7955
7956 /*
7957 * Find the first block in the stream that contains part of the buffer.
7958 */
7959 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7960
7961 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7962 {
7963 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7964 if(FAILED(rc))
7965 return rc;
7966 blockNoInSequence--;
7967 }
7968
7969 /*
7970 * Start reading the buffer.
7971 */
7972 bufferWalker = buffer;
7973
7974 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7975 {
7976 /*
7977 * Calculate how many bytes we can copy from this small block.
7978 */
7979 bytesToReadInBuffer =
7980 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7981
7982 /*
7983 * Calculate the offset of the small block in the small block file.
7984 */
7985 offsetInBigBlockFile.QuadPart =
7986 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7987
7988 offsetInBigBlockFile.QuadPart += offsetInBlock;
7989
7990 /*
7991 * Read those bytes in the buffer from the small block file.
7992 * The small block has already been identified so it shouldn't fail
7993 * unless the file is corrupt.
7994 */
7995 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7996 offsetInBigBlockFile,
7997 bytesToReadInBuffer,
7998 bufferWalker,
7999 &bytesReadFromBigBlockFile);
8000
8001 if (FAILED(rc))
8002 return rc;
8003
8004 if (!bytesReadFromBigBlockFile)
8005 return STG_E_DOCFILECORRUPT;
8006
8007 /*
8008 * Step to the next big block.
8009 */
8010 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8011 if(FAILED(rc))
8012 return STG_E_DOCFILECORRUPT;
8013
8014 bufferWalker += bytesReadFromBigBlockFile;
8015 size -= bytesReadFromBigBlockFile;
8016 *bytesRead += bytesReadFromBigBlockFile;
8017 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8018 }
8019
8020 return S_OK;
8021 }
8022
8023 /******************************************************************************
8024 * SmallBlockChainStream_WriteAt
8025 *
8026 * Writes the specified number of bytes to this chain at the specified offset.
8027 * Will fail if not all specified number of bytes have been written.
8028 */
8029 HRESULT SmallBlockChainStream_WriteAt(
8030 SmallBlockChainStream* This,
8031 ULARGE_INTEGER offset,
8032 ULONG size,
8033 const void* buffer,
8034 ULONG* bytesWritten)
8035 {
8036 ULARGE_INTEGER offsetInBigBlockFile;
8037 ULONG blockNoInSequence =
8038 offset.u.LowPart / This->parentStorage->smallBlockSize;
8039
8040 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8041 ULONG bytesToWriteInBuffer;
8042 ULONG blockIndex;
8043 ULONG bytesWrittenToBigBlockFile;
8044 const BYTE* bufferWalker;
8045 HRESULT res;
8046
8047 /*
8048 * This should never happen on a small block file.
8049 */
8050 assert(offset.u.HighPart==0);
8051
8052 /*
8053 * Find the first block in the stream that contains part of the buffer.
8054 */
8055 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8056
8057 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8058 {
8059 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8060 return STG_E_DOCFILECORRUPT;
8061 blockNoInSequence--;
8062 }
8063
8064 /*
8065 * Start writing the buffer.
8066 */
8067 *bytesWritten = 0;
8068 bufferWalker = buffer;
8069 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8070 {
8071 /*
8072 * Calculate how many bytes we can copy to this small block.
8073 */
8074 bytesToWriteInBuffer =
8075 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8076
8077 /*
8078 * Calculate the offset of the small block in the small block file.
8079 */
8080 offsetInBigBlockFile.QuadPart =
8081 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8082
8083 offsetInBigBlockFile.QuadPart += offsetInBlock;
8084
8085 /*
8086 * Write those bytes in the buffer to the small block file.
8087 */
8088 res = BlockChainStream_WriteAt(
8089 This->parentStorage->smallBlockRootChain,
8090 offsetInBigBlockFile,
8091 bytesToWriteInBuffer,
8092 bufferWalker,
8093 &bytesWrittenToBigBlockFile);
8094 if (FAILED(res))
8095 return res;
8096
8097 /*
8098 * Step to the next big block.
8099 */
8100 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8101 if (FAILED(res))
8102 return res;
8103 bufferWalker += bytesWrittenToBigBlockFile;
8104 size -= bytesWrittenToBigBlockFile;
8105 *bytesWritten += bytesWrittenToBigBlockFile;
8106 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8107 }
8108
8109 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8110 }
8111
8112 /******************************************************************************
8113 * SmallBlockChainStream_Shrink
8114 *
8115 * Shrinks this chain in the small block depot.
8116 */
8117 static BOOL SmallBlockChainStream_Shrink(
8118 SmallBlockChainStream* This,
8119 ULARGE_INTEGER newSize)
8120 {
8121 ULONG blockIndex, extraBlock;
8122 ULONG numBlocks;
8123 ULONG count = 0;
8124
8125 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8126
8127 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8128 numBlocks++;
8129
8130 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8131
8132 /*
8133 * Go to the new end of chain
8134 */
8135 while (count < numBlocks)
8136 {
8137 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8138 &blockIndex)))
8139 return FALSE;
8140 count++;
8141 }
8142
8143 /*
8144 * If the count is 0, we have a special case, the head of the chain was
8145 * just freed.
8146 */
8147 if (count == 0)
8148 {
8149 DirEntry chainEntry;
8150
8151 StorageImpl_ReadDirEntry(This->parentStorage,
8152 This->ownerDirEntry,
8153 &chainEntry);
8154
8155 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8156
8157 StorageImpl_WriteDirEntry(This->parentStorage,
8158 This->ownerDirEntry,
8159 &chainEntry);
8160
8161 /*
8162 * We start freeing the chain at the head block.
8163 */
8164 extraBlock = blockIndex;
8165 }
8166 else
8167 {
8168 /* Get the next block before marking the new end */
8169 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8170 &extraBlock)))
8171 return FALSE;
8172
8173 /* Mark the new end of chain */
8174 SmallBlockChainStream_SetNextBlockInChain(
8175 This,
8176 blockIndex,
8177 BLOCK_END_OF_CHAIN);
8178 }
8179
8180 /*
8181 * Mark the extra blocks as free
8182 */
8183 while (extraBlock != BLOCK_END_OF_CHAIN)
8184 {
8185 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8186 &blockIndex)))
8187 return FALSE;
8188 SmallBlockChainStream_FreeBlock(This, extraBlock);
8189 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8190 extraBlock = blockIndex;
8191 }
8192
8193 return TRUE;
8194 }
8195
8196 /******************************************************************************
8197 * SmallBlockChainStream_Enlarge
8198 *
8199 * Grows this chain in the small block depot.
8200 */
8201 static BOOL SmallBlockChainStream_Enlarge(
8202 SmallBlockChainStream* This,
8203 ULARGE_INTEGER newSize)
8204 {
8205 ULONG blockIndex, currentBlock;
8206 ULONG newNumBlocks;
8207 ULONG oldNumBlocks = 0;
8208
8209 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8210
8211 /*
8212 * Empty chain. Create the head.
8213 */
8214 if (blockIndex == BLOCK_END_OF_CHAIN)
8215 {
8216 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8217 SmallBlockChainStream_SetNextBlockInChain(
8218 This,
8219 blockIndex,
8220 BLOCK_END_OF_CHAIN);
8221
8222 if (This->headOfStreamPlaceHolder != NULL)
8223 {
8224 *(This->headOfStreamPlaceHolder) = blockIndex;
8225 }
8226 else
8227 {
8228 DirEntry chainEntry;
8229
8230 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8231 &chainEntry);
8232
8233 chainEntry.startingBlock = blockIndex;
8234
8235 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8236 &chainEntry);
8237 }
8238 }
8239
8240 currentBlock = blockIndex;
8241
8242 /*
8243 * Figure out how many blocks are needed to contain this stream
8244 */
8245 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8246
8247 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8248 newNumBlocks++;
8249
8250 /*
8251 * Go to the current end of chain
8252 */
8253 while (blockIndex != BLOCK_END_OF_CHAIN)
8254 {
8255 oldNumBlocks++;
8256 currentBlock = blockIndex;
8257 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8258 return FALSE;
8259 }
8260
8261 /*
8262 * Add new blocks to the chain
8263 */
8264 while (oldNumBlocks < newNumBlocks)
8265 {
8266 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8267 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8268
8269 SmallBlockChainStream_SetNextBlockInChain(
8270 This,
8271 blockIndex,
8272 BLOCK_END_OF_CHAIN);
8273
8274 currentBlock = blockIndex;
8275 oldNumBlocks++;
8276 }
8277
8278 return TRUE;
8279 }
8280
8281 /******************************************************************************
8282 * SmallBlockChainStream_SetSize
8283 *
8284 * Sets the size of this stream.
8285 * The file will grow if we grow the chain.
8286 *
8287 * TODO: Free the actual blocks in the file when we shrink the chain.
8288 * Currently, the blocks are still in the file. So the file size
8289 * doesn't shrink even if we shrink streams.
8290 */
8291 BOOL SmallBlockChainStream_SetSize(
8292 SmallBlockChainStream* This,
8293 ULARGE_INTEGER newSize)
8294 {
8295 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8296
8297 if (newSize.u.LowPart == size.u.LowPart)
8298 return TRUE;
8299
8300 if (newSize.u.LowPart < size.u.LowPart)
8301 {
8302 SmallBlockChainStream_Shrink(This, newSize);
8303 }
8304 else
8305 {
8306 SmallBlockChainStream_Enlarge(This, newSize);
8307 }
8308
8309 return TRUE;
8310 }
8311
8312 /******************************************************************************
8313 * SmallBlockChainStream_GetCount
8314 *
8315 * Returns the number of small blocks that comprises this chain.
8316 * This is not the size of the stream as the last block may not be full!
8317 *
8318 */
8319 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8320 {
8321 ULONG blockIndex;
8322 ULONG count = 0;
8323
8324 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8325
8326 while(blockIndex != BLOCK_END_OF_CHAIN)
8327 {
8328 count++;
8329
8330 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8331 blockIndex, &blockIndex)))
8332 return 0;
8333 }
8334
8335 return count;
8336 }
8337
8338 /******************************************************************************
8339 * SmallBlockChainStream_GetSize
8340 *
8341 * Returns the size of this chain.
8342 */
8343 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8344 {
8345 DirEntry chainEntry;
8346
8347 if(This->headOfStreamPlaceHolder != NULL)
8348 {
8349 ULARGE_INTEGER result;
8350 result.u.HighPart = 0;
8351
8352 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8353 This->parentStorage->smallBlockSize;
8354
8355 return result;
8356 }
8357
8358 StorageImpl_ReadDirEntry(
8359 This->parentStorage,
8360 This->ownerDirEntry,
8361 &chainEntry);
8362
8363 return chainEntry.size;
8364 }
8365
8366
8367 /************************************************************************
8368 * Miscellaneous storage functions
8369 ***********************************************************************/
8370
8371 static HRESULT create_storagefile(
8372 LPCOLESTR pwcsName,
8373 DWORD grfMode,
8374 DWORD grfAttrs,
8375 STGOPTIONS* pStgOptions,
8376 REFIID riid,
8377 void** ppstgOpen)
8378 {
8379 StorageBaseImpl* newStorage = 0;
8380 HANDLE hFile = INVALID_HANDLE_VALUE;
8381 HRESULT hr = STG_E_INVALIDFLAG;
8382 DWORD shareMode;
8383 DWORD accessMode;
8384 DWORD creationMode;
8385 DWORD fileAttributes;
8386 WCHAR tempFileName[MAX_PATH];
8387
8388 if (ppstgOpen == 0)
8389 return STG_E_INVALIDPOINTER;
8390
8391 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8392 return STG_E_INVALIDPARAMETER;
8393
8394 /* if no share mode given then DENY_NONE is the default */
8395 if (STGM_SHARE_MODE(grfMode) == 0)
8396 grfMode |= STGM_SHARE_DENY_NONE;
8397
8398 if ( FAILED( validateSTGM(grfMode) ))
8399 goto end;
8400
8401 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8402 switch(STGM_ACCESS_MODE(grfMode))
8403 {
8404 case STGM_WRITE:
8405 case STGM_READWRITE:
8406 break;
8407 default:
8408 goto end;
8409 }
8410
8411 /* in direct mode, can only use SHARE_EXCLUSIVE */
8412 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8413 goto end;
8414
8415 /* but in transacted mode, any share mode is valid */
8416
8417 /*
8418 * Generate a unique name.
8419 */
8420 if (pwcsName == 0)
8421 {
8422 WCHAR tempPath[MAX_PATH];
8423 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8424
8425 memset(tempPath, 0, sizeof(tempPath));
8426 memset(tempFileName, 0, sizeof(tempFileName));
8427
8428 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8429 tempPath[0] = '.';
8430
8431 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8432 pwcsName = tempFileName;
8433 else
8434 {
8435 hr = STG_E_INSUFFICIENTMEMORY;
8436 goto end;
8437 }
8438
8439 creationMode = TRUNCATE_EXISTING;
8440 }
8441 else
8442 {
8443 creationMode = GetCreationModeFromSTGM(grfMode);
8444 }
8445
8446 /*
8447 * Interpret the STGM value grfMode
8448 */
8449 shareMode = GetShareModeFromSTGM(grfMode);
8450 accessMode = GetAccessModeFromSTGM(grfMode);
8451
8452 if (grfMode & STGM_DELETEONRELEASE)
8453 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8454 else
8455 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8456
8457 *ppstgOpen = 0;
8458
8459 hFile = CreateFileW(pwcsName,
8460 accessMode,
8461 shareMode,
8462 NULL,
8463 creationMode,
8464 fileAttributes,
8465 0);
8466
8467 if (hFile == INVALID_HANDLE_VALUE)
8468 {
8469 if(GetLastError() == ERROR_FILE_EXISTS)
8470 hr = STG_E_FILEALREADYEXISTS;
8471 else
8472 hr = E_FAIL;
8473 goto end;
8474 }
8475
8476 /*
8477 * Allocate and initialize the new IStorage object.
8478 */
8479 hr = Storage_Construct(
8480 hFile,
8481 pwcsName,
8482 NULL,
8483 grfMode,
8484 TRUE,
8485 TRUE,
8486 pStgOptions->ulSectorSize,
8487 &newStorage);
8488
8489 if (FAILED(hr))
8490 {
8491 goto end;
8492 }
8493
8494 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8495 IStorage_Release(&newStorage->IStorage_iface);
8496
8497 end:
8498 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8499
8500 return hr;
8501 }
8502
8503 /******************************************************************************
8504 * StgCreateDocfile [OLE32.@]
8505 * Creates a new compound file storage object
8506 *
8507 * PARAMS
8508 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8509 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8510 * reserved [ ?] unused?, usually 0
8511 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8512 *
8513 * RETURNS
8514 * S_OK if the file was successfully created
8515 * some STG_E_ value if error
8516 * NOTES
8517 * if pwcsName is NULL, create file with new unique name
8518 * the function can returns
8519 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8520 * (unrealized now)
8521 */
8522 HRESULT WINAPI StgCreateDocfile(
8523 LPCOLESTR pwcsName,
8524 DWORD grfMode,
8525 DWORD reserved,
8526 IStorage **ppstgOpen)
8527 {
8528 STGOPTIONS stgoptions = {1, 0, 512};
8529
8530 TRACE("(%s, %x, %d, %p)\n",
8531 debugstr_w(pwcsName), grfMode,
8532 reserved, ppstgOpen);
8533
8534 if (ppstgOpen == 0)
8535 return STG_E_INVALIDPOINTER;
8536 if (reserved != 0)
8537 return STG_E_INVALIDPARAMETER;
8538
8539 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8540 }
8541
8542 /******************************************************************************
8543 * StgCreateStorageEx [OLE32.@]
8544 */
8545 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8546 {
8547 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8548 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8549
8550 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8551 {
8552 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8553 return STG_E_INVALIDPARAMETER;
8554 }
8555
8556 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8557 {
8558 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8559 return STG_E_INVALIDPARAMETER;
8560 }
8561
8562 if (stgfmt == STGFMT_FILE)
8563 {
8564 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8565 return STG_E_INVALIDPARAMETER;
8566 }
8567
8568 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8569 {
8570 STGOPTIONS defaultOptions = {1, 0, 512};
8571
8572 if (!pStgOptions) pStgOptions = &defaultOptions;
8573 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8574 }
8575
8576
8577 ERR("Invalid stgfmt argument\n");
8578 return STG_E_INVALIDPARAMETER;
8579 }
8580
8581 /******************************************************************************
8582 * StgCreatePropSetStg [OLE32.@]
8583 */
8584 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8585 IPropertySetStorage **propset)
8586 {
8587 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8588 if (reserved)
8589 return STG_E_INVALIDPARAMETER;
8590
8591 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8592 }
8593
8594 /******************************************************************************
8595 * StgOpenStorageEx [OLE32.@]
8596 */
8597 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8598 {
8599 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8600 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8601
8602 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8603 {
8604 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8605 return STG_E_INVALIDPARAMETER;
8606 }
8607
8608 switch (stgfmt)
8609 {
8610 case STGFMT_FILE:
8611 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8612 return STG_E_INVALIDPARAMETER;
8613
8614 case STGFMT_STORAGE:
8615 break;
8616
8617 case STGFMT_DOCFILE:
8618 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8619 {
8620 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8621 return STG_E_INVALIDPARAMETER;
8622 }
8623 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8624 break;
8625
8626 case STGFMT_ANY:
8627 WARN("STGFMT_ANY assuming storage\n");
8628 break;
8629
8630 default:
8631 return STG_E_INVALIDPARAMETER;
8632 }
8633
8634 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8635 }
8636
8637
8638 /******************************************************************************
8639 * StgOpenStorage [OLE32.@]
8640 */
8641 HRESULT WINAPI StgOpenStorage(
8642 const OLECHAR *pwcsName,
8643 IStorage *pstgPriority,
8644 DWORD grfMode,
8645 SNB snbExclude,
8646 DWORD reserved,
8647 IStorage **ppstgOpen)
8648 {
8649 StorageBaseImpl* newStorage = 0;
8650 HRESULT hr = S_OK;
8651 HANDLE hFile = 0;
8652 DWORD shareMode;
8653 DWORD accessMode;
8654 LPWSTR temp_name = NULL;
8655
8656 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8657 debugstr_w(pwcsName), pstgPriority, grfMode,
8658 snbExclude, reserved, ppstgOpen);
8659
8660 if (pstgPriority)
8661 {
8662 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8663 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8664 if (FAILED(hr)) goto end;
8665 pwcsName = temp_name;
8666 TRACE("using filename %s\n", debugstr_w(temp_name));
8667 }
8668
8669 if (pwcsName == 0)
8670 {
8671 hr = STG_E_INVALIDNAME;
8672 goto end;
8673 }
8674
8675 if (ppstgOpen == 0)
8676 {
8677 hr = STG_E_INVALIDPOINTER;
8678 goto end;
8679 }
8680
8681 if (reserved)
8682 {
8683 hr = STG_E_INVALIDPARAMETER;
8684 goto end;
8685 }
8686
8687 if (grfMode & STGM_PRIORITY)
8688 {
8689 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8690 return STG_E_INVALIDFLAG;
8691 if (grfMode & STGM_DELETEONRELEASE)
8692 return STG_E_INVALIDFUNCTION;
8693 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8694 return STG_E_INVALIDFLAG;
8695 grfMode &= ~0xf0; /* remove the existing sharing mode */
8696 grfMode |= STGM_SHARE_DENY_NONE;
8697 }
8698
8699 /*
8700 * Validate the sharing mode
8701 */
8702 if (grfMode & STGM_DIRECT_SWMR)
8703 {
8704 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8705 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8706 {
8707 hr = STG_E_INVALIDFLAG;
8708 goto end;
8709 }
8710 }
8711 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8712 switch(STGM_SHARE_MODE(grfMode))
8713 {
8714 case STGM_SHARE_EXCLUSIVE:
8715 case STGM_SHARE_DENY_WRITE:
8716 break;
8717 default:
8718 hr = STG_E_INVALIDFLAG;
8719 goto end;
8720 }
8721
8722 if ( FAILED( validateSTGM(grfMode) ) ||
8723 (grfMode&STGM_CREATE))
8724 {
8725 hr = STG_E_INVALIDFLAG;
8726 goto end;
8727 }
8728
8729 /* shared reading requires transacted or single writer mode */
8730 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8731 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8732 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8733 {
8734 hr = STG_E_INVALIDFLAG;
8735 goto end;
8736 }
8737
8738 /*
8739 * Interpret the STGM value grfMode
8740 */
8741 shareMode = GetShareModeFromSTGM(grfMode);
8742 accessMode = GetAccessModeFromSTGM(grfMode);
8743
8744 *ppstgOpen = 0;
8745
8746 hFile = CreateFileW( pwcsName,
8747 accessMode,
8748 shareMode,
8749 NULL,
8750 OPEN_EXISTING,
8751 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8752 0);
8753
8754 if (hFile==INVALID_HANDLE_VALUE)
8755 {
8756 DWORD last_error = GetLastError();
8757
8758 hr = E_FAIL;
8759
8760 switch (last_error)
8761 {
8762 case ERROR_FILE_NOT_FOUND:
8763 hr = STG_E_FILENOTFOUND;
8764 break;
8765
8766 case ERROR_PATH_NOT_FOUND:
8767 hr = STG_E_PATHNOTFOUND;
8768 break;
8769
8770 case ERROR_ACCESS_DENIED:
8771 case ERROR_WRITE_PROTECT:
8772 hr = STG_E_ACCESSDENIED;
8773 break;
8774
8775 case ERROR_SHARING_VIOLATION:
8776 hr = STG_E_SHAREVIOLATION;
8777 break;
8778
8779 default:
8780 hr = E_FAIL;
8781 }
8782
8783 goto end;
8784 }
8785
8786 /*
8787 * Refuse to open the file if it's too small to be a structured storage file
8788 * FIXME: verify the file when reading instead of here
8789 */
8790 if (GetFileSize(hFile, NULL) < 0x100)
8791 {
8792 CloseHandle(hFile);
8793 hr = STG_E_FILEALREADYEXISTS;
8794 goto end;
8795 }
8796
8797 /*
8798 * Allocate and initialize the new IStorage object.
8799 */
8800 hr = Storage_Construct(
8801 hFile,
8802 pwcsName,
8803 NULL,
8804 grfMode,
8805 TRUE,
8806 FALSE,
8807 512,
8808 &newStorage);
8809
8810 if (FAILED(hr))
8811 {
8812 /*
8813 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8814 */
8815 if(hr == STG_E_INVALIDHEADER)
8816 hr = STG_E_FILEALREADYEXISTS;
8817 goto end;
8818 }
8819
8820 *ppstgOpen = &newStorage->IStorage_iface;
8821
8822 end:
8823 CoTaskMemFree(temp_name);
8824 if (pstgPriority) IStorage_Release(pstgPriority);
8825 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8826 return hr;
8827 }
8828
8829 /******************************************************************************
8830 * StgCreateDocfileOnILockBytes [OLE32.@]
8831 */
8832 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8833 ILockBytes *plkbyt,
8834 DWORD grfMode,
8835 DWORD reserved,
8836 IStorage** ppstgOpen)
8837 {
8838 StorageBaseImpl* newStorage = 0;
8839 HRESULT hr = S_OK;
8840
8841 if ((ppstgOpen == 0) || (plkbyt == 0))
8842 return STG_E_INVALIDPOINTER;
8843
8844 /*
8845 * Allocate and initialize the new IStorage object.
8846 */
8847 hr = Storage_Construct(
8848 0,
8849 0,
8850 plkbyt,
8851 grfMode,
8852 FALSE,
8853 TRUE,
8854 512,
8855 &newStorage);
8856
8857 if (FAILED(hr))
8858 {
8859 return hr;
8860 }
8861
8862 *ppstgOpen = &newStorage->IStorage_iface;
8863
8864 return hr;
8865 }
8866
8867 /******************************************************************************
8868 * StgOpenStorageOnILockBytes [OLE32.@]
8869 */
8870 HRESULT WINAPI StgOpenStorageOnILockBytes(
8871 ILockBytes *plkbyt,
8872 IStorage *pstgPriority,
8873 DWORD grfMode,
8874 SNB snbExclude,
8875 DWORD reserved,
8876 IStorage **ppstgOpen)
8877 {
8878 StorageBaseImpl* newStorage = 0;
8879 HRESULT hr = S_OK;
8880
8881 if ((plkbyt == 0) || (ppstgOpen == 0))
8882 return STG_E_INVALIDPOINTER;
8883
8884 if ( FAILED( validateSTGM(grfMode) ))
8885 return STG_E_INVALIDFLAG;
8886
8887 *ppstgOpen = 0;
8888
8889 /*
8890 * Allocate and initialize the new IStorage object.
8891 */
8892 hr = Storage_Construct(
8893 0,
8894 0,
8895 plkbyt,
8896 grfMode,
8897 FALSE,
8898 FALSE,
8899 512,
8900 &newStorage);
8901
8902 if (FAILED(hr))
8903 {
8904 return hr;
8905 }
8906
8907 *ppstgOpen = &newStorage->IStorage_iface;
8908
8909 return hr;
8910 }
8911
8912 /******************************************************************************
8913 * StgSetTimes [ole32.@]
8914 * StgSetTimes [OLE32.@]
8915 *
8916 *
8917 */
8918 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8919 FILETIME const *patime, FILETIME const *pmtime)
8920 {
8921 IStorage *stg = NULL;
8922 HRESULT r;
8923
8924 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8925
8926 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8927 0, 0, &stg);
8928 if( SUCCEEDED(r) )
8929 {
8930 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8931 IStorage_Release(stg);
8932 }
8933
8934 return r;
8935 }
8936
8937 /******************************************************************************
8938 * StgIsStorageILockBytes [OLE32.@]
8939 *
8940 * Determines if the ILockBytes contains a storage object.
8941 */
8942 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8943 {
8944 BYTE sig[sizeof(STORAGE_magic)];
8945 ULARGE_INTEGER offset;
8946 ULONG read = 0;
8947
8948 offset.u.HighPart = 0;
8949 offset.u.LowPart = 0;
8950
8951 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8952
8953 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8954 return S_OK;
8955
8956 return S_FALSE;
8957 }
8958
8959 /******************************************************************************
8960 * WriteClassStg [OLE32.@]
8961 *
8962 * This method will store the specified CLSID in the specified storage object
8963 */
8964 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8965 {
8966 if(!pStg)
8967 return E_INVALIDARG;
8968
8969 if(!rclsid)
8970 return STG_E_INVALIDPOINTER;
8971
8972 return IStorage_SetClass(pStg, rclsid);
8973 }
8974
8975 /***********************************************************************
8976 * ReadClassStg (OLE32.@)
8977 *
8978 * This method reads the CLSID previously written to a storage object with
8979 * the WriteClassStg.
8980 *
8981 * PARAMS
8982 * pstg [I] IStorage pointer
8983 * pclsid [O] Pointer to where the CLSID is written
8984 *
8985 * RETURNS
8986 * Success: S_OK.
8987 * Failure: HRESULT code.
8988 */
8989 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8990
8991 STATSTG pstatstg;
8992 HRESULT hRes;
8993
8994 TRACE("(%p, %p)\n", pstg, pclsid);
8995
8996 if(!pstg || !pclsid)
8997 return E_INVALIDARG;
8998
8999 /*
9000 * read a STATSTG structure (contains the clsid) from the storage
9001 */
9002 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9003
9004 if(SUCCEEDED(hRes))
9005 *pclsid=pstatstg.clsid;
9006
9007 return hRes;
9008 }
9009
9010 /***********************************************************************
9011 * OleLoadFromStream (OLE32.@)
9012 *
9013 * This function loads an object from stream
9014 */
9015 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9016 {
9017 CLSID clsid;
9018 HRESULT res;
9019 LPPERSISTSTREAM xstm;
9020
9021 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9022
9023 res=ReadClassStm(pStm,&clsid);
9024 if (FAILED(res))
9025 return res;
9026 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9027 if (FAILED(res))
9028 return res;
9029 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9030 if (FAILED(res)) {
9031 IUnknown_Release((IUnknown*)*ppvObj);
9032 return res;
9033 }
9034 res=IPersistStream_Load(xstm,pStm);
9035 IPersistStream_Release(xstm);
9036 /* FIXME: all refcounts ok at this point? I think they should be:
9037 * pStm : unchanged
9038 * ppvObj : 1
9039 * xstm : 0 (released)
9040 */
9041 return res;
9042 }
9043
9044 /***********************************************************************
9045 * OleSaveToStream (OLE32.@)
9046 *
9047 * This function saves an object with the IPersistStream interface on it
9048 * to the specified stream.
9049 */
9050 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9051 {
9052
9053 CLSID clsid;
9054 HRESULT res;
9055
9056 TRACE("(%p,%p)\n",pPStm,pStm);
9057
9058 res=IPersistStream_GetClassID(pPStm,&clsid);
9059
9060 if (SUCCEEDED(res)){
9061
9062 res=WriteClassStm(pStm,&clsid);
9063
9064 if (SUCCEEDED(res))
9065
9066 res=IPersistStream_Save(pPStm,pStm,TRUE);
9067 }
9068
9069 TRACE("Finished Save\n");
9070 return res;
9071 }
9072
9073 /*************************************************************************
9074 * STORAGE_CreateOleStream [Internal]
9075 *
9076 * Creates the "\001OLE" stream in the IStorage if necessary.
9077 *
9078 * PARAMS
9079 * storage [I] Dest storage to create the stream in
9080 * flags [I] flags to be set for newly created stream
9081 *
9082 * RETURNS
9083 * HRESULT return value
9084 *
9085 * NOTES
9086 *
9087 * This stream is still unknown, MS Word seems to have extra data
9088 * but since the data is stored in the OLESTREAM there should be
9089 * no need to recreate the stream. If the stream is manually
9090 * deleted it will create it with this default data.
9091 *
9092 */
9093 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9094 {
9095 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9096 static const DWORD version_magic = 0x02000001;
9097 IStream *stream;
9098 HRESULT hr;
9099
9100 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9101 if (hr == S_OK)
9102 {
9103 struct empty_1ole_stream {
9104 DWORD version_magic;
9105 DWORD flags;
9106 DWORD update_options;
9107 DWORD reserved;
9108 DWORD mon_stream_size;
9109 };
9110 struct empty_1ole_stream stream_data;
9111
9112 stream_data.version_magic = version_magic;
9113 stream_data.flags = flags;
9114 stream_data.update_options = 0;
9115 stream_data.reserved = 0;
9116 stream_data.mon_stream_size = 0;
9117
9118 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9119 IStream_Release(stream);
9120 }
9121
9122 return hr;
9123 }
9124
9125 /* write a string to a stream, preceded by its length */
9126 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9127 {
9128 HRESULT r;
9129 LPSTR str;
9130 DWORD len = 0;
9131
9132 if( string )
9133 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9134 r = IStream_Write( stm, &len, sizeof(len), NULL);
9135 if( FAILED( r ) )
9136 return r;
9137 if(len == 0)
9138 return r;
9139 str = CoTaskMemAlloc( len );
9140 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9141 r = IStream_Write( stm, str, len, NULL);
9142 CoTaskMemFree( str );
9143 return r;
9144 }
9145
9146 /* read a string preceded by its length from a stream */
9147 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9148 {
9149 HRESULT r;
9150 DWORD len, count = 0;
9151 LPSTR str;
9152 LPWSTR wstr;
9153
9154 r = IStream_Read( stm, &len, sizeof(len), &count );
9155 if( FAILED( r ) )
9156 return r;
9157 if( count != sizeof(len) )
9158 return E_OUTOFMEMORY;
9159
9160 TRACE("%d bytes\n",len);
9161
9162 str = CoTaskMemAlloc( len );
9163 if( !str )
9164 return E_OUTOFMEMORY;
9165 count = 0;
9166 r = IStream_Read( stm, str, len, &count );
9167 if( FAILED( r ) )
9168 return r;
9169 if( count != len )
9170 {
9171 CoTaskMemFree( str );
9172 return E_OUTOFMEMORY;
9173 }
9174
9175 TRACE("Read string %s\n",debugstr_an(str,len));
9176
9177 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9178 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9179 if( wstr )
9180 {
9181 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9182 wstr[len] = 0;
9183 }
9184 CoTaskMemFree( str );
9185
9186 *string = wstr;
9187
9188 return r;
9189 }
9190
9191
9192 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9193 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9194 {
9195 IStream *pstm;
9196 HRESULT r = S_OK;
9197 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9198
9199 static const BYTE unknown1[12] =
9200 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9201 0xFF, 0xFF, 0xFF, 0xFF};
9202 static const BYTE unknown2[16] =
9203 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9205
9206 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9207 debugstr_w(lpszUserType), debugstr_w(szClipName),
9208 debugstr_w(szProgIDName));
9209
9210 /* Create a CompObj stream */
9211 r = IStorage_CreateStream(pstg, szwStreamName,
9212 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9213 if( FAILED (r) )
9214 return r;
9215
9216 /* Write CompObj Structure to stream */
9217 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9218
9219 if( SUCCEEDED( r ) )
9220 r = WriteClassStm( pstm, clsid );
9221
9222 if( SUCCEEDED( r ) )
9223 r = STREAM_WriteString( pstm, lpszUserType );
9224 if( SUCCEEDED( r ) )
9225 r = STREAM_WriteString( pstm, szClipName );
9226 if( SUCCEEDED( r ) )
9227 r = STREAM_WriteString( pstm, szProgIDName );
9228 if( SUCCEEDED( r ) )
9229 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9230
9231 IStream_Release( pstm );
9232
9233 return r;
9234 }
9235
9236 /***********************************************************************
9237 * WriteFmtUserTypeStg (OLE32.@)
9238 */
9239 HRESULT WINAPI WriteFmtUserTypeStg(
9240 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9241 {
9242 STATSTG stat;
9243 HRESULT r;
9244 WCHAR szwClipName[0x40];
9245 CLSID clsid;
9246 LPWSTR wstrProgID = NULL;
9247 DWORD n;
9248
9249 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9250
9251 /* get the clipboard format name */
9252 if( cf )
9253 {
9254 n = GetClipboardFormatNameW( cf, szwClipName,
9255 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9256 szwClipName[n]=0;
9257 }
9258
9259 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9260
9261 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9262 if(SUCCEEDED(r))
9263 clsid = stat.clsid;
9264 else
9265 clsid = CLSID_NULL;
9266
9267 ProgIDFromCLSID(&clsid, &wstrProgID);
9268
9269 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9270
9271 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9272 cf ? szwClipName : NULL, wstrProgID );
9273
9274 CoTaskMemFree(wstrProgID);
9275
9276 return r;
9277 }
9278
9279
9280 /******************************************************************************
9281 * ReadFmtUserTypeStg [OLE32.@]
9282 */
9283 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9284 {
9285 HRESULT r;
9286 IStream *stm = 0;
9287 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9288 unsigned char unknown1[12];
9289 unsigned char unknown2[16];
9290 DWORD count;
9291 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9292 CLSID clsid;
9293
9294 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9295
9296 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9297 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9298 if( FAILED ( r ) )
9299 {
9300 WARN("Failed to open stream r = %08x\n", r);
9301 return r;
9302 }
9303
9304 /* read the various parts of the structure */
9305 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9306 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9307 goto end;
9308 r = ReadClassStm( stm, &clsid );
9309 if( FAILED( r ) )
9310 goto end;
9311
9312 r = STREAM_ReadString( stm, &szCLSIDName );
9313 if( FAILED( r ) )
9314 goto end;
9315
9316 r = STREAM_ReadString( stm, &szOleTypeName );
9317 if( FAILED( r ) )
9318 goto end;
9319
9320 r = STREAM_ReadString( stm, &szProgIDName );
9321 if( FAILED( r ) )
9322 goto end;
9323
9324 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9325 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9326 goto end;
9327
9328 /* ok, success... now we just need to store what we found */
9329 if( pcf )
9330 *pcf = RegisterClipboardFormatW( szOleTypeName );
9331
9332 if( lplpszUserType )
9333 {
9334 *lplpszUserType = szCLSIDName;
9335 szCLSIDName = NULL;
9336 }
9337
9338 end:
9339 CoTaskMemFree( szCLSIDName );
9340 CoTaskMemFree( szOleTypeName );
9341 CoTaskMemFree( szProgIDName );
9342 IStream_Release( stm );
9343
9344 return r;
9345 }
9346
9347 /******************************************************************************
9348 * StgIsStorageFile [OLE32.@]
9349 * Verify if the file contains a storage object
9350 *
9351 * PARAMS
9352 * fn [ I] Filename
9353 *
9354 * RETURNS
9355 * S_OK if file has magic bytes as a storage object
9356 * S_FALSE if file is not storage
9357 */
9358 HRESULT WINAPI
9359 StgIsStorageFile(LPCOLESTR fn)
9360 {
9361 HANDLE hf;
9362 BYTE magic[8];
9363 DWORD bytes_read;
9364
9365 TRACE("%s\n", debugstr_w(fn));
9366 hf = CreateFileW(fn, GENERIC_READ,
9367 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9368 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9369
9370 if (hf == INVALID_HANDLE_VALUE)
9371 return STG_E_FILENOTFOUND;
9372
9373 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9374 {
9375 WARN(" unable to read file\n");
9376 CloseHandle(hf);
9377 return S_FALSE;
9378 }
9379
9380 CloseHandle(hf);
9381
9382 if (bytes_read != 8) {
9383 TRACE(" too short\n");
9384 return S_FALSE;
9385 }
9386
9387 if (!memcmp(magic,STORAGE_magic,8)) {
9388 TRACE(" -> YES\n");
9389 return S_OK;
9390 }
9391
9392 TRACE(" -> Invalid header.\n");
9393 return S_FALSE;
9394 }
9395
9396 /***********************************************************************
9397 * WriteClassStm (OLE32.@)
9398 *
9399 * Writes a CLSID to a stream.
9400 *
9401 * PARAMS
9402 * pStm [I] Stream to write to.
9403 * rclsid [I] CLSID to write.
9404 *
9405 * RETURNS
9406 * Success: S_OK.
9407 * Failure: HRESULT code.
9408 */
9409 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9410 {
9411 TRACE("(%p,%p)\n",pStm,rclsid);
9412
9413 if (!pStm || !rclsid)
9414 return E_INVALIDARG;
9415
9416 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9417 }
9418
9419 /***********************************************************************
9420 * ReadClassStm (OLE32.@)
9421 *
9422 * Reads a CLSID from a stream.
9423 *
9424 * PARAMS
9425 * pStm [I] Stream to read from.
9426 * rclsid [O] CLSID to read.
9427 *
9428 * RETURNS
9429 * Success: S_OK.
9430 * Failure: HRESULT code.
9431 */
9432 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9433 {
9434 ULONG nbByte;
9435 HRESULT res;
9436
9437 TRACE("(%p,%p)\n",pStm,pclsid);
9438
9439 if (!pStm || !pclsid)
9440 return E_INVALIDARG;
9441
9442 /* clear the output args */
9443 *pclsid = CLSID_NULL;
9444
9445 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9446
9447 if (FAILED(res))
9448 return res;
9449
9450 if (nbByte != sizeof(CLSID))
9451 return STG_E_READFAULT;
9452 else
9453 return S_OK;
9454 }
9455
9456
9457 /************************************************************************
9458 * OleConvert Functions
9459 ***********************************************************************/
9460
9461 #define OLESTREAM_ID 0x501
9462 #define OLESTREAM_MAX_STR_LEN 255
9463
9464 /* OLESTREAM memory structure to use for Get and Put Routines */
9465 typedef struct
9466 {
9467 DWORD dwOleID;
9468 DWORD dwTypeID;
9469 DWORD dwOleTypeNameLength;
9470 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9471 CHAR *pstrOleObjFileName;
9472 DWORD dwOleObjFileNameLength;
9473 DWORD dwMetaFileWidth;
9474 DWORD dwMetaFileHeight;
9475 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9476 DWORD dwDataLength;
9477 BYTE *pData;
9478 } OLECONVERT_OLESTREAM_DATA;
9479
9480 /* CompObj Stream structure */
9481 typedef struct
9482 {
9483 BYTE byUnknown1[12];
9484 CLSID clsid;
9485 DWORD dwCLSIDNameLength;
9486 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9487 DWORD dwOleTypeNameLength;
9488 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9489 DWORD dwProgIDNameLength;
9490 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9491 BYTE byUnknown2[16];
9492 } OLECONVERT_ISTORAGE_COMPOBJ;
9493
9494 /* Ole Presentation Stream structure */
9495 typedef struct
9496 {
9497 BYTE byUnknown1[28];
9498 DWORD dwExtentX;
9499 DWORD dwExtentY;
9500 DWORD dwSize;
9501 BYTE *pData;
9502 } OLECONVERT_ISTORAGE_OLEPRES;
9503
9504
9505 /*************************************************************************
9506 * OLECONVERT_LoadOLE10 [Internal]
9507 *
9508 * Loads the OLE10 STREAM to memory
9509 *
9510 * PARAMS
9511 * pOleStream [I] The OLESTREAM
9512 * pData [I] Data Structure for the OLESTREAM Data
9513 *
9514 * RETURNS
9515 * Success: S_OK
9516 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9517 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9518 *
9519 * NOTES
9520 * This function is used by OleConvertOLESTREAMToIStorage only.
9521 *
9522 * Memory allocated for pData must be freed by the caller
9523 */
9524 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9525 {
9526 DWORD dwSize;
9527 HRESULT hRes = S_OK;
9528 int nTryCnt=0;
9529 int max_try = 6;
9530
9531 pData->pData = NULL;
9532 pData->pstrOleObjFileName = NULL;
9533
9534 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9535 {
9536 /* Get the OleID */
9537 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9538 if(dwSize != sizeof(pData->dwOleID))
9539 {
9540 hRes = CONVERT10_E_OLESTREAM_GET;
9541 }
9542 else if(pData->dwOleID != OLESTREAM_ID)
9543 {
9544 hRes = CONVERT10_E_OLESTREAM_FMT;
9545 }
9546 else
9547 {
9548 hRes = S_OK;
9549 break;
9550 }
9551 }
9552
9553 if(hRes == S_OK)
9554 {
9555 /* Get the TypeID... more info needed for this field */
9556 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9557 if(dwSize != sizeof(pData->dwTypeID))
9558 {
9559 hRes = CONVERT10_E_OLESTREAM_GET;
9560 }
9561 }
9562 if(hRes == S_OK)
9563 {
9564 if(pData->dwTypeID != 0)
9565 {
9566 /* Get the length of the OleTypeName */
9567 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9568 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9569 {
9570 hRes = CONVERT10_E_OLESTREAM_GET;
9571 }
9572
9573 if(hRes == S_OK)
9574 {
9575 if(pData->dwOleTypeNameLength > 0)
9576 {
9577 /* Get the OleTypeName */
9578 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9579 if(dwSize != pData->dwOleTypeNameLength)
9580 {
9581 hRes = CONVERT10_E_OLESTREAM_GET;
9582 }
9583 }
9584 }
9585 if(bStrem1)
9586 {
9587 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9588 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9589 {
9590 hRes = CONVERT10_E_OLESTREAM_GET;
9591 }
9592 if(hRes == S_OK)
9593 {
9594 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9595 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9596 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9597 if(pData->pstrOleObjFileName)
9598 {
9599 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9600 if(dwSize != pData->dwOleObjFileNameLength)
9601 {
9602 hRes = CONVERT10_E_OLESTREAM_GET;
9603 }
9604 }
9605 else
9606 hRes = CONVERT10_E_OLESTREAM_GET;
9607 }
9608 }
9609 else
9610 {
9611 /* Get the Width of the Metafile */
9612 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9613 if(dwSize != sizeof(pData->dwMetaFileWidth))
9614 {
9615 hRes = CONVERT10_E_OLESTREAM_GET;
9616 }
9617 if(hRes == S_OK)
9618 {
9619 /* Get the Height of the Metafile */
9620 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9621 if(dwSize != sizeof(pData->dwMetaFileHeight))
9622 {
9623 hRes = CONVERT10_E_OLESTREAM_GET;
9624 }
9625 }
9626 }
9627 if(hRes == S_OK)
9628 {
9629 /* Get the Length of the Data */
9630 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9631 if(dwSize != sizeof(pData->dwDataLength))
9632 {
9633 hRes = CONVERT10_E_OLESTREAM_GET;
9634 }
9635 }
9636
9637 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9638 {
9639 if(!bStrem1) /* if it is a second OLE stream data */
9640 {
9641 pData->dwDataLength -= 8;
9642 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9643 if(dwSize != sizeof(pData->strUnknown))
9644 {
9645 hRes = CONVERT10_E_OLESTREAM_GET;
9646 }
9647 }
9648 }
9649 if(hRes == S_OK)
9650 {
9651 if(pData->dwDataLength > 0)
9652 {
9653 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9654
9655 /* Get Data (ex. IStorage, Metafile, or BMP) */
9656 if(pData->pData)
9657 {
9658 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9659 if(dwSize != pData->dwDataLength)
9660 {
9661 hRes = CONVERT10_E_OLESTREAM_GET;
9662 }
9663 }
9664 else
9665 {
9666 hRes = CONVERT10_E_OLESTREAM_GET;
9667 }
9668 }
9669 }
9670 }
9671 }
9672 return hRes;
9673 }
9674
9675 /*************************************************************************
9676 * OLECONVERT_SaveOLE10 [Internal]
9677 *
9678 * Saves the OLE10 STREAM From memory
9679 *
9680 * PARAMS
9681 * pData [I] Data Structure for the OLESTREAM Data
9682 * pOleStream [I] The OLESTREAM to save
9683 *
9684 * RETURNS
9685 * Success: S_OK
9686 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9687 *
9688 * NOTES
9689 * This function is used by OleConvertIStorageToOLESTREAM only.
9690 *
9691 */
9692 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9693 {
9694 DWORD dwSize;
9695 HRESULT hRes = S_OK;
9696
9697
9698 /* Set the OleID */
9699 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9700 if(dwSize != sizeof(pData->dwOleID))
9701 {
9702 hRes = CONVERT10_E_OLESTREAM_PUT;
9703 }
9704
9705 if(hRes == S_OK)
9706 {
9707 /* Set the TypeID */
9708 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9709 if(dwSize != sizeof(pData->dwTypeID))
9710 {
9711 hRes = CONVERT10_E_OLESTREAM_PUT;
9712 }
9713 }
9714
9715 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9716 {
9717 /* Set the Length of the OleTypeName */
9718 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9719 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9720 {
9721 hRes = CONVERT10_E_OLESTREAM_PUT;
9722 }
9723
9724 if(hRes == S_OK)
9725 {
9726 if(pData->dwOleTypeNameLength > 0)
9727 {
9728 /* Set the OleTypeName */
9729 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9730 if(dwSize != pData->dwOleTypeNameLength)
9731 {
9732 hRes = CONVERT10_E_OLESTREAM_PUT;
9733 }
9734 }
9735 }
9736
9737 if(hRes == S_OK)
9738 {
9739 /* Set the width of the Metafile */
9740 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9741 if(dwSize != sizeof(pData->dwMetaFileWidth))
9742 {
9743 hRes = CONVERT10_E_OLESTREAM_PUT;
9744 }
9745 }
9746
9747 if(hRes == S_OK)
9748 {
9749 /* Set the height of the Metafile */
9750 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9751 if(dwSize != sizeof(pData->dwMetaFileHeight))
9752 {
9753 hRes = CONVERT10_E_OLESTREAM_PUT;
9754 }
9755 }
9756
9757 if(hRes == S_OK)
9758 {
9759 /* Set the length of the Data */
9760 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9761 if(dwSize != sizeof(pData->dwDataLength))
9762 {
9763 hRes = CONVERT10_E_OLESTREAM_PUT;
9764 }
9765 }
9766
9767 if(hRes == S_OK)
9768 {
9769 if(pData->dwDataLength > 0)
9770 {
9771 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9772 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9773 if(dwSize != pData->dwDataLength)
9774 {
9775 hRes = CONVERT10_E_OLESTREAM_PUT;
9776 }
9777 }
9778 }
9779 }
9780 return hRes;
9781 }
9782
9783 /*************************************************************************
9784 * OLECONVERT_GetOLE20FromOLE10[Internal]
9785 *
9786 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9787 * opens it, and copies the content to the dest IStorage for
9788 * OleConvertOLESTREAMToIStorage
9789 *
9790 *
9791 * PARAMS
9792 * pDestStorage [I] The IStorage to copy the data to
9793 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9794 * nBufferLength [I] The size of the buffer
9795 *
9796 * RETURNS
9797 * Nothing
9798 *
9799 * NOTES
9800 *
9801 *
9802 */
9803 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9804 {
9805 HRESULT hRes;
9806 HANDLE hFile;
9807 IStorage *pTempStorage;
9808 DWORD dwNumOfBytesWritten;
9809 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9810 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9811
9812 /* Create a temp File */
9813 GetTempPathW(MAX_PATH, wstrTempDir);
9814 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9815 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9816
9817 if(hFile != INVALID_HANDLE_VALUE)
9818 {
9819 /* Write IStorage Data to File */
9820 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9821 CloseHandle(hFile);
9822
9823 /* Open and copy temp storage to the Dest Storage */
9824 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9825 if(hRes == S_OK)
9826 {
9827 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9828 IStorage_Release(pTempStorage);
9829 }
9830 DeleteFileW(wstrTempFile);
9831 }
9832 }
9833
9834
9835 /*************************************************************************
9836 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9837 *
9838 * Saves the OLE10 STREAM From memory
9839 *
9840 * PARAMS
9841 * pStorage [I] The Src IStorage to copy
9842 * pData [I] The Dest Memory to write to.
9843 *
9844 * RETURNS
9845 * The size in bytes allocated for pData
9846 *
9847 * NOTES
9848 * Memory allocated for pData must be freed by the caller
9849 *
9850 * Used by OleConvertIStorageToOLESTREAM only.
9851 *
9852 */
9853 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9854 {
9855 HANDLE hFile;
9856 HRESULT hRes;
9857 DWORD nDataLength = 0;
9858 IStorage *pTempStorage;
9859 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9860 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9861
9862 *pData = NULL;
9863
9864 /* Create temp Storage */
9865 GetTempPathW(MAX_PATH, wstrTempDir);
9866 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9867 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9868
9869 if(hRes == S_OK)
9870 {
9871 /* Copy Src Storage to the Temp Storage */
9872 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9873 IStorage_Release(pTempStorage);
9874
9875 /* Open Temp Storage as a file and copy to memory */
9876 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9877 if(hFile != INVALID_HANDLE_VALUE)
9878 {
9879 nDataLength = GetFileSize(hFile, NULL);
9880 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9881 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9882 CloseHandle(hFile);
9883 }
9884 DeleteFileW(wstrTempFile);
9885 }
9886 return nDataLength;
9887 }
9888
9889 /*************************************************************************
9890 * OLECONVERT_CreateCompObjStream [Internal]
9891 *
9892 * Creates a "\001CompObj" is the destination IStorage if necessary.
9893 *
9894 * PARAMS
9895 * pStorage [I] The dest IStorage to create the CompObj Stream
9896 * if necessary.
9897 * strOleTypeName [I] The ProgID
9898 *
9899 * RETURNS
9900 * Success: S_OK
9901 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9902 *
9903 * NOTES
9904 * This function is used by OleConvertOLESTREAMToIStorage only.
9905 *
9906 * The stream data is stored in the OLESTREAM and there should be
9907 * no need to recreate the stream. If the stream is manually
9908 * deleted it will attempt to create it by querying the registry.
9909 *
9910 *
9911 */
9912 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9913 {
9914 IStream *pStream;
9915 HRESULT hStorageRes, hRes = S_OK;
9916 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9917 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9918 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9919
9920 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9921 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9922
9923 /* Initialize the CompObj structure */
9924 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9925 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9926 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9927
9928
9929 /* Create a CompObj stream if it doesn't exist */
9930 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9931 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9932 if(hStorageRes == S_OK)
9933 {
9934 /* copy the OleTypeName to the compobj struct */
9935 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9936 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9937
9938 /* copy the OleTypeName to the compobj struct */
9939 /* Note: in the test made, these were Identical */
9940 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9941 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9942
9943 /* Get the CLSID */
9944 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9945 bufferW, OLESTREAM_MAX_STR_LEN );
9946 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9947
9948 if(hRes == S_OK)
9949 {
9950 HKEY hKey;
9951 LONG hErr;
9952 /* Get the CLSID Default Name from the Registry */
9953 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9954 if(hErr == ERROR_SUCCESS)
9955 {
9956 char strTemp[OLESTREAM_MAX_STR_LEN];
9957 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9958 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9959 if(hErr == ERROR_SUCCESS)
9960 {
9961 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9962 }
9963 RegCloseKey(hKey);
9964 }
9965 }
9966
9967 /* Write CompObj Structure to stream */
9968 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9969
9970 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9971
9972 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9973 if(IStorageCompObj.dwCLSIDNameLength > 0)
9974 {
9975 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9976 }
9977 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9978 if(IStorageCompObj.dwOleTypeNameLength > 0)
9979 {
9980 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9981 }
9982 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9983 if(IStorageCompObj.dwProgIDNameLength > 0)
9984 {
9985 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9986 }
9987 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9988 IStream_Release(pStream);
9989 }
9990 return hRes;
9991 }
9992
9993
9994 /*************************************************************************
9995 * OLECONVERT_CreateOlePresStream[Internal]
9996 *
9997 * Creates the "\002OlePres000" Stream with the Metafile data
9998 *
9999 * PARAMS
10000 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10001 * dwExtentX [I] Width of the Metafile
10002 * dwExtentY [I] Height of the Metafile
10003 * pData [I] Metafile data
10004 * dwDataLength [I] Size of the Metafile data
10005 *
10006 * RETURNS
10007 * Success: S_OK
10008 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10009 *
10010 * NOTES
10011 * This function is used by OleConvertOLESTREAMToIStorage only.
10012 *
10013 */
10014 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10015 {
10016 HRESULT hRes;
10017 IStream *pStream;
10018 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10019 BYTE pOlePresStreamHeader [] =
10020 {
10021 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10022 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10023 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10024 0x00, 0x00, 0x00, 0x00
10025 };
10026
10027 BYTE pOlePresStreamHeaderEmpty [] =
10028 {
10029 0x00, 0x00, 0x00, 0x00,
10030 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10031 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10032 0x00, 0x00, 0x00, 0x00
10033 };
10034
10035 /* Create the OlePres000 Stream */
10036 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10037 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10038
10039 if(hRes == S_OK)
10040 {
10041 DWORD nHeaderSize;
10042 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10043
10044 memset(&OlePres, 0, sizeof(OlePres));
10045 /* Do we have any metafile data to save */
10046 if(dwDataLength > 0)
10047 {
10048 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10049 nHeaderSize = sizeof(pOlePresStreamHeader);
10050 }
10051 else
10052 {
10053 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10054 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10055 }
10056 /* Set width and height of the metafile */
10057 OlePres.dwExtentX = dwExtentX;
10058 OlePres.dwExtentY = -dwExtentY;
10059
10060 /* Set Data and Length */
10061 if(dwDataLength > sizeof(METAFILEPICT16))
10062 {
10063 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10064 OlePres.pData = &(pData[8]);
10065 }
10066 /* Save OlePres000 Data to Stream */
10067 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10068 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10069 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10070 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10071 if(OlePres.dwSize > 0)
10072 {
10073 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10074 }
10075 IStream_Release(pStream);
10076 }
10077 }
10078
10079 /*************************************************************************
10080 * OLECONVERT_CreateOle10NativeStream [Internal]
10081 *
10082 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10083 *
10084 * PARAMS
10085 * pStorage [I] Dest storage to create the stream in
10086 * pData [I] Ole10 Native Data (ex. bmp)
10087 * dwDataLength [I] Size of the Ole10 Native Data
10088 *
10089 * RETURNS
10090 * Nothing
10091 *
10092 * NOTES
10093 * This function is used by OleConvertOLESTREAMToIStorage only.
10094 *
10095 * Might need to verify the data and return appropriate error message
10096 *
10097 */
10098 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10099 {
10100 HRESULT hRes;
10101 IStream *pStream;
10102 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10103
10104 /* Create the Ole10Native Stream */
10105 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10106 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10107
10108 if(hRes == S_OK)
10109 {
10110 /* Write info to stream */
10111 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10112 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10113 IStream_Release(pStream);
10114 }
10115
10116 }
10117
10118 /*************************************************************************
10119 * OLECONVERT_GetOLE10ProgID [Internal]
10120 *
10121 * Finds the ProgID (or OleTypeID) from the IStorage
10122 *
10123 * PARAMS
10124 * pStorage [I] The Src IStorage to get the ProgID
10125 * strProgID [I] the ProgID string to get
10126 * dwSize [I] the size of the string
10127 *
10128 * RETURNS
10129 * Success: S_OK
10130 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10131 *
10132 * NOTES
10133 * This function is used by OleConvertIStorageToOLESTREAM only.
10134 *
10135 *
10136 */
10137 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10138 {
10139 HRESULT hRes;
10140 IStream *pStream;
10141 LARGE_INTEGER iSeekPos;
10142 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10143 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10144
10145 /* Open the CompObj Stream */
10146 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10147 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10148 if(hRes == S_OK)
10149 {
10150
10151 /*Get the OleType from the CompObj Stream */
10152 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10153 iSeekPos.u.HighPart = 0;
10154
10155 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10156 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10157 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10158 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10159 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10160 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10161 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10162
10163 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10164 if(*dwSize > 0)
10165 {
10166 IStream_Read(pStream, strProgID, *dwSize, NULL);
10167 }
10168 IStream_Release(pStream);
10169 }
10170 else
10171 {
10172 STATSTG stat;
10173 LPOLESTR wstrProgID;
10174
10175 /* Get the OleType from the registry */
10176 REFCLSID clsid = &(stat.clsid);
10177 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10178 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10179 if(hRes == S_OK)
10180 {
10181 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10182 CoTaskMemFree(wstrProgID);
10183 }
10184
10185 }
10186 return hRes;
10187 }
10188
10189 /*************************************************************************
10190 * OLECONVERT_GetOle10PresData [Internal]
10191 *
10192 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10193 *
10194 * PARAMS
10195 * pStorage [I] Src IStroage
10196 * pOleStream [I] Dest OleStream Mem Struct
10197 *
10198 * RETURNS
10199 * Nothing
10200 *
10201 * NOTES
10202 * This function is used by OleConvertIStorageToOLESTREAM only.
10203 *
10204 * Memory allocated for pData must be freed by the caller
10205 *
10206 *
10207 */
10208 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10209 {
10210
10211 HRESULT hRes;
10212 IStream *pStream;
10213 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10214
10215 /* Initialize Default data for OLESTREAM */
10216 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10217 pOleStreamData[0].dwTypeID = 2;
10218 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10219 pOleStreamData[1].dwTypeID = 0;
10220 pOleStreamData[0].dwMetaFileWidth = 0;
10221 pOleStreamData[0].dwMetaFileHeight = 0;
10222 pOleStreamData[0].pData = NULL;
10223 pOleStreamData[1].pData = NULL;
10224
10225 /* Open Ole10Native Stream */
10226 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10227 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10228 if(hRes == S_OK)
10229 {
10230
10231 /* Read Size and Data */
10232 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10233 if(pOleStreamData->dwDataLength > 0)
10234 {
10235 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10236 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10237 }
10238 IStream_Release(pStream);
10239 }
10240
10241 }
10242
10243
10244 /*************************************************************************
10245 * OLECONVERT_GetOle20PresData[Internal]
10246 *
10247 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10248 *
10249 * PARAMS
10250 * pStorage [I] Src IStroage
10251 * pOleStreamData [I] Dest OleStream Mem Struct
10252 *
10253 * RETURNS
10254 * Nothing
10255 *
10256 * NOTES
10257 * This function is used by OleConvertIStorageToOLESTREAM only.
10258 *
10259 * Memory allocated for pData must be freed by the caller
10260 */
10261 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10262 {
10263 HRESULT hRes;
10264 IStream *pStream;
10265 OLECONVERT_ISTORAGE_OLEPRES olePress;
10266 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10267
10268 /* Initialize Default data for OLESTREAM */
10269 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10270 pOleStreamData[0].dwTypeID = 2;
10271 pOleStreamData[0].dwMetaFileWidth = 0;
10272 pOleStreamData[0].dwMetaFileHeight = 0;
10273 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10274 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10275 pOleStreamData[1].dwTypeID = 0;
10276 pOleStreamData[1].dwOleTypeNameLength = 0;
10277 pOleStreamData[1].strOleTypeName[0] = 0;
10278 pOleStreamData[1].dwMetaFileWidth = 0;
10279 pOleStreamData[1].dwMetaFileHeight = 0;
10280 pOleStreamData[1].pData = NULL;
10281 pOleStreamData[1].dwDataLength = 0;
10282
10283
10284 /* Open OlePress000 stream */
10285 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10286 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10287 if(hRes == S_OK)
10288 {
10289 LARGE_INTEGER iSeekPos;
10290 METAFILEPICT16 MetaFilePict;
10291 static const char strMetafilePictName[] = "METAFILEPICT";
10292
10293 /* Set the TypeID for a Metafile */
10294 pOleStreamData[1].dwTypeID = 5;
10295
10296 /* Set the OleTypeName to Metafile */
10297 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10298 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10299
10300 iSeekPos.u.HighPart = 0;
10301 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10302
10303 /* Get Presentation Data */
10304 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10305 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10306 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10307 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10308
10309 /*Set width and Height */
10310 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10311 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10312 if(olePress.dwSize > 0)
10313 {
10314 /* Set Length */
10315 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10316
10317 /* Set MetaFilePict struct */
10318 MetaFilePict.mm = 8;
10319 MetaFilePict.xExt = olePress.dwExtentX;
10320 MetaFilePict.yExt = olePress.dwExtentY;
10321 MetaFilePict.hMF = 0;
10322
10323 /* Get Metafile Data */
10324 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10325 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10326 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10327 }
10328 IStream_Release(pStream);
10329 }
10330 }
10331
10332 /*************************************************************************
10333 * OleConvertOLESTREAMToIStorage [OLE32.@]
10334 *
10335 * Read info on MSDN
10336 *
10337 * TODO
10338 * DVTARGETDEVICE parameter is not handled
10339 * Still unsure of some mem fields for OLE 10 Stream
10340 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10341 * and "\001OLE" streams
10342 *
10343 */
10344 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10345 LPOLESTREAM pOleStream,
10346 LPSTORAGE pstg,
10347 const DVTARGETDEVICE* ptd)
10348 {
10349 int i;
10350 HRESULT hRes=S_OK;
10351 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10352
10353 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10354
10355 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10356
10357 if(ptd != NULL)
10358 {
10359 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10360 }
10361
10362 if(pstg == NULL || pOleStream == NULL)
10363 {
10364 hRes = E_INVALIDARG;
10365 }
10366
10367 if(hRes == S_OK)
10368 {
10369 /* Load the OLESTREAM to Memory */
10370 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10371 }
10372
10373 if(hRes == S_OK)
10374 {
10375 /* Load the OLESTREAM to Memory (part 2)*/
10376 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10377 }
10378
10379 if(hRes == S_OK)
10380 {
10381
10382 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10383 {
10384 /* Do we have the IStorage Data in the OLESTREAM */
10385 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10386 {
10387 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10388 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10389 }
10390 else
10391 {
10392 /* It must be an original OLE 1.0 source */
10393 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10394 }
10395 }
10396 else
10397 {
10398 /* It must be an original OLE 1.0 source */
10399 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10400 }
10401
10402 /* Create CompObj Stream if necessary */
10403 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10404 if(hRes == S_OK)
10405 {
10406 /*Create the Ole Stream if necessary */
10407 STORAGE_CreateOleStream(pstg, 0);
10408 }
10409 }
10410
10411
10412 /* Free allocated memory */
10413 for(i=0; i < 2; i++)
10414 {
10415 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10416 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10417 pOleStreamData[i].pstrOleObjFileName = NULL;
10418 }
10419 return hRes;
10420 }
10421
10422 /*************************************************************************
10423 * OleConvertIStorageToOLESTREAM [OLE32.@]
10424 *
10425 * Read info on MSDN
10426 *
10427 * Read info on MSDN
10428 *
10429 * TODO
10430 * Still unsure of some mem fields for OLE 10 Stream
10431 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10432 * and "\001OLE" streams.
10433 *
10434 */
10435 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10436 LPSTORAGE pstg,
10437 LPOLESTREAM pOleStream)
10438 {
10439 int i;
10440 HRESULT hRes = S_OK;
10441 IStream *pStream;
10442 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10443 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10444
10445 TRACE("%p %p\n", pstg, pOleStream);
10446
10447 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10448
10449 if(pstg == NULL || pOleStream == NULL)
10450 {
10451 hRes = E_INVALIDARG;
10452 }
10453 if(hRes == S_OK)
10454 {
10455 /* Get the ProgID */
10456 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10457 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10458 }
10459 if(hRes == S_OK)
10460 {
10461 /* Was it originally Ole10 */
10462 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10463 if(hRes == S_OK)
10464 {
10465 IStream_Release(pStream);
10466 /* Get Presentation Data for Ole10Native */
10467 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10468 }
10469 else
10470 {
10471 /* Get Presentation Data (OLE20) */
10472 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10473 }
10474
10475 /* Save OLESTREAM */
10476 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10477 if(hRes == S_OK)
10478 {
10479 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10480 }
10481
10482 }
10483
10484 /* Free allocated memory */
10485 for(i=0; i < 2; i++)
10486 {
10487 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10488 }
10489
10490 return hRes;
10491 }
10492
10493 enum stream_1ole_flags {
10494 OleStream_LinkedObject = 0x00000001,
10495 OleStream_Convert = 0x00000004
10496 };
10497
10498 /***********************************************************************
10499 * GetConvertStg (OLE32.@)
10500 */
10501 HRESULT WINAPI GetConvertStg(IStorage *stg)
10502 {
10503 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10504 static const DWORD version_magic = 0x02000001;
10505 DWORD header[2];
10506 IStream *stream;
10507 HRESULT hr;
10508
10509 TRACE("%p\n", stg);
10510
10511 if (!stg) return E_INVALIDARG;
10512
10513 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10514 if (FAILED(hr)) return hr;
10515
10516 hr = IStream_Read(stream, header, sizeof(header), NULL);
10517 IStream_Release(stream);
10518 if (FAILED(hr)) return hr;
10519
10520 if (header[0] != version_magic)
10521 {
10522 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10523 return E_FAIL;
10524 }
10525
10526 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10527 }
10528
10529 /***********************************************************************
10530 * SetConvertStg (OLE32.@)
10531 */
10532 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10533 {
10534 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10535 DWORD flags = convert ? OleStream_Convert : 0;
10536 IStream *stream;
10537 DWORD header[2];
10538 HRESULT hr;
10539
10540 TRACE("(%p, %d)\n", storage, convert);
10541
10542 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10543 if (FAILED(hr))
10544 {
10545 if (hr != STG_E_FILENOTFOUND)
10546 return hr;
10547
10548 return STORAGE_CreateOleStream(storage, flags);
10549 }
10550
10551 hr = IStream_Read(stream, header, sizeof(header), NULL);
10552 if (FAILED(hr))
10553 {
10554 IStream_Release(stream);
10555 return hr;
10556 }
10557
10558 /* update flag if differs */
10559 if ((header[1] ^ flags) & OleStream_Convert)
10560 {
10561 LARGE_INTEGER pos = {{0}};
10562
10563 if (header[1] & OleStream_Convert)
10564 flags = header[1] & ~OleStream_Convert;
10565 else
10566 flags = header[1] | OleStream_Convert;
10567
10568 pos.QuadPart = sizeof(DWORD);
10569 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10570 if (FAILED(hr))
10571 {
10572 IStream_Release(stream);
10573 return hr;
10574 }
10575
10576 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10577 }
10578
10579 IStream_Release(stream);
10580 return hr;
10581 }