xref: /reactos/dll/win32/ole32/storage32.c (revision 0f5d91b7)
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 
55 WINE_DEFAULT_DEBUG_CHANNEL(storage);
56 
57 
58 /*
59  * These are signatures to detect the type of Document file.
60  */
61 static const BYTE STORAGE_magic[8]    ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
62 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
63 
64 extern 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  */
74 typedef struct StorageInternalImpl
75 {
76   struct StorageBaseImpl base;
77 
78   /*
79    * Entry in the parent's stream tracking list
80    */
81   struct list ParentListEntry;
82 
83   StorageBaseImpl *parentStorage;
84 } StorageInternalImpl;
85 
86 static const IStorageVtbl StorageInternalImpl_Vtbl;
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
88 
89 typedef 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. */
93   DirRef transactedParentEntry;
94 
95   /* True if this entry is being used. */
96   BOOL inuse;
97 
98   /* True if data is up to date. */
99   BOOL read;
100 
101   /* True if this entry has been modified. */
102   BOOL dirty;
103 
104   /* True if this entry's stream has been modified. */
105   BOOL stream_dirty;
106 
107   /* True if this entry has been deleted in the transacted storage, but the
108    * delete has not yet been committed. */
109   BOOL deleted;
110 
111   /* If this entry's stream has been modified, a reference to where the stream
112    * is stored in the snapshot file. */
113   DirRef stream_entry;
114 
115   /* This directory entry's data, including any changes that have been made. */
116   DirEntry data;
117 
118   /* A reference to the parent of this node. This is only valid while we are
119    * committing changes. */
120   DirRef parent;
121 
122   /* A reference to a newly-created entry in the transacted parent. This is
123    * always equal to transactedParentEntry except when committing changes. */
124   DirRef newTransactedParentEntry;
125 } TransactedDirEntry;
126 
127 
128 /****************************************************************************
129  * Transacted storage object.
130  */
131 typedef struct TransactedSnapshotImpl
132 {
133   struct StorageBaseImpl base;
134 
135   /*
136    * Modified streams are temporarily saved to the scratch file.
137    */
138   StorageBaseImpl *scratch;
139 
140   /* The directory structure is kept here, so that we can track how these
141    * entries relate to those in the parent storage. */
142   TransactedDirEntry *entries;
143   ULONG entries_size;
144   ULONG firstFreeEntry;
145 
146   /*
147    * Changes are committed to the transacted parent.
148    */
149   StorageBaseImpl *transactedParent;
150 
151   /* The transaction signature from when we last committed */
152   ULONG lastTransactionSig;
153 } TransactedSnapshotImpl;
154 
155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
156 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
157 
158 typedef struct TransactedSharedImpl
159 {
160   struct StorageBaseImpl base;
161 
162   /*
163    * Snapshot and uncommitted changes go here.
164    */
165   TransactedSnapshotImpl *scratch;
166 
167   /*
168    * Changes are committed to the transacted parent.
169    */
170   StorageBaseImpl *transactedParent;
171 
172   /* The transaction signature from when we last committed */
173   ULONG lastTransactionSig;
174 } TransactedSharedImpl;
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 
184 struct BlockChainRun
185 {
186   /* This represents a range of blocks that happen reside in consecutive sectors. */
187   ULONG firstSector;
188   ULONG firstOffset;
189   ULONG lastOffset;
190 };
191 
192 typedef struct BlockChainBlock
193 {
194   ULONG index;
195   ULONG sector;
196   BOOL  read;
197   BOOL  dirty;
198   BYTE data[MAX_BIG_BLOCK_SIZE];
199 } BlockChainBlock;
200 
201 struct BlockChainStream
202 {
203   StorageImpl* parentStorage;
204   ULONG*       headOfStreamPlaceHolder;
205   DirRef       ownerDirEntry;
206   struct BlockChainRun* indexCache;
207   ULONG        indexCacheLen;
208   ULONG        indexCacheSize;
209   BlockChainBlock cachedBlocks[2];
210   ULONG        blockToEvict;
211   ULONG        tailIndex;
212   ULONG        numBlocks;
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  */
BlockChainStream_GetCount(BlockChainStream * This)218 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
219 {
220   return This->numBlocks;
221 }
222 
223 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
224 static void BlockChainStream_Destroy(BlockChainStream*);
225 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
226 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
227 static HRESULT BlockChainStream_Flush(BlockChainStream*);
228 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
229 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);
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 
239 struct SmallBlockChainStream
240 {
241   StorageImpl* parentStorage;
242   DirRef         ownerDirEntry;
243   ULONG*         headOfStreamPlaceHolder;
244 };
245 
246 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
247 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
248 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
249 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
250 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
251 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
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  */
validateSTGM(DWORD stgm)287 static HRESULT validateSTGM(DWORD stgm)
288 {
289   DWORD access = STGM_ACCESS_MODE(stgm);
290   DWORD share  = STGM_SHARE_MODE(stgm);
291   DWORD create = STGM_CREATE_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   {
311   case STGM_SHARE_DENY_NONE:
312   case STGM_SHARE_DENY_READ:
313   case STGM_SHARE_DENY_WRITE:
314   case STGM_SHARE_EXCLUSIVE:
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  */
GetShareModeFromSTGM(DWORD stgm)371 static DWORD GetShareModeFromSTGM(DWORD stgm)
372 {
373   switch (STGM_SHARE_MODE(stgm))
374   {
375   case 0:
376     assert(stgm & STGM_TRANSACTED);
377     /* fall-through */
378   case STGM_SHARE_DENY_NONE:
379     return FILE_SHARE_READ | FILE_SHARE_WRITE;
380   case STGM_SHARE_DENY_READ:
381     return FILE_SHARE_WRITE;
382   case STGM_SHARE_DENY_WRITE:
383   case STGM_SHARE_EXCLUSIVE:
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  */
GetAccessModeFromSTGM(DWORD stgm)397 static DWORD GetAccessModeFromSTGM(DWORD stgm)
398 {
399   switch (STGM_ACCESS_MODE(stgm))
400   {
401   case STGM_READ:
402     return GENERIC_READ;
403   case STGM_WRITE:
404   case STGM_READWRITE:
405     return GENERIC_READ | GENERIC_WRITE;
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  */
GetCreationModeFromSTGM(DWORD stgm)418 static DWORD GetCreationModeFromSTGM(DWORD stgm)
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 
impl_from_IDirectWriterLock(IDirectWriterLock * iface)440 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
441 {
442     return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
443 }
444 
directwriterlock_QueryInterface(IDirectWriterLock * iface,REFIID riid,void ** obj)445 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
446 {
447   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
448   return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
449 }
450 
directwriterlock_AddRef(IDirectWriterLock * iface)451 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
452 {
453   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
454   return IStorage_AddRef(&This->IStorage_iface);
455 }
456 
directwriterlock_Release(IDirectWriterLock * iface)457 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
458 {
459   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
460   return IStorage_Release(&This->IStorage_iface);
461 }
462 
directwriterlock_WaitForWriteAccess(IDirectWriterLock * iface,DWORD timeout)463 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
464 {
465   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
466   FIXME("(%p)->(%d): stub\n", This, timeout);
467   return E_NOTIMPL;
468 }
469 
directwriterlock_ReleaseWriteAccess(IDirectWriterLock * iface)470 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
471 {
472   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
473   FIXME("(%p): stub\n", This);
474   return E_NOTIMPL;
475 }
476 
directwriterlock_HaveWriteAccess(IDirectWriterLock * iface)477 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
478 {
479   StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
480   FIXME("(%p): stub\n", This);
481   return E_NOTIMPL;
482 }
483 
484 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
485 {
486   directwriterlock_QueryInterface,
487   directwriterlock_AddRef,
488   directwriterlock_Release,
489   directwriterlock_WaitForWriteAccess,
490   directwriterlock_ReleaseWriteAccess,
491   directwriterlock_HaveWriteAccess
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  */
entryNameCmp(const OLECHAR * name1,const OLECHAR * name2)510 static LONG entryNameCmp(
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  */
findElement(StorageBaseImpl * storage,DirRef storageEntry,const OLECHAR * name,DirEntry * data)533 static 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 
549     cmp = entryNameCmp(name, data->name);
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  */
findTreeParent(StorageBaseImpl * storage,DirRef storageEntry,const OLECHAR * childName,DirEntry * parentData,DirRef * parentEntry,ULONG * relation)574 static 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 
setEntryLink(DirEntry * entry,ULONG relation,DirRef new_target)626 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
627 {
628   switch (relation)
629   {
630     case DIRENTRY_RELATION_PREVIOUS:
631       entry->leftChild = new_target;
632       break;
633     case DIRENTRY_RELATION_NEXT:
634       entry->rightChild = new_target;
635       break;
636     case DIRENTRY_RELATION_DIR:
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  */
insertIntoTree(StorageBaseImpl * This,DirRef parentStorageIndex,DirRef newEntryIndex)650 static HRESULT insertIntoTree(
651   StorageBaseImpl *This,
652   DirRef        parentStorageIndex,
653   DirRef        newEntryIndex)
654 {
655   DirEntry currentEntry;
656   DirEntry newEntry;
657 
658   /*
659    * Read the inserted entry
660    */
661   StorageBaseImpl_ReadDirEntry(This,
662                                newEntryIndex,
663                                &newEntry);
664 
665   /*
666    * Read the storage entry
667    */
668   StorageBaseImpl_ReadDirEntry(This,
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      */
689     StorageBaseImpl_ReadDirEntry(This,
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         {
705           StorageBaseImpl_ReadDirEntry(This,
706                                        previous,
707                                        &currentEntry);
708           current = previous;
709         }
710         else
711         {
712           currentEntry.leftChild = newEntryIndex;
713           StorageBaseImpl_WriteDirEntry(This,
714                                         current,
715                                         &currentEntry);
716           found = TRUE;
717         }
718       }
719       else if (diff > 0)
720       {
721         if (next != DIRENTRY_NULL)
722         {
723           StorageBaseImpl_ReadDirEntry(This,
724                                        next,
725                                        &currentEntry);
726           current = next;
727         }
728         else
729         {
730           currentEntry.rightChild = newEntryIndex;
731           StorageBaseImpl_WriteDirEntry(This,
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 	 */
743 	return STG_E_FILEALREADYEXISTS;
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;
756     StorageBaseImpl_WriteDirEntry(This,
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  */
removeFromTree(StorageBaseImpl * This,DirRef parentStorageIndex,DirRef deletedIndex)771 static HRESULT removeFromTree(
772   StorageBaseImpl *This,
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 
803     hr = StorageBaseImpl_WriteDirEntry(
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       {
824         hr = StorageBaseImpl_ReadDirEntry(
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 
839       hr = StorageBaseImpl_WriteDirEntry(
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 
856     hr = StorageBaseImpl_WriteDirEntry(
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  */
881 struct IEnumSTATSTGImpl
882 {
883   IEnumSTATSTG   IEnumSTATSTG_iface;
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 
impl_from_IEnumSTATSTG(IEnumSTATSTG * iface)892 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
893 {
894   return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
895 }
896 
IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl * This)897 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
898 {
899   IStorage_Release(&This->parentStorage->IStorage_iface);
900   HeapFree(GetProcessHeap(), 0, This);
901 }
902 
IEnumSTATSTGImpl_QueryInterface(IEnumSTATSTG * iface,REFIID riid,void ** ppvObject)903 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
904   IEnumSTATSTG*     iface,
905   REFIID            riid,
906   void**            ppvObject)
907 {
908   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
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 
917   if (IsEqualGUID(&IID_IUnknown, riid) ||
918       IsEqualGUID(&IID_IEnumSTATSTG, riid))
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 
IEnumSTATSTGImpl_AddRef(IEnumSTATSTG * iface)930 static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
931   IEnumSTATSTG* iface)
932 {
933   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
934   return InterlockedIncrement(&This->ref);
935 }
936 
IEnumSTATSTGImpl_Release(IEnumSTATSTG * iface)937 static ULONG   WINAPI IEnumSTATSTGImpl_Release(
938   IEnumSTATSTG* iface)
939 {
940   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
941 
942   ULONG newRef;
943 
944   newRef = InterlockedDecrement(&This->ref);
945 
946   if (newRef==0)
947   {
948     IEnumSTATSTGImpl_Destroy(This);
949   }
950 
951   return newRef;
952 }
953 
IEnumSTATSTGImpl_GetNextRef(IEnumSTATSTGImpl * This,DirRef * ref)954 static HRESULT IEnumSTATSTGImpl_GetNextRef(
955   IEnumSTATSTGImpl* This,
956   DirRef *ref)
957 {
958   DirRef result = DIRENTRY_NULL;
959   DirRef searchNode;
960   DirEntry entry;
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 
IEnumSTATSTGImpl_Next(IEnumSTATSTG * iface,ULONG celt,STATSTG * rgelt,ULONG * pceltFetched)1002 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
1003   IEnumSTATSTG* iface,
1004   ULONG             celt,
1005   STATSTG*          rgelt,
1006   ULONG*            pceltFetched)
1007 {
1008   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
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      */
1061     StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
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 
IEnumSTATSTGImpl_Skip(IEnumSTATSTG * iface,ULONG celt)1081 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
1082   IEnumSTATSTG* iface,
1083   ULONG             celt)
1084 {
1085   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
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 
IEnumSTATSTGImpl_Reset(IEnumSTATSTG * iface)1116 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
1117   IEnumSTATSTG* iface)
1118 {
1119   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
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 
1134 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1135 
IEnumSTATSTGImpl_Clone(IEnumSTATSTG * iface,IEnumSTATSTG ** ppenum)1136 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
1137   IEnumSTATSTG* iface,
1138   IEnumSTATSTG**    ppenum)
1139 {
1140   IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
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  */
1176 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1177 {
1178     IEnumSTATSTGImpl_QueryInterface,
1179     IEnumSTATSTGImpl_AddRef,
1180     IEnumSTATSTGImpl_Release,
1181     IEnumSTATSTGImpl_Next,
1182     IEnumSTATSTGImpl_Skip,
1183     IEnumSTATSTGImpl_Reset,
1184     IEnumSTATSTGImpl_Clone
1185 };
1186 
IEnumSTATSTGImpl_Construct(StorageBaseImpl * parentStorage,DirRef storageDirEntry)1187 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
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 
impl_from_IStorage(IStorage * iface)1219 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
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  */
StorageBaseImpl_QueryInterface(IStorage * iface,REFIID riid,void ** ppvObject)1232 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
1233   IStorage*        iface,
1234   REFIID             riid,
1235   void**             ppvObject)
1236 {
1237   StorageBaseImpl *This = impl_from_IStorage(iface);
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  */
StorageBaseImpl_AddRef(IStorage * iface)1279 static ULONG WINAPI StorageBaseImpl_AddRef(
1280             IStorage* iface)
1281 {
1282   StorageBaseImpl *This = impl_from_IStorage(iface);
1283   ULONG ref = InterlockedIncrement(&This->ref);
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  */
StorageBaseImpl_Release(IStorage * iface)1298 static ULONG WINAPI StorageBaseImpl_Release(
1299       IStorage* iface)
1300 {
1301   StorageBaseImpl *This = impl_from_IStorage(iface);
1302 
1303   ULONG ref = InterlockedDecrement(&This->ref);
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      */
1314     StorageBaseImpl_Destroy(This);
1315   }
1316 
1317   return ref;
1318 }
1319 
1320 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1321     DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1322     SNB snbExclude, IStorage *pstgDest);
1323 
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl * This,DirRef srcEntry,BOOL skip_storage,BOOL skip_stream,SNB snbExclude,IStorage * pstgDest)1324 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
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,
1363                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1364                                    0, 0,
1365                                    &pstgTmp );
1366 
1367       /*
1368        * if it already exist, don't create a new one use this one
1369        */
1370       if (hr == STG_E_FILEALREADYEXISTS)
1371       {
1372         hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1373                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
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,
1392                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
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       {
1401         StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
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 
StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg,DirRef streamEntry)1454 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
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 
StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg,DirRef storageEntry)1471 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
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  */
StorageBaseImpl_OpenStream(IStorage * iface,const OLECHAR * pwcsName,void * reserved1,DWORD grfMode,DWORD reserved2,IStream ** ppstm)1495 static HRESULT WINAPI StorageBaseImpl_OpenStream(
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 {
1503   StorageBaseImpl *This = impl_from_IStorage(iface);
1504   StgStreamImpl*    newStream;
1505   DirEntry          currentEntry;
1506   DirRef            streamEntryRef;
1507   HRESULT           res = STG_E_UNKNOWN;
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) ) ||
1521        STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1522   {
1523     res = STG_E_INVALIDFLAG;
1524     goto end;
1525   }
1526 
1527   /*
1528    * As documented.
1529    */
1530   if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1531   {
1532     res = STG_E_INVALIDFUNCTION;
1533     goto end;
1534   }
1535 
1536   if (This->reverted)
1537   {
1538     res = STG_E_REVERTED;
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     {
1549       res = STG_E_INVALIDFLAG;
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. */
1572       res = STG_E_ACCESSDENIED;
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 
1589     res = E_OUTOFMEMORY;
1590     goto end;
1591   }
1592 
1593   res = STG_E_FILENOTFOUND;
1594 
1595 end:
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  */
StorageBaseImpl_OpenStorage(IStorage * iface,const OLECHAR * pwcsName,IStorage * pstgPriority,DWORD grfMode,SNB snbExclude,DWORD reserved,IStorage ** ppstg)1609 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
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 {
1618   StorageBaseImpl *This = impl_from_IStorage(iface);
1619   StorageInternalImpl*   newStorage;
1620   StorageBaseImpl*       newTransactedStorage;
1621   DirEntry               currentEntry;
1622   DirRef                 storageEntryRef;
1623   HRESULT                res = STG_E_UNKNOWN;
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   {
1637     res = STG_E_INVALIDFUNCTION;
1638     goto end;
1639   }
1640 
1641   /* as documented */
1642   if (snbExclude != NULL)
1643   {
1644     res = STG_E_INVALIDPARAMETER;
1645     goto end;
1646   }
1647 
1648   if ( FAILED( validateSTGM(grfMode) ))
1649   {
1650     res = STG_E_INVALIDFLAG;
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   {
1661     res = STG_E_INVALIDFUNCTION;
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     {
1675       res = STG_E_ACCESSDENIED;
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. */
1694       res = STG_E_ACCESSDENIED;
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 
1728     res = STG_E_INSUFFICIENTMEMORY;
1729     goto end;
1730   }
1731 
1732   res = STG_E_FILENOTFOUND;
1733 
1734 end:
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  */
StorageBaseImpl_EnumElements(IStorage * iface,DWORD reserved1,void * reserved2,DWORD reserved3,IEnumSTATSTG ** ppenum)1747 static HRESULT WINAPI StorageBaseImpl_EnumElements(
1748   IStorage*       iface,
1749   DWORD           reserved1, /* [in] */
1750   void*           reserved2, /* [size_is][unique][in] */
1751   DWORD           reserved3, /* [in] */
1752   IEnumSTATSTG**  ppenum)    /* [out] */
1753 {
1754   StorageBaseImpl *This = impl_from_IStorage(iface);
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 
1766   newEnum = IEnumSTATSTGImpl_Construct(
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  */
StorageBaseImpl_Stat(IStorage * iface,STATSTG * pstatstg,DWORD grfStatFlag)1786 static HRESULT WINAPI StorageBaseImpl_Stat(
1787   IStorage*        iface,
1788   STATSTG*         pstatstg,     /* [out] */
1789   DWORD            grfStatFlag)  /* [in] */
1790 {
1791   StorageBaseImpl *This = impl_from_IStorage(iface);
1792   DirEntry       currentEntry;
1793   HRESULT        res = STG_E_UNKNOWN;
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   {
1806     res = STG_E_REVERTED;
1807     goto end;
1808   }
1809 
1810   res = StorageBaseImpl_ReadDirEntry(
1811                     This,
1812                     This->storageDirEntry,
1813                     &currentEntry);
1814 
1815   if (SUCCEEDED(res))
1816   {
1817     StorageUtl_CopyDirEntryToSTATSTG(
1818       This,
1819       pstatstg,
1820       &currentEntry,
1821       grfStatFlag);
1822 
1823     pstatstg->grfMode = This->openFlags;
1824     pstatstg->grfStateBits = This->stateBits;
1825   }
1826 
1827 end:
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  */
StorageBaseImpl_RenameElement(IStorage * iface,const OLECHAR * pwcsOldName,const OLECHAR * pwcsNewName)1843 static HRESULT WINAPI StorageBaseImpl_RenameElement(
1844             IStorage*        iface,
1845             const OLECHAR*   pwcsOldName,  /* [in] */
1846             const OLECHAR*   pwcsNewName)  /* [in] */
1847 {
1848   StorageBaseImpl *This = impl_from_IStorage(iface);
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      */
1868     return STG_E_FILEALREADYEXISTS;
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 
1914   return StorageBaseImpl_Flush(This);
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  */
StorageBaseImpl_CreateStream(IStorage * iface,const OLECHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStream ** ppstm)1924 static HRESULT WINAPI StorageBaseImpl_CreateStream(
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 {
1932   StorageBaseImpl *This = impl_from_IStorage(iface);
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)
1949     return STG_E_INVALIDPARAMETER;
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
2001       return STG_E_FILEALREADYEXISTS;
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    */
2043   hr = insertIntoTree(
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   {
2065     return STG_E_INSUFFICIENTMEMORY;
2066   }
2067 
2068   return StorageBaseImpl_Flush(This);
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  */
StorageBaseImpl_SetClass(IStorage * iface,REFCLSID clsid)2079 static HRESULT WINAPI StorageBaseImpl_SetClass(
2080   IStorage*        iface,
2081   REFCLSID         clsid) /* [in] */
2082 {
2083   StorageBaseImpl *This = impl_from_IStorage(iface);
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 
2092   hRes = StorageBaseImpl_ReadDirEntry(This,
2093                                       This->storageDirEntry,
2094                                       &currentEntry);
2095   if (SUCCEEDED(hRes))
2096   {
2097     currentEntry.clsid = *clsid;
2098 
2099     hRes = StorageBaseImpl_WriteDirEntry(This,
2100                                          This->storageDirEntry,
2101                                          &currentEntry);
2102   }
2103 
2104   if (SUCCEEDED(hRes))
2105     hRes = StorageBaseImpl_Flush(This);
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  */
StorageBaseImpl_CreateStorage(IStorage * iface,const OLECHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStorage ** ppstg)2117 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
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 {
2125   StorageBaseImpl* This = impl_from_IStorage(iface);
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");
2191       return STG_E_FILEALREADYEXISTS;
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 
2205   if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
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;
2214   newEntry.startingBlock = BLOCK_END_OF_CHAIN;
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    */
2239   hr = insertIntoTree(
2240     This,
2241     This->storageDirEntry,
2242     newEntryRef);
2243   if (FAILED(hr))
2244   {
2245     StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
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))
2260     hr = StorageBaseImpl_Flush(This);
2261 
2262   return S_OK;
2263 }
2264 
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl * This,DirRef srcEntry,BOOL skip_storage,BOOL skip_stream,SNB snbExclude,IStorage * pstgDest)2265 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
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  */
StorageBaseImpl_CopyTo(IStorage * iface,DWORD ciidExclude,const IID * rgiidExclude,SNB snbExclude,IStorage * pstgDest)2288 static HRESULT WINAPI StorageBaseImpl_CopyTo(
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 {
2295   StorageBaseImpl *This = impl_from_IStorage(iface);
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       {
2331         TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
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  */
StorageBaseImpl_MoveElementTo(IStorage * iface,const OLECHAR * pwcsName,IStorage * pstgDest,const OLECHAR * pwcsNewName,DWORD grfFlags)2377 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
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  */
StorageBaseImpl_Commit(IStorage * iface,DWORD grfCommitFlags)2398 static HRESULT WINAPI StorageBaseImpl_Commit(
2399   IStorage*   iface,
2400   DWORD         grfCommitFlags)/* [in] */
2401 {
2402   StorageBaseImpl* This = impl_from_IStorage(iface);
2403   TRACE("(%p %d)\n", iface, grfCommitFlags);
2404   return StorageBaseImpl_Flush(This);
2405 }
2406 
2407 /*************************************************************************
2408  * Revert (IStorage)
2409  *
2410  * Discard all changes that have been made since the last commit operation
2411  */
StorageBaseImpl_Revert(IStorage * iface)2412 static HRESULT WINAPI StorageBaseImpl_Revert(
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  */
deleteStorageContents(StorageBaseImpl * parentStorage,DirRef indexToDelete,DirEntry entryDataToDelete)2426 static HRESULT deleteStorageContents(
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     {
2445       StorageBaseImpl_Invalidate(&stg->base);
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,
2456         STGM_WRITE | STGM_SHARE_EXCLUSIVE,
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  */
deleteStreamContents(StorageBaseImpl * parentStorage,DirRef indexToDelete,DirEntry entryDataToDelete)2513 static HRESULT deleteStreamContents(
2514   StorageBaseImpl *parentStorage,
2515   DirRef        indexToDelete,
2516   DirEntry      entryDataToDelete)
2517 {
2518   IStream      *pis;
2519   HRESULT        hr;
2520   ULARGE_INTEGER size;
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  */
StorageBaseImpl_DestroyElement(IStorage * iface,const OLECHAR * pwcsName)2576 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
2577   IStorage*     iface,
2578   const OLECHAR *pwcsName)/* [string][in] */
2579 {
2580   StorageBaseImpl *This = impl_from_IStorage(iface);
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   {
2613     hr = deleteStorageContents(
2614            This,
2615            entryToDeleteRef,
2616            entryToDelete);
2617   }
2618   else if ( entryToDelete.stgType == STGTY_STREAM )
2619   {
2620     hr = deleteStreamContents(
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    */
2635   hr = removeFromTree(
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))
2647     hr = StorageBaseImpl_Flush(This);
2648 
2649   TRACE("<-- %08x\n", hr);
2650   return hr;
2651 }
2652 
StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)2653 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
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;
2663     list_remove(cur);
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   {
2673     StorageBaseImpl_Invalidate(stg->transactedChild);
2674 
2675     stg->transactedChild = NULL;
2676   }
2677 }
2678 
2679 /******************************************************************************
2680  * SetElementTimes (IStorage)
2681  */
StorageBaseImpl_SetElementTimes(IStorage * iface,const OLECHAR * pwcsName,const FILETIME * pctime,const FILETIME * patime,const FILETIME * pmtime)2682 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
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  */
StorageBaseImpl_SetStateBits(IStorage * iface,DWORD grfStateBits,DWORD grfMask)2696 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2697   IStorage*   iface,
2698   DWORD         grfStateBits,/* [in] */
2699   DWORD         grfMask)     /* [in] */
2700 {
2701   StorageBaseImpl *This = impl_from_IStorage(iface);
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 
StorageBaseImpl_AddStream(StorageBaseImpl * stg,StgStreamImpl * strm)2714 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2715 {
2716   TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2717   list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2718 }
2719 
StorageBaseImpl_RemoveStream(StorageBaseImpl * stg,StgStreamImpl * strm)2720 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2721 {
2722   TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2723   list_remove(&(strm->StrmListEntry));
2724 }
2725 
StorageBaseImpl_CopyStream(StorageBaseImpl * dst,DirRef dst_entry,StorageBaseImpl * src,DirRef src_entry)2726 static HRESULT StorageBaseImpl_CopyStream(
2727   StorageBaseImpl *dst, DirRef dst_entry,
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   {
2740     hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
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 
StorageBaseImpl_DupStorageTree(StorageBaseImpl * dst,DirRef * dst_entry,StorageBaseImpl * src,DirRef src_entry)2765 static HRESULT StorageBaseImpl_DupStorageTree(
2766   StorageBaseImpl *dst, DirRef *dst_entry,
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   {
2775     *dst_entry = DIRENTRY_NULL;
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))
2796     hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
2797 
2798   if (SUCCEEDED(hr) && has_stream)
2799     hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2800 
2801   return hr;
2802 }
2803 
StorageBaseImpl_CopyStorageTree(StorageBaseImpl * dst,DirRef dst_entry,StorageBaseImpl * src,DirRef src_entry)2804 static HRESULT StorageBaseImpl_CopyStorageTree(
2805   StorageBaseImpl *dst, DirRef dst_entry,
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   {
2821     hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
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))
2829     hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2830 
2831   return hr;
2832 }
2833 
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl * This,DirRef entry,BOOL include_siblings)2834 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2835 {
2836   HRESULT hr;
2837   DirEntry data;
2838   ULARGE_INTEGER zero;
2839 
2840   if (entry == DIRENTRY_NULL)
2841     return S_OK;
2842 
2843   zero.QuadPart = 0;
2844 
2845   hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2846 
2847   if (SUCCEEDED(hr) && include_siblings)
2848     hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2849 
2850   if (SUCCEEDED(hr) && include_siblings)
2851     hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2852 
2853   if (SUCCEEDED(hr))
2854     hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2855 
2856   if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2857     hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2858 
2859   if (SUCCEEDED(hr))
2860     hr = StorageBaseImpl_DestroyDirEntry(This, entry);
2861 
2862   return hr;
2863 }
2864 
2865 
2866 /************************************************************************
2867  * StorageImpl implementation
2868  ***********************************************************************/
2869 
StorageImpl_ReadAt(StorageImpl * This,ULARGE_INTEGER offset,void * buffer,ULONG size,ULONG * bytesRead)2870 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
2871   ULARGE_INTEGER offset,
2872   void*          buffer,
2873   ULONG          size,
2874   ULONG*         bytesRead)
2875 {
2876     return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2877 }
2878 
StorageImpl_WriteAt(StorageImpl * This,ULARGE_INTEGER offset,const void * buffer,const ULONG size,ULONG * bytesWritten)2879 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
2880   ULARGE_INTEGER offset,
2881   const void*    buffer,
2882   const ULONG    size,
2883   ULONG*         bytesWritten)
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  */
StorageImpl_LoadFileHeader(StorageImpl * This)2893 static HRESULT StorageImpl_LoadFileHeader(
2894           StorageImpl* This)
2895 {
2896   HRESULT hr;
2897   BYTE    headerBigBlock[HEADER_SIZE];
2898   int     index;
2899   ULARGE_INTEGER offset;
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)
2910     hr = STG_E_FILENOTFOUND;
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 
2931     StorageUtl_ReadWord(
2932       headerBigBlock,
2933       OFFSET_BIGBLOCKSIZEBITS,
2934       &This->bigBlockSizeBits);
2935 
2936     StorageUtl_ReadWord(
2937       headerBigBlock,
2938       OFFSET_SMALLBLOCKSIZEBITS,
2939       &This->smallBlockSizeBits);
2940 
2941     StorageUtl_ReadDWord(
2942       headerBigBlock,
2943       OFFSET_BBDEPOTCOUNT,
2944       &This->bigBlockDepotCount);
2945 
2946     StorageUtl_ReadDWord(
2947       headerBigBlock,
2948       OFFSET_ROOTSTARTBLOCK,
2949       &This->rootStartBlock);
2950 
2951     StorageUtl_ReadDWord(
2952       headerBigBlock,
2953       OFFSET_TRANSACTIONSIG,
2954       &This->transactionSig);
2955 
2956     StorageUtl_ReadDWord(
2957       headerBigBlock,
2958       OFFSET_SMALLBLOCKLIMIT,
2959       &This->smallBlockLimit);
2960 
2961     StorageUtl_ReadDWord(
2962       headerBigBlock,
2963       OFFSET_SBDEPOTSTART,
2964       &This->smallBlockDepotStart);
2965 
2966     StorageUtl_ReadDWord(
2967       headerBigBlock,
2968       OFFSET_EXTBBDEPOTSTART,
2969       &This->extBigBlockDepotStart);
2970 
2971     StorageUtl_ReadDWord(
2972       headerBigBlock,
2973       OFFSET_EXTBBDEPOTCOUNT,
2974       &This->extBigBlockDepotCount);
2975 
2976     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2977     {
2978       StorageUtl_ReadDWord(
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);
3000 	hr = STG_E_INVALIDHEADER;
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  */
StorageImpl_SaveFileHeader(StorageImpl * This)3014 static void StorageImpl_SaveFileHeader(
3015           StorageImpl* This)
3016 {
3017   BYTE   headerBigBlock[HEADER_SIZE];
3018   int    index;
3019   HRESULT hr;
3020   ULARGE_INTEGER offset;
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)
3031     hr = STG_E_FILENOTFOUND;
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    */
3062   StorageUtl_WriteWord(
3063     headerBigBlock,
3064     OFFSET_MINORVERSION,
3065     0x3e);
3066 
3067   StorageUtl_WriteWord(
3068     headerBigBlock,
3069     OFFSET_MAJORVERSION,
3070     major_version);
3071 
3072   StorageUtl_WriteWord(
3073     headerBigBlock,
3074     OFFSET_BYTEORDERMARKER,
3075     (WORD)-2);
3076 
3077   StorageUtl_WriteWord(
3078     headerBigBlock,
3079     OFFSET_BIGBLOCKSIZEBITS,
3080     This->bigBlockSizeBits);
3081 
3082   StorageUtl_WriteWord(
3083     headerBigBlock,
3084     OFFSET_SMALLBLOCKSIZEBITS,
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 
3099   StorageUtl_WriteDWord(
3100     headerBigBlock,
3101     OFFSET_DIRSECTORCOUNT,
3102     dirsectorcount);
3103 
3104   StorageUtl_WriteDWord(
3105     headerBigBlock,
3106     OFFSET_BBDEPOTCOUNT,
3107     This->bigBlockDepotCount);
3108 
3109   StorageUtl_WriteDWord(
3110     headerBigBlock,
3111     OFFSET_ROOTSTARTBLOCK,
3112     This->rootStartBlock);
3113 
3114   StorageUtl_WriteDWord(
3115     headerBigBlock,
3116     OFFSET_TRANSACTIONSIG,
3117     This->transactionSig);
3118 
3119   StorageUtl_WriteDWord(
3120     headerBigBlock,
3121     OFFSET_SMALLBLOCKLIMIT,
3122     This->smallBlockLimit);
3123 
3124   StorageUtl_WriteDWord(
3125     headerBigBlock,
3126     OFFSET_SBDEPOTSTART,
3127     This->smallBlockDepotStart);
3128 
3129   StorageUtl_WriteDWord(
3130     headerBigBlock,
3131     OFFSET_SBDEPOTCOUNT,
3132     This->smallBlockDepotChain ?
3133      BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3134 
3135   StorageUtl_WriteDWord(
3136     headerBigBlock,
3137     OFFSET_EXTBBDEPOTSTART,
3138     This->extBigBlockDepotStart);
3139 
3140   StorageUtl_WriteDWord(
3141     headerBigBlock,
3142     OFFSET_EXTBBDEPOTCOUNT,
3143     This->extBigBlockDepotCount);
3144 
3145   for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3146   {
3147     StorageUtl_WriteDWord(
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  */
StorageImpl_ReadRawDirEntry(StorageImpl * This,ULONG index,BYTE * buffer)3171 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3172 {
3173   ULARGE_INTEGER offset;
3174   HRESULT hr;
3175   ULONG bytesRead;
3176 
3177   offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3178 
3179   hr = BlockChainStream_ReadAt(
3180                     This->rootBlockChain,
3181                     offset,
3182                     RAW_DIRENTRY_SIZE,
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  */
StorageImpl_WriteRawDirEntry(StorageImpl * This,ULONG index,const BYTE * buffer)3199 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3200 {
3201   ULARGE_INTEGER offset;
3202   ULONG bytesRead;
3203 
3204   offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3205 
3206   return BlockChainStream_WriteAt(
3207                     This->rootBlockChain,
3208                     offset,
3209                     RAW_DIRENTRY_SIZE,
3210                     buffer,
3211                     &bytesRead);
3212 }
3213 
3214 /***************************************************************************
3215  *
3216  * Internal Method
3217  *
3218  * Mark a directory entry in the file as free.
3219  */
StorageImpl_DestroyDirEntry(StorageBaseImpl * base,DirRef index)3220 static HRESULT StorageImpl_DestroyDirEntry(
3221   StorageBaseImpl *base,
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  */
UpdateRawDirEntry(BYTE * buffer,const DirEntry * newData)3239 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3240 {
3241   memset(buffer, 0, RAW_DIRENTRY_SIZE);
3242 
3243   memcpy(
3244     buffer + OFFSET_PS_NAME,
3245     newData->name,
3246     DIRENTRY_NAME_BUFFER_LEN );
3247 
3248   memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3249 
3250   StorageUtl_WriteWord(
3251     buffer,
3252       OFFSET_PS_NAMELENGTH,
3253       newData->sizeOfNameString);
3254 
3255   StorageUtl_WriteDWord(
3256     buffer,
3257       OFFSET_PS_LEFTCHILD,
3258       newData->leftChild);
3259 
3260   StorageUtl_WriteDWord(
3261     buffer,
3262       OFFSET_PS_RIGHTCHILD,
3263       newData->rightChild);
3264 
3265   StorageUtl_WriteDWord(
3266     buffer,
3267       OFFSET_PS_DIRROOT,
3268       newData->dirRootEntry);
3269 
3270   StorageUtl_WriteGUID(
3271     buffer,
3272       OFFSET_PS_GUID,
3273       &newData->clsid);
3274 
3275   StorageUtl_WriteDWord(
3276     buffer,
3277       OFFSET_PS_CTIMELOW,
3278       newData->ctime.dwLowDateTime);
3279 
3280   StorageUtl_WriteDWord(
3281     buffer,
3282       OFFSET_PS_CTIMEHIGH,
3283       newData->ctime.dwHighDateTime);
3284 
3285   StorageUtl_WriteDWord(
3286     buffer,
3287       OFFSET_PS_MTIMELOW,
3288       newData->mtime.dwLowDateTime);
3289 
3290   StorageUtl_WriteDWord(
3291     buffer,
3292       OFFSET_PS_MTIMEHIGH,
3293       newData->ctime.dwHighDateTime);
3294 
3295   StorageUtl_WriteDWord(
3296     buffer,
3297       OFFSET_PS_STARTBLOCK,
3298       newData->startingBlock);
3299 
3300   StorageUtl_WriteDWord(
3301     buffer,
3302       OFFSET_PS_SIZE,
3303       newData->size.u.LowPart);
3304 
3305   StorageUtl_WriteDWord(
3306     buffer,
3307       OFFSET_PS_SIZE_HIGH,
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  */
StorageImpl_CreateDirEntry(StorageBaseImpl * base,const DirEntry * newData,DirRef * index)3317 static HRESULT StorageImpl_CreateDirEntry(
3318   StorageBaseImpl *base,
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   {
3331     hr = StorageImpl_ReadRawDirEntry(storage,
3332                                      currentEntryIndex,
3333                                      currentData);
3334 
3335     if (SUCCEEDED(hr))
3336     {
3337       StorageUtl_ReadWord(
3338         currentData,
3339         OFFSET_PS_NAMELENGTH,
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     {
3404       StorageImpl_WriteRawDirEntry(
3405         storage,
3406         entryIndex,
3407         emptyData);
3408     }
3409 
3410     StorageImpl_SaveFileHeader(storage);
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  */
StorageImpl_ReadDirEntry(StorageImpl * This,DirRef index,DirEntry * buffer)3428 static HRESULT StorageImpl_ReadDirEntry(
3429   StorageImpl* This,
3430   DirRef         index,
3431   DirEntry*      buffer)
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,
3444       DIRENTRY_NAME_BUFFER_LEN );
3445     TRACE("storage name: %s\n", debugstr_w(buffer->name));
3446 
3447     memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3448 
3449     StorageUtl_ReadWord(
3450       currentEntry,
3451       OFFSET_PS_NAMELENGTH,
3452       &buffer->sizeOfNameString);
3453 
3454     StorageUtl_ReadDWord(
3455       currentEntry,
3456       OFFSET_PS_LEFTCHILD,
3457       &buffer->leftChild);
3458 
3459     StorageUtl_ReadDWord(
3460       currentEntry,
3461       OFFSET_PS_RIGHTCHILD,
3462       &buffer->rightChild);
3463 
3464     StorageUtl_ReadDWord(
3465       currentEntry,
3466       OFFSET_PS_DIRROOT,
3467       &buffer->dirRootEntry);
3468 
3469     StorageUtl_ReadGUID(
3470       currentEntry,
3471       OFFSET_PS_GUID,
3472       &buffer->clsid);
3473 
3474     StorageUtl_ReadDWord(
3475       currentEntry,
3476       OFFSET_PS_CTIMELOW,
3477       &buffer->ctime.dwLowDateTime);
3478 
3479     StorageUtl_ReadDWord(
3480       currentEntry,
3481       OFFSET_PS_CTIMEHIGH,
3482       &buffer->ctime.dwHighDateTime);
3483 
3484     StorageUtl_ReadDWord(
3485       currentEntry,
3486       OFFSET_PS_MTIMELOW,
3487       &buffer->mtime.dwLowDateTime);
3488 
3489     StorageUtl_ReadDWord(
3490       currentEntry,
3491       OFFSET_PS_MTIMEHIGH,
3492       &buffer->mtime.dwHighDateTime);
3493 
3494     StorageUtl_ReadDWord(
3495       currentEntry,
3496       OFFSET_PS_STARTBLOCK,
3497       &buffer->startingBlock);
3498 
3499     StorageUtl_ReadDWord(
3500       currentEntry,
3501       OFFSET_PS_SIZE,
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     {
3511       StorageUtl_ReadDWord(
3512         currentEntry,
3513         OFFSET_PS_SIZE_HIGH,
3514         &buffer->size.u.HighPart);
3515     }
3516   }
3517 
3518   return readRes;
3519 }
3520 
3521 /*********************************************************************
3522  * Write the specified directory entry to the file
3523  */
StorageImpl_WriteDirEntry(StorageImpl * This,DirRef index,const DirEntry * buffer)3524 static HRESULT StorageImpl_WriteDirEntry(
3525   StorageImpl*          This,
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 
StorageImpl_GetBigBlockOffset(StorageImpl * This,ULONG index)3541 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
3542 {
3543     return (ULONGLONG)(index+1) * This->bigBlockSize;
3544 }
3545 
StorageImpl_ReadBigBlock(StorageImpl * This,ULONG blockIndex,void * buffer,ULONG * out_read)3546 static HRESULT StorageImpl_ReadBigBlock(
3547   StorageImpl* This,
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 
StorageImpl_ReadDWordFromBigBlock(StorageImpl * This,ULONG blockIndex,ULONG offset,DWORD * value)3571 static BOOL StorageImpl_ReadDWordFromBigBlock(
3572   StorageImpl*  This,
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 
StorageImpl_WriteBigBlock(StorageImpl * This,ULONG blockIndex,const void * buffer)3589 static BOOL StorageImpl_WriteBigBlock(
3590   StorageImpl*  This,
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 
StorageImpl_WriteDWordToBigBlock(StorageImpl * This,ULONG blockIndex,ULONG offset,DWORD value)3603 static BOOL StorageImpl_WriteDWordToBigBlock(
3604   StorageImpl* This,
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  */
Storage32Impl_SmallBlocksToBigBlocks(StorageImpl * This,SmallBlockChainStream ** ppsbChain)3626 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3627                       StorageImpl* This,
3628                       SmallBlockChainStream** ppsbChain)
3629 {
3630   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3631   ULARGE_INTEGER size, offset;
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,
3649                                            DIRENTRY_NULL);
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 
3665   buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
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);
3697   HeapFree(GetProcessHeap(),0,buffer);
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;
3714   SmallBlockChainStream_SetSize(*ppsbChain, size);
3715   SmallBlockChainStream_Destroy(*ppsbChain);
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  */
Storage32Impl_BigBlocksToSmallBlocks(StorageImpl * This,BlockChainStream ** ppbbChain,ULARGE_INTEGER newSize)3746 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3747                            StorageImpl* This,
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,
3762             DIRENTRY_NULL);
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     }
3802     HeapFree(GetProcessHeap(), 0, buffer);
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  */
Storage32Impl_AddBlockDepot(StorageImpl * This,ULONG blockIndex,ULONG depotIndex)3835 static 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  */
Storage32Impl_GetExtDepotBlock(StorageImpl * This,ULONG depotIndex)3863 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
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  */
Storage32Impl_SetExtDepotBlock(StorageImpl * This,ULONG depotIndex,ULONG blockIndex)3908 static 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   {
3924     StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
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  */
Storage32Impl_AddExtBlockDepot(StorageImpl * This)3940 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3941 {
3942   ULONG numExtBlocks           = This->extBigBlockDepotCount;
3943   ULONG nextExtBlock           = This->extBigBlockDepotStart;
3944   BYTE  depotBuffer[MAX_BIG_BLOCK_SIZE];
3945   ULONG index                  = BLOCK_UNUSED;
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  */
StorageImpl_GetNextBlockInChain(StorageImpl * This,ULONG blockIndex,ULONG * nextBlockIndex)4021 static HRESULT StorageImpl_GetNextBlockInChain(
4022   StorageImpl* This,
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  */
Storage32Impl_GetNextExtendedBlock(StorageImpl * This,ULONG blockIndex)4096 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
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  */
StorageImpl_SetNextBlockInChain(StorageImpl * This,ULONG blockIndex,ULONG nextBlock)4121 static void StorageImpl_SetNextBlockInChain(
4122           StorageImpl* This,
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  */
StorageImpl_GetNextFreeBigBlock(StorageImpl * This)4171 static ULONG StorageImpl_GetNextFreeBigBlock(
4172   StorageImpl* This)
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          */
4214         StorageImpl_SetNextBlockInChain(This,
4215                                           depotBlockIndexPos,
4216                                           BLOCK_SPECIAL);
4217 
4218         /* Save new header information.
4219          */
4220         StorageImpl_SaveFileHeader(This);
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            */
4240           extIndex = Storage32Impl_AddExtBlockDepot(This);
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          */
4256         StorageImpl_SetNextBlockInChain(This,
4257                                           depotBlockIndexPos,
4258                                           BLOCK_SPECIAL);
4259 
4260         /* If necessary, flag the extended depot block.
4261          */
4262         if (extIndex != BLOCK_UNUSED)
4263           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4264 
4265         /* Save header information.
4266          */
4267         StorageImpl_SaveFileHeader(This);
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  */
StorageImpl_FreeBigBlock(StorageImpl * This,ULONG blockIndex)4314 static void StorageImpl_FreeBigBlock(
4315   StorageImpl* This,
4316   ULONG          blockIndex)
4317 {
4318   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4319 
4320   if (blockIndex < This->prevFreeBlock)
4321     This->prevFreeBlock = blockIndex;
4322 }
4323 
4324 
StorageImpl_BaseWriteDirEntry(StorageBaseImpl * base,DirRef index,const DirEntry * data)4325 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
4326   DirRef index, const DirEntry *data)
4327 {
4328   StorageImpl *This = (StorageImpl*)base;
4329   return StorageImpl_WriteDirEntry(This, index, data);
4330 }
4331 
StorageImpl_BaseReadDirEntry(StorageBaseImpl * base,DirRef index,DirEntry * data)4332 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
4333   DirRef index, DirEntry *data)
4334 {
4335   StorageImpl *This = (StorageImpl*)base;
4336   return StorageImpl_ReadDirEntry(This, index, data);
4337 }
4338 
StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl * This)4339 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
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 
StorageImpl_GetCachedBlockChainStream(StorageImpl * This,DirRef index)4363 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
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 
StorageImpl_DeleteCachedBlockChainStream(StorageImpl * This,DirRef index)4396 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
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 
StorageImpl_StreamReadAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)4411 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
4412   ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4413 {
4414   StorageImpl *This = (StorageImpl*)base;
4415   DirEntry data;
4416   HRESULT hr;
4417   ULONG bytesToRead;
4418 
4419   hr = StorageImpl_ReadDirEntry(This, index, &data);
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   {
4439     SmallBlockChainStream *stream;
4440 
4441     stream = SmallBlockChainStream_Construct(This, NULL, index);
4442     if (!stream) return E_OUTOFMEMORY;
4443 
4444     hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4445 
4446     SmallBlockChainStream_Destroy(stream);
4447 
4448     return hr;
4449   }
4450   else
4451   {
4452     BlockChainStream *stream = NULL;
4453 
4454     stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4455     if (!stream) return E_OUTOFMEMORY;
4456 
4457     hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4458 
4459     return hr;
4460   }
4461 }
4462 
StorageImpl_StreamSetSize(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER newsize)4463 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
4464   ULARGE_INTEGER newsize)
4465 {
4466   StorageImpl *This = (StorageImpl*)base;
4467   DirEntry data;
4468   HRESULT hr;
4469   SmallBlockChainStream *smallblock=NULL;
4470   BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4471 
4472   hr = StorageImpl_ReadDirEntry(This, index, &data);
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     {
4487       smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4488       if (!smallblock) return E_OUTOFMEMORY;
4489     }
4490     else
4491     {
4492       pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
4493       bigblock = *pbigblock;
4494       if (!bigblock) return E_OUTOFMEMORY;
4495     }
4496   }
4497   else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4498   {
4499     smallblock = SmallBlockChainStream_Construct(This, NULL, index);
4500     if (!smallblock) return E_OUTOFMEMORY;
4501   }
4502   else
4503   {
4504     pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
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     {
4515       SmallBlockChainStream_Destroy(smallblock);
4516       return E_FAIL;
4517     }
4518 
4519     pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
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);
4533     SmallBlockChainStream_Destroy(smallblock);
4534   }
4535   else
4536   {
4537     BlockChainStream_SetSize(bigblock, newsize);
4538   }
4539 
4540   /* Set the size in the directory entry. */
4541   hr = StorageImpl_ReadDirEntry(This, index, &data);
4542   if (SUCCEEDED(hr))
4543   {
4544     data.size = newsize;
4545 
4546     hr = StorageImpl_WriteDirEntry(This, index, &data);
4547   }
4548   return hr;
4549 }
4550 
StorageImpl_StreamWriteAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)4551 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
4552   ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4553 {
4554   StorageImpl *This = (StorageImpl*)base;
4555   DirEntry data;
4556   HRESULT hr;
4557   ULARGE_INTEGER newSize;
4558 
4559   hr = StorageImpl_ReadDirEntry(This, index, &data);
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   {
4567     hr = StorageImpl_StreamSetSize(base, index, newSize);
4568     if (FAILED(hr))
4569       return hr;
4570 
4571     hr = StorageImpl_ReadDirEntry(This, index, &data);
4572     if (FAILED(hr)) return hr;
4573   }
4574 
4575   if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4576   {
4577     SmallBlockChainStream *stream;
4578 
4579     stream = SmallBlockChainStream_Construct(This, NULL, index);
4580     if (!stream) return E_OUTOFMEMORY;
4581 
4582     hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4583 
4584     SmallBlockChainStream_Destroy(stream);
4585 
4586     return hr;
4587   }
4588   else
4589   {
4590     BlockChainStream *stream;
4591 
4592     stream = *StorageImpl_GetCachedBlockChainStream(This, index);
4593     if (!stream) return E_OUTOFMEMORY;
4594 
4595     return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4596   }
4597 }
4598 
StorageImpl_StreamLink(StorageBaseImpl * base,DirRef dst,DirRef src)4599 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
4600   DirRef src)
4601 {
4602   StorageImpl *This = (StorageImpl*)base;
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   {
4613     StorageImpl_DeleteCachedBlockChainStream(This, src);
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 
StorageImpl_Refresh(StorageImpl * This,BOOL new_object,BOOL create)4623 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
4624 {
4625   HRESULT hr=S_OK;
4626   DirEntry currentEntry;
4627   DirRef      currentEntryRef;
4628   BlockChainStream *blockChainStream;
4629 
4630   if (create)
4631   {
4632     ULARGE_INTEGER size;
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,
4646             BLOCK_UNUSED,
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 
4662     StorageImpl_SaveFileHeader(This);
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      */
4684     hr = StorageImpl_LoadFileHeader(This);
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 =
4741        BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
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;
4776     rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
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   {
4790     hr = StorageImpl_ReadDirEntry(
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 
StorageImpl_GetTransactionSig(StorageBaseImpl * base,ULONG * result,BOOL refresh)4838 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
4839   ULONG* result, BOOL refresh)
4840 {
4841   StorageImpl *This = (StorageImpl*)base;
4842   HRESULT hr=S_OK;
4843   DWORD oldTransactionSig = This->transactionSig;
4844 
4845   if (refresh)
4846   {
4847     ULARGE_INTEGER offset;
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 
4864         hr = StorageImpl_Refresh(This, FALSE, FALSE);
4865       }
4866 
4867       if (FAILED(hr))
4868         This->transactionSig = oldTransactionSig;
4869     }
4870   }
4871 
4872   *result = This->transactionSig;
4873 
4874   return hr;
4875 }
4876 
StorageImpl_SetTransactionSig(StorageBaseImpl * base,ULONG value)4877 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
4878   ULONG value)
4879 {
4880   StorageImpl *This = (StorageImpl*)base;
4881 
4882   This->transactionSig = value;
4883   StorageImpl_SaveFileHeader(This);
4884 
4885   return S_OK;
4886 }
4887 
StorageImpl_LockRegion(StorageImpl * This,ULARGE_INTEGER offset,ULARGE_INTEGER cb,DWORD dwLockType,BOOL * supported)4888 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
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 
StorageImpl_UnlockRegion(StorageImpl * This,ULARGE_INTEGER offset,ULARGE_INTEGER cb,DWORD dwLockType)4901 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
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 */
StorageImpl_LockRegionSync(StorageImpl * This,ULARGE_INTEGER offset,ULARGE_INTEGER cb,DWORD dwLockType,BOOL * supported)4911 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
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;
4921     sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4922 
4923     do
4924     {
4925         hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4926 
4927         if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4928         {
4929             DWORD current_time = GetTickCount();
4930             if (current_time - start_time >= 20000)
4931             {
4932                 /* timeout */
4933                 break;
4934             }
4935             if (current_time - last_sanity_check >= 500)
4936             {
4937                 /* Any storage implementation with the file open in a
4938                  * shared mode should not lock these bytes for writing. However,
4939                  * some programs (LibreOffice Writer) will keep ALL bytes locked
4940                  * when opening in exclusive mode. We can use a read lock to
4941                  * detect this case early, and not hang a full 20 seconds.
4942                  *
4943                  * This can collide with another attempt to open the file in
4944                  * exclusive mode, but it's unlikely, and someone would fail anyway. */
4945                 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4946                 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
4947                     break;
4948                 if (SUCCEEDED(hr))
4949                 {
4950                     StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4951                     hr = STG_E_ACCESSDENIED;
4952                 }
4953 
4954                 last_sanity_check = current_time;
4955             }
4956             Sleep(delay);
4957             if (delay < 150) delay++;
4958         }
4959     } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4960 
4961     return hr;
4962 }
4963 
StorageImpl_LockTransaction(StorageBaseImpl * base,BOOL write)4964 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4965 {
4966   StorageImpl *This = (StorageImpl*)base;
4967   HRESULT hr;
4968   ULARGE_INTEGER offset, cb;
4969 
4970   if (write)
4971   {
4972     /* Synchronous grab of second priority range, the commit lock, and the
4973      * lock-checking lock. */
4974     offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4975     cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4976   }
4977   else
4978   {
4979     offset.QuadPart = RANGELOCK_COMMIT;
4980     cb.QuadPart = 1;
4981   }
4982 
4983   hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4984 
4985   return hr;
4986 }
4987 
StorageImpl_UnlockTransaction(StorageBaseImpl * base,BOOL write)4988 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4989 {
4990   StorageImpl *This = (StorageImpl*)base;
4991   HRESULT hr;
4992   ULARGE_INTEGER offset, cb;
4993 
4994   if (write)
4995   {
4996     offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
4997     cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
4998   }
4999   else
5000   {
5001     offset.QuadPart = RANGELOCK_COMMIT;
5002     cb.QuadPart = 1;
5003   }
5004 
5005   hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5006 
5007   return hr;
5008 }
5009 
StorageImpl_GetFilename(StorageBaseImpl * iface,LPWSTR * result)5010 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5011 {
5012   StorageImpl *This = (StorageImpl*) iface;
5013   STATSTG statstg;
5014   HRESULT hr;
5015 
5016   hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
5017 
5018   *result = statstg.pwcsName;
5019 
5020   return hr;
5021 }
5022 
StorageImpl_CheckLockRange(StorageImpl * This,ULONG start,ULONG end,HRESULT fail_hr)5023 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
5024     ULONG end, HRESULT fail_hr)
5025 {
5026     HRESULT hr;
5027     ULARGE_INTEGER offset, cb;
5028 
5029     offset.QuadPart = start;
5030     cb.QuadPart = 1 + end - start;
5031 
5032     hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5033     if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5034 
5035     if (FAILED(hr))
5036         return fail_hr;
5037     else
5038         return S_OK;
5039 }
5040 
StorageImpl_LockOne(StorageImpl * This,ULONG start,ULONG end)5041 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
5042 {
5043     HRESULT hr=S_OK;
5044     int i, j;
5045     ULARGE_INTEGER offset, cb;
5046 
5047     cb.QuadPart = 1;
5048 
5049     for (i=start; i<=end; i++)
5050     {
5051         offset.QuadPart = i;
5052         hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5053         if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
5054             break;
5055     }
5056 
5057     if (SUCCEEDED(hr))
5058     {
5059         for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++)
5060         {
5061             if (This->locked_bytes[j] == 0)
5062             {
5063                 This->locked_bytes[j] = i;
5064                 break;
5065             }
5066         }
5067     }
5068 
5069     return hr;
5070 }
5071 
StorageImpl_GrabLocks(StorageImpl * This,DWORD openFlags)5072 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5073 {
5074     HRESULT hr;
5075     ULARGE_INTEGER offset;
5076     ULARGE_INTEGER cb;
5077     DWORD share_mode = STGM_SHARE_MODE(openFlags);
5078     BOOL supported;
5079 
5080     if (openFlags & STGM_NOSNAPSHOT)
5081     {
5082         /* STGM_NOSNAPSHOT implies deny write */
5083         if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5084         else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5085     }
5086 
5087     /* Wrap all other locking inside a single lock so we can check ranges safely */
5088     offset.QuadPart = RANGELOCK_CHECKLOCKS;
5089     cb.QuadPart = 1;
5090     hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5091 
5092     /* If the ILockBytes doesn't support locking that's ok. */
5093     if (!supported) return S_OK;
5094     else if (FAILED(hr)) return hr;
5095 
5096     hr = S_OK;
5097 
5098     /* First check for any conflicting locks. */
5099     if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5100         hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5101 
5102     if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5103         hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5104 
5105     if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5106         hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5107 
5108     if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5109         hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
5110 
5111     if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5112         hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
5113 
5114     if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5115     {
5116         hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);
5117 
5118         if (SUCCEEDED(hr))
5119             hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
5120     }
5121 
5122     /* Then grab our locks. */
5123     if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5124     {
5125         hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
5126         if (SUCCEEDED(hr))
5127             hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5128     }
5129 
5130     if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5131         hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5132 
5133     if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5134         hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5135 
5136     if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5137         hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5138 
5139     if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5140         hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5141 
5142     if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5143         hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5144 
5145     offset.QuadPart = RANGELOCK_CHECKLOCKS;
5146     cb.QuadPart = 1;
5147     StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5148 
5149     return hr;
5150 }
5151 
StorageImpl_Flush(StorageBaseImpl * storage)5152 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5153 {
5154   StorageImpl *This = (StorageImpl*)storage;
5155   int i;
5156   HRESULT hr;
5157   TRACE("(%p)\n", This);
5158 
5159   hr = BlockChainStream_Flush(This->smallBlockRootChain);
5160 
5161   if (SUCCEEDED(hr))
5162     hr = BlockChainStream_Flush(This->rootBlockChain);
5163 
5164   if (SUCCEEDED(hr))
5165     hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5166 
5167   for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5168     if (This->blockChainCache[i])
5169       hr = BlockChainStream_Flush(This->blockChainCache[i]);
5170 
5171   if (SUCCEEDED(hr))
5172     hr = ILockBytes_Flush(This->lockBytes);
5173 
5174   return hr;
5175 }
5176 
StorageImpl_Invalidate(StorageBaseImpl * iface)5177 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
5178 {
5179   StorageImpl *This = (StorageImpl*) iface;
5180 
5181   StorageBaseImpl_DeleteAll(&This->base);
5182 
5183   This->base.reverted = TRUE;
5184 }
5185 
StorageImpl_Destroy(StorageBaseImpl * iface)5186 static void StorageImpl_Destroy(StorageBaseImpl* iface)
5187 {
5188   StorageImpl *This = (StorageImpl*) iface;
5189   int i;
5190   TRACE("(%p)\n", This);
5191 
5192   StorageImpl_Flush(iface);
5193 
5194   StorageImpl_Invalidate(iface);
5195 
5196   HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5197 
5198   BlockChainStream_Destroy(This->smallBlockRootChain);
5199   BlockChainStream_Destroy(This->rootBlockChain);
5200   BlockChainStream_Destroy(This->smallBlockDepotChain);
5201 
5202   for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++)
5203     BlockChainStream_Destroy(This->blockChainCache[i]);
5204 
5205   for (i = 0; i < ARRAY_SIZE(This->locked_bytes); i++)
5206   {
5207     ULARGE_INTEGER offset, cb;
5208     cb.QuadPart = 1;
5209     if (This->locked_bytes[i] != 0)
5210     {
5211       offset.QuadPart = This->locked_bytes[i];
5212       StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5213     }
5214   }
5215 
5216   if (This->lockBytes)
5217     ILockBytes_Release(This->lockBytes);
5218   HeapFree(GetProcessHeap(), 0, This);
5219 }
5220 
5221 
5222 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
5223 {
5224   StorageImpl_Destroy,
5225   StorageImpl_Invalidate,
5226   StorageImpl_Flush,
5227   StorageImpl_GetFilename,
5228   StorageImpl_CreateDirEntry,
5229   StorageImpl_BaseWriteDirEntry,
5230   StorageImpl_BaseReadDirEntry,
5231   StorageImpl_DestroyDirEntry,
5232   StorageImpl_StreamReadAt,
5233   StorageImpl_StreamWriteAt,
5234   StorageImpl_StreamSetSize,
5235   StorageImpl_StreamLink,
5236   StorageImpl_GetTransactionSig,
5237   StorageImpl_SetTransactionSig,
5238   StorageImpl_LockTransaction,
5239   StorageImpl_UnlockTransaction
5240 };
5241 
5242 
5243 /*
5244  * Virtual function table for the IStorageBaseImpl class.
5245  */
5246 static const IStorageVtbl StorageImpl_Vtbl =
5247 {
5248     StorageBaseImpl_QueryInterface,
5249     StorageBaseImpl_AddRef,
5250     StorageBaseImpl_Release,
5251     StorageBaseImpl_CreateStream,
5252     StorageBaseImpl_OpenStream,
5253     StorageBaseImpl_CreateStorage,
5254     StorageBaseImpl_OpenStorage,
5255     StorageBaseImpl_CopyTo,
5256     StorageBaseImpl_MoveElementTo,
5257     StorageBaseImpl_Commit,
5258     StorageBaseImpl_Revert,
5259     StorageBaseImpl_EnumElements,
5260     StorageBaseImpl_DestroyElement,
5261     StorageBaseImpl_RenameElement,
5262     StorageBaseImpl_SetElementTimes,
5263     StorageBaseImpl_SetClass,
5264     StorageBaseImpl_SetStateBits,
5265     StorageBaseImpl_Stat
5266 };
5267 
StorageImpl_Construct(HANDLE hFile,LPCOLESTR pwcsName,ILockBytes * pLkbyt,DWORD openFlags,BOOL fileBased,BOOL create,ULONG sector_size,StorageImpl ** result)5268 static HRESULT StorageImpl_Construct(
5269   HANDLE       hFile,
5270   LPCOLESTR    pwcsName,
5271   ILockBytes*  pLkbyt,
5272   DWORD        openFlags,
5273   BOOL         fileBased,
5274   BOOL         create,
5275   ULONG        sector_size,
5276   StorageImpl** result)
5277 {
5278   StorageImpl* This;
5279   HRESULT hr = S_OK;
5280   STATSTG stat;
5281 
5282   if ( FAILED( validateSTGM(openFlags) ))
5283     return STG_E_INVALIDFLAG;
5284 
5285   This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5286   if (!This)
5287     return E_OUTOFMEMORY;
5288 
5289   memset(This, 0, sizeof(StorageImpl));
5290 
5291   list_init(&This->base.strmHead);
5292 
5293   list_init(&This->base.storageHead);
5294 
5295   This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
5296   This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5297   This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
5298   This->base.baseVtbl = &StorageImpl_BaseVtbl;
5299   This->base.openFlags = (openFlags & ~STGM_CREATE);
5300   This->base.ref = 1;
5301   This->base.create = create;
5302 
5303   if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
5304     This->base.lockingrole = SWMR_Writer;
5305   else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
5306     This->base.lockingrole = SWMR_Reader;
5307   else
5308     This->base.lockingrole = SWMR_None;
5309 
5310   This->base.reverted = FALSE;
5311 
5312   /*
5313    * Initialize the big block cache.
5314    */
5315   This->bigBlockSize   = sector_size;
5316   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
5317   if (hFile)
5318     hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
5319   else
5320   {
5321     This->lockBytes = pLkbyt;
5322     ILockBytes_AddRef(pLkbyt);
5323   }
5324 
5325   if (SUCCEEDED(hr))
5326     hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);
5327 
5328   if (SUCCEEDED(hr))
5329   {
5330     This->locks_supported = stat.grfLocksSupported;
5331     if (!hFile)
5332         /* Don't try to use wine-internal locking flag with custom ILockBytes */
5333         This->locks_supported &= ~WINE_LOCK_READ;
5334 
5335     hr = StorageImpl_GrabLocks(This, openFlags);
5336   }
5337 
5338   if (SUCCEEDED(hr))
5339     hr = StorageImpl_Refresh(This, TRUE, create);
5340 
5341   if (FAILED(hr))
5342   {
5343     IStorage_Release(&This->base.IStorage_iface);
5344     *result = NULL;
5345   }
5346   else
5347   {
5348     StorageImpl_Flush(&This->base);
5349     *result = This;
5350   }
5351 
5352   return hr;
5353 }
5354 
5355 
5356 /************************************************************************
5357  * StorageInternalImpl implementation
5358  ***********************************************************************/
5359 
StorageInternalImpl_Invalidate(StorageBaseImpl * base)5360 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5361 {
5362   StorageInternalImpl* This = (StorageInternalImpl*) base;
5363 
5364   if (!This->base.reverted)
5365   {
5366     TRACE("Storage invalidated (stg=%p)\n", This);
5367 
5368     This->base.reverted = TRUE;
5369 
5370     This->parentStorage = NULL;
5371 
5372     StorageBaseImpl_DeleteAll(&This->base);
5373 
5374     list_remove(&This->ParentListEntry);
5375   }
5376 }
5377 
StorageInternalImpl_Destroy(StorageBaseImpl * iface)5378 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5379 {
5380   StorageInternalImpl* This = (StorageInternalImpl*) iface;
5381 
5382   StorageInternalImpl_Invalidate(&This->base);
5383 
5384   HeapFree(GetProcessHeap(), 0, This);
5385 }
5386 
StorageInternalImpl_Flush(StorageBaseImpl * iface)5387 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5388 {
5389   StorageInternalImpl* This = (StorageInternalImpl*) iface;
5390 
5391   return StorageBaseImpl_Flush(This->parentStorage);
5392 }
5393 
StorageInternalImpl_GetFilename(StorageBaseImpl * iface,LPWSTR * result)5394 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5395 {
5396   StorageInternalImpl* This = (StorageInternalImpl*) iface;
5397 
5398   return StorageBaseImpl_GetFilename(This->parentStorage, result);
5399 }
5400 
StorageInternalImpl_CreateDirEntry(StorageBaseImpl * base,const DirEntry * newData,DirRef * index)5401 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5402   const DirEntry *newData, DirRef *index)
5403 {
5404   StorageInternalImpl* This = (StorageInternalImpl*) base;
5405 
5406   return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5407     newData, index);
5408 }
5409 
StorageInternalImpl_WriteDirEntry(StorageBaseImpl * base,DirRef index,const DirEntry * data)5410 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5411   DirRef index, const DirEntry *data)
5412 {
5413   StorageInternalImpl* This = (StorageInternalImpl*) base;
5414 
5415   return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5416     index, data);
5417 }
5418 
StorageInternalImpl_ReadDirEntry(StorageBaseImpl * base,DirRef index,DirEntry * data)5419 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5420   DirRef index, DirEntry *data)
5421 {
5422   StorageInternalImpl* This = (StorageInternalImpl*) base;
5423 
5424   return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5425     index, data);
5426 }
5427 
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl * base,DirRef index)5428 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5429   DirRef index)
5430 {
5431   StorageInternalImpl* This = (StorageInternalImpl*) base;
5432 
5433   return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5434     index);
5435 }
5436 
StorageInternalImpl_StreamReadAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)5437 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5438   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5439 {
5440   StorageInternalImpl* This = (StorageInternalImpl*) base;
5441 
5442   return StorageBaseImpl_StreamReadAt(This->parentStorage,
5443     index, offset, size, buffer, bytesRead);
5444 }
5445 
StorageInternalImpl_StreamWriteAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)5446 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5447   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5448 {
5449   StorageInternalImpl* This = (StorageInternalImpl*) base;
5450 
5451   return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5452     index, offset, size, buffer, bytesWritten);
5453 }
5454 
StorageInternalImpl_StreamSetSize(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER newsize)5455 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5456   DirRef index, ULARGE_INTEGER newsize)
5457 {
5458   StorageInternalImpl* This = (StorageInternalImpl*) base;
5459 
5460   return StorageBaseImpl_StreamSetSize(This->parentStorage,
5461     index, newsize);
5462 }
5463 
StorageInternalImpl_StreamLink(StorageBaseImpl * base,DirRef dst,DirRef src)5464 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5465   DirRef dst, DirRef src)
5466 {
5467   StorageInternalImpl* This = (StorageInternalImpl*) base;
5468 
5469   return StorageBaseImpl_StreamLink(This->parentStorage,
5470     dst, src);
5471 }
5472 
StorageInternalImpl_GetTransactionSig(StorageBaseImpl * base,ULONG * result,BOOL refresh)5473 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5474   ULONG* result, BOOL refresh)
5475 {
5476   return E_NOTIMPL;
5477 }
5478 
StorageInternalImpl_SetTransactionSig(StorageBaseImpl * base,ULONG value)5479 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5480   ULONG value)
5481 {
5482   return E_NOTIMPL;
5483 }
5484 
StorageInternalImpl_LockTransaction(StorageBaseImpl * base,BOOL write)5485 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5486 {
5487   return E_NOTIMPL;
5488 }
5489 
StorageInternalImpl_UnlockTransaction(StorageBaseImpl * base,BOOL write)5490 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5491 {
5492   return E_NOTIMPL;
5493 }
5494 
5495 /******************************************************************************
5496 **
5497 ** StorageInternalImpl_Commit
5498 **
5499 */
StorageInternalImpl_Commit(IStorage * iface,DWORD grfCommitFlags)5500 static HRESULT WINAPI StorageInternalImpl_Commit(
5501   IStorage*            iface,
5502   DWORD                  grfCommitFlags)  /* [in] */
5503 {
5504   StorageBaseImpl* This = impl_from_IStorage(iface);
5505   TRACE("(%p,%x)\n", iface, grfCommitFlags);
5506   return StorageBaseImpl_Flush(This);
5507 }
5508 
5509 /******************************************************************************
5510 **
5511 ** StorageInternalImpl_Revert
5512 **
5513 */
StorageInternalImpl_Revert(IStorage * iface)5514 static HRESULT WINAPI StorageInternalImpl_Revert(
5515   IStorage*            iface)
5516 {
5517   FIXME("(%p): stub\n", iface);
5518   return S_OK;
5519 }
5520 
5521 /*
5522  * Virtual function table for the StorageInternalImpl class.
5523  */
5524 static const IStorageVtbl StorageInternalImpl_Vtbl =
5525 {
5526     StorageBaseImpl_QueryInterface,
5527     StorageBaseImpl_AddRef,
5528     StorageBaseImpl_Release,
5529     StorageBaseImpl_CreateStream,
5530     StorageBaseImpl_OpenStream,
5531     StorageBaseImpl_CreateStorage,
5532     StorageBaseImpl_OpenStorage,
5533     StorageBaseImpl_CopyTo,
5534     StorageBaseImpl_MoveElementTo,
5535     StorageInternalImpl_Commit,
5536     StorageInternalImpl_Revert,
5537     StorageBaseImpl_EnumElements,
5538     StorageBaseImpl_DestroyElement,
5539     StorageBaseImpl_RenameElement,
5540     StorageBaseImpl_SetElementTimes,
5541     StorageBaseImpl_SetClass,
5542     StorageBaseImpl_SetStateBits,
5543     StorageBaseImpl_Stat
5544 };
5545 
5546 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5547 {
5548   StorageInternalImpl_Destroy,
5549   StorageInternalImpl_Invalidate,
5550   StorageInternalImpl_Flush,
5551   StorageInternalImpl_GetFilename,
5552   StorageInternalImpl_CreateDirEntry,
5553   StorageInternalImpl_WriteDirEntry,
5554   StorageInternalImpl_ReadDirEntry,
5555   StorageInternalImpl_DestroyDirEntry,
5556   StorageInternalImpl_StreamReadAt,
5557   StorageInternalImpl_StreamWriteAt,
5558   StorageInternalImpl_StreamSetSize,
5559   StorageInternalImpl_StreamLink,
5560   StorageInternalImpl_GetTransactionSig,
5561   StorageInternalImpl_SetTransactionSig,
5562   StorageInternalImpl_LockTransaction,
5563   StorageInternalImpl_UnlockTransaction
5564 };
5565 
StorageInternalImpl_Construct(StorageBaseImpl * parentStorage,DWORD openFlags,DirRef storageDirEntry)5566 static StorageInternalImpl* StorageInternalImpl_Construct(
5567   StorageBaseImpl* parentStorage,
5568   DWORD        openFlags,
5569   DirRef       storageDirEntry)
5570 {
5571   StorageInternalImpl* newStorage;
5572 
5573   newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5574 
5575   if (newStorage!=0)
5576   {
5577     list_init(&newStorage->base.strmHead);
5578 
5579     list_init(&newStorage->base.storageHead);
5580 
5581     /*
5582      * Initialize the virtual function table.
5583      */
5584     newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
5585     newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5586     newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5587     newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5588 
5589     newStorage->base.reverted = FALSE;
5590 
5591     newStorage->base.ref = 1;
5592 
5593     newStorage->parentStorage = parentStorage;
5594 
5595     /*
5596      * Keep a reference to the directory entry of this storage
5597      */
5598     newStorage->base.storageDirEntry = storageDirEntry;
5599 
5600     newStorage->base.create = FALSE;
5601 
5602     return newStorage;
5603   }
5604 
5605   return 0;
5606 }
5607 
5608 
5609 /************************************************************************
5610  * TransactedSnapshotImpl implementation
5611  ***********************************************************************/
5612 
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl * This)5613 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
5614 {
5615   DirRef result=This->firstFreeEntry;
5616 
5617   while (result < This->entries_size && This->entries[result].inuse)
5618     result++;
5619 
5620   if (result == This->entries_size)
5621   {
5622     ULONG new_size = This->entries_size * 2;
5623     TransactedDirEntry *new_entries;
5624 
5625     new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
5626     if (!new_entries) return DIRENTRY_NULL;
5627 
5628     memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
5629     HeapFree(GetProcessHeap(), 0, This->entries);
5630 
5631     This->entries = new_entries;
5632     This->entries_size = new_size;
5633   }
5634 
5635   This->entries[result].inuse = TRUE;
5636 
5637   This->firstFreeEntry = result+1;
5638 
5639   return result;
5640 }
5641 
TransactedSnapshotImpl_CreateStubEntry(TransactedSnapshotImpl * This,DirRef parentEntryRef)5642 static DirRef TransactedSnapshotImpl_CreateStubEntry(
5643   TransactedSnapshotImpl *This, DirRef parentEntryRef)
5644 {
5645   DirRef stubEntryRef;
5646   TransactedDirEntry *entry;
5647 
5648   stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5649 
5650   if (stubEntryRef != DIRENTRY_NULL)
5651   {
5652     entry = &This->entries[stubEntryRef];
5653 
5654     entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5655 
5656     entry->read = FALSE;
5657   }
5658 
5659   return stubEntryRef;
5660 }
5661 
TransactedSnapshotImpl_EnsureReadEntry(TransactedSnapshotImpl * This,DirRef entry)5662 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
5663   TransactedSnapshotImpl *This, DirRef entry)
5664 {
5665   HRESULT hr=S_OK;
5666   DirEntry data;
5667 
5668   if (!This->entries[entry].read)
5669   {
5670     hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5671         This->entries[entry].transactedParentEntry,
5672         &data);
5673 
5674     if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
5675     {
5676       data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5677 
5678       if (data.leftChild == DIRENTRY_NULL)
5679         hr = E_OUTOFMEMORY;
5680     }
5681 
5682     if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
5683     {
5684       data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5685 
5686       if (data.rightChild == DIRENTRY_NULL)
5687         hr = E_OUTOFMEMORY;
5688     }
5689 
5690     if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
5691     {
5692       data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5693 
5694       if (data.dirRootEntry == DIRENTRY_NULL)
5695         hr = E_OUTOFMEMORY;
5696     }
5697 
5698     if (SUCCEEDED(hr))
5699     {
5700       memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
5701       This->entries[entry].read = TRUE;
5702     }
5703   }
5704 
5705   return hr;
5706 }
5707 
TransactedSnapshotImpl_MakeStreamDirty(TransactedSnapshotImpl * This,DirRef entry)5708 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
5709   TransactedSnapshotImpl *This, DirRef entry)
5710 {
5711   HRESULT hr = S_OK;
5712 
5713   if (!This->entries[entry].stream_dirty)
5714   {
5715     DirEntry new_entrydata;
5716 
5717     memset(&new_entrydata, 0, sizeof(DirEntry));
5718     new_entrydata.name[0] = 'S';
5719     new_entrydata.sizeOfNameString = 1;
5720     new_entrydata.stgType = STGTY_STREAM;
5721     new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
5722     new_entrydata.leftChild = DIRENTRY_NULL;
5723     new_entrydata.rightChild = DIRENTRY_NULL;
5724     new_entrydata.dirRootEntry = DIRENTRY_NULL;
5725 
5726     hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
5727       &This->entries[entry].stream_entry);
5728 
5729     if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5730     {
5731       hr = StorageBaseImpl_CopyStream(
5732         This->scratch, This->entries[entry].stream_entry,
5733         This->transactedParent, This->entries[entry].transactedParentEntry);
5734 
5735       if (FAILED(hr))
5736         StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
5737     }
5738 
5739     if (SUCCEEDED(hr))
5740       This->entries[entry].stream_dirty = TRUE;
5741 
5742     if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
5743     {
5744       /* Since this entry is modified, and we aren't using its stream data, we
5745        * no longer care about the original entry. */
5746       DirRef delete_ref;
5747       delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5748 
5749       if (delete_ref != DIRENTRY_NULL)
5750         This->entries[delete_ref].deleted = TRUE;
5751 
5752       This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
5753     }
5754   }
5755 
5756   return hr;
5757 }
5758 
5759 /* Find the first entry in a depth-first traversal. */
TransactedSnapshotImpl_FindFirstChild(TransactedSnapshotImpl * This,DirRef parent)5760 static DirRef TransactedSnapshotImpl_FindFirstChild(
5761   TransactedSnapshotImpl* This, DirRef parent)
5762 {
5763   DirRef cursor, prev;
5764   TransactedDirEntry *entry;
5765 
5766   cursor = parent;
5767   entry = &This->entries[cursor];
5768   while (entry->read)
5769   {
5770     if (entry->data.leftChild != DIRENTRY_NULL)
5771     {
5772       prev = cursor;
5773       cursor = entry->data.leftChild;
5774       entry = &This->entries[cursor];
5775       entry->parent = prev;
5776     }
5777     else if (entry->data.rightChild != DIRENTRY_NULL)
5778     {
5779       prev = cursor;
5780       cursor = entry->data.rightChild;
5781       entry = &This->entries[cursor];
5782       entry->parent = prev;
5783     }
5784     else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5785     {
5786       prev = cursor;
5787       cursor = entry->data.dirRootEntry;
5788       entry = &This->entries[cursor];
5789       entry->parent = prev;
5790     }
5791     else
5792       break;
5793   }
5794 
5795   return cursor;
5796 }
5797 
5798 /* Find the next entry in a depth-first traversal. */
TransactedSnapshotImpl_FindNextChild(TransactedSnapshotImpl * This,DirRef current)5799 static DirRef TransactedSnapshotImpl_FindNextChild(
5800   TransactedSnapshotImpl* This, DirRef current)
5801 {
5802   DirRef parent;
5803   TransactedDirEntry *parent_entry;
5804 
5805   parent = This->entries[current].parent;
5806   parent_entry = &This->entries[parent];
5807 
5808   if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
5809   {
5810     if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5811     {
5812       This->entries[parent_entry->data.rightChild].parent = parent;
5813       return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5814     }
5815 
5816     if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5817     {
5818       This->entries[parent_entry->data.dirRootEntry].parent = parent;
5819       return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5820     }
5821   }
5822 
5823   return parent;
5824 }
5825 
5826 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
TransactedSnapshotImpl_MadeCopy(TransactedSnapshotImpl * This,DirRef entry)5827 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5828   TransactedSnapshotImpl* This, DirRef entry)
5829 {
5830   return entry != DIRENTRY_NULL &&
5831     This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5832 }
5833 
5834 /* Destroy the entries created by CopyTree. */
TransactedSnapshotImpl_DestroyTemporaryCopy(TransactedSnapshotImpl * This,DirRef stop)5835 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5836   TransactedSnapshotImpl* This, DirRef stop)
5837 {
5838   DirRef cursor;
5839   TransactedDirEntry *entry;
5840   ULARGE_INTEGER zero;
5841 
5842   zero.QuadPart = 0;
5843 
5844   if (!This->entries[This->base.storageDirEntry].read)
5845     return;
5846 
5847   cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5848 
5849   if (cursor == DIRENTRY_NULL)
5850     return;
5851 
5852   cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5853 
5854   while (cursor != DIRENTRY_NULL && cursor != stop)
5855   {
5856     if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5857     {
5858       entry = &This->entries[cursor];
5859 
5860       if (entry->stream_dirty)
5861         StorageBaseImpl_StreamSetSize(This->transactedParent,
5862           entry->newTransactedParentEntry, zero);
5863 
5864       StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5865         entry->newTransactedParentEntry);
5866 
5867       entry->newTransactedParentEntry = entry->transactedParentEntry;
5868     }
5869 
5870     cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5871   }
5872 }
5873 
5874 /* Make a copy of our edited tree that we can use in the parent. */
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl * This)5875 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5876 {
5877   DirRef cursor;
5878   TransactedDirEntry *entry;
5879   HRESULT hr = S_OK;
5880 
5881   cursor = This->base.storageDirEntry;
5882   entry = &This->entries[cursor];
5883   entry->parent = DIRENTRY_NULL;
5884   entry->newTransactedParentEntry = entry->transactedParentEntry;
5885 
5886   if (entry->data.dirRootEntry == DIRENTRY_NULL)
5887     return S_OK;
5888 
5889   This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5890 
5891   cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5892   entry = &This->entries[cursor];
5893 
5894   while (cursor != DIRENTRY_NULL)
5895   {
5896     /* Make a copy of this entry in the transacted parent. */
5897     if (!entry->read ||
5898         (!entry->dirty && !entry->stream_dirty &&
5899          !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5900          !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5901          !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5902       entry->newTransactedParentEntry = entry->transactedParentEntry;
5903     else
5904     {
5905       DirEntry newData;
5906 
5907       memcpy(&newData, &entry->data, sizeof(DirEntry));
5908 
5909       newData.size.QuadPart = 0;
5910       newData.startingBlock = BLOCK_END_OF_CHAIN;
5911 
5912       if (newData.leftChild != DIRENTRY_NULL)
5913         newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5914 
5915       if (newData.rightChild != DIRENTRY_NULL)
5916         newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5917 
5918       if (newData.dirRootEntry != DIRENTRY_NULL)
5919         newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5920 
5921       hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5922         &entry->newTransactedParentEntry);
5923       if (FAILED(hr))
5924       {
5925         TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5926         return hr;
5927       }
5928 
5929       if (entry->stream_dirty)
5930       {
5931         hr = StorageBaseImpl_CopyStream(
5932           This->transactedParent, entry->newTransactedParentEntry,
5933           This->scratch, entry->stream_entry);
5934       }
5935       else if (entry->data.size.QuadPart)
5936       {
5937         hr = StorageBaseImpl_StreamLink(
5938           This->transactedParent, entry->newTransactedParentEntry,
5939           entry->transactedParentEntry);
5940       }
5941 
5942       if (FAILED(hr))
5943       {
5944         cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5945         TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5946         return hr;
5947       }
5948     }
5949 
5950     cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5951     entry = &This->entries[cursor];
5952   }
5953 
5954   return hr;
5955 }
5956 
TransactedSnapshotImpl_Commit(IStorage * iface,DWORD grfCommitFlags)5957 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5958   IStorage*            iface,
5959   DWORD                  grfCommitFlags)  /* [in] */
5960 {
5961   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5962   TransactedDirEntry *root_entry;
5963   DirRef i, dir_root_ref;
5964   DirEntry data;
5965   ULARGE_INTEGER zero;
5966   HRESULT hr;
5967   ULONG transactionSig;
5968 
5969   zero.QuadPart = 0;
5970 
5971   TRACE("(%p,%x)\n", iface, grfCommitFlags);
5972 
5973   /* Cannot commit a read-only transacted storage */
5974   if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5975     return STG_E_ACCESSDENIED;
5976 
5977   hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5978   if (hr == E_NOTIMPL) hr = S_OK;
5979   if (SUCCEEDED(hr))
5980   {
5981     hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5982     if (SUCCEEDED(hr))
5983     {
5984       if (transactionSig != This->lastTransactionSig)
5985       {
5986         ERR("file was externally modified\n");
5987         hr = STG_E_NOTCURRENT;
5988       }
5989 
5990       if (SUCCEEDED(hr))
5991       {
5992         This->lastTransactionSig = transactionSig+1;
5993         hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5994       }
5995     }
5996     else if (hr == E_NOTIMPL)
5997       hr = S_OK;
5998 
5999     if (FAILED(hr)) goto end;
6000 
6001     /* To prevent data loss, we create the new structure in the file before we
6002      * delete the old one, so that in case of errors the old data is intact. We
6003      * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
6004      * needed in the rare situation where we have just enough free disk space to
6005      * overwrite the existing data. */
6006 
6007     root_entry = &This->entries[This->base.storageDirEntry];
6008 
6009     if (!root_entry->read)
6010       goto end;
6011 
6012     hr = TransactedSnapshotImpl_CopyTree(This);
6013     if (FAILED(hr)) goto end;
6014 
6015     if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
6016       dir_root_ref = DIRENTRY_NULL;
6017     else
6018       dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
6019 
6020     hr = StorageBaseImpl_Flush(This->transactedParent);
6021 
6022     /* Update the storage to use the new data in one step. */
6023     if (SUCCEEDED(hr))
6024       hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
6025         root_entry->transactedParentEntry, &data);
6026 
6027     if (SUCCEEDED(hr))
6028     {
6029       data.dirRootEntry = dir_root_ref;
6030       data.clsid = root_entry->data.clsid;
6031       data.ctime = root_entry->data.ctime;
6032       data.mtime = root_entry->data.mtime;
6033 
6034       hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
6035         root_entry->transactedParentEntry, &data);
6036     }
6037 
6038     /* Try to flush after updating the root storage, but if the flush fails, keep
6039      * going, on the theory that it'll either succeed later or the subsequent
6040      * writes will fail. */
6041     StorageBaseImpl_Flush(This->transactedParent);
6042 
6043     if (SUCCEEDED(hr))
6044     {
6045       /* Destroy the old now-orphaned data. */
6046       for (i=0; i<This->entries_size; i++)
6047       {
6048         TransactedDirEntry *entry = &This->entries[i];
6049         if (entry->inuse)
6050         {
6051           if (entry->deleted)
6052           {
6053             StorageBaseImpl_StreamSetSize(This->transactedParent,
6054               entry->transactedParentEntry, zero);
6055             StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6056               entry->transactedParentEntry);
6057             memset(entry, 0, sizeof(TransactedDirEntry));
6058             This->firstFreeEntry = min(i, This->firstFreeEntry);
6059           }
6060           else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
6061           {
6062             if (entry->transactedParentEntry != DIRENTRY_NULL)
6063               StorageBaseImpl_DestroyDirEntry(This->transactedParent,
6064                 entry->transactedParentEntry);
6065             if (entry->stream_dirty)
6066             {
6067               StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
6068               StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
6069               entry->stream_dirty = FALSE;
6070             }
6071             entry->dirty = FALSE;
6072             entry->transactedParentEntry = entry->newTransactedParentEntry;
6073           }
6074         }
6075       }
6076     }
6077     else
6078     {
6079       TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
6080     }
6081 
6082     if (SUCCEEDED(hr))
6083       hr = StorageBaseImpl_Flush(This->transactedParent);
6084 end:
6085     StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6086   }
6087 
6088   TRACE("<-- %08x\n", hr);
6089   return hr;
6090 }
6091 
TransactedSnapshotImpl_Revert(IStorage * iface)6092 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
6093   IStorage*            iface)
6094 {
6095   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
6096   ULARGE_INTEGER zero;
6097   ULONG i;
6098 
6099   TRACE("(%p)\n", iface);
6100 
6101   /* Destroy the open objects. */
6102   StorageBaseImpl_DeleteAll(&This->base);
6103 
6104   /* Clear out the scratch file. */
6105   zero.QuadPart = 0;
6106   for (i=0; i<This->entries_size; i++)
6107   {
6108     if (This->entries[i].stream_dirty)
6109     {
6110       StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
6111         zero);
6112 
6113       StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
6114     }
6115   }
6116 
6117   memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6118 
6119   This->firstFreeEntry = 0;
6120   This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6121 
6122   return S_OK;
6123 }
6124 
TransactedSnapshotImpl_Invalidate(StorageBaseImpl * This)6125 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6126 {
6127   if (!This->reverted)
6128   {
6129     TRACE("Storage invalidated (stg=%p)\n", This);
6130 
6131     This->reverted = TRUE;
6132 
6133     StorageBaseImpl_DeleteAll(This);
6134   }
6135 }
6136 
TransactedSnapshotImpl_Destroy(StorageBaseImpl * iface)6137 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6138 {
6139   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6140 
6141   IStorage_Revert(&This->base.IStorage_iface);
6142   IStorage_Release(&This->transactedParent->IStorage_iface);
6143   IStorage_Release(&This->scratch->IStorage_iface);
6144   HeapFree(GetProcessHeap(), 0, This->entries);
6145   HeapFree(GetProcessHeap(), 0, This);
6146 }
6147 
TransactedSnapshotImpl_Flush(StorageBaseImpl * iface)6148 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6149 {
6150   /* We only need to flush when committing. */
6151   return S_OK;
6152 }
6153 
TransactedSnapshotImpl_GetFilename(StorageBaseImpl * iface,LPWSTR * result)6154 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6155 {
6156   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6157 
6158   return StorageBaseImpl_GetFilename(This->transactedParent, result);
6159 }
6160 
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl * base,const DirEntry * newData,DirRef * index)6161 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
6162   const DirEntry *newData, DirRef *index)
6163 {
6164   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6165   DirRef new_ref;
6166   TransactedDirEntry *new_entry;
6167 
6168   new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
6169   if (new_ref == DIRENTRY_NULL)
6170     return E_OUTOFMEMORY;
6171 
6172   new_entry = &This->entries[new_ref];
6173 
6174   new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
6175   new_entry->read = TRUE;
6176   new_entry->dirty = TRUE;
6177   memcpy(&new_entry->data, newData, sizeof(DirEntry));
6178 
6179   *index = new_ref;
6180 
6181   TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
6182 
6183   return S_OK;
6184 }
6185 
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl * base,DirRef index,const DirEntry * data)6186 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6187   DirRef index, const DirEntry *data)
6188 {
6189   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6190   HRESULT hr;
6191 
6192   TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6193 
6194   hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6195   if (FAILED(hr))
6196   {
6197     TRACE("<-- %08x\n", hr);
6198     return hr;
6199   }
6200 
6201   memcpy(&This->entries[index].data, data, sizeof(DirEntry));
6202 
6203   if (index != This->base.storageDirEntry)
6204   {
6205     This->entries[index].dirty = TRUE;
6206 
6207     if (data->size.QuadPart == 0 &&
6208         This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6209     {
6210       /* Since this entry is modified, and we aren't using its stream data, we
6211        * no longer care about the original entry. */
6212       DirRef delete_ref;
6213       delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6214 
6215       if (delete_ref != DIRENTRY_NULL)
6216         This->entries[delete_ref].deleted = TRUE;
6217 
6218       This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6219     }
6220   }
6221   TRACE("<-- S_OK\n");
6222   return S_OK;
6223 }
6224 
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl * base,DirRef index,DirEntry * data)6225 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6226   DirRef index, DirEntry *data)
6227 {
6228   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6229   HRESULT hr;
6230 
6231   hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6232   if (FAILED(hr))
6233   {
6234     TRACE("<-- %08x\n", hr);
6235     return hr;
6236   }
6237 
6238   memcpy(data, &This->entries[index].data, sizeof(DirEntry));
6239 
6240   TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
6241 
6242   return S_OK;
6243 }
6244 
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl * base,DirRef index)6245 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6246   DirRef index)
6247 {
6248   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6249 
6250   if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
6251       This->entries[index].data.size.QuadPart != 0)
6252   {
6253     /* If we deleted this entry while it has stream data. We must have left the
6254      * data because some other entry is using it, and we need to leave the
6255      * original entry alone. */
6256     memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
6257     This->firstFreeEntry = min(index, This->firstFreeEntry);
6258   }
6259   else
6260   {
6261     This->entries[index].deleted = TRUE;
6262   }
6263 
6264   return S_OK;
6265 }
6266 
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)6267 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6268   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6269 {
6270   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6271 
6272   if (This->entries[index].stream_dirty)
6273   {
6274     return StorageBaseImpl_StreamReadAt(This->scratch,
6275         This->entries[index].stream_entry, offset, size, buffer, bytesRead);
6276   }
6277   else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
6278   {
6279     /* This stream doesn't live in the parent, and we haven't allocated storage
6280      * for it yet */
6281     *bytesRead = 0;
6282     return S_OK;
6283   }
6284   else
6285   {
6286     return StorageBaseImpl_StreamReadAt(This->transactedParent,
6287         This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
6288   }
6289 }
6290 
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)6291 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6292   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6293 {
6294   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6295   HRESULT hr;
6296 
6297   hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6298   if (FAILED(hr))
6299   {
6300     TRACE("<-- %08x\n", hr);
6301     return hr;
6302   }
6303 
6304   hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6305   if (FAILED(hr))
6306   {
6307     TRACE("<-- %08x\n", hr);
6308     return hr;
6309   }
6310 
6311   hr = StorageBaseImpl_StreamWriteAt(This->scratch,
6312     This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
6313 
6314   if (SUCCEEDED(hr) && size != 0)
6315     This->entries[index].data.size.QuadPart = max(
6316         This->entries[index].data.size.QuadPart,
6317         offset.QuadPart + size);
6318 
6319   TRACE("<-- %08x\n", hr);
6320   return hr;
6321 }
6322 
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER newsize)6323 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6324   DirRef index, ULARGE_INTEGER newsize)
6325 {
6326   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6327   HRESULT hr;
6328 
6329   hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
6330   if (FAILED(hr))
6331   {
6332     TRACE("<-- %08x\n", hr);
6333     return hr;
6334   }
6335 
6336   if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
6337     return S_OK;
6338 
6339   if (newsize.QuadPart == 0)
6340   {
6341     /* Destroy any parent references or entries in the scratch file. */
6342     if (This->entries[index].stream_dirty)
6343     {
6344       ULARGE_INTEGER zero;
6345       zero.QuadPart = 0;
6346       StorageBaseImpl_StreamSetSize(This->scratch,
6347         This->entries[index].stream_entry, zero);
6348       StorageBaseImpl_DestroyDirEntry(This->scratch,
6349         This->entries[index].stream_entry);
6350       This->entries[index].stream_dirty = FALSE;
6351     }
6352     else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
6353     {
6354       DirRef delete_ref;
6355       delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
6356 
6357       if (delete_ref != DIRENTRY_NULL)
6358         This->entries[delete_ref].deleted = TRUE;
6359 
6360       This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
6361     }
6362   }
6363   else
6364   {
6365     hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
6366     if (FAILED(hr)) return hr;
6367 
6368     hr = StorageBaseImpl_StreamSetSize(This->scratch,
6369       This->entries[index].stream_entry, newsize);
6370   }
6371 
6372   if (SUCCEEDED(hr))
6373     This->entries[index].data.size = newsize;
6374 
6375   TRACE("<-- %08x\n", hr);
6376   return hr;
6377 }
6378 
TransactedSnapshotImpl_StreamLink(StorageBaseImpl * base,DirRef dst,DirRef src)6379 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6380   DirRef dst, DirRef src)
6381 {
6382   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6383   HRESULT hr;
6384   TransactedDirEntry *dst_entry, *src_entry;
6385 
6386   hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
6387   if (FAILED(hr))
6388   {
6389     TRACE("<-- %08x\n", hr);
6390     return hr;
6391   }
6392 
6393   hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
6394   if (FAILED(hr))
6395   {
6396     TRACE("<-- %08x\n", hr);
6397     return hr;
6398   }
6399 
6400   dst_entry = &This->entries[dst];
6401   src_entry = &This->entries[src];
6402 
6403   dst_entry->stream_dirty = src_entry->stream_dirty;
6404   dst_entry->stream_entry = src_entry->stream_entry;
6405   dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
6406   dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
6407   dst_entry->data.size = src_entry->data.size;
6408 
6409   return S_OK;
6410 }
6411 
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl * base,ULONG * result,BOOL refresh)6412 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6413   ULONG* result, BOOL refresh)
6414 {
6415   return E_NOTIMPL;
6416 }
6417 
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl * base,ULONG value)6418 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6419   ULONG value)
6420 {
6421   return E_NOTIMPL;
6422 }
6423 
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl * base,BOOL write)6424 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6425 {
6426   return E_NOTIMPL;
6427 }
6428 
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl * base,BOOL write)6429 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6430 {
6431   return E_NOTIMPL;
6432 }
6433 
6434 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6435 {
6436     StorageBaseImpl_QueryInterface,
6437     StorageBaseImpl_AddRef,
6438     StorageBaseImpl_Release,
6439     StorageBaseImpl_CreateStream,
6440     StorageBaseImpl_OpenStream,
6441     StorageBaseImpl_CreateStorage,
6442     StorageBaseImpl_OpenStorage,
6443     StorageBaseImpl_CopyTo,
6444     StorageBaseImpl_MoveElementTo,
6445     TransactedSnapshotImpl_Commit,
6446     TransactedSnapshotImpl_Revert,
6447     StorageBaseImpl_EnumElements,
6448     StorageBaseImpl_DestroyElement,
6449     StorageBaseImpl_RenameElement,
6450     StorageBaseImpl_SetElementTimes,
6451     StorageBaseImpl_SetClass,
6452     StorageBaseImpl_SetStateBits,
6453     StorageBaseImpl_Stat
6454 };
6455 
6456 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6457 {
6458   TransactedSnapshotImpl_Destroy,
6459   TransactedSnapshotImpl_Invalidate,
6460   TransactedSnapshotImpl_Flush,
6461   TransactedSnapshotImpl_GetFilename,
6462   TransactedSnapshotImpl_CreateDirEntry,
6463   TransactedSnapshotImpl_WriteDirEntry,
6464   TransactedSnapshotImpl_ReadDirEntry,
6465   TransactedSnapshotImpl_DestroyDirEntry,
6466   TransactedSnapshotImpl_StreamReadAt,
6467   TransactedSnapshotImpl_StreamWriteAt,
6468   TransactedSnapshotImpl_StreamSetSize,
6469   TransactedSnapshotImpl_StreamLink,
6470   TransactedSnapshotImpl_GetTransactionSig,
6471   TransactedSnapshotImpl_SetTransactionSig,
6472   TransactedSnapshotImpl_LockTransaction,
6473   TransactedSnapshotImpl_UnlockTransaction
6474 };
6475 
TransactedSnapshotImpl_Construct(StorageBaseImpl * parentStorage,TransactedSnapshotImpl ** result)6476 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
6477   TransactedSnapshotImpl** result)
6478 {
6479   HRESULT hr;
6480 
6481   *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
6482   if (*result)
6483   {
6484     IStorage *scratch;
6485 
6486     (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6487 
6488     /* This is OK because the property set storage functions use the IStorage functions. */
6489     (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6490     (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6491 
6492     list_init(&(*result)->base.strmHead);
6493 
6494     list_init(&(*result)->base.storageHead);
6495 
6496     (*result)->base.ref = 1;
6497 
6498     (*result)->base.openFlags = parentStorage->openFlags;
6499 
6500     /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6501     StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6502 
6503     /* Create a new temporary storage to act as the scratch file. */
6504     hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
6505         0, &scratch);
6506     (*result)->scratch = impl_from_IStorage(scratch);
6507 
6508     if (SUCCEEDED(hr))
6509     {
6510         ULONG num_entries = 20;
6511 
6512         (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
6513         (*result)->entries_size = num_entries;
6514         (*result)->firstFreeEntry = 0;
6515 
6516         if ((*result)->entries)
6517         {
6518             /* parentStorage already has 1 reference, which we take over here. */
6519             (*result)->transactedParent = parentStorage;
6520 
6521             parentStorage->transactedChild = &(*result)->base;
6522 
6523             (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
6524         }
6525         else
6526         {
6527             IStorage_Release(scratch);
6528 
6529             hr = E_OUTOFMEMORY;
6530         }
6531     }
6532 
6533     if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6534 
6535     return hr;
6536   }
6537   else
6538     return E_OUTOFMEMORY;
6539 }
6540 
6541 
6542 /************************************************************************
6543  * TransactedSharedImpl implementation
6544  ***********************************************************************/
6545 
TransactedSharedImpl_Invalidate(StorageBaseImpl * This)6546 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
6547 {
6548   if (!This->reverted)
6549   {
6550     TRACE("Storage invalidated (stg=%p)\n", This);
6551 
6552     This->reverted = TRUE;
6553 
6554     StorageBaseImpl_DeleteAll(This);
6555   }
6556 }
6557 
TransactedSharedImpl_Destroy(StorageBaseImpl * iface)6558 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
6559 {
6560   TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6561 
6562   TransactedSharedImpl_Invalidate(&This->base);
6563   IStorage_Release(&This->transactedParent->IStorage_iface);
6564   IStorage_Release(&This->scratch->base.IStorage_iface);
6565   HeapFree(GetProcessHeap(), 0, This);
6566 }
6567 
TransactedSharedImpl_Flush(StorageBaseImpl * iface)6568 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
6569 {
6570   /* We only need to flush when committing. */
6571   return S_OK;
6572 }
6573 
TransactedSharedImpl_GetFilename(StorageBaseImpl * iface,LPWSTR * result)6574 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6575 {
6576   TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6577 
6578   return StorageBaseImpl_GetFilename(This->transactedParent, result);
6579 }
6580 
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl * base,const DirEntry * newData,DirRef * index)6581 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
6582   const DirEntry *newData, DirRef *index)
6583 {
6584   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6585 
6586   return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
6587     newData, index);
6588 }
6589 
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl * base,DirRef index,const DirEntry * data)6590 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
6591   DirRef index, const DirEntry *data)
6592 {
6593   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6594 
6595   return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
6596     index, data);
6597 }
6598 
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl * base,DirRef index,DirEntry * data)6599 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
6600   DirRef index, DirEntry *data)
6601 {
6602   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6603 
6604   return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
6605     index, data);
6606 }
6607 
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl * base,DirRef index)6608 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
6609   DirRef index)
6610 {
6611   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6612 
6613   return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
6614     index);
6615 }
6616 
TransactedSharedImpl_StreamReadAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)6617 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
6618   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6619 {
6620   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6621 
6622   return StorageBaseImpl_StreamReadAt(&This->scratch->base,
6623     index, offset, size, buffer, bytesRead);
6624 }
6625 
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)6626 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
6627   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6628 {
6629   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6630 
6631   return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
6632     index, offset, size, buffer, bytesWritten);
6633 }
6634 
TransactedSharedImpl_StreamSetSize(StorageBaseImpl * base,DirRef index,ULARGE_INTEGER newsize)6635 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
6636   DirRef index, ULARGE_INTEGER newsize)
6637 {
6638   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6639 
6640   return StorageBaseImpl_StreamSetSize(&This->scratch->base,
6641     index, newsize);
6642 }
6643 
TransactedSharedImpl_StreamLink(StorageBaseImpl * base,DirRef dst,DirRef src)6644 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
6645   DirRef dst, DirRef src)
6646 {
6647   TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6648 
6649   return StorageBaseImpl_StreamLink(&This->scratch->base,
6650     dst, src);
6651 }
6652 
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl * base,ULONG * result,BOOL refresh)6653 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
6654   ULONG* result, BOOL refresh)
6655 {
6656   return E_NOTIMPL;
6657 }
6658 
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl * base,ULONG value)6659 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
6660   ULONG value)
6661 {
6662   return E_NOTIMPL;
6663 }
6664 
TransactedSharedImpl_LockTransaction(StorageBaseImpl * base,BOOL write)6665 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6666 {
6667   return E_NOTIMPL;
6668 }
6669 
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl * base,BOOL write)6670 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6671 {
6672   return E_NOTIMPL;
6673 }
6674 
TransactedSharedImpl_Commit(IStorage * iface,DWORD grfCommitFlags)6675 static HRESULT WINAPI TransactedSharedImpl_Commit(
6676   IStorage*            iface,
6677   DWORD                  grfCommitFlags)  /* [in] */
6678 {
6679   TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6680   DirRef new_storage_ref, prev_storage_ref;
6681   DirEntry src_data, dst_data;
6682   HRESULT hr;
6683   ULONG transactionSig;
6684 
6685   TRACE("(%p,%x)\n", iface, grfCommitFlags);
6686 
6687   /* Cannot commit a read-only transacted storage */
6688   if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
6689     return STG_E_ACCESSDENIED;
6690 
6691   hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
6692   if (hr == E_NOTIMPL) hr = S_OK;
6693   if (SUCCEEDED(hr))
6694   {
6695     hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
6696     if (SUCCEEDED(hr))
6697     {
6698       if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
6699         hr = STG_E_NOTCURRENT;
6700 
6701       if (SUCCEEDED(hr))
6702         hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
6703     }
6704     else if (hr == E_NOTIMPL)
6705       hr = S_OK;
6706 
6707     if (SUCCEEDED(hr))
6708       hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
6709 
6710     /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
6711     if (SUCCEEDED(hr))
6712       hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
6713 
6714     if (SUCCEEDED(hr))
6715       hr = StorageBaseImpl_Flush(This->transactedParent);
6716 
6717     if (SUCCEEDED(hr))
6718       hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6719 
6720     if (SUCCEEDED(hr))
6721     {
6722       prev_storage_ref = dst_data.dirRootEntry;
6723       dst_data.dirRootEntry = new_storage_ref;
6724       dst_data.clsid = src_data.clsid;
6725       dst_data.ctime = src_data.ctime;
6726       dst_data.mtime = src_data.mtime;
6727       hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
6728     }
6729 
6730     if (SUCCEEDED(hr))
6731     {
6732       /* Try to flush after updating the root storage, but if the flush fails, keep
6733        * going, on the theory that it'll either succeed later or the subsequent
6734        * writes will fail. */
6735       StorageBaseImpl_Flush(This->transactedParent);
6736 
6737       hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
6738     }
6739 
6740     if (SUCCEEDED(hr))
6741       hr = StorageBaseImpl_Flush(This->transactedParent);
6742 
6743     StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
6744 
6745     if (SUCCEEDED(hr))
6746       hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
6747 
6748     if (SUCCEEDED(hr))
6749     {
6750       This->lastTransactionSig = transactionSig+1;
6751     }
6752   }
6753   TRACE("<-- %08x\n", hr);
6754   return hr;
6755 }
6756 
TransactedSharedImpl_Revert(IStorage * iface)6757 static HRESULT WINAPI TransactedSharedImpl_Revert(
6758   IStorage*            iface)
6759 {
6760   TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
6761 
6762   TRACE("(%p)\n", iface);
6763 
6764   /* Destroy the open objects. */
6765   StorageBaseImpl_DeleteAll(&This->base);
6766 
6767   return IStorage_Revert(&This->scratch->base.IStorage_iface);
6768 }
6769 
6770 static const IStorageVtbl TransactedSharedImpl_Vtbl =
6771 {
6772     StorageBaseImpl_QueryInterface,
6773     StorageBaseImpl_AddRef,
6774     StorageBaseImpl_Release,
6775     StorageBaseImpl_CreateStream,
6776     StorageBaseImpl_OpenStream,
6777     StorageBaseImpl_CreateStorage,
6778     StorageBaseImpl_OpenStorage,
6779     StorageBaseImpl_CopyTo,
6780     StorageBaseImpl_MoveElementTo,
6781     TransactedSharedImpl_Commit,
6782     TransactedSharedImpl_Revert,
6783     StorageBaseImpl_EnumElements,
6784     StorageBaseImpl_DestroyElement,
6785     StorageBaseImpl_RenameElement,
6786     StorageBaseImpl_SetElementTimes,
6787     StorageBaseImpl_SetClass,
6788     StorageBaseImpl_SetStateBits,
6789     StorageBaseImpl_Stat
6790 };
6791 
6792 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6793 {
6794   TransactedSharedImpl_Destroy,
6795   TransactedSharedImpl_Invalidate,
6796   TransactedSharedImpl_Flush,
6797   TransactedSharedImpl_GetFilename,
6798   TransactedSharedImpl_CreateDirEntry,
6799   TransactedSharedImpl_WriteDirEntry,
6800   TransactedSharedImpl_ReadDirEntry,
6801   TransactedSharedImpl_DestroyDirEntry,
6802   TransactedSharedImpl_StreamReadAt,
6803   TransactedSharedImpl_StreamWriteAt,
6804   TransactedSharedImpl_StreamSetSize,
6805   TransactedSharedImpl_StreamLink,
6806   TransactedSharedImpl_GetTransactionSig,
6807   TransactedSharedImpl_SetTransactionSig,
6808   TransactedSharedImpl_LockTransaction,
6809   TransactedSharedImpl_UnlockTransaction
6810 };
6811 
TransactedSharedImpl_Construct(StorageBaseImpl * parentStorage,TransactedSharedImpl ** result)6812 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
6813   TransactedSharedImpl** result)
6814 {
6815   HRESULT hr;
6816 
6817   *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
6818   if (*result)
6819   {
6820     IStorage *scratch;
6821 
6822     (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6823 
6824     /* This is OK because the property set storage functions use the IStorage functions. */
6825     (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
6826     (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6827 
6828     list_init(&(*result)->base.strmHead);
6829 
6830     list_init(&(*result)->base.storageHead);
6831 
6832     (*result)->base.ref = 1;
6833 
6834     (*result)->base.openFlags = parentStorage->openFlags;
6835 
6836     hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6837 
6838     if (SUCCEEDED(hr))
6839     {
6840       STGOPTIONS stgo;
6841 
6842       /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6843       StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6844 
6845       stgo.usVersion = 1;
6846       stgo.reserved = 0;
6847       stgo.ulSectorSize = 4096;
6848       stgo.pwcsTemplateFile = NULL;
6849 
6850       /* Create a new temporary storage to act as the scratch file. */
6851       hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6852           STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6853       (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6854 
6855       if (SUCCEEDED(hr))
6856       {
6857         hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6858           parentStorage, parentStorage->storageDirEntry);
6859 
6860         if (SUCCEEDED(hr))
6861         {
6862           hr = IStorage_Commit(scratch, STGC_DEFAULT);
6863 
6864           (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6865           (*result)->transactedParent = parentStorage;
6866         }
6867 
6868         if (FAILED(hr))
6869           IStorage_Release(scratch);
6870       }
6871 
6872       StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6873     }
6874 
6875     if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6876 
6877     return hr;
6878   }
6879   else
6880     return E_OUTOFMEMORY;
6881 }
6882 
Storage_ConstructTransacted(StorageBaseImpl * parentStorage,BOOL toplevel,StorageBaseImpl ** result)6883 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6884   BOOL toplevel, StorageBaseImpl** result)
6885 {
6886   static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6887 
6888   if (parentStorage->openFlags & fixme_flags)
6889   {
6890     fixme_flags &= ~parentStorage->openFlags;
6891     FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6892   }
6893 
6894   if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6895       STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6896       STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6897   {
6898     /* Need to create a temp file for the snapshot */
6899     return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6900   }
6901 
6902   return TransactedSnapshotImpl_Construct(parentStorage,
6903     (TransactedSnapshotImpl**)result);
6904 }
6905 
Storage_Construct(HANDLE hFile,LPCOLESTR pwcsName,ILockBytes * pLkbyt,DWORD openFlags,BOOL fileBased,BOOL create,ULONG sector_size,StorageBaseImpl ** result)6906 static HRESULT Storage_Construct(
6907   HANDLE       hFile,
6908   LPCOLESTR    pwcsName,
6909   ILockBytes*  pLkbyt,
6910   DWORD        openFlags,
6911   BOOL         fileBased,
6912   BOOL         create,
6913   ULONG        sector_size,
6914   StorageBaseImpl** result)
6915 {
6916   StorageImpl *newStorage;
6917   StorageBaseImpl *newTransactedStorage;
6918   HRESULT hr;
6919 
6920   hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6921   if (FAILED(hr)) goto end;
6922 
6923   if (openFlags & STGM_TRANSACTED)
6924   {
6925     hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6926     if (FAILED(hr))
6927       IStorage_Release(&newStorage->base.IStorage_iface);
6928     else
6929       *result = newTransactedStorage;
6930   }
6931   else
6932     *result = &newStorage->base;
6933 
6934 end:
6935   return hr;
6936 }
6937 
6938 
6939 /************************************************************************
6940  * StorageUtl helper functions
6941  ***********************************************************************/
6942 
StorageUtl_ReadWord(const BYTE * buffer,ULONG offset,WORD * value)6943 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6944 {
6945   WORD tmp;
6946 
6947   memcpy(&tmp, buffer+offset, sizeof(WORD));
6948   *value = lendian16toh(tmp);
6949 }
6950 
StorageUtl_WriteWord(BYTE * buffer,ULONG offset,WORD value)6951 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6952 {
6953   value = htole16(value);
6954   memcpy(buffer+offset, &value, sizeof(WORD));
6955 }
6956 
StorageUtl_ReadDWord(const BYTE * buffer,ULONG offset,DWORD * value)6957 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6958 {
6959   DWORD tmp;
6960 
6961   memcpy(&tmp, buffer+offset, sizeof(DWORD));
6962   *value = lendian32toh(tmp);
6963 }
6964 
StorageUtl_WriteDWord(BYTE * buffer,ULONG offset,DWORD value)6965 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6966 {
6967   value = htole32(value);
6968   memcpy(buffer+offset, &value, sizeof(DWORD));
6969 }
6970 
StorageUtl_ReadULargeInteger(const BYTE * buffer,ULONG offset,ULARGE_INTEGER * value)6971 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6972  ULARGE_INTEGER* value)
6973 {
6974 #ifdef WORDS_BIGENDIAN
6975     ULARGE_INTEGER tmp;
6976 
6977     memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6978     value->u.LowPart = htole32(tmp.u.HighPart);
6979     value->u.HighPart = htole32(tmp.u.LowPart);
6980 #else
6981     memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6982 #endif
6983 }
6984 
StorageUtl_WriteULargeInteger(BYTE * buffer,ULONG offset,const ULARGE_INTEGER * value)6985 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6986  const ULARGE_INTEGER *value)
6987 {
6988 #ifdef WORDS_BIGENDIAN
6989     ULARGE_INTEGER tmp;
6990 
6991     tmp.u.LowPart = htole32(value->u.HighPart);
6992     tmp.u.HighPart = htole32(value->u.LowPart);
6993     memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6994 #else
6995     memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6996 #endif
6997 }
6998 
StorageUtl_ReadGUID(const BYTE * buffer,ULONG offset,GUID * value)6999 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
7000 {
7001   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
7002   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
7003   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
7004 
7005   memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
7006 }
7007 
StorageUtl_WriteGUID(BYTE * buffer,ULONG offset,const GUID * value)7008 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
7009 {
7010   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
7011   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
7012   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
7013 
7014   memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
7015 }
7016 
StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl * storage,STATSTG * destination,const DirEntry * source,int statFlags)7017 void StorageUtl_CopyDirEntryToSTATSTG(
7018   StorageBaseImpl*      storage,
7019   STATSTG*              destination,
7020   const DirEntry*       source,
7021   int                   statFlags)
7022 {
7023   /*
7024    * The copy of the string occurs only when the flag is not set
7025    */
7026   if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
7027   {
7028     /* Use the filename for the root storage. */
7029     destination->pwcsName = 0;
7030     StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
7031   }
7032   else if( ((statFlags & STATFLAG_NONAME) != 0) ||
7033        (source->name[0] == 0) )
7034   {
7035     destination->pwcsName = 0;
7036   }
7037   else
7038   {
7039     destination->pwcsName =
7040       CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
7041 
7042     lstrcpyW(destination->pwcsName, source->name);
7043   }
7044 
7045   switch (source->stgType)
7046   {
7047     case STGTY_STORAGE:
7048     case STGTY_ROOT:
7049       destination->type = STGTY_STORAGE;
7050       break;
7051     case STGTY_STREAM:
7052       destination->type = STGTY_STREAM;
7053       break;
7054     default:
7055       destination->type = STGTY_STREAM;
7056       break;
7057   }
7058 
7059   destination->cbSize            = source->size;
7060 /*
7061   currentReturnStruct->mtime     = {0}; TODO
7062   currentReturnStruct->ctime     = {0};
7063   currentReturnStruct->atime     = {0};
7064 */
7065   destination->grfMode           = 0;
7066   destination->grfLocksSupported = 0;
7067   destination->clsid             = source->clsid;
7068   destination->grfStateBits      = 0;
7069   destination->reserved          = 0;
7070 }
7071 
7072 
7073 /************************************************************************
7074  * BlockChainStream implementation
7075  ***********************************************************************/
7076 
7077 /******************************************************************************
7078  *      BlockChainStream_GetHeadOfChain
7079  *
7080  * Returns the head of this stream chain.
7081  * Some special chains don't have directory entries, their heads are kept in
7082  * This->headOfStreamPlaceHolder.
7083  *
7084  */
BlockChainStream_GetHeadOfChain(BlockChainStream * This)7085 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
7086 {
7087   DirEntry  chainEntry;
7088   HRESULT   hr;
7089 
7090   if (This->headOfStreamPlaceHolder != 0)
7091     return *(This->headOfStreamPlaceHolder);
7092 
7093   if (This->ownerDirEntry != DIRENTRY_NULL)
7094   {
7095     hr = StorageImpl_ReadDirEntry(
7096                       This->parentStorage,
7097                       This->ownerDirEntry,
7098                       &chainEntry);
7099 
7100     if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7101       return chainEntry.startingBlock;
7102   }
7103 
7104   return BLOCK_END_OF_CHAIN;
7105 }
7106 
7107 /* Read and save the index of all blocks in this stream. */
BlockChainStream_UpdateIndexCache(BlockChainStream * This)7108 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7109 {
7110   ULONG  next_sector, next_offset;
7111   HRESULT hr;
7112   struct BlockChainRun *last_run;
7113 
7114   if (This->indexCacheLen == 0)
7115   {
7116     last_run = NULL;
7117     next_offset = 0;
7118     next_sector = BlockChainStream_GetHeadOfChain(This);
7119   }
7120   else
7121   {
7122     last_run = &This->indexCache[This->indexCacheLen-1];
7123     next_offset = last_run->lastOffset+1;
7124     hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
7125         last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
7126         &next_sector);
7127     if (FAILED(hr)) return hr;
7128   }
7129 
7130   while (next_sector != BLOCK_END_OF_CHAIN)
7131   {
7132     if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
7133     {
7134       /* Add the current block to the cache. */
7135       if (This->indexCacheSize == 0)
7136       {
7137         This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
7138         if (!This->indexCache) return E_OUTOFMEMORY;
7139         This->indexCacheSize = 16;
7140       }
7141       else if (This->indexCacheSize == This->indexCacheLen)
7142       {
7143         struct BlockChainRun *new_cache;
7144         ULONG new_size;
7145 
7146         new_size = This->indexCacheSize * 2;
7147         new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
7148         if (!new_cache) return E_OUTOFMEMORY;
7149         memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
7150 
7151         HeapFree(GetProcessHeap(), 0, This->indexCache);
7152         This->indexCache = new_cache;
7153         This->indexCacheSize = new_size;
7154       }
7155 
7156       This->indexCacheLen++;
7157       last_run = &This->indexCache[This->indexCacheLen-1];
7158       last_run->firstSector = next_sector;
7159       last_run->firstOffset = next_offset;
7160     }
7161 
7162     last_run->lastOffset = next_offset;
7163 
7164     /* Find the next block. */
7165     next_offset++;
7166     hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
7167     if (FAILED(hr)) return hr;
7168   }
7169 
7170   if (This->indexCacheLen)
7171   {
7172     This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
7173     This->numBlocks = last_run->lastOffset+1;
7174   }
7175   else
7176   {
7177     This->tailIndex = BLOCK_END_OF_CHAIN;
7178     This->numBlocks = 0;
7179   }
7180 
7181   return S_OK;
7182 }
7183 
7184 /* Locate the nth block in this stream. */
BlockChainStream_GetSectorOfOffset(BlockChainStream * This,ULONG offset)7185 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7186 {
7187   ULONG min_offset = 0, max_offset = This->numBlocks-1;
7188   ULONG min_run = 0, max_run = This->indexCacheLen-1;
7189 
7190   if (offset >= This->numBlocks)
7191     return BLOCK_END_OF_CHAIN;
7192 
7193   while (min_run < max_run)
7194   {
7195     ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
7196     if (offset < This->indexCache[run_to_check].firstOffset)
7197     {
7198       max_offset = This->indexCache[run_to_check].firstOffset-1;
7199       max_run = run_to_check-1;
7200     }
7201     else if (offset > This->indexCache[run_to_check].lastOffset)
7202     {
7203       min_offset = This->indexCache[run_to_check].lastOffset+1;
7204       min_run = run_to_check+1;
7205     }
7206     else
7207       /* Block is in this run. */
7208       min_run = max_run = run_to_check;
7209   }
7210 
7211   return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
7212 }
7213 
BlockChainStream_GetBlockAtOffset(BlockChainStream * This,ULONG index,BlockChainBlock ** block,ULONG * sector,BOOL create)7214 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7215     ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
7216 {
7217   BlockChainBlock *result=NULL;
7218   int i;
7219 
7220   for (i=0; i<2; i++)
7221     if (This->cachedBlocks[i].index == index)
7222     {
7223       *sector = This->cachedBlocks[i].sector;
7224       *block = &This->cachedBlocks[i];
7225       return S_OK;
7226     }
7227 
7228   *sector = BlockChainStream_GetSectorOfOffset(This, index);
7229   if (*sector == BLOCK_END_OF_CHAIN)
7230     return STG_E_DOCFILECORRUPT;
7231 
7232   if (create)
7233   {
7234     if (This->cachedBlocks[0].index == 0xffffffff)
7235       result = &This->cachedBlocks[0];
7236     else if (This->cachedBlocks[1].index == 0xffffffff)
7237       result = &This->cachedBlocks[1];
7238     else
7239     {
7240       result = &This->cachedBlocks[This->blockToEvict++];
7241       if (This->blockToEvict == 2)
7242         This->blockToEvict = 0;
7243     }
7244 
7245     if (result->dirty)
7246     {
7247       if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
7248         return STG_E_WRITEFAULT;
7249       result->dirty = FALSE;
7250     }
7251 
7252     result->read = FALSE;
7253     result->index = index;
7254     result->sector = *sector;
7255   }
7256 
7257   *block = result;
7258   return S_OK;
7259 }
7260 
BlockChainStream_Construct(StorageImpl * parentStorage,ULONG * headOfStreamPlaceHolder,DirRef dirEntry)7261 BlockChainStream* BlockChainStream_Construct(
7262   StorageImpl* parentStorage,
7263   ULONG*         headOfStreamPlaceHolder,
7264   DirRef         dirEntry)
7265 {
7266   BlockChainStream* newStream;
7267 
7268   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
7269   if(!newStream)
7270     return NULL;
7271 
7272   newStream->parentStorage           = parentStorage;
7273   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7274   newStream->ownerDirEntry           = dirEntry;
7275   newStream->indexCache              = NULL;
7276   newStream->indexCacheLen           = 0;
7277   newStream->indexCacheSize          = 0;
7278   newStream->cachedBlocks[0].index = 0xffffffff;
7279   newStream->cachedBlocks[0].dirty = FALSE;
7280   newStream->cachedBlocks[1].index = 0xffffffff;
7281   newStream->cachedBlocks[1].dirty = FALSE;
7282   newStream->blockToEvict          = 0;
7283 
7284   if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
7285   {
7286     HeapFree(GetProcessHeap(), 0, newStream->indexCache);
7287     HeapFree(GetProcessHeap(), 0, newStream);
7288     return NULL;
7289   }
7290 
7291   return newStream;
7292 }
7293 
BlockChainStream_Flush(BlockChainStream * This)7294 HRESULT BlockChainStream_Flush(BlockChainStream* This)
7295 {
7296   int i;
7297   if (!This) return S_OK;
7298   for (i=0; i<2; i++)
7299   {
7300     if (This->cachedBlocks[i].dirty)
7301     {
7302       if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7303         This->cachedBlocks[i].dirty = FALSE;
7304       else
7305         return STG_E_WRITEFAULT;
7306     }
7307   }
7308   return S_OK;
7309 }
7310 
BlockChainStream_Destroy(BlockChainStream * This)7311 void BlockChainStream_Destroy(BlockChainStream* This)
7312 {
7313   if (This)
7314   {
7315     BlockChainStream_Flush(This);
7316     HeapFree(GetProcessHeap(), 0, This->indexCache);
7317   }
7318   HeapFree(GetProcessHeap(), 0, This);
7319 }
7320 
7321 /******************************************************************************
7322  *      BlockChainStream_Shrink
7323  *
7324  * Shrinks this chain in the big block depot.
7325  */
BlockChainStream_Shrink(BlockChainStream * This,ULARGE_INTEGER newSize)7326 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7327                                     ULARGE_INTEGER    newSize)
7328 {
7329   ULONG blockIndex;
7330   ULONG numBlocks;
7331   int i;
7332 
7333   /*
7334    * Figure out how many blocks are needed to contain the new size
7335    */
7336   numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7337 
7338   if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7339     numBlocks++;
7340 
7341   if (numBlocks)
7342   {
7343     /*
7344      * Go to the new end of chain
7345      */
7346     blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7347 
7348     /* Mark the new end of chain */
7349     StorageImpl_SetNextBlockInChain(
7350       This->parentStorage,
7351       blockIndex,
7352       BLOCK_END_OF_CHAIN);
7353 
7354     This->tailIndex = blockIndex;
7355   }
7356   else
7357   {
7358     if (This->headOfStreamPlaceHolder != 0)
7359     {
7360       *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7361     }
7362     else
7363     {
7364       DirEntry chainEntry;
7365       assert(This->ownerDirEntry != DIRENTRY_NULL);
7366 
7367       StorageImpl_ReadDirEntry(
7368         This->parentStorage,
7369         This->ownerDirEntry,
7370         &chainEntry);
7371 
7372       chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7373 
7374       StorageImpl_WriteDirEntry(
7375         This->parentStorage,
7376         This->ownerDirEntry,
7377         &chainEntry);
7378     }
7379 
7380     This->tailIndex = BLOCK_END_OF_CHAIN;
7381   }
7382 
7383   This->numBlocks = numBlocks;
7384 
7385   /*
7386    * Mark the extra blocks as free
7387    */
7388   while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7389   {
7390     struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7391     StorageImpl_FreeBigBlock(This->parentStorage,
7392       last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7393     if (last_run->lastOffset == last_run->firstOffset)
7394       This->indexCacheLen--;
7395     else
7396       last_run->lastOffset--;
7397   }
7398 
7399   /*
7400    * Reset the last accessed block cache.
7401    */
7402   for (i=0; i<2; i++)
7403   {
7404     if (This->cachedBlocks[i].index >= numBlocks)
7405     {
7406       This->cachedBlocks[i].index = 0xffffffff;
7407       This->cachedBlocks[i].dirty = FALSE;
7408     }
7409   }
7410 
7411   return TRUE;
7412 }
7413 
7414 /******************************************************************************
7415  *      BlockChainStream_Enlarge
7416  *
7417  * Grows this chain in the big block depot.
7418  */
BlockChainStream_Enlarge(BlockChainStream * This,ULARGE_INTEGER newSize)7419 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7420                                      ULARGE_INTEGER    newSize)
7421 {
7422   ULONG blockIndex, currentBlock;
7423   ULONG newNumBlocks;
7424   ULONG oldNumBlocks = 0;
7425 
7426   blockIndex = BlockChainStream_GetHeadOfChain(This);
7427 
7428   /*
7429    * Empty chain. Create the head.
7430    */
7431   if (blockIndex == BLOCK_END_OF_CHAIN)
7432   {
7433     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7434     StorageImpl_SetNextBlockInChain(This->parentStorage,
7435                                       blockIndex,
7436                                       BLOCK_END_OF_CHAIN);
7437 
7438     if (This->headOfStreamPlaceHolder != 0)
7439     {
7440       *(This->headOfStreamPlaceHolder) = blockIndex;
7441     }
7442     else
7443     {
7444       DirEntry chainEntry;
7445       assert(This->ownerDirEntry != DIRENTRY_NULL);
7446 
7447       StorageImpl_ReadDirEntry(
7448         This->parentStorage,
7449         This->ownerDirEntry,
7450         &chainEntry);
7451 
7452       chainEntry.startingBlock = blockIndex;
7453 
7454       StorageImpl_WriteDirEntry(
7455         This->parentStorage,
7456         This->ownerDirEntry,
7457         &chainEntry);
7458     }
7459 
7460     This->tailIndex = blockIndex;
7461     This->numBlocks = 1;
7462   }
7463 
7464   /*
7465    * Figure out how many blocks are needed to contain this stream
7466    */
7467   newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7468 
7469   if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7470     newNumBlocks++;
7471 
7472   /*
7473    * Go to the current end of chain
7474    */
7475   if (This->tailIndex == BLOCK_END_OF_CHAIN)
7476   {
7477     currentBlock = blockIndex;
7478 
7479     while (blockIndex != BLOCK_END_OF_CHAIN)
7480     {
7481       This->numBlocks++;
7482       currentBlock = blockIndex;
7483 
7484       if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7485 						&blockIndex)))
7486 	return FALSE;
7487     }
7488 
7489     This->tailIndex = currentBlock;
7490   }
7491 
7492   currentBlock = This->tailIndex;
7493   oldNumBlocks = This->numBlocks;
7494 
7495   /*
7496    * Add new blocks to the chain
7497    */
7498   if (oldNumBlocks < newNumBlocks)
7499   {
7500     while (oldNumBlocks < newNumBlocks)
7501     {
7502       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7503 
7504       StorageImpl_SetNextBlockInChain(
7505 	This->parentStorage,
7506 	currentBlock,
7507 	blockIndex);
7508 
7509       StorageImpl_SetNextBlockInChain(
7510         This->parentStorage,
7511 	blockIndex,
7512 	BLOCK_END_OF_CHAIN);
7513 
7514       currentBlock = blockIndex;
7515       oldNumBlocks++;
7516     }
7517 
7518     This->tailIndex = blockIndex;
7519     This->numBlocks = newNumBlocks;
7520   }
7521 
7522   if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7523     return FALSE;
7524 
7525   return TRUE;
7526 }
7527 
7528 
7529 /******************************************************************************
7530  *      BlockChainStream_GetSize
7531  *
7532  * Returns the size of this chain.
7533  * Will return the block count if this chain doesn't have a directory entry.
7534  */
BlockChainStream_GetSize(BlockChainStream * This)7535 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7536 {
7537   DirEntry chainEntry;
7538 
7539   if(This->headOfStreamPlaceHolder == NULL)
7540   {
7541     /*
7542      * This chain has a directory entry so use the size value from there.
7543      */
7544     StorageImpl_ReadDirEntry(
7545       This->parentStorage,
7546       This->ownerDirEntry,
7547       &chainEntry);
7548 
7549     return chainEntry.size;
7550   }
7551   else
7552   {
7553     /*
7554      * this chain is a chain that does not have a directory entry, figure out the
7555      * size by making the product number of used blocks times the
7556      * size of them
7557      */
7558     ULARGE_INTEGER result;
7559     result.QuadPart =
7560       (ULONGLONG)BlockChainStream_GetCount(This) *
7561       This->parentStorage->bigBlockSize;
7562 
7563     return result;
7564   }
7565 }
7566 
7567 /******************************************************************************
7568  *      BlockChainStream_SetSize
7569  *
7570  * Sets the size of this stream. The big block depot will be updated.
7571  * The file will grow if we grow the chain.
7572  *
7573  * TODO: Free the actual blocks in the file when we shrink the chain.
7574  *       Currently, the blocks are still in the file. So the file size
7575  *       doesn't shrink even if we shrink streams.
7576  */
BlockChainStream_SetSize(BlockChainStream * This,ULARGE_INTEGER newSize)7577 BOOL BlockChainStream_SetSize(
7578   BlockChainStream* This,
7579   ULARGE_INTEGER    newSize)
7580 {
7581   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7582 
7583   if (newSize.QuadPart == size.QuadPart)
7584     return TRUE;
7585 
7586   if (newSize.QuadPart < size.QuadPart)
7587   {
7588     BlockChainStream_Shrink(This, newSize);
7589   }
7590   else
7591   {
7592     BlockChainStream_Enlarge(This, newSize);
7593   }
7594 
7595   return TRUE;
7596 }
7597 
7598 /******************************************************************************
7599  *      BlockChainStream_ReadAt
7600  *
7601  * Reads a specified number of bytes from this chain at the specified offset.
7602  * bytesRead may be NULL.
7603  * Failure will be returned if the specified number of bytes has not been read.
7604  */
BlockChainStream_ReadAt(BlockChainStream * This,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)7605 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7606   ULARGE_INTEGER offset,
7607   ULONG          size,
7608   void*          buffer,
7609   ULONG*         bytesRead)
7610 {
7611   ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7612   ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
7613   ULONG bytesToReadInBuffer;
7614   ULONG blockIndex;
7615   BYTE* bufferWalker;
7616   ULARGE_INTEGER stream_size;
7617   HRESULT hr;
7618   BlockChainBlock *cachedBlock;
7619 
7620   TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7621 
7622   /*
7623    * Find the first block in the stream that contains part of the buffer.
7624    */
7625   blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7626 
7627   *bytesRead   = 0;
7628 
7629   stream_size = BlockChainStream_GetSize(This);
7630   if (stream_size.QuadPart > offset.QuadPart)
7631     size = min(stream_size.QuadPart - offset.QuadPart, size);
7632   else
7633     return S_OK;
7634 
7635   /*
7636    * Start reading the buffer.
7637    */
7638   bufferWalker = buffer;
7639 
7640   while (size > 0)
7641   {
7642     ULARGE_INTEGER ulOffset;
7643     DWORD bytesReadAt;
7644 
7645     /*
7646      * Calculate how many bytes we can copy from this big block.
7647      */
7648     bytesToReadInBuffer =
7649       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7650 
7651     hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7652 
7653     if (FAILED(hr))
7654       return hr;
7655 
7656     if (!cachedBlock)
7657     {
7658       /* Not in cache, and we're going to read past the end of the block. */
7659       ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7660                                offsetInBlock;
7661 
7662       StorageImpl_ReadAt(This->parentStorage,
7663            ulOffset,
7664            bufferWalker,
7665            bytesToReadInBuffer,
7666            &bytesReadAt);
7667     }
7668     else
7669     {
7670       if (!cachedBlock->read)
7671       {
7672         ULONG read;
7673         if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7674           return STG_E_READFAULT;
7675 
7676         cachedBlock->read = TRUE;
7677       }
7678 
7679       memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7680       bytesReadAt = bytesToReadInBuffer;
7681     }
7682 
7683     blockNoInSequence++;
7684     bufferWalker += bytesReadAt;
7685     size         -= bytesReadAt;
7686     *bytesRead   += bytesReadAt;
7687     offsetInBlock = 0;  /* There is no offset on the next block */
7688 
7689     if (bytesToReadInBuffer != bytesReadAt)
7690         break;
7691   }
7692 
7693   return S_OK;
7694 }
7695 
7696 /******************************************************************************
7697  *      BlockChainStream_WriteAt
7698  *
7699  * Writes the specified number of bytes to this chain at the specified offset.
7700  * Will fail if not all specified number of bytes have been written.
7701  */
BlockChainStream_WriteAt(BlockChainStream * This,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)7702 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7703   ULARGE_INTEGER    offset,
7704   ULONG             size,
7705   const void*       buffer,
7706   ULONG*            bytesWritten)
7707 {
7708   ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7709   ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
7710   ULONG bytesToWrite;
7711   ULONG blockIndex;
7712   const BYTE* bufferWalker;
7713   HRESULT hr;
7714   BlockChainBlock *cachedBlock;
7715 
7716   *bytesWritten   = 0;
7717   bufferWalker = buffer;
7718 
7719   while (size > 0)
7720   {
7721     ULARGE_INTEGER ulOffset;
7722     DWORD bytesWrittenAt;
7723 
7724     /*
7725      * Calculate how many bytes we can copy to this big block.
7726      */
7727     bytesToWrite =
7728       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7729 
7730     hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7731 
7732     /* BlockChainStream_SetSize should have already been called to ensure we have
7733      * enough blocks in the chain to write into */
7734     if (FAILED(hr))
7735     {
7736       ERR("not enough blocks in chain to write data\n");
7737       return hr;
7738     }
7739 
7740     if (!cachedBlock)
7741     {
7742       /* Not in cache, and we're going to write past the end of the block. */
7743       ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7744                                offsetInBlock;
7745 
7746       StorageImpl_WriteAt(This->parentStorage,
7747            ulOffset,
7748            bufferWalker,
7749            bytesToWrite,
7750            &bytesWrittenAt);
7751     }
7752     else
7753     {
7754       if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7755       {
7756         ULONG read;
7757         if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7758           return STG_E_READFAULT;
7759       }
7760 
7761       memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7762       bytesWrittenAt = bytesToWrite;
7763       cachedBlock->read = TRUE;
7764       cachedBlock->dirty = TRUE;
7765     }
7766 
7767     blockNoInSequence++;
7768     bufferWalker  += bytesWrittenAt;
7769     size          -= bytesWrittenAt;
7770     *bytesWritten += bytesWrittenAt;
7771     offsetInBlock  = 0;      /* There is no offset on the next block */
7772 
7773     if (bytesWrittenAt != bytesToWrite)
7774       break;
7775   }
7776 
7777   return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7778 }
7779 
7780 
7781 /************************************************************************
7782  * SmallBlockChainStream implementation
7783  ***********************************************************************/
7784 
SmallBlockChainStream_Construct(StorageImpl * parentStorage,ULONG * headOfStreamPlaceHolder,DirRef dirEntry)7785 SmallBlockChainStream* SmallBlockChainStream_Construct(
7786   StorageImpl* parentStorage,
7787   ULONG*         headOfStreamPlaceHolder,
7788   DirRef         dirEntry)
7789 {
7790   SmallBlockChainStream* newStream;
7791 
7792   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7793 
7794   newStream->parentStorage      = parentStorage;
7795   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7796   newStream->ownerDirEntry      = dirEntry;
7797 
7798   return newStream;
7799 }
7800 
SmallBlockChainStream_Destroy(SmallBlockChainStream * This)7801 void SmallBlockChainStream_Destroy(
7802   SmallBlockChainStream* This)
7803 {
7804   HeapFree(GetProcessHeap(), 0, This);
7805 }
7806 
7807 /******************************************************************************
7808  *      SmallBlockChainStream_GetHeadOfChain
7809  *
7810  * Returns the head of this chain of small blocks.
7811  */
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream * This)7812 static ULONG SmallBlockChainStream_GetHeadOfChain(
7813   SmallBlockChainStream* This)
7814 {
7815   DirEntry  chainEntry;
7816   HRESULT   hr;
7817 
7818   if (This->headOfStreamPlaceHolder != NULL)
7819     return *(This->headOfStreamPlaceHolder);
7820 
7821   if (This->ownerDirEntry)
7822   {
7823     hr = StorageImpl_ReadDirEntry(
7824                       This->parentStorage,
7825                       This->ownerDirEntry,
7826                       &chainEntry);
7827 
7828     if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7829       return chainEntry.startingBlock;
7830   }
7831 
7832   return BLOCK_END_OF_CHAIN;
7833 }
7834 
7835 /******************************************************************************
7836  *      SmallBlockChainStream_GetNextBlockInChain
7837  *
7838  * Returns the index of the next small block in this chain.
7839  *
7840  * Return Values:
7841  *    - BLOCK_END_OF_CHAIN: end of this chain
7842  *    - BLOCK_UNUSED: small block 'blockIndex' is free
7843  */
SmallBlockChainStream_GetNextBlockInChain(SmallBlockChainStream * This,ULONG blockIndex,ULONG * nextBlockInChain)7844 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7845   SmallBlockChainStream* This,
7846   ULONG                  blockIndex,
7847   ULONG*                 nextBlockInChain)
7848 {
7849   ULARGE_INTEGER offsetOfBlockInDepot;
7850   DWORD  buffer;
7851   ULONG  bytesRead;
7852   HRESULT res;
7853 
7854   *nextBlockInChain = BLOCK_END_OF_CHAIN;
7855 
7856   offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
7857 
7858   /*
7859    * Read those bytes in the buffer from the small block file.
7860    */
7861   res = BlockChainStream_ReadAt(
7862               This->parentStorage->smallBlockDepotChain,
7863               offsetOfBlockInDepot,
7864               sizeof(DWORD),
7865               &buffer,
7866               &bytesRead);
7867 
7868   if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7869     res = STG_E_READFAULT;
7870 
7871   if (SUCCEEDED(res))
7872   {
7873     StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7874     return S_OK;
7875   }
7876 
7877   return res;
7878 }
7879 
7880 /******************************************************************************
7881  *       SmallBlockChainStream_SetNextBlockInChain
7882  *
7883  * Writes the index of the next block of the specified block in the small
7884  * block depot.
7885  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7886  * To flag a block as free use BLOCK_UNUSED as nextBlock.
7887  */
SmallBlockChainStream_SetNextBlockInChain(SmallBlockChainStream * This,ULONG blockIndex,ULONG nextBlock)7888 static void SmallBlockChainStream_SetNextBlockInChain(
7889   SmallBlockChainStream* This,
7890   ULONG                  blockIndex,
7891   ULONG                  nextBlock)
7892 {
7893   ULARGE_INTEGER offsetOfBlockInDepot;
7894   DWORD  buffer;
7895   ULONG  bytesWritten;
7896 
7897   offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
7898 
7899   StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7900 
7901   /*
7902    * Read those bytes in the buffer from the small block file.
7903    */
7904   BlockChainStream_WriteAt(
7905     This->parentStorage->smallBlockDepotChain,
7906     offsetOfBlockInDepot,
7907     sizeof(DWORD),
7908     &buffer,
7909     &bytesWritten);
7910 }
7911 
7912 /******************************************************************************
7913  *      SmallBlockChainStream_FreeBlock
7914  *
7915  * Flag small block 'blockIndex' as free in the small block depot.
7916  */
SmallBlockChainStream_FreeBlock(SmallBlockChainStream * This,ULONG blockIndex)7917 static void SmallBlockChainStream_FreeBlock(
7918   SmallBlockChainStream* This,
7919   ULONG                  blockIndex)
7920 {
7921   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7922 }
7923 
7924 /******************************************************************************
7925  *      SmallBlockChainStream_GetNextFreeBlock
7926  *
7927  * Returns the index of a free small block. The small block depot will be
7928  * enlarged if necessary. The small block chain will also be enlarged if
7929  * necessary.
7930  */
SmallBlockChainStream_GetNextFreeBlock(SmallBlockChainStream * This)7931 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7932   SmallBlockChainStream* This)
7933 {
7934   ULARGE_INTEGER offsetOfBlockInDepot;
7935   DWORD buffer;
7936   ULONG bytesRead;
7937   ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7938   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7939   HRESULT res = S_OK;
7940   ULONG smallBlocksPerBigBlock;
7941   DirEntry rootEntry;
7942   ULONG blocksRequired;
7943   ULARGE_INTEGER old_size, size_required;
7944 
7945   offsetOfBlockInDepot.u.HighPart = 0;
7946 
7947   /*
7948    * Scan the small block depot for a free block
7949    */
7950   while (nextBlockIndex != BLOCK_UNUSED)
7951   {
7952     offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7953 
7954     res = BlockChainStream_ReadAt(
7955                 This->parentStorage->smallBlockDepotChain,
7956                 offsetOfBlockInDepot,
7957                 sizeof(DWORD),
7958                 &buffer,
7959                 &bytesRead);
7960 
7961     /*
7962      * If we run out of space for the small block depot, enlarge it
7963      */
7964     if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7965     {
7966       StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7967 
7968       if (nextBlockIndex != BLOCK_UNUSED)
7969         blockIndex++;
7970     }
7971     else
7972     {
7973       ULONG count =
7974         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7975 
7976       BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7977       ULARGE_INTEGER newSize, offset;
7978       ULONG bytesWritten;
7979 
7980       newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7981       BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7982 
7983       /*
7984        * Initialize all the small blocks to free
7985        */
7986       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7987       offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7988       BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7989         offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7990 
7991       StorageImpl_SaveFileHeader(This->parentStorage);
7992     }
7993   }
7994 
7995   This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7996 
7997   smallBlocksPerBigBlock =
7998     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7999 
8000   /*
8001    * Verify if we have to allocate big blocks to contain small blocks
8002    */
8003   blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
8004 
8005   size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
8006 
8007   old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
8008 
8009   if (size_required.QuadPart > old_size.QuadPart)
8010   {
8011     BlockChainStream_SetSize(
8012       This->parentStorage->smallBlockRootChain,
8013       size_required);
8014 
8015     StorageImpl_ReadDirEntry(
8016       This->parentStorage,
8017       This->parentStorage->base.storageDirEntry,
8018       &rootEntry);
8019 
8020     rootEntry.size = size_required;
8021 
8022     StorageImpl_WriteDirEntry(
8023       This->parentStorage,
8024       This->parentStorage->base.storageDirEntry,
8025       &rootEntry);
8026   }
8027 
8028   return blockIndex;
8029 }
8030 
8031 /******************************************************************************
8032  *      SmallBlockChainStream_ReadAt
8033  *
8034  * Reads a specified number of bytes from this chain at the specified offset.
8035  * bytesRead may be NULL.
8036  * Failure will be returned if the specified number of bytes has not been read.
8037  */
SmallBlockChainStream_ReadAt(SmallBlockChainStream * This,ULARGE_INTEGER offset,ULONG size,void * buffer,ULONG * bytesRead)8038 HRESULT SmallBlockChainStream_ReadAt(
8039   SmallBlockChainStream* This,
8040   ULARGE_INTEGER         offset,
8041   ULONG                  size,
8042   void*                  buffer,
8043   ULONG*                 bytesRead)
8044 {
8045   HRESULT rc = S_OK;
8046   ULARGE_INTEGER offsetInBigBlockFile;
8047   ULONG blockNoInSequence =
8048     offset.u.LowPart / This->parentStorage->smallBlockSize;
8049 
8050   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8051   ULONG bytesToReadInBuffer;
8052   ULONG blockIndex;
8053   ULONG bytesReadFromBigBlockFile;
8054   BYTE* bufferWalker;
8055   ULARGE_INTEGER stream_size;
8056 
8057   /*
8058    * This should never happen on a small block file.
8059    */
8060   assert(offset.u.HighPart==0);
8061 
8062   *bytesRead   = 0;
8063 
8064   stream_size = SmallBlockChainStream_GetSize(This);
8065   if (stream_size.QuadPart > offset.QuadPart)
8066     size = min(stream_size.QuadPart - offset.QuadPart, size);
8067   else
8068     return S_OK;
8069 
8070   /*
8071    * Find the first block in the stream that contains part of the buffer.
8072    */
8073   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8074 
8075   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
8076   {
8077     rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8078     if(FAILED(rc))
8079       return rc;
8080     blockNoInSequence--;
8081   }
8082 
8083   /*
8084    * Start reading the buffer.
8085    */
8086   bufferWalker = buffer;
8087 
8088   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8089   {
8090     /*
8091      * Calculate how many bytes we can copy from this small block.
8092      */
8093     bytesToReadInBuffer =
8094       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8095 
8096     /*
8097      * Calculate the offset of the small block in the small block file.
8098      */
8099     offsetInBigBlockFile.QuadPart   =
8100       (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8101 
8102     offsetInBigBlockFile.QuadPart  += offsetInBlock;
8103 
8104     /*
8105      * Read those bytes in the buffer from the small block file.
8106      * The small block has already been identified so it shouldn't fail
8107      * unless the file is corrupt.
8108      */
8109     rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8110       offsetInBigBlockFile,
8111       bytesToReadInBuffer,
8112       bufferWalker,
8113       &bytesReadFromBigBlockFile);
8114 
8115     if (FAILED(rc))
8116       return rc;
8117 
8118     if (!bytesReadFromBigBlockFile)
8119       return STG_E_DOCFILECORRUPT;
8120 
8121     /*
8122      * Step to the next big block.
8123      */
8124     rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8125     if(FAILED(rc))
8126       return STG_E_DOCFILECORRUPT;
8127 
8128     bufferWalker += bytesReadFromBigBlockFile;
8129     size         -= bytesReadFromBigBlockFile;
8130     *bytesRead   += bytesReadFromBigBlockFile;
8131     offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8132   }
8133 
8134   return S_OK;
8135 }
8136 
8137 /******************************************************************************
8138  *       SmallBlockChainStream_WriteAt
8139  *
8140  * Writes the specified number of bytes to this chain at the specified offset.
8141  * Will fail if not all specified number of bytes have been written.
8142  */
SmallBlockChainStream_WriteAt(SmallBlockChainStream * This,ULARGE_INTEGER offset,ULONG size,const void * buffer,ULONG * bytesWritten)8143 HRESULT SmallBlockChainStream_WriteAt(
8144   SmallBlockChainStream* This,
8145   ULARGE_INTEGER offset,
8146   ULONG          size,
8147   const void*    buffer,
8148   ULONG*         bytesWritten)
8149 {
8150   ULARGE_INTEGER offsetInBigBlockFile;
8151   ULONG blockNoInSequence =
8152     offset.u.LowPart / This->parentStorage->smallBlockSize;
8153 
8154   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8155   ULONG bytesToWriteInBuffer;
8156   ULONG blockIndex;
8157   ULONG bytesWrittenToBigBlockFile;
8158   const BYTE* bufferWalker;
8159   HRESULT res;
8160 
8161   /*
8162    * This should never happen on a small block file.
8163    */
8164   assert(offset.u.HighPart==0);
8165 
8166   /*
8167    * Find the first block in the stream that contains part of the buffer.
8168    */
8169   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8170 
8171   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
8172   {
8173     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8174       return STG_E_DOCFILECORRUPT;
8175     blockNoInSequence--;
8176   }
8177 
8178   /*
8179    * Start writing the buffer.
8180    */
8181   *bytesWritten   = 0;
8182   bufferWalker = buffer;
8183   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
8184   {
8185     /*
8186      * Calculate how many bytes we can copy to this small block.
8187      */
8188     bytesToWriteInBuffer =
8189       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8190 
8191     /*
8192      * Calculate the offset of the small block in the small block file.
8193      */
8194     offsetInBigBlockFile.QuadPart   =
8195       (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8196 
8197     offsetInBigBlockFile.QuadPart  += offsetInBlock;
8198 
8199     /*
8200      * Write those bytes in the buffer to the small block file.
8201      */
8202     res = BlockChainStream_WriteAt(
8203       This->parentStorage->smallBlockRootChain,
8204       offsetInBigBlockFile,
8205       bytesToWriteInBuffer,
8206       bufferWalker,
8207       &bytesWrittenToBigBlockFile);
8208     if (FAILED(res))
8209       return res;
8210 
8211     /*
8212      * Step to the next big block.
8213      */
8214     res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
8215     if (FAILED(res))
8216       return res;
8217     bufferWalker  += bytesWrittenToBigBlockFile;
8218     size          -= bytesWrittenToBigBlockFile;
8219     *bytesWritten += bytesWrittenToBigBlockFile;
8220     offsetInBlock  = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8221   }
8222 
8223   return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8224 }
8225 
8226 /******************************************************************************
8227  *       SmallBlockChainStream_Shrink
8228  *
8229  * Shrinks this chain in the small block depot.
8230  */
SmallBlockChainStream_Shrink(SmallBlockChainStream * This,ULARGE_INTEGER newSize)8231 static BOOL SmallBlockChainStream_Shrink(
8232   SmallBlockChainStream* This,
8233   ULARGE_INTEGER newSize)
8234 {
8235   ULONG blockIndex, extraBlock;
8236   ULONG numBlocks;
8237   ULONG count = 0;
8238 
8239   numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8240 
8241   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8242     numBlocks++;
8243 
8244   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8245 
8246   /*
8247    * Go to the new end of chain
8248    */
8249   while (count < numBlocks)
8250   {
8251     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8252 							&blockIndex)))
8253       return FALSE;
8254     count++;
8255   }
8256 
8257   /*
8258    * If the count is 0, we have a special case, the head of the chain was
8259    * just freed.
8260    */
8261   if (count == 0)
8262   {
8263     DirEntry chainEntry;
8264 
8265     StorageImpl_ReadDirEntry(This->parentStorage,
8266 			     This->ownerDirEntry,
8267 			     &chainEntry);
8268 
8269     chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8270 
8271     StorageImpl_WriteDirEntry(This->parentStorage,
8272 			      This->ownerDirEntry,
8273 			      &chainEntry);
8274 
8275     /*
8276      * We start freeing the chain at the head block.
8277      */
8278     extraBlock = blockIndex;
8279   }
8280   else
8281   {
8282     /* Get the next block before marking the new end */
8283     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
8284 							&extraBlock)))
8285       return FALSE;
8286 
8287     /* Mark the new end of chain */
8288     SmallBlockChainStream_SetNextBlockInChain(
8289       This,
8290       blockIndex,
8291       BLOCK_END_OF_CHAIN);
8292   }
8293 
8294   /*
8295    * Mark the extra blocks as free
8296    */
8297   while (extraBlock != BLOCK_END_OF_CHAIN)
8298   {
8299     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8300 							&blockIndex)))
8301       return FALSE;
8302     SmallBlockChainStream_FreeBlock(This, extraBlock);
8303     This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8304     extraBlock = blockIndex;
8305   }
8306 
8307   return TRUE;
8308 }
8309 
8310 /******************************************************************************
8311  *      SmallBlockChainStream_Enlarge
8312  *
8313  * Grows this chain in the small block depot.
8314  */
SmallBlockChainStream_Enlarge(SmallBlockChainStream * This,ULARGE_INTEGER newSize)8315 static BOOL SmallBlockChainStream_Enlarge(
8316   SmallBlockChainStream* This,
8317   ULARGE_INTEGER newSize)
8318 {
8319   ULONG blockIndex, currentBlock;
8320   ULONG newNumBlocks;
8321   ULONG oldNumBlocks = 0;
8322 
8323   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8324 
8325   /*
8326    * Empty chain. Create the head.
8327    */
8328   if (blockIndex == BLOCK_END_OF_CHAIN)
8329   {
8330     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8331     SmallBlockChainStream_SetNextBlockInChain(
8332         This,
8333         blockIndex,
8334         BLOCK_END_OF_CHAIN);
8335 
8336     if (This->headOfStreamPlaceHolder != NULL)
8337     {
8338       *(This->headOfStreamPlaceHolder) = blockIndex;
8339     }
8340     else
8341     {
8342       DirEntry chainEntry;
8343 
8344       StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8345                                    &chainEntry);
8346 
8347       chainEntry.startingBlock = blockIndex;
8348 
8349       StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8350                                   &chainEntry);
8351     }
8352   }
8353 
8354   currentBlock = blockIndex;
8355 
8356   /*
8357    * Figure out how many blocks are needed to contain this stream
8358    */
8359   newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8360 
8361   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8362     newNumBlocks++;
8363 
8364   /*
8365    * Go to the current end of chain
8366    */
8367   while (blockIndex != BLOCK_END_OF_CHAIN)
8368   {
8369     oldNumBlocks++;
8370     currentBlock = blockIndex;
8371     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8372       return FALSE;
8373   }
8374 
8375   /*
8376    * Add new blocks to the chain
8377    */
8378   while (oldNumBlocks < newNumBlocks)
8379   {
8380     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8381     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8382 
8383     SmallBlockChainStream_SetNextBlockInChain(
8384       This,
8385       blockIndex,
8386       BLOCK_END_OF_CHAIN);
8387 
8388     currentBlock = blockIndex;
8389     oldNumBlocks++;
8390   }
8391 
8392   return TRUE;
8393 }
8394 
8395 /******************************************************************************
8396  *      SmallBlockChainStream_SetSize
8397  *
8398  * Sets the size of this stream.
8399  * The file will grow if we grow the chain.
8400  *
8401  * TODO: Free the actual blocks in the file when we shrink the chain.
8402  *       Currently, the blocks are still in the file. So the file size
8403  *       doesn't shrink even if we shrink streams.
8404  */
SmallBlockChainStream_SetSize(SmallBlockChainStream * This,ULARGE_INTEGER newSize)8405 BOOL SmallBlockChainStream_SetSize(
8406                 SmallBlockChainStream* This,
8407                 ULARGE_INTEGER    newSize)
8408 {
8409   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8410 
8411   if (newSize.u.LowPart == size.u.LowPart)
8412     return TRUE;
8413 
8414   if (newSize.u.LowPart < size.u.LowPart)
8415   {
8416     SmallBlockChainStream_Shrink(This, newSize);
8417   }
8418   else
8419   {
8420     SmallBlockChainStream_Enlarge(This, newSize);
8421   }
8422 
8423   return TRUE;
8424 }
8425 
8426 /******************************************************************************
8427  *       SmallBlockChainStream_GetCount
8428  *
8429  * Returns the number of small blocks that comprises this chain.
8430  * This is not the size of the stream as the last block may not be full!
8431  *
8432  */
SmallBlockChainStream_GetCount(SmallBlockChainStream * This)8433 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8434 {
8435     ULONG blockIndex;
8436     ULONG count = 0;
8437 
8438     blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8439 
8440     while(blockIndex != BLOCK_END_OF_CHAIN)
8441     {
8442         count++;
8443 
8444         if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8445                         blockIndex, &blockIndex)))
8446             return 0;
8447     }
8448 
8449     return count;
8450 }
8451 
8452 /******************************************************************************
8453  *      SmallBlockChainStream_GetSize
8454  *
8455  * Returns the size of this chain.
8456  */
SmallBlockChainStream_GetSize(SmallBlockChainStream * This)8457 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8458 {
8459   DirEntry chainEntry;
8460 
8461   if(This->headOfStreamPlaceHolder != NULL)
8462   {
8463     ULARGE_INTEGER result;
8464     result.u.HighPart = 0;
8465 
8466     result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8467         This->parentStorage->smallBlockSize;
8468 
8469     return result;
8470   }
8471 
8472   StorageImpl_ReadDirEntry(
8473     This->parentStorage,
8474     This->ownerDirEntry,
8475     &chainEntry);
8476 
8477   return chainEntry.size;
8478 }
8479 
8480 
8481 /************************************************************************
8482  * Miscellaneous storage functions
8483  ***********************************************************************/
8484 
create_storagefile(LPCOLESTR pwcsName,DWORD grfMode,DWORD grfAttrs,STGOPTIONS * pStgOptions,REFIID riid,void ** ppstgOpen)8485 static HRESULT create_storagefile(
8486   LPCOLESTR pwcsName,
8487   DWORD       grfMode,
8488   DWORD       grfAttrs,
8489   STGOPTIONS* pStgOptions,
8490   REFIID      riid,
8491   void**      ppstgOpen)
8492 {
8493   StorageBaseImpl* newStorage = 0;
8494   HANDLE       hFile      = INVALID_HANDLE_VALUE;
8495   HRESULT        hr         = STG_E_INVALIDFLAG;
8496   DWORD          shareMode;
8497   DWORD          accessMode;
8498   DWORD          creationMode;
8499   DWORD          fileAttributes;
8500   WCHAR          tempFileName[MAX_PATH];
8501 
8502   if (ppstgOpen == 0)
8503     return STG_E_INVALIDPOINTER;
8504 
8505   if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8506     return STG_E_INVALIDPARAMETER;
8507 
8508   /* if no share mode given then DENY_NONE is the default */
8509   if (STGM_SHARE_MODE(grfMode) == 0)
8510       grfMode |= STGM_SHARE_DENY_NONE;
8511 
8512   if ( FAILED( validateSTGM(grfMode) ))
8513     goto end;
8514 
8515   /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8516   switch(STGM_ACCESS_MODE(grfMode))
8517   {
8518   case STGM_WRITE:
8519   case STGM_READWRITE:
8520     break;
8521   default:
8522     goto end;
8523   }
8524 
8525   /* in direct mode, can only use SHARE_EXCLUSIVE */
8526   if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8527     goto end;
8528 
8529   /* but in transacted mode, any share mode is valid */
8530 
8531   /*
8532    * Generate a unique name.
8533    */
8534   if (pwcsName == 0)
8535   {
8536     WCHAR tempPath[MAX_PATH];
8537     static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8538 
8539     memset(tempPath, 0, sizeof(tempPath));
8540     memset(tempFileName, 0, sizeof(tempFileName));
8541 
8542     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8543       tempPath[0] = '.';
8544 
8545     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8546       pwcsName = tempFileName;
8547     else
8548     {
8549       hr = STG_E_INSUFFICIENTMEMORY;
8550       goto end;
8551     }
8552 
8553     creationMode = TRUNCATE_EXISTING;
8554   }
8555   else
8556   {
8557     creationMode = GetCreationModeFromSTGM(grfMode);
8558   }
8559 
8560   /*
8561    * Interpret the STGM value grfMode
8562    */
8563   shareMode    = GetShareModeFromSTGM(grfMode);
8564   accessMode   = GetAccessModeFromSTGM(grfMode);
8565 
8566   if (grfMode & STGM_DELETEONRELEASE)
8567     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8568   else
8569     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8570 
8571   *ppstgOpen = 0;
8572 
8573   hFile = CreateFileW(pwcsName,
8574                         accessMode,
8575                         shareMode,
8576                         NULL,
8577                         creationMode,
8578                         fileAttributes,
8579                         0);
8580 
8581   if (hFile == INVALID_HANDLE_VALUE)
8582   {
8583     if(GetLastError() == ERROR_FILE_EXISTS)
8584       hr = STG_E_FILEALREADYEXISTS;
8585     else
8586       hr = E_FAIL;
8587     goto end;
8588   }
8589 
8590   /*
8591    * Allocate and initialize the new IStorage object.
8592    */
8593   hr = Storage_Construct(
8594          hFile,
8595         pwcsName,
8596          NULL,
8597          grfMode,
8598          TRUE,
8599          TRUE,
8600          pStgOptions->ulSectorSize,
8601          &newStorage);
8602 
8603   if (FAILED(hr))
8604   {
8605     goto end;
8606   }
8607 
8608   hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8609   IStorage_Release(&newStorage->IStorage_iface);
8610 
8611 end:
8612   TRACE("<-- %p  r = %08x\n", *ppstgOpen, hr);
8613 
8614   return hr;
8615 }
8616 
8617 /******************************************************************************
8618  *    StgCreateDocfile  [OLE32.@]
8619  * Creates a new compound file storage object
8620  *
8621  * PARAMS
8622  *  pwcsName  [ I] Unicode string with filename (can be relative or NULL)
8623  *  grfMode   [ I] Access mode for opening the new storage object (see STGM_ constants)
8624  *  reserved  [ ?] unused?, usually 0
8625  *  ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8626  *
8627  * RETURNS
8628  *  S_OK if the file was successfully created
8629  *  some STG_E_ value if error
8630  * NOTES
8631  *  if pwcsName is NULL, create file with new unique name
8632  *  the function can returns
8633  *  STG_S_CONVERTED if the specified file was successfully converted to storage format
8634  *  (unrealized now)
8635  */
StgCreateDocfile(LPCOLESTR pwcsName,DWORD grfMode,DWORD reserved,IStorage ** ppstgOpen)8636 HRESULT WINAPI StgCreateDocfile(
8637   LPCOLESTR pwcsName,
8638   DWORD       grfMode,
8639   DWORD       reserved,
8640   IStorage  **ppstgOpen)
8641 {
8642   STGOPTIONS stgoptions = {1, 0, 512};
8643 
8644   TRACE("(%s, %x, %d, %p)\n",
8645 	debugstr_w(pwcsName), grfMode,
8646 	reserved, ppstgOpen);
8647 
8648   if (ppstgOpen == 0)
8649     return STG_E_INVALIDPOINTER;
8650   if (reserved != 0)
8651     return STG_E_INVALIDPARAMETER;
8652 
8653   return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8654 }
8655 
8656 /******************************************************************************
8657  *              StgCreateStorageEx        [OLE32.@]
8658  */
StgCreateStorageEx(const WCHAR * pwcsName,DWORD grfMode,DWORD stgfmt,DWORD grfAttrs,STGOPTIONS * pStgOptions,void * reserved,REFIID riid,void ** ppObjectOpen)8659 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8660 {
8661     TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8662           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8663 
8664     if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8665     {
8666         ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8667         return STG_E_INVALIDPARAMETER;
8668     }
8669 
8670     if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8671     {
8672         ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8673         return STG_E_INVALIDPARAMETER;
8674     }
8675 
8676     if (stgfmt == STGFMT_FILE)
8677     {
8678         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8679         return STG_E_INVALIDPARAMETER;
8680     }
8681 
8682     if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8683     {
8684         STGOPTIONS defaultOptions = {1, 0, 512};
8685 
8686         if (!pStgOptions) pStgOptions = &defaultOptions;
8687         return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8688     }
8689 
8690 
8691     ERR("Invalid stgfmt argument\n");
8692     return STG_E_INVALIDPARAMETER;
8693 }
8694 
8695 /******************************************************************************
8696  *              StgCreatePropSetStg       [OLE32.@]
8697  */
StgCreatePropSetStg(IStorage * pstg,DWORD reserved,IPropertySetStorage ** propset)8698 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8699  IPropertySetStorage **propset)
8700 {
8701     TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8702     if (reserved)
8703         return STG_E_INVALIDPARAMETER;
8704 
8705     return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8706 }
8707 
8708 /******************************************************************************
8709  *              StgOpenStorageEx      [OLE32.@]
8710  */
StgOpenStorageEx(const WCHAR * pwcsName,DWORD grfMode,DWORD stgfmt,DWORD grfAttrs,STGOPTIONS * pStgOptions,void * reserved,REFIID riid,void ** ppObjectOpen)8711 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8712 {
8713     TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8714           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8715 
8716     if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8717     {
8718         ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8719         return STG_E_INVALIDPARAMETER;
8720     }
8721 
8722     switch (stgfmt)
8723     {
8724     case STGFMT_FILE:
8725         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8726         return STG_E_INVALIDPARAMETER;
8727 
8728     case STGFMT_STORAGE:
8729         break;
8730 
8731     case STGFMT_DOCFILE:
8732         if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8733         {
8734             ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8735             return STG_E_INVALIDPARAMETER;
8736         }
8737         FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8738         break;
8739 
8740     case STGFMT_ANY:
8741         WARN("STGFMT_ANY assuming storage\n");
8742         break;
8743 
8744     default:
8745         return STG_E_INVALIDPARAMETER;
8746     }
8747 
8748     return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8749 }
8750 
8751 
8752 /******************************************************************************
8753  *              StgOpenStorage        [OLE32.@]
8754  */
StgOpenStorage(const OLECHAR * pwcsName,IStorage * pstgPriority,DWORD grfMode,SNB snbExclude,DWORD reserved,IStorage ** ppstgOpen)8755 HRESULT WINAPI StgOpenStorage(
8756   const OLECHAR *pwcsName,
8757   IStorage      *pstgPriority,
8758   DWORD          grfMode,
8759   SNB            snbExclude,
8760   DWORD          reserved,
8761   IStorage     **ppstgOpen)
8762 {
8763   StorageBaseImpl* newStorage = 0;
8764   HRESULT        hr = S_OK;
8765   HANDLE         hFile = 0;
8766   DWORD          shareMode;
8767   DWORD          accessMode;
8768   LPWSTR         temp_name = NULL;
8769 
8770   TRACE("(%s, %p, %x, %p, %d, %p)\n",
8771 	debugstr_w(pwcsName), pstgPriority, grfMode,
8772 	snbExclude, reserved, ppstgOpen);
8773 
8774   if (pstgPriority)
8775   {
8776     /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8777     hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8778     if (FAILED(hr)) goto end;
8779     pwcsName = temp_name;
8780     TRACE("using filename %s\n", debugstr_w(temp_name));
8781   }
8782 
8783   if (pwcsName == 0)
8784   {
8785     hr = STG_E_INVALIDNAME;
8786     goto end;
8787   }
8788 
8789   if (ppstgOpen == 0)
8790   {
8791     hr = STG_E_INVALIDPOINTER;
8792     goto end;
8793   }
8794 
8795   if (reserved)
8796   {
8797     hr = STG_E_INVALIDPARAMETER;
8798     goto end;
8799   }
8800 
8801   if (grfMode & STGM_PRIORITY)
8802   {
8803     if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8804       return STG_E_INVALIDFLAG;
8805     if (grfMode & STGM_DELETEONRELEASE)
8806       return STG_E_INVALIDFUNCTION;
8807     if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8808       return STG_E_INVALIDFLAG;
8809     grfMode &= ~0xf0; /* remove the existing sharing mode */
8810     grfMode |= STGM_SHARE_DENY_NONE;
8811   }
8812 
8813   /*
8814    * Validate the sharing mode
8815    */
8816   if (grfMode & STGM_DIRECT_SWMR)
8817   {
8818     if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8819         (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8820     {
8821       hr = STG_E_INVALIDFLAG;
8822       goto end;
8823     }
8824   }
8825   else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8826     switch(STGM_SHARE_MODE(grfMode))
8827     {
8828       case STGM_SHARE_EXCLUSIVE:
8829       case STGM_SHARE_DENY_WRITE:
8830         break;
8831       default:
8832         hr = STG_E_INVALIDFLAG;
8833         goto end;
8834     }
8835 
8836   if ( FAILED( validateSTGM(grfMode) ) ||
8837        (grfMode&STGM_CREATE))
8838   {
8839     hr = STG_E_INVALIDFLAG;
8840     goto end;
8841   }
8842 
8843   /* shared reading requires transacted or single writer mode */
8844   if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8845       STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8846      !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8847   {
8848     hr = STG_E_INVALIDFLAG;
8849     goto end;
8850   }
8851 
8852   /*
8853    * Interpret the STGM value grfMode
8854    */
8855   shareMode    = GetShareModeFromSTGM(grfMode);
8856   accessMode   = GetAccessModeFromSTGM(grfMode);
8857 
8858   *ppstgOpen = 0;
8859 
8860   hFile = CreateFileW( pwcsName,
8861                        accessMode,
8862                        shareMode,
8863                        NULL,
8864                        OPEN_EXISTING,
8865                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8866                        0);
8867 
8868   if (hFile==INVALID_HANDLE_VALUE)
8869   {
8870     DWORD last_error = GetLastError();
8871 
8872     hr = E_FAIL;
8873 
8874     switch (last_error)
8875     {
8876       case ERROR_FILE_NOT_FOUND:
8877         hr = STG_E_FILENOTFOUND;
8878         break;
8879 
8880       case ERROR_PATH_NOT_FOUND:
8881         hr = STG_E_PATHNOTFOUND;
8882         break;
8883 
8884       case ERROR_ACCESS_DENIED:
8885       case ERROR_WRITE_PROTECT:
8886         hr = STG_E_ACCESSDENIED;
8887         break;
8888 
8889       case ERROR_SHARING_VIOLATION:
8890         hr = STG_E_SHAREVIOLATION;
8891         break;
8892 
8893       default:
8894         hr = E_FAIL;
8895     }
8896 
8897     goto end;
8898   }
8899 
8900   /*
8901    * Refuse to open the file if it's too small to be a structured storage file
8902    * FIXME: verify the file when reading instead of here
8903    */
8904   if (GetFileSize(hFile, NULL) < HEADER_SIZE)
8905   {
8906     CloseHandle(hFile);
8907     hr = STG_E_FILEALREADYEXISTS;
8908     goto end;
8909   }
8910 
8911   /*
8912    * Allocate and initialize the new IStorage object.
8913    */
8914   hr = Storage_Construct(
8915          hFile,
8916          pwcsName,
8917          NULL,
8918          grfMode,
8919          TRUE,
8920          FALSE,
8921          512,
8922          &newStorage);
8923 
8924   if (FAILED(hr))
8925   {
8926     /*
8927      * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8928      */
8929     if(hr == STG_E_INVALIDHEADER)
8930 	hr = STG_E_FILEALREADYEXISTS;
8931     goto end;
8932   }
8933 
8934   *ppstgOpen = &newStorage->IStorage_iface;
8935 
8936 end:
8937   CoTaskMemFree(temp_name);
8938   if (pstgPriority) IStorage_Release(pstgPriority);
8939   TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8940   return hr;
8941 }
8942 
8943 /******************************************************************************
8944  *    StgCreateDocfileOnILockBytes    [OLE32.@]
8945  */
StgCreateDocfileOnILockBytes(ILockBytes * plkbyt,DWORD grfMode,DWORD reserved,IStorage ** ppstgOpen)8946 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8947       ILockBytes *plkbyt,
8948       DWORD grfMode,
8949       DWORD reserved,
8950       IStorage** ppstgOpen)
8951 {
8952   StorageBaseImpl* newStorage = 0;
8953   HRESULT        hr         = S_OK;
8954 
8955   if ((ppstgOpen == 0) || (plkbyt == 0))
8956     return STG_E_INVALIDPOINTER;
8957 
8958   /*
8959    * Allocate and initialize the new IStorage object.
8960    */
8961   hr = Storage_Construct(
8962          0,
8963         0,
8964          plkbyt,
8965          grfMode,
8966          FALSE,
8967          TRUE,
8968          512,
8969          &newStorage);
8970 
8971   if (FAILED(hr))
8972   {
8973     return hr;
8974   }
8975 
8976   *ppstgOpen = &newStorage->IStorage_iface;
8977 
8978   return hr;
8979 }
8980 
8981 /******************************************************************************
8982  *    StgOpenStorageOnILockBytes    [OLE32.@]
8983  */
StgOpenStorageOnILockBytes(ILockBytes * plkbyt,IStorage * pstgPriority,DWORD grfMode,SNB snbExclude,DWORD reserved,IStorage ** ppstgOpen)8984 HRESULT WINAPI StgOpenStorageOnILockBytes(
8985       ILockBytes *plkbyt,
8986       IStorage *pstgPriority,
8987       DWORD grfMode,
8988       SNB snbExclude,
8989       DWORD reserved,
8990       IStorage **ppstgOpen)
8991 {
8992   StorageBaseImpl* newStorage = 0;
8993   HRESULT        hr = S_OK;
8994 
8995   if ((plkbyt == 0) || (ppstgOpen == 0))
8996     return STG_E_INVALIDPOINTER;
8997 
8998   if ( FAILED( validateSTGM(grfMode) ))
8999     return STG_E_INVALIDFLAG;
9000 
9001   *ppstgOpen = 0;
9002 
9003   /*
9004    * Allocate and initialize the new IStorage object.
9005    */
9006   hr = Storage_Construct(
9007          0,
9008          0,
9009          plkbyt,
9010          grfMode,
9011          FALSE,
9012          FALSE,
9013          512,
9014          &newStorage);
9015 
9016   if (FAILED(hr))
9017   {
9018     return hr;
9019   }
9020 
9021   *ppstgOpen = &newStorage->IStorage_iface;
9022 
9023   return hr;
9024 }
9025 
9026 /******************************************************************************
9027  *              StgSetTimes [ole32.@]
9028  *              StgSetTimes [OLE32.@]
9029  *
9030  *
9031  */
StgSetTimes(OLECHAR const * str,FILETIME const * pctime,FILETIME const * patime,FILETIME const * pmtime)9032 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
9033                            FILETIME const *patime, FILETIME const *pmtime)
9034 {
9035   IStorage *stg = NULL;
9036   HRESULT r;
9037 
9038   TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
9039 
9040   r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
9041                      0, 0, &stg);
9042   if( SUCCEEDED(r) )
9043   {
9044     r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
9045     IStorage_Release(stg);
9046   }
9047 
9048   return r;
9049 }
9050 
9051 /******************************************************************************
9052  *              StgIsStorageILockBytes        [OLE32.@]
9053  *
9054  * Determines if the ILockBytes contains a storage object.
9055  */
StgIsStorageILockBytes(ILockBytes * plkbyt)9056 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
9057 {
9058   BYTE sig[sizeof(STORAGE_magic)];
9059   ULARGE_INTEGER offset;
9060   ULONG read = 0;
9061 
9062   offset.u.HighPart = 0;
9063   offset.u.LowPart  = 0;
9064 
9065   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
9066 
9067   if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
9068     return S_OK;
9069 
9070   return S_FALSE;
9071 }
9072 
9073 /******************************************************************************
9074  *              WriteClassStg        [OLE32.@]
9075  *
9076  * This method will store the specified CLSID in the specified storage object
9077  */
WriteClassStg(IStorage * pStg,REFCLSID rclsid)9078 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
9079 {
9080   if(!pStg)
9081     return E_INVALIDARG;
9082 
9083   if(!rclsid)
9084     return STG_E_INVALIDPOINTER;
9085 
9086   return IStorage_SetClass(pStg, rclsid);
9087 }
9088 
9089 /***********************************************************************
9090  *    ReadClassStg (OLE32.@)
9091  *
9092  * This method reads the CLSID previously written to a storage object with
9093  * the WriteClassStg.
9094  *
9095  * PARAMS
9096  *  pstg    [I] IStorage pointer
9097  *  pclsid  [O] Pointer to where the CLSID is written
9098  *
9099  * RETURNS
9100  *  Success: S_OK.
9101  *  Failure: HRESULT code.
9102  */
ReadClassStg(IStorage * pstg,CLSID * pclsid)9103 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
9104 
9105     STATSTG pstatstg;
9106     HRESULT hRes;
9107 
9108     TRACE("(%p, %p)\n", pstg, pclsid);
9109 
9110     if(!pstg || !pclsid)
9111         return E_INVALIDARG;
9112 
9113    /*
9114     * read a STATSTG structure (contains the clsid) from the storage
9115     */
9116     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
9117 
9118     if(SUCCEEDED(hRes))
9119         *pclsid=pstatstg.clsid;
9120 
9121     return hRes;
9122 }
9123 
9124 /***********************************************************************
9125  *    OleLoadFromStream (OLE32.@)
9126  *
9127  * This function loads an object from stream
9128  */
OleLoadFromStream(IStream * pStm,REFIID iidInterface,void ** ppvObj)9129 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
9130 {
9131     CLSID	clsid;
9132     HRESULT	res;
9133     LPPERSISTSTREAM	xstm;
9134 
9135     TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
9136 
9137     res=ReadClassStm(pStm,&clsid);
9138     if (FAILED(res))
9139 	return res;
9140     res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
9141     if (FAILED(res))
9142 	return res;
9143     res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
9144     if (FAILED(res)) {
9145 	IUnknown_Release((IUnknown*)*ppvObj);
9146 	return res;
9147     }
9148     res=IPersistStream_Load(xstm,pStm);
9149     IPersistStream_Release(xstm);
9150     /* FIXME: all refcounts ok at this point? I think they should be:
9151      * 		pStm	: unchanged
9152      *		ppvObj	: 1
9153      *		xstm	: 0 (released)
9154      */
9155     return res;
9156 }
9157 
9158 /***********************************************************************
9159  *    OleSaveToStream (OLE32.@)
9160  *
9161  * This function saves an object with the IPersistStream interface on it
9162  * to the specified stream.
9163  */
OleSaveToStream(IPersistStream * pPStm,IStream * pStm)9164 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
9165 {
9166 
9167     CLSID clsid;
9168     HRESULT res;
9169 
9170     TRACE("(%p,%p)\n",pPStm,pStm);
9171 
9172     res=IPersistStream_GetClassID(pPStm,&clsid);
9173 
9174     if (SUCCEEDED(res)){
9175 
9176         res=WriteClassStm(pStm,&clsid);
9177 
9178         if (SUCCEEDED(res))
9179 
9180             res=IPersistStream_Save(pPStm,pStm,TRUE);
9181     }
9182 
9183     TRACE("Finished Save\n");
9184     return res;
9185 }
9186 
9187 /*************************************************************************
9188  * STORAGE_CreateOleStream [Internal]
9189  *
9190  * Creates the "\001OLE" stream in the IStorage if necessary.
9191  *
9192  * PARAMS
9193  *     storage     [I] Dest storage to create the stream in
9194  *     flags       [I] flags to be set for newly created stream
9195  *
9196  * RETURNS
9197  *     HRESULT return value
9198  *
9199  * NOTES
9200  *
9201  *     This stream is still unknown, MS Word seems to have extra data
9202  *     but since the data is stored in the OLESTREAM there should be
9203  *     no need to recreate the stream.  If the stream is manually
9204  *     deleted it will create it with this default data.
9205  *
9206  */
STORAGE_CreateOleStream(IStorage * storage,DWORD flags)9207 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9208 {
9209     static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9210     static const DWORD version_magic = 0x02000001;
9211     IStream *stream;
9212     HRESULT hr;
9213 
9214     hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9215     if (hr == S_OK)
9216     {
9217         struct empty_1ole_stream {
9218             DWORD version_magic;
9219             DWORD flags;
9220             DWORD update_options;
9221             DWORD reserved;
9222             DWORD mon_stream_size;
9223         };
9224         struct empty_1ole_stream stream_data;
9225 
9226         stream_data.version_magic = version_magic;
9227         stream_data.flags = flags;
9228         stream_data.update_options = 0;
9229         stream_data.reserved = 0;
9230         stream_data.mon_stream_size = 0;
9231 
9232         hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9233         IStream_Release(stream);
9234     }
9235 
9236     return hr;
9237 }
9238 
9239 /* write a string to a stream, preceded by its length */
STREAM_WriteString(IStream * stm,LPCWSTR string)9240 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9241 {
9242     HRESULT r;
9243     LPSTR str;
9244     DWORD len = 0;
9245 
9246     if( string )
9247         len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9248     r = IStream_Write( stm, &len, sizeof(len), NULL);
9249     if( FAILED( r ) )
9250         return r;
9251     if(len == 0)
9252         return r;
9253     str = CoTaskMemAlloc( len );
9254     WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9255     r = IStream_Write( stm, str, len, NULL);
9256     CoTaskMemFree( str );
9257     return r;
9258 }
9259 
9260 /* read a string preceded by its length from a stream */
STREAM_ReadString(IStream * stm,LPWSTR * string)9261 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9262 {
9263     HRESULT r;
9264     DWORD len, count = 0;
9265     LPSTR str;
9266     LPWSTR wstr;
9267 
9268     r = IStream_Read( stm, &len, sizeof(len), &count );
9269     if( FAILED( r ) )
9270         return r;
9271     if( count != sizeof(len) )
9272         return E_OUTOFMEMORY;
9273 
9274     TRACE("%d bytes\n",len);
9275 
9276     str = CoTaskMemAlloc( len );
9277     if( !str )
9278         return E_OUTOFMEMORY;
9279     count = 0;
9280     r = IStream_Read( stm, str, len, &count );
9281     if( FAILED( r ) )
9282     {
9283         CoTaskMemFree( str );
9284         return r;
9285     }
9286     if( count != len )
9287     {
9288         CoTaskMemFree( str );
9289         return E_OUTOFMEMORY;
9290     }
9291 
9292     TRACE("Read string %s\n",debugstr_an(str,len));
9293 
9294     len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9295     wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9296     if( wstr )
9297     {
9298          MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9299          wstr[len] = 0;
9300     }
9301     CoTaskMemFree( str );
9302 
9303     *string = wstr;
9304 
9305     return r;
9306 }
9307 
9308 
STORAGE_WriteCompObj(LPSTORAGE pstg,CLSID * clsid,LPCWSTR lpszUserType,LPCWSTR szClipName,LPCWSTR szProgIDName)9309 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9310     LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9311 {
9312     IStream *pstm;
9313     HRESULT r = S_OK;
9314     static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9315 
9316     static const BYTE unknown1[12] =
9317        { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9318          0xFF, 0xFF, 0xFF, 0xFF};
9319     static const BYTE unknown2[16] =
9320        { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9321          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9322 
9323     TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9324            debugstr_w(lpszUserType), debugstr_w(szClipName),
9325            debugstr_w(szProgIDName));
9326 
9327     /*  Create a CompObj stream */
9328     r = IStorage_CreateStream(pstg, szwStreamName,
9329         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9330     if( FAILED (r) )
9331         return r;
9332 
9333     /* Write CompObj Structure to stream */
9334     r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9335 
9336     if( SUCCEEDED( r ) )
9337         r = WriteClassStm( pstm, clsid );
9338 
9339     if( SUCCEEDED( r ) )
9340         r = STREAM_WriteString( pstm, lpszUserType );
9341     if( SUCCEEDED( r ) )
9342         r = STREAM_WriteString( pstm, szClipName );
9343     if( SUCCEEDED( r ) )
9344         r = STREAM_WriteString( pstm, szProgIDName );
9345     if( SUCCEEDED( r ) )
9346         r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9347 
9348     IStream_Release( pstm );
9349 
9350     return r;
9351 }
9352 
9353 /***********************************************************************
9354  *               WriteFmtUserTypeStg (OLE32.@)
9355  */
WriteFmtUserTypeStg(LPSTORAGE pstg,CLIPFORMAT cf,LPOLESTR lpszUserType)9356 HRESULT WINAPI WriteFmtUserTypeStg(
9357 	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9358 {
9359     STATSTG stat;
9360     HRESULT r;
9361     WCHAR szwClipName[0x40];
9362     CLSID clsid;
9363     LPWSTR wstrProgID = NULL;
9364     DWORD n;
9365 
9366     TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9367 
9368     /* get the clipboard format name */
9369     if( cf )
9370     {
9371         n = GetClipboardFormatNameW(cf, szwClipName, ARRAY_SIZE(szwClipName));
9372         szwClipName[n]=0;
9373     }
9374 
9375     TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9376 
9377     r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9378     if(SUCCEEDED(r))
9379         clsid = stat.clsid;
9380     else
9381         clsid = CLSID_NULL;
9382 
9383     ProgIDFromCLSID(&clsid, &wstrProgID);
9384 
9385     TRACE("progid is %s\n",debugstr_w(wstrProgID));
9386 
9387     r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9388             cf ? szwClipName : NULL, wstrProgID );
9389 
9390     CoTaskMemFree(wstrProgID);
9391 
9392     return r;
9393 }
9394 
9395 
9396 /******************************************************************************
9397  *              ReadFmtUserTypeStg        [OLE32.@]
9398  */
ReadFmtUserTypeStg(LPSTORAGE pstg,CLIPFORMAT * pcf,LPOLESTR * lplpszUserType)9399 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9400 {
9401     HRESULT r;
9402     IStream *stm = 0;
9403     static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9404     unsigned char unknown1[12];
9405     unsigned char unknown2[16];
9406     DWORD count;
9407     LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9408     CLSID clsid;
9409 
9410     TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9411 
9412     r = IStorage_OpenStream( pstg, szCompObj, NULL,
9413                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9414     if( FAILED ( r ) )
9415     {
9416         WARN("Failed to open stream r = %08x\n", r);
9417         return r;
9418     }
9419 
9420     /* read the various parts of the structure */
9421     r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9422     if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9423         goto end;
9424     r = ReadClassStm( stm, &clsid );
9425     if( FAILED( r ) )
9426         goto end;
9427 
9428     r = STREAM_ReadString( stm, &szCLSIDName );
9429     if( FAILED( r ) )
9430         goto end;
9431 
9432     r = STREAM_ReadString( stm, &szOleTypeName );
9433     if( FAILED( r ) )
9434         goto end;
9435 
9436     r = STREAM_ReadString( stm, &szProgIDName );
9437     if( FAILED( r ) )
9438         goto end;
9439 
9440     r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9441     if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9442         goto end;
9443 
9444     /* ok, success... now we just need to store what we found */
9445     if( pcf )
9446         *pcf = RegisterClipboardFormatW( szOleTypeName );
9447 
9448     if( lplpszUserType )
9449     {
9450         *lplpszUserType = szCLSIDName;
9451         szCLSIDName = NULL;
9452     }
9453 
9454 end:
9455     CoTaskMemFree( szCLSIDName );
9456     CoTaskMemFree( szOleTypeName );
9457     CoTaskMemFree( szProgIDName );
9458     IStream_Release( stm );
9459 
9460     return r;
9461 }
9462 
9463 /******************************************************************************
9464  * StgIsStorageFile [OLE32.@]
9465  * Verify if the file contains a storage object
9466  *
9467  * PARAMS
9468  *  fn      [ I] Filename
9469  *
9470  * RETURNS
9471  *  S_OK    if file has magic bytes as a storage object
9472  *  S_FALSE if file is not storage
9473  */
9474 HRESULT WINAPI
StgIsStorageFile(LPCOLESTR fn)9475 StgIsStorageFile(LPCOLESTR fn)
9476 {
9477 	HANDLE		hf;
9478 	BYTE		magic[8];
9479 	DWORD		bytes_read;
9480 
9481 	TRACE("%s\n", debugstr_w(fn));
9482 	hf = CreateFileW(fn, GENERIC_READ,
9483 	                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9484 	                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9485 
9486 	if (hf == INVALID_HANDLE_VALUE)
9487 		return STG_E_FILENOTFOUND;
9488 
9489 	if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9490 	{
9491 		WARN(" unable to read file\n");
9492 		CloseHandle(hf);
9493 		return S_FALSE;
9494 	}
9495 
9496 	CloseHandle(hf);
9497 
9498 	if (bytes_read != 8) {
9499 		TRACE(" too short\n");
9500 		return S_FALSE;
9501 	}
9502 
9503 	if (!memcmp(magic,STORAGE_magic,8)) {
9504 		TRACE(" -> YES\n");
9505 		return S_OK;
9506 	}
9507 
9508 	TRACE(" -> Invalid header.\n");
9509 	return S_FALSE;
9510 }
9511 
9512 /***********************************************************************
9513  *		WriteClassStm (OLE32.@)
9514  *
9515  * Writes a CLSID to a stream.
9516  *
9517  * PARAMS
9518  *  pStm   [I] Stream to write to.
9519  *  rclsid [I] CLSID to write.
9520  *
9521  * RETURNS
9522  *  Success: S_OK.
9523  *  Failure: HRESULT code.
9524  */
WriteClassStm(IStream * pStm,REFCLSID rclsid)9525 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9526 {
9527     TRACE("(%p,%p)\n",pStm,rclsid);
9528 
9529     if (!pStm || !rclsid)
9530         return E_INVALIDARG;
9531 
9532     return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9533 }
9534 
9535 /***********************************************************************
9536  *		ReadClassStm (OLE32.@)
9537  *
9538  * Reads a CLSID from a stream.
9539  *
9540  * PARAMS
9541  *  pStm   [I] Stream to read from.
9542  *  rclsid [O] CLSID to read.
9543  *
9544  * RETURNS
9545  *  Success: S_OK.
9546  *  Failure: HRESULT code.
9547  */
ReadClassStm(IStream * pStm,CLSID * pclsid)9548 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9549 {
9550     ULONG nbByte;
9551     HRESULT res;
9552 
9553     TRACE("(%p,%p)\n",pStm,pclsid);
9554 
9555     if (!pStm || !pclsid)
9556         return E_INVALIDARG;
9557 
9558     /* clear the output args */
9559     *pclsid = CLSID_NULL;
9560 
9561     res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9562 
9563     if (FAILED(res))
9564         return res;
9565 
9566     if (nbByte != sizeof(CLSID))
9567         return STG_E_READFAULT;
9568     else
9569         return S_OK;
9570 }
9571 
9572 
9573 /************************************************************************
9574  * OleConvert Functions
9575  ***********************************************************************/
9576 
9577 #define OLESTREAM_ID 0x501
9578 #define OLESTREAM_MAX_STR_LEN 255
9579 
9580 /* OLESTREAM memory structure to use for Get and Put Routines */
9581 typedef struct
9582 {
9583     DWORD dwOleID;
9584     DWORD dwTypeID;
9585     DWORD dwOleTypeNameLength;
9586     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
9587     CHAR  *pstrOleObjFileName;
9588     DWORD dwOleObjFileNameLength;
9589     DWORD dwMetaFileWidth;
9590     DWORD dwMetaFileHeight;
9591     CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
9592     DWORD dwDataLength;
9593     BYTE *pData;
9594 } OLECONVERT_OLESTREAM_DATA;
9595 
9596 /* CompObj Stream structure */
9597 typedef struct
9598 {
9599     BYTE byUnknown1[12];
9600     CLSID clsid;
9601     DWORD dwCLSIDNameLength;
9602     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
9603     DWORD dwOleTypeNameLength;
9604     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
9605     DWORD dwProgIDNameLength;
9606     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
9607     BYTE byUnknown2[16];
9608 } OLECONVERT_ISTORAGE_COMPOBJ;
9609 
9610 /* Ole Presentation Stream structure */
9611 typedef struct
9612 {
9613     BYTE byUnknown1[28];
9614     DWORD dwExtentX;
9615     DWORD dwExtentY;
9616     DWORD dwSize;
9617     BYTE *pData;
9618 } OLECONVERT_ISTORAGE_OLEPRES;
9619 
9620 
9621 /*************************************************************************
9622  * OLECONVERT_LoadOLE10 [Internal]
9623  *
9624  * Loads the OLE10 STREAM to memory
9625  *
9626  * PARAMS
9627  *     pOleStream   [I] The OLESTREAM
9628  *     pData        [I] Data Structure for the OLESTREAM Data
9629  *
9630  * RETURNS
9631  *     Success:  S_OK
9632  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
9633  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9634  *
9635  * NOTES
9636  *     This function is used by OleConvertOLESTREAMToIStorage only.
9637  *
9638  *     Memory allocated for pData must be freed by the caller
9639  */
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream,OLECONVERT_OLESTREAM_DATA * pData,BOOL bStrem1)9640 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9641 {
9642 	DWORD dwSize;
9643 	HRESULT hRes = S_OK;
9644 	int nTryCnt=0;
9645 	int max_try = 6;
9646 
9647 	pData->pData = NULL;
9648 	pData->pstrOleObjFileName = NULL;
9649 
9650 	for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9651 	{
9652 	/* Get the OleID */
9653 	dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9654 	if(dwSize != sizeof(pData->dwOleID))
9655 	{
9656 		hRes = CONVERT10_E_OLESTREAM_GET;
9657 	}
9658 	else if(pData->dwOleID != OLESTREAM_ID)
9659 	{
9660 		hRes = CONVERT10_E_OLESTREAM_FMT;
9661 	}
9662 		else
9663 		{
9664 			hRes = S_OK;
9665 			break;
9666 		}
9667 	}
9668 
9669 	if(hRes == S_OK)
9670 	{
9671 		/* Get the TypeID... more info needed for this field */
9672 		dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9673 		if(dwSize != sizeof(pData->dwTypeID))
9674 		{
9675 			hRes = CONVERT10_E_OLESTREAM_GET;
9676 		}
9677 	}
9678 	if(hRes == S_OK)
9679 	{
9680 		if(pData->dwTypeID != 0)
9681 		{
9682 			/* Get the length of the OleTypeName */
9683 			dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9684 			if(dwSize != sizeof(pData->dwOleTypeNameLength))
9685 			{
9686 				hRes = CONVERT10_E_OLESTREAM_GET;
9687 			}
9688 
9689 			if(hRes == S_OK)
9690 			{
9691 				if(pData->dwOleTypeNameLength > 0)
9692 				{
9693 					/* Get the OleTypeName */
9694 					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9695 					if(dwSize != pData->dwOleTypeNameLength)
9696 					{
9697 						hRes = CONVERT10_E_OLESTREAM_GET;
9698 					}
9699 				}
9700 			}
9701 			if(bStrem1)
9702 			{
9703 				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9704 				if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9705 				{
9706 					hRes = CONVERT10_E_OLESTREAM_GET;
9707 				}
9708 			if(hRes == S_OK)
9709 			{
9710 					if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9711 						pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9712 					pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9713 					if(pData->pstrOleObjFileName)
9714 					{
9715 						dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9716 						if(dwSize != pData->dwOleObjFileNameLength)
9717 						{
9718 							hRes = CONVERT10_E_OLESTREAM_GET;
9719 						}
9720 					}
9721 					else
9722 						hRes = CONVERT10_E_OLESTREAM_GET;
9723 				}
9724 			}
9725 			else
9726 			{
9727 				/* Get the Width of the Metafile */
9728 				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9729 				if(dwSize != sizeof(pData->dwMetaFileWidth))
9730 				{
9731 					hRes = CONVERT10_E_OLESTREAM_GET;
9732 				}
9733 			if(hRes == S_OK)
9734 			{
9735 				/* Get the Height of the Metafile */
9736 				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9737 				if(dwSize != sizeof(pData->dwMetaFileHeight))
9738 				{
9739 					hRes = CONVERT10_E_OLESTREAM_GET;
9740 				}
9741 			}
9742 			}
9743 			if(hRes == S_OK)
9744 			{
9745 				/* Get the Length of the Data */
9746 				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9747 				if(dwSize != sizeof(pData->dwDataLength))
9748 				{
9749 					hRes = CONVERT10_E_OLESTREAM_GET;
9750 				}
9751 			}
9752 
9753 			if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9754 			{
9755 				if(!bStrem1) /* if it is a second OLE stream data */
9756 				{
9757 					pData->dwDataLength -= 8;
9758 					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9759 					if(dwSize != sizeof(pData->strUnknown))
9760 					{
9761 						hRes = CONVERT10_E_OLESTREAM_GET;
9762 					}
9763 				}
9764 			}
9765 			if(hRes == S_OK)
9766 			{
9767 				if(pData->dwDataLength > 0)
9768 				{
9769 					pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9770 
9771 					/* Get Data (ex. IStorage, Metafile, or BMP) */
9772 					if(pData->pData)
9773 					{
9774 						dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9775 						if(dwSize != pData->dwDataLength)
9776 						{
9777 							hRes = CONVERT10_E_OLESTREAM_GET;
9778 						}
9779 					}
9780 					else
9781 					{
9782 						hRes = CONVERT10_E_OLESTREAM_GET;
9783 					}
9784 				}
9785 			}
9786 		}
9787 	}
9788 	return hRes;
9789 }
9790 
9791 /*************************************************************************
9792  * OLECONVERT_SaveOLE10 [Internal]
9793  *
9794  * Saves the OLE10 STREAM From memory
9795  *
9796  * PARAMS
9797  *     pData        [I] Data Structure for the OLESTREAM Data
9798  *     pOleStream   [I] The OLESTREAM to save
9799  *
9800  * RETURNS
9801  *     Success:  S_OK
9802  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
9803  *
9804  * NOTES
9805  *     This function is used by OleConvertIStorageToOLESTREAM only.
9806  *
9807  */
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA * pData,LPOLESTREAM pOleStream)9808 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9809 {
9810     DWORD dwSize;
9811     HRESULT hRes = S_OK;
9812 
9813 
9814    /* Set the OleID */
9815     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9816     if(dwSize != sizeof(pData->dwOleID))
9817     {
9818         hRes = CONVERT10_E_OLESTREAM_PUT;
9819     }
9820 
9821     if(hRes == S_OK)
9822     {
9823         /* Set the TypeID */
9824         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9825         if(dwSize != sizeof(pData->dwTypeID))
9826         {
9827             hRes = CONVERT10_E_OLESTREAM_PUT;
9828         }
9829     }
9830 
9831     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9832     {
9833         /* Set the Length of the OleTypeName */
9834         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9835         if(dwSize != sizeof(pData->dwOleTypeNameLength))
9836         {
9837             hRes = CONVERT10_E_OLESTREAM_PUT;
9838         }
9839 
9840         if(hRes == S_OK)
9841         {
9842             if(pData->dwOleTypeNameLength > 0)
9843             {
9844                 /* Set the OleTypeName */
9845                 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9846                 if(dwSize != pData->dwOleTypeNameLength)
9847                 {
9848                     hRes = CONVERT10_E_OLESTREAM_PUT;
9849                 }
9850             }
9851         }
9852 
9853         if(hRes == S_OK)
9854         {
9855             /* Set the width of the Metafile */
9856             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9857             if(dwSize != sizeof(pData->dwMetaFileWidth))
9858             {
9859                 hRes = CONVERT10_E_OLESTREAM_PUT;
9860             }
9861         }
9862 
9863         if(hRes == S_OK)
9864         {
9865             /* Set the height of the Metafile */
9866             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9867             if(dwSize != sizeof(pData->dwMetaFileHeight))
9868             {
9869                 hRes = CONVERT10_E_OLESTREAM_PUT;
9870             }
9871         }
9872 
9873         if(hRes == S_OK)
9874         {
9875             /* Set the length of the Data */
9876             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9877             if(dwSize != sizeof(pData->dwDataLength))
9878             {
9879                 hRes = CONVERT10_E_OLESTREAM_PUT;
9880             }
9881         }
9882 
9883         if(hRes == S_OK)
9884         {
9885             if(pData->dwDataLength > 0)
9886             {
9887                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9888                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
9889                 if(dwSize != pData->dwDataLength)
9890                 {
9891                     hRes = CONVERT10_E_OLESTREAM_PUT;
9892                 }
9893             }
9894         }
9895     }
9896     return hRes;
9897 }
9898 
9899 /*************************************************************************
9900  * OLECONVERT_GetOLE20FromOLE10[Internal]
9901  *
9902  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9903  * opens it, and copies the content to the dest IStorage for
9904  * OleConvertOLESTREAMToIStorage
9905  *
9906  *
9907  * PARAMS
9908  *     pDestStorage  [I] The IStorage to copy the data to
9909  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
9910  *     nBufferLength [I] The size of the buffer
9911  *
9912  * RETURNS
9913  *     Nothing
9914  *
9915  * NOTES
9916  *
9917  *
9918  */
OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage,const BYTE * pBuffer,DWORD nBufferLength)9919 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9920 {
9921     HRESULT hRes;
9922     HANDLE hFile;
9923     IStorage *pTempStorage;
9924     DWORD dwNumOfBytesWritten;
9925     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9926     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9927 
9928     /* Create a temp File */
9929     GetTempPathW(MAX_PATH, wstrTempDir);
9930     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9931     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9932 
9933     if(hFile != INVALID_HANDLE_VALUE)
9934     {
9935         /* Write IStorage Data to File */
9936         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9937         CloseHandle(hFile);
9938 
9939         /* Open and copy temp storage to the Dest Storage */
9940         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9941         if(hRes == S_OK)
9942         {
9943             hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9944             IStorage_Release(pTempStorage);
9945         }
9946         DeleteFileW(wstrTempFile);
9947     }
9948 }
9949 
9950 
9951 /*************************************************************************
9952  * OLECONVERT_WriteOLE20ToBuffer [Internal]
9953  *
9954  * Saves the OLE10 STREAM From memory
9955  *
9956  * PARAMS
9957  *     pStorage  [I] The Src IStorage to copy
9958  *     pData     [I] The Dest Memory to write to.
9959  *
9960  * RETURNS
9961  *     The size in bytes allocated for pData
9962  *
9963  * NOTES
9964  *     Memory allocated for pData must be freed by the caller
9965  *
9966  *     Used by OleConvertIStorageToOLESTREAM only.
9967  *
9968  */
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage,BYTE ** pData)9969 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9970 {
9971     HANDLE hFile;
9972     HRESULT hRes;
9973     DWORD nDataLength = 0;
9974     IStorage *pTempStorage;
9975     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9976     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9977 
9978     *pData = NULL;
9979 
9980     /* Create temp Storage */
9981     GetTempPathW(MAX_PATH, wstrTempDir);
9982     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9983     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9984 
9985     if(hRes == S_OK)
9986     {
9987         /* Copy Src Storage to the Temp Storage */
9988         IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9989         IStorage_Release(pTempStorage);
9990 
9991         /* Open Temp Storage as a file and copy to memory */
9992         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9993         if(hFile != INVALID_HANDLE_VALUE)
9994         {
9995             nDataLength = GetFileSize(hFile, NULL);
9996             *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9997             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9998             CloseHandle(hFile);
9999         }
10000         DeleteFileW(wstrTempFile);
10001     }
10002     return nDataLength;
10003 }
10004 
10005 /*************************************************************************
10006  * OLECONVERT_CreateCompObjStream [Internal]
10007  *
10008  * Creates a "\001CompObj" is the destination IStorage if necessary.
10009  *
10010  * PARAMS
10011  *     pStorage       [I] The dest IStorage to create the CompObj Stream
10012  *                        if necessary.
10013  *     strOleTypeName [I] The ProgID
10014  *
10015  * RETURNS
10016  *     Success:  S_OK
10017  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10018  *
10019  * NOTES
10020  *     This function is used by OleConvertOLESTREAMToIStorage only.
10021  *
10022  *     The stream data is stored in the OLESTREAM and there should be
10023  *     no need to recreate the stream.  If the stream is manually
10024  *     deleted it will attempt to create it by querying the registry.
10025  *
10026  *
10027  */
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage,LPCSTR strOleTypeName)10028 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
10029 {
10030     IStream *pStream;
10031     HRESULT hStorageRes, hRes = S_OK;
10032     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
10033     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10034     WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
10035 
10036     static const BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
10037     static const BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
10038 
10039     /* Initialize the CompObj structure */
10040     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
10041     memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
10042     memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
10043 
10044 
10045     /*  Create a CompObj stream if it doesn't exist */
10046     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
10047         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10048     if(hStorageRes == S_OK)
10049     {
10050         /* copy the OleTypeName to the compobj struct */
10051         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
10052         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
10053 
10054         /* copy the OleTypeName to the compobj struct */
10055         /* Note: in the test made, these were Identical      */
10056         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
10057         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
10058 
10059         /* Get the CLSID */
10060         MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
10061                              bufferW, OLESTREAM_MAX_STR_LEN );
10062         hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
10063 
10064         if(hRes == S_OK)
10065         {
10066             HKEY hKey;
10067             LONG hErr;
10068             /* Get the CLSID Default Name from the Registry */
10069             hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
10070             if(hErr == ERROR_SUCCESS)
10071             {
10072                 char strTemp[OLESTREAM_MAX_STR_LEN];
10073                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
10074                 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
10075                 if(hErr == ERROR_SUCCESS)
10076                 {
10077                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
10078                 }
10079                 RegCloseKey(hKey);
10080             }
10081         }
10082 
10083         /* Write CompObj Structure to stream */
10084         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
10085 
10086         WriteClassStm(pStream,&(IStorageCompObj.clsid));
10087 
10088         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
10089         if(IStorageCompObj.dwCLSIDNameLength > 0)
10090         {
10091             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
10092         }
10093         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
10094         if(IStorageCompObj.dwOleTypeNameLength > 0)
10095         {
10096             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
10097         }
10098         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
10099         if(IStorageCompObj.dwProgIDNameLength > 0)
10100         {
10101             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10102         }
10103         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10104         IStream_Release(pStream);
10105     }
10106     return hRes;
10107 }
10108 
10109 
10110 /*************************************************************************
10111  * OLECONVERT_CreateOlePresStream[Internal]
10112  *
10113  * Creates the "\002OlePres000" Stream with the Metafile data
10114  *
10115  * PARAMS
10116  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
10117  *     dwExtentX    [I] Width of the Metafile
10118  *     dwExtentY    [I] Height of the Metafile
10119  *     pData        [I] Metafile data
10120  *     dwDataLength [I] Size of the Metafile data
10121  *
10122  * RETURNS
10123  *     Success:  S_OK
10124  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
10125  *
10126  * NOTES
10127  *     This function is used by OleConvertOLESTREAMToIStorage only.
10128  *
10129  */
OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage,DWORD dwExtentX,DWORD dwExtentY,BYTE * pData,DWORD dwDataLength)10130 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10131 {
10132     HRESULT hRes;
10133     IStream *pStream;
10134     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10135     static const BYTE pOlePresStreamHeader[] =
10136     {
10137         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
10138         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10139         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10140         0x00, 0x00, 0x00, 0x00
10141     };
10142 
10143     static const BYTE pOlePresStreamHeaderEmpty[] =
10144     {
10145         0x00, 0x00, 0x00, 0x00,
10146         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10147         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
10148         0x00, 0x00, 0x00, 0x00
10149     };
10150 
10151     /* Create the OlePres000 Stream */
10152     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10153         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10154 
10155     if(hRes == S_OK)
10156     {
10157         DWORD nHeaderSize;
10158         OLECONVERT_ISTORAGE_OLEPRES OlePres;
10159 
10160         memset(&OlePres, 0, sizeof(OlePres));
10161         /* Do we have any metafile data to save */
10162         if(dwDataLength > 0)
10163         {
10164             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
10165             nHeaderSize = sizeof(pOlePresStreamHeader);
10166         }
10167         else
10168         {
10169             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
10170             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
10171         }
10172         /* Set width and height of the metafile */
10173         OlePres.dwExtentX = dwExtentX;
10174         OlePres.dwExtentY = -dwExtentY;
10175 
10176         /* Set Data and Length */
10177         if(dwDataLength > sizeof(METAFILEPICT16))
10178         {
10179             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
10180             OlePres.pData = &(pData[8]);
10181         }
10182         /* Save OlePres000 Data to Stream */
10183         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
10184         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
10185         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
10186         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
10187         if(OlePres.dwSize > 0)
10188         {
10189             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
10190         }
10191         IStream_Release(pStream);
10192     }
10193 }
10194 
10195 /*************************************************************************
10196  * OLECONVERT_CreateOle10NativeStream [Internal]
10197  *
10198  * Creates the "\001Ole10Native" Stream (should contain a BMP)
10199  *
10200  * PARAMS
10201  *     pStorage     [I] Dest storage to create the stream in
10202  *     pData        [I] Ole10 Native Data (ex. bmp)
10203  *     dwDataLength [I] Size of the Ole10 Native Data
10204  *
10205  * RETURNS
10206  *     Nothing
10207  *
10208  * NOTES
10209  *     This function is used by OleConvertOLESTREAMToIStorage only.
10210  *
10211  *     Might need to verify the data and return appropriate error message
10212  *
10213  */
OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage,const BYTE * pData,DWORD dwDataLength)10214 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10215 {
10216     HRESULT hRes;
10217     IStream *pStream;
10218     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10219 
10220     /* Create the Ole10Native Stream */
10221     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10222         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
10223 
10224     if(hRes == S_OK)
10225     {
10226         /* Write info to stream */
10227         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
10228         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
10229         IStream_Release(pStream);
10230     }
10231 
10232 }
10233 
10234 /*************************************************************************
10235  * OLECONVERT_GetOLE10ProgID [Internal]
10236  *
10237  * Finds the ProgID (or OleTypeID) from the IStorage
10238  *
10239  * PARAMS
10240  *     pStorage        [I] The Src IStorage to get the ProgID
10241  *     strProgID       [I] the ProgID string to get
10242  *     dwSize          [I] the size of the string
10243  *
10244  * RETURNS
10245  *     Success:  S_OK
10246  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
10247  *
10248  * NOTES
10249  *     This function is used by OleConvertIStorageToOLESTREAM only.
10250  *
10251  *
10252  */
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage,char * strProgID,DWORD * dwSize)10253 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10254 {
10255     HRESULT hRes;
10256     IStream *pStream;
10257     LARGE_INTEGER iSeekPos;
10258     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10259     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10260 
10261     /* Open the CompObj Stream */
10262     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10263         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10264     if(hRes == S_OK)
10265     {
10266 
10267         /*Get the OleType from the CompObj Stream */
10268         iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
10269         iSeekPos.u.HighPart = 0;
10270 
10271         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10272         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10273         iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10274         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10275         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10276         iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10277         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10278 
10279         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10280         if(*dwSize > 0)
10281         {
10282             IStream_Read(pStream, strProgID, *dwSize, NULL);
10283         }
10284         IStream_Release(pStream);
10285     }
10286     else
10287     {
10288         STATSTG stat;
10289         LPOLESTR wstrProgID;
10290 
10291         /* Get the OleType from the registry */
10292         REFCLSID clsid = &(stat.clsid);
10293         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10294         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10295         if(hRes == S_OK)
10296         {
10297             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10298             CoTaskMemFree(wstrProgID);
10299         }
10300 
10301     }
10302     return hRes;
10303 }
10304 
10305 /*************************************************************************
10306  * OLECONVERT_GetOle10PresData [Internal]
10307  *
10308  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10309  *
10310  * PARAMS
10311  *     pStorage     [I] Src IStroage
10312  *     pOleStream   [I] Dest OleStream Mem Struct
10313  *
10314  * RETURNS
10315  *     Nothing
10316  *
10317  * NOTES
10318  *     This function is used by OleConvertIStorageToOLESTREAM only.
10319  *
10320  *     Memory allocated for pData must be freed by the caller
10321  *
10322  *
10323  */
OLECONVERT_GetOle10PresData(LPSTORAGE pStorage,OLECONVERT_OLESTREAM_DATA * pOleStreamData)10324 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10325 {
10326 
10327     HRESULT hRes;
10328     IStream *pStream;
10329     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10330 
10331     /* Initialize Default data for OLESTREAM */
10332     pOleStreamData[0].dwOleID = OLESTREAM_ID;
10333     pOleStreamData[0].dwTypeID = 2;
10334     pOleStreamData[1].dwOleID = OLESTREAM_ID;
10335     pOleStreamData[1].dwTypeID = 0;
10336     pOleStreamData[0].dwMetaFileWidth = 0;
10337     pOleStreamData[0].dwMetaFileHeight = 0;
10338     pOleStreamData[0].pData = NULL;
10339     pOleStreamData[1].pData = NULL;
10340 
10341     /* Open Ole10Native Stream */
10342     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10343         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10344     if(hRes == S_OK)
10345     {
10346 
10347         /* Read Size and Data */
10348         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10349         if(pOleStreamData->dwDataLength > 0)
10350         {
10351             pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10352             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10353         }
10354         IStream_Release(pStream);
10355     }
10356 
10357 }
10358 
10359 
10360 /*************************************************************************
10361  * OLECONVERT_GetOle20PresData[Internal]
10362  *
10363  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10364  *
10365  * PARAMS
10366  *     pStorage         [I] Src IStroage
10367  *     pOleStreamData   [I] Dest OleStream Mem Struct
10368  *
10369  * RETURNS
10370  *     Nothing
10371  *
10372  * NOTES
10373  *     This function is used by OleConvertIStorageToOLESTREAM only.
10374  *
10375  *     Memory allocated for pData must be freed by the caller
10376  */
OLECONVERT_GetOle20PresData(LPSTORAGE pStorage,OLECONVERT_OLESTREAM_DATA * pOleStreamData)10377 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10378 {
10379     HRESULT hRes;
10380     IStream *pStream;
10381     OLECONVERT_ISTORAGE_OLEPRES olePress;
10382     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10383 
10384     /* Initialize Default data for OLESTREAM */
10385     pOleStreamData[0].dwOleID = OLESTREAM_ID;
10386     pOleStreamData[0].dwTypeID = 2;
10387     pOleStreamData[0].dwMetaFileWidth = 0;
10388     pOleStreamData[0].dwMetaFileHeight = 0;
10389     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10390     pOleStreamData[1].dwOleID = OLESTREAM_ID;
10391     pOleStreamData[1].dwTypeID = 0;
10392     pOleStreamData[1].dwOleTypeNameLength = 0;
10393     pOleStreamData[1].strOleTypeName[0] = 0;
10394     pOleStreamData[1].dwMetaFileWidth = 0;
10395     pOleStreamData[1].dwMetaFileHeight = 0;
10396     pOleStreamData[1].pData = NULL;
10397     pOleStreamData[1].dwDataLength = 0;
10398 
10399 
10400     /* Open OlePress000 stream */
10401     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10402         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10403     if(hRes == S_OK)
10404     {
10405         LARGE_INTEGER iSeekPos;
10406         METAFILEPICT16 MetaFilePict;
10407         static const char strMetafilePictName[] = "METAFILEPICT";
10408 
10409         /* Set the TypeID for a Metafile */
10410         pOleStreamData[1].dwTypeID = 5;
10411 
10412         /* Set the OleTypeName to Metafile */
10413         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10414         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10415 
10416         iSeekPos.u.HighPart = 0;
10417         iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10418 
10419         /* Get Presentation Data */
10420         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10421         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10422         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10423         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10424 
10425         /*Set width and Height */
10426         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10427         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10428         if(olePress.dwSize > 0)
10429         {
10430             /* Set Length */
10431             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
10432 
10433             /* Set MetaFilePict struct */
10434             MetaFilePict.mm = 8;
10435             MetaFilePict.xExt = olePress.dwExtentX;
10436             MetaFilePict.yExt = olePress.dwExtentY;
10437             MetaFilePict.hMF = 0;
10438 
10439             /* Get Metafile Data */
10440             pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10441             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10442             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10443         }
10444         IStream_Release(pStream);
10445     }
10446 }
10447 
10448 /*************************************************************************
10449  * OleConvertOLESTREAMToIStorage [OLE32.@]
10450  *
10451  * Read info on MSDN
10452  *
10453  * TODO
10454  *      DVTARGETDEVICE parameter is not handled
10455  *      Still unsure of some mem fields for OLE 10 Stream
10456  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10457  *      and "\001OLE" streams
10458  *
10459  */
OleConvertOLESTREAMToIStorage(LPOLESTREAM pOleStream,LPSTORAGE pstg,const DVTARGETDEVICE * ptd)10460 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10461     LPOLESTREAM pOleStream,
10462     LPSTORAGE pstg,
10463     const DVTARGETDEVICE* ptd)
10464 {
10465     int i;
10466     HRESULT hRes=S_OK;
10467     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10468 
10469     TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10470 
10471     memset(pOleStreamData, 0, sizeof(pOleStreamData));
10472 
10473     if(ptd != NULL)
10474     {
10475         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10476     }
10477 
10478     if(pstg == NULL || pOleStream == NULL)
10479     {
10480         hRes = E_INVALIDARG;
10481     }
10482 
10483     if(hRes == S_OK)
10484     {
10485         /* Load the OLESTREAM to Memory */
10486         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10487     }
10488 
10489     if(hRes == S_OK)
10490     {
10491         /* Load the OLESTREAM to Memory (part 2)*/
10492         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10493     }
10494 
10495     if(hRes == S_OK)
10496     {
10497 
10498         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10499         {
10500             /* Do we have the IStorage Data in the OLESTREAM */
10501             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10502             {
10503                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10504                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10505             }
10506             else
10507             {
10508                 /* It must be an original OLE 1.0 source */
10509                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10510             }
10511         }
10512         else
10513         {
10514             /* It must be an original OLE 1.0 source */
10515             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10516         }
10517 
10518         /* Create CompObj Stream if necessary */
10519         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10520         if(hRes == S_OK)
10521         {
10522             /*Create the Ole Stream if necessary */
10523             STORAGE_CreateOleStream(pstg, 0);
10524         }
10525     }
10526 
10527 
10528     /* Free allocated memory */
10529     for(i=0; i < 2; i++)
10530     {
10531         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10532         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10533         pOleStreamData[i].pstrOleObjFileName = NULL;
10534     }
10535     return hRes;
10536 }
10537 
10538 /*************************************************************************
10539  * OleConvertIStorageToOLESTREAM [OLE32.@]
10540  *
10541  * Read info on MSDN
10542  *
10543  * Read info on MSDN
10544  *
10545  * TODO
10546  *      Still unsure of some mem fields for OLE 10 Stream
10547  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10548  *      and "\001OLE" streams.
10549  *
10550  */
OleConvertIStorageToOLESTREAM(LPSTORAGE pstg,LPOLESTREAM pOleStream)10551 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10552     LPSTORAGE pstg,
10553     LPOLESTREAM pOleStream)
10554 {
10555     int i;
10556     HRESULT hRes = S_OK;
10557     IStream *pStream;
10558     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10559     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10560 
10561     TRACE("%p %p\n", pstg, pOleStream);
10562 
10563     memset(pOleStreamData, 0, sizeof(pOleStreamData));
10564 
10565     if(pstg == NULL || pOleStream == NULL)
10566     {
10567         hRes = E_INVALIDARG;
10568     }
10569     if(hRes == S_OK)
10570     {
10571         /* Get the ProgID */
10572         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10573         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10574     }
10575     if(hRes == S_OK)
10576     {
10577         /* Was it originally Ole10 */
10578         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10579         if(hRes == S_OK)
10580         {
10581             IStream_Release(pStream);
10582             /* Get Presentation Data for Ole10Native */
10583             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10584         }
10585         else
10586         {
10587             /* Get Presentation Data (OLE20) */
10588             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10589         }
10590 
10591         /* Save OLESTREAM */
10592         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10593         if(hRes == S_OK)
10594         {
10595             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10596         }
10597 
10598     }
10599 
10600     /* Free allocated memory */
10601     for(i=0; i < 2; i++)
10602     {
10603         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10604     }
10605 
10606     return hRes;
10607 }
10608 
10609 enum stream_1ole_flags {
10610     OleStream_LinkedObject = 0x00000001,
10611     OleStream_Convert      = 0x00000004
10612 };
10613 
10614 /***********************************************************************
10615  *		GetConvertStg (OLE32.@)
10616  */
GetConvertStg(IStorage * stg)10617 HRESULT WINAPI GetConvertStg(IStorage *stg)
10618 {
10619     static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10620     static const DWORD version_magic = 0x02000001;
10621     DWORD header[2];
10622     IStream *stream;
10623     HRESULT hr;
10624 
10625     TRACE("%p\n", stg);
10626 
10627     if (!stg) return E_INVALIDARG;
10628 
10629     hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10630     if (FAILED(hr)) return hr;
10631 
10632     hr = IStream_Read(stream, header, sizeof(header), NULL);
10633     IStream_Release(stream);
10634     if (FAILED(hr)) return hr;
10635 
10636     if (header[0] != version_magic)
10637     {
10638         ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10639         return E_FAIL;
10640     }
10641 
10642     return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10643 }
10644 
10645 /***********************************************************************
10646  *		SetConvertStg (OLE32.@)
10647  */
SetConvertStg(IStorage * storage,BOOL convert)10648 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10649 {
10650     static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10651     DWORD flags = convert ? OleStream_Convert : 0;
10652     IStream *stream;
10653     DWORD header[2];
10654     HRESULT hr;
10655 
10656     TRACE("(%p, %d)\n", storage, convert);
10657 
10658     hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10659     if (FAILED(hr))
10660     {
10661         if (hr != STG_E_FILENOTFOUND)
10662             return hr;
10663 
10664         return STORAGE_CreateOleStream(storage, flags);
10665     }
10666 
10667     hr = IStream_Read(stream, header, sizeof(header), NULL);
10668     if (FAILED(hr))
10669     {
10670         IStream_Release(stream);
10671         return hr;
10672     }
10673 
10674     /* update flag if differs */
10675     if ((header[1] ^ flags) & OleStream_Convert)
10676     {
10677         LARGE_INTEGER pos = {{0}};
10678 
10679         if (header[1] & OleStream_Convert)
10680             flags = header[1] & ~OleStream_Convert;
10681         else
10682             flags = header[1] |  OleStream_Convert;
10683 
10684         pos.QuadPart = sizeof(DWORD);
10685         hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10686         if (FAILED(hr))
10687         {
10688             IStream_Release(stream);
10689             return hr;
10690         }
10691 
10692         hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10693     }
10694 
10695     IStream_Release(stream);
10696     return hr;
10697 }
10698