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