a151f709ef3e486ec94dba63ce6459b35c8ef6c3
[reactos.git] / reactos / dll / win32 / ole32 / storage32.c
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
4 *
5 * This file contains the compound file implementation
6 * of the storage interface.
7 *
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 *
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
31 */
32
33 #include "precomp.h"
34 #include "storage32.h"
35
36 #include <wine/wingdi16.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(storage);
39
40
41 /*
42 * These are signatures to detect the type of Document file.
43 */
44 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
45 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
46
47 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
48
49
50 /****************************************************************************
51 * StorageInternalImpl definitions.
52 *
53 * Definition of the implementation structure for the IStorage interface.
54 * This one implements the IStorage interface for storage that are
55 * inside another storage.
56 */
57 typedef struct StorageInternalImpl
58 {
59 struct StorageBaseImpl base;
60
61 /*
62 * Entry in the parent's stream tracking list
63 */
64 struct list ParentListEntry;
65
66 StorageBaseImpl *parentStorage;
67 } StorageInternalImpl;
68
69 static const IStorageVtbl StorageInternalImpl_Vtbl;
70 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
71
72 typedef struct TransactedDirEntry
73 {
74 /* If applicable, a reference to the original DirEntry in the transacted
75 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
76 DirRef transactedParentEntry;
77
78 /* True if this entry is being used. */
79 BOOL inuse;
80
81 /* True if data is up to date. */
82 BOOL read;
83
84 /* True if this entry has been modified. */
85 BOOL dirty;
86
87 /* True if this entry's stream has been modified. */
88 BOOL stream_dirty;
89
90 /* True if this entry has been deleted in the transacted storage, but the
91 * delete has not yet been committed. */
92 BOOL deleted;
93
94 /* If this entry's stream has been modified, a reference to where the stream
95 * is stored in the snapshot file. */
96 DirRef stream_entry;
97
98 /* This directory entry's data, including any changes that have been made. */
99 DirEntry data;
100
101 /* A reference to the parent of this node. This is only valid while we are
102 * committing changes. */
103 DirRef parent;
104
105 /* A reference to a newly-created entry in the transacted parent. This is
106 * always equal to transactedParentEntry except when committing changes. */
107 DirRef newTransactedParentEntry;
108 } TransactedDirEntry;
109
110
111 /****************************************************************************
112 * Transacted storage object.
113 */
114 typedef struct TransactedSnapshotImpl
115 {
116 struct StorageBaseImpl base;
117
118 /*
119 * Modified streams are temporarily saved to the scratch file.
120 */
121 StorageBaseImpl *scratch;
122
123 /* The directory structure is kept here, so that we can track how these
124 * entries relate to those in the parent storage. */
125 TransactedDirEntry *entries;
126 ULONG entries_size;
127 ULONG firstFreeEntry;
128
129 /*
130 * Changes are committed to the transacted parent.
131 */
132 StorageBaseImpl *transactedParent;
133
134 /* The transaction signature from when we last committed */
135 ULONG lastTransactionSig;
136 } TransactedSnapshotImpl;
137
138 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
139 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
140
141 typedef struct TransactedSharedImpl
142 {
143 struct StorageBaseImpl base;
144
145 /*
146 * Snapshot and uncommitted changes go here.
147 */
148 TransactedSnapshotImpl *scratch;
149
150 /*
151 * Changes are committed to the transacted parent.
152 */
153 StorageBaseImpl *transactedParent;
154
155 /* The transaction signature from when we last committed */
156 ULONG lastTransactionSig;
157 } TransactedSharedImpl;
158
159
160 /****************************************************************************
161 * BlockChainStream definitions.
162 *
163 * The BlockChainStream class is a utility class that is used to create an
164 * abstraction of the big block chains in the storage file.
165 */
166
167 struct BlockChainRun
168 {
169 /* This represents a range of blocks that happen reside in consecutive sectors. */
170 ULONG firstSector;
171 ULONG firstOffset;
172 ULONG lastOffset;
173 };
174
175 typedef struct BlockChainBlock
176 {
177 ULONG index;
178 ULONG sector;
179 BOOL read;
180 BOOL dirty;
181 BYTE data[MAX_BIG_BLOCK_SIZE];
182 } BlockChainBlock;
183
184 struct BlockChainStream
185 {
186 StorageImpl* parentStorage;
187 ULONG* headOfStreamPlaceHolder;
188 DirRef ownerDirEntry;
189 struct BlockChainRun* indexCache;
190 ULONG indexCacheLen;
191 ULONG indexCacheSize;
192 BlockChainBlock cachedBlocks[2];
193 ULONG blockToEvict;
194 ULONG tailIndex;
195 ULONG numBlocks;
196 };
197
198 /* Returns the number of blocks that comprises this chain.
199 * This is not the size of the stream as the last block may not be full!
200 */
201 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
202 {
203 return This->numBlocks;
204 }
205
206 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
207 static void BlockChainStream_Destroy(BlockChainStream*);
208 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
209 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
210 static HRESULT BlockChainStream_Flush(BlockChainStream*);
211 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
212 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
213
214
215 /****************************************************************************
216 * SmallBlockChainStream definitions.
217 *
218 * The SmallBlockChainStream class is a utility class that is used to create an
219 * abstraction of the small block chains in the storage file.
220 */
221
222 struct SmallBlockChainStream
223 {
224 StorageImpl* parentStorage;
225 DirRef ownerDirEntry;
226 ULONG* headOfStreamPlaceHolder;
227 };
228
229 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
230 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
231 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
232 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
233 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
234 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
235
236
237 /************************************************************************
238 * STGM Functions
239 ***********************************************************************/
240
241 /************************************************************************
242 * This method validates an STGM parameter that can contain the values below
243 *
244 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
245 * The stgm values contained in 0xffff0000 are bitmasks.
246 *
247 * STGM_DIRECT 0x00000000
248 * STGM_TRANSACTED 0x00010000
249 * STGM_SIMPLE 0x08000000
250 *
251 * STGM_READ 0x00000000
252 * STGM_WRITE 0x00000001
253 * STGM_READWRITE 0x00000002
254 *
255 * STGM_SHARE_DENY_NONE 0x00000040
256 * STGM_SHARE_DENY_READ 0x00000030
257 * STGM_SHARE_DENY_WRITE 0x00000020
258 * STGM_SHARE_EXCLUSIVE 0x00000010
259 *
260 * STGM_PRIORITY 0x00040000
261 * STGM_DELETEONRELEASE 0x04000000
262 *
263 * STGM_CREATE 0x00001000
264 * STGM_CONVERT 0x00020000
265 * STGM_FAILIFTHERE 0x00000000
266 *
267 * STGM_NOSCRATCH 0x00100000
268 * STGM_NOSNAPSHOT 0x00200000
269 */
270 static HRESULT validateSTGM(DWORD stgm)
271 {
272 DWORD access = STGM_ACCESS_MODE(stgm);
273 DWORD share = STGM_SHARE_MODE(stgm);
274 DWORD create = STGM_CREATE_MODE(stgm);
275
276 if (stgm&~STGM_KNOWN_FLAGS)
277 {
278 ERR("unknown flags %08x\n", stgm);
279 return E_FAIL;
280 }
281
282 switch (access)
283 {
284 case STGM_READ:
285 case STGM_WRITE:
286 case STGM_READWRITE:
287 break;
288 default:
289 return E_FAIL;
290 }
291
292 switch (share)
293 {
294 case STGM_SHARE_DENY_NONE:
295 case STGM_SHARE_DENY_READ:
296 case STGM_SHARE_DENY_WRITE:
297 case STGM_SHARE_EXCLUSIVE:
298 break;
299 case 0:
300 if (!(stgm & STGM_TRANSACTED))
301 return E_FAIL;
302 break;
303 default:
304 return E_FAIL;
305 }
306
307 switch (create)
308 {
309 case STGM_CREATE:
310 case STGM_FAILIFTHERE:
311 break;
312 default:
313 return E_FAIL;
314 }
315
316 /*
317 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
318 */
319 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
320 return E_FAIL;
321
322 /*
323 * STGM_CREATE | STGM_CONVERT
324 * if both are false, STGM_FAILIFTHERE is set to TRUE
325 */
326 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
327 return E_FAIL;
328
329 /*
330 * STGM_NOSCRATCH requires STGM_TRANSACTED
331 */
332 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
333 return E_FAIL;
334
335 /*
336 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
337 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
338 */
339 if ( (stgm & STGM_NOSNAPSHOT) &&
340 (!(stgm & STGM_TRANSACTED) ||
341 share == STGM_SHARE_EXCLUSIVE ||
342 share == STGM_SHARE_DENY_WRITE) )
343 return E_FAIL;
344
345 return S_OK;
346 }
347
348 /************************************************************************
349 * GetShareModeFromSTGM
350 *
351 * This method will return a share mode flag from a STGM value.
352 * The STGM value is assumed valid.
353 */
354 static DWORD GetShareModeFromSTGM(DWORD stgm)
355 {
356 switch (STGM_SHARE_MODE(stgm))
357 {
358 case 0:
359 assert(stgm & STGM_TRANSACTED);
360 /* fall-through */
361 case STGM_SHARE_DENY_NONE:
362 return FILE_SHARE_READ | FILE_SHARE_WRITE;
363 case STGM_SHARE_DENY_READ:
364 return FILE_SHARE_WRITE;
365 case STGM_SHARE_DENY_WRITE:
366 case STGM_SHARE_EXCLUSIVE:
367 return FILE_SHARE_READ;
368 }
369 ERR("Invalid share mode!\n");
370 assert(0);
371 return 0;
372 }
373
374 /************************************************************************
375 * GetAccessModeFromSTGM
376 *
377 * This method will return an access mode flag from a STGM value.
378 * The STGM value is assumed valid.
379 */
380 static DWORD GetAccessModeFromSTGM(DWORD stgm)
381 {
382 switch (STGM_ACCESS_MODE(stgm))
383 {
384 case STGM_READ:
385 return GENERIC_READ;
386 case STGM_WRITE:
387 case STGM_READWRITE:
388 return GENERIC_READ | GENERIC_WRITE;
389 }
390 ERR("Invalid access mode!\n");
391 assert(0);
392 return 0;
393 }
394
395 /************************************************************************
396 * GetCreationModeFromSTGM
397 *
398 * This method will return a creation mode flag from a STGM value.
399 * The STGM value is assumed valid.
400 */
401 static DWORD GetCreationModeFromSTGM(DWORD stgm)
402 {
403 switch(STGM_CREATE_MODE(stgm))
404 {
405 case STGM_CREATE:
406 return CREATE_ALWAYS;
407 case STGM_CONVERT:
408 FIXME("STGM_CONVERT not implemented!\n");
409 return CREATE_NEW;
410 case STGM_FAILIFTHERE:
411 return CREATE_NEW;
412 }
413 ERR("Invalid create mode!\n");
414 assert(0);
415 return 0;
416 }
417
418
419 /************************************************************************
420 * IDirectWriterLock implementation
421 ***********************************************************************/
422
423 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
424 {
425 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
426 }
427
428 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
429 {
430 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
431 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
432 }
433
434 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
435 {
436 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
437 return IStorage_AddRef(&This->IStorage_iface);
438 }
439
440 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
441 {
442 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
443 return IStorage_Release(&This->IStorage_iface);
444 }
445
446 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
447 {
448 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
449 FIXME("(%p)->(%d): stub\n", This, timeout);
450 return E_NOTIMPL;
451 }
452
453 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
454 {
455 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
456 FIXME("(%p): stub\n", This);
457 return E_NOTIMPL;
458 }
459
460 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
461 {
462 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
463 FIXME("(%p): stub\n", This);
464 return E_NOTIMPL;
465 }
466
467 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
468 {
469 directwriterlock_QueryInterface,
470 directwriterlock_AddRef,
471 directwriterlock_Release,
472 directwriterlock_WaitForWriteAccess,
473 directwriterlock_ReleaseWriteAccess,
474 directwriterlock_HaveWriteAccess
475 };
476
477
478 /************************************************************************
479 * StorageBaseImpl implementation : Tree helper functions
480 ***********************************************************************/
481
482 /****************************************************************************
483 *
484 * Internal Method
485 *
486 * Case insensitive comparison of DirEntry.name by first considering
487 * their size.
488 *
489 * Returns <0 when name1 < name2
490 * >0 when name1 > name2
491 * 0 when name1 == name2
492 */
493 static LONG entryNameCmp(
494 const OLECHAR *name1,
495 const OLECHAR *name2)
496 {
497 LONG diff = lstrlenW(name1) - lstrlenW(name2);
498
499 while (diff == 0 && *name1 != 0)
500 {
501 /*
502 * We compare the string themselves only when they are of the same length
503 */
504 diff = toupperW(*name1++) - toupperW(*name2++);
505 }
506
507 return diff;
508 }
509
510 /****************************************************************************
511 *
512 * Internal Method
513 *
514 * Find and read the element of a storage with the given name.
515 */
516 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
517 const OLECHAR *name, DirEntry *data)
518 {
519 DirRef currentEntry;
520
521 /* Read the storage entry to find the root of the tree. */
522 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
523
524 currentEntry = data->dirRootEntry;
525
526 while (currentEntry != DIRENTRY_NULL)
527 {
528 LONG cmp;
529
530 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
531
532 cmp = entryNameCmp(name, data->name);
533
534 if (cmp == 0)
535 /* found it */
536 break;
537
538 else if (cmp < 0)
539 currentEntry = data->leftChild;
540
541 else if (cmp > 0)
542 currentEntry = data->rightChild;
543 }
544
545 return currentEntry;
546 }
547
548 /****************************************************************************
549 *
550 * Internal Method
551 *
552 * Find and read the binary tree parent of the element with the given name.
553 *
554 * If there is no such element, find a place where it could be inserted and
555 * return STG_E_FILENOTFOUND.
556 */
557 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
558 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
559 ULONG *relation)
560 {
561 DirRef childEntry;
562 DirEntry childData;
563
564 /* Read the storage entry to find the root of the tree. */
565 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
566
567 *parentEntry = storageEntry;
568 *relation = DIRENTRY_RELATION_DIR;
569
570 childEntry = parentData->dirRootEntry;
571
572 while (childEntry != DIRENTRY_NULL)
573 {
574 LONG cmp;
575
576 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
577
578 cmp = entryNameCmp(childName, childData.name);
579
580 if (cmp == 0)
581 /* found it */
582 break;
583
584 else if (cmp < 0)
585 {
586 *parentData = childData;
587 *parentEntry = childEntry;
588 *relation = DIRENTRY_RELATION_PREVIOUS;
589
590 childEntry = parentData->leftChild;
591 }
592
593 else if (cmp > 0)
594 {
595 *parentData = childData;
596 *parentEntry = childEntry;
597 *relation = DIRENTRY_RELATION_NEXT;
598
599 childEntry = parentData->rightChild;
600 }
601 }
602
603 if (childEntry == DIRENTRY_NULL)
604 return STG_E_FILENOTFOUND;
605 else
606 return S_OK;
607 }
608
609 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
610 {
611 switch (relation)
612 {
613 case DIRENTRY_RELATION_PREVIOUS:
614 entry->leftChild = new_target;
615 break;
616 case DIRENTRY_RELATION_NEXT:
617 entry->rightChild = new_target;
618 break;
619 case DIRENTRY_RELATION_DIR:
620 entry->dirRootEntry = new_target;
621 break;
622 default:
623 assert(0);
624 }
625 }
626
627 /****************************************************************************
628 *
629 * Internal Method
630 *
631 * Add a directory entry to a storage
632 */
633 static HRESULT insertIntoTree(
634 StorageBaseImpl *This,
635 DirRef parentStorageIndex,
636 DirRef newEntryIndex)
637 {
638 DirEntry currentEntry;
639 DirEntry newEntry;
640
641 /*
642 * Read the inserted entry
643 */
644 StorageBaseImpl_ReadDirEntry(This,
645 newEntryIndex,
646 &newEntry);
647
648 /*
649 * Read the storage entry
650 */
651 StorageBaseImpl_ReadDirEntry(This,
652 parentStorageIndex,
653 &currentEntry);
654
655 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
656 {
657 /*
658 * The root storage contains some element, therefore, start the research
659 * for the appropriate location.
660 */
661 BOOL found = FALSE;
662 DirRef current, next, previous, currentEntryId;
663
664 /*
665 * Keep a reference to the root of the storage's element tree
666 */
667 currentEntryId = currentEntry.dirRootEntry;
668
669 /*
670 * Read
671 */
672 StorageBaseImpl_ReadDirEntry(This,
673 currentEntry.dirRootEntry,
674 &currentEntry);
675
676 previous = currentEntry.leftChild;
677 next = currentEntry.rightChild;
678 current = currentEntryId;
679
680 while (!found)
681 {
682 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
683
684 if (diff < 0)
685 {
686 if (previous != DIRENTRY_NULL)
687 {
688 StorageBaseImpl_ReadDirEntry(This,
689 previous,
690 &currentEntry);
691 current = previous;
692 }
693 else
694 {
695 currentEntry.leftChild = newEntryIndex;
696 StorageBaseImpl_WriteDirEntry(This,
697 current,
698 &currentEntry);
699 found = TRUE;
700 }
701 }
702 else if (diff > 0)
703 {
704 if (next != DIRENTRY_NULL)
705 {
706 StorageBaseImpl_ReadDirEntry(This,
707 next,
708 &currentEntry);
709 current = next;
710 }
711 else
712 {
713 currentEntry.rightChild = newEntryIndex;
714 StorageBaseImpl_WriteDirEntry(This,
715 current,
716 &currentEntry);
717 found = TRUE;
718 }
719 }
720 else
721 {
722 /*
723 * Trying to insert an item with the same name in the
724 * subtree structure.
725 */
726 return STG_E_FILEALREADYEXISTS;
727 }
728
729 previous = currentEntry.leftChild;
730 next = currentEntry.rightChild;
731 }
732 }
733 else
734 {
735 /*
736 * The storage is empty, make the new entry the root of its element tree
737 */
738 currentEntry.dirRootEntry = newEntryIndex;
739 StorageBaseImpl_WriteDirEntry(This,
740 parentStorageIndex,
741 &currentEntry);
742 }
743
744 return S_OK;
745 }
746
747 /*************************************************************************
748 *
749 * Internal Method
750 *
751 * This method removes a directory entry from its parent storage tree without
752 * freeing any resources attached to it.
753 */
754 static HRESULT removeFromTree(
755 StorageBaseImpl *This,
756 DirRef parentStorageIndex,
757 DirRef deletedIndex)
758 {
759 DirEntry entryToDelete;
760 DirEntry parentEntry;
761 DirRef parentEntryRef;
762 ULONG typeOfRelation;
763 HRESULT hr;
764
765 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
766
767 if (hr != S_OK)
768 return hr;
769
770 /*
771 * Find the element that links to the one we want to delete.
772 */
773 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
774 &parentEntry, &parentEntryRef, &typeOfRelation);
775
776 if (hr != S_OK)
777 return hr;
778
779 if (entryToDelete.leftChild != DIRENTRY_NULL)
780 {
781 /*
782 * Replace the deleted entry with its left child
783 */
784 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
785
786 hr = StorageBaseImpl_WriteDirEntry(
787 This,
788 parentEntryRef,
789 &parentEntry);
790 if(FAILED(hr))
791 {
792 return hr;
793 }
794
795 if (entryToDelete.rightChild != DIRENTRY_NULL)
796 {
797 /*
798 * We need to reinsert the right child somewhere. We already know it and
799 * its children are greater than everything in the left tree, so we
800 * insert it at the rightmost point in the left tree.
801 */
802 DirRef newRightChildParent = entryToDelete.leftChild;
803 DirEntry newRightChildParentEntry;
804
805 do
806 {
807 hr = StorageBaseImpl_ReadDirEntry(
808 This,
809 newRightChildParent,
810 &newRightChildParentEntry);
811 if (FAILED(hr))
812 {
813 return hr;
814 }
815
816 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
817 newRightChildParent = newRightChildParentEntry.rightChild;
818 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
819
820 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
821
822 hr = StorageBaseImpl_WriteDirEntry(
823 This,
824 newRightChildParent,
825 &newRightChildParentEntry);
826 if (FAILED(hr))
827 {
828 return hr;
829 }
830 }
831 }
832 else
833 {
834 /*
835 * Replace the deleted entry with its right child
836 */
837 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
838
839 hr = StorageBaseImpl_WriteDirEntry(
840 This,
841 parentEntryRef,
842 &parentEntry);
843 if(FAILED(hr))
844 {
845 return hr;
846 }
847 }
848
849 return hr;
850 }
851
852
853 /************************************************************************
854 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
855 ***********************************************************************/
856
857 /*
858 * IEnumSTATSTGImpl definitions.
859 *
860 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
861 * This class allows iterating through the content of a storage and 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, %p)\n", iface, 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
7253 newStream->parentStorage = parentStorage;
7254 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7255 newStream->ownerDirEntry = dirEntry;
7256 newStream->indexCache = NULL;
7257 newStream->indexCacheLen = 0;
7258 newStream->indexCacheSize = 0;
7259 newStream->cachedBlocks[0].index = 0xffffffff;
7260 newStream->cachedBlocks[0].dirty = FALSE;
7261 newStream->cachedBlocks[1].index = 0xffffffff;
7262 newStream->cachedBlocks[1].dirty = FALSE;
7263 newStream->blockToEvict = 0;
7264
7265 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7266 {
7267 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7268 HeapFree(GetProcessHeap(), 0, newStream);
7269 return NULL;
7270 }
7271
7272 return newStream;
7273 }
7274
7275 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7276 {
7277 int i;
7278 if (!This) return S_OK;
7279 for (i=0; i<2; i++)
7280 {
7281 if (This->cachedBlocks[i].dirty)
7282 {
7283 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7284 This->cachedBlocks[i].dirty = FALSE;
7285 else
7286 return STG_E_WRITEFAULT;
7287 }
7288 }
7289 return S_OK;
7290 }
7291
7292 void BlockChainStream_Destroy(BlockChainStream* This)
7293 {
7294 if (This)
7295 {
7296 BlockChainStream_Flush(This);
7297 HeapFree(GetProcessHeap(), 0, This->indexCache);
7298 }
7299 HeapFree(GetProcessHeap(), 0, This);
7300 }
7301
7302 /******************************************************************************
7303 * BlockChainStream_Shrink
7304 *
7305 * Shrinks this chain in the big block depot.
7306 */
7307 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7308 ULARGE_INTEGER newSize)
7309 {
7310 ULONG blockIndex;
7311 ULONG numBlocks;
7312 int i;
7313
7314 /*
7315 * Figure out how many blocks are needed to contain the new size
7316 */
7317 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7318
7319 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7320 numBlocks++;
7321
7322 if (numBlocks)
7323 {
7324 /*
7325 * Go to the new end of chain
7326 */
7327 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7328
7329 /* Mark the new end of chain */
7330 StorageImpl_SetNextBlockInChain(
7331 This->parentStorage,
7332 blockIndex,
7333 BLOCK_END_OF_CHAIN);
7334
7335 This->tailIndex = blockIndex;
7336 }
7337 else
7338 {
7339 if (This->headOfStreamPlaceHolder != 0)
7340 {
7341 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7342 }
7343 else
7344 {
7345 DirEntry chainEntry;
7346 assert(This->ownerDirEntry != DIRENTRY_NULL);
7347
7348 StorageImpl_ReadDirEntry(
7349 This->parentStorage,
7350 This->ownerDirEntry,
7351 &chainEntry);
7352
7353 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7354
7355 StorageImpl_WriteDirEntry(
7356 This->parentStorage,
7357 This->ownerDirEntry,
7358 &chainEntry);
7359 }
7360
7361 This->tailIndex = BLOCK_END_OF_CHAIN;
7362 }
7363
7364 This->numBlocks = numBlocks;
7365
7366 /*
7367 * Mark the extra blocks as free
7368 */
7369 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7370 {
7371 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7372 StorageImpl_FreeBigBlock(This->parentStorage,
7373 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7374 if (last_run->lastOffset == last_run->firstOffset)
7375 This->indexCacheLen--;
7376 else
7377 last_run->lastOffset--;
7378 }
7379
7380 /*
7381 * Reset the last accessed block cache.
7382 */
7383 for (i=0; i<2; i++)
7384 {
7385 if (This->cachedBlocks[i].index >= numBlocks)
7386 {
7387 This->cachedBlocks[i].index = 0xffffffff;
7388 This->cachedBlocks[i].dirty = FALSE;
7389 }
7390 }
7391
7392 return TRUE;
7393 }
7394
7395 /******************************************************************************
7396 * BlockChainStream_Enlarge
7397 *
7398 * Grows this chain in the big block depot.
7399 */
7400 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7401 ULARGE_INTEGER newSize)
7402 {
7403 ULONG blockIndex, currentBlock;
7404 ULONG newNumBlocks;
7405 ULONG oldNumBlocks = 0;
7406
7407 blockIndex = BlockChainStream_GetHeadOfChain(This);
7408
7409 /*
7410 * Empty chain. Create the head.
7411 */
7412 if (blockIndex == BLOCK_END_OF_CHAIN)
7413 {
7414 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7415 StorageImpl_SetNextBlockInChain(This->parentStorage,
7416 blockIndex,
7417 BLOCK_END_OF_CHAIN);
7418
7419 if (This->headOfStreamPlaceHolder != 0)
7420 {
7421 *(This->headOfStreamPlaceHolder) = blockIndex;
7422 }
7423 else
7424 {
7425 DirEntry chainEntry;
7426 assert(This->ownerDirEntry != DIRENTRY_NULL);
7427
7428 StorageImpl_ReadDirEntry(
7429 This->parentStorage,
7430 This->ownerDirEntry,
7431 &chainEntry);
7432
7433 chainEntry.startingBlock = blockIndex;
7434
7435 StorageImpl_WriteDirEntry(
7436 This->parentStorage,
7437 This->ownerDirEntry,
7438 &chainEntry);
7439 }
7440
7441 This->tailIndex = blockIndex;
7442 This->numBlocks = 1;
7443 }
7444
7445 /*
7446 * Figure out how many blocks are needed to contain this stream
7447 */
7448 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7449
7450 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7451 newNumBlocks++;
7452
7453 /*
7454 * Go to the current end of chain
7455 */
7456 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7457 {
7458 currentBlock = blockIndex;
7459
7460 while (blockIndex != BLOCK_END_OF_CHAIN)
7461 {
7462 This->numBlocks++;
7463 currentBlock = blockIndex;
7464
7465 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7466 &blockIndex)))
7467 return FALSE;
7468 }
7469
7470 This->tailIndex = currentBlock;
7471 }
7472
7473 currentBlock = This->tailIndex;
7474 oldNumBlocks = This->numBlocks;
7475
7476 /*
7477 * Add new blocks to the chain
7478 */
7479 if (oldNumBlocks < newNumBlocks)
7480 {
7481 while (oldNumBlocks < newNumBlocks)
7482 {
7483 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7484
7485 StorageImpl_SetNextBlockInChain(
7486 This->parentStorage,
7487 currentBlock,
7488 blockIndex);
7489
7490 StorageImpl_SetNextBlockInChain(
7491 This->parentStorage,
7492 blockIndex,
7493 BLOCK_END_OF_CHAIN);
7494
7495 currentBlock = blockIndex;
7496 oldNumBlocks++;
7497 }
7498
7499 This->tailIndex = blockIndex;
7500 This->numBlocks = newNumBlocks;
7501 }
7502
7503 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7504 return FALSE;
7505
7506 return TRUE;
7507 }
7508
7509
7510 /******************************************************************************
7511 * BlockChainStream_GetSize
7512 *
7513 * Returns the size of this chain.
7514 * Will return the block count if this chain doesn't have a directory entry.
7515 */
7516 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7517 {
7518 DirEntry chainEntry;
7519
7520 if(This->headOfStreamPlaceHolder == NULL)
7521 {
7522 /*
7523 * This chain has a directory entry so use the size value from there.
7524 */
7525 StorageImpl_ReadDirEntry(
7526 This->parentStorage,
7527 This->ownerDirEntry,
7528 &chainEntry);
7529
7530 return chainEntry.size;
7531 }
7532 else
7533 {
7534 /*
7535 * this chain is a chain that does not have a directory entry, figure out the
7536 * size by making the product number of used blocks times the
7537 * size of them
7538 */
7539 ULARGE_INTEGER result;
7540 result.QuadPart =
7541 (ULONGLONG)BlockChainStream_GetCount(This) *
7542 This->parentStorage->bigBlockSize;
7543
7544 return result;
7545 }
7546 }
7547
7548 /******************************************************************************
7549 * BlockChainStream_SetSize
7550 *
7551 * Sets the size of this stream. The big block depot will be updated.
7552 * The file will grow if we grow the chain.
7553 *
7554 * TODO: Free the actual blocks in the file when we shrink the chain.
7555 * Currently, the blocks are still in the file. So the file size
7556 * doesn't shrink even if we shrink streams.
7557 */
7558 BOOL BlockChainStream_SetSize(
7559 BlockChainStream* This,
7560 ULARGE_INTEGER newSize)
7561 {
7562 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7563
7564 if (newSize.QuadPart == size.QuadPart)
7565 return TRUE;
7566
7567 if (newSize.QuadPart < size.QuadPart)
7568 {
7569 BlockChainStream_Shrink(This, newSize);
7570 }
7571 else
7572 {
7573 BlockChainStream_Enlarge(This, newSize);
7574 }
7575
7576 return TRUE;
7577 }
7578
7579 /******************************************************************************
7580 * BlockChainStream_ReadAt
7581 *
7582 * Reads a specified number of bytes from this chain at the specified offset.
7583 * bytesRead may be NULL.
7584 * Failure will be returned if the specified number of bytes has not been read.
7585 */
7586 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7587 ULARGE_INTEGER offset,
7588 ULONG size,
7589 void* buffer,
7590 ULONG* bytesRead)
7591 {
7592 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7593 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7594 ULONG bytesToReadInBuffer;
7595 ULONG blockIndex;
7596 BYTE* bufferWalker;
7597 ULARGE_INTEGER stream_size;
7598 HRESULT hr;
7599 BlockChainBlock *cachedBlock;
7600
7601 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7602
7603 /*
7604 * Find the first block in the stream that contains part of the buffer.
7605 */
7606 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7607
7608 *bytesRead = 0;
7609
7610 stream_size = BlockChainStream_GetSize(This);
7611 if (stream_size.QuadPart > offset.QuadPart)
7612 size = min(stream_size.QuadPart - offset.QuadPart, size);
7613 else
7614 return S_OK;
7615
7616 /*
7617 * Start reading the buffer.
7618 */
7619 bufferWalker = buffer;
7620
7621 while (size > 0)
7622 {
7623 ULARGE_INTEGER ulOffset;
7624 DWORD bytesReadAt;
7625
7626 /*
7627 * Calculate how many bytes we can copy from this big block.
7628 */
7629 bytesToReadInBuffer =
7630 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7631
7632 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7633
7634 if (FAILED(hr))
7635 return hr;
7636
7637 if (!cachedBlock)
7638 {
7639 /* Not in cache, and we're going to read past the end of the block. */
7640 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7641 offsetInBlock;
7642
7643 StorageImpl_ReadAt(This->parentStorage,
7644 ulOffset,
7645 bufferWalker,
7646 bytesToReadInBuffer,
7647 &bytesReadAt);
7648 }
7649 else
7650 {
7651 if (!cachedBlock->read)
7652 {
7653 ULONG read;
7654 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7655 return STG_E_READFAULT;
7656
7657 cachedBlock->read = TRUE;
7658 }
7659
7660 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7661 bytesReadAt = bytesToReadInBuffer;
7662 }
7663
7664 blockNoInSequence++;
7665 bufferWalker += bytesReadAt;
7666 size -= bytesReadAt;
7667 *bytesRead += bytesReadAt;
7668 offsetInBlock = 0; /* There is no offset on the next block */
7669
7670 if (bytesToReadInBuffer != bytesReadAt)
7671 break;
7672 }
7673
7674 return S_OK;
7675 }
7676
7677 /******************************************************************************
7678 * BlockChainStream_WriteAt
7679 *
7680 * Writes the specified number of bytes to this chain at the specified offset.
7681 * Will fail if not all specified number of bytes have been written.
7682 */
7683 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7684 ULARGE_INTEGER offset,
7685 ULONG size,
7686 const void* buffer,
7687 ULONG* bytesWritten)
7688 {
7689 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7690 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7691 ULONG bytesToWrite;
7692 ULONG blockIndex;
7693 const BYTE* bufferWalker;
7694 HRESULT hr;
7695 BlockChainBlock *cachedBlock;
7696
7697 *bytesWritten = 0;
7698 bufferWalker = buffer;
7699
7700 while (size > 0)
7701 {
7702 ULARGE_INTEGER ulOffset;
7703 DWORD bytesWrittenAt;
7704
7705 /*
7706 * Calculate how many bytes we can copy to this big block.
7707 */
7708 bytesToWrite =
7709 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7710
7711 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7712
7713 /* BlockChainStream_SetSize should have already been called to ensure we have
7714 * enough blocks in the chain to write into */
7715 if (FAILED(hr))
7716 {
7717 ERR("not enough blocks in chain to write data\n");
7718 return hr;
7719 }
7720
7721 if (!cachedBlock)
7722 {
7723 /* Not in cache, and we're going to write past the end of the block. */
7724 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7725 offsetInBlock;
7726
7727 StorageImpl_WriteAt(This->parentStorage,
7728 ulOffset,
7729 bufferWalker,
7730 bytesToWrite,
7731 &bytesWrittenAt);
7732 }
7733 else
7734 {
7735 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7736 {
7737 ULONG read;
7738 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7739 return STG_E_READFAULT;
7740 }
7741
7742 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7743 bytesWrittenAt = bytesToWrite;
7744 cachedBlock->read = TRUE;
7745 cachedBlock->dirty = TRUE;
7746 }
7747
7748 blockNoInSequence++;
7749 bufferWalker += bytesWrittenAt;
7750 size -= bytesWrittenAt;
7751 *bytesWritten += bytesWrittenAt;
7752 offsetInBlock = 0; /* There is no offset on the next block */
7753
7754 if (bytesWrittenAt != bytesToWrite)
7755 break;
7756 }
7757
7758 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7759 }
7760
7761
7762 /************************************************************************
7763 * SmallBlockChainStream implementation
7764 ***********************************************************************/
7765
7766 SmallBlockChainStream* SmallBlockChainStream_Construct(
7767 StorageImpl* parentStorage,
7768 ULONG* headOfStreamPlaceHolder,
7769 DirRef dirEntry)
7770 {
7771 SmallBlockChainStream* newStream;
7772
7773 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7774
7775 newStream->parentStorage = parentStorage;
7776 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7777 newStream->ownerDirEntry = dirEntry;
7778
7779 return newStream;
7780 }
7781
7782 void SmallBlockChainStream_Destroy(
7783 SmallBlockChainStream* This)
7784 {
7785 HeapFree(GetProcessHeap(), 0, This);
7786 }
7787
7788 /******************************************************************************
7789 * SmallBlockChainStream_GetHeadOfChain
7790 *
7791 * Returns the head of this chain of small blocks.
7792 */
7793 static ULONG SmallBlockChainStream_GetHeadOfChain(
7794 SmallBlockChainStream* This)
7795 {
7796 DirEntry chainEntry;
7797 HRESULT hr;
7798
7799 if (This->headOfStreamPlaceHolder != NULL)
7800 return *(This->headOfStreamPlaceHolder);
7801
7802 if (This->ownerDirEntry)
7803 {
7804 hr = StorageImpl_ReadDirEntry(
7805 This->parentStorage,
7806 This->ownerDirEntry,
7807 &chainEntry);
7808
7809 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7810 return chainEntry.startingBlock;
7811 }
7812
7813 return BLOCK_END_OF_CHAIN;
7814 }
7815
7816 /******************************************************************************
7817 * SmallBlockChainStream_GetNextBlockInChain
7818 *
7819 * Returns the index of the next small block in this chain.
7820 *
7821 * Return Values:
7822 * - BLOCK_END_OF_CHAIN: end of this chain
7823 * - BLOCK_UNUSED: small block 'blockIndex' is free
7824 */
7825 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7826 SmallBlockChainStream* This,
7827 ULONG blockIndex,
7828 ULONG* nextBlockInChain)
7829 {
7830 ULARGE_INTEGER offsetOfBlockInDepot;
7831 DWORD buffer;
7832 ULONG bytesRead;
7833 HRESULT res;
7834
7835 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7836
7837 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7838
7839 /*
7840 * Read those bytes in the buffer from the small block file.
7841 */
7842 res = BlockChainStream_ReadAt(
7843 This->parentStorage->smallBlockDepotChain,
7844 offsetOfBlockInDepot,
7845 sizeof(DWORD),
7846 &buffer,
7847 &bytesRead);
7848
7849 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7850 res = STG_E_READFAULT;
7851
7852 if (SUCCEEDED(res))
7853 {
7854 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7855 return S_OK;
7856 }
7857
7858 return res;
7859 }
7860
7861 /******************************************************************************
7862 * SmallBlockChainStream_SetNextBlockInChain
7863 *
7864 * Writes the index of the next block of the specified block in the small
7865 * block depot.
7866 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7867 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7868 */
7869 static void SmallBlockChainStream_SetNextBlockInChain(
7870 SmallBlockChainStream* This,
7871 ULONG blockIndex,
7872 ULONG nextBlock)
7873 {
7874 ULARGE_INTEGER offsetOfBlockInDepot;
7875 DWORD buffer;
7876 ULONG bytesWritten;
7877
7878 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7879
7880 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7881
7882 /*
7883 * Read those bytes in the buffer from the small block file.
7884 */
7885 BlockChainStream_WriteAt(
7886 This->parentStorage->smallBlockDepotChain,
7887 offsetOfBlockInDepot,
7888 sizeof(DWORD),
7889 &buffer,
7890 &bytesWritten);
7891 }
7892
7893 /******************************************************************************
7894 * SmallBlockChainStream_FreeBlock
7895 *
7896 * Flag small block 'blockIndex' as free in the small block depot.
7897 */
7898 static void SmallBlockChainStream_FreeBlock(
7899 SmallBlockChainStream* This,
7900 ULONG blockIndex)
7901 {
7902 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7903 }
7904
7905 /******************************************************************************
7906 * SmallBlockChainStream_GetNextFreeBlock
7907 *
7908 * Returns the index of a free small block. The small block depot will be
7909 * enlarged if necessary. The small block chain will also be enlarged if
7910 * necessary.
7911 */
7912 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7913 SmallBlockChainStream* This)
7914 {
7915 ULARGE_INTEGER offsetOfBlockInDepot;
7916 DWORD buffer;
7917 ULONG bytesRead;
7918 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7919 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7920 HRESULT res = S_OK;
7921 ULONG smallBlocksPerBigBlock;
7922 DirEntry rootEntry;
7923 ULONG blocksRequired;
7924 ULARGE_INTEGER old_size, size_required;
7925
7926 offsetOfBlockInDepot.u.HighPart = 0;
7927
7928 /*
7929 * Scan the small block depot for a free block
7930 */
7931 while (nextBlockIndex != BLOCK_UNUSED)
7932 {
7933 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7934
7935 res = BlockChainStream_ReadAt(
7936 This->parentStorage->smallBlockDepotChain,
7937 offsetOfBlockInDepot,
7938 sizeof(DWORD),
7939 &buffer,
7940 &bytesRead);
7941
7942 /*
7943 * If we run out of space for the small block depot, enlarge it
7944 */
7945 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7946 {
7947 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7948
7949 if (nextBlockIndex != BLOCK_UNUSED)
7950 blockIndex++;
7951 }
7952 else
7953 {
7954 ULONG count =
7955 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7956
7957 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7958 ULARGE_INTEGER newSize, offset;
7959 ULONG bytesWritten;
7960
7961 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7962 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7963
7964 /*
7965 * Initialize all the small blocks to free
7966 */
7967 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7968 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7969 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7970 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7971
7972 StorageImpl_SaveFileHeader(This->parentStorage);
7973 }
7974 }
7975
7976 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7977
7978 smallBlocksPerBigBlock =
7979 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7980
7981 /*
7982 * Verify if we have to allocate big blocks to contain small blocks
7983 */
7984 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7985
7986 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7987
7988 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7989
7990 if (size_required.QuadPart > old_size.QuadPart)
7991 {
7992 BlockChainStream_SetSize(
7993 This->parentStorage->smallBlockRootChain,
7994 size_required);
7995
7996 StorageImpl_ReadDirEntry(
7997 This->parentStorage,
7998 This->parentStorage->base.storageDirEntry,
7999 &rootEntry);
8000
8001 rootEntry.size = size_required;
8002
8003 StorageImpl_WriteDirEntry(
8004 This->parentStorage,
8005 This->parentStorage->base.storageDirEntry,
8006 &rootEntry);
8007 }
8008
8009 return blockIndex;
8010 }
8011
8012 /******************************************************************************
8013 * SmallBlockChainStream_ReadAt
8014 *
8015 * Reads a specified number of bytes from this chain at the specified offset.
8016 * bytesRead may be NULL.
8017 * Failure will be returned if the specified number of bytes has not been read.
8018 */
8019 HRESULT SmallBlockChainStream_ReadAt(
8020 SmallBlockChainStream* This,
8021 ULARGE_INTEGER offset,
8022 ULONG size,
8023 void* buffer,
8024 ULONG* bytesRead)
8025 {
8026 HRESULT rc = S_OK;
8027 ULARGE_INTEGER offsetInBigBlockFile;
8028 ULONG blockNoInSequence =
8029 offset.u.LowPart / This->parentStorage->smallBlockSize;
8030
8031 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8032 ULONG bytesToReadInBuffer;
8033 ULONG blockIndex;
8034 ULONG bytesReadFromBigBlockFile;
8035 BYTE* bufferWalker;
8036 ULARGE_INTEGER stream_size;
8037
8038 /*
8039 * This should never happen on a small block file.
8040 */
8041 assert(offset.u.HighPart==0);
8042
8043 *bytesRead = 0;
8044
8045 stream_size = SmallBlockChainStream_GetSize(This);
8046 if (stream_size.QuadPart > offset.QuadPart)
8047 size = min(stream_size.QuadPart - offset.QuadPart, size);
8048 else
8049 return S_OK;
8050
8051 /*
8052 * Find the first block in the stream that contains part of the buffer.
8053 */
8054 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8055
8056 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8057 {
8058 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8059 if(FAILED(rc))
8060 return rc;
8061 blockNoInSequence--;
8062 }
8063
8064 /*
8065 * Start reading the buffer.
8066 */
8067 bufferWalker = buffer;
8068
8069 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8070 {
8071 /*
8072 * Calculate how many bytes we can copy from this small block.
8073 */
8074 bytesToReadInBuffer =
8075 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8076
8077 /*
8078 * Calculate the offset of the small block in the small block file.
8079 */
8080 offsetInBigBlockFile.QuadPart =
8081 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8082
8083 offsetInBigBlockFile.QuadPart += offsetInBlock;
8084
8085 /*
8086 * Read those bytes in the buffer from the small block file.
8087 * The small block has already been identified so it shouldn't fail
8088 * unless the file is corrupt.
8089 */
8090 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8091 offsetInBigBlockFile,
8092 bytesToReadInBuffer,
8093 bufferWalker,
8094 &bytesReadFromBigBlockFile);
8095
8096 if (FAILED(rc))
8097 return rc;
8098
8099 if (!bytesReadFromBigBlockFile)
8100 return STG_E_DOCFILECORRUPT;
8101
8102 /*
8103 * Step to the next big block.
8104 */
8105 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8106 if(FAILED(rc))
8107 return STG_E_DOCFILECORRUPT;
8108
8109 bufferWalker += bytesReadFromBigBlockFile;
8110 size -= bytesReadFromBigBlockFile;
8111 *bytesRead += bytesReadFromBigBlockFile;
8112 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8113 }
8114
8115 return S_OK;
8116 }
8117
8118 /******************************************************************************
8119 * SmallBlockChainStream_WriteAt
8120 *
8121 * Writes the specified number of bytes to this chain at the specified offset.
8122 * Will fail if not all specified number of bytes have been written.
8123 */
8124 HRESULT SmallBlockChainStream_WriteAt(
8125 SmallBlockChainStream* This,
8126 ULARGE_INTEGER offset,
8127 ULONG size,
8128 const void* buffer,
8129 ULONG* bytesWritten)
8130 {
8131 ULARGE_INTEGER offsetInBigBlockFile;
8132 ULONG blockNoInSequence =
8133 offset.u.LowPart / This->parentStorage->smallBlockSize;
8134
8135 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8136 ULONG bytesToWriteInBuffer;
8137 ULONG blockIndex;
8138 ULONG bytesWrittenToBigBlockFile;
8139 const BYTE* bufferWalker;
8140 HRESULT res;
8141
8142 /*
8143 * This should never happen on a small block file.
8144 */
8145 assert(offset.u.HighPart==0);
8146
8147 /*
8148 * Find the first block in the stream that contains part of the buffer.
8149 */
8150 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8151
8152 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
8153 {
8154 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8155 return STG_E_DOCFILECORRUPT;
8156 blockNoInSequence--;
8157 }
8158
8159 /*
8160 * Start writing the buffer.
8161 */
8162 *bytesWritten = 0;
8163 bufferWalker = buffer;
8164 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8165 {
8166 /*
8167 * Calculate how many bytes we can copy to this small block.
8168 */
8169 bytesToWriteInBuffer =
8170 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8171
8172 /*
8173 * Calculate the offset of the small block in the small block file.
8174 */
8175 offsetInBigBlockFile.QuadPart =
8176 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8177
8178 offsetInBigBlockFile.QuadPart += offsetInBlock;
8179
8180 /*
8181 * Write those bytes in the buffer to the small block file.
8182 */
8183 res = BlockChainStream_WriteAt(
8184 This->parentStorage->smallBlockRootChain,
8185 offsetInBigBlockFile,
8186 bytesToWriteInBuffer,
8187 bufferWalker,
8188 &bytesWrittenToBigBlockFile);
8189 if (FAILED(res))
8190 return res;
8191
8192 /*
8193 * Step to the next big block.
8194 */
8195 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8196 if (FAILED(res))
8197 return res;
8198 bufferWalker += bytesWrittenToBigBlockFile;
8199 size -= bytesWrittenToBigBlockFile;
8200 *bytesWritten += bytesWrittenToBigBlockFile;
8201 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8202 }
8203
8204 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8205 }
8206
8207 /******************************************************************************
8208 * SmallBlockChainStream_Shrink
8209 *
8210 * Shrinks this chain in the small block depot.
8211 */
8212 static BOOL SmallBlockChainStream_Shrink(
8213 SmallBlockChainStream* This,
8214 ULARGE_INTEGER newSize)
8215 {
8216 ULONG blockIndex, extraBlock;
8217 ULONG numBlocks;
8218 ULONG count = 0;
8219
8220 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8221
8222 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8223 numBlocks++;
8224
8225 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8226
8227 /*
8228 * Go to the new end of chain
8229 */
8230 while (count < numBlocks)
8231 {
8232 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8233 &blockIndex)))
8234 return FALSE;
8235 count++;
8236 }
8237
8238 /*
8239 * If the count is 0, we have a special case, the head of the chain was
8240 * just freed.
8241 */
8242 if (count == 0)
8243 {
8244 DirEntry chainEntry;
8245
8246 StorageImpl_ReadDirEntry(This->parentStorage,
8247 This->ownerDirEntry,
8248 &chainEntry);
8249
8250 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8251
8252 StorageImpl_WriteDirEntry(This->parentStorage,
8253 This->ownerDirEntry,
8254 &chainEntry);
8255
8256 /*
8257 * We start freeing the chain at the head block.
8258 */
8259 extraBlock = blockIndex;
8260 }
8261 else
8262 {
8263 /* Get the next block before marking the new end */
8264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8265 &extraBlock)))
8266 return FALSE;
8267
8268 /* Mark the new end of chain */
8269 SmallBlockChainStream_SetNextBlockInChain(
8270 This,
8271 blockIndex,
8272 BLOCK_END_OF_CHAIN);
8273 }
8274
8275 /*
8276 * Mark the extra blocks as free
8277 */
8278 while (extraBlock != BLOCK_END_OF_CHAIN)
8279 {
8280 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8281 &blockIndex)))
8282 return FALSE;
8283 SmallBlockChainStream_FreeBlock(This, extraBlock);
8284 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8285 extraBlock = blockIndex;
8286 }
8287
8288 return TRUE;
8289 }
8290
8291 /******************************************************************************
8292 * SmallBlockChainStream_Enlarge
8293 *
8294 * Grows this chain in the small block depot.
8295 */
8296 static BOOL SmallBlockChainStream_Enlarge(
8297 SmallBlockChainStream* This,
8298 ULARGE_INTEGER newSize)
8299 {
8300 ULONG blockIndex, currentBlock;
8301 ULONG newNumBlocks;
8302 ULONG oldNumBlocks = 0;
8303
8304 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8305
8306 /*
8307 * Empty chain. Create the head.
8308 */
8309 if (blockIndex == BLOCK_END_OF_CHAIN)
8310 {
8311 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8312 SmallBlockChainStream_SetNextBlockInChain(
8313 This,
8314 blockIndex,
8315 BLOCK_END_OF_CHAIN);
8316
8317 if (This->headOfStreamPlaceHolder != NULL)
8318 {
8319 *(This->headOfStreamPlaceHolder) = blockIndex;
8320 }
8321 else
8322 {
8323 DirEntry chainEntry;
8324
8325 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8326 &chainEntry);
8327
8328 chainEntry.startingBlock = blockIndex;
8329
8330 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8331 &chainEntry);
8332 }
8333 }
8334
8335 currentBlock = blockIndex;
8336
8337 /*
8338 * Figure out how many blocks are needed to contain this stream
8339 */
8340 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8341
8342 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8343 newNumBlocks++;
8344
8345 /*
8346 * Go to the current end of chain
8347 */
8348 while (blockIndex != BLOCK_END_OF_CHAIN)
8349 {
8350 oldNumBlocks++;
8351 currentBlock = blockIndex;
8352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8353 return FALSE;
8354 }
8355
8356 /*
8357 * Add new blocks to the chain
8358 */
8359 while (oldNumBlocks < newNumBlocks)
8360 {
8361 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8362 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8363
8364 SmallBlockChainStream_SetNextBlockInChain(
8365 This,
8366 blockIndex,
8367 BLOCK_END_OF_CHAIN);
8368
8369 currentBlock = blockIndex;
8370 oldNumBlocks++;
8371 }
8372
8373 return TRUE;
8374 }
8375
8376 /******************************************************************************
8377 * SmallBlockChainStream_SetSize
8378 *
8379 * Sets the size of this stream.
8380 * The file will grow if we grow the chain.
8381 *
8382 * TODO: Free the actual blocks in the file when we shrink the chain.
8383 * Currently, the blocks are still in the file. So the file size
8384 * doesn't shrink even if we shrink streams.
8385 */
8386 BOOL SmallBlockChainStream_SetSize(
8387 SmallBlockChainStream* This,
8388 ULARGE_INTEGER newSize)
8389 {
8390 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8391
8392 if (newSize.u.LowPart == size.u.LowPart)
8393 return TRUE;
8394
8395 if (newSize.u.LowPart < size.u.LowPart)
8396 {
8397 SmallBlockChainStream_Shrink(This, newSize);
8398 }
8399 else
8400 {
8401 SmallBlockChainStream_Enlarge(This, newSize);
8402 }
8403
8404 return TRUE;
8405 }
8406
8407 /******************************************************************************
8408 * SmallBlockChainStream_GetCount
8409 *
8410 * Returns the number of small blocks that comprises this chain.
8411 * This is not the size of the stream as the last block may not be full!
8412 *
8413 */
8414 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8415 {
8416 ULONG blockIndex;
8417 ULONG count = 0;
8418
8419 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8420
8421 while(blockIndex != BLOCK_END_OF_CHAIN)
8422 {
8423 count++;
8424
8425 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8426 blockIndex, &blockIndex)))
8427 return 0;
8428 }
8429
8430 return count;
8431 }
8432
8433 /******************************************************************************
8434 * SmallBlockChainStream_GetSize
8435 *
8436 * Returns the size of this chain.
8437 */
8438 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8439 {
8440 DirEntry chainEntry;
8441
8442 if(This->headOfStreamPlaceHolder != NULL)
8443 {
8444 ULARGE_INTEGER result;
8445 result.u.HighPart = 0;
8446
8447 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8448 This->parentStorage->smallBlockSize;
8449
8450 return result;
8451 }
8452
8453 StorageImpl_ReadDirEntry(
8454 This->parentStorage,
8455 This->ownerDirEntry,
8456 &chainEntry);
8457
8458 return chainEntry.size;
8459 }
8460
8461
8462 /************************************************************************
8463 * Miscellaneous storage functions
8464 ***********************************************************************/
8465
8466 static HRESULT create_storagefile(
8467 LPCOLESTR pwcsName,
8468 DWORD grfMode,
8469 DWORD grfAttrs,
8470 STGOPTIONS* pStgOptions,
8471 REFIID riid,
8472 void** ppstgOpen)
8473 {
8474 StorageBaseImpl* newStorage = 0;
8475 HANDLE hFile = INVALID_HANDLE_VALUE;
8476 HRESULT hr = STG_E_INVALIDFLAG;
8477 DWORD shareMode;
8478 DWORD accessMode;
8479 DWORD creationMode;
8480 DWORD fileAttributes;
8481 WCHAR tempFileName[MAX_PATH];
8482
8483 if (ppstgOpen == 0)
8484 return STG_E_INVALIDPOINTER;
8485
8486 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8487 return STG_E_INVALIDPARAMETER;
8488
8489 /* if no share mode given then DENY_NONE is the default */
8490 if (STGM_SHARE_MODE(grfMode) == 0)
8491 grfMode |= STGM_SHARE_DENY_NONE;
8492
8493 if ( FAILED( validateSTGM(grfMode) ))
8494 goto end;
8495
8496 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8497 switch(STGM_ACCESS_MODE(grfMode))
8498 {
8499 case STGM_WRITE:
8500 case STGM_READWRITE:
8501 break;
8502 default:
8503 goto end;
8504 }
8505
8506 /* in direct mode, can only use SHARE_EXCLUSIVE */
8507 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8508 goto end;
8509
8510 /* but in transacted mode, any share mode is valid */
8511
8512 /*
8513 * Generate a unique name.
8514 */
8515 if (pwcsName == 0)
8516 {
8517 WCHAR tempPath[MAX_PATH];
8518 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8519
8520 memset(tempPath, 0, sizeof(tempPath));
8521 memset(tempFileName, 0, sizeof(tempFileName));
8522
8523 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8524 tempPath[0] = '.';
8525
8526 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8527 pwcsName = tempFileName;
8528 else
8529 {
8530 hr = STG_E_INSUFFICIENTMEMORY;
8531 goto end;
8532 }
8533
8534 creationMode = TRUNCATE_EXISTING;
8535 }
8536 else
8537 {
8538 creationMode = GetCreationModeFromSTGM(grfMode);
8539 }
8540
8541 /*
8542 * Interpret the STGM value grfMode
8543 */
8544 shareMode = GetShareModeFromSTGM(grfMode);
8545 accessMode = GetAccessModeFromSTGM(grfMode);
8546
8547 if (grfMode & STGM_DELETEONRELEASE)
8548 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8549 else
8550 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8551
8552 *ppstgOpen = 0;
8553
8554 hFile = CreateFileW(pwcsName,
8555 accessMode,
8556 shareMode,
8557 NULL,
8558 creationMode,
8559 fileAttributes,
8560 0);
8561
8562 if (hFile == INVALID_HANDLE_VALUE)
8563 {
8564 if(GetLastError() == ERROR_FILE_EXISTS)
8565 hr = STG_E_FILEALREADYEXISTS;
8566 else
8567 hr = E_FAIL;
8568 goto end;
8569 }
8570
8571 /*
8572 * Allocate and initialize the new IStorage object.
8573 */
8574 hr = Storage_Construct(
8575 hFile,
8576 pwcsName,
8577 NULL,
8578 grfMode,
8579 TRUE,
8580 TRUE,
8581 pStgOptions->ulSectorSize,
8582 &newStorage);
8583
8584 if (FAILED(hr))
8585 {
8586 goto end;
8587 }
8588
8589 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8590 IStorage_Release(&newStorage->IStorage_iface);
8591
8592 end:
8593 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8594
8595 return hr;
8596 }
8597
8598 /******************************************************************************
8599 * StgCreateDocfile [OLE32.@]
8600 * Creates a new compound file storage object
8601 *
8602 * PARAMS
8603 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8604 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8605 * reserved [ ?] unused?, usually 0
8606 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8607 *
8608 * RETURNS
8609 * S_OK if the file was successfully created
8610 * some STG_E_ value if error
8611 * NOTES
8612 * if pwcsName is NULL, create file with new unique name
8613 * the function can returns
8614 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8615 * (unrealized now)
8616 */
8617 HRESULT WINAPI StgCreateDocfile(
8618 LPCOLESTR pwcsName,
8619 DWORD grfMode,
8620 DWORD reserved,
8621 IStorage **ppstgOpen)
8622 {
8623 STGOPTIONS stgoptions = {1, 0, 512};
8624
8625 TRACE("(%s, %x, %d, %p)\n",
8626 debugstr_w(pwcsName), grfMode,
8627 reserved, ppstgOpen);
8628
8629 if (ppstgOpen == 0)
8630 return STG_E_INVALIDPOINTER;
8631 if (reserved != 0)
8632 return STG_E_INVALIDPARAMETER;
8633
8634 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8635 }
8636
8637 /******************************************************************************
8638 * StgCreateStorageEx [OLE32.@]
8639 */
8640 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8641 {
8642 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8643 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8644
8645 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8646 {
8647 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8648 return STG_E_INVALIDPARAMETER;
8649 }
8650
8651 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8652 {
8653 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8654 return STG_E_INVALIDPARAMETER;
8655 }
8656
8657 if (stgfmt == STGFMT_FILE)
8658 {
8659 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8660 return STG_E_INVALIDPARAMETER;
8661 }
8662
8663 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8664 {
8665 STGOPTIONS defaultOptions = {1, 0, 512};
8666
8667 if (!pStgOptions) pStgOptions = &defaultOptions;
8668 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8669 }
8670
8671
8672 ERR("Invalid stgfmt argument\n");
8673 return STG_E_INVALIDPARAMETER;
8674 }
8675
8676 /******************************************************************************
8677 * StgCreatePropSetStg [OLE32.@]
8678 */
8679 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8680 IPropertySetStorage **propset)
8681 {
8682 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8683 if (reserved)
8684 return STG_E_INVALIDPARAMETER;
8685
8686 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8687 }
8688
8689 /******************************************************************************
8690 * StgOpenStorageEx [OLE32.@]
8691 */
8692 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8693 {
8694 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8695 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8696
8697 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8698 {
8699 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8700 return STG_E_INVALIDPARAMETER;
8701 }
8702
8703 switch (stgfmt)
8704 {
8705 case STGFMT_FILE:
8706 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8707 return STG_E_INVALIDPARAMETER;
8708
8709 case STGFMT_STORAGE:
8710 break;
8711
8712 case STGFMT_DOCFILE:
8713 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8714 {
8715 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8716 return STG_E_INVALIDPARAMETER;
8717 }
8718 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8719 break;
8720
8721 case STGFMT_ANY:
8722 WARN("STGFMT_ANY assuming storage\n");
8723 break;
8724
8725 default:
8726 return STG_E_INVALIDPARAMETER;
8727 }
8728
8729 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8730 }
8731
8732
8733 /******************************************************************************
8734 * StgOpenStorage [OLE32.@]
8735 */
8736 HRESULT WINAPI StgOpenStorage(
8737 const OLECHAR *pwcsName,
8738 IStorage *pstgPriority,
8739 DWORD grfMode,
8740 SNB snbExclude,
8741 DWORD reserved,
8742 IStorage **ppstgOpen)
8743 {
8744 StorageBaseImpl* newStorage = 0;
8745 HRESULT hr = S_OK;
8746 HANDLE hFile = 0;
8747 DWORD shareMode;
8748 DWORD accessMode;
8749 LPWSTR temp_name = NULL;
8750
8751 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8752 debugstr_w(pwcsName), pstgPriority, grfMode,
8753 snbExclude, reserved, ppstgOpen);
8754
8755 if (pstgPriority)
8756 {
8757 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8758 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8759 if (FAILED(hr)) goto end;
8760 pwcsName = temp_name;
8761 TRACE("using filename %s\n", debugstr_w(temp_name));
8762 }
8763
8764 if (pwcsName == 0)
8765 {
8766 hr = STG_E_INVALIDNAME;
8767 goto end;
8768 }
8769
8770 if (ppstgOpen == 0)
8771 {
8772 hr = STG_E_INVALIDPOINTER;
8773 goto end;
8774 }
8775
8776 if (reserved)
8777 {
8778 hr = STG_E_INVALIDPARAMETER;
8779 goto end;
8780 }
8781
8782 if (grfMode & STGM_PRIORITY)
8783 {
8784 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8785 return STG_E_INVALIDFLAG;
8786 if (grfMode & STGM_DELETEONRELEASE)
8787 return STG_E_INVALIDFUNCTION;
8788 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8789 return STG_E_INVALIDFLAG;
8790 grfMode &= ~0xf0; /* remove the existing sharing mode */
8791 grfMode |= STGM_SHARE_DENY_NONE;
8792 }
8793
8794 /*
8795 * Validate the sharing mode
8796 */
8797 if (grfMode & STGM_DIRECT_SWMR)
8798 {
8799 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8800 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8801 {
8802 hr = STG_E_INVALIDFLAG;
8803 goto end;
8804 }
8805 }
8806 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8807 switch(STGM_SHARE_MODE(grfMode))
8808 {
8809 case STGM_SHARE_EXCLUSIVE:
8810 case STGM_SHARE_DENY_WRITE:
8811 break;
8812 default:
8813 hr = STG_E_INVALIDFLAG;
8814 goto end;
8815 }
8816
8817 if ( FAILED( validateSTGM(grfMode) ) ||
8818 (grfMode&STGM_CREATE))
8819 {
8820 hr = STG_E_INVALIDFLAG;
8821 goto end;
8822 }
8823
8824 /* shared reading requires transacted or single writer mode */
8825 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8826 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8827 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8828 {
8829 hr = STG_E_INVALIDFLAG;
8830 goto end;
8831 }
8832
8833 /*
8834 * Interpret the STGM value grfMode
8835 */
8836 shareMode = GetShareModeFromSTGM(grfMode);
8837 accessMode = GetAccessModeFromSTGM(grfMode);
8838
8839 *ppstgOpen = 0;
8840
8841 hFile = CreateFileW( pwcsName,
8842 accessMode,
8843 shareMode,
8844 NULL,
8845 OPEN_EXISTING,
8846 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8847 0);
8848
8849 if (hFile==INVALID_HANDLE_VALUE)
8850 {
8851 DWORD last_error = GetLastError();
8852
8853 hr = E_FAIL;
8854
8855 switch (last_error)
8856 {
8857 case ERROR_FILE_NOT_FOUND:
8858 hr = STG_E_FILENOTFOUND;
8859 break;
8860
8861 case ERROR_PATH_NOT_FOUND:
8862 hr = STG_E_PATHNOTFOUND;
8863 break;
8864
8865 case ERROR_ACCESS_DENIED:
8866 case ERROR_WRITE_PROTECT:
8867 hr = STG_E_ACCESSDENIED;
8868 break;
8869
8870 case ERROR_SHARING_VIOLATION:
8871 hr = STG_E_SHAREVIOLATION;
8872 break;
8873
8874 default:
8875 hr = E_FAIL;
8876 }
8877
8878 goto end;
8879 }
8880
8881 /*
8882 * Refuse to open the file if it's too small to be a structured storage file
8883 * FIXME: verify the file when reading instead of here
8884 */
8885 if (GetFileSize(hFile, NULL) < 0x100)
8886 {
8887 CloseHandle(hFile);
8888 hr = STG_E_FILEALREADYEXISTS;
8889 goto end;
8890 }
8891
8892 /*
8893 * Allocate and initialize the new IStorage object.
8894 */
8895 hr = Storage_Construct(
8896 hFile,
8897 pwcsName,
8898 NULL,
8899 grfMode,
8900 TRUE,
8901 FALSE,
8902 512,
8903 &newStorage);
8904
8905 if (FAILED(hr))
8906 {
8907 /*
8908 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8909 */
8910 if(hr == STG_E_INVALIDHEADER)
8911 hr = STG_E_FILEALREADYEXISTS;
8912 goto end;
8913 }
8914
8915 *ppstgOpen = &newStorage->IStorage_iface;
8916
8917 end:
8918 CoTaskMemFree(temp_name);
8919 if (pstgPriority) IStorage_Release(pstgPriority);
8920 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8921 return hr;
8922 }
8923
8924 /******************************************************************************
8925 * StgCreateDocfileOnILockBytes [OLE32.@]
8926 */
8927 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8928 ILockBytes *plkbyt,
8929 DWORD grfMode,
8930 DWORD reserved,
8931 IStorage** ppstgOpen)
8932 {
8933 StorageBaseImpl* newStorage = 0;
8934 HRESULT hr = S_OK;
8935
8936 if ((ppstgOpen == 0) || (plkbyt == 0))
8937 return STG_E_INVALIDPOINTER;
8938
8939 /*
8940 * Allocate and initialize the new IStorage object.
8941 */
8942 hr = Storage_Construct(
8943 0,
8944 0,
8945 plkbyt,
8946 grfMode,
8947 FALSE,
8948 TRUE,
8949 512,
8950 &newStorage);
8951
8952 if (FAILED(hr))
8953 {
8954 return hr;
8955 }
8956
8957 *ppstgOpen = &newStorage->IStorage_iface;
8958
8959 return hr;
8960 }
8961
8962 /******************************************************************************
8963 * StgOpenStorageOnILockBytes [OLE32.@]
8964 */
8965 HRESULT WINAPI StgOpenStorageOnILockBytes(
8966 ILockBytes *plkbyt,
8967 IStorage *pstgPriority,
8968 DWORD grfMode,
8969 SNB snbExclude,
8970 DWORD reserved,
8971 IStorage **ppstgOpen)
8972 {
8973 StorageBaseImpl* newStorage = 0;
8974 HRESULT hr = S_OK;
8975
8976 if ((plkbyt == 0) || (ppstgOpen == 0))
8977 return STG_E_INVALIDPOINTER;
8978
8979 if ( FAILED( validateSTGM(grfMode) ))
8980 return STG_E_INVALIDFLAG;
8981
8982 *ppstgOpen = 0;
8983
8984 /*
8985 * Allocate and initialize the new IStorage object.
8986 */
8987 hr = Storage_Construct(
8988 0,
8989 0,
8990 plkbyt,
8991 grfMode,
8992 FALSE,
8993 FALSE,
8994 512,
8995 &newStorage);
8996
8997 if (FAILED(hr))
8998 {
8999 return hr;
9000 }
9001
9002 *ppstgOpen = &newStorage->IStorage_iface;
9003
9004 return hr;
9005 }
9006
9007 /******************************************************************************
9008 * StgSetTimes [ole32.@]
9009 * StgSetTimes [OLE32.@]
9010 *
9011 *
9012 */
9013 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
9014 FILETIME const *patime, FILETIME const *pmtime)
9015 {
9016 IStorage *stg = NULL;
9017 HRESULT r;
9018
9019 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
9020
9021 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
9022 0, 0, &stg);
9023 if( SUCCEEDED(r) )
9024 {
9025 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9026 IStorage_Release(stg);
9027 }
9028
9029 return r;
9030 }
9031
9032 /******************************************************************************
9033 * StgIsStorageILockBytes [OLE32.@]
9034 *
9035 * Determines if the ILockBytes contains a storage object.
9036 */
9037 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9038 {
9039 BYTE sig[sizeof(STORAGE_magic)];
9040 ULARGE_INTEGER offset;
9041 ULONG read = 0;
9042
9043 offset.u.HighPart = 0;
9044 offset.u.LowPart = 0;
9045
9046 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9047
9048 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9049 return S_OK;
9050
9051 return S_FALSE;
9052 }
9053
9054 /******************************************************************************
9055 * WriteClassStg [OLE32.@]
9056 *
9057 * This method will store the specified CLSID in the specified storage object
9058 */
9059 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9060 {
9061 if(!pStg)
9062 return E_INVALIDARG;
9063
9064 if(!rclsid)
9065 return STG_E_INVALIDPOINTER;
9066
9067 return IStorage_SetClass(pStg, rclsid);
9068 }
9069
9070 /***********************************************************************
9071 * ReadClassStg (OLE32.@)
9072 *
9073 * This method reads the CLSID previously written to a storage object with
9074 * the WriteClassStg.
9075 *
9076 * PARAMS
9077 * pstg [I] IStorage pointer
9078 * pclsid [O] Pointer to where the CLSID is written
9079 *
9080 * RETURNS
9081 * Success: S_OK.
9082 * Failure: HRESULT code.
9083 */
9084 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9085
9086 STATSTG pstatstg;
9087 HRESULT hRes;
9088
9089 TRACE("(%p, %p)\n", pstg, pclsid);
9090
9091 if(!pstg || !pclsid)
9092 return E_INVALIDARG;
9093
9094 /*
9095 * read a STATSTG structure (contains the clsid) from the storage
9096 */
9097 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9098
9099 if(SUCCEEDED(hRes))
9100 *pclsid=pstatstg.clsid;
9101
9102 return hRes;
9103 }
9104
9105 /***********************************************************************
9106 * OleLoadFromStream (OLE32.@)
9107 *
9108 * This function loads an object from stream
9109 */
9110 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9111 {
9112 CLSID clsid;
9113 HRESULT res;
9114 LPPERSISTSTREAM xstm;
9115
9116 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9117
9118 res=ReadClassStm(pStm,&clsid);
9119 if (FAILED(res))
9120 return res;
9121 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9122 if (FAILED(res))
9123 return res;
9124 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9125 if (FAILED(res)) {
9126 IUnknown_Release((IUnknown*)*ppvObj);
9127 return res;
9128 }
9129 res=IPersistStream_Load(xstm,pStm);
9130 IPersistStream_Release(xstm);
9131 /* FIXME: all refcounts ok at this point? I think they should be:
9132 * pStm : unchanged
9133 * ppvObj : 1
9134 * xstm : 0 (released)
9135 */
9136 return res;
9137 }
9138
9139 /***********************************************************************
9140 * OleSaveToStream (OLE32.@)
9141 *
9142 * This function saves an object with the IPersistStream interface on it
9143 * to the specified stream.
9144 */
9145 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9146 {
9147
9148 CLSID clsid;
9149 HRESULT res;
9150
9151 TRACE("(%p,%p)\n",pPStm,pStm);
9152
9153 res=IPersistStream_GetClassID(pPStm,&clsid);
9154
9155 if (SUCCEEDED(res)){
9156
9157 res=WriteClassStm(pStm,&clsid);
9158
9159 if (SUCCEEDED(res))
9160
9161 res=IPersistStream_Save(pPStm,pStm,TRUE);
9162 }
9163
9164 TRACE("Finished Save\n");
9165 return res;
9166 }
9167
9168 /*************************************************************************
9169 * STORAGE_CreateOleStream [Internal]
9170 *
9171 * Creates the "\001OLE" stream in the IStorage if necessary.
9172 *
9173 * PARAMS
9174 * storage [I] Dest storage to create the stream in
9175 * flags [I] flags to be set for newly created stream
9176 *
9177 * RETURNS
9178 * HRESULT return value
9179 *
9180 * NOTES
9181 *
9182 * This stream is still unknown, MS Word seems to have extra data
9183 * but since the data is stored in the OLESTREAM there should be
9184 * no need to recreate the stream. If the stream is manually
9185 * deleted it will create it with this default data.
9186 *
9187 */
9188 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9189 {
9190 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9191 static const DWORD version_magic = 0x02000001;
9192 IStream *stream;
9193 HRESULT hr;
9194
9195 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9196 if (hr == S_OK)
9197 {
9198 struct empty_1ole_stream {
9199 DWORD version_magic;
9200 DWORD flags;
9201 DWORD update_options;
9202 DWORD reserved;
9203 DWORD mon_stream_size;
9204 };
9205 struct empty_1ole_stream stream_data;
9206
9207 stream_data.version_magic = version_magic;
9208 stream_data.flags = flags;
9209 stream_data.update_options = 0;
9210 stream_data.reserved = 0;
9211 stream_data.mon_stream_size = 0;
9212
9213 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9214 IStream_Release(stream);
9215 }
9216
9217 return hr;
9218 }
9219
9220 /* write a string to a stream, preceded by its length */
9221 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9222 {
9223 HRESULT r;
9224 LPSTR str;
9225 DWORD len = 0;
9226
9227 if( string )
9228 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9229 r = IStream_Write( stm, &len, sizeof(len), NULL);
9230 if( FAILED( r ) )
9231 return r;
9232 if(len == 0)
9233 return r;
9234 str = CoTaskMemAlloc( len );
9235 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9236 r = IStream_Write( stm, str, len, NULL);
9237 CoTaskMemFree( str );
9238 return r;
9239 }
9240
9241 /* read a string preceded by its length from a stream */
9242 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9243 {
9244 HRESULT r;
9245 DWORD len, count = 0;
9246 LPSTR str;
9247 LPWSTR wstr;
9248
9249 r = IStream_Read( stm, &len, sizeof(len), &count );
9250 if( FAILED( r ) )
9251 return r;
9252 if( count != sizeof(len) )
9253 return E_OUTOFMEMORY;
9254
9255 TRACE("%d bytes\n",len);
9256
9257 str = CoTaskMemAlloc( len );
9258 if( !str )
9259 return E_OUTOFMEMORY;
9260 count = 0;
9261 r = IStream_Read( stm, str, len, &count );
9262 if( FAILED( r ) )
9263 {
9264 CoTaskMemFree( str );
9265 return r;
9266 }
9267 if( count != len )
9268 {
9269 CoTaskMemFree( str );
9270 return E_OUTOFMEMORY;
9271 }
9272
9273 TRACE("Read string %s\n",debugstr_an(str,len));
9274
9275 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9276 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9277 if( wstr )
9278 {
9279 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9280 wstr[len] = 0;
9281 }
9282 CoTaskMemFree( str );
9283
9284 *string = wstr;
9285
9286 return r;
9287 }
9288
9289
9290 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9291 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9292 {
9293 IStream *pstm;
9294 HRESULT r = S_OK;
9295 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9296
9297 static const BYTE unknown1[12] =
9298 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9299 0xFF, 0xFF, 0xFF, 0xFF};
9300 static const BYTE unknown2[16] =
9301 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9303
9304 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9305 debugstr_w(lpszUserType), debugstr_w(szClipName),
9306 debugstr_w(szProgIDName));
9307
9308 /* Create a CompObj stream */
9309 r = IStorage_CreateStream(pstg, szwStreamName,
9310 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9311 if( FAILED (r) )
9312 return r;
9313
9314 /* Write CompObj Structure to stream */
9315 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9316
9317 if( SUCCEEDED( r ) )
9318 r = WriteClassStm( pstm, clsid );
9319
9320 if( SUCCEEDED( r ) )
9321 r = STREAM_WriteString( pstm, lpszUserType );
9322 if( SUCCEEDED( r ) )
9323 r = STREAM_WriteString( pstm, szClipName );
9324 if( SUCCEEDED( r ) )
9325 r = STREAM_WriteString( pstm, szProgIDName );
9326 if( SUCCEEDED( r ) )
9327 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9328
9329 IStream_Release( pstm );
9330
9331 return r;
9332 }
9333
9334 /***********************************************************************
9335 * WriteFmtUserTypeStg (OLE32.@)
9336 */
9337 HRESULT WINAPI WriteFmtUserTypeStg(
9338 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9339 {
9340 STATSTG stat;
9341 HRESULT r;
9342 WCHAR szwClipName[0x40];
9343 CLSID clsid;
9344 LPWSTR wstrProgID = NULL;
9345 DWORD n;
9346
9347 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9348
9349 /* get the clipboard format name */
9350 if( cf )
9351 {
9352 n = GetClipboardFormatNameW( cf, szwClipName,
9353 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9354 szwClipName[n]=0;
9355 }
9356
9357 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9358
9359 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9360 if(SUCCEEDED(r))
9361 clsid = stat.clsid;
9362 else
9363 clsid = CLSID_NULL;
9364
9365 ProgIDFromCLSID(&clsid, &wstrProgID);
9366
9367 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9368
9369 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9370 cf ? szwClipName : NULL, wstrProgID );
9371
9372 CoTaskMemFree(wstrProgID);
9373
9374 return r;
9375 }
9376
9377
9378 /******************************************************************************
9379 * ReadFmtUserTypeStg [OLE32.@]
9380 */
9381 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9382 {
9383 HRESULT r;
9384 IStream *stm = 0;
9385 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9386 unsigned char unknown1[12];
9387 unsigned char unknown2[16];
9388 DWORD count;
9389 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9390 CLSID clsid;
9391
9392 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9393
9394 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9395 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9396 if( FAILED ( r ) )
9397 {
9398 WARN("Failed to open stream r = %08x\n", r);
9399 return r;
9400 }
9401
9402 /* read the various parts of the structure */
9403 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9404 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9405 goto end;
9406 r = ReadClassStm( stm, &clsid );
9407 if( FAILED( r ) )
9408 goto end;
9409
9410 r = STREAM_ReadString( stm, &szCLSIDName );
9411 if( FAILED( r ) )
9412 goto end;
9413
9414 r = STREAM_ReadString( stm, &szOleTypeName );
9415 if( FAILED( r ) )
9416 goto end;
9417
9418 r = STREAM_ReadString( stm, &szProgIDName );
9419 if( FAILED( r ) )
9420 goto end;
9421
9422 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9423 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9424 goto end;
9425
9426 /* ok, success... now we just need to store what we found */
9427 if( pcf )
9428 *pcf = RegisterClipboardFormatW( szOleTypeName );
9429
9430 if( lplpszUserType )
9431 {
9432 *lplpszUserType = szCLSIDName;
9433 szCLSIDName = NULL;
9434 }
9435
9436 end:
9437 CoTaskMemFree( szCLSIDName );
9438 CoTaskMemFree( szOleTypeName );
9439 CoTaskMemFree( szProgIDName );
9440 IStream_Release( stm );
9441
9442 return r;
9443 }
9444
9445 /******************************************************************************
9446 * StgIsStorageFile [OLE32.@]
9447 * Verify if the file contains a storage object
9448 *
9449 * PARAMS
9450 * fn [ I] Filename
9451 *
9452 * RETURNS
9453 * S_OK if file has magic bytes as a storage object
9454 * S_FALSE if file is not storage
9455 */
9456 HRESULT WINAPI
9457 StgIsStorageFile(LPCOLESTR fn)
9458 {
9459 HANDLE hf;
9460 BYTE magic[8];
9461 DWORD bytes_read;
9462
9463 TRACE("%s\n", debugstr_w(fn));
9464 hf = CreateFileW(fn, GENERIC_READ,
9465 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9466 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9467
9468 if (hf == INVALID_HANDLE_VALUE)
9469 return STG_E_FILENOTFOUND;
9470
9471 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9472 {
9473 WARN(" unable to read file\n");
9474 CloseHandle(hf);
9475 return S_FALSE;
9476 }
9477
9478 CloseHandle(hf);
9479
9480 if (bytes_read != 8) {
9481 TRACE(" too short\n");
9482 return S_FALSE;
9483 }
9484
9485 if (!memcmp(magic,STORAGE_magic,8)) {
9486 TRACE(" -> YES\n");
9487 return S_OK;
9488 }
9489
9490 TRACE(" -> Invalid header.\n");
9491 return S_FALSE;
9492 }
9493
9494 /***********************************************************************
9495 * WriteClassStm (OLE32.@)
9496 *
9497 * Writes a CLSID to a stream.
9498 *
9499 * PARAMS
9500 * pStm [I] Stream to write to.
9501 * rclsid [I] CLSID to write.
9502 *
9503 * RETURNS
9504 * Success: S_OK.
9505 * Failure: HRESULT code.
9506 */
9507 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9508 {
9509 TRACE("(%p,%p)\n",pStm,rclsid);
9510
9511 if (!pStm || !rclsid)
9512 return E_INVALIDARG;
9513
9514 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9515 }
9516
9517 /***********************************************************************
9518 * ReadClassStm (OLE32.@)
9519 *
9520 * Reads a CLSID from a stream.
9521 *
9522 * PARAMS
9523 * pStm [I] Stream to read from.
9524 * rclsid [O] CLSID to read.
9525 *
9526 * RETURNS
9527 * Success: S_OK.
9528 * Failure: HRESULT code.
9529 */
9530 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9531 {
9532 ULONG nbByte;
9533 HRESULT res;
9534
9535 TRACE("(%p,%p)\n",pStm,pclsid);
9536
9537 if (!pStm || !pclsid)
9538 return E_INVALIDARG;
9539
9540 /* clear the output args */
9541 *pclsid = CLSID_NULL;
9542
9543 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9544
9545 if (FAILED(res))
9546 return res;
9547
9548 if (nbByte != sizeof(CLSID))
9549 return STG_E_READFAULT;
9550 else
9551 return S_OK;
9552 }
9553
9554
9555 /************************************************************************
9556 * OleConvert Functions
9557 ***********************************************************************/
9558
9559 #define OLESTREAM_ID 0x501
9560 #define OLESTREAM_MAX_STR_LEN 255
9561
9562 /* OLESTREAM memory structure to use for Get and Put Routines */
9563 typedef struct
9564 {
9565 DWORD dwOleID;
9566 DWORD dwTypeID;
9567 DWORD dwOleTypeNameLength;
9568 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9569 CHAR *pstrOleObjFileName;
9570 DWORD dwOleObjFileNameLength;
9571 DWORD dwMetaFileWidth;
9572 DWORD dwMetaFileHeight;
9573 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9574 DWORD dwDataLength;
9575 BYTE *pData;
9576 } OLECONVERT_OLESTREAM_DATA;
9577
9578 /* CompObj Stream structure */
9579 typedef struct
9580 {
9581 BYTE byUnknown1[12];
9582 CLSID clsid;
9583 DWORD dwCLSIDNameLength;
9584 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9585 DWORD dwOleTypeNameLength;
9586 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9587 DWORD dwProgIDNameLength;
9588 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9589 BYTE byUnknown2[16];
9590 } OLECONVERT_ISTORAGE_COMPOBJ;
9591
9592 /* Ole Presentation Stream structure */
9593 typedef struct
9594 {
9595 BYTE byUnknown1[28];
9596 DWORD dwExtentX;
9597 DWORD dwExtentY;
9598 DWORD dwSize;
9599 BYTE *pData;
9600 } OLECONVERT_ISTORAGE_OLEPRES;
9601
9602
9603 /*************************************************************************
9604 * OLECONVERT_LoadOLE10 [Internal]
9605 *
9606 * Loads the OLE10 STREAM to memory
9607 *
9608 * PARAMS
9609 * pOleStream [I] The OLESTREAM
9610 * pData [I] Data Structure for the OLESTREAM Data
9611 *
9612 * RETURNS
9613 * Success: S_OK
9614 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9615 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9616 *
9617 * NOTES
9618 * This function is used by OleConvertOLESTREAMToIStorage only.
9619 *
9620 * Memory allocated for pData must be freed by the caller
9621 */
9622 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9623 {
9624 DWORD dwSize;
9625 HRESULT hRes = S_OK;
9626 int nTryCnt=0;
9627 int max_try = 6;
9628
9629 pData->pData = NULL;
9630 pData->pstrOleObjFileName = NULL;
9631
9632 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9633 {
9634 /* Get the OleID */
9635 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9636 if(dwSize != sizeof(pData->dwOleID))
9637 {
9638 hRes = CONVERT10_E_OLESTREAM_GET;
9639 }
9640 else if(pData->dwOleID != OLESTREAM_ID)
9641 {
9642 hRes = CONVERT10_E_OLESTREAM_FMT;
9643 }
9644 else
9645 {
9646 hRes = S_OK;
9647 break;
9648 }
9649 }
9650
9651 if(hRes == S_OK)
9652 {
9653 /* Get the TypeID... more info needed for this field */
9654 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9655 if(dwSize != sizeof(pData->dwTypeID))
9656 {
9657 hRes = CONVERT10_E_OLESTREAM_GET;
9658 }
9659 }
9660 if(hRes == S_OK)
9661 {
9662 if(pData->dwTypeID != 0)
9663 {
9664 /* Get the length of the OleTypeName */
9665 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9666 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9667 {
9668 hRes = CONVERT10_E_OLESTREAM_GET;
9669 }
9670
9671 if(hRes == S_OK)
9672 {
9673 if(pData->dwOleTypeNameLength > 0)
9674 {
9675 /* Get the OleTypeName */
9676 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9677 if(dwSize != pData->dwOleTypeNameLength)
9678 {
9679 hRes = CONVERT10_E_OLESTREAM_GET;
9680 }
9681 }
9682 }
9683 if(bStrem1)
9684 {
9685 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9686 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9687 {
9688 hRes = CONVERT10_E_OLESTREAM_GET;
9689 }
9690 if(hRes == S_OK)
9691 {
9692 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9693 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9694 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9695 if(pData->pstrOleObjFileName)
9696 {
9697 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9698 if(dwSize != pData->dwOleObjFileNameLength)
9699 {
9700 hRes = CONVERT10_E_OLESTREAM_GET;
9701 }
9702 }
9703 else
9704 hRes = CONVERT10_E_OLESTREAM_GET;
9705 }
9706 }
9707 else
9708 {
9709 /* Get the Width of the Metafile */
9710 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9711 if(dwSize != sizeof(pData->dwMetaFileWidth))
9712 {
9713 hRes = CONVERT10_E_OLESTREAM_GET;
9714 }
9715 if(hRes == S_OK)
9716 {
9717 /* Get the Height of the Metafile */
9718 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9719 if(dwSize != sizeof(pData->dwMetaFileHeight))
9720 {
9721 hRes = CONVERT10_E_OLESTREAM_GET;
9722 }
9723 }
9724 }
9725 if(hRes == S_OK)
9726 {
9727 /* Get the Length of the Data */
9728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9729 if(dwSize != sizeof(pData->dwDataLength))
9730 {
9731 hRes = CONVERT10_E_OLESTREAM_GET;
9732 }
9733 }
9734
9735 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9736 {
9737 if(!bStrem1) /* if it is a second OLE stream data */
9738 {
9739 pData->dwDataLength -= 8;
9740 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9741 if(dwSize != sizeof(pData->strUnknown))
9742 {
9743 hRes = CONVERT10_E_OLESTREAM_GET;
9744 }
9745 }
9746 }
9747 if(hRes == S_OK)
9748 {
9749 if(pData->dwDataLength > 0)
9750 {
9751 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9752
9753 /* Get Data (ex. IStorage, Metafile, or BMP) */
9754 if(pData->pData)
9755 {
9756 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9757 if(dwSize != pData->dwDataLength)
9758 {
9759 hRes = CONVERT10_E_OLESTREAM_GET;
9760 }
9761 }
9762 else
9763 {
9764 hRes = CONVERT10_E_OLESTREAM_GET;
9765 }
9766 }
9767 }
9768 }
9769 }
9770 return hRes;
9771 }
9772
9773 /*************************************************************************
9774 * OLECONVERT_SaveOLE10 [Internal]
9775 *
9776 * Saves the OLE10 STREAM From memory
9777 *
9778 * PARAMS
9779 * pData [I] Data Structure for the OLESTREAM Data
9780 * pOleStream [I] The OLESTREAM to save
9781 *
9782 * RETURNS
9783 * Success: S_OK
9784 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9785 *
9786 * NOTES
9787 * This function is used by OleConvertIStorageToOLESTREAM only.
9788 *
9789 */
9790 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9791 {
9792 DWORD dwSize;
9793 HRESULT hRes = S_OK;
9794
9795
9796 /* Set the OleID */
9797 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9798 if(dwSize != sizeof(pData->dwOleID))
9799 {
9800 hRes = CONVERT10_E_OLESTREAM_PUT;
9801 }
9802
9803 if(hRes == S_OK)
9804 {
9805 /* Set the TypeID */
9806 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9807 if(dwSize != sizeof(pData->dwTypeID))
9808 {
9809 hRes = CONVERT10_E_OLESTREAM_PUT;
9810 }
9811 }
9812
9813 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9814 {
9815 /* Set the Length of the OleTypeName */
9816 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9817 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9818 {
9819 hRes = CONVERT10_E_OLESTREAM_PUT;
9820 }
9821
9822 if(hRes == S_OK)
9823 {
9824 if(pData->dwOleTypeNameLength > 0)
9825 {
9826 /* Set the OleTypeName */
9827 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9828 if(dwSize != pData->dwOleTypeNameLength)
9829 {
9830 hRes = CONVERT10_E_OLESTREAM_PUT;
9831 }
9832 }
9833 }
9834
9835 if(hRes == S_OK)
9836 {
9837 /* Set the width of the Metafile */
9838 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9839 if(dwSize != sizeof(pData->dwMetaFileWidth))
9840 {
9841 hRes = CONVERT10_E_OLESTREAM_PUT;
9842 }
9843 }
9844
9845 if(hRes == S_OK)
9846 {
9847 /* Set the height of the Metafile */
9848 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9849 if(dwSize != sizeof(pData->dwMetaFileHeight))
9850 {
9851 hRes = CONVERT10_E_OLESTREAM_PUT;
9852 }
9853 }
9854
9855 if(hRes == S_OK)
9856 {
9857 /* Set the length of the Data */
9858 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9859 if(dwSize != sizeof(pData->dwDataLength))
9860 {
9861 hRes = CONVERT10_E_OLESTREAM_PUT;
9862 }
9863 }
9864
9865 if(hRes == S_OK)
9866 {
9867 if(pData->dwDataLength > 0)
9868 {
9869 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9870 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9871 if(dwSize != pData->dwDataLength)
9872 {
9873 hRes = CONVERT10_E_OLESTREAM_PUT;
9874 }
9875 }
9876 }
9877 }
9878 return hRes;
9879 }
9880
9881 /*************************************************************************
9882 * OLECONVERT_GetOLE20FromOLE10[Internal]
9883 *
9884 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9885 * opens it, and copies the content to the dest IStorage for
9886 * OleConvertOLESTREAMToIStorage
9887 *
9888 *
9889 * PARAMS
9890 * pDestStorage [I] The IStorage to copy the data to
9891 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9892 * nBufferLength [I] The size of the buffer
9893 *
9894 * RETURNS
9895 * Nothing
9896 *
9897 * NOTES
9898 *
9899 *
9900 */
9901 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9902 {
9903 HRESULT hRes;
9904 HANDLE hFile;
9905 IStorage *pTempStorage;
9906 DWORD dwNumOfBytesWritten;
9907 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9908 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9909
9910 /* Create a temp File */
9911 GetTempPathW(MAX_PATH, wstrTempDir);
9912 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9913 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9914
9915 if(hFile != INVALID_HANDLE_VALUE)
9916 {
9917 /* Write IStorage Data to File */
9918 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9919 CloseHandle(hFile);
9920
9921 /* Open and copy temp storage to the Dest Storage */
9922 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9923 if(hRes == S_OK)
9924 {
9925 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9926 IStorage_Release(pTempStorage);
9927 }
9928 DeleteFileW(wstrTempFile);
9929 }
9930 }
9931
9932
9933 /*************************************************************************
9934 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9935 *
9936 * Saves the OLE10 STREAM From memory
9937 *
9938 * PARAMS
9939 * pStorage [I] The Src IStorage to copy
9940 * pData [I] The Dest Memory to write to.
9941 *
9942 * RETURNS
9943 * The size in bytes allocated for pData
9944 *
9945 * NOTES
9946 * Memory allocated for pData must be freed by the caller
9947 *
9948 * Used by OleConvertIStorageToOLESTREAM only.
9949 *
9950 */
9951 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9952 {
9953 HANDLE hFile;
9954 HRESULT hRes;
9955 DWORD nDataLength = 0;
9956 IStorage *pTempStorage;
9957 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9958 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9959
9960 *pData = NULL;
9961
9962 /* Create temp Storage */
9963 GetTempPathW(MAX_PATH, wstrTempDir);
9964 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9965 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9966
9967 if(hRes == S_OK)
9968 {
9969 /* Copy Src Storage to the Temp Storage */
9970 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9971 IStorage_Release(pTempStorage);
9972
9973 /* Open Temp Storage as a file and copy to memory */
9974 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9975 if(hFile != INVALID_HANDLE_VALUE)
9976 {
9977 nDataLength = GetFileSize(hFile, NULL);
9978 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9979 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9980 CloseHandle(hFile);
9981 }
9982 DeleteFileW(wstrTempFile);
9983 }
9984 return nDataLength;
9985 }
9986
9987 /*************************************************************************
9988 * OLECONVERT_CreateCompObjStream [Internal]
9989 *
9990 * Creates a "\001CompObj" is the destination IStorage if necessary.
9991 *
9992 * PARAMS
9993 * pStorage [I] The dest IStorage to create the CompObj Stream
9994 * if necessary.
9995 * strOleTypeName [I] The ProgID
9996 *
9997 * RETURNS
9998 * Success: S_OK
9999 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10000 *
10001 * NOTES
10002 * This function is used by OleConvertOLESTREAMToIStorage only.
10003 *
10004 * The stream data is stored in the OLESTREAM and there should be
10005 * no need to recreate the stream. If the stream is manually
10006 * deleted it will attempt to create it by querying the registry.
10007 *
10008 *
10009 */
10010 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
10011 {
10012 IStream *pStream;
10013 HRESULT hStorageRes, hRes = S_OK;
10014 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
10015 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10016 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
10017
10018 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10019 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
10020
10021 /* Initialize the CompObj structure */
10022 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
10023 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
10024 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
10025
10026
10027 /* Create a CompObj stream if it doesn't exist */
10028 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
10029 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10030 if(hStorageRes == S_OK)
10031 {
10032 /* copy the OleTypeName to the compobj struct */
10033 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10034 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10035
10036 /* copy the OleTypeName to the compobj struct */
10037 /* Note: in the test made, these were Identical */
10038 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10039 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10040
10041 /* Get the CLSID */
10042 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10043 bufferW, OLESTREAM_MAX_STR_LEN );
10044 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10045
10046 if(hRes == S_OK)
10047 {
10048 HKEY hKey;
10049 LONG hErr;
10050 /* Get the CLSID Default Name from the Registry */
10051 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10052 if(hErr == ERROR_SUCCESS)
10053 {
10054 char strTemp[OLESTREAM_MAX_STR_LEN];
10055 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10056 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10057 if(hErr == ERROR_SUCCESS)
10058 {
10059 strcpy(IStorageCompObj.strCLSIDName, strTemp);
10060 }
10061 RegCloseKey(hKey);
10062 }
10063 }
10064
10065 /* Write CompObj Structure to stream */
10066 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10067
10068 WriteClassStm(pStream,&(IStorageCompObj.clsid));
10069
10070 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10071 if(IStorageCompObj.dwCLSIDNameLength > 0)
10072 {
10073 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10074 }
10075 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10076 if(IStorageCompObj.dwOleTypeNameLength > 0)
10077 {
10078 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10079 }
10080 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10081 if(IStorageCompObj.dwProgIDNameLength > 0)
10082 {
10083 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10084 }
10085 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10086 IStream_Release(pStream);
10087 }
10088 return hRes;
10089 }
10090
10091
10092 /*************************************************************************
10093 * OLECONVERT_CreateOlePresStream[Internal]
10094 *
10095 * Creates the "\002OlePres000" Stream with the Metafile data
10096 *
10097 * PARAMS
10098 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
10099 * dwExtentX [I] Width of the Metafile
10100 * dwExtentY [I] Height of the Metafile
10101 * pData [I] Metafile data
10102 * dwDataLength [I] Size of the Metafile data
10103 *
10104 * RETURNS
10105 * Success: S_OK
10106 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
10107 *
10108 * NOTES
10109 * This function is used by OleConvertOLESTREAMToIStorage only.
10110 *
10111 */
10112 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10113 {
10114 HRESULT hRes;
10115 IStream *pStream;
10116 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10117 BYTE pOlePresStreamHeader [] =
10118 {
10119 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10120 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10121 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10122 0x00, 0x00, 0x00, 0x00
10123 };
10124
10125 BYTE pOlePresStreamHeaderEmpty [] =
10126 {
10127 0x00, 0x00, 0x00, 0x00,
10128 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10129 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10130 0x00, 0x00, 0x00, 0x00
10131 };
10132
10133 /* Create the OlePres000 Stream */
10134 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10135 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10136
10137 if(hRes == S_OK)
10138 {
10139 DWORD nHeaderSize;
10140 OLECONVERT_ISTORAGE_OLEPRES OlePres;
10141
10142 memset(&OlePres, 0, sizeof(OlePres));
10143 /* Do we have any metafile data to save */
10144 if(dwDataLength > 0)
10145 {
10146 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10147 nHeaderSize = sizeof(pOlePresStreamHeader);
10148 }
10149 else
10150 {
10151 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10152 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10153 }
10154 /* Set width and height of the metafile */
10155 OlePres.dwExtentX = dwExtentX;
10156 OlePres.dwExtentY = -dwExtentY;
10157
10158 /* Set Data and Length */
10159 if(dwDataLength > sizeof(METAFILEPICT16))
10160 {
10161 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10162 OlePres.pData = &(pData[8]);
10163 }
10164 /* Save OlePres000 Data to Stream */
10165 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10166 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10167 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10168 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10169 if(OlePres.dwSize > 0)
10170 {
10171 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10172 }
10173 IStream_Release(pStream);
10174 }
10175 }
10176
10177 /*************************************************************************
10178 * OLECONVERT_CreateOle10NativeStream [Internal]
10179 *
10180 * Creates the "\001Ole10Native" Stream (should contain a BMP)
10181 *
10182 * PARAMS
10183 * pStorage [I] Dest storage to create the stream in
10184 * pData [I] Ole10 Native Data (ex. bmp)
10185 * dwDataLength [I] Size of the Ole10 Native Data
10186 *
10187 * RETURNS
10188 * Nothing
10189 *
10190 * NOTES
10191 * This function is used by OleConvertOLESTREAMToIStorage only.
10192 *
10193 * Might need to verify the data and return appropriate error message
10194 *
10195 */
10196 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10197 {
10198 HRESULT hRes;
10199 IStream *pStream;
10200 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10201
10202 /* Create the Ole10Native Stream */
10203 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10204 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10205
10206 if(hRes == S_OK)
10207 {
10208 /* Write info to stream */
10209 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10210 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10211 IStream_Release(pStream);
10212 }
10213
10214 }
10215
10216 /*************************************************************************
10217 * OLECONVERT_GetOLE10ProgID [Internal]
10218 *
10219 * Finds the ProgID (or OleTypeID) from the IStorage
10220 *
10221 * PARAMS
10222 * pStorage [I] The Src IStorage to get the ProgID
10223 * strProgID [I] the ProgID string to get
10224 * dwSize [I] the size of the string
10225 *
10226 * RETURNS
10227 * Success: S_OK
10228 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10229 *
10230 * NOTES
10231 * This function is used by OleConvertIStorageToOLESTREAM only.
10232 *
10233 *
10234 */
10235 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10236 {
10237 HRESULT hRes;
10238 IStream *pStream;
10239 LARGE_INTEGER iSeekPos;
10240 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10241 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10242
10243 /* Open the CompObj Stream */
10244 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10245 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10246 if(hRes == S_OK)
10247 {
10248
10249 /*Get the OleType from the CompObj Stream */
10250 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10251 iSeekPos.u.HighPart = 0;
10252
10253 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10254 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10255 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10256 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10257 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10258 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10259 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10260
10261 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10262 if(*dwSize > 0)
10263 {
10264 IStream_Read(pStream, strProgID, *dwSize, NULL);
10265 }
10266 IStream_Release(pStream);
10267 }
10268 else
10269 {
10270 STATSTG stat;
10271 LPOLESTR wstrProgID;
10272
10273 /* Get the OleType from the registry */
10274 REFCLSID clsid = &(stat.clsid);
10275 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10276 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10277 if(hRes == S_OK)
10278 {
10279 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10280 CoTaskMemFree(wstrProgID);
10281 }
10282
10283 }
10284 return hRes;
10285 }
10286
10287 /*************************************************************************
10288 * OLECONVERT_GetOle10PresData [Internal]
10289 *
10290 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10291 *
10292 * PARAMS
10293 * pStorage [I] Src IStroage
10294 * pOleStream [I] Dest OleStream Mem Struct
10295 *
10296 * RETURNS
10297 * Nothing
10298 *
10299 * NOTES
10300 * This function is used by OleConvertIStorageToOLESTREAM only.
10301 *
10302 * Memory allocated for pData must be freed by the caller
10303 *
10304 *
10305 */
10306 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10307 {
10308
10309 HRESULT hRes;
10310 IStream *pStream;
10311 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10312
10313 /* Initialize Default data for OLESTREAM */
10314 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10315 pOleStreamData[0].dwTypeID = 2;
10316 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10317 pOleStreamData[1].dwTypeID = 0;
10318 pOleStreamData[0].dwMetaFileWidth = 0;
10319 pOleStreamData[0].dwMetaFileHeight = 0;
10320 pOleStreamData[0].pData = NULL;
10321 pOleStreamData[1].pData = NULL;
10322
10323 /* Open Ole10Native Stream */
10324 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10325 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10326 if(hRes == S_OK)
10327 {
10328
10329 /* Read Size and Data */
10330 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10331 if(pOleStreamData->dwDataLength > 0)
10332 {
10333 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10334 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10335 }
10336 IStream_Release(pStream);
10337 }
10338
10339 }
10340
10341
10342 /*************************************************************************
10343 * OLECONVERT_GetOle20PresData[Internal]
10344 *
10345 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10346 *
10347 * PARAMS
10348 * pStorage [I] Src IStroage
10349 * pOleStreamData [I] Dest OleStream Mem Struct
10350 *
10351 * RETURNS
10352 * Nothing
10353 *
10354 * NOTES
10355 * This function is used by OleConvertIStorageToOLESTREAM only.
10356 *
10357 * Memory allocated for pData must be freed by the caller
10358 */
10359 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10360 {
10361 HRESULT hRes;
10362 IStream *pStream;
10363 OLECONVERT_ISTORAGE_OLEPRES olePress;
10364 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10365
10366 /* Initialize Default data for OLESTREAM */
10367 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10368 pOleStreamData[0].dwTypeID = 2;
10369 pOleStreamData[0].dwMetaFileWidth = 0;
10370 pOleStreamData[0].dwMetaFileHeight = 0;
10371 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10372 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10373 pOleStreamData[1].dwTypeID = 0;
10374 pOleStreamData[1].dwOleTypeNameLength = 0;
10375 pOleStreamData[1].strOleTypeName[0] = 0;
10376 pOleStreamData[1].dwMetaFileWidth = 0;
10377 pOleStreamData[1].dwMetaFileHeight = 0;
10378 pOleStreamData[1].pData = NULL;
10379 pOleStreamData[1].dwDataLength = 0;
10380
10381
10382 /* Open OlePress000 stream */
10383 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10384 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10385 if(hRes == S_OK)
10386 {
10387 LARGE_INTEGER iSeekPos;
10388 METAFILEPICT16 MetaFilePict;
10389 static const char strMetafilePictName[] = "METAFILEPICT";
10390
10391 /* Set the TypeID for a Metafile */
10392 pOleStreamData[1].dwTypeID = 5;
10393
10394 /* Set the OleTypeName to Metafile */
10395 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10396 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10397
10398 iSeekPos.u.HighPart = 0;
10399 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10400
10401 /* Get Presentation Data */
10402 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10403 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10404 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10405 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10406
10407 /*Set width and Height */
10408 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10409 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10410 if(olePress.dwSize > 0)
10411 {
10412 /* Set Length */
10413 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10414
10415 /* Set MetaFilePict struct */
10416 MetaFilePict.mm = 8;
10417 MetaFilePict.xExt = olePress.dwExtentX;
10418 MetaFilePict.yExt = olePress.dwExtentY;
10419 MetaFilePict.hMF = 0;
10420
10421 /* Get Metafile Data */
10422 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10423 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10424 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10425 }
10426 IStream_Release(pStream);
10427 }
10428 }
10429
10430 /*************************************************************************
10431 * OleConvertOLESTREAMToIStorage [OLE32.@]
10432 *
10433 * Read info on MSDN
10434 *
10435 * TODO
10436 * DVTARGETDEVICE parameter is not handled
10437 * Still unsure of some mem fields for OLE 10 Stream
10438 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10439 * and "\001OLE" streams
10440 *
10441 */
10442 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10443 LPOLESTREAM pOleStream,
10444 LPSTORAGE pstg,
10445 const DVTARGETDEVICE* ptd)
10446 {
10447 int i;
10448 HRESULT hRes=S_OK;
10449 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10450
10451 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10452
10453 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10454
10455 if(ptd != NULL)
10456 {
10457 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10458 }
10459
10460 if(pstg == NULL || pOleStream == NULL)
10461 {
10462 hRes = E_INVALIDARG;
10463 }
10464
10465 if(hRes == S_OK)
10466 {
10467 /* Load the OLESTREAM to Memory */
10468 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10469 }
10470
10471 if(hRes == S_OK)
10472 {
10473 /* Load the OLESTREAM to Memory (part 2)*/
10474 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10475 }
10476
10477 if(hRes == S_OK)
10478 {
10479
10480 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10481 {
10482 /* Do we have the IStorage Data in the OLESTREAM */
10483 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10484 {
10485 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10486 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10487 }
10488 else
10489 {
10490 /* It must be an original OLE 1.0 source */
10491 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10492 }
10493 }
10494 else
10495 {
10496 /* It must be an original OLE 1.0 source */
10497 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10498 }
10499
10500 /* Create CompObj Stream if necessary */
10501 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10502 if(hRes == S_OK)
10503 {
10504 /*Create the Ole Stream if necessary */
10505 STORAGE_CreateOleStream(pstg, 0);
10506 }
10507 }
10508
10509
10510 /* Free allocated memory */
10511 for(i=0; i < 2; i++)
10512 {
10513 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10514 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10515 pOleStreamData[i].pstrOleObjFileName = NULL;
10516 }
10517 return hRes;
10518 }
10519
10520 /*************************************************************************
10521 * OleConvertIStorageToOLESTREAM [OLE32.@]
10522 *
10523 * Read info on MSDN
10524 *
10525 * Read info on MSDN
10526 *
10527 * TODO
10528 * Still unsure of some mem fields for OLE 10 Stream
10529 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10530 * and "\001OLE" streams.
10531 *
10532 */
10533 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10534 LPSTORAGE pstg,
10535 LPOLESTREAM pOleStream)
10536 {
10537 int i;
10538 HRESULT hRes = S_OK;
10539 IStream *pStream;
10540 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10541 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10542
10543 TRACE("%p %p\n", pstg, pOleStream);
10544
10545 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10546
10547 if(pstg == NULL || pOleStream == NULL)
10548 {
10549 hRes = E_INVALIDARG;
10550 }
10551 if(hRes == S_OK)
10552 {
10553 /* Get the ProgID */
10554 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10555 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10556 }
10557 if(hRes == S_OK)
10558 {
10559 /* Was it originally Ole10 */
10560 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10561 if(hRes == S_OK)
10562 {
10563 IStream_Release(pStream);
10564 /* Get Presentation Data for Ole10Native */
10565 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10566 }
10567 else
10568 {
10569 /* Get Presentation Data (OLE20) */
10570 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10571 }
10572
10573 /* Save OLESTREAM */
10574 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10575 if(hRes == S_OK)
10576 {
10577 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10578 }
10579
10580 }
10581
10582 /* Free allocated memory */
10583 for(i=0; i < 2; i++)
10584 {
10585 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10586 }
10587
10588 return hRes;
10589 }
10590
10591 enum stream_1ole_flags {
10592 OleStream_LinkedObject = 0x00000001,
10593 OleStream_Convert = 0x00000004
10594 };
10595
10596 /***********************************************************************
10597 * GetConvertStg (OLE32.@)
10598 */
10599 HRESULT WINAPI GetConvertStg(IStorage *stg)
10600 {
10601 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10602 static const DWORD version_magic = 0x02000001;
10603 DWORD header[2];
10604 IStream *stream;
10605 HRESULT hr;
10606
10607 TRACE("%p\n", stg);
10608
10609 if (!stg) return E_INVALIDARG;
10610
10611 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10612 if (FAILED(hr)) return hr;
10613
10614 hr = IStream_Read(stream, header, sizeof(header), NULL);
10615 IStream_Release(stream);
10616 if (FAILED(hr)) return hr;
10617
10618 if (header[0] != version_magic)
10619 {
10620 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10621 return E_FAIL;
10622 }
10623
10624 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10625 }
10626
10627 /***********************************************************************
10628 * SetConvertStg (OLE32.@)
10629 */
10630 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10631 {
10632 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10633 DWORD flags = convert ? OleStream_Convert : 0;
10634 IStream *stream;
10635 DWORD header[2];
10636 HRESULT hr;
10637
10638 TRACE("(%p, %d)\n", storage, convert);
10639
10640 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10641 if (FAILED(hr))
10642 {
10643 if (hr != STG_E_FILENOTFOUND)
10644 return hr;
10645
10646 return STORAGE_CreateOleStream(storage, flags);
10647 }
10648
10649 hr = IStream_Read(stream, header, sizeof(header), NULL);
10650 if (FAILED(hr))
10651 {
10652 IStream_Release(stream);
10653 return hr;
10654 }
10655
10656 /* update flag if differs */
10657 if ((header[1] ^ flags) & OleStream_Convert)
10658 {
10659 LARGE_INTEGER pos = {{0}};
10660
10661 if (header[1] & OleStream_Convert)
10662 flags = header[1] & ~OleStream_Convert;
10663 else
10664 flags = header[1] | OleStream_Convert;
10665
10666 pos.QuadPart = sizeof(DWORD);
10667 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10668 if (FAILED(hr))
10669 {
10670 IStream_Release(stream);
10671 return hr;
10672 }
10673
10674 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10675 }
10676
10677 IStream_Release(stream);
10678 return hr;
10679 }