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