ReactOS 0.4.16-dev-36-g301675c
storage32.c
Go to the documentation of this file.
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/debug.h"
47
48#include "storage32.h"
49#include "ole2.h" /* For Write/ReadClassStm */
50
51#include "winreg.h"
52#include "wine/wingdi16.h"
53#include "compobj_private.h"
54
56
57
58/*
59 * These are signatures to detect the type of Document file.
60 */
61static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
62static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
63
64extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
65
66
67/****************************************************************************
68 * StorageInternalImpl definitions.
69 *
70 * Definition of the implementation structure for the IStorage interface.
71 * This one implements the IStorage interface for storage that are
72 * inside another storage.
73 */
74typedef struct StorageInternalImpl
75{
77
78 /*
79 * Entry in the parent's stream tracking list
80 */
82
85
86static const IStorageVtbl StorageInternalImpl_Vtbl;
88
89typedef struct TransactedDirEntry
90{
91 /* If applicable, a reference to the original DirEntry in the transacted
92 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
94
95 /* True if this entry is being used. */
97
98 /* True if data is up to date. */
100
101 /* True if this entry has been modified. */
103
104 /* True if this entry's stream has been modified. */
106
107 /* True if this entry has been deleted in the transacted storage, but the
108 * delete has not yet been committed. */
110
111 /* If this entry's stream has been modified, a reference to where the stream
112 * is stored in the snapshot file. */
114
115 /* This directory entry's data, including any changes that have been made. */
117
118 /* A reference to the parent of this node. This is only valid while we are
119 * committing changes. */
121
122 /* A reference to a newly-created entry in the transacted parent. This is
123 * always equal to transactedParentEntry except when committing changes. */
126
127
128/****************************************************************************
129 * Transacted storage object.
130 */
132{
134
135 /*
136 * Modified streams are temporarily saved to the scratch file.
137 */
139
140 /* The directory structure is kept here, so that we can track how these
141 * entries relate to those in the parent storage. */
145
146 /*
147 * Changes are committed to the transacted parent.
148 */
150
151 /* The transaction signature from when we last committed */
154
155static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
157
159{
161
162 /*
163 * Snapshot and uncommitted changes go here.
164 */
166
167 /*
168 * Changes are committed to the transacted parent.
169 */
171
172 /* The transaction signature from when we last committed */
175
176
177/****************************************************************************
178 * BlockChainStream definitions.
179 *
180 * The BlockChainStream class is a utility class that is used to create an
181 * abstraction of the big block chains in the storage file.
182 */
183
185{
186 /* This represents a range of blocks that happen reside in consecutive sectors. */
190};
191
192typedef struct BlockChainBlock
193{
200
202{
213};
214
215/* Returns the number of blocks that comprises this chain.
216 * This is not the size of the stream as the last block may not be full!
217 */
219{
220 return This->numBlocks;
221}
222
230
231
232/****************************************************************************
233 * SmallBlockChainStream definitions.
234 *
235 * The SmallBlockChainStream class is a utility class that is used to create an
236 * abstraction of the small block chains in the storage file.
237 */
238
240{
244};
245
252
253
254/************************************************************************
255 * STGM Functions
256 ***********************************************************************/
257
258/************************************************************************
259 * This method validates an STGM parameter that can contain the values below
260 *
261 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
262 * The stgm values contained in 0xffff0000 are bitmasks.
263 *
264 * STGM_DIRECT 0x00000000
265 * STGM_TRANSACTED 0x00010000
266 * STGM_SIMPLE 0x08000000
267 *
268 * STGM_READ 0x00000000
269 * STGM_WRITE 0x00000001
270 * STGM_READWRITE 0x00000002
271 *
272 * STGM_SHARE_DENY_NONE 0x00000040
273 * STGM_SHARE_DENY_READ 0x00000030
274 * STGM_SHARE_DENY_WRITE 0x00000020
275 * STGM_SHARE_EXCLUSIVE 0x00000010
276 *
277 * STGM_PRIORITY 0x00040000
278 * STGM_DELETEONRELEASE 0x04000000
279 *
280 * STGM_CREATE 0x00001000
281 * STGM_CONVERT 0x00020000
282 * STGM_FAILIFTHERE 0x00000000
283 *
284 * STGM_NOSCRATCH 0x00100000
285 * STGM_NOSNAPSHOT 0x00200000
286 */
288{
290 DWORD share = STGM_SHARE_MODE(stgm);
292
293 if (stgm&~STGM_KNOWN_FLAGS)
294 {
295 ERR("unknown flags %08x\n", stgm);
296 return E_FAIL;
297 }
298
299 switch (access)
300 {
301 case STGM_READ:
302 case STGM_WRITE:
303 case STGM_READWRITE:
304 break;
305 default:
306 return E_FAIL;
307 }
308
309 switch (share)
310 {
315 break;
316 case 0:
317 if (!(stgm & STGM_TRANSACTED))
318 return E_FAIL;
319 break;
320 default:
321 return E_FAIL;
322 }
323
324 switch (create)
325 {
326 case STGM_CREATE:
327 case STGM_FAILIFTHERE:
328 break;
329 default:
330 return E_FAIL;
331 }
332
333 /*
334 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
335 */
336 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
337 return E_FAIL;
338
339 /*
340 * STGM_CREATE | STGM_CONVERT
341 * if both are false, STGM_FAILIFTHERE is set to TRUE
342 */
343 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
344 return E_FAIL;
345
346 /*
347 * STGM_NOSCRATCH requires STGM_TRANSACTED
348 */
349 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
350 return E_FAIL;
351
352 /*
353 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
354 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
355 */
356 if ( (stgm & STGM_NOSNAPSHOT) &&
357 (!(stgm & STGM_TRANSACTED) ||
358 share == STGM_SHARE_EXCLUSIVE ||
359 share == STGM_SHARE_DENY_WRITE) )
360 return E_FAIL;
361
362 return S_OK;
363}
364
365/************************************************************************
366 * GetShareModeFromSTGM
367 *
368 * This method will return a share mode flag from a STGM value.
369 * The STGM value is assumed valid.
370 */
372{
373 switch (STGM_SHARE_MODE(stgm))
374 {
375 case 0:
376 assert(stgm & STGM_TRANSACTED);
377 /* fall-through */
381 return FILE_SHARE_WRITE;
384 return FILE_SHARE_READ;
385 }
386 ERR("Invalid share mode!\n");
387 assert(0);
388 return 0;
389}
390
391/************************************************************************
392 * GetAccessModeFromSTGM
393 *
394 * This method will return an access mode flag from a STGM value.
395 * The STGM value is assumed valid.
396 */
398{
399 switch (STGM_ACCESS_MODE(stgm))
400 {
401 case STGM_READ:
402 return GENERIC_READ;
403 case STGM_WRITE:
404 case STGM_READWRITE:
406 }
407 ERR("Invalid access mode!\n");
408 assert(0);
409 return 0;
410}
411
412/************************************************************************
413 * GetCreationModeFromSTGM
414 *
415 * This method will return a creation mode flag from a STGM value.
416 * The STGM value is assumed valid.
417 */
419{
420 switch(STGM_CREATE_MODE(stgm))
421 {
422 case STGM_CREATE:
423 return CREATE_ALWAYS;
424 case STGM_CONVERT:
425 FIXME("STGM_CONVERT not implemented!\n");
426 return CREATE_NEW;
427 case STGM_FAILIFTHERE:
428 return CREATE_NEW;
429 }
430 ERR("Invalid create mode!\n");
431 assert(0);
432 return 0;
433}
434
435
436/************************************************************************
437 * IDirectWriterLock implementation
438 ***********************************************************************/
439
441{
442 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
443}
444
446{
448 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
449}
450
452{
454 return IStorage_AddRef(&This->IStorage_iface);
455}
456
458{
460 return IStorage_Release(&This->IStorage_iface);
461}
462
464{
466 FIXME("(%p)->(%d): stub\n", This, timeout);
467 return E_NOTIMPL;
468}
469
471{
473 FIXME("(%p): stub\n", This);
474 return E_NOTIMPL;
475}
476
478{
480 FIXME("(%p): stub\n", This);
481 return E_NOTIMPL;
482}
483
484static const IDirectWriterLockVtbl DirectWriterLockVtbl =
485{
492};
493
494
495/************************************************************************
496 * StorageBaseImpl implementation : Tree helper functions
497 ***********************************************************************/
498
499/****************************************************************************
500 *
501 * Internal Method
502 *
503 * Case insensitive comparison of DirEntry.name by first considering
504 * their size.
505 *
506 * Returns <0 when name1 < name2
507 * >0 when name1 > name2
508 * 0 when name1 == name2
509 */
511 const OLECHAR *name1,
512 const OLECHAR *name2)
513{
514 LONG diff = lstrlenW(name1) - lstrlenW(name2);
515
516 while (diff == 0 && *name1 != 0)
517 {
518 /*
519 * We compare the string themselves only when they are of the same length
520 */
521 diff = towupper(*name1++) - towupper(*name2++);
522 }
523
524 return diff;
525}
526
527/****************************************************************************
528 *
529 * Internal Method
530 *
531 * Find and read the element of a storage with the given name.
532 */
533static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
534 const OLECHAR *name, DirEntry *data)
535{
536 DirRef currentEntry;
537
538 /* Read the storage entry to find the root of the tree. */
539 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
540
541 currentEntry = data->dirRootEntry;
542
543 while (currentEntry != DIRENTRY_NULL)
544 {
545 LONG cmp;
546
547 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
548
550
551 if (cmp == 0)
552 /* found it */
553 break;
554
555 else if (cmp < 0)
556 currentEntry = data->leftChild;
557
558 else if (cmp > 0)
559 currentEntry = data->rightChild;
560 }
561
562 return currentEntry;
563}
564
565/****************************************************************************
566 *
567 * Internal Method
568 *
569 * Find and read the binary tree parent of the element with the given name.
570 *
571 * If there is no such element, find a place where it could be inserted and
572 * return STG_E_FILENOTFOUND.
573 */
574static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
575 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
576 ULONG *relation)
577{
578 DirRef childEntry;
579 DirEntry childData;
580
581 /* Read the storage entry to find the root of the tree. */
582 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
583
584 *parentEntry = storageEntry;
585 *relation = DIRENTRY_RELATION_DIR;
586
587 childEntry = parentData->dirRootEntry;
588
589 while (childEntry != DIRENTRY_NULL)
590 {
591 LONG cmp;
592
593 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
594
595 cmp = entryNameCmp(childName, childData.name);
596
597 if (cmp == 0)
598 /* found it */
599 break;
600
601 else if (cmp < 0)
602 {
603 *parentData = childData;
604 *parentEntry = childEntry;
605 *relation = DIRENTRY_RELATION_PREVIOUS;
606
607 childEntry = parentData->leftChild;
608 }
609
610 else if (cmp > 0)
611 {
612 *parentData = childData;
613 *parentEntry = childEntry;
614 *relation = DIRENTRY_RELATION_NEXT;
615
616 childEntry = parentData->rightChild;
617 }
618 }
619
620 if (childEntry == DIRENTRY_NULL)
621 return STG_E_FILENOTFOUND;
622 else
623 return S_OK;
624}
625
626static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
627{
628 switch (relation)
629 {
631 entry->leftChild = new_target;
632 break;
634 entry->rightChild = new_target;
635 break;
637 entry->dirRootEntry = new_target;
638 break;
639 default:
640 assert(0);
641 }
642}
643
644/****************************************************************************
645 *
646 * Internal Method
647 *
648 * Add a directory entry to a storage
649 */
652 DirRef parentStorageIndex,
653 DirRef newEntryIndex)
654{
655 DirEntry currentEntry;
656 DirEntry newEntry;
657
658 /*
659 * Read the inserted entry
660 */
662 newEntryIndex,
663 &newEntry);
664
665 /*
666 * Read the storage entry
667 */
669 parentStorageIndex,
670 &currentEntry);
671
672 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
673 {
674 /*
675 * The root storage contains some element, therefore, start the research
676 * for the appropriate location.
677 */
678 BOOL found = FALSE;
679 DirRef current, next, previous, currentEntryId;
680
681 /*
682 * Keep a reference to the root of the storage's element tree
683 */
684 currentEntryId = currentEntry.dirRootEntry;
685
686 /*
687 * Read
688 */
690 currentEntry.dirRootEntry,
691 &currentEntry);
692
693 previous = currentEntry.leftChild;
694 next = currentEntry.rightChild;
695 current = currentEntryId;
696
697 while (!found)
698 {
699 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
700
701 if (diff < 0)
702 {
703 if (previous != DIRENTRY_NULL)
704 {
706 previous,
707 &currentEntry);
708 current = previous;
709 }
710 else
711 {
712 currentEntry.leftChild = newEntryIndex;
714 current,
715 &currentEntry);
716 found = TRUE;
717 }
718 }
719 else if (diff > 0)
720 {
721 if (next != DIRENTRY_NULL)
722 {
724 next,
725 &currentEntry);
726 current = next;
727 }
728 else
729 {
730 currentEntry.rightChild = newEntryIndex;
732 current,
733 &currentEntry);
734 found = TRUE;
735 }
736 }
737 else
738 {
739 /*
740 * Trying to insert an item with the same name in the
741 * subtree structure.
742 */
744 }
745
746 previous = currentEntry.leftChild;
747 next = currentEntry.rightChild;
748 }
749 }
750 else
751 {
752 /*
753 * The storage is empty, make the new entry the root of its element tree
754 */
755 currentEntry.dirRootEntry = newEntryIndex;
757 parentStorageIndex,
758 &currentEntry);
759 }
760
761 return S_OK;
762}
763
764/*************************************************************************
765 *
766 * Internal Method
767 *
768 * This method removes a directory entry from its parent storage tree without
769 * freeing any resources attached to it.
770 */
773 DirRef parentStorageIndex,
774 DirRef deletedIndex)
775{
776 DirEntry entryToDelete;
777 DirEntry parentEntry;
778 DirRef parentEntryRef;
779 ULONG typeOfRelation;
780 HRESULT hr;
781
782 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
783
784 if (hr != S_OK)
785 return hr;
786
787 /*
788 * Find the element that links to the one we want to delete.
789 */
790 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
791 &parentEntry, &parentEntryRef, &typeOfRelation);
792
793 if (hr != S_OK)
794 return hr;
795
796 if (entryToDelete.leftChild != DIRENTRY_NULL)
797 {
798 /*
799 * Replace the deleted entry with its left child
800 */
801 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
802
804 This,
805 parentEntryRef,
806 &parentEntry);
807 if(FAILED(hr))
808 {
809 return hr;
810 }
811
812 if (entryToDelete.rightChild != DIRENTRY_NULL)
813 {
814 /*
815 * We need to reinsert the right child somewhere. We already know it and
816 * its children are greater than everything in the left tree, so we
817 * insert it at the rightmost point in the left tree.
818 */
819 DirRef newRightChildParent = entryToDelete.leftChild;
820 DirEntry newRightChildParentEntry;
821
822 do
823 {
825 This,
826 newRightChildParent,
827 &newRightChildParentEntry);
828 if (FAILED(hr))
829 {
830 return hr;
831 }
832
833 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
834 newRightChildParent = newRightChildParentEntry.rightChild;
835 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
836
837 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
838
840 This,
841 newRightChildParent,
842 &newRightChildParentEntry);
843 if (FAILED(hr))
844 {
845 return hr;
846 }
847 }
848 }
849 else
850 {
851 /*
852 * Replace the deleted entry with its right child
853 */
854 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
855
857 This,
858 parentEntryRef,
859 &parentEntry);
860 if(FAILED(hr))
861 {
862 return hr;
863 }
864 }
865
866 return hr;
867}
868
869
870/************************************************************************
871 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
872 ***********************************************************************/
873
874/*
875 * IEnumSTATSTGImpl definitions.
876 *
877 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
878 * This class allows iterating through the content of a storage and finding
879 * specific items inside it.
880 */
882{
884
885 LONG ref; /* Reference count */
886 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
887 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
888
889 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
890};
891
893{
894 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
895}
896
898{
899 IStorage_Release(&This->parentStorage->IStorage_iface);
901}
902
904 IEnumSTATSTG* iface,
905 REFIID riid,
906 void** ppvObject)
907{
909
910 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
911
912 if (ppvObject==0)
913 return E_INVALIDARG;
914
915 *ppvObject = 0;
916
919 {
920 *ppvObject = &This->IEnumSTATSTG_iface;
921 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
922 TRACE("<-- %p\n", *ppvObject);
923 return S_OK;
924 }
925
926 TRACE("<-- E_NOINTERFACE\n");
927 return E_NOINTERFACE;
928}
929
931 IEnumSTATSTG* iface)
932{
934 return InterlockedIncrement(&This->ref);
935}
936
938 IEnumSTATSTG* iface)
939{
941
942 ULONG newRef;
943
944 newRef = InterlockedDecrement(&This->ref);
945
946 if (newRef==0)
947 {
949 }
950
951 return newRef;
952}
953
956 DirRef *ref)
957{
959 DirRef searchNode;
961 HRESULT hr;
962 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
963
964 TRACE("%p,%p\n", This, ref);
965
966 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
967 This->parentStorage->storageDirEntry, &entry);
968 searchNode = entry.dirRootEntry;
969
970 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
971 {
972 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
973
974 if (SUCCEEDED(hr))
975 {
976 LONG diff = entryNameCmp( entry.name, This->name);
977
978 if (diff <= 0)
979 {
980 searchNode = entry.rightChild;
981 }
982 else
983 {
984 result = searchNode;
985 memcpy(result_name, entry.name, sizeof(result_name));
986 searchNode = entry.leftChild;
987 }
988 }
989 }
990
991 if (SUCCEEDED(hr))
992 {
993 *ref = result;
994 if (result != DIRENTRY_NULL)
995 memcpy(This->name, result_name, sizeof(result_name));
996 }
997
998 TRACE("<-- %08x\n", hr);
999 return hr;
1000}
1001
1003 IEnumSTATSTG* iface,
1004 ULONG celt,
1005 STATSTG* rgelt,
1006 ULONG* pceltFetched)
1007{
1009
1010 DirEntry currentEntry;
1011 STATSTG* currentReturnStruct = rgelt;
1012 ULONG objectFetched = 0;
1013 DirRef currentSearchNode;
1014 HRESULT hr=S_OK;
1015
1016 TRACE("%p,%u,%p,%p\n", iface, celt, rgelt, pceltFetched);
1017
1018 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
1019 return E_INVALIDARG;
1020
1021 if (This->parentStorage->reverted)
1022 {
1023 TRACE("<-- STG_E_REVERTED\n");
1024 return STG_E_REVERTED;
1025 }
1026
1027 /*
1028 * To avoid the special case, get another pointer to a ULONG value if
1029 * the caller didn't supply one.
1030 */
1031 if (pceltFetched==0)
1032 pceltFetched = &objectFetched;
1033
1034 /*
1035 * Start the iteration, we will iterate until we hit the end of the
1036 * linked list or until we hit the number of items to iterate through
1037 */
1038 *pceltFetched = 0;
1039
1040 while ( *pceltFetched < celt )
1041 {
1042 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1043
1044 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1045 {
1046 memset(currentReturnStruct, 0, sizeof(*currentReturnStruct));
1047 break;
1048 }
1049
1050 /*
1051 * Read the entry from the storage.
1052 */
1053 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
1054 currentSearchNode,
1055 &currentEntry);
1056 if (FAILED(hr)) break;
1057
1058 /*
1059 * Copy the information to the return buffer.
1060 */
1062 currentReturnStruct,
1063 &currentEntry,
1064 STATFLAG_DEFAULT);
1065
1066 /*
1067 * Step to the next item in the iteration
1068 */
1069 (*pceltFetched)++;
1070 currentReturnStruct++;
1071 }
1072
1073 if (SUCCEEDED(hr) && *pceltFetched != celt)
1074 hr = S_FALSE;
1075
1076 TRACE("<-- %08x (asked %u, got %u)\n", hr, celt, *pceltFetched);
1077 return hr;
1078}
1079
1080
1082 IEnumSTATSTG* iface,
1083 ULONG celt)
1084{
1086
1087 ULONG objectFetched = 0;
1088 DirRef currentSearchNode;
1089 HRESULT hr=S_OK;
1090
1091 TRACE("%p,%u\n", iface, celt);
1092
1093 if (This->parentStorage->reverted)
1094 {
1095 TRACE("<-- STG_E_REVERTED\n");
1096 return STG_E_REVERTED;
1097 }
1098
1099 while ( (objectFetched < celt) )
1100 {
1101 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1102
1103 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1104 break;
1105
1106 objectFetched++;
1107 }
1108
1109 if (SUCCEEDED(hr) && objectFetched != celt)
1110 return S_FALSE;
1111
1112 TRACE("<-- %08x\n", hr);
1113 return hr;
1114}
1115
1117 IEnumSTATSTG* iface)
1118{
1120
1121 TRACE("%p\n", iface);
1122
1123 if (This->parentStorage->reverted)
1124 {
1125 TRACE("<-- STG_E_REVERTED\n");
1126 return STG_E_REVERTED;
1127 }
1128
1129 This->name[0] = 0;
1130
1131 return S_OK;
1132}
1133
1135
1137 IEnumSTATSTG* iface,
1138 IEnumSTATSTG** ppenum)
1139{
1141 IEnumSTATSTGImpl* newClone;
1142
1143 TRACE("%p,%p\n", iface, ppenum);
1144
1145 if (This->parentStorage->reverted)
1146 {
1147 TRACE("<-- STG_E_REVERTED\n");
1148 return STG_E_REVERTED;
1149 }
1150
1151 if (ppenum==0)
1152 return E_INVALIDARG;
1153
1154 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1155 This->storageDirEntry);
1156 if (!newClone)
1157 {
1158 *ppenum = NULL;
1159 return E_OUTOFMEMORY;
1160 }
1161
1162 /*
1163 * The new clone enumeration must point to the same current node as
1164 * the old one.
1165 */
1166 memcpy(newClone->name, This->name, sizeof(newClone->name));
1167
1168 *ppenum = &newClone->IEnumSTATSTG_iface;
1169
1170 return S_OK;
1171}
1172
1173/*
1174 * Virtual function table for the IEnumSTATSTGImpl class.
1175 */
1176static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1177{
1185};
1186
1188 StorageBaseImpl* parentStorage,
1189 DirRef storageDirEntry)
1190{
1191 IEnumSTATSTGImpl* newEnumeration;
1192
1193 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1194
1195 if (newEnumeration)
1196 {
1197 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1198 newEnumeration->ref = 1;
1199 newEnumeration->name[0] = 0;
1200
1201 /*
1202 * We want to nail-down the reference to the storage in case the
1203 * enumeration out-lives the storage in the client application.
1204 */
1205 newEnumeration->parentStorage = parentStorage;
1206 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1207
1208 newEnumeration->storageDirEntry = storageDirEntry;
1209 }
1210
1211 return newEnumeration;
1212}
1213
1214
1215/************************************************************************
1216 * StorageBaseImpl implementation
1217 ***********************************************************************/
1218
1220{
1221 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1222}
1223
1224/************************************************************************
1225 * StorageBaseImpl_QueryInterface (IUnknown)
1226 *
1227 * This method implements the common QueryInterface for all IStorage
1228 * implementations contained in this file.
1229 *
1230 * See Windows documentation for more details on IUnknown methods.
1231 */
1233 IStorage* iface,
1234 REFIID riid,
1235 void** ppvObject)
1236{
1238
1239 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
1240
1241 if (!ppvObject)
1242 return E_INVALIDARG;
1243
1244 *ppvObject = 0;
1245
1246 if (IsEqualGUID(&IID_IUnknown, riid) ||
1247 IsEqualGUID(&IID_IStorage, riid))
1248 {
1249 *ppvObject = &This->IStorage_iface;
1250 }
1251 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1252 {
1253 *ppvObject = &This->IPropertySetStorage_iface;
1254 }
1255 /* locking interface is reported for writer only */
1256 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1257 {
1258 *ppvObject = &This->IDirectWriterLock_iface;
1259 }
1260 else
1261 {
1262 TRACE("<-- E_NOINTERFACE\n");
1263 return E_NOINTERFACE;
1264 }
1265
1266 IStorage_AddRef(iface);
1267 TRACE("<-- %p\n", *ppvObject);
1268 return S_OK;
1269}
1270
1271/************************************************************************
1272 * StorageBaseImpl_AddRef (IUnknown)
1273 *
1274 * This method implements the common AddRef for all IStorage
1275 * implementations contained in this file.
1276 *
1277 * See Windows documentation for more details on IUnknown methods.
1278 */
1280 IStorage* iface)
1281{
1284
1285 TRACE("(%p) AddRef to %d\n", This, ref);
1286
1287 return ref;
1288}
1289
1290/************************************************************************
1291 * StorageBaseImpl_Release (IUnknown)
1292 *
1293 * This method implements the common Release for all IStorage
1294 * implementations contained in this file.
1295 *
1296 * See Windows documentation for more details on IUnknown methods.
1297 */
1299 IStorage* iface)
1300{
1302
1304
1305 TRACE("(%p) ReleaseRef to %d\n", This, ref);
1306
1307 if (ref == 0)
1308 {
1309 /*
1310 * Since we are using a system of base-classes, we want to call the
1311 * destructor of the appropriate derived class. To do this, we are
1312 * using virtual functions to implement the destructor.
1313 */
1315 }
1316
1317 return ref;
1318}
1319
1321 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1322 SNB snbExclude, IStorage *pstgDest);
1323
1325 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1326 SNB snbExclude, IStorage *pstgDest)
1327{
1328 DirEntry data;
1329 HRESULT hr;
1330 BOOL skip = FALSE;
1331 IStorage *pstgTmp;
1332 IStream *pstrChild, *pstrTmp;
1333 STATSTG strStat;
1334
1335 if (srcEntry == DIRENTRY_NULL)
1336 return S_OK;
1337
1338 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1339
1340 if (FAILED(hr))
1341 return hr;
1342
1343 if ( snbExclude )
1344 {
1345 WCHAR **snb = snbExclude;
1346
1347 while ( *snb != NULL && !skip )
1348 {
1349 if ( wcscmp(data.name, *snb) == 0 )
1350 skip = TRUE;
1351 ++snb;
1352 }
1353 }
1354
1355 if (!skip)
1356 {
1357 if (data.stgType == STGTY_STORAGE && !skip_storage)
1358 {
1359 /*
1360 * create a new storage in destination storage
1361 */
1362 hr = IStorage_CreateStorage( pstgDest, data.name,
1364 0, 0,
1365 &pstgTmp );
1366
1367 /*
1368 * if it already exist, don't create a new one use this one
1369 */
1371 {
1372 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1374 NULL, 0, &pstgTmp );
1375 }
1376
1377 if (SUCCEEDED(hr))
1378 {
1379 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1380 skip_stream, NULL, pstgTmp );
1381
1382 IStorage_Release(pstgTmp);
1383 }
1384 }
1385 else if (data.stgType == STGTY_STREAM && !skip_stream)
1386 {
1387 /*
1388 * create a new stream in destination storage. If the stream already
1389 * exist, it will be deleted and a new one will be created.
1390 */
1391 hr = IStorage_CreateStream( pstgDest, data.name,
1393 0, 0, &pstrTmp );
1394
1395 /*
1396 * open child stream storage. This operation must succeed even if the
1397 * stream is already open, so we use internal functions to do it.
1398 */
1399 if (hr == S_OK)
1400 {
1402
1403 if (streamimpl)
1404 {
1405 pstrChild = &streamimpl->IStream_iface;
1406 if (pstrChild)
1407 IStream_AddRef(pstrChild);
1408 }
1409 else
1410 {
1411 pstrChild = NULL;
1412 hr = E_OUTOFMEMORY;
1413 }
1414 }
1415
1416 if (hr == S_OK)
1417 {
1418 /*
1419 * Get the size of the source stream
1420 */
1421 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1422
1423 /*
1424 * Set the size of the destination stream.
1425 */
1426 IStream_SetSize(pstrTmp, strStat.cbSize);
1427
1428 /*
1429 * do the copy
1430 */
1431 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1432 NULL, NULL );
1433
1434 IStream_Release( pstrChild );
1435 }
1436
1437 IStream_Release( pstrTmp );
1438 }
1439 }
1440
1441 /* copy siblings */
1442 if (SUCCEEDED(hr))
1443 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1444 skip_stream, snbExclude, pstgDest );
1445
1446 if (SUCCEEDED(hr))
1447 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1448 skip_stream, snbExclude, pstgDest );
1449
1450 TRACE("<-- %08x\n", hr);
1451 return hr;
1452}
1453
1455{
1456 StgStreamImpl *strm;
1457
1458 TRACE("%p,%d\n", stg, streamEntry);
1459
1460 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1461 {
1462 if (strm->dirEntry == streamEntry)
1463 {
1464 return TRUE;
1465 }
1466 }
1467
1468 return FALSE;
1469}
1470
1472{
1473 StorageInternalImpl *childstg;
1474
1475 TRACE("%p,%d\n", stg, storageEntry);
1476
1477 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1478 {
1479 if (childstg->base.storageDirEntry == storageEntry)
1480 {
1481 return TRUE;
1482 }
1483 }
1484
1485 return FALSE;
1486}
1487
1488/************************************************************************
1489 * StorageBaseImpl_OpenStream (IStorage)
1490 *
1491 * This method will open the specified stream object from the current storage.
1492 *
1493 * See Windows documentation for more details on IStorage methods.
1494 */
1496 IStorage* iface,
1497 const OLECHAR* pwcsName, /* [string][in] */
1498 void* reserved1, /* [unique][in] */
1499 DWORD grfMode, /* [in] */
1500 DWORD reserved2, /* [in] */
1501 IStream** ppstm) /* [out] */
1502{
1504 StgStreamImpl* newStream;
1505 DirEntry currentEntry;
1506 DirRef streamEntryRef;
1508
1509 TRACE("(%p, %s, %p, %x, %d, %p)\n",
1510 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1511
1512 if ( (pwcsName==NULL) || (ppstm==0) )
1513 {
1514 res = E_INVALIDARG;
1515 goto end;
1516 }
1517
1518 *ppstm = NULL;
1519
1520 if ( FAILED( validateSTGM(grfMode) ) ||
1522 {
1524 goto end;
1525 }
1526
1527 /*
1528 * As documented.
1529 */
1530 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1531 {
1533 goto end;
1534 }
1535
1536 if (This->reverted)
1537 {
1539 goto end;
1540 }
1541
1542 /*
1543 * Check that we're compatible with the parent's storage mode, but
1544 * only if we are not in transacted mode
1545 */
1546 if(!(This->openFlags & STGM_TRANSACTED)) {
1547 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1548 {
1550 goto end;
1551 }
1552 }
1553
1554 /*
1555 * Search for the element with the given name
1556 */
1557 streamEntryRef = findElement(
1558 This,
1559 This->storageDirEntry,
1560 pwcsName,
1561 &currentEntry);
1562
1563 /*
1564 * If it was found, construct the stream object and return a pointer to it.
1565 */
1566 if ( (streamEntryRef!=DIRENTRY_NULL) &&
1567 (currentEntry.stgType==STGTY_STREAM) )
1568 {
1569 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1570 {
1571 /* A single stream cannot be opened a second time. */
1573 goto end;
1574 }
1575
1576 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1577
1578 if (newStream)
1579 {
1580 newStream->grfMode = grfMode;
1581 *ppstm = &newStream->IStream_iface;
1582
1583 IStream_AddRef(*ppstm);
1584
1585 res = S_OK;
1586 goto end;
1587 }
1588
1590 goto end;
1591 }
1592
1594
1595end:
1596 if (res == S_OK)
1597 TRACE("<-- IStream %p\n", *ppstm);
1598 TRACE("<-- %08x\n", res);
1599 return res;
1600}
1601
1602/************************************************************************
1603 * StorageBaseImpl_OpenStorage (IStorage)
1604 *
1605 * This method will open a new storage object from the current storage.
1606 *
1607 * See Windows documentation for more details on IStorage methods.
1608 */
1610 IStorage* iface,
1611 const OLECHAR* pwcsName, /* [string][unique][in] */
1612 IStorage* pstgPriority, /* [unique][in] */
1613 DWORD grfMode, /* [in] */
1614 SNB snbExclude, /* [unique][in] */
1615 DWORD reserved, /* [in] */
1616 IStorage** ppstg) /* [out] */
1617{
1619 StorageInternalImpl* newStorage;
1620 StorageBaseImpl* newTransactedStorage;
1621 DirEntry currentEntry;
1622 DirRef storageEntryRef;
1624
1625 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1626 iface, debugstr_w(pwcsName), pstgPriority,
1627 grfMode, snbExclude, reserved, ppstg);
1628
1629 if ((pwcsName==NULL) || (ppstg==0) )
1630 {
1631 res = E_INVALIDARG;
1632 goto end;
1633 }
1634
1635 if (This->openFlags & STGM_SIMPLE)
1636 {
1638 goto end;
1639 }
1640
1641 /* as documented */
1642 if (snbExclude != NULL)
1643 {
1645 goto end;
1646 }
1647
1648 if ( FAILED( validateSTGM(grfMode) ))
1649 {
1651 goto end;
1652 }
1653
1654 /*
1655 * As documented.
1656 */
1657 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1658 (grfMode & STGM_DELETEONRELEASE) ||
1659 (grfMode & STGM_PRIORITY) )
1660 {
1662 goto end;
1663 }
1664
1665 if (This->reverted)
1666 return STG_E_REVERTED;
1667
1668 /*
1669 * Check that we're compatible with the parent's storage mode,
1670 * but only if we are not transacted
1671 */
1672 if(!(This->openFlags & STGM_TRANSACTED)) {
1673 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1674 {
1676 goto end;
1677 }
1678 }
1679
1680 *ppstg = NULL;
1681
1682 storageEntryRef = findElement(
1683 This,
1684 This->storageDirEntry,
1685 pwcsName,
1686 &currentEntry);
1687
1688 if ( (storageEntryRef!=DIRENTRY_NULL) &&
1689 (currentEntry.stgType==STGTY_STORAGE) )
1690 {
1691 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1692 {
1693 /* A single storage cannot be opened a second time. */
1695 goto end;
1696 }
1697
1698 newStorage = StorageInternalImpl_Construct(
1699 This,
1700 grfMode,
1701 storageEntryRef);
1702
1703 if (newStorage != 0)
1704 {
1705 if (grfMode & STGM_TRANSACTED)
1706 {
1707 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1708
1709 if (FAILED(res))
1710 {
1711 HeapFree(GetProcessHeap(), 0, newStorage);
1712 goto end;
1713 }
1714
1715 *ppstg = &newTransactedStorage->IStorage_iface;
1716 }
1717 else
1718 {
1719 *ppstg = &newStorage->base.IStorage_iface;
1720 }
1721
1722 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1723
1724 res = S_OK;
1725 goto end;
1726 }
1727
1729 goto end;
1730 }
1731
1733
1734end:
1735 TRACE("<-- %08x\n", res);
1736 return res;
1737}
1738
1739/************************************************************************
1740 * StorageBaseImpl_EnumElements (IStorage)
1741 *
1742 * This method will create an enumerator object that can be used to
1743 * retrieve information about all the elements in the storage object.
1744 *
1745 * See Windows documentation for more details on IStorage methods.
1746 */
1748 IStorage* iface,
1749 DWORD reserved1, /* [in] */
1750 void* reserved2, /* [size_is][unique][in] */
1751 DWORD reserved3, /* [in] */
1752 IEnumSTATSTG** ppenum) /* [out] */
1753{
1755 IEnumSTATSTGImpl* newEnum;
1756
1757 TRACE("(%p, %d, %p, %d, %p)\n",
1758 iface, reserved1, reserved2, reserved3, ppenum);
1759
1760 if (!ppenum)
1761 return E_INVALIDARG;
1762
1763 if (This->reverted)
1764 return STG_E_REVERTED;
1765
1767 This,
1768 This->storageDirEntry);
1769
1770 if (newEnum)
1771 {
1772 *ppenum = &newEnum->IEnumSTATSTG_iface;
1773 return S_OK;
1774 }
1775
1776 return E_OUTOFMEMORY;
1777}
1778
1779/************************************************************************
1780 * StorageBaseImpl_Stat (IStorage)
1781 *
1782 * This method will retrieve information about this storage object.
1783 *
1784 * See Windows documentation for more details on IStorage methods.
1785 */
1787 IStorage* iface,
1788 STATSTG* pstatstg, /* [out] */
1789 DWORD grfStatFlag) /* [in] */
1790{
1792 DirEntry currentEntry;
1794
1795 TRACE("(%p, %p, %x)\n",
1796 iface, pstatstg, grfStatFlag);
1797
1798 if (!pstatstg)
1799 {
1800 res = E_INVALIDARG;
1801 goto end;
1802 }
1803
1804 if (This->reverted)
1805 {
1807 goto end;
1808 }
1809
1811 This,
1812 This->storageDirEntry,
1813 &currentEntry);
1814
1815 if (SUCCEEDED(res))
1816 {
1818 This,
1819 pstatstg,
1820 &currentEntry,
1821 grfStatFlag);
1822
1823 pstatstg->grfMode = This->openFlags;
1824 pstatstg->grfStateBits = This->stateBits;
1825 }
1826
1827end:
1828 if (res == S_OK)
1829 {
1830 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);
1831 }
1832 TRACE("<-- %08x\n", res);
1833 return res;
1834}
1835
1836/************************************************************************
1837 * StorageBaseImpl_RenameElement (IStorage)
1838 *
1839 * This method will rename the specified element.
1840 *
1841 * See Windows documentation for more details on IStorage methods.
1842 */
1844 IStorage* iface,
1845 const OLECHAR* pwcsOldName, /* [in] */
1846 const OLECHAR* pwcsNewName) /* [in] */
1847{
1849 DirEntry currentEntry;
1850 DirRef currentEntryRef;
1851
1852 TRACE("(%p, %s, %s)\n",
1853 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1854
1855 if (This->reverted)
1856 return STG_E_REVERTED;
1857
1858 currentEntryRef = findElement(This,
1859 This->storageDirEntry,
1860 pwcsNewName,
1861 &currentEntry);
1862
1863 if (currentEntryRef != DIRENTRY_NULL)
1864 {
1865 /*
1866 * There is already an element with the new name
1867 */
1869 }
1870
1871 /*
1872 * Search for the old element name
1873 */
1874 currentEntryRef = findElement(This,
1875 This->storageDirEntry,
1876 pwcsOldName,
1877 &currentEntry);
1878
1879 if (currentEntryRef != DIRENTRY_NULL)
1880 {
1881 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1882 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1883 {
1884 WARN("Element is already open; cannot rename.\n");
1885 return STG_E_ACCESSDENIED;
1886 }
1887
1888 /* Remove the element from its current position in the tree */
1889 removeFromTree(This, This->storageDirEntry,
1890 currentEntryRef);
1891
1892 /* Change the name of the element */
1893 lstrcpyW(currentEntry.name, pwcsNewName);
1894
1895 /* Delete any sibling links */
1896 currentEntry.leftChild = DIRENTRY_NULL;
1897 currentEntry.rightChild = DIRENTRY_NULL;
1898
1899 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1900 &currentEntry);
1901
1902 /* Insert the element in a new position in the tree */
1903 insertIntoTree(This, This->storageDirEntry,
1904 currentEntryRef);
1905 }
1906 else
1907 {
1908 /*
1909 * There is no element with the old name
1910 */
1911 return STG_E_FILENOTFOUND;
1912 }
1913
1915}
1916
1917/************************************************************************
1918 * StorageBaseImpl_CreateStream (IStorage)
1919 *
1920 * This method will create a stream object within this storage
1921 *
1922 * See Windows documentation for more details on IStorage methods.
1923 */
1925 IStorage* iface,
1926 const OLECHAR* pwcsName, /* [string][in] */
1927 DWORD grfMode, /* [in] */
1928 DWORD reserved1, /* [in] */
1929 DWORD reserved2, /* [in] */
1930 IStream** ppstm) /* [out] */
1931{
1933 StgStreamImpl* newStream;
1934 DirEntry currentEntry, newStreamEntry;
1935 DirRef currentEntryRef, newStreamEntryRef;
1936 HRESULT hr;
1937
1938 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1939 iface, debugstr_w(pwcsName), grfMode,
1940 reserved1, reserved2, ppstm);
1941
1942 if (ppstm == 0)
1943 return STG_E_INVALIDPOINTER;
1944
1945 if (pwcsName == 0)
1946 return STG_E_INVALIDNAME;
1947
1948 if (reserved1 || reserved2)
1950
1951 if ( FAILED( validateSTGM(grfMode) ))
1952 return STG_E_INVALIDFLAG;
1953
1954 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1955 return STG_E_INVALIDFLAG;
1956
1957 if (This->reverted)
1958 return STG_E_REVERTED;
1959
1960 /*
1961 * As documented.
1962 */
1963 if ((grfMode & STGM_DELETEONRELEASE) ||
1964 (grfMode & STGM_TRANSACTED))
1965 return STG_E_INVALIDFUNCTION;
1966
1967 /*
1968 * Don't worry about permissions in transacted mode, as we can always write
1969 * changes; we just can't always commit them.
1970 */
1971 if(!(This->openFlags & STGM_TRANSACTED)) {
1972 /* Can't create a stream on read-only storage */
1973 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1974 return STG_E_ACCESSDENIED;
1975
1976 /* Can't create a stream with greater access than the parent. */
1977 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1978 return STG_E_ACCESSDENIED;
1979 }
1980
1981 if(This->openFlags & STGM_SIMPLE)
1982 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1983
1984 *ppstm = 0;
1985
1986 currentEntryRef = findElement(This,
1987 This->storageDirEntry,
1988 pwcsName,
1989 &currentEntry);
1990
1991 if (currentEntryRef != DIRENTRY_NULL)
1992 {
1993 /*
1994 * An element with this name already exists
1995 */
1996 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1997 {
1998 IStorage_DestroyElement(iface, pwcsName);
1999 }
2000 else
2002 }
2003
2004 /*
2005 * memset the empty entry
2006 */
2007 memset(&newStreamEntry, 0, sizeof(DirEntry));
2008
2009 newStreamEntry.sizeOfNameString =
2010 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2011
2012 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2013 return STG_E_INVALIDNAME;
2014
2015 lstrcpyW(newStreamEntry.name, pwcsName);
2016
2017 newStreamEntry.stgType = STGTY_STREAM;
2018 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2019 newStreamEntry.size.u.LowPart = 0;
2020 newStreamEntry.size.u.HighPart = 0;
2021
2022 newStreamEntry.leftChild = DIRENTRY_NULL;
2023 newStreamEntry.rightChild = DIRENTRY_NULL;
2024 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2025
2026 /* call CoFileTime to get the current time
2027 newStreamEntry.ctime
2028 newStreamEntry.mtime
2029 */
2030
2031 /* newStreamEntry.clsid */
2032
2033 /*
2034 * Create an entry with the new data
2035 */
2036 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2037 if (FAILED(hr))
2038 return hr;
2039
2040 /*
2041 * Insert the new entry in the parent storage's tree.
2042 */
2044 This,
2045 This->storageDirEntry,
2046 newStreamEntryRef);
2047 if (FAILED(hr))
2048 {
2049 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2050 return hr;
2051 }
2052
2053 /*
2054 * Open the stream to return it.
2055 */
2056 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2057
2058 if (newStream)
2059 {
2060 *ppstm = &newStream->IStream_iface;
2061 IStream_AddRef(*ppstm);
2062 }
2063 else
2064 {
2066 }
2067
2069}
2070
2071/************************************************************************
2072 * StorageBaseImpl_SetClass (IStorage)
2073 *
2074 * This method will write the specified CLSID in the directory entry of this
2075 * storage.
2076 *
2077 * See Windows documentation for more details on IStorage methods.
2078 */
2080 IStorage* iface,
2081 REFCLSID clsid) /* [in] */
2082{
2084 HRESULT hRes;
2085 DirEntry currentEntry;
2086
2087 TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid));
2088
2089 if (This->reverted)
2090 return STG_E_REVERTED;
2091
2093 This->storageDirEntry,
2094 &currentEntry);
2095 if (SUCCEEDED(hRes))
2096 {
2097 currentEntry.clsid = *clsid;
2098
2100 This->storageDirEntry,
2101 &currentEntry);
2102 }
2103
2104 if (SUCCEEDED(hRes))
2106
2107 return hRes;
2108}
2109
2110/************************************************************************
2111 * StorageBaseImpl_CreateStorage (IStorage)
2112 *
2113 * This method will create the storage object within the provided storage.
2114 *
2115 * See Windows documentation for more details on IStorage methods.
2116 */
2118 IStorage* iface,
2119 const OLECHAR *pwcsName, /* [string][in] */
2120 DWORD grfMode, /* [in] */
2121 DWORD reserved1, /* [in] */
2122 DWORD reserved2, /* [in] */
2123 IStorage **ppstg) /* [out] */
2124{
2126
2127 DirEntry currentEntry;
2128 DirEntry newEntry;
2129 DirRef currentEntryRef;
2130 DirRef newEntryRef;
2131 HRESULT hr;
2132
2133 TRACE("(%p, %s, %x, %d, %d, %p)\n",
2134 iface, debugstr_w(pwcsName), grfMode,
2135 reserved1, reserved2, ppstg);
2136
2137 if (ppstg == 0)
2138 return STG_E_INVALIDPOINTER;
2139
2140 if (This->openFlags & STGM_SIMPLE)
2141 {
2142 return STG_E_INVALIDFUNCTION;
2143 }
2144
2145 if (pwcsName == 0)
2146 return STG_E_INVALIDNAME;
2147
2148 *ppstg = NULL;
2149
2150 if ( FAILED( validateSTGM(grfMode) ) ||
2151 (grfMode & STGM_DELETEONRELEASE) )
2152 {
2153 WARN("bad grfMode: 0x%x\n", grfMode);
2154 return STG_E_INVALIDFLAG;
2155 }
2156
2157 if (This->reverted)
2158 return STG_E_REVERTED;
2159
2160 /*
2161 * Check that we're compatible with the parent's storage mode
2162 */
2163 if ( !(This->openFlags & STGM_TRANSACTED) &&
2164 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2165 {
2166 WARN("access denied\n");
2167 return STG_E_ACCESSDENIED;
2168 }
2169
2170 currentEntryRef = findElement(This,
2171 This->storageDirEntry,
2172 pwcsName,
2173 &currentEntry);
2174
2175 if (currentEntryRef != DIRENTRY_NULL)
2176 {
2177 /*
2178 * An element with this name already exists
2179 */
2180 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2181 ((This->openFlags & STGM_TRANSACTED) ||
2182 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2183 {
2184 hr = IStorage_DestroyElement(iface, pwcsName);
2185 if (FAILED(hr))
2186 return hr;
2187 }
2188 else
2189 {
2190 WARN("file already exists\n");
2192 }
2193 }
2194 else if (!(This->openFlags & STGM_TRANSACTED) &&
2195 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2196 {
2197 WARN("read-only storage\n");
2198 return STG_E_ACCESSDENIED;
2199 }
2200
2201 memset(&newEntry, 0, sizeof(DirEntry));
2202
2203 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2204
2206 {
2207 FIXME("name too long\n");
2208 return STG_E_INVALIDNAME;
2209 }
2210
2211 lstrcpyW(newEntry.name, pwcsName);
2212
2213 newEntry.stgType = STGTY_STORAGE;
2215 newEntry.size.u.LowPart = 0;
2216 newEntry.size.u.HighPart = 0;
2217
2218 newEntry.leftChild = DIRENTRY_NULL;
2219 newEntry.rightChild = DIRENTRY_NULL;
2220 newEntry.dirRootEntry = DIRENTRY_NULL;
2221
2222 /* call CoFileTime to get the current time
2223 newEntry.ctime
2224 newEntry.mtime
2225 */
2226
2227 /* newEntry.clsid */
2228
2229 /*
2230 * Create a new directory entry for the storage
2231 */
2232 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2233 if (FAILED(hr))
2234 return hr;
2235
2236 /*
2237 * Insert the new directory entry into the parent storage's tree
2238 */
2240 This,
2241 This->storageDirEntry,
2242 newEntryRef);
2243 if (FAILED(hr))
2244 {
2246 return hr;
2247 }
2248
2249 /*
2250 * Open it to get a pointer to return.
2251 */
2252 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2253
2254 if( (hr != S_OK) || (*ppstg == NULL))
2255 {
2256 return hr;
2257 }
2258
2259 if (SUCCEEDED(hr))
2261
2262 return S_OK;
2263}
2264
2266 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2267 SNB snbExclude, IStorage *pstgDest)
2268{
2269 DirEntry data;
2270 HRESULT hr;
2271
2272 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2273
2274 if (SUCCEEDED(hr))
2275 hr = IStorage_SetClass( pstgDest, &data.clsid );
2276
2277 if (SUCCEEDED(hr))
2278 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2279 skip_stream, snbExclude, pstgDest );
2280
2281 TRACE("<-- %08x\n", hr);
2282 return hr;
2283}
2284
2285/*************************************************************************
2286 * CopyTo (IStorage)
2287 */
2289 IStorage* iface,
2290 DWORD ciidExclude, /* [in] */
2291 const IID* rgiidExclude, /* [size_is][unique][in] */
2292 SNB snbExclude, /* [unique][in] */
2293 IStorage* pstgDest) /* [unique][in] */
2294{
2296
2297 BOOL skip_storage = FALSE, skip_stream = FALSE;
2298 DWORD i;
2299
2300 TRACE("(%p, %d, %p, %p, %p)\n",
2301 iface, ciidExclude, rgiidExclude,
2302 snbExclude, pstgDest);
2303
2304 if ( pstgDest == 0 )
2305 return STG_E_INVALIDPOINTER;
2306
2307 for(i = 0; i < ciidExclude; ++i)
2308 {
2309 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2310 skip_storage = TRUE;
2311 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2312 skip_stream = TRUE;
2313 else
2314 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2315 }
2316
2317 if (!skip_storage)
2318 {
2319 /* Give up early if it looks like this would be infinitely recursive.
2320 * Oddly enough, this includes some cases that aren't really recursive, like
2321 * copying to a transacted child. */
2322 IStorage *pstgDestAncestor = pstgDest;
2323 IStorage *pstgDestAncestorChild = NULL;
2324
2325 /* Go up the chain from the destination until we find the source storage. */
2326 while (pstgDestAncestor != iface) {
2327 pstgDestAncestorChild = pstgDest;
2328
2329 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2330 {
2332
2333 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2334 }
2335 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2336 {
2337 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2338
2339 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2340 }
2341 else
2342 break;
2343 }
2344
2345 if (pstgDestAncestor == iface)
2346 {
2347 BOOL fail = TRUE;
2348
2349 if (pstgDestAncestorChild && snbExclude)
2350 {
2351 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2352 DirEntry data;
2353 WCHAR **snb = snbExclude;
2354
2355 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2356
2357 while ( *snb != NULL && fail )
2358 {
2359 if ( wcscmp(data.name, *snb) == 0 )
2360 fail = FALSE;
2361 ++snb;
2362 }
2363 }
2364
2365 if (fail)
2366 return STG_E_ACCESSDENIED;
2367 }
2368 }
2369
2370 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2371 skip_storage, skip_stream, snbExclude, pstgDest );
2372}
2373
2374/*************************************************************************
2375 * MoveElementTo (IStorage)
2376 */
2378 IStorage* iface,
2379 const OLECHAR *pwcsName, /* [string][in] */
2380 IStorage *pstgDest, /* [unique][in] */
2381 const OLECHAR *pwcsNewName,/* [string][in] */
2382 DWORD grfFlags) /* [in] */
2383{
2384 FIXME("(%p %s %p %s %u): stub\n", iface,
2385 debugstr_w(pwcsName), pstgDest,
2386 debugstr_w(pwcsNewName), grfFlags);
2387 return E_NOTIMPL;
2388}
2389
2390/*************************************************************************
2391 * Commit (IStorage)
2392 *
2393 * Ensures that any changes made to a storage object open in transacted mode
2394 * are reflected in the parent storage
2395 *
2396 * In a non-transacted mode, this ensures all cached writes are completed.
2397 */
2399 IStorage* iface,
2400 DWORD grfCommitFlags)/* [in] */
2401{
2403 TRACE("(%p %d)\n", iface, grfCommitFlags);
2405}
2406
2407/*************************************************************************
2408 * Revert (IStorage)
2409 *
2410 * Discard all changes that have been made since the last commit operation
2411 */
2413 IStorage* iface)
2414{
2415 TRACE("(%p)\n", iface);
2416 return S_OK;
2417}
2418
2419/*********************************************************************
2420 *
2421 * Internal helper function for StorageBaseImpl_DestroyElement()
2422 *
2423 * Delete the contents of a storage entry.
2424 *
2425 */
2427 StorageBaseImpl *parentStorage,
2428 DirRef indexToDelete,
2429 DirEntry entryDataToDelete)
2430{
2431 IEnumSTATSTG *elements = 0;
2432 IStorage *childStorage = 0;
2433 STATSTG currentElement;
2434 HRESULT hr;
2435 HRESULT destroyHr = S_OK;
2436 StorageInternalImpl *stg, *stg2;
2437
2438 TRACE("%p,%d\n", parentStorage, indexToDelete);
2439
2440 /* Invalidate any open storage objects. */
2441 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2442 {
2443 if (stg->base.storageDirEntry == indexToDelete)
2444 {
2446 }
2447 }
2448
2449 /*
2450 * Open the storage and enumerate it
2451 */
2452 hr = IStorage_OpenStorage(
2453 &parentStorage->IStorage_iface,
2454 entryDataToDelete.name,
2455 0,
2457 0,
2458 0,
2459 &childStorage);
2460
2461 if (hr != S_OK)
2462 {
2463 TRACE("<-- %08x\n", hr);
2464 return hr;
2465 }
2466
2467 /*
2468 * Enumerate the elements
2469 */
2470 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2471 if (FAILED(hr))
2472 {
2473 IStorage_Release(childStorage);
2474 TRACE("<-- %08x\n", hr);
2475 return hr;
2476 }
2477
2478 do
2479 {
2480 /*
2481 * Obtain the next element
2482 */
2483 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2484 if (hr==S_OK)
2485 {
2486 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2487
2488 CoTaskMemFree(currentElement.pwcsName);
2489 }
2490
2491 /*
2492 * We need to Reset the enumeration every time because we delete elements
2493 * and the enumeration could be invalid
2494 */
2495 IEnumSTATSTG_Reset(elements);
2496
2497 } while ((hr == S_OK) && (destroyHr == S_OK));
2498
2499 IStorage_Release(childStorage);
2500 IEnumSTATSTG_Release(elements);
2501
2502 TRACE("%08x\n", hr);
2503 return destroyHr;
2504}
2505
2506/*********************************************************************
2507 *
2508 * Internal helper function for StorageBaseImpl_DestroyElement()
2509 *
2510 * Perform the deletion of a stream's data
2511 *
2512 */
2514 StorageBaseImpl *parentStorage,
2515 DirRef indexToDelete,
2516 DirEntry entryDataToDelete)
2517{
2518 IStream *pis;
2519 HRESULT hr;
2521 StgStreamImpl *strm, *strm2;
2522
2523 /* Invalidate any open stream objects. */
2524 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2525 {
2526 if (strm->dirEntry == indexToDelete)
2527 {
2528 TRACE("Stream deleted %p\n", strm);
2529 strm->parentStorage = NULL;
2530 list_remove(&strm->StrmListEntry);
2531 }
2532 }
2533
2534 size.u.HighPart = 0;
2535 size.u.LowPart = 0;
2536
2537 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2538 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2539
2540 if (hr!=S_OK)
2541 {
2542 TRACE("<-- %08x\n", hr);
2543 return(hr);
2544 }
2545
2546 /*
2547 * Zap the stream
2548 */
2549 hr = IStream_SetSize(pis, size);
2550
2551 if(hr != S_OK)
2552 {
2553 TRACE("<-- %08x\n", hr);
2554 return hr;
2555 }
2556
2557 /*
2558 * Release the stream object.
2559 */
2560 IStream_Release(pis);
2561 TRACE("<-- %08x\n", hr);
2562 return S_OK;
2563}
2564
2565/*************************************************************************
2566 * DestroyElement (IStorage)
2567 *
2568 * Strategy: This implementation is built this way for simplicity not for speed.
2569 * I always delete the topmost element of the enumeration and adjust
2570 * the deleted element pointer all the time. This takes longer to
2571 * do but allows reinvoking DestroyElement whenever we encounter a
2572 * storage object. The optimisation resides in the usage of another
2573 * enumeration strategy that would give all the leaves of a storage
2574 * first. (postfix order)
2575 */
2577 IStorage* iface,
2578 const OLECHAR *pwcsName)/* [string][in] */
2579{
2581
2582 HRESULT hr = S_OK;
2583 DirEntry entryToDelete;
2584 DirRef entryToDeleteRef;
2585
2586 TRACE("(%p, %s)\n",
2587 iface, debugstr_w(pwcsName));
2588
2589 if (pwcsName==NULL)
2590 return STG_E_INVALIDPOINTER;
2591
2592 if (This->reverted)
2593 return STG_E_REVERTED;
2594
2595 if ( !(This->openFlags & STGM_TRANSACTED) &&
2596 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2597 return STG_E_ACCESSDENIED;
2598
2599 entryToDeleteRef = findElement(
2600 This,
2601 This->storageDirEntry,
2602 pwcsName,
2603 &entryToDelete);
2604
2605 if ( entryToDeleteRef == DIRENTRY_NULL )
2606 {
2607 TRACE("<-- STG_E_FILENOTFOUND\n");
2608 return STG_E_FILENOTFOUND;
2609 }
2610
2611 if ( entryToDelete.stgType == STGTY_STORAGE )
2612 {
2614 This,
2615 entryToDeleteRef,
2616 entryToDelete);
2617 }
2618 else if ( entryToDelete.stgType == STGTY_STREAM )
2619 {
2621 This,
2622 entryToDeleteRef,
2623 entryToDelete);
2624 }
2625
2626 if (hr!=S_OK)
2627 {
2628 TRACE("<-- %08x\n", hr);
2629 return hr;
2630 }
2631
2632 /*
2633 * Remove the entry from its parent storage
2634 */
2636 This,
2637 This->storageDirEntry,
2638 entryToDeleteRef);
2639
2640 /*
2641 * Invalidate the entry
2642 */
2643 if (SUCCEEDED(hr))
2644 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2645
2646 if (SUCCEEDED(hr))
2648
2649 TRACE("<-- %08x\n", hr);
2650 return hr;
2651}
2652
2654{
2655 struct list *cur, *cur2;
2656 StgStreamImpl *strm=NULL;
2657 StorageInternalImpl *childstg=NULL;
2658
2659 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2660 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2661 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2662 strm->parentStorage = NULL;
2664 }
2665
2666 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2667 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2668 StorageBaseImpl_Invalidate( &childstg->base );
2669 }
2670
2671 if (stg->transactedChild)
2672 {
2674
2675 stg->transactedChild = NULL;
2676 }
2677}
2678
2679/******************************************************************************
2680 * SetElementTimes (IStorage)
2681 */
2683 IStorage* iface,
2684 const OLECHAR *pwcsName,/* [string][in] */
2685 const FILETIME *pctime, /* [in] */
2686 const FILETIME *patime, /* [in] */
2687 const FILETIME *pmtime) /* [in] */
2688{
2689 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2690 return S_OK;
2691}
2692
2693/******************************************************************************
2694 * SetStateBits (IStorage)
2695 */
2697 IStorage* iface,
2698 DWORD grfStateBits,/* [in] */
2699 DWORD grfMask) /* [in] */
2700{
2702
2703 if (This->reverted)
2704 return STG_E_REVERTED;
2705
2706 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2707 return S_OK;
2708}
2709
2710/******************************************************************************
2711 * Internal stream list handlers
2712 */
2713
2715{
2716 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2717 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2718}
2719
2721{
2722 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2723 list_remove(&(strm->StrmListEntry));
2724}
2725
2728 StorageBaseImpl *src, DirRef src_entry)
2729{
2730 HRESULT hr;
2731 BYTE data[4096];
2732 DirEntry srcdata;
2733 ULARGE_INTEGER bytes_copied;
2734 ULONG bytestocopy, bytesread, byteswritten;
2735
2736 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2737
2738 if (SUCCEEDED(hr))
2739 {
2741
2742 bytes_copied.QuadPart = 0;
2743 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2744 {
2745 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2746
2747 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2748 data, &bytesread);
2749 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2750
2751 if (SUCCEEDED(hr))
2752 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2753 data, &byteswritten);
2754 if (SUCCEEDED(hr))
2755 {
2756 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2757 bytes_copied.QuadPart += byteswritten;
2758 }
2759 }
2760 }
2761
2762 return hr;
2763}
2764
2767 StorageBaseImpl *src, DirRef src_entry)
2768{
2769 HRESULT hr;
2770 DirEntry data;
2771 BOOL has_stream=FALSE;
2772
2773 if (src_entry == DIRENTRY_NULL)
2774 {
2776 return S_OK;
2777 }
2778
2779 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2780 if (SUCCEEDED(hr))
2781 {
2782 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2783 data.startingBlock = BLOCK_END_OF_CHAIN;
2784 data.size.QuadPart = 0;
2785
2786 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2787 }
2788
2789 if (SUCCEEDED(hr))
2790 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2791
2792 if (SUCCEEDED(hr))
2793 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2794
2795 if (SUCCEEDED(hr))
2797
2798 if (SUCCEEDED(hr) && has_stream)
2800
2801 return hr;
2802}
2803
2806 StorageBaseImpl *src, DirRef src_entry)
2807{
2808 HRESULT hr;
2809 DirEntry src_data, dst_data;
2810 DirRef new_root_entry;
2811
2812 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2813
2814 if (SUCCEEDED(hr))
2815 {
2816 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2817 }
2818
2819 if (SUCCEEDED(hr))
2820 {
2822 dst_data.clsid = src_data.clsid;
2823 dst_data.ctime = src_data.ctime;
2824 dst_data.mtime = src_data.mtime;
2825 dst_data.dirRootEntry = new_root_entry;
2826 }
2827
2828 if (SUCCEEDED(hr))
2830
2831 return hr;
2832}
2833
2835{
2836 HRESULT hr;
2837 DirEntry data;
2839
2840 if (entry == DIRENTRY_NULL)
2841 return S_OK;
2842
2843 zero.QuadPart = 0;
2844
2846
2847 if (SUCCEEDED(hr) && include_siblings)
2849
2850 if (SUCCEEDED(hr) && include_siblings)
2852
2853 if (SUCCEEDED(hr))
2855
2856 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2858
2859 if (SUCCEEDED(hr))
2861
2862 return hr;
2863}
2864
2865
2866/************************************************************************
2867 * StorageImpl implementation
2868 ***********************************************************************/
2869
2872 void* buffer,
2873 ULONG size,
2874 ULONG* bytesRead)
2875{
2876 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2877}
2878
2881 const void* buffer,
2882 const ULONG size,
2884{
2885 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2886}
2887
2888/******************************************************************************
2889 * StorageImpl_LoadFileHeader
2890 *
2891 * This method will read in the file header
2892 */
2895{
2896 HRESULT hr;
2897 BYTE headerBigBlock[HEADER_SIZE];
2898 int index;
2900 DWORD bytes_read;
2901
2902 TRACE("\n");
2903 /*
2904 * Get a pointer to the big block of data containing the header.
2905 */
2906 offset.u.HighPart = 0;
2907 offset.u.LowPart = 0;
2908 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2909 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2911
2912 /*
2913 * Extract the information from the header.
2914 */
2915 if (SUCCEEDED(hr))
2916 {
2917 /*
2918 * Check for the "magic number" signature and return an error if it is not
2919 * found.
2920 */
2921 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922 {
2923 return STG_E_OLDFORMAT;
2924 }
2925
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2927 {
2928 return STG_E_INVALIDHEADER;
2929 }
2930
2932 headerBigBlock,
2934 &This->bigBlockSizeBits);
2935
2937 headerBigBlock,
2939 &This->smallBlockSizeBits);
2940
2942 headerBigBlock,
2944 &This->bigBlockDepotCount);
2945
2947 headerBigBlock,
2949 &This->rootStartBlock);
2950
2952 headerBigBlock,
2954 &This->transactionSig);
2955
2957 headerBigBlock,
2959 &This->smallBlockLimit);
2960
2962 headerBigBlock,
2964 &This->smallBlockDepotStart);
2965
2967 headerBigBlock,
2969 &This->extBigBlockDepotStart);
2970
2972 headerBigBlock,
2974 &This->extBigBlockDepotCount);
2975
2976 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2977 {
2979 headerBigBlock,
2980 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2981 &(This->bigBlockDepotStart[index]));
2982 }
2983
2984 /*
2985 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2986 */
2987 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2988 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2989
2990 /*
2991 * Right now, the code is making some assumptions about the size of the
2992 * blocks, just make sure they are what we're expecting.
2993 */
2994 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2995 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2996 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2997 {
2998 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2999 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3001 }
3002 else
3003 hr = S_OK;
3004 }
3005
3006 return hr;
3007}
3008
3009/******************************************************************************
3010 * StorageImpl_SaveFileHeader
3011 *
3012 * This method will save to the file the header
3013 */
3016{
3017 BYTE headerBigBlock[HEADER_SIZE];
3018 int index;
3019 HRESULT hr;
3021 DWORD bytes_read, bytes_written;
3022 DWORD major_version, dirsectorcount;
3023
3024 /*
3025 * Get a pointer to the big block of data containing the header.
3026 */
3027 offset.u.HighPart = 0;
3028 offset.u.LowPart = 0;
3029 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3030 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3032
3033 if (This->bigBlockSizeBits == 0x9)
3034 major_version = 3;
3035 else if (This->bigBlockSizeBits == 0xc)
3036 major_version = 4;
3037 else
3038 {
3039 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3040 major_version = 4;
3041 }
3042
3043 /*
3044 * If the block read failed, the file is probably new.
3045 */
3046 if (FAILED(hr))
3047 {
3048 /*
3049 * Initialize for all unknown fields.
3050 */
3051 memset(headerBigBlock, 0, HEADER_SIZE);
3052
3053 /*
3054 * Initialize the magic number.
3055 */
3056 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3057 }
3058
3059 /*
3060 * Write the information to the header.
3061 */
3063 headerBigBlock,
3065 0x3e);
3066
3068 headerBigBlock,
3070 major_version);
3071
3073 headerBigBlock,
3075 (WORD)-2);
3076
3078 headerBigBlock,
3080 This->bigBlockSizeBits);
3081
3083 headerBigBlock,
3085 This->smallBlockSizeBits);
3086
3087 if (major_version >= 4)
3088 {
3089 if (This->rootBlockChain)
3090 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3091 else
3092 /* This file is being created, and it will start out with one block. */
3093 dirsectorcount = 1;
3094 }
3095 else
3096 /* This field must be 0 in versions older than 4 */
3097 dirsectorcount = 0;
3098
3100 headerBigBlock,
3102 dirsectorcount);
3103
3105 headerBigBlock,
3107 This->bigBlockDepotCount);
3108
3110 headerBigBlock,
3112 This->rootStartBlock);
3113
3115 headerBigBlock,
3117 This->transactionSig);
3118
3120 headerBigBlock,
3122 This->smallBlockLimit);
3123
3125 headerBigBlock,
3127 This->smallBlockDepotStart);
3128
3130 headerBigBlock,
3132 This->smallBlockDepotChain ?
3133 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3134
3136 headerBigBlock,
3138 This->extBigBlockDepotStart);
3139
3141 headerBigBlock,
3143 This->extBigBlockDepotCount);
3144
3145 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3146 {
3148 headerBigBlock,
3149 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3150 (This->bigBlockDepotStart[index]));
3151 }
3152
3153 /*
3154 * Write the big block back to the file.
3155 */
3156 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3157}
3158
3159
3160/************************************************************************
3161 * StorageImpl implementation : DirEntry methods
3162 ***********************************************************************/
3163
3164/******************************************************************************
3165 * StorageImpl_ReadRawDirEntry
3166 *
3167 * This method will read the raw data from a directory entry in the file.
3168 *
3169 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3170 */
3172{
3174 HRESULT hr;
3175 ULONG bytesRead;
3176
3178
3180 This->rootBlockChain,
3181 offset,
3183 buffer,
3184 &bytesRead);
3185
3186 if (bytesRead != RAW_DIRENTRY_SIZE)
3187 return STG_E_READFAULT;
3188
3189 return hr;
3190}
3191
3192/******************************************************************************
3193 * StorageImpl_WriteRawDirEntry
3194 *
3195 * This method will write the raw data from a directory entry in the file.
3196 *
3197 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3198 */
3200{
3202 ULONG bytesRead;
3203
3205
3207 This->rootBlockChain,
3208 offset,
3210 buffer,
3211 &bytesRead);
3212}
3213
3214/***************************************************************************
3215 *
3216 * Internal Method
3217 *
3218 * Mark a directory entry in the file as free.
3219 */
3222 DirRef index)
3223{
3224 BYTE emptyData[RAW_DIRENTRY_SIZE];
3225 StorageImpl *storage = (StorageImpl*)base;
3226
3227 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3228
3229 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3230}
3231
3232/******************************************************************************
3233 * UpdateRawDirEntry
3234 *
3235 * Update raw directory entry data from the fields in newData.
3236 *
3237 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3238 */
3239static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3240{
3242
3243 memcpy(
3245 newData->name,
3247
3248 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3249
3251 buffer,
3253 newData->sizeOfNameString);
3254
3256 buffer,
3258 newData->leftChild);
3259
3261 buffer,
3263 newData->rightChild);
3264
3266 buffer,
3268 newData->dirRootEntry);
3269
3271 buffer,
3273 &newData->clsid);
3274
3276 buffer,
3278 newData->ctime.dwLowDateTime);
3279
3281 buffer,
3283 newData->ctime.dwHighDateTime);
3284
3286 buffer,
3288 newData->mtime.dwLowDateTime);
3289
3291 buffer,
3293 newData->ctime.dwHighDateTime);
3294
3296 buffer,
3298 newData->startingBlock);
3299
3301 buffer,
3303 newData->size.u.LowPart);
3304
3306 buffer,
3308 newData->size.u.HighPart);
3309}
3310
3311/***************************************************************************
3312 *
3313 * Internal Method
3314 *
3315 * Reserve a directory entry in the file and initialize it.
3316 */
3319 const DirEntry *newData,
3320 DirRef *index)
3321{
3322 StorageImpl *storage = (StorageImpl*)base;
3323 ULONG currentEntryIndex = 0;
3324 ULONG newEntryIndex = DIRENTRY_NULL;
3325 HRESULT hr = S_OK;
3326 BYTE currentData[RAW_DIRENTRY_SIZE];
3327 WORD sizeOfNameString;
3328
3329 do
3330 {
3332 currentEntryIndex,
3333 currentData);
3334
3335 if (SUCCEEDED(hr))
3336 {
3338 currentData,
3340 &sizeOfNameString);
3341
3342 if (sizeOfNameString == 0)
3343 {
3344 /*
3345 * The entry exists and is available, we found it.
3346 */
3347 newEntryIndex = currentEntryIndex;
3348 }
3349 }
3350 else
3351 {
3352 /*
3353 * We exhausted the directory entries, we will create more space below
3354 */
3355 newEntryIndex = currentEntryIndex;
3356 }
3357 currentEntryIndex++;
3358
3359 } while (newEntryIndex == DIRENTRY_NULL);
3360
3361 /*
3362 * grow the directory stream
3363 */
3364 if (FAILED(hr))
3365 {
3366 BYTE emptyData[RAW_DIRENTRY_SIZE];
3367 ULARGE_INTEGER newSize;
3368 ULONG entryIndex;
3369 ULONG lastEntry = 0;
3370 ULONG blockCount = 0;
3371
3372 /*
3373 * obtain the new count of blocks in the directory stream
3374 */
3375 blockCount = BlockChainStream_GetCount(
3376 storage->rootBlockChain)+1;
3377
3378 /*
3379 * initialize the size used by the directory stream
3380 */
3381 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3382
3383 /*
3384 * add a block to the directory stream
3385 */
3386 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3387
3388 /*
3389 * memset the empty entry in order to initialize the unused newly
3390 * created entries
3391 */
3392 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3393
3394 /*
3395 * initialize them
3396 */
3397 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3398
3399 for(
3400 entryIndex = newEntryIndex + 1;
3401 entryIndex < lastEntry;
3402 entryIndex++)
3403 {
3405 storage,
3406 entryIndex,
3407 emptyData);
3408 }
3409
3411 }
3412
3413 UpdateRawDirEntry(currentData, newData);
3414
3415 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3416
3417 if (SUCCEEDED(hr))
3418 *index = newEntryIndex;
3419
3420 return hr;
3421}
3422
3423/******************************************************************************
3424 * StorageImpl_ReadDirEntry
3425 *
3426 * This method will read the specified directory entry.
3427 */
3430 DirRef index,
3432{
3433 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3434 HRESULT readRes;
3435
3436 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3437
3438 if (SUCCEEDED(readRes))
3439 {
3440 memset(buffer->name, 0, sizeof(buffer->name));
3441 memcpy(
3442 buffer->name,
3443 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3445 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3446
3447 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3448
3450 currentEntry,
3452 &buffer->sizeOfNameString);
3453
3455 currentEntry,
3457 &buffer->leftChild);
3458
3460 currentEntry,
3462 &buffer->rightChild);
3463
3465 currentEntry,
3467 &buffer->dirRootEntry);
3468
3470 currentEntry,
3472 &buffer->clsid);
3473
3475 currentEntry,
3477 &buffer->ctime.dwLowDateTime);
3478
3480 currentEntry,
3482 &buffer->ctime.dwHighDateTime);
3483
3485 currentEntry,
3487 &buffer->mtime.dwLowDateTime);
3488
3490 currentEntry,
3492 &buffer->mtime.dwHighDateTime);
3493
3495 currentEntry,
3497 &buffer->startingBlock);
3498
3500 currentEntry,
3502 &buffer->size.u.LowPart);
3503
3504 if (This->bigBlockSize < 4096)
3505 {
3506 /* Version 3 files may have junk in the high part of size. */
3507 buffer->size.u.HighPart = 0;
3508 }
3509 else
3510 {
3512 currentEntry,
3514 &buffer->size.u.HighPart);
3515 }
3516 }
3517
3518 return readRes;
3519}
3520
3521/*********************************************************************
3522 * Write the specified directory entry to the file
3523 */
3526 DirRef index,
3527 const DirEntry* buffer)
3528{
3529 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3530
3531 UpdateRawDirEntry(currentEntry, buffer);
3532
3533 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3534}
3535
3536
3537/************************************************************************
3538 * StorageImpl implementation : Block methods
3539 ***********************************************************************/
3540
3542{
3543 return (ULONGLONG)(index+1) * This->bigBlockSize;
3544}
3545
3548 ULONG blockIndex,
3549 void* buffer,
3550 ULONG* out_read)
3551{
3552 ULARGE_INTEGER ulOffset;
3553 DWORD read=0;
3554 HRESULT hr;
3555
3556 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3557
3558 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3559
3560 if (SUCCEEDED(hr) && read < This->bigBlockSize)
3561 {
3562 /* File ends during this block; fill the rest with 0's. */
3563 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3564 }
3565
3566 if (out_read) *out_read = read;
3567
3568 return hr;
3569}
3570
3573 ULONG blockIndex,
3574 ULONG offset,
3575 DWORD* value)
3576{
3577 ULARGE_INTEGER ulOffset;
3578 DWORD read;
3579 DWORD tmp;
3580
3581 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3582 ulOffset.QuadPart += offset;
3583
3584 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3585 *value = lendian32toh(tmp);
3586 return (read == sizeof(DWORD));
3587}
3588
3591 ULONG blockIndex,
3592 const void* buffer)
3593{
3594 ULARGE_INTEGER ulOffset;
3595 DWORD wrote;
3596
3597 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3598
3599 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3600 return (wrote == This->bigBlockSize);
3601}
3602
3605 ULONG blockIndex,
3606 ULONG offset,
3607 DWORD value)
3608{
3609 ULARGE_INTEGER ulOffset;
3610 DWORD wrote;
3611
3612 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3613 ulOffset.QuadPart += offset;
3614
3615 value = htole32(value);
3616 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3617 return (wrote == sizeof(DWORD));
3618}
3619
3620/******************************************************************************
3621 * Storage32Impl_SmallBlocksToBigBlocks
3622 *
3623 * This method will convert a small block chain to a big block chain.
3624 * The small block chain will be destroyed.
3625 */
3628 SmallBlockChainStream** ppsbChain)
3629{
3630 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3632 ULONG cbRead, cbWritten;
3633 ULARGE_INTEGER cbTotalRead;
3634 DirRef streamEntryRef;
3635 HRESULT resWrite = S_OK;
3636 HRESULT resRead;
3637 DirEntry streamEntry;
3638 BYTE *buffer;
3639 BlockChainStream *bbTempChain = NULL;
3640 BlockChainStream *bigBlockChain = NULL;
3641
3642 /*
3643 * Create a temporary big block chain that doesn't have
3644 * an associated directory entry. This temporary chain will be
3645 * used to copy data from small blocks to big blocks.
3646 */
3647 bbTempChain = BlockChainStream_Construct(This,
3648 &bbHeadOfChain,
3650 if(!bbTempChain) return NULL;
3651 /*
3652 * Grow the big block chain.
3653 */
3654 size = SmallBlockChainStream_GetSize(*ppsbChain);
3655 BlockChainStream_SetSize(bbTempChain, size);
3656
3657 /*
3658 * Copy the contents of the small block chain to the big block chain
3659 * by small block size increments.
3660 */
3661 offset.u.LowPart = 0;
3662 offset.u.HighPart = 0;
3663 cbTotalRead.QuadPart = 0;
3664
3666 do
3667 {
3668 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3669 offset,
3670 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3671 buffer,
3672 &cbRead);
3673 if (FAILED(resRead))
3674 break;
3675
3676 if (cbRead > 0)
3677 {
3678 cbTotalRead.QuadPart += cbRead;
3679
3680 resWrite = BlockChainStream_WriteAt(bbTempChain,
3681 offset,
3682 cbRead,
3683 buffer,
3684 &cbWritten);
3685
3686 if (FAILED(resWrite))
3687 break;
3688
3689 offset.u.LowPart += cbRead;
3690 }
3691 else
3692 {
3693 resRead = STG_E_READFAULT;
3694 break;
3695 }
3696 } while (cbTotalRead.QuadPart < size.QuadPart);
3698
3699 size.u.HighPart = 0;
3700 size.u.LowPart = 0;
3701
3702 if (FAILED(resRead) || FAILED(resWrite))
3703 {
3704 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3705 BlockChainStream_SetSize(bbTempChain, size);
3706 BlockChainStream_Destroy(bbTempChain);
3707 return NULL;
3708 }
3709
3710 /*
3711 * Destroy the small block chain.
3712 */
3713 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3716 *ppsbChain = 0;
3717
3718 /*
3719 * Change the directory entry. This chain is now a big block chain
3720 * and it doesn't reside in the small blocks chain anymore.
3721 */
3722 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3723
3724 streamEntry.startingBlock = bbHeadOfChain;
3725
3726 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3727
3728 /*
3729 * Destroy the temporary entryless big block chain.
3730 * Create a new big block chain associated with this entry.
3731 */
3732 BlockChainStream_Destroy(bbTempChain);
3733 bigBlockChain = BlockChainStream_Construct(This,
3734 NULL,
3735 streamEntryRef);
3736
3737 return bigBlockChain;
3738}
3739
3740/******************************************************************************
3741 * Storage32Impl_BigBlocksToSmallBlocks
3742 *
3743 * This method will convert a big block chain to a small block chain.
3744 * The big block chain will be destroyed on success.
3745 */
3748 BlockChainStream** ppbbChain,
3749 ULARGE_INTEGER newSize)
3750{
3751 ULARGE_INTEGER size, offset, cbTotalRead;
3752 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3753 DirRef streamEntryRef;
3754 HRESULT resWrite = S_OK, resRead = S_OK;
3755 DirEntry streamEntry;
3756 BYTE* buffer;
3757 SmallBlockChainStream* sbTempChain;
3758
3759 TRACE("%p %p\n", This, ppbbChain);
3760
3761 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3763
3764 if(!sbTempChain)
3765 return NULL;
3766
3767 SmallBlockChainStream_SetSize(sbTempChain, newSize);
3768 size = BlockChainStream_GetSize(*ppbbChain);
3769 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3770
3771 offset.u.HighPart = 0;
3772 offset.u.LowPart = 0;
3773 cbTotalRead.QuadPart = 0;
3774 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3775 while(cbTotalRead.QuadPart < size.QuadPart)
3776 {
3777 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3778 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3779 buffer, &cbRead);
3780
3781 if(FAILED(resRead))
3782 break;
3783
3784 if(cbRead > 0)
3785 {
3786 cbTotalRead.QuadPart += cbRead;
3787
3788 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3789 cbRead, buffer, &cbWritten);
3790
3791 if(FAILED(resWrite))
3792 break;
3793
3794 offset.u.LowPart += cbRead;
3795 }
3796 else
3797 {
3798 resRead = STG_E_READFAULT;
3799 break;
3800 }
3801 }
3803
3804 size.u.HighPart = 0;
3805 size.u.LowPart = 0;
3806
3807 if(FAILED(resRead) || FAILED(resWrite))
3808 {
3809 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3810 SmallBlockChainStream_SetSize(sbTempChain, size);
3811 SmallBlockChainStream_Destroy(sbTempChain);
3812 return NULL;
3813 }
3814
3815 /* destroy the original big block chain */
3816 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3817 BlockChainStream_SetSize(*ppbbChain, size);
3818 BlockChainStream_Destroy(*ppbbChain);
3819 *ppbbChain = NULL;
3820
3821 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3822 streamEntry.startingBlock = sbHeadOfChain;
3823 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3824
3825 SmallBlockChainStream_Destroy(sbTempChain);
3826 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3827}
3828
3829/******************************************************************************
3830 * Storage32Impl_AddBlockDepot
3831 *
3832 * This will create a depot block, essentially it is a block initialized
3833 * to BLOCK_UNUSEDs.
3834 */
3835static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3836{
3837 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3838 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3839 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3840 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3841
3842 /*
3843 * Initialize blocks as free
3844 */
3845 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3846
3847 /* Reserve the range lock sector */
3848 if (depotIndex == rangeLockDepot)
3849 {
3850 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3851 }
3852
3853 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3854}
3855
3856/******************************************************************************
3857 * Storage32Impl_GetExtDepotBlock
3858 *
3859 * Returns the index of the block that corresponds to the specified depot
3860 * index. This method is only for depot indexes equal or greater than
3861 * COUNT_BBDEPOTINHEADER.
3862 */
3864{
3865 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3866 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3867 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3868 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3869 ULONG blockIndex = BLOCK_UNUSED;
3870 ULONG extBlockIndex;
3871 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3872 int index, num_blocks;
3873
3874 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3875
3876 if (extBlockCount >= This->extBigBlockDepotCount)
3877 return BLOCK_UNUSED;
3878
3879 if (This->indexExtBlockDepotCached != extBlockCount)
3880 {
3881 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3882
3883 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3884
3885 num_blocks = This->bigBlockSize / 4;
3886
3887 for (index = 0; index < num_blocks; index++)
3888 {
3889 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3890 This->extBlockDepotCached[index] = blockIndex;
3891 }
3892
3893 This->indexExtBlockDepotCached = extBlockCount;
3894 }
3895
3896 blockIndex = This->extBlockDepotCached[extBlockOffset];
3897
3898 return blockIndex;
3899}
3900
3901/******************************************************************************
3902 * Storage32Impl_SetExtDepotBlock
3903 *
3904 * Associates the specified block index to the specified depot index.
3905 * This method is only for depot indexes equal or greater than
3906 * COUNT_BBDEPOTINHEADER.
3907 */
3908static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3909{
3910 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3911 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3912 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3913 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3914 ULONG extBlockIndex;
3915
3916 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3917
3918 assert(extBlockCount < This->extBigBlockDepotCount);
3919
3920 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3921
3922 if (extBlockIndex != BLOCK_UNUSED)
3923 {
3925 extBlockOffset * sizeof(ULONG),
3926 blockIndex);
3927 }
3928
3929 if (This->indexExtBlockDepotCached == extBlockCount)
3930 {
3931 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3932 }
3933}
3934
3935/******************************************************************************
3936 * Storage32Impl_AddExtBlockDepot
3937 *
3938 * Creates an extended depot block.
3939 */
3941{
3942 ULONG numExtBlocks = This->extBigBlockDepotCount;
3943 ULONG nextExtBlock = This->extBigBlockDepotStart;
3944 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3946 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3947 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3948 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3949
3950 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3951 blocksPerDepotBlock;
3952
3953 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3954 {
3955 /*
3956 * The first extended block.
3957 */
3958 This->extBigBlockDepotStart = index;
3959 }
3960 else
3961 {
3962 /*
3963 * Find the last existing extended block.
3964 */
3965 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3966
3967 /*
3968 * Add the new extended block to the chain.
3969 */
3970 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3971 index);
3972 }
3973
3974 /*
3975 * Initialize this block.
3976 */
3977 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3978 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3979
3980 /* Add the block to our cache. */
3981 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3982 {
3983 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3984 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3985
3986 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3987 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3988
3989 This->extBigBlockDepotLocations = new_cache;
3990 This->extBigBlockDepotLocationsSize = new_cache_size;
3991 }
3992 This->extBigBlockDepotLocations[numExtBlocks] = index;
3993
3994 return index;
3995}
3996
3997/************************************************************************
3998 * StorageImpl_GetNextBlockInChain
3999 *
4000 * This method will retrieve the block index of the next big block in
4001 * in the chain.
4002 *
4003 * Params: This - Pointer to the Storage object.
4004 * blockIndex - Index of the block to retrieve the chain
4005 * for.
4006 * nextBlockIndex - receives the return value.
4007 *
4008 * Returns: This method returns the index of the next block in the chain.
4009 * It will return the constants:
4010 * BLOCK_SPECIAL - If the block given was not part of a
4011 * chain.
4012 * BLOCK_END_OF_CHAIN - If the block given was the last in
4013 * a chain.
4014 * BLOCK_UNUSED - If the block given was not past of a chain
4015 * and is available.
4016 * BLOCK_EXTBBDEPOT - This block is part of the extended
4017 * big block depot.
4018 *
4019 * See Windows documentation for more details on IStorage methods.
4020 */
4023 ULONG blockIndex,
4024 ULONG* nextBlockIndex)
4025{
4026 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4027 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4028 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4029 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4030 ULONG read;
4031 ULONG depotBlockIndexPos;
4032 int index, num_blocks;
4033
4034 *nextBlockIndex = BLOCK_SPECIAL;
4035
4036 if(depotBlockCount >= This->bigBlockDepotCount)
4037 {
4038 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
4039 This->bigBlockDepotCount);
4040 return STG_E_READFAULT;
4041 }
4042
4043 /*
4044 * Cache the currently accessed depot block.
4045 */
4046 if (depotBlockCount != This->indexBlockDepotCached)
4047 {
4048 This->indexBlockDepotCached = depotBlockCount;
4049
4050 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4051 {
4052 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4053 }
4054 else
4055 {
4056 /*
4057 * We have to look in the extended depot.
4058 */
4059 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4060 }
4061
4062 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4063
4064 if (!read)
4065 return STG_E_READFAULT;
4066
4067 num_blocks = This->bigBlockSize / 4;
4068
4069 for (index = 0; index < num_blocks; index++)
4070 {
4071 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4072 This->blockDepotCached[index] = *nextBlockIndex;
4073 }
4074 }
4075
4076 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4077
4078 return S_OK;
4079}
4080
4081/******************************************************************************
4082 * Storage32Impl_GetNextExtendedBlock
4083 *
4084 * Given an extended block this method will return the next extended block.
4085 *
4086 * NOTES:
4087 * The last ULONG of an extended block is the block index of the next
4088 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4089 * depot.
4090 *
4091 * Return values:
4092 * - The index of the next extended block
4093 * - BLOCK_UNUSED: there is no next extended block.
4094 * - Any other return values denotes failure.
4095 */
4097{
4098 ULONG nextBlockIndex = BLOCK_SPECIAL;
4099 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4100
4101 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4102 &nextBlockIndex);
4103
4104 return nextBlockIndex;
4105}
4106
4107/******************************************************************************
4108 * StorageImpl_SetNextBlockInChain
4109 *
4110 * This method will write the index of the specified block's next block
4111 * in the big block depot.
4112 *
4113 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4114 * do the following
4115 *
4116 * StorageImpl_SetNextBlockInChain(This, 3, 1);
4117 * StorageImpl_SetNextBlockInChain(This, 1, 7);
4118 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4119 *
4120 */
4123 ULONG blockIndex,
4124 ULONG nextBlock)
4125{
4126 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4127 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4128 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4129 ULONG depotBlockIndexPos;
4130
4131 assert(depotBlockCount < This->bigBlockDepotCount);
4132 assert(blockIndex != nextBlock);
4133
4134 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4135 /* This should never happen (storage file format spec forbids it), but
4136 * older versions of Wine may have generated broken files. We don't want to
4137 * assert and potentially lose data, but we do want to know if this ever
4138 * happens in a newly-created file. */
4139 ERR("Using range lock page\n");
4140
4141 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4142 {
4143 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4144 }
4145 else
4146 {
4147 /*
4148 * We have to look in the extended depot.
4149 */
4150 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4151 }
4152
4153 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4154 nextBlock);
4155 /*
4156 * Update the cached block depot, if necessary.
4157 */
4158 if (depotBlockCount == This->indexBlockDepotCached)
4159 {
4160 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4161 }
4162}
4163
4164/******************************************************************************
4165 * StorageImpl_GetNextFreeBigBlock
4166 *
4167 * Returns the index of the next free big block.
4168 * If the big block depot is filled, this method will enlarge it.
4169 *
4170 */
4173{
4174 ULONG depotBlockIndexPos;
4175 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4176 ULONG depotBlockOffset;
4177 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4178 ULONG nextBlockIndex = BLOCK_SPECIAL;
4179 int depotIndex = 0;
4180 ULONG freeBlock = BLOCK_UNUSED;
4181 ULONG read;
4182 ULARGE_INTEGER neededSize;
4183 STATSTG statstg;
4184
4185 depotIndex = This->prevFreeBlock / blocksPerDepot;
4186 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4187
4188 /*
4189 * Scan the entire big block depot until we find a block marked free
4190 */
4191 while (nextBlockIndex != BLOCK_UNUSED)
4192 {
4193 if (depotIndex < COUNT_BBDEPOTINHEADER)
4194 {
4195 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4196
4197 /*
4198 * Grow the primary depot.
4199 */
4200 if (depotBlockIndexPos == BLOCK_UNUSED)
4201 {
4202 depotBlockIndexPos = depotIndex*blocksPerDepot;
4203
4204 /*
4205 * Add a block depot.
4206 */
4207 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4208 This->bigBlockDepotCount++;
4209 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4210
4211 /*
4212 * Flag it as a block depot.
4213 */
4215 depotBlockIndexPos,
4217
4218 /* Save new header information.
4219 */
4221 }
4222 }
4223 else
4224 {
4225 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4226
4227 if (depotBlockIndexPos == BLOCK_UNUSED)
4228 {
4229 /*
4230 * Grow the extended depot.
4231 */
4232 ULONG extIndex = BLOCK_UNUSED;
4233 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4234 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4235
4236 if (extBlockOffset == 0)
4237 {
4238 /* We need an extended block.
4239 */
4241 This->extBigBlockDepotCount++;
4242 depotBlockIndexPos = extIndex + 1;
4243 }
4244 else
4245 depotBlockIndexPos = depotIndex * blocksPerDepot;
4246
4247 /*
4248 * Add a block depot and mark it in the extended block.
4249 */
4250 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4251 This->bigBlockDepotCount++;
4252 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4253
4254 /* Flag the block depot.
4255 */
4257 depotBlockIndexPos,
4259
4260 /* If necessary, flag the extended depot block.
4261 */
4262 if (extIndex != BLOCK_UNUSED)
4264
4265 /* Save header information.
4266 */
4268 }
4269 }
4270
4271 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4272
4273 if (read)
4274 {
4275 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4276 ( nextBlockIndex != BLOCK_UNUSED))
4277 {
4278 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4279
4280 if (nextBlockIndex == BLOCK_UNUSED)
4281 {
4282 freeBlock = (depotIndex * blocksPerDepot) +
4283 (depotBlockOffset/sizeof(ULONG));
4284 }
4285
4286 depotBlockOffset += sizeof(ULONG);
4287 }
4288 }
4289
4290 depotIndex++;
4291 depotBlockOffset = 0;
4292 }
4293
4294 /*
4295 * make sure that the block physically exists before using it
4296 */
4297 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4298
4299 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4300
4301 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4302 ILockBytes_SetSize(This->lockBytes, neededSize);
4303
4304 This->prevFreeBlock = freeBlock;
4305
4306 return freeBlock;
4307}
4308
4309/******************************************************************************
4310 * StorageImpl_FreeBigBlock
4311 *
4312 * This method will flag the specified block as free in the big block depot.
4313 */
4316 ULONG blockIndex)
4317{
4319
4320 if (blockIndex < This->prevFreeBlock)
4321 This->prevFreeBlock = blockIndex;
4322}
4323
4324
4326 DirRef index, const DirEntry *data)
4327{
4330}
4331
4334{
4337}
4338
4340{
4341 int i;
4342
4343 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4344 {
4345 if (!This->blockChainCache[i])
4346 {
4347 return &This->blockChainCache[i];
4348 }
4349 }
4350
4351 i = This->blockChainToEvict;
4352
4353 BlockChainStream_Destroy(This->blockChainCache[i]);
4354 This->blockChainCache[i] = NULL;
4355
4356 This->blockChainToEvict++;
4357 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4358 This->blockChainToEvict = 0;
4359
4360 return &This->blockChainCache[i];
4361}
4362
4364 DirRef index)
4365{
4366 int i, free_index=-1;
4367
4368 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4369 {
4370 if (!This->blockChainCache[i])
4371 {
4372 if (free_index == -1) free_index = i;
4373 }
4374 else if (This->blockChainCache[i]->ownerDirEntry == index)
4375 {
4376 return &This->blockChainCache[i];
4377 }
4378 }
4379
4380 if (free_index == -1)
4381 {
4382 free_index = This->blockChainToEvict;
4383
4384 BlockChainStream_Destroy(This->blockChainCache[free_index]);
4385 This->blockChainCache[free_index] = NULL;
4386
4387 This->blockChainToEvict++;
4388 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4389 This->blockChainToEvict = 0;
4390 }
4391
4392 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4393 return &This->blockChainCache[free_index];
4394}
4395
4397{
4398 int i;
4399
4400 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4401 {
4402 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4403 {
4404 BlockChainStream_Destroy(This->blockChainCache[i]);
4405 This->blockChainCache[i] = NULL;
4406 return;
4407 }
4408 }
4409}
4410
4412 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4413{
4415 DirEntry data;
4416 HRESULT hr;
4417 ULONG bytesToRead;
4418
4420 if (FAILED(hr)) return hr;
4421
4422 if (data.size.QuadPart == 0)
4423 {
4424 *bytesRead = 0;
4425 return S_OK;
4426 }
4427
4428 if (offset.QuadPart + size > data.size.QuadPart)
4429 {
4430 bytesToRead = data.size.QuadPart - offset.QuadPart;
4431 }
4432 else
4433 {
4434 bytesToRead = size;
4435 }
4436
4437 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4438 {
4440
4442 if (!stream) return E_OUTOFMEMORY;
4443
4444 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4445
4447
4448 return hr;
4449 }
4450 else
4451 {
4453
4455 if (!stream) return E_OUTOFMEMORY;
4456
4457 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4458
4459 return hr;
4460 }
4461}
4462
4464 ULARGE_INTEGER newsize)
4465{
4467 DirEntry data;
4468 HRESULT hr;
4469 SmallBlockChainStream *smallblock=NULL;
4470 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4471
4473 if (FAILED(hr)) return hr;
4474
4475 /* In simple mode keep the stream size above the small block limit */
4476 if (This->base.openFlags & STGM_SIMPLE)
4477 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4478
4479 if (data.size.QuadPart == newsize.QuadPart)
4480 return S_OK;
4481
4482 /* Create a block chain object of the appropriate type */
4483 if (data.size.QuadPart == 0)
4484 {
4485 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4486 {
4488 if (!smallblock) return E_OUTOFMEMORY;
4489 }
4490 else
4491 {
4493 bigblock = *pbigblock;
4494 if (!bigblock) return E_OUTOFMEMORY;
4495 }
4496 }
4497 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4498 {
4500 if (!smallblock) return E_OUTOFMEMORY;
4501 }
4502 else
4503 {
4505 bigblock = *pbigblock;
4506 if (!bigblock) return E_OUTOFMEMORY;
4507 }
4508
4509 /* Change the block chain type if necessary. */
4510 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4511 {
4512 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4513 if (!bigblock)
4514 {
4516 return E_FAIL;
4517 }
4518
4520 *pbigblock = bigblock;
4521 }
4522 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4523 {
4524 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4525 if (!smallblock)
4526 return E_FAIL;
4527 }
4528
4529 /* Set the size of the block chain. */
4530 if (smallblock)
4531 {
4532 SmallBlockChainStream_SetSize(smallblock, newsize);
4534 }
4535 else
4536 {
4537 BlockChainStream_SetSize(bigblock, newsize);
4538 }
4539
4540 /* Set the size in the directory entry. */
4542 if (SUCCEEDED(hr))
4543 {
4544 data.size = newsize;
4545
4547 }
4548 return hr;
4549}
4550
4553{
4555 DirEntry data;
4556 HRESULT hr;
4557 ULARGE_INTEGER newSize;
4558
4560 if (FAILED(hr)) return hr;
4561
4562 /* Grow the stream if necessary */
4563 newSize.QuadPart = offset.QuadPart + size;
4564
4565 if (newSize.QuadPart > data.size.QuadPart)
4566 {
4568 if (FAILED(hr))
4569 return hr;
4570
4572 if (FAILED(hr)) return hr;
4573 }
4574
4575 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4576 {
4578
4580 if (!stream) return E_OUTOFMEMORY;
4581
4583
4585
4586 return hr;
4587 }
4588 else
4589 {
4591
4593 if (!stream) return E_OUTOFMEMORY;
4594
4596 }
4597}
4598
4600 DirRef src)
4601{
4603 DirEntry dst_data, src_data;
4604 HRESULT hr;
4605
4606 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4607
4608 if (SUCCEEDED(hr))
4609 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4610
4611 if (SUCCEEDED(hr))
4612 {
4614 dst_data.startingBlock = src_data.startingBlock;
4615 dst_data.size = src_data.size;
4616
4617 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4618 }
4619
4620 return hr;
4621}
4622
4624{
4625 HRESULT hr=S_OK;
4626 DirEntry currentEntry;
4627 DirRef currentEntryRef;
4628 BlockChainStream *blockChainStream;
4629
4630 if (create)
4631 {
4633 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4634
4635 /* Discard any existing data. */
4636 size.QuadPart = 0;
4637 ILockBytes_SetSize(This->lockBytes, size);
4638
4639 /*
4640 * Initialize all header variables:
4641 * - The big block depot consists of one block and it is at block 0
4642 * - The directory table starts at block 1
4643 * - There is no small block depot
4644 */
4645 memset( This->bigBlockDepotStart,
4647 sizeof(This->bigBlockDepotStart));
4648
4649 This->bigBlockDepotCount = 1;
4650 This->bigBlockDepotStart[0] = 0;
4651 This->rootStartBlock = 1;
4652 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4653 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4654 if (This->bigBlockSize == 4096)
4655 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4656 else
4657 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4658 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4659 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4660 This->extBigBlockDepotCount = 0;
4661
4663
4664 /*
4665 * Add one block for the big block depot and one block for the directory table
4666 */
4667 size.u.HighPart = 0;
4668 size.u.LowPart = This->bigBlockSize * 3;
4669 ILockBytes_SetSize(This->lockBytes, size);
4670
4671 /*
4672 * Initialize the big block depot
4673 */
4674 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4675 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4676 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4677 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4678 }
4679 else
4680 {
4681 /*
4682 * Load the header for the file.
4683 */
4685
4686 if (FAILED(hr))
4687 {
4688 return hr;
4689 }
4690 }
4691
4692 /*
4693 * There is no block depot cached yet.
4694 */
4695 This->indexBlockDepotCached = 0xFFFFFFFF;
4696 This->indexExtBlockDepotCached = 0xFFFFFFFF;
4697
4698 /*
4699 * Start searching for free blocks with block 0.
4700 */
4701 This->prevFreeBlock = 0;
4702
4703 This->firstFreeSmallBlock = 0;
4704
4705 /* Read the extended big block depot locations. */
4706 if (This->extBigBlockDepotCount != 0)
4707 {
4708 ULONG current_block = This->extBigBlockDepotStart;
4709 ULONG cache_size = This->extBigBlockDepotCount * 2;
4710 ULONG i;
4711
4712 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4713 if (!This->extBigBlockDepotLocations)
4714 {
4715 return E_OUTOFMEMORY;
4716 }
4717
4718 This->extBigBlockDepotLocationsSize = cache_size;
4719
4720 for (i=0; i<This->extBigBlockDepotCount; i++)
4721 {
4722 if (current_block == BLOCK_END_OF_CHAIN)
4723 {
4724 WARN("File has too few extended big block depot blocks.\n");
4725 return STG_E_DOCFILECORRUPT;
4726 }
4727 This->extBigBlockDepotLocations[i] = current_block;
4728 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4729 }
4730 }
4731 else
4732 {
4733 This->extBigBlockDepotLocations = NULL;
4734 This->extBigBlockDepotLocationsSize = 0;
4735 }
4736
4737 /*
4738 * Create the block chain abstractions.
4739 */
4740 if(!(blockChainStream =
4742 {
4743 return STG_E_READFAULT;
4744 }
4745 if (!new_object)
4746 BlockChainStream_Destroy(This->rootBlockChain);
4747 This->rootBlockChain = blockChainStream;
4748
4749 if(!(blockChainStream =
4750 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4751 DIRENTRY_NULL)))
4752 {
4753 return STG_E_READFAULT;
4754 }
4755 if (!new_object)
4756 BlockChainStream_Destroy(This->smallBlockDepotChain);
4757 This->smallBlockDepotChain = blockChainStream;
4758
4759 /*
4760 * Write the root storage entry (memory only)
4761 */
4762 if (create)
4763 {
4764 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4765 DirEntry rootEntry;
4766 /*
4767 * Initialize the directory table
4768 */
4769 memset(&rootEntry, 0, sizeof(rootEntry));
4770 lstrcpyW(rootEntry.name, rootentryW);
4771 rootEntry.sizeOfNameString = sizeof(rootentryW);
4772 rootEntry.stgType = STGTY_ROOT;
4773 rootEntry.leftChild = DIRENTRY_NULL;
4774 rootEntry.rightChild = DIRENTRY_NULL;
4775 rootEntry.dirRootEntry = DIRENTRY_NULL;
4777 rootEntry.size.u.HighPart = 0;
4778 rootEntry.size.u.LowPart = 0;
4779
4780 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4781 }
4782
4783 /*
4784 * Find the ID of the root storage.
4785 */
4786 currentEntryRef = 0;
4787
4788 do
4789 {
4791 This,
4792 currentEntryRef,
4793 &currentEntry);
4794
4795 if (SUCCEEDED(hr))
4796 {
4797 if ( (currentEntry.sizeOfNameString != 0 ) &&
4798 (currentEntry.stgType == STGTY_ROOT) )
4799 {
4800 This->base.storageDirEntry = currentEntryRef;
4801 }
4802 }
4803
4804 currentEntryRef++;
4805
4806 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4807
4808 if (FAILED(hr))
4809 {
4810 return STG_E_READFAULT;
4811 }
4812
4813 /*
4814 * Create the block chain abstraction for the small block root chain.
4815 */
4816 if(!(blockChainStream =
4817 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4818 {
4819 return STG_E_READFAULT;
4820 }
4821 if (!new_object)
4822 BlockChainStream_Destroy(This->smallBlockRootChain);
4823 This->smallBlockRootChain = blockChainStream;
4824
4825 if (!new_object)
4826 {
4827 int i;
4828 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4829 {
4830 BlockChainStream_Destroy(This->blockChainCache[i]);
4831 This->blockChainCache[i] = NULL;
4832 }
4833 }
4834
4835 return hr;
4836}
4837
4839 ULONG* result, BOOL refresh)
4840{
4842 HRESULT hr=S_OK;
4843 DWORD oldTransactionSig = This->transactionSig;
4844
4845 if (refresh)
4846 {
4848 ULONG bytes_read;
4849 BYTE data[4];
4850
4851 offset.u.HighPart = 0;
4852 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4853 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4854
4855 if (SUCCEEDED(hr))
4856 {
4857 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4858
4859 if (oldTransactionSig != This->transactionSig)
4860 {
4861 /* Someone else wrote to this, so toss all cached information. */
4862 TRACE("signature changed\n");
4863
4865 }
4866
4867 if (FAILED(hr))
4868 This->transactionSig = oldTransactionSig;
4869 }
4870 }
4871
4872 *result = This->transactionSig;
4873
4874 return hr;
4875}
4876
4878 ULONG value)
4879{
4881
4882 This->transactionSig = value;
4884
4885 return S_OK;
4886}
4887
4889 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4890{
4891 if ((dwLockType & This->locks_supported) == 0)
4892 {
4893 if (supported) *supported = FALSE;
4894 return S_OK;
4895 }
4896
4897 if (supported) *supported = TRUE;
4898 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4899}
4900
4902 ULARGE_INTEGER cb, DWORD dwLockType)
4903{
4904 if ((dwLockType & This->locks_supported) == 0)
4905 return S_OK;
4906
4907 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4908}
4909
4910/* Internal function */
4912 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4913{
4914 HRESULT hr;
4915 int delay = 0;
4916 DWORD start_time = GetTickCount();
4917 DWORD last_sanity_check = start_time;
4918 ULARGE_INTEGER sanity_offset, sanity_cb;
4919
4920 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4922
4923 do
4924 {
4925 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4926
4927 if (hr ==