ole32: Compare upper character values directly in entryNameCmp.
[wine] / dlls / ole32 / storage32.c
1 /*
2  * Compound Storage (32 bit version)
3  * Storage implementation
4  *
5  * This file contains the compound file implementation
6  * of the storage interface.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Sylvain St-Germain
10  * Copyright 1999 Thuy Nguyen
11  * Copyright 2005 Mike McCormack
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26  *
27  * NOTES
28  *  The compound file implementation of IStorage used for create
29  *  and manage substorages and streams within a storage object
30  *  residing in a compound file object.
31  */
32
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
49
50 #include "storage32.h"
51 #include "ole2.h"      /* For Write/ReadClassStm */
52
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
57
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
61
62 /*
63  * These are signatures to detect the type of Document file.
64  */
65 static const BYTE STORAGE_magic[8]    ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
67
68 static const char rootEntryName[] = "Root Entry";
69
70 /****************************************************************************
71  * Storage32InternalImpl definitions.
72  *
73  * Definition of the implementation structure for the IStorage32 interface.
74  * This one implements the IStorage32 interface for storage that are
75  * inside another storage.
76  */
77 struct StorageInternalImpl
78 {
79   struct StorageBaseImpl base;
80
81   /*
82    * Entry in the parent's stream tracking list
83    */
84   struct list ParentListEntry;
85
86   StorageBaseImpl *parentStorage;
87 };
88 typedef struct StorageInternalImpl StorageInternalImpl;
89
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92                                                           DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
96 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
98 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
99 static void StorageImpl_SaveFileHeader(StorageImpl* This);
100
101 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
103 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
104 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
106
107 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
109 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
110
111 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
112 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
113 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
114     ULONG blockIndex, ULONG offset, DWORD value);
115 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl*  This,
116     ULONG blockIndex, ULONG offset, DWORD* value);
117
118 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
119 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
120
121 /****************************************************************************
122  * Transacted storage object that reads/writes a snapshot file.
123  */
124 typedef struct TransactedSnapshotImpl
125 {
126   struct StorageBaseImpl base;
127
128   /*
129    * Changes are temporarily saved to the snapshot.
130    */
131   StorageBaseImpl *snapshot;
132
133   /*
134    * Changes are committed to the transacted parent.
135    */
136   StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
138
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
141
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
144 typedef struct
145 {
146     DWORD dwOleID;
147     DWORD dwTypeID;
148     DWORD dwOleTypeNameLength;
149     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
150     CHAR  *pstrOleObjFileName;
151     DWORD dwOleObjFileNameLength;
152     DWORD dwMetaFileWidth;
153     DWORD dwMetaFileHeight;
154     CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
155     DWORD dwDataLength;
156     BYTE *pData;
157 }OLECONVERT_OLESTREAM_DATA;
158
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 typedef struct
162 {
163     BYTE byUnknown1[12];
164     CLSID clsid;
165     DWORD dwCLSIDNameLength;
166     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167     DWORD dwOleTypeNameLength;
168     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169     DWORD dwProgIDNameLength;
170     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
171     BYTE byUnknown2[16];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
173
174
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
177 typedef struct
178 {
179     BYTE byUnknown1[28];
180     DWORD dwExtentX;
181     DWORD dwExtentY;
182     DWORD dwSize;
183     BYTE *pData;
184 }OLECONVERT_ISTORAGE_OLEPRES;
185
186
187
188 /***********************************************************************
189  * Forward declaration of internal functions used by the method DestroyElement
190  */
191 static HRESULT deleteStorageContents(
192   StorageBaseImpl *parentStorage,
193   DirRef       indexToDelete,
194   DirEntry     entryDataToDelete);
195
196 static HRESULT deleteStreamContents(
197   StorageBaseImpl *parentStorage,
198   DirRef        indexToDelete,
199   DirEntry      entryDataToDelete);
200
201 static HRESULT removeFromTree(
202   StorageBaseImpl *This,
203   DirRef        parentStorageIndex,
204   DirRef        deletedIndex);
205
206 /***********************************************************************
207  * Declaration of the functions used to manipulate DirEntry
208  */
209
210 static HRESULT insertIntoTree(
211   StorageBaseImpl *This,
212   DirRef        parentStorageIndex,
213   DirRef        newEntryIndex);
214
215 static LONG entryNameCmp(
216     const OLECHAR *name1,
217     const OLECHAR *name2);
218
219 static DirRef findElement(
220     StorageBaseImpl *storage,
221     DirRef storageEntry,
222     const OLECHAR *name,
223     DirEntry *data);
224
225 static HRESULT findTreeParent(
226     StorageBaseImpl *storage,
227     DirRef storageEntry,
228     const OLECHAR *childName,
229     DirEntry *parentData,
230     DirRef *parentEntry,
231     ULONG *relation);
232
233 /***********************************************************************
234  * Declaration of miscellaneous functions...
235  */
236 static HRESULT validateSTGM(DWORD stgmValue);
237
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
241
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
243
244
245 /****************************************************************************
246  * IEnumSTATSTGImpl definitions.
247  *
248  * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249  * This class allows iterating through the content of a storage and to find
250  * specific items inside it.
251  */
252 struct IEnumSTATSTGImpl
253 {
254   const IEnumSTATSTGVtbl *lpVtbl;    /* Needs to be the first item in the struct
255                                 * since we want to cast this in an IEnumSTATSTG pointer */
256
257   LONG           ref;                   /* Reference count */
258   StorageBaseImpl* parentStorage;         /* Reference to the parent storage */
259   DirRef         storageDirEntry;     /* Directory entry of the storage to enumerate */
260
261   /*
262    * The current implementation of the IEnumSTATSTGImpl class uses a stack
263    * to walk the directory entries to get the content of a storage. This stack
264    * is implemented by the following 3 data members
265    */
266   ULONG          stackSize;
267   ULONG          stackMaxSize;
268   DirRef*        stackToVisit;
269
270 #define ENUMSTATSGT_SIZE_INCREMENT 10
271 };
272
273
274 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
275 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
276 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
277 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
278
279 /************************************************************************
280 ** Block Functions
281 */
282
283 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
284 {
285     if (index == 0xffffffff)
286         index = 0;
287     else
288         index ++;
289
290     return index * BIG_BLOCK_SIZE;
291 }
292
293 /************************************************************************
294 ** Storage32BaseImpl implementation
295 */
296 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
297   ULARGE_INTEGER offset,
298   void*          buffer,
299   ULONG          size,
300   ULONG*         bytesRead)
301 {
302     return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
303 }
304
305 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
306   ULARGE_INTEGER offset,
307   const void*    buffer,
308   const ULONG    size,
309   ULONG*         bytesWritten)
310 {
311     return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
312 }
313
314 /************************************************************************
315  * Storage32BaseImpl_QueryInterface (IUnknown)
316  *
317  * This method implements the common QueryInterface for all IStorage32
318  * implementations contained in this file.
319  *
320  * See Windows documentation for more details on IUnknown methods.
321  */
322 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
323   IStorage*        iface,
324   REFIID             riid,
325   void**             ppvObject)
326 {
327   StorageBaseImpl *This = (StorageBaseImpl *)iface;
328
329   if ( (This==0) || (ppvObject==0) )
330     return E_INVALIDARG;
331
332   *ppvObject = 0;
333
334   if (IsEqualGUID(&IID_IUnknown, riid) ||
335       IsEqualGUID(&IID_IStorage, riid))
336   {
337     *ppvObject = This;
338   }
339   else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
340   {
341     *ppvObject = &This->pssVtbl;
342   }
343
344   if ((*ppvObject)==0)
345     return E_NOINTERFACE;
346
347   IStorage_AddRef(iface);
348
349   return S_OK;
350 }
351
352 /************************************************************************
353  * Storage32BaseImpl_AddRef (IUnknown)
354  *
355  * This method implements the common AddRef for all IStorage32
356  * implementations contained in this file.
357  *
358  * See Windows documentation for more details on IUnknown methods.
359  */
360 static ULONG WINAPI StorageBaseImpl_AddRef(
361             IStorage* iface)
362 {
363   StorageBaseImpl *This = (StorageBaseImpl *)iface;
364   ULONG ref = InterlockedIncrement(&This->ref);
365
366   TRACE("(%p) AddRef to %d\n", This, ref);
367
368   return ref;
369 }
370
371 /************************************************************************
372  * Storage32BaseImpl_Release (IUnknown)
373  *
374  * This method implements the common Release for all IStorage32
375  * implementations contained in this file.
376  *
377  * See Windows documentation for more details on IUnknown methods.
378  */
379 static ULONG WINAPI StorageBaseImpl_Release(
380       IStorage* iface)
381 {
382   StorageBaseImpl *This = (StorageBaseImpl *)iface;
383
384   ULONG ref = InterlockedDecrement(&This->ref);
385
386   TRACE("(%p) ReleaseRef to %d\n", This, ref);
387
388   if (ref == 0)
389   {
390     /*
391      * Since we are using a system of base-classes, we want to call the
392      * destructor of the appropriate derived class. To do this, we are
393      * using virtual functions to implement the destructor.
394      */
395     StorageBaseImpl_Destroy(This);
396   }
397
398   return ref;
399 }
400
401 /************************************************************************
402  * Storage32BaseImpl_OpenStream (IStorage)
403  *
404  * This method will open the specified stream object from the current storage.
405  *
406  * See Windows documentation for more details on IStorage methods.
407  */
408 static HRESULT WINAPI StorageBaseImpl_OpenStream(
409   IStorage*        iface,
410   const OLECHAR*   pwcsName,  /* [string][in] */
411   void*            reserved1, /* [unique][in] */
412   DWORD            grfMode,   /* [in]  */
413   DWORD            reserved2, /* [in]  */
414   IStream**        ppstm)     /* [out] */
415 {
416   StorageBaseImpl *This = (StorageBaseImpl *)iface;
417   StgStreamImpl*    newStream;
418   DirEntry          currentEntry;
419   DirRef            streamEntryRef;
420   HRESULT           res = STG_E_UNKNOWN;
421
422   TRACE("(%p, %s, %p, %x, %d, %p)\n",
423         iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
424
425   if ( (pwcsName==NULL) || (ppstm==0) )
426   {
427     res = E_INVALIDARG;
428     goto end;
429   }
430
431   *ppstm = NULL;
432
433   if ( FAILED( validateSTGM(grfMode) ) ||
434        STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
435   {
436     res = STG_E_INVALIDFLAG;
437     goto end;
438   }
439
440   /*
441    * As documented.
442    */
443   if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
444   {
445     res = STG_E_INVALIDFUNCTION;
446     goto end;
447   }
448
449   if (This->reverted)
450   {
451     res = STG_E_REVERTED;
452     goto end;
453   }
454
455   /*
456    * Check that we're compatible with the parent's storage mode, but
457    * only if we are not in transacted mode
458    */
459   if(!(This->openFlags & STGM_TRANSACTED)) {
460     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
461     {
462       res = STG_E_ACCESSDENIED;
463       goto end;
464     }
465   }
466
467   /*
468    * Search for the element with the given name
469    */
470   streamEntryRef = findElement(
471     This,
472     This->storageDirEntry,
473     pwcsName,
474     &currentEntry);
475
476   /*
477    * If it was found, construct the stream object and return a pointer to it.
478    */
479   if ( (streamEntryRef!=DIRENTRY_NULL) &&
480        (currentEntry.stgType==STGTY_STREAM) )
481   {
482     if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
483     {
484       /* A single stream cannot be opened a second time. */
485       res = STG_E_ACCESSDENIED;
486       goto end;
487     }
488
489     newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
490
491     if (newStream!=0)
492     {
493       newStream->grfMode = grfMode;
494       *ppstm = (IStream*)newStream;
495
496       IStream_AddRef(*ppstm);
497
498       res = S_OK;
499       goto end;
500     }
501
502     res = E_OUTOFMEMORY;
503     goto end;
504   }
505
506   res = STG_E_FILENOTFOUND;
507
508 end:
509   if (res == S_OK)
510     TRACE("<-- IStream %p\n", *ppstm);
511   TRACE("<-- %08x\n", res);
512   return res;
513 }
514
515 /************************************************************************
516  * Storage32BaseImpl_OpenStorage (IStorage)
517  *
518  * This method will open a new storage object from the current storage.
519  *
520  * See Windows documentation for more details on IStorage methods.
521  */
522 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
523   IStorage*        iface,
524   const OLECHAR*   pwcsName,      /* [string][unique][in] */
525   IStorage*        pstgPriority,  /* [unique][in] */
526   DWORD            grfMode,       /* [in] */
527   SNB              snbExclude,    /* [unique][in] */
528   DWORD            reserved,      /* [in] */
529   IStorage**       ppstg)         /* [out] */
530 {
531   StorageBaseImpl *This = (StorageBaseImpl *)iface;
532   StorageInternalImpl*   newStorage;
533   StorageBaseImpl*       newTransactedStorage;
534   DirEntry               currentEntry;
535   DirRef                 storageEntryRef;
536   HRESULT                res = STG_E_UNKNOWN;
537
538   TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
539         iface, debugstr_w(pwcsName), pstgPriority,
540         grfMode, snbExclude, reserved, ppstg);
541
542   if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
543   {
544     res = E_INVALIDARG;
545     goto end;
546   }
547
548   if (This->openFlags & STGM_SIMPLE)
549   {
550     res = STG_E_INVALIDFUNCTION;
551     goto end;
552   }
553
554   /* as documented */
555   if (snbExclude != NULL)
556   {
557     res = STG_E_INVALIDPARAMETER;
558     goto end;
559   }
560
561   if ( FAILED( validateSTGM(grfMode) ))
562   {
563     res = STG_E_INVALIDFLAG;
564     goto end;
565   }
566
567   /*
568    * As documented.
569    */
570   if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
571         (grfMode & STGM_DELETEONRELEASE) ||
572         (grfMode & STGM_PRIORITY) )
573   {
574     res = STG_E_INVALIDFUNCTION;
575     goto end;
576   }
577
578   if (This->reverted)
579     return STG_E_REVERTED;
580
581   /*
582    * Check that we're compatible with the parent's storage mode,
583    * but only if we are not transacted
584    */
585   if(!(This->openFlags & STGM_TRANSACTED)) {
586     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
587     {
588       res = STG_E_ACCESSDENIED;
589       goto end;
590     }
591   }
592
593   *ppstg = NULL;
594
595   storageEntryRef = findElement(
596                          This,
597                          This->storageDirEntry,
598                          pwcsName,
599                          &currentEntry);
600
601   if ( (storageEntryRef!=DIRENTRY_NULL) &&
602        (currentEntry.stgType==STGTY_STORAGE) )
603   {
604     if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
605     {
606       /* A single storage cannot be opened a second time. */
607       res = STG_E_ACCESSDENIED;
608       goto end;
609     }
610
611     newStorage = StorageInternalImpl_Construct(
612                    This,
613                    grfMode,
614                    storageEntryRef);
615
616     if (newStorage != 0)
617     {
618       if (grfMode & STGM_TRANSACTED)
619       {
620         res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
621
622         if (FAILED(res))
623         {
624           HeapFree(GetProcessHeap(), 0, newStorage);
625           goto end;
626         }
627
628         *ppstg = (IStorage*)newTransactedStorage;
629       }
630       else
631       {
632         *ppstg = (IStorage*)newStorage;
633       }
634
635       list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
636
637       res = S_OK;
638       goto end;
639     }
640
641     res = STG_E_INSUFFICIENTMEMORY;
642     goto end;
643   }
644
645   res = STG_E_FILENOTFOUND;
646
647 end:
648   TRACE("<-- %08x\n", res);
649   return res;
650 }
651
652 /************************************************************************
653  * Storage32BaseImpl_EnumElements (IStorage)
654  *
655  * This method will create an enumerator object that can be used to
656  * retrieve information about all the elements in the storage object.
657  *
658  * See Windows documentation for more details on IStorage methods.
659  */
660 static HRESULT WINAPI StorageBaseImpl_EnumElements(
661   IStorage*       iface,
662   DWORD           reserved1, /* [in] */
663   void*           reserved2, /* [size_is][unique][in] */
664   DWORD           reserved3, /* [in] */
665   IEnumSTATSTG**  ppenum)    /* [out] */
666 {
667   StorageBaseImpl *This = (StorageBaseImpl *)iface;
668   IEnumSTATSTGImpl* newEnum;
669
670   TRACE("(%p, %d, %p, %d, %p)\n",
671         iface, reserved1, reserved2, reserved3, ppenum);
672
673   if ( (This==0) || (ppenum==0))
674     return E_INVALIDARG;
675
676   if (This->reverted)
677     return STG_E_REVERTED;
678
679   newEnum = IEnumSTATSTGImpl_Construct(
680               This,
681               This->storageDirEntry);
682
683   if (newEnum!=0)
684   {
685     *ppenum = (IEnumSTATSTG*)newEnum;
686
687     IEnumSTATSTG_AddRef(*ppenum);
688
689     return S_OK;
690   }
691
692   return E_OUTOFMEMORY;
693 }
694
695 /************************************************************************
696  * Storage32BaseImpl_Stat (IStorage)
697  *
698  * This method will retrieve information about this storage object.
699  *
700  * See Windows documentation for more details on IStorage methods.
701  */
702 static HRESULT WINAPI StorageBaseImpl_Stat(
703   IStorage*        iface,
704   STATSTG*         pstatstg,     /* [out] */
705   DWORD            grfStatFlag)  /* [in] */
706 {
707   StorageBaseImpl *This = (StorageBaseImpl *)iface;
708   DirEntry       currentEntry;
709   HRESULT        res = STG_E_UNKNOWN;
710
711   TRACE("(%p, %p, %x)\n",
712         iface, pstatstg, grfStatFlag);
713
714   if ( (This==0) || (pstatstg==0))
715   {
716     res = E_INVALIDARG;
717     goto end;
718   }
719
720   if (This->reverted)
721   {
722     res = STG_E_REVERTED;
723     goto end;
724   }
725
726   res = StorageBaseImpl_ReadDirEntry(
727                     This,
728                     This->storageDirEntry,
729                     &currentEntry);
730
731   if (SUCCEEDED(res))
732   {
733     StorageUtl_CopyDirEntryToSTATSTG(
734       This,
735       pstatstg,
736       &currentEntry,
737       grfStatFlag);
738
739     pstatstg->grfMode = This->openFlags;
740     pstatstg->grfStateBits = This->stateBits;
741   }
742
743 end:
744   if (res == S_OK)
745   {
746     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);
747   }
748   TRACE("<-- %08x\n", res);
749   return res;
750 }
751
752 /************************************************************************
753  * Storage32BaseImpl_RenameElement (IStorage)
754  *
755  * This method will rename the specified element.
756  *
757  * See Windows documentation for more details on IStorage methods.
758  */
759 static HRESULT WINAPI StorageBaseImpl_RenameElement(
760             IStorage*        iface,
761             const OLECHAR*   pwcsOldName,  /* [in] */
762             const OLECHAR*   pwcsNewName)  /* [in] */
763 {
764   StorageBaseImpl *This = (StorageBaseImpl *)iface;
765   DirEntry          currentEntry;
766   DirRef            currentEntryRef;
767
768   TRACE("(%p, %s, %s)\n",
769         iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
770
771   if (This->reverted)
772     return STG_E_REVERTED;
773
774   currentEntryRef = findElement(This,
775                                    This->storageDirEntry,
776                                    pwcsNewName,
777                                    &currentEntry);
778
779   if (currentEntryRef != DIRENTRY_NULL)
780   {
781     /*
782      * There is already an element with the new name
783      */
784     return STG_E_FILEALREADYEXISTS;
785   }
786
787   /*
788    * Search for the old element name
789    */
790   currentEntryRef = findElement(This,
791                                    This->storageDirEntry,
792                                    pwcsOldName,
793                                    &currentEntry);
794
795   if (currentEntryRef != DIRENTRY_NULL)
796   {
797     if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
798         StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
799     {
800       WARN("Element is already open; cannot rename.\n");
801       return STG_E_ACCESSDENIED;
802     }
803
804     /* Remove the element from its current position in the tree */
805     removeFromTree(This, This->storageDirEntry,
806         currentEntryRef);
807
808     /* Change the name of the element */
809     strcpyW(currentEntry.name, pwcsNewName);
810
811     StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
812         &currentEntry);
813
814     /* Insert the element in a new position in the tree */
815     insertIntoTree(This, This->storageDirEntry,
816         currentEntryRef);
817   }
818   else
819   {
820     /*
821      * There is no element with the old name
822      */
823     return STG_E_FILENOTFOUND;
824   }
825
826   return S_OK;
827 }
828
829 /************************************************************************
830  * Storage32BaseImpl_CreateStream (IStorage)
831  *
832  * This method will create a stream object within this storage
833  *
834  * See Windows documentation for more details on IStorage methods.
835  */
836 static HRESULT WINAPI StorageBaseImpl_CreateStream(
837             IStorage*        iface,
838             const OLECHAR*   pwcsName,  /* [string][in] */
839             DWORD            grfMode,   /* [in] */
840             DWORD            reserved1, /* [in] */
841             DWORD            reserved2, /* [in] */
842             IStream**        ppstm)     /* [out] */
843 {
844   StorageBaseImpl *This = (StorageBaseImpl *)iface;
845   StgStreamImpl*    newStream;
846   DirEntry          currentEntry, newStreamEntry;
847   DirRef            currentEntryRef, newStreamEntryRef;
848
849   TRACE("(%p, %s, %x, %d, %d, %p)\n",
850         iface, debugstr_w(pwcsName), grfMode,
851         reserved1, reserved2, ppstm);
852
853   if (ppstm == 0)
854     return STG_E_INVALIDPOINTER;
855
856   if (pwcsName == 0)
857     return STG_E_INVALIDNAME;
858
859   if (reserved1 || reserved2)
860     return STG_E_INVALIDPARAMETER;
861
862   if ( FAILED( validateSTGM(grfMode) ))
863     return STG_E_INVALIDFLAG;
864
865   if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
866     return STG_E_INVALIDFLAG;
867
868   if (This->reverted)
869     return STG_E_REVERTED;
870
871   /*
872    * As documented.
873    */
874   if ((grfMode & STGM_DELETEONRELEASE) ||
875       (grfMode & STGM_TRANSACTED))
876     return STG_E_INVALIDFUNCTION;
877
878   /* Can't create a stream on read-only storage */
879   if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
880     return STG_E_ACCESSDENIED;
881
882   /*
883    * Check that we're compatible with the parent's storage mode
884    * if not in transacted mode
885    */
886   if(!(This->openFlags & STGM_TRANSACTED)) {
887     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
888       return STG_E_ACCESSDENIED;
889   }
890
891   if(This->openFlags & STGM_SIMPLE)
892     if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
893
894   *ppstm = 0;
895
896   currentEntryRef = findElement(This,
897                                    This->storageDirEntry,
898                                    pwcsName,
899                                    &currentEntry);
900
901   if (currentEntryRef != DIRENTRY_NULL)
902   {
903     /*
904      * An element with this name already exists
905      */
906     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
907     {
908       IStorage_DestroyElement(iface, pwcsName);
909     }
910     else
911       return STG_E_FILEALREADYEXISTS;
912   }
913   else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
914   {
915     WARN("read-only storage\n");
916     return STG_E_ACCESSDENIED;
917   }
918
919   /*
920    * memset the empty entry
921    */
922   memset(&newStreamEntry, 0, sizeof(DirEntry));
923
924   newStreamEntry.sizeOfNameString =
925       ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
926
927   if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
928     return STG_E_INVALIDNAME;
929
930   strcpyW(newStreamEntry.name, pwcsName);
931
932   newStreamEntry.stgType       = STGTY_STREAM;
933   newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
934   newStreamEntry.size.u.LowPart  = 0;
935   newStreamEntry.size.u.HighPart = 0;
936
937   newStreamEntry.leftChild        = DIRENTRY_NULL;
938   newStreamEntry.rightChild       = DIRENTRY_NULL;
939   newStreamEntry.dirRootEntry     = DIRENTRY_NULL;
940
941   /* call CoFileTime to get the current time
942   newStreamEntry.ctime
943   newStreamEntry.mtime
944   */
945
946   /*  newStreamEntry.clsid */
947
948   /*
949    * Create an entry with the new data
950    */
951   StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
952   /*
953    * Insert the new entry in the parent storage's tree.
954    */
955   insertIntoTree(
956     This,
957     This->storageDirEntry,
958     newStreamEntryRef);
959
960   /*
961    * Open the stream to return it.
962    */
963   newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
964
965   if (newStream != 0)
966   {
967     *ppstm = (IStream*)newStream;
968
969     IStream_AddRef(*ppstm);
970   }
971   else
972   {
973     return STG_E_INSUFFICIENTMEMORY;
974   }
975
976   return S_OK;
977 }
978
979 /************************************************************************
980  * Storage32BaseImpl_SetClass (IStorage)
981  *
982  * This method will write the specified CLSID in the directory entry of this
983  * storage.
984  *
985  * See Windows documentation for more details on IStorage methods.
986  */
987 static HRESULT WINAPI StorageBaseImpl_SetClass(
988   IStorage*        iface,
989   REFCLSID         clsid) /* [in] */
990 {
991   StorageBaseImpl *This = (StorageBaseImpl *)iface;
992   HRESULT hRes;
993   DirEntry currentEntry;
994
995   TRACE("(%p, %p)\n", iface, clsid);
996
997   if (This->reverted)
998     return STG_E_REVERTED;
999
1000   hRes = StorageBaseImpl_ReadDirEntry(This,
1001                                       This->storageDirEntry,
1002                                       &currentEntry);
1003   if (SUCCEEDED(hRes))
1004   {
1005     currentEntry.clsid = *clsid;
1006
1007     hRes = StorageBaseImpl_WriteDirEntry(This,
1008                                          This->storageDirEntry,
1009                                          &currentEntry);
1010   }
1011
1012   return hRes;
1013 }
1014
1015 /************************************************************************
1016 ** Storage32Impl implementation
1017 */
1018
1019 /************************************************************************
1020  * Storage32BaseImpl_CreateStorage (IStorage)
1021  *
1022  * This method will create the storage object within the provided storage.
1023  *
1024  * See Windows documentation for more details on IStorage methods.
1025  */
1026 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1027   IStorage*      iface,
1028   const OLECHAR  *pwcsName, /* [string][in] */
1029   DWORD            grfMode,   /* [in] */
1030   DWORD            reserved1, /* [in] */
1031   DWORD            reserved2, /* [in] */
1032   IStorage       **ppstg)   /* [out] */
1033 {
1034   StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1035
1036   DirEntry         currentEntry;
1037   DirEntry         newEntry;
1038   DirRef           currentEntryRef;
1039   DirRef           newEntryRef;
1040   HRESULT          hr;
1041
1042   TRACE("(%p, %s, %x, %d, %d, %p)\n",
1043         iface, debugstr_w(pwcsName), grfMode,
1044         reserved1, reserved2, ppstg);
1045
1046   if (ppstg == 0)
1047     return STG_E_INVALIDPOINTER;
1048
1049   if (This->openFlags & STGM_SIMPLE)
1050   {
1051     return STG_E_INVALIDFUNCTION;
1052   }
1053
1054   if (pwcsName == 0)
1055     return STG_E_INVALIDNAME;
1056
1057   *ppstg = NULL;
1058
1059   if ( FAILED( validateSTGM(grfMode) ) ||
1060        (grfMode & STGM_DELETEONRELEASE) )
1061   {
1062     WARN("bad grfMode: 0x%x\n", grfMode);
1063     return STG_E_INVALIDFLAG;
1064   }
1065
1066   if (This->reverted)
1067     return STG_E_REVERTED;
1068
1069   /*
1070    * Check that we're compatible with the parent's storage mode
1071    */
1072   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1073   {
1074     WARN("access denied\n");
1075     return STG_E_ACCESSDENIED;
1076   }
1077
1078   currentEntryRef = findElement(This,
1079                                    This->storageDirEntry,
1080                                    pwcsName,
1081                                    &currentEntry);
1082
1083   if (currentEntryRef != DIRENTRY_NULL)
1084   {
1085     /*
1086      * An element with this name already exists
1087      */
1088     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1089         STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1090     {
1091       hr = IStorage_DestroyElement(iface, pwcsName);
1092       if (FAILED(hr))
1093         return hr;
1094     }
1095     else
1096     {
1097       WARN("file already exists\n");
1098       return STG_E_FILEALREADYEXISTS;
1099     }
1100   }
1101   else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1102   {
1103     WARN("read-only storage\n");
1104     return STG_E_ACCESSDENIED;
1105   }
1106
1107   memset(&newEntry, 0, sizeof(DirEntry));
1108
1109   newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1110
1111   if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1112   {
1113     FIXME("name too long\n");
1114     return STG_E_INVALIDNAME;
1115   }
1116
1117   strcpyW(newEntry.name, pwcsName);
1118
1119   newEntry.stgType       = STGTY_STORAGE;
1120   newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1121   newEntry.size.u.LowPart  = 0;
1122   newEntry.size.u.HighPart = 0;
1123
1124   newEntry.leftChild        = DIRENTRY_NULL;
1125   newEntry.rightChild       = DIRENTRY_NULL;
1126   newEntry.dirRootEntry     = DIRENTRY_NULL;
1127
1128   /* call CoFileTime to get the current time
1129   newEntry.ctime
1130   newEntry.mtime
1131   */
1132
1133   /*  newEntry.clsid */
1134
1135   /*
1136    * Create a new directory entry for the storage
1137    */
1138   StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1139
1140   /*
1141    * Insert the new directory entry into the parent storage's tree
1142    */
1143   insertIntoTree(
1144     This,
1145     This->storageDirEntry,
1146     newEntryRef);
1147
1148   /*
1149    * Open it to get a pointer to return.
1150    */
1151   hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1152
1153   if( (hr != S_OK) || (*ppstg == NULL))
1154   {
1155     return hr;
1156   }
1157
1158
1159   return S_OK;
1160 }
1161
1162
1163 /***************************************************************************
1164  *
1165  * Internal Method
1166  *
1167  * Reserve a directory entry in the file and initialize it.
1168  */
1169 static HRESULT StorageImpl_CreateDirEntry(
1170   StorageBaseImpl *base,
1171   const DirEntry *newData,
1172   DirRef *index)
1173 {
1174   StorageImpl *storage = (StorageImpl*)base;
1175   ULONG       currentEntryIndex    = 0;
1176   ULONG       newEntryIndex        = DIRENTRY_NULL;
1177   HRESULT hr = S_OK;
1178   BYTE currentData[RAW_DIRENTRY_SIZE];
1179   WORD sizeOfNameString;
1180
1181   do
1182   {
1183     hr = StorageImpl_ReadRawDirEntry(storage,
1184                                      currentEntryIndex,
1185                                      currentData);
1186
1187     if (SUCCEEDED(hr))
1188     {
1189       StorageUtl_ReadWord(
1190         currentData,
1191         OFFSET_PS_NAMELENGTH,
1192         &sizeOfNameString);
1193
1194       if (sizeOfNameString == 0)
1195       {
1196         /*
1197          * The entry exists and is available, we found it.
1198          */
1199         newEntryIndex = currentEntryIndex;
1200       }
1201     }
1202     else
1203     {
1204       /*
1205        * We exhausted the directory entries, we will create more space below
1206        */
1207       newEntryIndex = currentEntryIndex;
1208     }
1209     currentEntryIndex++;
1210
1211   } while (newEntryIndex == DIRENTRY_NULL);
1212
1213   /*
1214    * grow the directory stream
1215    */
1216   if (FAILED(hr))
1217   {
1218     BYTE           emptyData[RAW_DIRENTRY_SIZE];
1219     ULARGE_INTEGER newSize;
1220     ULONG          entryIndex;
1221     ULONG          lastEntry     = 0;
1222     ULONG          blockCount    = 0;
1223
1224     /*
1225      * obtain the new count of blocks in the directory stream
1226      */
1227     blockCount = BlockChainStream_GetCount(
1228                    storage->rootBlockChain)+1;
1229
1230     /*
1231      * initialize the size used by the directory stream
1232      */
1233     newSize.u.HighPart = 0;
1234     newSize.u.LowPart  = storage->bigBlockSize * blockCount;
1235
1236     /*
1237      * add a block to the directory stream
1238      */
1239     BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1240
1241     /*
1242      * memset the empty entry in order to initialize the unused newly
1243      * created entries
1244      */
1245     memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1246
1247     /*
1248      * initialize them
1249      */
1250     lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1251
1252     for(
1253       entryIndex = newEntryIndex + 1;
1254       entryIndex < lastEntry;
1255       entryIndex++)
1256     {
1257       StorageImpl_WriteRawDirEntry(
1258         storage,
1259         entryIndex,
1260         emptyData);
1261     }
1262   }
1263
1264   UpdateRawDirEntry(currentData, newData);
1265
1266   hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1267
1268   if (SUCCEEDED(hr))
1269     *index = newEntryIndex;
1270
1271   return hr;
1272 }
1273
1274 /***************************************************************************
1275  *
1276  * Internal Method
1277  *
1278  * Mark a directory entry in the file as free.
1279  */
1280 static HRESULT StorageImpl_DestroyDirEntry(
1281   StorageBaseImpl *base,
1282   DirRef index)
1283 {
1284   HRESULT hr;
1285   BYTE emptyData[RAW_DIRENTRY_SIZE];
1286   StorageImpl *storage = (StorageImpl*)base;
1287
1288   memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1289
1290   hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1291
1292   return hr;
1293 }
1294
1295
1296 /***************************************************************************
1297  *
1298  * Internal Method
1299  *
1300  * Destroy an entry, its attached data, and all entries reachable from it.
1301  */
1302 static HRESULT DestroyReachableEntries(
1303   StorageBaseImpl *base,
1304   DirRef index)
1305 {
1306   HRESULT hr = S_OK;
1307   DirEntry data;
1308   ULARGE_INTEGER zero;
1309
1310   zero.QuadPart = 0;
1311
1312   if (index != DIRENTRY_NULL)
1313   {
1314     hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1315
1316     if (SUCCEEDED(hr))
1317       hr = DestroyReachableEntries(base, data.dirRootEntry);
1318
1319     if (SUCCEEDED(hr))
1320       hr = DestroyReachableEntries(base, data.leftChild);
1321
1322     if (SUCCEEDED(hr))
1323       hr = DestroyReachableEntries(base, data.rightChild);
1324
1325     if (SUCCEEDED(hr))
1326       hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1327
1328     if (SUCCEEDED(hr))
1329       hr = StorageBaseImpl_DestroyDirEntry(base, index);
1330   }
1331
1332   return hr;
1333 }
1334
1335
1336 /****************************************************************************
1337  *
1338  * Internal Method
1339  *
1340  * Case insensitive comparison of DirEntry.name by first considering
1341  * their size.
1342  *
1343  * Returns <0 when name1 < name2
1344  *         >0 when name1 > name2
1345  *          0 when name1 == name2
1346  */
1347 static LONG entryNameCmp(
1348     const OLECHAR *name1,
1349     const OLECHAR *name2)
1350 {
1351   LONG diff      = lstrlenW(name1) - lstrlenW(name2);
1352
1353   while (diff == 0 && *name1 != 0)
1354   {
1355     /*
1356      * We compare the string themselves only when they are of the same length
1357      */
1358     diff = toupperW(*name1++) - toupperW(*name2++);
1359   }
1360
1361   return diff;
1362 }
1363
1364 /****************************************************************************
1365  *
1366  * Internal Method
1367  *
1368  * Add a directory entry to a storage
1369  */
1370 static HRESULT insertIntoTree(
1371   StorageBaseImpl *This,
1372   DirRef        parentStorageIndex,
1373   DirRef        newEntryIndex)
1374 {
1375   DirEntry currentEntry;
1376   DirEntry newEntry;
1377
1378   /*
1379    * Read the inserted entry
1380    */
1381   StorageBaseImpl_ReadDirEntry(This,
1382                                newEntryIndex,
1383                                &newEntry);
1384
1385   /*
1386    * Read the storage entry
1387    */
1388   StorageBaseImpl_ReadDirEntry(This,
1389                                parentStorageIndex,
1390                                &currentEntry);
1391
1392   if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1393   {
1394     /*
1395      * The root storage contains some element, therefore, start the research
1396      * for the appropriate location.
1397      */
1398     BOOL found = 0;
1399     DirRef current, next, previous, currentEntryId;
1400
1401     /*
1402      * Keep a reference to the root of the storage's element tree
1403      */
1404     currentEntryId = currentEntry.dirRootEntry;
1405
1406     /*
1407      * Read
1408      */
1409     StorageBaseImpl_ReadDirEntry(This,
1410                                  currentEntry.dirRootEntry,
1411                                  &currentEntry);
1412
1413     previous = currentEntry.leftChild;
1414     next     = currentEntry.rightChild;
1415     current  = currentEntryId;
1416
1417     while (found == 0)
1418     {
1419       LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1420
1421       if (diff < 0)
1422       {
1423         if (previous != DIRENTRY_NULL)
1424         {
1425           StorageBaseImpl_ReadDirEntry(This,
1426                                        previous,
1427                                        &currentEntry);
1428           current = previous;
1429         }
1430         else
1431         {
1432           currentEntry.leftChild = newEntryIndex;
1433           StorageBaseImpl_WriteDirEntry(This,
1434                                         current,
1435                                         &currentEntry);
1436           found = 1;
1437         }
1438       }
1439       else if (diff > 0)
1440       {
1441         if (next != DIRENTRY_NULL)
1442         {
1443           StorageBaseImpl_ReadDirEntry(This,
1444                                        next,
1445                                        &currentEntry);
1446           current = next;
1447         }
1448         else
1449         {
1450           currentEntry.rightChild = newEntryIndex;
1451           StorageBaseImpl_WriteDirEntry(This,
1452                                         current,
1453                                         &currentEntry);
1454           found = 1;
1455         }
1456       }
1457       else
1458       {
1459         /*
1460          * Trying to insert an item with the same name in the
1461          * subtree structure.
1462          */
1463         return STG_E_FILEALREADYEXISTS;
1464       }
1465
1466       previous = currentEntry.leftChild;
1467       next     = currentEntry.rightChild;
1468     }
1469   }
1470   else
1471   {
1472     /*
1473      * The storage is empty, make the new entry the root of its element tree
1474      */
1475     currentEntry.dirRootEntry = newEntryIndex;
1476     StorageBaseImpl_WriteDirEntry(This,
1477                                   parentStorageIndex,
1478                                   &currentEntry);
1479   }
1480
1481   return S_OK;
1482 }
1483
1484 /****************************************************************************
1485  *
1486  * Internal Method
1487  *
1488  * Find and read the element of a storage with the given name.
1489  */
1490 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1491     const OLECHAR *name, DirEntry *data)
1492 {
1493   DirRef currentEntry;
1494
1495   /* Read the storage entry to find the root of the tree. */
1496   StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1497
1498   currentEntry = data->dirRootEntry;
1499
1500   while (currentEntry != DIRENTRY_NULL)
1501   {
1502     LONG cmp;
1503
1504     StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1505
1506     cmp = entryNameCmp(name, data->name);
1507
1508     if (cmp == 0)
1509       /* found it */
1510       break;
1511
1512     else if (cmp < 0)
1513       currentEntry = data->leftChild;
1514
1515     else if (cmp > 0)
1516       currentEntry = data->rightChild;
1517   }
1518
1519   return currentEntry;
1520 }
1521
1522 /****************************************************************************
1523  *
1524  * Internal Method
1525  *
1526  * Find and read the binary tree parent of the element with the given name.
1527  *
1528  * If there is no such element, find a place where it could be inserted and
1529  * return STG_E_FILENOTFOUND.
1530  */
1531 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1532     const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1533     ULONG *relation)
1534 {
1535   DirRef childEntry;
1536   DirEntry childData;
1537
1538   /* Read the storage entry to find the root of the tree. */
1539   StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1540
1541   *parentEntry = storageEntry;
1542   *relation = DIRENTRY_RELATION_DIR;
1543
1544   childEntry = parentData->dirRootEntry;
1545
1546   while (childEntry != DIRENTRY_NULL)
1547   {
1548     LONG cmp;
1549
1550     StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1551
1552     cmp = entryNameCmp(childName, childData.name);
1553
1554     if (cmp == 0)
1555       /* found it */
1556       break;
1557
1558     else if (cmp < 0)
1559     {
1560       *parentData = childData;
1561       *parentEntry = childEntry;
1562       *relation = DIRENTRY_RELATION_PREVIOUS;
1563
1564       childEntry = parentData->leftChild;
1565     }
1566
1567     else if (cmp > 0)
1568     {
1569       *parentData = childData;
1570       *parentEntry = childEntry;
1571       *relation = DIRENTRY_RELATION_NEXT;
1572
1573       childEntry = parentData->rightChild;
1574     }
1575   }
1576
1577   if (childEntry == DIRENTRY_NULL)
1578     return STG_E_FILENOTFOUND;
1579   else
1580     return S_OK;
1581 }
1582
1583
1584 /*************************************************************************
1585  * CopyTo (IStorage)
1586  */
1587 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1588   IStorage*   iface,
1589   DWORD       ciidExclude,  /* [in] */
1590   const IID*  rgiidExclude, /* [size_is][unique][in] */
1591   SNB         snbExclude,   /* [unique][in] */
1592   IStorage*   pstgDest)     /* [unique][in] */
1593 {
1594   IEnumSTATSTG *elements     = 0;
1595   STATSTG      curElement, strStat;
1596   HRESULT      hr;
1597   IStorage     *pstgTmp, *pstgChild;
1598   IStream      *pstrTmp, *pstrChild;
1599   BOOL         skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1600   int          i;
1601
1602   TRACE("(%p, %d, %p, %p, %p)\n",
1603         iface, ciidExclude, rgiidExclude,
1604         snbExclude, pstgDest);
1605
1606   if ( pstgDest == 0 )
1607     return STG_E_INVALIDPOINTER;
1608
1609   /*
1610    * Enumerate the elements
1611    */
1612   hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1613
1614   if ( hr != S_OK )
1615     return hr;
1616
1617   /*
1618    * set the class ID
1619    */
1620   IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1621   IStorage_SetClass( pstgDest, &curElement.clsid );
1622
1623   for(i = 0; i < ciidExclude; ++i)
1624   {
1625     if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1626         skip_storage = TRUE;
1627     else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1628         skip_stream = TRUE;
1629     else
1630         WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1631   }
1632
1633   do
1634   {
1635     /*
1636      * Obtain the next element
1637      */
1638     hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1639
1640     if ( hr == S_FALSE )
1641     {
1642       hr = S_OK;   /* done, every element has been copied */
1643       break;
1644     }
1645
1646     if ( snbExclude )
1647     {
1648       WCHAR **snb = snbExclude;
1649       skip = FALSE;
1650       while ( *snb != NULL && !skip )
1651       {
1652         if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1653           skip = TRUE;
1654         ++snb;
1655       }
1656     }
1657
1658     if ( skip )
1659       goto cleanup;
1660
1661     if (curElement.type == STGTY_STORAGE)
1662     {
1663       if(skip_storage)
1664         goto cleanup;
1665
1666       /*
1667        * open child source storage
1668        */
1669       hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1670                                  STGM_READ|STGM_SHARE_EXCLUSIVE,
1671                                  NULL, 0, &pstgChild );
1672
1673       if (hr != S_OK)
1674         goto cleanup;
1675
1676       /*
1677        * create a new storage in destination storage
1678        */
1679       hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1680                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1681                                    0, 0,
1682                                    &pstgTmp );
1683       /*
1684        * if it already exist, don't create a new one use this one
1685        */
1686       if (hr == STG_E_FILEALREADYEXISTS)
1687       {
1688         hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1689                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1690                                    NULL, 0, &pstgTmp );
1691       }
1692
1693       if (hr == S_OK)
1694       {
1695         /*
1696          * do the copy recursively
1697          */
1698         hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1699                                  NULL, pstgTmp );
1700
1701         IStorage_Release( pstgTmp );
1702       }
1703
1704       IStorage_Release( pstgChild );
1705     }
1706     else if (curElement.type == STGTY_STREAM)
1707     {
1708       if(skip_stream)
1709         goto cleanup;
1710
1711       /*
1712        * create a new stream in destination storage. If the stream already
1713        * exist, it will be deleted and a new one will be created.
1714        */
1715       hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1716                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1717                                   0, 0, &pstrTmp );
1718
1719       if (hr != S_OK)
1720         goto cleanup;
1721
1722       /*
1723        * open child stream storage
1724        */
1725       hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1726                                 STGM_READ|STGM_SHARE_EXCLUSIVE,
1727                                 0, &pstrChild );
1728
1729       if (hr == S_OK)
1730       {
1731         /*
1732          * Get the size of the source stream
1733          */
1734         IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1735
1736         /*
1737          * Set the size of the destination stream.
1738          */
1739         IStream_SetSize(pstrTmp, strStat.cbSize);
1740
1741         /*
1742          * do the copy
1743          */
1744         hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1745                              NULL, NULL );
1746
1747         IStream_Release( pstrChild );
1748       }
1749
1750       IStream_Release( pstrTmp );
1751     }
1752     else
1753     {
1754       WARN("unknown element type: %d\n", curElement.type);
1755     }
1756
1757 cleanup:
1758     CoTaskMemFree(curElement.pwcsName);
1759   } while (hr == S_OK);
1760
1761   /*
1762    * Clean-up
1763    */
1764   IEnumSTATSTG_Release(elements);
1765
1766   return hr;
1767 }
1768
1769 /*************************************************************************
1770  * MoveElementTo (IStorage)
1771  */
1772 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1773   IStorage*     iface,
1774   const OLECHAR *pwcsName,   /* [string][in] */
1775   IStorage      *pstgDest,   /* [unique][in] */
1776   const OLECHAR *pwcsNewName,/* [string][in] */
1777   DWORD           grfFlags)    /* [in] */
1778 {
1779   FIXME("(%p %s %p %s %u): stub\n", iface,
1780          debugstr_w(pwcsName), pstgDest,
1781          debugstr_w(pwcsNewName), grfFlags);
1782   return E_NOTIMPL;
1783 }
1784
1785 /*************************************************************************
1786  * Commit (IStorage)
1787  *
1788  * Ensures that any changes made to a storage object open in transacted mode
1789  * are reflected in the parent storage
1790  *
1791  * NOTES
1792  *  Wine doesn't implement transacted mode, which seems to be a basic
1793  *  optimization, so we can ignore this stub for now.
1794  */
1795 static HRESULT WINAPI StorageImpl_Commit(
1796   IStorage*   iface,
1797   DWORD         grfCommitFlags)/* [in] */
1798 {
1799   FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1800   return S_OK;
1801 }
1802
1803 /*************************************************************************
1804  * Revert (IStorage)
1805  *
1806  * Discard all changes that have been made since the last commit operation
1807  */
1808 static HRESULT WINAPI StorageImpl_Revert(
1809   IStorage* iface)
1810 {
1811   FIXME("(%p): stub\n", iface);
1812   return E_NOTIMPL;
1813 }
1814
1815 /*************************************************************************
1816  * DestroyElement (IStorage)
1817  *
1818  * Strategy: This implementation is built this way for simplicity not for speed.
1819  *          I always delete the topmost element of the enumeration and adjust
1820  *          the deleted element pointer all the time.  This takes longer to
1821  *          do but allow to reinvoke DestroyElement whenever we encounter a
1822  *          storage object.  The optimisation resides in the usage of another
1823  *          enumeration strategy that would give all the leaves of a storage
1824  *          first. (postfix order)
1825  */
1826 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1827   IStorage*     iface,
1828   const OLECHAR *pwcsName)/* [string][in] */
1829 {
1830   StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1831
1832   HRESULT           hr = S_OK;
1833   DirEntry          entryToDelete;
1834   DirRef            entryToDeleteRef;
1835
1836   TRACE("(%p, %s)\n",
1837         iface, debugstr_w(pwcsName));
1838
1839   if (pwcsName==NULL)
1840     return STG_E_INVALIDPOINTER;
1841
1842   if (This->reverted)
1843     return STG_E_REVERTED;
1844
1845   if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1846     return STG_E_ACCESSDENIED;
1847
1848   entryToDeleteRef = findElement(
1849     This,
1850     This->storageDirEntry,
1851     pwcsName,
1852     &entryToDelete);
1853
1854   if ( entryToDeleteRef == DIRENTRY_NULL )
1855   {
1856     return STG_E_FILENOTFOUND;
1857   }
1858
1859   if ( entryToDelete.stgType == STGTY_STORAGE )
1860   {
1861     hr = deleteStorageContents(
1862            This,
1863            entryToDeleteRef,
1864            entryToDelete);
1865   }
1866   else if ( entryToDelete.stgType == STGTY_STREAM )
1867   {
1868     hr = deleteStreamContents(
1869            This,
1870            entryToDeleteRef,
1871            entryToDelete);
1872   }
1873
1874   if (hr!=S_OK)
1875     return hr;
1876
1877   /*
1878    * Remove the entry from its parent storage
1879    */
1880   hr = removeFromTree(
1881         This,
1882         This->storageDirEntry,
1883         entryToDeleteRef);
1884
1885   /*
1886    * Invalidate the entry
1887    */
1888   if (SUCCEEDED(hr))
1889     StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1890
1891   return hr;
1892 }
1893
1894
1895 /******************************************************************************
1896  * Internal stream list handlers
1897  */
1898
1899 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 {
1901   TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1902   list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1903 }
1904
1905 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 {
1907   TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1908   list_remove(&(strm->StrmListEntry));
1909 }
1910
1911 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1912 {
1913   StgStreamImpl *strm;
1914
1915   LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1916   {
1917     if (strm->dirEntry == streamEntry)
1918     {
1919       return TRUE;
1920     }
1921   }
1922
1923   return FALSE;
1924 }
1925
1926 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1927 {
1928   StorageInternalImpl *childstg;
1929
1930   LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1931   {
1932     if (childstg->base.storageDirEntry == storageEntry)
1933     {
1934       return TRUE;
1935     }
1936   }
1937
1938   return FALSE;
1939 }
1940
1941 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1942 {
1943   struct list *cur, *cur2;
1944   StgStreamImpl *strm=NULL;
1945   StorageInternalImpl *childstg=NULL;
1946
1947   LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1948     strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1949     TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1950     strm->parentStorage = NULL;
1951     list_remove(cur);
1952   }
1953
1954   LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1955     childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1956     StorageBaseImpl_Invalidate( &childstg->base );
1957   }
1958
1959   if (stg->transactedChild)
1960   {
1961     StorageBaseImpl_Invalidate(stg->transactedChild);
1962
1963     stg->transactedChild = NULL;
1964   }
1965 }
1966
1967
1968 /*********************************************************************
1969  *
1970  * Internal Method
1971  *
1972  * Delete the contents of a storage entry.
1973  *
1974  */
1975 static HRESULT deleteStorageContents(
1976   StorageBaseImpl *parentStorage,
1977   DirRef       indexToDelete,
1978   DirEntry     entryDataToDelete)
1979 {
1980   IEnumSTATSTG *elements     = 0;
1981   IStorage   *childStorage = 0;
1982   STATSTG      currentElement;
1983   HRESULT      hr;
1984   HRESULT      destroyHr = S_OK;
1985   StorageInternalImpl *stg, *stg2;
1986
1987   /* Invalidate any open storage objects. */
1988   LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1989   {
1990     if (stg->base.storageDirEntry == indexToDelete)
1991     {
1992       StorageBaseImpl_Invalidate(&stg->base);
1993     }
1994   }
1995
1996   /*
1997    * Open the storage and enumerate it
1998    */
1999   hr = StorageBaseImpl_OpenStorage(
2000         (IStorage*)parentStorage,
2001         entryDataToDelete.name,
2002         0,
2003         STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2004         0,
2005         0,
2006         &childStorage);
2007
2008   if (hr != S_OK)
2009   {
2010     return hr;
2011   }
2012
2013   /*
2014    * Enumerate the elements
2015    */
2016   IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2017
2018   do
2019   {
2020     /*
2021      * Obtain the next element
2022      */
2023     hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2024     if (hr==S_OK)
2025     {
2026       destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2027
2028       CoTaskMemFree(currentElement.pwcsName);
2029     }
2030
2031     /*
2032      * We need to Reset the enumeration every time because we delete elements
2033      * and the enumeration could be invalid
2034      */
2035     IEnumSTATSTG_Reset(elements);
2036
2037   } while ((hr == S_OK) && (destroyHr == S_OK));
2038
2039   IStorage_Release(childStorage);
2040   IEnumSTATSTG_Release(elements);
2041
2042   return destroyHr;
2043 }
2044
2045 /*********************************************************************
2046  *
2047  * Internal Method
2048  *
2049  * Perform the deletion of a stream's data
2050  *
2051  */
2052 static HRESULT deleteStreamContents(
2053   StorageBaseImpl *parentStorage,
2054   DirRef        indexToDelete,
2055   DirEntry      entryDataToDelete)
2056 {
2057   IStream      *pis;
2058   HRESULT        hr;
2059   ULARGE_INTEGER size;
2060   StgStreamImpl *strm, *strm2;
2061
2062   /* Invalidate any open stream objects. */
2063   LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2064   {
2065     if (strm->dirEntry == indexToDelete)
2066     {
2067       TRACE("Stream deleted %p\n", strm);
2068       strm->parentStorage = NULL;
2069       list_remove(&strm->StrmListEntry);
2070     }
2071   }
2072
2073   size.u.HighPart = 0;
2074   size.u.LowPart = 0;
2075
2076   hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2077         entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2078
2079   if (hr!=S_OK)
2080   {
2081     return(hr);
2082   }
2083
2084   /*
2085    * Zap the stream
2086    */
2087   hr = IStream_SetSize(pis, size);
2088
2089   if(hr != S_OK)
2090   {
2091     return hr;
2092   }
2093
2094   /*
2095    * Release the stream object.
2096    */
2097   IStream_Release(pis);
2098
2099   return S_OK;
2100 }
2101
2102 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2103 {
2104   switch (relation)
2105   {
2106     case DIRENTRY_RELATION_PREVIOUS:
2107       entry->leftChild = new_target;
2108       break;
2109     case DIRENTRY_RELATION_NEXT:
2110       entry->rightChild = new_target;
2111       break;
2112     case DIRENTRY_RELATION_DIR:
2113       entry->dirRootEntry = new_target;
2114       break;
2115     default:
2116       assert(0);
2117   }
2118 }
2119
2120 /*************************************************************************
2121  *
2122  * Internal Method
2123  *
2124  * This method removes a directory entry from its parent storage tree without
2125  * freeing any resources attached to it.
2126  */
2127 static HRESULT removeFromTree(
2128   StorageBaseImpl *This,
2129   DirRef        parentStorageIndex,
2130   DirRef        deletedIndex)
2131 {
2132   HRESULT hr                     = S_OK;
2133   DirEntry   entryToDelete;
2134   DirEntry   parentEntry;
2135   DirRef parentEntryRef;
2136   ULONG typeOfRelation;
2137
2138   hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2139
2140   if (hr != S_OK)
2141     return hr;
2142
2143   /*
2144    * Find the element that links to the one we want to delete.
2145    */
2146   hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2147     &parentEntry, &parentEntryRef, &typeOfRelation);
2148
2149   if (hr != S_OK)
2150     return hr;
2151
2152   if (entryToDelete.leftChild != DIRENTRY_NULL)
2153   {
2154     /*
2155      * Replace the deleted entry with its left child
2156      */
2157     setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2158
2159     hr = StorageBaseImpl_WriteDirEntry(
2160             This,
2161             parentEntryRef,
2162             &parentEntry);
2163     if(FAILED(hr))
2164     {
2165       return hr;
2166     }
2167
2168     if (entryToDelete.rightChild != DIRENTRY_NULL)
2169     {
2170       /*
2171        * We need to reinsert the right child somewhere. We already know it and
2172        * its children are greater than everything in the left tree, so we
2173        * insert it at the rightmost point in the left tree.
2174        */
2175       DirRef newRightChildParent = entryToDelete.leftChild;
2176       DirEntry newRightChildParentEntry;
2177
2178       do
2179       {
2180         hr = StorageBaseImpl_ReadDirEntry(
2181                 This,
2182                 newRightChildParent,
2183                 &newRightChildParentEntry);
2184         if (FAILED(hr))
2185         {
2186           return hr;
2187         }
2188
2189         if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2190           newRightChildParent = newRightChildParentEntry.rightChild;
2191       } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2192
2193       newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2194
2195       hr = StorageBaseImpl_WriteDirEntry(
2196               This,
2197               newRightChildParent,
2198               &newRightChildParentEntry);
2199       if (FAILED(hr))
2200       {
2201         return hr;
2202       }
2203     }
2204   }
2205   else
2206   {
2207     /*
2208      * Replace the deleted entry with its right child
2209      */
2210     setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2211
2212     hr = StorageBaseImpl_WriteDirEntry(
2213             This,
2214             parentEntryRef,
2215             &parentEntry);
2216     if(FAILED(hr))
2217     {
2218       return hr;
2219     }
2220   }
2221
2222   return hr;
2223 }
2224
2225
2226 /******************************************************************************
2227  * SetElementTimes (IStorage)
2228  */
2229 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2230   IStorage*     iface,
2231   const OLECHAR *pwcsName,/* [string][in] */
2232   const FILETIME  *pctime,  /* [in] */
2233   const FILETIME  *patime,  /* [in] */
2234   const FILETIME  *pmtime)  /* [in] */
2235 {
2236   FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2237   return S_OK;
2238 }
2239
2240 /******************************************************************************
2241  * SetStateBits (IStorage)
2242  */
2243 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2244   IStorage*   iface,
2245   DWORD         grfStateBits,/* [in] */
2246   DWORD         grfMask)     /* [in] */
2247 {
2248   StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2249
2250   if (This->reverted)
2251     return STG_E_REVERTED;
2252
2253   This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2254   return S_OK;
2255 }
2256
2257 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2258   DirRef index, const DirEntry *data)
2259 {
2260   StorageImpl *This = (StorageImpl*)base;
2261   return StorageImpl_WriteDirEntry(This, index, data);
2262 }
2263
2264 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2265   DirRef index, DirEntry *data)
2266 {
2267   StorageImpl *This = (StorageImpl*)base;
2268   return StorageImpl_ReadDirEntry(This, index, data);
2269 }
2270
2271 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2272 {
2273   int i;
2274
2275   for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2276   {
2277     if (!This->blockChainCache[i])
2278     {
2279       return &This->blockChainCache[i];
2280     }
2281   }
2282
2283   i = This->blockChainToEvict;
2284
2285   BlockChainStream_Destroy(This->blockChainCache[i]);
2286   This->blockChainCache[i] = NULL;
2287
2288   This->blockChainToEvict++;
2289   if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2290     This->blockChainToEvict = 0;
2291
2292   return &This->blockChainCache[i];
2293 }
2294
2295 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2296     DirRef index)
2297 {
2298   int i, free_index=-1;
2299
2300   for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2301   {
2302     if (!This->blockChainCache[i])
2303     {
2304       if (free_index == -1) free_index = i;
2305     }
2306     else if (This->blockChainCache[i]->ownerDirEntry == index)
2307     {
2308       return &This->blockChainCache[i];
2309     }
2310   }
2311
2312   if (free_index == -1)
2313   {
2314     free_index = This->blockChainToEvict;
2315
2316     BlockChainStream_Destroy(This->blockChainCache[free_index]);
2317     This->blockChainCache[free_index] = NULL;
2318
2319     This->blockChainToEvict++;
2320     if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2321       This->blockChainToEvict = 0;
2322   }
2323
2324   This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2325   return &This->blockChainCache[free_index];
2326 }
2327
2328 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2329   ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2330 {
2331   StorageImpl *This = (StorageImpl*)base;
2332   DirEntry data;
2333   HRESULT hr;
2334   ULONG bytesToRead;
2335
2336   hr = StorageImpl_ReadDirEntry(This, index, &data);
2337   if (FAILED(hr)) return hr;
2338
2339   if (data.size.QuadPart == 0)
2340   {
2341     *bytesRead = 0;
2342     return S_OK;
2343   }
2344
2345   if (offset.QuadPart + size > data.size.QuadPart)
2346   {
2347     bytesToRead = data.size.QuadPart - offset.QuadPart;
2348   }
2349   else
2350   {
2351     bytesToRead = size;
2352   }
2353
2354   if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2355   {
2356     SmallBlockChainStream *stream;
2357
2358     stream = SmallBlockChainStream_Construct(This, NULL, index);
2359     if (!stream) return E_OUTOFMEMORY;
2360
2361     hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2362
2363     SmallBlockChainStream_Destroy(stream);
2364
2365     return hr;
2366   }
2367   else
2368   {
2369     BlockChainStream *stream = NULL;
2370
2371     stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2372     if (!stream) return E_OUTOFMEMORY;
2373
2374     hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2375
2376     return hr;
2377   }
2378 }
2379
2380 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2381   ULARGE_INTEGER newsize)
2382 {
2383   StorageImpl *This = (StorageImpl*)base;
2384   DirEntry data;
2385   HRESULT hr;
2386   SmallBlockChainStream *smallblock=NULL;
2387   BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2388
2389   hr = StorageImpl_ReadDirEntry(This, index, &data);
2390   if (FAILED(hr)) return hr;
2391
2392   /* In simple mode keep the stream size above the small block limit */
2393   if (This->base.openFlags & STGM_SIMPLE)
2394     newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2395
2396   if (data.size.QuadPart == newsize.QuadPart)
2397     return S_OK;
2398
2399   /* Create a block chain object of the appropriate type */
2400   if (data.size.QuadPart == 0)
2401   {
2402     if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2403     {
2404       smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2405       if (!smallblock) return E_OUTOFMEMORY;
2406     }
2407     else
2408     {
2409       pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2410       bigblock = *pbigblock;
2411       if (!bigblock) return E_OUTOFMEMORY;
2412     }
2413   }
2414   else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2415   {
2416     smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2417     if (!smallblock) return E_OUTOFMEMORY;
2418   }
2419   else
2420   {
2421     pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2422     bigblock = *pbigblock;
2423     if (!bigblock) return E_OUTOFMEMORY;
2424   }
2425
2426   /* Change the block chain type if necessary. */
2427   if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2428   {
2429     bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2430     if (!bigblock)
2431     {
2432       SmallBlockChainStream_Destroy(smallblock);
2433       return E_FAIL;
2434     }
2435
2436     pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2437     *pbigblock = bigblock;
2438   }
2439   else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2440   {
2441     smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2442     if (!smallblock)
2443       return E_FAIL;
2444   }
2445
2446   /* Set the size of the block chain. */
2447   if (smallblock)
2448   {
2449     SmallBlockChainStream_SetSize(smallblock, newsize);
2450     SmallBlockChainStream_Destroy(smallblock);
2451   }
2452   else
2453   {
2454     BlockChainStream_SetSize(bigblock, newsize);
2455   }
2456
2457   /* Set the size in the directory entry. */
2458   hr = StorageImpl_ReadDirEntry(This, index, &data);
2459   if (SUCCEEDED(hr))
2460   {
2461     data.size = newsize;
2462
2463     hr = StorageImpl_WriteDirEntry(This, index, &data);
2464   }
2465   return hr;
2466 }
2467
2468 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2469   ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2470 {
2471   StorageImpl *This = (StorageImpl*)base;
2472   DirEntry data;
2473   HRESULT hr;
2474   ULARGE_INTEGER newSize;
2475
2476   hr = StorageImpl_ReadDirEntry(This, index, &data);
2477   if (FAILED(hr)) return hr;
2478
2479   /* Grow the stream if necessary */
2480   newSize.QuadPart = 0;
2481   newSize.QuadPart = offset.QuadPart + size;
2482
2483   if (newSize.QuadPart > data.size.QuadPart)
2484   {
2485     hr = StorageImpl_StreamSetSize(base, index, newSize);
2486     if (FAILED(hr))
2487       return hr;
2488
2489     data.size = newSize;
2490   }
2491
2492   if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2493   {
2494     SmallBlockChainStream *stream;
2495
2496     stream = SmallBlockChainStream_Construct(This, NULL, index);
2497     if (!stream) return E_OUTOFMEMORY;
2498
2499     hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2500
2501     SmallBlockChainStream_Destroy(stream);
2502
2503     return hr;
2504   }
2505   else
2506   {
2507     BlockChainStream *stream;
2508
2509     stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2510     if (!stream) return E_OUTOFMEMORY;
2511
2512     hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2513
2514     return hr;
2515   }
2516 }
2517
2518 /*
2519  * Virtual function table for the IStorage32Impl class.
2520  */
2521 static const IStorageVtbl Storage32Impl_Vtbl =
2522 {
2523     StorageBaseImpl_QueryInterface,
2524     StorageBaseImpl_AddRef,
2525     StorageBaseImpl_Release,
2526     StorageBaseImpl_CreateStream,
2527     StorageBaseImpl_OpenStream,
2528     StorageBaseImpl_CreateStorage,
2529     StorageBaseImpl_OpenStorage,
2530     StorageBaseImpl_CopyTo,
2531     StorageBaseImpl_MoveElementTo,
2532     StorageImpl_Commit,
2533     StorageImpl_Revert,
2534     StorageBaseImpl_EnumElements,
2535     StorageBaseImpl_DestroyElement,
2536     StorageBaseImpl_RenameElement,
2537     StorageBaseImpl_SetElementTimes,
2538     StorageBaseImpl_SetClass,
2539     StorageBaseImpl_SetStateBits,
2540     StorageBaseImpl_Stat
2541 };
2542
2543 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2544 {
2545   StorageImpl_Destroy,
2546   StorageImpl_Invalidate,
2547   StorageImpl_CreateDirEntry,
2548   StorageImpl_BaseWriteDirEntry,
2549   StorageImpl_BaseReadDirEntry,
2550   StorageImpl_DestroyDirEntry,
2551   StorageImpl_StreamReadAt,
2552   StorageImpl_StreamWriteAt,
2553   StorageImpl_StreamSetSize
2554 };
2555
2556 static HRESULT StorageImpl_Construct(
2557   HANDLE       hFile,
2558   LPCOLESTR    pwcsName,
2559   ILockBytes*  pLkbyt,
2560   DWORD        openFlags,
2561   BOOL         fileBased,
2562   BOOL         create,
2563   StorageImpl** result)
2564 {
2565   StorageImpl* This;
2566   HRESULT     hr = S_OK;
2567   DirEntry currentEntry;
2568   DirRef      currentEntryRef;
2569   WCHAR fullpath[MAX_PATH];
2570
2571   if ( FAILED( validateSTGM(openFlags) ))
2572     return STG_E_INVALIDFLAG;
2573
2574   This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2575   if (!This)
2576     return E_OUTOFMEMORY;
2577
2578   memset(This, 0, sizeof(StorageImpl));
2579
2580   list_init(&This->base.strmHead);
2581
2582   list_init(&This->base.storageHead);
2583
2584   This->base.lpVtbl = &Storage32Impl_Vtbl;
2585   This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2586   This->base.baseVtbl = &StorageImpl_BaseVtbl;
2587   This->base.openFlags = (openFlags & ~STGM_CREATE);
2588   This->base.ref = 1;
2589   This->base.create = create;
2590
2591   This->base.reverted = 0;
2592
2593   This->hFile = hFile;
2594
2595   if(pwcsName) {
2596       if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2597       {
2598         lstrcpynW(fullpath, pwcsName, MAX_PATH);
2599       }
2600       This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2601                                 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2602       if (!This->pwcsName)
2603       {
2604          hr = STG_E_INSUFFICIENTMEMORY;
2605          goto end;
2606       }
2607       strcpyW(This->pwcsName, fullpath);
2608       This->base.filename = This->pwcsName;
2609   }
2610
2611   /*
2612    * Initialize the big block cache.
2613    */
2614   This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
2615   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2616   This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2617                                                 pLkbyt,
2618                                                 openFlags,
2619                                                 This->bigBlockSize,
2620                                                 fileBased);
2621
2622   if (This->bigBlockFile == 0)
2623   {
2624     hr = E_FAIL;
2625     goto end;
2626   }
2627
2628   if (create)
2629   {
2630     ULARGE_INTEGER size;
2631     BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2632
2633     /*
2634      * Initialize all header variables:
2635      * - The big block depot consists of one block and it is at block 0
2636      * - The directory table starts at block 1
2637      * - There is no small block depot
2638      */
2639     memset( This->bigBlockDepotStart,
2640             BLOCK_UNUSED,
2641             sizeof(This->bigBlockDepotStart));
2642
2643     This->bigBlockDepotCount    = 1;
2644     This->bigBlockDepotStart[0] = 0;
2645     This->rootStartBlock        = 1;
2646     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2647     This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
2648     This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
2649     This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2650     This->extBigBlockDepotCount = 0;
2651
2652     StorageImpl_SaveFileHeader(This);
2653
2654     /*
2655      * Add one block for the big block depot and one block for the directory table
2656      */
2657     size.u.HighPart = 0;
2658     size.u.LowPart  = This->bigBlockSize * 3;
2659     BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2660
2661     /*
2662      * Initialize the big block depot
2663      */
2664     memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2665     StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2666     StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2667     StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2668   }
2669   else
2670   {
2671     /*
2672      * Load the header for the file.
2673      */
2674     hr = StorageImpl_LoadFileHeader(This);
2675
2676     if (FAILED(hr))
2677     {
2678       goto end;
2679     }
2680   }
2681
2682   /*
2683    * There is no block depot cached yet.
2684    */
2685   This->indexBlockDepotCached = 0xFFFFFFFF;
2686
2687   /*
2688    * Start searching for free blocks with block 0.
2689    */
2690   This->prevFreeBlock = 0;
2691
2692   /*
2693    * Create the block chain abstractions.
2694    */
2695   if(!(This->rootBlockChain =
2696        BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2697   {
2698     hr = STG_E_READFAULT;
2699     goto end;
2700   }
2701
2702   if(!(This->smallBlockDepotChain =
2703        BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2704                                   DIRENTRY_NULL)))
2705   {
2706     hr = STG_E_READFAULT;
2707     goto end;
2708   }
2709
2710   /*
2711    * Write the root storage entry (memory only)
2712    */
2713   if (create)
2714   {
2715     DirEntry rootEntry;
2716     /*
2717      * Initialize the directory table
2718      */
2719     memset(&rootEntry, 0, sizeof(rootEntry));
2720     MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2721                          sizeof(rootEntry.name)/sizeof(WCHAR) );
2722     rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2723     rootEntry.stgType          = STGTY_ROOT;
2724     rootEntry.leftChild = DIRENTRY_NULL;
2725     rootEntry.rightChild     = DIRENTRY_NULL;
2726     rootEntry.dirRootEntry     = DIRENTRY_NULL;
2727     rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
2728     rootEntry.size.u.HighPart    = 0;
2729     rootEntry.size.u.LowPart     = 0;
2730
2731     StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2732   }
2733
2734   /*
2735    * Find the ID of the root storage.
2736    */
2737   currentEntryRef = 0;
2738
2739   do
2740   {
2741     hr = StorageImpl_ReadDirEntry(
2742                       This,
2743                       currentEntryRef,
2744                       &currentEntry);
2745
2746     if (SUCCEEDED(hr))
2747     {
2748       if ( (currentEntry.sizeOfNameString != 0 ) &&
2749            (currentEntry.stgType          == STGTY_ROOT) )
2750       {
2751         This->base.storageDirEntry = currentEntryRef;
2752       }
2753     }
2754
2755     currentEntryRef++;
2756
2757   } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2758
2759   if (FAILED(hr))
2760   {
2761     hr = STG_E_READFAULT;
2762     goto end;
2763   }
2764
2765   /*
2766    * Create the block chain abstraction for the small block root chain.
2767    */
2768   if(!(This->smallBlockRootChain =
2769        BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2770   {
2771     hr = STG_E_READFAULT;
2772   }
2773
2774 end:
2775   if (FAILED(hr))
2776   {
2777     IStorage_Release((IStorage*)This);
2778     *result = NULL;
2779   }
2780   else
2781     *result = This;
2782
2783   return hr;
2784 }
2785
2786 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2787 {
2788   StorageImpl *This = (StorageImpl*) iface;
2789
2790   StorageBaseImpl_DeleteAll(&This->base);
2791
2792   This->base.reverted = 1;
2793 }
2794
2795 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2796 {
2797   StorageImpl *This = (StorageImpl*) iface;
2798   int i;
2799   TRACE("(%p)\n", This);
2800
2801   StorageImpl_Invalidate(iface);
2802
2803   HeapFree(GetProcessHeap(), 0, This->pwcsName);
2804
2805   BlockChainStream_Destroy(This->smallBlockRootChain);
2806   BlockChainStream_Destroy(This->rootBlockChain);
2807   BlockChainStream_Destroy(This->smallBlockDepotChain);
2808
2809   for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2810     BlockChainStream_Destroy(This->blockChainCache[i]);
2811
2812   if (This->bigBlockFile)
2813     BIGBLOCKFILE_Destructor(This->bigBlockFile);
2814   HeapFree(GetProcessHeap(), 0, This);
2815 }
2816
2817 /******************************************************************************
2818  *      Storage32Impl_GetNextFreeBigBlock
2819  *
2820  * Returns the index of the next free big block.
2821  * If the big block depot is filled, this method will enlarge it.
2822  *
2823  */
2824 static ULONG StorageImpl_GetNextFreeBigBlock(
2825   StorageImpl* This)
2826 {
2827   ULONG depotBlockIndexPos;
2828   BYTE depotBuffer[BIG_BLOCK_SIZE];
2829   BOOL success;
2830   ULONG depotBlockOffset;
2831   ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
2832   ULONG nextBlockIndex    = BLOCK_SPECIAL;
2833   int   depotIndex        = 0;
2834   ULONG freeBlock         = BLOCK_UNUSED;
2835
2836   depotIndex = This->prevFreeBlock / blocksPerDepot;
2837   depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2838
2839   /*
2840    * Scan the entire big block depot until we find a block marked free
2841    */
2842   while (nextBlockIndex != BLOCK_UNUSED)
2843   {
2844     if (depotIndex < COUNT_BBDEPOTINHEADER)
2845     {
2846       depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2847
2848       /*
2849        * Grow the primary depot.
2850        */
2851       if (depotBlockIndexPos == BLOCK_UNUSED)
2852       {
2853         depotBlockIndexPos = depotIndex*blocksPerDepot;
2854
2855         /*
2856          * Add a block depot.
2857          */
2858         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2859         This->bigBlockDepotCount++;
2860         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2861
2862         /*
2863          * Flag it as a block depot.
2864          */
2865         StorageImpl_SetNextBlockInChain(This,
2866                                           depotBlockIndexPos,
2867                                           BLOCK_SPECIAL);
2868
2869         /* Save new header information.
2870          */
2871         StorageImpl_SaveFileHeader(This);
2872       }
2873     }
2874     else
2875     {
2876       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2877
2878       if (depotBlockIndexPos == BLOCK_UNUSED)
2879       {
2880         /*
2881          * Grow the extended depot.
2882          */
2883         ULONG extIndex       = BLOCK_UNUSED;
2884         ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
2885         ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2886
2887         if (extBlockOffset == 0)
2888         {
2889           /* We need an extended block.
2890            */
2891           extIndex = Storage32Impl_AddExtBlockDepot(This);
2892           This->extBigBlockDepotCount++;
2893           depotBlockIndexPos = extIndex + 1;
2894         }
2895         else
2896           depotBlockIndexPos = depotIndex * blocksPerDepot;
2897
2898         /*
2899          * Add a block depot and mark it in the extended block.
2900          */
2901         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2902         This->bigBlockDepotCount++;
2903         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2904
2905         /* Flag the block depot.
2906          */
2907         StorageImpl_SetNextBlockInChain(This,
2908                                           depotBlockIndexPos,
2909                                           BLOCK_SPECIAL);
2910
2911         /* If necessary, flag the extended depot block.
2912          */
2913         if (extIndex != BLOCK_UNUSED)
2914           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2915
2916         /* Save header information.
2917          */
2918         StorageImpl_SaveFileHeader(This);
2919       }
2920     }
2921
2922     success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2923
2924     if (success)
2925     {
2926       while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2927               ( nextBlockIndex != BLOCK_UNUSED))
2928       {
2929         StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2930
2931         if (nextBlockIndex == BLOCK_UNUSED)
2932         {
2933           freeBlock = (depotIndex * blocksPerDepot) +
2934                       (depotBlockOffset/sizeof(ULONG));
2935         }
2936
2937         depotBlockOffset += sizeof(ULONG);
2938       }
2939     }
2940
2941     depotIndex++;
2942     depotBlockOffset = 0;
2943   }
2944
2945   /*
2946    * make sure that the block physically exists before using it
2947    */
2948   BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2949
2950   This->prevFreeBlock = freeBlock;
2951
2952   return freeBlock;
2953 }
2954
2955 /******************************************************************************
2956  *      Storage32Impl_AddBlockDepot
2957  *
2958  * This will create a depot block, essentially it is a block initialized
2959  * to BLOCK_UNUSEDs.
2960  */
2961 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2962 {
2963   BYTE blockBuffer[BIG_BLOCK_SIZE];
2964
2965   /*
2966    * Initialize blocks as free
2967    */
2968   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2969   StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2970 }
2971
2972 /******************************************************************************
2973  *      Storage32Impl_GetExtDepotBlock
2974  *
2975  * Returns the index of the block that corresponds to the specified depot
2976  * index. This method is only for depot indexes equal or greater than
2977  * COUNT_BBDEPOTINHEADER.
2978  */
2979 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2980 {
2981   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2982   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2983   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2984   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2985   ULONG blockIndex             = BLOCK_UNUSED;
2986   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2987
2988   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2989
2990   if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2991     return BLOCK_UNUSED;
2992
2993   while (extBlockCount > 0)
2994   {
2995     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2996     extBlockCount--;
2997   }
2998
2999   if (extBlockIndex != BLOCK_UNUSED)
3000     StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3001                         extBlockOffset * sizeof(ULONG), &blockIndex);
3002
3003   return blockIndex;
3004 }
3005
3006 /******************************************************************************
3007  *      Storage32Impl_SetExtDepotBlock
3008  *
3009  * Associates the specified block index to the specified depot index.
3010  * This method is only for depot indexes equal or greater than
3011  * COUNT_BBDEPOTINHEADER.
3012  */
3013 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3014 {
3015   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3016   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
3017   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
3018   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
3019   ULONG extBlockIndex          = This->extBigBlockDepotStart;
3020
3021   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3022
3023   while (extBlockCount > 0)
3024   {
3025     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3026     extBlockCount--;
3027   }
3028
3029   if (extBlockIndex != BLOCK_UNUSED)
3030   {
3031     StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3032                         extBlockOffset * sizeof(ULONG),
3033                         blockIndex);
3034   }
3035 }
3036
3037 /******************************************************************************
3038  *      Storage32Impl_AddExtBlockDepot
3039  *
3040  * Creates an extended depot block.
3041  */
3042 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3043 {
3044   ULONG numExtBlocks           = This->extBigBlockDepotCount;
3045   ULONG nextExtBlock           = This->extBigBlockDepotStart;
3046   BYTE  depotBuffer[BIG_BLOCK_SIZE];
3047   ULONG index                  = BLOCK_UNUSED;
3048   ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
3049   ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
3050   ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3051
3052   index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3053           blocksPerDepotBlock;
3054
3055   if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3056   {
3057     /*
3058      * The first extended block.
3059      */
3060     This->extBigBlockDepotStart = index;
3061   }
3062   else
3063   {
3064     unsigned int i;
3065     /*
3066      * Follow the chain to the last one.
3067      */
3068     for (i = 0; i < (numExtBlocks - 1); i++)
3069     {
3070       nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3071     }
3072
3073     /*
3074      * Add the new extended block to the chain.
3075      */
3076     StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3077                                      index);
3078   }
3079
3080   /*
3081    * Initialize this block.
3082    */
3083   memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3084   StorageImpl_WriteBigBlock(This, index, depotBuffer);
3085
3086   return index;
3087 }
3088
3089 /******************************************************************************
3090  *      Storage32Impl_FreeBigBlock
3091  *
3092  * This method will flag the specified block as free in the big block depot.
3093  */
3094 static void StorageImpl_FreeBigBlock(
3095   StorageImpl* This,
3096   ULONG          blockIndex)
3097 {
3098   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3099
3100   if (blockIndex < This->prevFreeBlock)
3101     This->prevFreeBlock = blockIndex;
3102 }
3103
3104 /************************************************************************
3105  * Storage32Impl_GetNextBlockInChain
3106  *
3107  * This method will retrieve the block index of the next big block in
3108  * in the chain.
3109  *
3110  * Params:  This       - Pointer to the Storage object.
3111  *          blockIndex - Index of the block to retrieve the chain
3112  *                       for.
3113  *          nextBlockIndex - receives the return value.
3114  *
3115  * Returns: This method returns the index of the next block in the chain.
3116  *          It will return the constants:
3117  *              BLOCK_SPECIAL - If the block given was not part of a
3118  *                              chain.
3119  *              BLOCK_END_OF_CHAIN - If the block given was the last in
3120  *                                   a chain.
3121  *              BLOCK_UNUSED - If the block given was not past of a chain
3122  *                             and is available.
3123  *              BLOCK_EXTBBDEPOT - This block is part of the extended
3124  *                                 big block depot.
3125  *
3126  * See Windows documentation for more details on IStorage methods.
3127  */
3128 static HRESULT StorageImpl_GetNextBlockInChain(
3129   StorageImpl* This,
3130   ULONG        blockIndex,
3131   ULONG*       nextBlockIndex)
3132 {
3133   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
3134   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
3135   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3136   BYTE depotBuffer[BIG_BLOCK_SIZE];
3137   BOOL success;
3138   ULONG depotBlockIndexPos;
3139   int index;
3140
3141   *nextBlockIndex   = BLOCK_SPECIAL;
3142
3143   if(depotBlockCount >= This->bigBlockDepotCount)
3144   {
3145     WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3146          This->bigBlockDepotCount);
3147     return STG_E_READFAULT;
3148   }
3149
3150   /*
3151    * Cache the currently accessed depot block.
3152    */
3153   if (depotBlockCount != This->indexBlockDepotCached)
3154   {
3155     This->indexBlockDepotCached = depotBlockCount;
3156
3157     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3158     {
3159       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3160     }
3161     else
3162     {
3163       /*
3164        * We have to look in the extended depot.
3165        */
3166       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3167     }
3168
3169     success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3170
3171     if (!success)
3172       return STG_E_READFAULT;
3173
3174     for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3175     {
3176       StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3177       This->blockDepotCached[index] = *nextBlockIndex;
3178     }
3179   }
3180
3181   *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3182
3183   return S_OK;
3184 }
3185
3186 /******************************************************************************
3187  *      Storage32Impl_GetNextExtendedBlock
3188  *
3189  * Given an extended block this method will return the next extended block.
3190  *
3191  * NOTES:
3192  * The last ULONG of an extended block is the block index of the next
3193  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3194  * depot.
3195  *
3196  * Return values:
3197  *    - The index of the next extended block
3198  *    - BLOCK_UNUSED: there is no next extended block.
3199  *    - Any other return values denotes failure.
3200  */
3201 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3202 {
3203   ULONG nextBlockIndex   = BLOCK_SPECIAL;
3204   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3205
3206   StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3207                         &nextBlockIndex);
3208
3209   return nextBlockIndex;
3210 }
3211
3212 /******************************************************************************
3213  *      Storage32Impl_SetNextBlockInChain
3214  *
3215  * This method will write the index of the specified block's next block
3216  * in the big block depot.
3217  *
3218  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3219  *              do the following
3220  *
3221  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3222  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3223  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3224  *
3225  */
3226 static void StorageImpl_SetNextBlockInChain(
3227           StorageImpl* This,
3228           ULONG          blockIndex,
3229           ULONG          nextBlock)
3230 {
3231   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
3232   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
3233   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3234   ULONG depotBlockIndexPos;
3235
3236   assert(depotBlockCount < This->bigBlockDepotCount);
3237   assert(blockIndex != nextBlock);
3238
3239   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3240   {
3241     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3242   }
3243   else
3244   {
3245     /*
3246      * We have to look in the extended depot.
3247      */
3248     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3249   }
3250
3251   StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3252                         nextBlock);
3253   /*
3254    * Update the cached block depot, if necessary.
3255    */
3256   if (depotBlockCount == This->indexBlockDepotCached)
3257   {
3258     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3259   }
3260 }
3261
3262 /******************************************************************************
3263  *      Storage32Impl_LoadFileHeader
3264  *
3265  * This method will read in the file header, i.e. big block index -1.
3266  */
3267 static HRESULT StorageImpl_LoadFileHeader(
3268           StorageImpl* This)
3269 {
3270   HRESULT hr = STG_E_FILENOTFOUND;
3271   BYTE    headerBigBlock[BIG_BLOCK_SIZE];
3272   BOOL    success;
3273   int     index;
3274
3275   TRACE("\n");
3276   /*
3277    * Get a pointer to the big block of data containing the header.
3278    */
3279   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3280
3281   /*
3282    * Extract the information from the header.
3283    */
3284   if (success)
3285   {
3286     /*
3287      * Check for the "magic number" signature and return an error if it is not
3288      * found.
3289      */
3290     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3291     {
3292       return STG_E_OLDFORMAT;
3293     }
3294
3295     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3296     {
3297       return STG_E_INVALIDHEADER;
3298     }
3299
3300     StorageUtl_ReadWord(
3301       headerBigBlock,
3302       OFFSET_BIGBLOCKSIZEBITS,
3303       &This->bigBlockSizeBits);
3304
3305     StorageUtl_ReadWord(
3306       headerBigBlock,
3307       OFFSET_SMALLBLOCKSIZEBITS,
3308       &This->smallBlockSizeBits);
3309
3310     StorageUtl_ReadDWord(
3311       headerBigBlock,
3312       OFFSET_BBDEPOTCOUNT,
3313       &This->bigBlockDepotCount);
3314
3315     StorageUtl_ReadDWord(
3316       headerBigBlock,
3317       OFFSET_ROOTSTARTBLOCK,
3318       &This->rootStartBlock);
3319
3320     StorageUtl_ReadDWord(
3321       headerBigBlock,
3322       OFFSET_SBDEPOTSTART,
3323       &This->smallBlockDepotStart);
3324
3325     StorageUtl_ReadDWord(
3326       headerBigBlock,
3327       OFFSET_EXTBBDEPOTSTART,
3328       &This->extBigBlockDepotStart);
3329
3330     StorageUtl_ReadDWord(
3331       headerBigBlock,
3332       OFFSET_EXTBBDEPOTCOUNT,
3333       &This->extBigBlockDepotCount);
3334
3335     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3336     {
3337       StorageUtl_ReadDWord(
3338         headerBigBlock,
3339         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3340         &(This->bigBlockDepotStart[index]));
3341     }
3342
3343     /*
3344      * Make the bitwise arithmetic to get the size of the blocks in bytes.
3345      */
3346     This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3347     This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3348
3349     /*
3350      * Right now, the code is making some assumptions about the size of the
3351      * blocks, just make sure they are what we're expecting.
3352      */
3353     if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3354         This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3355     {
3356         WARN("Broken OLE storage file\n");
3357         hr = STG_E_INVALIDHEADER;
3358     }
3359     else
3360         hr = S_OK;
3361   }
3362
3363   return hr;
3364 }
3365
3366 /******************************************************************************
3367  *      Storage32Impl_SaveFileHeader
3368  *
3369  * This method will save to the file the header, i.e. big block -1.
3370  */
3371 static void StorageImpl_SaveFileHeader(
3372           StorageImpl* This)
3373 {
3374   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
3375   int    index;
3376   BOOL success;
3377
3378   /*
3379    * Get a pointer to the big block of data containing the header.
3380    */
3381   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3382
3383   /*
3384    * If the block read failed, the file is probably new.
3385    */
3386   if (!success)
3387   {
3388     /*
3389      * Initialize for all unknown fields.
3390      */
3391     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3392
3393     /*
3394      * Initialize the magic number.
3395      */
3396     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3397
3398     /*
3399      * And a bunch of things we don't know what they mean
3400      */
3401     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
3402     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
3403     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
3404     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3405   }
3406
3407   /*
3408    * Write the information to the header.
3409    */
3410   StorageUtl_WriteWord(
3411     headerBigBlock,
3412     OFFSET_BIGBLOCKSIZEBITS,
3413     This->bigBlockSizeBits);
3414
3415   StorageUtl_WriteWord(
3416     headerBigBlock,
3417     OFFSET_SMALLBLOCKSIZEBITS,
3418     This->smallBlockSizeBits);
3419
3420   StorageUtl_WriteDWord(
3421     headerBigBlock,
3422     OFFSET_BBDEPOTCOUNT,
3423     This->bigBlockDepotCount);
3424
3425   StorageUtl_WriteDWord(
3426     headerBigBlock,
3427     OFFSET_ROOTSTARTBLOCK,
3428     This->rootStartBlock);
3429
3430   StorageUtl_WriteDWord(
3431     headerBigBlock,
3432     OFFSET_SBDEPOTSTART,
3433     This->smallBlockDepotStart);
3434
3435   StorageUtl_WriteDWord(
3436     headerBigBlock,
3437     OFFSET_SBDEPOTCOUNT,
3438     This->smallBlockDepotChain ?
3439      BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3440
3441   StorageUtl_WriteDWord(
3442     headerBigBlock,
3443     OFFSET_EXTBBDEPOTSTART,
3444     This->extBigBlockDepotStart);
3445
3446   StorageUtl_WriteDWord(
3447     headerBigBlock,
3448     OFFSET_EXTBBDEPOTCOUNT,
3449     This->extBigBlockDepotCount);
3450
3451   for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3452   {
3453     StorageUtl_WriteDWord(
3454       headerBigBlock,
3455       OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3456       (This->bigBlockDepotStart[index]));
3457   }
3458
3459   /*
3460    * Write the big block back to the file.
3461    */
3462   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3463 }
3464
3465 /******************************************************************************
3466  *      StorageImpl_ReadRawDirEntry
3467  *
3468  * This method will read the raw data from a directory entry in the file.
3469  *
3470  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3471  */
3472 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3473 {
3474   ULARGE_INTEGER offset;
3475   HRESULT hr;
3476   ULONG bytesRead;
3477
3478   offset.u.HighPart = 0;
3479   offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
3480
3481   hr = BlockChainStream_ReadAt(
3482                     This->rootBlockChain,
3483                     offset,
3484                     RAW_DIRENTRY_SIZE,
3485                     buffer,
3486                     &bytesRead);
3487
3488   return hr;
3489 }
3490
3491 /******************************************************************************
3492  *      StorageImpl_WriteRawDirEntry
3493  *
3494  * This method will write the raw data from a directory entry in the file.
3495  *
3496  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3497  */
3498 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3499 {
3500   ULARGE_INTEGER offset;
3501   HRESULT hr;
3502   ULONG bytesRead;
3503
3504   offset.u.HighPart = 0;
3505   offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
3506
3507   hr = BlockChainStream_WriteAt(
3508                     This->rootBlockChain,
3509                     offset,
3510                     RAW_DIRENTRY_SIZE,
3511                     buffer,
3512                     &bytesRead);
3513
3514   return hr;
3515 }
3516
3517 /******************************************************************************
3518  *      UpdateRawDirEntry
3519  *
3520  * Update raw directory entry data from the fields in newData.
3521  *
3522  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3523  */
3524 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3525 {
3526   memset(buffer, 0, RAW_DIRENTRY_SIZE);
3527
3528   memcpy(
3529     buffer + OFFSET_PS_NAME,
3530     newData->name,
3531     DIRENTRY_NAME_BUFFER_LEN );
3532
3533   memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3534
3535   StorageUtl_WriteWord(
3536     buffer,
3537       OFFSET_PS_NAMELENGTH,
3538       newData->sizeOfNameString);
3539
3540   StorageUtl_WriteDWord(
3541     buffer,
3542       OFFSET_PS_LEFTCHILD,
3543       newData->leftChild);
3544
3545   StorageUtl_WriteDWord(
3546     buffer,
3547       OFFSET_PS_RIGHTCHILD,
3548       newData->rightChild);
3549
3550   StorageUtl_WriteDWord(
3551     buffer,
3552       OFFSET_PS_DIRROOT,
3553       newData->dirRootEntry);
3554
3555   StorageUtl_WriteGUID(
3556     buffer,
3557       OFFSET_PS_GUID,
3558       &newData->clsid);
3559
3560   StorageUtl_WriteDWord(
3561     buffer,
3562       OFFSET_PS_CTIMELOW,
3563       newData->ctime.dwLowDateTime);
3564
3565   StorageUtl_WriteDWord(
3566     buffer,
3567       OFFSET_PS_CTIMEHIGH,
3568       newData->ctime.dwHighDateTime);
3569
3570   StorageUtl_WriteDWord(
3571     buffer,
3572       OFFSET_PS_MTIMELOW,
3573       newData->mtime.dwLowDateTime);
3574
3575   StorageUtl_WriteDWord(
3576     buffer,
3577       OFFSET_PS_MTIMEHIGH,
3578       newData->ctime.dwHighDateTime);
3579
3580   StorageUtl_WriteDWord(
3581     buffer,
3582       OFFSET_PS_STARTBLOCK,
3583       newData->startingBlock);
3584
3585   StorageUtl_WriteDWord(
3586     buffer,
3587       OFFSET_PS_SIZE,
3588       newData->size.u.LowPart);
3589 }
3590
3591 /******************************************************************************
3592  *      Storage32Impl_ReadDirEntry
3593  *
3594  * This method will read the specified directory entry.
3595  */
3596 HRESULT StorageImpl_ReadDirEntry(
3597   StorageImpl* This,
3598   DirRef         index,
3599   DirEntry*      buffer)
3600 {
3601   BYTE           currentEntry[RAW_DIRENTRY_SIZE];
3602   HRESULT        readRes;
3603
3604   readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3605
3606   if (SUCCEEDED(readRes))
3607   {
3608     memset(buffer->name, 0, sizeof(buffer->name));
3609     memcpy(
3610       buffer->name,
3611       (WCHAR *)currentEntry+OFFSET_PS_NAME,
3612       DIRENTRY_NAME_BUFFER_LEN );
3613     TRACE("storage name: %s\n", debugstr_w(buffer->name));
3614
3615     memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3616
3617     StorageUtl_ReadWord(
3618       currentEntry,
3619       OFFSET_PS_NAMELENGTH,
3620       &buffer->sizeOfNameString);
3621
3622     StorageUtl_ReadDWord(
3623       currentEntry,
3624       OFFSET_PS_LEFTCHILD,
3625       &buffer->leftChild);
3626
3627     StorageUtl_ReadDWord(
3628       currentEntry,
3629       OFFSET_PS_RIGHTCHILD,
3630       &buffer->rightChild);
3631
3632     StorageUtl_ReadDWord(
3633       currentEntry,
3634       OFFSET_PS_DIRROOT,
3635       &buffer->dirRootEntry);
3636
3637     StorageUtl_ReadGUID(
3638       currentEntry,
3639       OFFSET_PS_GUID,
3640       &buffer->clsid);
3641
3642     StorageUtl_ReadDWord(
3643       currentEntry,
3644       OFFSET_PS_CTIMELOW,
3645       &buffer->ctime.dwLowDateTime);
3646
3647     StorageUtl_ReadDWord(
3648       currentEntry,
3649       OFFSET_PS_CTIMEHIGH,
3650       &buffer->ctime.dwHighDateTime);
3651
3652     StorageUtl_ReadDWord(
3653       currentEntry,
3654       OFFSET_PS_MTIMELOW,
3655       &buffer->mtime.dwLowDateTime);
3656
3657     StorageUtl_ReadDWord(
3658       currentEntry,
3659       OFFSET_PS_MTIMEHIGH,
3660       &buffer->mtime.dwHighDateTime);
3661
3662     StorageUtl_ReadDWord(
3663       currentEntry,
3664       OFFSET_PS_STARTBLOCK,
3665       &buffer->startingBlock);
3666
3667     StorageUtl_ReadDWord(
3668       currentEntry,
3669       OFFSET_PS_SIZE,
3670       &buffer->size.u.LowPart);
3671
3672     buffer->size.u.HighPart = 0;
3673   }
3674
3675   return readRes;
3676 }
3677
3678 /*********************************************************************
3679  * Write the specified directory entry to the file
3680  */
3681 HRESULT StorageImpl_WriteDirEntry(
3682   StorageImpl*          This,
3683   DirRef                index,
3684   const DirEntry*       buffer)
3685 {
3686   BYTE           currentEntry[RAW_DIRENTRY_SIZE];
3687   HRESULT        writeRes;
3688
3689   UpdateRawDirEntry(currentEntry, buffer);
3690
3691   writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3692   return writeRes;
3693 }
3694
3695 static BOOL StorageImpl_ReadBigBlock(
3696   StorageImpl* This,
3697   ULONG          blockIndex,
3698   void*          buffer)
3699 {
3700   ULARGE_INTEGER ulOffset;
3701   DWORD  read;
3702
3703   ulOffset.u.HighPart = 0;
3704   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3705
3706   StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3707   return (read == This->bigBlockSize);
3708 }
3709
3710 static BOOL StorageImpl_ReadDWordFromBigBlock(
3711   StorageImpl*  This,
3712   ULONG         blockIndex,
3713   ULONG         offset,
3714   DWORD*        value)
3715 {
3716   ULARGE_INTEGER ulOffset;
3717   DWORD  read;
3718   DWORD  tmp;
3719
3720   ulOffset.u.HighPart = 0;
3721   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3722   ulOffset.u.LowPart += offset;
3723
3724   StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3725   *value = lendian32toh(tmp);
3726   return (read == sizeof(DWORD));
3727 }
3728
3729 static BOOL StorageImpl_WriteBigBlock(
3730   StorageImpl*  This,
3731   ULONG         blockIndex,
3732   const void*   buffer)
3733 {
3734   ULARGE_INTEGER ulOffset;
3735   DWORD  wrote;
3736
3737   ulOffset.u.HighPart = 0;
3738   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3739
3740   StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3741   return (wrote == This->bigBlockSize);
3742 }
3743
3744 static BOOL StorageImpl_WriteDWordToBigBlock(
3745   StorageImpl* This,
3746   ULONG         blockIndex,
3747   ULONG         offset,
3748   DWORD         value)
3749 {
3750   ULARGE_INTEGER ulOffset;
3751   DWORD  wrote;
3752
3753   ulOffset.u.HighPart = 0;
3754   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3755   ulOffset.u.LowPart += offset;
3756
3757   value = htole32(value);
3758   StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3759   return (wrote == sizeof(DWORD));
3760 }
3761
3762 /******************************************************************************
3763  *              Storage32Impl_SmallBlocksToBigBlocks
3764  *
3765  * This method will convert a small block chain to a big block chain.
3766  * The small block chain will be destroyed.
3767  */
3768 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3769                       StorageImpl* This,
3770                       SmallBlockChainStream** ppsbChain)
3771 {
3772   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3773   ULARGE_INTEGER size, offset;
3774   ULONG cbRead, cbWritten;
3775   ULARGE_INTEGER cbTotalRead;
3776   DirRef streamEntryRef;
3777   HRESULT resWrite = S_OK;
3778   HRESULT resRead;
3779   DirEntry streamEntry;
3780   BYTE *buffer;
3781   BlockChainStream *bbTempChain = NULL;
3782   BlockChainStream *bigBlockChain = NULL;
3783
3784   /*
3785    * Create a temporary big block chain that doesn't have
3786    * an associated directory entry. This temporary chain will be
3787    * used to copy data from small blocks to big blocks.
3788    */
3789   bbTempChain = BlockChainStream_Construct(This,
3790                                            &bbHeadOfChain,
3791                                            DIRENTRY_NULL);
3792   if(!bbTempChain) return NULL;
3793   /*
3794    * Grow the big block chain.
3795    */
3796   size = SmallBlockChainStream_GetSize(*ppsbChain);
3797   BlockChainStream_SetSize(bbTempChain, size);
3798
3799   /*
3800    * Copy the contents of the small block chain to the big block chain
3801    * by small block size increments.
3802    */
3803   offset.u.LowPart = 0;
3804   offset.u.HighPart = 0;
3805   cbTotalRead.QuadPart = 0;
3806
3807   buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3808   do
3809   {
3810     resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3811                                            offset,
3812                                            min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3813                                            buffer,
3814                                            &cbRead);
3815     if (FAILED(resRead))
3816         break;
3817
3818     if (cbRead > 0)
3819     {
3820         cbTotalRead.QuadPart += cbRead;
3821
3822         resWrite = BlockChainStream_WriteAt(bbTempChain,
3823                                             offset,
3824                                             cbRead,
3825                                             buffer,
3826                                             &cbWritten);
3827
3828         if (FAILED(resWrite))
3829             break;
3830
3831         offset.u.LowPart += cbRead;
3832     }
3833   } while (cbTotalRead.QuadPart < size.QuadPart);
3834   HeapFree(GetProcessHeap(),0,buffer);
3835
3836   size.u.HighPart = 0;
3837   size.u.LowPart  = 0;
3838
3839   if (FAILED(resRead) || FAILED(resWrite))
3840   {
3841     ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3842     BlockChainStream_SetSize(bbTempChain, size);
3843     BlockChainStream_Destroy(bbTempChain);
3844     return NULL;
3845   }
3846
3847   /*
3848    * Destroy the small block chain.
3849    */
3850   streamEntryRef = (*ppsbChain)->ownerDirEntry;
3851   SmallBlockChainStream_SetSize(*ppsbChain, size);
3852   SmallBlockChainStream_Destroy(*ppsbChain);
3853   *ppsbChain = 0;
3854
3855   /*
3856    * Change the directory entry. This chain is now a big block chain
3857    * and it doesn't reside in the small blocks chain anymore.
3858    */
3859   StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3860
3861   streamEntry.startingBlock = bbHeadOfChain;
3862
3863   StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3864
3865   /*
3866    * Destroy the temporary entryless big block chain.
3867    * Create a new big block chain associated with this entry.
3868    */
3869   BlockChainStream_Destroy(bbTempChain);
3870   bigBlockChain = BlockChainStream_Construct(This,
3871                                              NULL,
3872                                              streamEntryRef);
3873
3874   return bigBlockChain;
3875 }
3876
3877 /******************************************************************************
3878  *              Storage32Impl_BigBlocksToSmallBlocks
3879  *
3880  * This method will convert a big block chain to a small block chain.
3881  * The big block chain will be destroyed on success.
3882  */
3883 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3884                            StorageImpl* This,
3885                            BlockChainStream** ppbbChain)
3886 {
3887     ULARGE_INTEGER size, offset, cbTotalRead;
3888     ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3889     DirRef streamEntryRef;
3890     HRESULT resWrite = S_OK, resRead;
3891     DirEntry streamEntry;
3892     BYTE* buffer;
3893     SmallBlockChainStream* sbTempChain;
3894
3895     TRACE("%p %p\n", This, ppbbChain);
3896
3897     sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3898             DIRENTRY_NULL);
3899
3900     if(!sbTempChain)
3901         return NULL;
3902
3903     size = BlockChainStream_GetSize(*ppbbChain);
3904     SmallBlockChainStream_SetSize(sbTempChain, size);
3905
3906     offset.u.HighPart = 0;
3907     offset.u.LowPart = 0;
3908     cbTotalRead.QuadPart = 0;
3909     buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3910     do
3911     {
3912         resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3913                 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3914                 buffer, &cbRead);
3915
3916         if(FAILED(resRead))
3917             break;
3918
3919         if(cbRead > 0)
3920         {
3921             cbTotalRead.QuadPart += cbRead;
3922
3923             resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3924                     cbRead, buffer, &cbWritten);
3925
3926             if(FAILED(resWrite))
3927                 break;
3928
3929             offset.u.LowPart += cbRead;
3930         }
3931     }while(cbTotalRead.QuadPart < size.QuadPart);
3932     HeapFree(GetProcessHeap(), 0, buffer);
3933
3934     size.u.HighPart = 0;
3935     size.u.LowPart = 0;
3936
3937     if(FAILED(resRead) || FAILED(resWrite))
3938     {
3939         ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3940         SmallBlockChainStream_SetSize(sbTempChain, size);
3941         SmallBlockChainStream_Destroy(sbTempChain);
3942         return NULL;
3943     }
3944
3945     /* destroy the original big block chain */
3946     streamEntryRef = (*ppbbChain)->ownerDirEntry;
3947     BlockChainStream_SetSize(*ppbbChain, size);
3948     BlockChainStream_Destroy(*ppbbChain);
3949     *ppbbChain = NULL;
3950
3951     StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3952     streamEntry.startingBlock = sbHeadOfChain;
3953     StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3954
3955     SmallBlockChainStream_Destroy(sbTempChain);
3956     return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3957 }
3958
3959 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3960 {
3961   HRESULT hr;
3962   DirEntry parentData, snapshotData;
3963
3964   hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3965       0, (IStorage**)snapshot);
3966
3967   if (SUCCEEDED(hr))
3968   {
3969     hr = StorageBaseImpl_ReadDirEntry(original,
3970       original->storageDirEntry, &parentData);
3971
3972     if (SUCCEEDED(hr))
3973       hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3974         (*snapshot)->storageDirEntry, &snapshotData);
3975
3976     if (SUCCEEDED(hr))
3977     {
3978       memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3979       snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3980       snapshotData.stgType = parentData.stgType;
3981       snapshotData.clsid = parentData.clsid;
3982       snapshotData.ctime = parentData.ctime;
3983       snapshotData.mtime = parentData.mtime;
3984       hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3985         (*snapshot)->storageDirEntry, &snapshotData);
3986     }
3987
3988     if (SUCCEEDED(hr))
3989       hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3990           (IStorage*)(*snapshot));
3991
3992     if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3993   }
3994
3995   return hr;
3996 }
3997
3998 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
3999   IStorage*            iface,
4000   DWORD                  grfCommitFlags)  /* [in] */
4001 {
4002   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4003   HRESULT hr;
4004   DirEntry data, tempStorageData, snapshotRootData;
4005   DirRef tempStorageEntry, oldDirRoot;
4006   StorageInternalImpl *tempStorage;
4007
4008   TRACE("(%p,%x)\n", iface, grfCommitFlags);
4009
4010   /* Cannot commit a read-only transacted storage */
4011   if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4012     return STG_E_ACCESSDENIED;
4013
4014   /* To prevent data loss, we create the new structure in the file before we
4015    * delete the old one, so that in case of errors the old data is intact. We
4016    * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4017    * needed in the rare situation where we have just enough free disk space to
4018    * overwrite the existing data. */
4019
4020   /* Create an orphaned storage in the parent for the new directory structure. */
4021   memset(&data, 0, sizeof(data));
4022   data.name[0] = 'D';
4023   data.sizeOfNameString = 1;
4024   data.stgType = STGTY_STORAGE;
4025   data.leftChild = DIRENTRY_NULL;
4026   data.rightChild = DIRENTRY_NULL;
4027   data.dirRootEntry = DIRENTRY_NULL;
4028   hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4029
4030   if (FAILED(hr)) return hr;
4031
4032   tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4033     STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4034   if (tempStorage)
4035   {
4036     hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4037         (IStorage*)tempStorage);
4038
4039     list_init(&tempStorage->ParentListEntry);
4040
4041     IStorage_Release((IStorage*) tempStorage);
4042   }
4043   else
4044     hr = E_OUTOFMEMORY;
4045
4046   if (FAILED(hr))
4047   {
4048     DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4049     return hr;
4050   }
4051
4052   /* Update the storage to use the new data in one step. */
4053   hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4054     This->transactedParent->storageDirEntry, &data);
4055
4056   if (SUCCEEDED(hr))
4057   {
4058     hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4059       tempStorageEntry, &tempStorageData);
4060   }
4061
4062   if (SUCCEEDED(hr))
4063   {
4064     hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4065       This->snapshot->storageDirEntry, &snapshotRootData);
4066   }
4067
4068   if (SUCCEEDED(hr))
4069   {
4070     oldDirRoot = data.dirRootEntry;
4071     data.dirRootEntry = tempStorageData.dirRootEntry;
4072     data.clsid = snapshotRootData.clsid;
4073     data.ctime = snapshotRootData.ctime;
4074     data.mtime = snapshotRootData.mtime;
4075
4076     hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4077       This->transactedParent->storageDirEntry, &data);
4078   }
4079
4080   if (SUCCEEDED(hr))
4081   {
4082     /* Destroy the old now-orphaned data. */
4083     DestroyReachableEntries(This->transactedParent, oldDirRoot);
4084     StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4085   }
4086   else
4087   {
4088     DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4089   }
4090
4091   return hr;
4092 }
4093
4094 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4095   IStorage*            iface)
4096 {
4097   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4098   StorageBaseImpl *newSnapshot;
4099   HRESULT hr;
4100
4101   TRACE("(%p)\n", iface);
4102
4103   /* Create a new copy of the parent data. */
4104   hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4105   if (FAILED(hr)) return hr;
4106
4107   /* Destroy the open objects. */
4108   StorageBaseImpl_DeleteAll(&This->base);
4109
4110   /* Replace our current snapshot. */
4111   IStorage_Release((IStorage*)This->snapshot);
4112   This->snapshot = newSnapshot;
4113
4114   return S_OK;
4115 }
4116
4117 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4118 {
4119   if (!This->reverted)
4120   {
4121     TRACE("Storage invalidated (stg=%p)\n", This);
4122
4123     This->reverted = 1;
4124
4125     StorageBaseImpl_DeleteAll(This);
4126   }
4127 }
4128
4129 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4130 {
4131   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4132
4133   TransactedSnapshotImpl_Invalidate(iface);
4134
4135   IStorage_Release((IStorage*)This->transactedParent);
4136
4137   IStorage_Release((IStorage*)This->snapshot);
4138
4139   HeapFree(GetProcessHeap(), 0, This);
4140 }
4141
4142 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4143   const DirEntry *newData, DirRef *index)
4144 {
4145   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4146
4147   return StorageBaseImpl_CreateDirEntry(This->snapshot,
4148     newData, index);
4149 }
4150
4151 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4152   DirRef index, const DirEntry *data)
4153 {
4154   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4155
4156   return StorageBaseImpl_WriteDirEntry(This->snapshot,
4157     index, data);
4158 }
4159
4160 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4161   DirRef index, DirEntry *data)
4162 {
4163   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4164
4165   return StorageBaseImpl_ReadDirEntry(This->snapshot,
4166     index, data);
4167 }
4168
4169 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4170   DirRef index)
4171 {
4172   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4173
4174   return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4175     index);
4176 }
4177
4178 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4179   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4180 {
4181   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4182
4183   return StorageBaseImpl_StreamReadAt(This->snapshot,
4184     index, offset, size, buffer, bytesRead);
4185 }
4186
4187 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4188   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4189 {
4190   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4191
4192   return StorageBaseImpl_StreamWriteAt(This->snapshot,
4193     index, offset, size, buffer, bytesWritten);
4194 }
4195
4196 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4197   DirRef index, ULARGE_INTEGER newsize)
4198 {
4199   TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4200
4201   return StorageBaseImpl_StreamSetSize(This->snapshot,
4202     index, newsize);
4203 }
4204
4205 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4206 {
4207     StorageBaseImpl_QueryInterface,
4208     StorageBaseImpl_AddRef,
4209     StorageBaseImpl_Release,
4210     StorageBaseImpl_CreateStream,
4211     StorageBaseImpl_OpenStream,
4212     StorageBaseImpl_CreateStorage,
4213     StorageBaseImpl_OpenStorage,
4214     StorageBaseImpl_CopyTo,
4215     StorageBaseImpl_MoveElementTo,
4216     TransactedSnapshotImpl_Commit,
4217     TransactedSnapshotImpl_Revert,
4218     StorageBaseImpl_EnumElements,
4219     StorageBaseImpl_DestroyElement,
4220     StorageBaseImpl_RenameElement,
4221     StorageBaseImpl_SetElementTimes,
4222     StorageBaseImpl_SetClass,
4223     StorageBaseImpl_SetStateBits,
4224     StorageBaseImpl_Stat
4225 };
4226
4227 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4228 {
4229   TransactedSnapshotImpl_Destroy,
4230   TransactedSnapshotImpl_Invalidate,
4231   TransactedSnapshotImpl_CreateDirEntry,
4232   TransactedSnapshotImpl_WriteDirEntry,
4233   TransactedSnapshotImpl_ReadDirEntry,
4234   TransactedSnapshotImpl_DestroyDirEntry,
4235   TransactedSnapshotImpl_StreamReadAt,
4236   TransactedSnapshotImpl_StreamWriteAt,
4237   TransactedSnapshotImpl_StreamSetSize
4238 };
4239
4240 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4241   TransactedSnapshotImpl** result)
4242 {
4243   HRESULT hr;
4244
4245   *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4246   if (*result)
4247   {
4248     (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4249
4250     /* This is OK because the property set storage functions use the IStorage functions. */
4251     (*result)->base.pssVtbl = parentStorage->pssVtbl;
4252
4253     (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4254
4255     list_init(&(*result)->base.strmHead);
4256
4257     list_init(&(*result)->base.storageHead);
4258
4259     (*result)->base.ref = 1;
4260
4261     (*result)->base.openFlags = parentStorage->openFlags;
4262
4263     (*result)->base.filename = parentStorage->filename;
4264
4265     /* Create a new temporary storage to act as the snapshot */
4266     hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4267
4268     if (SUCCEEDED(hr))
4269     {
4270         (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4271
4272         /* parentStorage already has 1 reference, which we take over here. */
4273         (*result)->transactedParent = parentStorage;
4274
4275         parentStorage->transactedChild = (StorageBaseImpl*)*result;
4276     }
4277
4278     if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4279
4280     return hr;
4281   }
4282   else
4283     return E_OUTOFMEMORY;
4284 }
4285
4286 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4287   StorageBaseImpl** result)
4288 {
4289   static int fixme=0;
4290
4291   if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4292   {
4293     FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4294   }
4295
4296   return TransactedSnapshotImpl_Construct(parentStorage,
4297     (TransactedSnapshotImpl**)result);
4298 }
4299
4300 static HRESULT Storage_Construct(
4301   HANDLE       hFile,
4302   LPCOLESTR    pwcsName,
4303   ILockBytes*  pLkbyt,
4304   DWORD        openFlags,
4305   BOOL         fileBased,
4306   BOOL         create,
4307   StorageBaseImpl** result)
4308 {
4309   StorageImpl *newStorage;
4310   StorageBaseImpl *newTransactedStorage;
4311   HRESULT hr;
4312
4313   hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4314   if (FAILED(hr)) goto end;
4315
4316   if (openFlags & STGM_TRANSACTED)
4317   {
4318     hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4319     if (FAILED(hr))
4320       IStorage_Release((IStorage*)newStorage);
4321     else
4322       *result = newTransactedStorage;
4323   }
4324   else
4325     *result = &newStorage->base;
4326
4327 end:
4328   return hr;
4329 }
4330
4331 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4332 {
4333   StorageInternalImpl* This = (StorageInternalImpl*) base;
4334
4335   if (!This->base.reverted)
4336   {
4337     TRACE("Storage invalidated (stg=%p)\n", This);
4338
4339     This->base.reverted = 1;
4340
4341     This->parentStorage = NULL;
4342
4343     StorageBaseImpl_DeleteAll(&This->base);
4344
4345     list_remove(&This->ParentListEntry);
4346   }
4347 }
4348
4349 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4350 {
4351   StorageInternalImpl* This = (StorageInternalImpl*) iface;
4352
4353   StorageInternalImpl_Invalidate(&This->base);
4354
4355   HeapFree(GetProcessHeap(), 0, This);
4356 }
4357
4358 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4359   const DirEntry *newData, DirRef *index)
4360 {
4361   StorageInternalImpl* This = (StorageInternalImpl*) base;
4362
4363   return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4364     newData, index);
4365 }
4366
4367 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4368   DirRef index, const DirEntry *data)
4369 {
4370   StorageInternalImpl* This = (StorageInternalImpl*) base;
4371
4372   return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4373     index, data);
4374 }
4375
4376 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4377   DirRef index, DirEntry *data)
4378 {
4379   StorageInternalImpl* This = (StorageInternalImpl*) base;
4380
4381   return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4382     index, data);
4383 }
4384
4385 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4386   DirRef index)
4387 {
4388   StorageInternalImpl* This = (StorageInternalImpl*) base;
4389
4390   return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4391     index);
4392 }
4393
4394 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4395   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4396 {
4397   StorageInternalImpl* This = (StorageInternalImpl*) base;
4398
4399   return StorageBaseImpl_StreamReadAt(This->parentStorage,
4400     index, offset, size, buffer, bytesRead);
4401 }
4402
4403 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4404   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4405 {
4406   StorageInternalImpl* This = (StorageInternalImpl*) base;
4407
4408   return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4409     index, offset, size, buffer, bytesWritten);
4410 }
4411
4412 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4413   DirRef index, ULARGE_INTEGER newsize)
4414 {
4415   StorageInternalImpl* This = (StorageInternalImpl*) base;
4416
4417   return StorageBaseImpl_StreamSetSize(This->parentStorage,
4418     index, newsize);
4419 }
4420
4421 /******************************************************************************
4422 **
4423 ** Storage32InternalImpl_Commit
4424 **
4425 */
4426 static HRESULT WINAPI StorageInternalImpl_Commit(
4427   IStorage*            iface,
4428   DWORD                  grfCommitFlags)  /* [in] */
4429 {
4430   FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4431   return S_OK;
4432 }
4433
4434 /******************************************************************************
4435 **
4436 ** Storage32InternalImpl_Revert
4437 **
4438 */
4439 static HRESULT WINAPI StorageInternalImpl_Revert(
4440   IStorage*            iface)
4441 {
4442   FIXME("(%p): stub\n", iface);
4443   return S_OK;
4444 }
4445
4446 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4447 {
4448   IStorage_Release((IStorage*)This->parentStorage);
4449   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
4450   HeapFree(GetProcessHeap(), 0, This);
4451 }
4452
4453 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4454   IEnumSTATSTG*     iface,
4455   REFIID            riid,
4456   void**            ppvObject)
4457 {
4458   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4459
4460   if (ppvObject==0)
4461     return E_INVALIDARG;
4462
4463   *ppvObject = 0;
4464
4465   if (IsEqualGUID(&IID_IUnknown, riid) ||
4466       IsEqualGUID(&IID_IEnumSTATSTG, riid))
4467   {
4468     *ppvObject = This;
4469     IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4470     return S_OK;
4471   }
4472
4473   return E_NOINTERFACE;
4474 }
4475
4476 static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
4477   IEnumSTATSTG* iface)
4478 {
4479   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4480   return InterlockedIncrement(&This->ref);
4481 }
4482
4483 static ULONG   WINAPI IEnumSTATSTGImpl_Release(
4484   IEnumSTATSTG* iface)
4485 {
4486   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4487
4488   ULONG newRef;
4489
4490   newRef = InterlockedDecrement(&This->ref);
4491
4492   if (newRef==0)
4493   {
4494     IEnumSTATSTGImpl_Destroy(This);
4495   }
4496
4497   return newRef;
4498 }
4499
4500 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4501   IEnumSTATSTG* iface,
4502   ULONG             celt,
4503   STATSTG*          rgelt,
4504   ULONG*            pceltFetched)
4505 {
4506   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4507
4508   DirEntry    currentEntry;
4509   STATSTG*    currentReturnStruct = rgelt;
4510   ULONG       objectFetched       = 0;
4511   DirRef      currentSearchNode;
4512
4513   if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4514     return E_INVALIDARG;
4515
4516   /*
4517    * To avoid the special case, get another pointer to a ULONG value if
4518    * the caller didn't supply one.
4519    */
4520   if (pceltFetched==0)
4521     pceltFetched = &objectFetched;
4522
4523   /*
4524    * Start the iteration, we will iterate until we hit the end of the
4525    * linked list or until we hit the number of items to iterate through
4526    */
4527   *pceltFetched = 0;
4528
4529   /*
4530    * Start with the node at the top of the stack.
4531    */
4532   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4533
4534   while ( ( *pceltFetched < celt) &&
4535           ( currentSearchNode!=DIRENTRY_NULL) )
4536   {
4537     /*
4538      * Remove the top node from the stack
4539      */
4540     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4541
4542     /*
4543      * Read the entry from the storage.
4544      */
4545     StorageBaseImpl_ReadDirEntry(This->parentStorage,
4546       currentSearchNode,
4547       &currentEntry);
4548
4549     /*
4550      * Copy the information to the return buffer.
4551      */
4552     StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4553       currentReturnStruct,
4554       &currentEntry,
4555       STATFLAG_DEFAULT);
4556
4557     /*
4558      * Step to the next item in the iteration
4559      */
4560     (*pceltFetched)++;
4561     currentReturnStruct++;
4562
4563     /*
4564      * Push the next search node in the search stack.
4565      */
4566     IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4567
4568     /*
4569      * continue the iteration.
4570      */
4571     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4572   }
4573
4574   if (*pceltFetched == celt)
4575     return S_OK;
4576
4577   return S_FALSE;
4578 }
4579
4580
4581 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4582   IEnumSTATSTG* iface,
4583   ULONG             celt)
4584 {
4585   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4586
4587   DirEntry    currentEntry;
4588   ULONG       objectFetched       = 0;
4589   DirRef      currentSearchNode;
4590
4591   /*
4592    * Start with the node at the top of the stack.
4593    */
4594   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4595
4596   while ( (objectFetched < celt) &&
4597           (currentSearchNode!=DIRENTRY_NULL) )
4598   {
4599     /*
4600      * Remove the top node from the stack
4601      */
4602     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4603
4604     /*
4605      * Read the entry from the storage.
4606      */
4607     StorageBaseImpl_ReadDirEntry(This->parentStorage,
4608       currentSearchNode,
4609       &currentEntry);
4610
4611     /*
4612      * Step to the next item in the iteration
4613      */
4614     objectFetched++;
4615
4616     /*
4617      * Push the next search node in the search stack.
4618      */
4619     IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4620
4621     /*
4622      * continue the iteration.
4623      */
4624     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4625   }
4626
4627   if (objectFetched == celt)
4628     return S_OK;
4629
4630   return S_FALSE;
4631 }
4632
4633 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4634   IEnumSTATSTG* iface)
4635 {
4636   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4637
4638   DirEntry  storageEntry;
4639   HRESULT   hr;
4640
4641   /*
4642    * Re-initialize the search stack to an empty stack
4643    */
4644   This->stackSize = 0;
4645
4646   /*
4647    * Read the storage entry from the top-level storage.
4648    */
4649   hr = StorageBaseImpl_ReadDirEntry(
4650                     This->parentStorage,
4651                     This->storageDirEntry,
4652                     &storageEntry);
4653
4654   if (SUCCEEDED(hr))
4655   {
4656     assert(storageEntry.sizeOfNameString!=0);
4657
4658     /*
4659      * Push the search node in the search stack.
4660      */
4661     IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
4662   }
4663
4664   return hr;
4665 }
4666
4667 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4668   IEnumSTATSTG* iface,
4669   IEnumSTATSTG**    ppenum)
4670 {
4671   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4672
4673   IEnumSTATSTGImpl* newClone;
4674
4675   /*
4676    * Perform a sanity check on the parameters.
4677    */
4678   if (ppenum==0)
4679     return E_INVALIDARG;
4680
4681   newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4682                This->storageDirEntry);
4683
4684
4685   /*
4686    * The new clone enumeration must point to the same current node as
4687    * the ole one.
4688    */
4689   newClone->stackSize    = This->stackSize    ;
4690   newClone->stackMaxSize = This->stackMaxSize ;
4691   newClone->stackToVisit =
4692     HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4693
4694   memcpy(
4695     newClone->stackToVisit,
4696     This->stackToVisit,
4697     sizeof(DirRef) * newClone->stackSize);
4698
4699   *ppenum = (IEnumSTATSTG*)newClone;
4700
4701   /*
4702    * Don't forget to nail down a reference to the clone before
4703    * returning it.
4704    */
4705   IEnumSTATSTGImpl_AddRef(*ppenum);
4706
4707   return S_OK;
4708 }
4709
4710 static void IEnumSTATSTGImpl_PushSearchNode(
4711   IEnumSTATSTGImpl* This,
4712   DirRef            nodeToPush)
4713 {
4714   DirEntry  storageEntry;
4715   HRESULT   hr;
4716
4717   /*
4718    * First, make sure we're not trying to push an unexisting node.
4719    */
4720   if (nodeToPush==DIRENTRY_NULL)
4721     return;
4722
4723   /*
4724    * First push the node to the stack
4725    */
4726   if (This->stackSize == This->stackMaxSize)
4727   {
4728     This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4729
4730     This->stackToVisit = HeapReAlloc(
4731                            GetProcessHeap(),
4732                            0,
4733                            This->stackToVisit,
4734                            sizeof(DirRef) * This->stackMaxSize);
4735   }
4736
4737   This->stackToVisit[This->stackSize] = nodeToPush;
4738   This->stackSize++;
4739
4740   /*
4741    * Read the storage entry from the top-level storage.
4742    */
4743   hr = StorageBaseImpl_ReadDirEntry(
4744                     This->parentStorage,
4745                     nodeToPush,
4746                     &storageEntry);
4747
4748   if (SUCCEEDED(hr))
4749   {
4750     assert(storageEntry.sizeOfNameString!=0);
4751
4752     /*
4753      * Push the previous search node in the search stack.
4754      */
4755     IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
4756   }
4757 }
4758
4759 static DirRef IEnumSTATSTGImpl_PopSearchNode(
4760   IEnumSTATSTGImpl* This,
4761   BOOL            remove)
4762 {
4763   DirRef topNode;
4764
4765   if (This->stackSize == 0)
4766     return DIRENTRY_NULL;
4767
4768   topNode = This->stackToVisit[This->stackSize-1];
4769
4770   if (remove)
4771     This->stackSize--;
4772
4773   return topNode;
4774 }
4775
4776 /*
4777  * Virtual function table for the IEnumSTATSTGImpl class.
4778  */
4779 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4780 {
4781     IEnumSTATSTGImpl_QueryInterface,
4782     IEnumSTATSTGImpl_AddRef,
4783     IEnumSTATSTGImpl_Release,
4784     IEnumSTATSTGImpl_Next,
4785     IEnumSTATSTGImpl_Skip,
4786     IEnumSTATSTGImpl_Reset,
4787     IEnumSTATSTGImpl_Clone
4788 };
4789
4790 /******************************************************************************
4791 ** IEnumSTATSTGImpl implementation
4792 */
4793
4794 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4795   StorageBaseImpl* parentStorage,
4796   DirRef         storageDirEntry)
4797 {
4798   IEnumSTATSTGImpl* newEnumeration;
4799
4800   newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4801
4802   if (newEnumeration!=0)
4803   {
4804     /*
4805      * Set-up the virtual function table and reference count.
4806      */
4807     newEnumeration->lpVtbl    = &IEnumSTATSTGImpl_Vtbl;
4808     newEnumeration->ref       = 0;
4809
4810     /*
4811      * We want to nail-down the reference to the storage in case the
4812      * enumeration out-lives the storage in the client application.
4813      */
4814     newEnumeration->parentStorage = parentStorage;
4815     IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4816
4817     newEnumeration->storageDirEntry   = storageDirEntry;
4818
4819     /*
4820      * Initialize the search stack
4821      */
4822     newEnumeration->stackSize    = 0;
4823     newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4824     newEnumeration->stackToVisit =
4825       HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4826
4827     /*
4828      * Make sure the current node of the iterator is the first one.
4829      */
4830     IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4831   }
4832
4833   return newEnumeration;
4834 }
4835
4836 /*
4837  * Virtual function table for the Storage32InternalImpl class.
4838  */
4839 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4840 {
4841     StorageBaseImpl_QueryInterface,
4842     StorageBaseImpl_AddRef,
4843     StorageBaseImpl_Release,
4844     StorageBaseImpl_CreateStream,
4845     StorageBaseImpl_OpenStream,
4846     StorageBaseImpl_CreateStorage,
4847     StorageBaseImpl_OpenStorage,
4848     StorageBaseImpl_CopyTo,
4849     StorageBaseImpl_MoveElementTo,
4850     StorageInternalImpl_Commit,
4851     StorageInternalImpl_Revert,
4852     StorageBaseImpl_EnumElements,
4853     StorageBaseImpl_DestroyElement,
4854     StorageBaseImpl_RenameElement,
4855     StorageBaseImpl_SetElementTimes,
4856     StorageBaseImpl_SetClass,
4857     StorageBaseImpl_SetStateBits,
4858     StorageBaseImpl_Stat
4859 };
4860
4861 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4862 {
4863   StorageInternalImpl_Destroy,
4864   StorageInternalImpl_Invalidate,
4865   StorageInternalImpl_CreateDirEntry,
4866   StorageInternalImpl_WriteDirEntry,
4867   StorageInternalImpl_ReadDirEntry,
4868   StorageInternalImpl_DestroyDirEntry,
4869   StorageInternalImpl_StreamReadAt,
4870   StorageInternalImpl_StreamWriteAt,
4871   StorageInternalImpl_StreamSetSize
4872 };
4873
4874 /******************************************************************************
4875 ** Storage32InternalImpl implementation
4876 */
4877
4878 static StorageInternalImpl* StorageInternalImpl_Construct(
4879   StorageBaseImpl* parentStorage,
4880   DWORD        openFlags,
4881   DirRef       storageDirEntry)
4882 {
4883   StorageInternalImpl* newStorage;
4884
4885   newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4886
4887   if (newStorage!=0)
4888   {
4889     list_init(&newStorage->base.strmHead);
4890
4891     list_init(&newStorage->base.storageHead);
4892
4893     /*
4894      * Initialize the virtual function table.
4895      */
4896     newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4897     newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4898     newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4899
4900     newStorage->base.reverted = 0;
4901
4902     newStorage->base.ref = 1;
4903
4904     newStorage->parentStorage = parentStorage;
4905
4906     /*
4907      * Keep a reference to the directory entry of this storage
4908      */
4909     newStorage->base.storageDirEntry = storageDirEntry;
4910
4911     newStorage->base.create = 0;
4912
4913     return newStorage;
4914   }
4915
4916   return 0;
4917 }
4918
4919 /******************************************************************************
4920 ** StorageUtl implementation
4921 */
4922
4923 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4924 {
4925   WORD tmp;
4926
4927   memcpy(&tmp, buffer+offset, sizeof(WORD));
4928   *value = lendian16toh(tmp);
4929 }
4930
4931 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4932 {
4933   value = htole16(value);
4934   memcpy(buffer+offset, &value, sizeof(WORD));
4935 }
4936
4937 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4938 {
4939   DWORD tmp;
4940
4941   memcpy(&tmp, buffer+offset, sizeof(DWORD));
4942   *value = lendian32toh(tmp);
4943 }
4944
4945 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4946 {
4947   value = htole32(value);
4948   memcpy(buffer+offset, &value, sizeof(DWORD));
4949 }
4950
4951 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4952  ULARGE_INTEGER* value)
4953 {
4954 #ifdef WORDS_BIGENDIAN
4955     ULARGE_INTEGER tmp;
4956
4957     memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4958     value->u.LowPart = htole32(tmp.u.HighPart);
4959     value->u.HighPart = htole32(tmp.u.LowPart);
4960 #else
4961     memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4962 #endif
4963 }
4964
4965 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4966  const ULARGE_INTEGER *value)
4967 {
4968 #ifdef WORDS_BIGENDIAN
4969     ULARGE_INTEGER tmp;
4970
4971     tmp.u.LowPart = htole32(value->u.HighPart);
4972     tmp.u.HighPart = htole32(value->u.LowPart);
4973     memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4974 #else
4975     memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4976 #endif
4977 }
4978
4979 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4980 {
4981   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
4982   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
4983   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
4984
4985   memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4986 }
4987
4988 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4989 {
4990   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
4991   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
4992   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
4993
4994   memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4995 }
4996
4997 void StorageUtl_CopyDirEntryToSTATSTG(
4998   StorageBaseImpl*      storage,
4999   STATSTG*              destination,
5000   const DirEntry*       source,
5001   int                   statFlags)
5002 {
5003   LPCWSTR entryName;
5004
5005   if (source->stgType == STGTY_ROOT)
5006   {
5007     /* replace the name of root entry (often "Root Entry") by the file name */
5008     entryName = storage->filename;
5009   }
5010   else
5011   {
5012     entryName = source->name;
5013   }
5014
5015   /*
5016    * The copy of the string occurs only when the flag is not set
5017    */
5018   if( ((statFlags & STATFLAG_NONAME) != 0) || 
5019        (entryName == NULL) ||
5020        (entryName[0] == 0) )
5021   {
5022     destination->pwcsName = 0;
5023   }
5024   else
5025   {
5026     destination->pwcsName =
5027       CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
5028
5029     strcpyW(destination->pwcsName, entryName);
5030   }
5031
5032   switch (source->stgType)
5033   {
5034     case STGTY_STORAGE:
5035     case STGTY_ROOT:
5036       destination->type = STGTY_STORAGE;
5037       break;
5038     case STGTY_STREAM:
5039       destination->type = STGTY_STREAM;
5040       break;
5041     default:
5042       destination->type = STGTY_STREAM;
5043       break;
5044   }
5045
5046   destination->cbSize            = source->size;
5047 /*
5048   currentReturnStruct->mtime     = {0}; TODO
5049   currentReturnStruct->ctime     = {0};
5050   currentReturnStruct->atime     = {0};
5051 */
5052   destination->grfMode           = 0;
5053   destination->grfLocksSupported = 0;
5054   destination->clsid             = source->clsid;
5055   destination->grfStateBits      = 0;
5056   destination->reserved          = 0;
5057 }
5058
5059 /******************************************************************************
5060 ** BlockChainStream implementation
5061 */
5062
5063 BlockChainStream* BlockChainStream_Construct(
5064   StorageImpl* parentStorage,
5065   ULONG*         headOfStreamPlaceHolder,
5066   DirRef         dirEntry)
5067 {
5068   BlockChainStream* newStream;
5069   ULONG blockIndex;
5070
5071   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5072
5073   newStream->parentStorage           = parentStorage;
5074   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5075   newStream->ownerDirEntry           = dirEntry;
5076   newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
5077   newStream->tailIndex               = BLOCK_END_OF_CHAIN;
5078   newStream->numBlocks               = 0;
5079
5080   blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5081
5082   while (blockIndex != BLOCK_END_OF_CHAIN)
5083   {
5084     newStream->numBlocks++;
5085     newStream->tailIndex = blockIndex;
5086
5087     if(FAILED(StorageImpl_GetNextBlockInChain(
5088               parentStorage,
5089               blockIndex,
5090               &blockIndex)))
5091     {
5092       HeapFree(GetProcessHeap(), 0, newStream);
5093       return NULL;
5094     }
5095   }
5096
5097   return newStream;
5098 }
5099
5100 void BlockChainStream_Destroy(BlockChainStream* This)
5101 {
5102   HeapFree(GetProcessHeap(), 0, This);
5103 }
5104
5105 /******************************************************************************
5106  *      BlockChainStream_GetHeadOfChain
5107  *
5108  * Returns the head of this stream chain.
5109  * Some special chains don't have directory entries, their heads are kept in
5110  * This->headOfStreamPlaceHolder.
5111  *
5112  */
5113 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5114 {
5115   DirEntry  chainEntry;
5116   HRESULT   hr;
5117
5118   if (This->headOfStreamPlaceHolder != 0)
5119     return *(This->headOfStreamPlaceHolder);
5120
5121   if (This->ownerDirEntry != DIRENTRY_NULL)
5122   {
5123     hr = StorageImpl_ReadDirEntry(
5124                       This->parentStorage,
5125                       This->ownerDirEntry,
5126                       &chainEntry);
5127
5128     if (SUCCEEDED(hr))
5129     {
5130       return chainEntry.startingBlock;
5131     }
5132   }
5133
5134   return BLOCK_END_OF_CHAIN;
5135 }
5136
5137 /******************************************************************************
5138  *       BlockChainStream_GetCount
5139  *
5140  * Returns the number of blocks that comprises this chain.
5141  * This is not the size of the stream as the last block may not be full!
5142  *
5143  */
5144 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5145 {
5146   ULONG blockIndex;
5147   ULONG count = 0;
5148
5149   blockIndex = BlockChainStream_GetHeadOfChain(This);
5150
5151   while (blockIndex != BLOCK_END_OF_CHAIN)
5152   {
5153     count++;
5154
5155     if(FAILED(StorageImpl_GetNextBlockInChain(
5156                    This->parentStorage,
5157                    blockIndex,
5158                    &blockIndex)))
5159       return 0;
5160   }
5161
5162   return count;
5163 }
5164
5165 /******************************************************************************
5166  *      BlockChainStream_ReadAt
5167  *
5168  * Reads a specified number of bytes from this chain at the specified offset.
5169  * bytesRead may be NULL.
5170  * Failure will be returned if the specified number of bytes has not been read.
5171  */
5172 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5173   ULARGE_INTEGER offset,
5174   ULONG          size,
5175   void*          buffer,
5176   ULONG*         bytesRead)
5177 {
5178   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5179   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
5180   ULONG bytesToReadInBuffer;
5181   ULONG blockIndex;
5182   BYTE* bufferWalker;
5183
5184   TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5185
5186   /*
5187    * Find the first block in the stream that contains part of the buffer.
5188    */
5189   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5190        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5191        (blockNoInSequence < This->lastBlockNoInSequence) )
5192   {
5193     blockIndex = BlockChainStream_GetHeadOfChain(This);
5194     This->lastBlockNoInSequence = blockNoInSequence;
5195   }
5196   else
5197   {
5198     ULONG temp = blockNoInSequence;
5199
5200     blockIndex = This->lastBlockNoInSequenceIndex;
5201     blockNoInSequence -= This->lastBlockNoInSequence;
5202     This->lastBlockNoInSequence = temp;
5203   }
5204
5205   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5206   {
5207     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5208       return STG_E_DOCFILECORRUPT;
5209     blockNoInSequence--;
5210   }
5211
5212   if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5213       return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5214
5215   This->lastBlockNoInSequenceIndex = blockIndex;
5216
5217   /*
5218    * Start reading the buffer.
5219    */
5220   *bytesRead   = 0;
5221   bufferWalker = buffer;
5222
5223   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5224   {
5225     ULARGE_INTEGER ulOffset;
5226     DWORD bytesReadAt;
5227     /*
5228      * Calculate how many bytes we can copy from this big block.
5229      */
5230     bytesToReadInBuffer =
5231       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5232
5233      TRACE("block %i\n",blockIndex);
5234      ulOffset.u.HighPart = 0;
5235      ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5236                              offsetInBlock;
5237
5238      StorageImpl_ReadAt(This->parentStorage,
5239          ulOffset,
5240          bufferWalker,
5241          bytesToReadInBuffer,
5242          &bytesReadAt);
5243     /*
5244      * Step to the next big block.
5245      */
5246     if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5247       return STG_E_DOCFILECORRUPT;
5248
5249     bufferWalker += bytesReadAt;
5250     size         -= bytesReadAt;
5251     *bytesRead   += bytesReadAt;
5252     offsetInBlock = 0;  /* There is no offset on the next block */
5253
5254     if (bytesToReadInBuffer != bytesReadAt)
5255         break;
5256   }
5257
5258   return (size == 0) ? S_OK : STG_E_READFAULT;
5259 }
5260
5261 /******************************************************************************
5262  *      BlockChainStream_WriteAt
5263  *
5264  * Writes the specified number of bytes to this chain at the specified offset.
5265  * Will fail if not all specified number of bytes have been written.
5266  */
5267 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5268   ULARGE_INTEGER    offset,
5269   ULONG             size,
5270   const void*       buffer,
5271   ULONG*            bytesWritten)
5272 {
5273   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5274   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
5275   ULONG bytesToWrite;
5276   ULONG blockIndex;
5277   const BYTE* bufferWalker;
5278
5279   /*
5280    * Find the first block in the stream that contains part of the buffer.
5281    */
5282   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5283        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5284        (blockNoInSequence < This->lastBlockNoInSequence) )
5285   {
5286     blockIndex = BlockChainStream_GetHeadOfChain(This);
5287     This->lastBlockNoInSequence = blockNoInSequence;
5288   }
5289   else
5290   {
5291     ULONG temp = blockNoInSequence;
5292
5293     blockIndex = This->lastBlockNoInSequenceIndex;
5294     blockNoInSequence -= This->lastBlockNoInSequence;
5295     This->lastBlockNoInSequence = temp;
5296   }
5297
5298   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5299   {
5300     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5301                                               &blockIndex)))
5302       return STG_E_DOCFILECORRUPT;
5303     blockNoInSequence--;
5304   }
5305
5306   This->lastBlockNoInSequenceIndex = blockIndex;
5307
5308   /* BlockChainStream_SetSize should have already been called to ensure we have
5309    * enough blocks in the chain to write into */
5310   if (blockIndex == BLOCK_END_OF_CHAIN)
5311   {
5312     ERR("not enough blocks in chain to write data\n");
5313     return STG_E_DOCFILECORRUPT;
5314   }
5315
5316   *bytesWritten   = 0;
5317   bufferWalker = buffer;
5318
5319   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5320   {
5321     ULARGE_INTEGER ulOffset;
5322     DWORD bytesWrittenAt;
5323     /*
5324      * Calculate how many bytes we can copy from this big block.
5325      */
5326     bytesToWrite =
5327       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5328
5329     TRACE("block %i\n",blockIndex);
5330     ulOffset.u.HighPart = 0;
5331     ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5332                              offsetInBlock;
5333
5334     StorageImpl_WriteAt(This->parentStorage,
5335          ulOffset,
5336          bufferWalker,
5337          bytesToWrite,
5338          &bytesWrittenAt);
5339
5340     /*
5341      * Step to the next big block.
5342      */
5343     if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5344                                               &blockIndex)))
5345       return STG_E_DOCFILECORRUPT;
5346
5347     bufferWalker  += bytesWrittenAt;
5348     size          -= bytesWrittenAt;
5349     *bytesWritten += bytesWrittenAt;
5350     offsetInBlock  = 0;      /* There is no offset on the next block */
5351
5352     if (bytesWrittenAt != bytesToWrite)
5353       break;
5354   }
5355
5356   return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5357 }
5358
5359 /******************************************************************************
5360  *      BlockChainStream_Shrink
5361  *
5362  * Shrinks this chain in the big block depot.
5363  */
5364 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5365                                     ULARGE_INTEGER    newSize)
5366 {
5367   ULONG blockIndex, extraBlock;
5368   ULONG numBlocks;
5369   ULONG count = 1;
5370
5371   /*
5372    * Reset the last accessed block cache.
5373    */
5374   This->lastBlockNoInSequence = 0xFFFFFFFF;
5375   This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5376
5377   /*
5378    * Figure out how many blocks are needed to contain the new size
5379    */
5380   numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5381
5382   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5383     numBlocks++;
5384
5385   blockIndex = BlockChainStream_GetHeadOfChain(This);
5386
5387   /*
5388    * Go to the new end of chain
5389    */
5390   while (count < numBlocks)
5391   {
5392     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5393                                               &blockIndex)))
5394       return FALSE;
5395     count++;
5396   }
5397
5398   /* Get the next block before marking the new end */
5399   if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5400                                             &extraBlock)))
5401     return FALSE;
5402
5403   /* Mark the new end of chain */
5404   StorageImpl_SetNextBlockInChain(
5405     This->parentStorage,
5406     blockIndex,
5407     BLOCK_END_OF_CHAIN);
5408
5409   This->tailIndex = blockIndex;
5410   This->numBlocks = numBlocks;
5411
5412   /*
5413    * Mark the extra blocks as free
5414    */
5415   while (extraBlock != BLOCK_END_OF_CHAIN)
5416   {
5417     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5418                                               &blockIndex)))
5419       return FALSE;
5420     StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5421     extraBlock = blockIndex;
5422   }
5423
5424   return TRUE;
5425 }
5426
5427 /******************************************************************************
5428  *      BlockChainStream_Enlarge
5429  *
5430  * Grows this chain in the big block depot.
5431  */
5432 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5433                                      ULARGE_INTEGER    newSize)
5434 {
5435   ULONG blockIndex, currentBlock;
5436   ULONG newNumBlocks;
5437   ULONG oldNumBlocks = 0;
5438
5439   blockIndex = BlockChainStream_GetHeadOfChain(This);
5440
5441   /*
5442    * Empty chain. Create the head.
5443    */
5444   if (blockIndex == BLOCK_END_OF_CHAIN)
5445   {
5446     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5447     StorageImpl_SetNextBlockInChain(This->parentStorage,
5448                                       blockIndex,
5449                                       BLOCK_END_OF_CHAIN);
5450
5451     if (This->headOfStreamPlaceHolder != 0)
5452     {
5453       *(This->headOfStreamPlaceHolder) = blockIndex;
5454     }
5455     else
5456     {
5457       DirEntry chainEntry;
5458       assert(This->ownerDirEntry != DIRENTRY_NULL);
5459
5460       StorageImpl_ReadDirEntry(
5461         This->parentStorage,
5462         This->ownerDirEntry,
5463         &chainEntry);
5464
5465       chainEntry.startingBlock = blockIndex;
5466
5467       StorageImpl_WriteDirEntry(
5468         This->parentStorage,
5469         This->ownerDirEntry,
5470         &chainEntry);
5471     }
5472
5473     This->tailIndex = blockIndex;
5474     This->numBlocks = 1;
5475   }
5476
5477   /*
5478    * Figure out how many blocks are needed to contain this stream
5479    */
5480   newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5481
5482   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5483     newNumBlocks++;
5484
5485   /*
5486    * Go to the current end of chain
5487    */
5488   if (This->tailIndex == BLOCK_END_OF_CHAIN)
5489   {
5490     currentBlock = blockIndex;
5491
5492     while (blockIndex != BLOCK_END_OF_CHAIN)
5493     {
5494       This->numBlocks++;
5495       currentBlock = blockIndex;
5496
5497       if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5498                                                 &blockIndex)))
5499         return FALSE;
5500     }
5501
5502     This->tailIndex = currentBlock;
5503   }
5504
5505   currentBlock = This->tailIndex;
5506   oldNumBlocks = This->numBlocks;
5507
5508   /*
5509    * Add new blocks to the chain
5510    */
5511   if (oldNumBlocks < newNumBlocks)
5512   {
5513     while (oldNumBlocks < newNumBlocks)
5514     {
5515       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5516
5517       StorageImpl_SetNextBlockInChain(
5518         This->parentStorage,
5519         currentBlock,
5520         blockIndex);
5521
5522       StorageImpl_SetNextBlockInChain(
5523         This->parentStorage,
5524         blockIndex,
5525         BLOCK_END_OF_CHAIN);
5526
5527       currentBlock = blockIndex;
5528       oldNumBlocks++;
5529     }
5530
5531     This->tailIndex = blockIndex;
5532     This->numBlocks = newNumBlocks;
5533   }
5534
5535   return TRUE;
5536 }
5537
5538 /******************************************************************************
5539  *      BlockChainStream_SetSize
5540  *
5541  * Sets the size of this stream. The big block depot will be updated.
5542  * The file will grow if we grow the chain.
5543  *
5544  * TODO: Free the actual blocks in the file when we shrink the chain.
5545  *       Currently, the blocks are still in the file. So the file size
5546  *       doesn't shrink even if we shrink streams.
5547  */
5548 BOOL BlockChainStream_SetSize(
5549   BlockChainStream* This,
5550   ULARGE_INTEGER    newSize)
5551 {
5552   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5553
5554   if (newSize.u.LowPart == size.u.LowPart)
5555     return TRUE;
5556
5557   if (newSize.u.LowPart < size.u.LowPart)
5558   {
5559     BlockChainStream_Shrink(This, newSize);
5560   }
5561   else
5562   {
5563     BlockChainStream_Enlarge(This, newSize);
5564   }
5565
5566   return TRUE;
5567 }
5568
5569 /******************************************************************************
5570  *      BlockChainStream_GetSize
5571  *
5572  * Returns the size of this chain.
5573  * Will return the block count if this chain doesn't have a directory entry.
5574  */
5575 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5576 {
5577   DirEntry chainEntry;
5578
5579   if(This->headOfStreamPlaceHolder == NULL)
5580   {
5581     /*
5582      * This chain has a directory entry so use the size value from there.
5583      */
5584     StorageImpl_ReadDirEntry(
5585       This->parentStorage,
5586       This->ownerDirEntry,
5587       &chainEntry);
5588
5589     return chainEntry.size;
5590   }
5591   else
5592   {
5593     /*
5594      * this chain is a chain that does not have a directory entry, figure out the
5595      * size by making the product number of used blocks times the
5596      * size of them
5597      */
5598     ULARGE_INTEGER result;
5599     result.u.HighPart = 0;
5600
5601     result.u.LowPart  =
5602       BlockChainStream_GetCount(This) *
5603       This->parentStorage->bigBlockSize;
5604
5605     return result;
5606   }
5607 }
5608
5609 /******************************************************************************
5610 ** SmallBlockChainStream implementation
5611 */
5612
5613 SmallBlockChainStream* SmallBlockChainStream_Construct(
5614   StorageImpl* parentStorage,
5615   ULONG*         headOfStreamPlaceHolder,
5616   DirRef         dirEntry)
5617 {
5618   SmallBlockChainStream* newStream;
5619
5620   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5621
5622   newStream->parentStorage      = parentStorage;
5623   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5624   newStream->ownerDirEntry      = dirEntry;
5625
5626   return newStream;
5627 }
5628
5629 void SmallBlockChainStream_Destroy(
5630   SmallBlockChainStream* This)
5631 {
5632   HeapFree(GetProcessHeap(), 0, This);
5633 }
5634
5635 /******************************************************************************
5636  *      SmallBlockChainStream_GetHeadOfChain
5637  *
5638  * Returns the head of this chain of small blocks.
5639  */
5640 static ULONG SmallBlockChainStream_GetHeadOfChain(
5641   SmallBlockChainStream* This)
5642 {
5643   DirEntry  chainEntry;
5644   HRESULT   hr;
5645
5646   if (This->headOfStreamPlaceHolder != NULL)
5647     return *(This->headOfStreamPlaceHolder);
5648
5649   if (This->ownerDirEntry)
5650   {
5651     hr = StorageImpl_ReadDirEntry(
5652                       This->parentStorage,
5653                       This->ownerDirEntry,
5654                       &chainEntry);
5655
5656     if (SUCCEEDED(hr))
5657     {
5658       return chainEntry.startingBlock;
5659     }
5660
5661   }
5662
5663   return BLOCK_END_OF_CHAIN;
5664 }
5665
5666 /******************************************************************************
5667  *      SmallBlockChainStream_GetNextBlockInChain
5668  *
5669  * Returns the index of the next small block in this chain.
5670  *
5671  * Return Values:
5672  *    - BLOCK_END_OF_CHAIN: end of this chain
5673  *    - BLOCK_UNUSED: small block 'blockIndex' is free
5674  */
5675 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5676   SmallBlockChainStream* This,
5677   ULONG                  blockIndex,
5678   ULONG*                 nextBlockInChain)
5679 {
5680   ULARGE_INTEGER offsetOfBlockInDepot;
5681   DWORD  buffer;
5682   ULONG  bytesRead;
5683   HRESULT res;
5684
5685   *nextBlockInChain = BLOCK_END_OF_CHAIN;
5686
5687   offsetOfBlockInDepot.u.HighPart = 0;
5688   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
5689
5690   /*
5691    * Read those bytes in the buffer from the small block file.
5692    */
5693   res = BlockChainStream_ReadAt(
5694               This->parentStorage->smallBlockDepotChain,
5695               offsetOfBlockInDepot,
5696               sizeof(DWORD),
5697               &buffer,
5698               &bytesRead);
5699
5700   if (SUCCEEDED(res))
5701   {
5702     StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5703     return S_OK;
5704   }
5705
5706   return res;
5707 }
5708
5709 /******************************************************************************
5710  *       SmallBlockChainStream_SetNextBlockInChain
5711  *
5712  * Writes the index of the next block of the specified block in the small
5713  * block depot.
5714  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5715  * To flag a block as free use BLOCK_UNUSED as nextBlock.
5716  */
5717 static void SmallBlockChainStream_SetNextBlockInChain(
5718   SmallBlockChainStream* This,
5719   ULONG                  blockIndex,
5720   ULONG                  nextBlock)
5721 {
5722   ULARGE_INTEGER offsetOfBlockInDepot;
5723   DWORD  buffer;
5724   ULONG  bytesWritten;
5725
5726   offsetOfBlockInDepot.u.HighPart = 0;
5727   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
5728
5729   StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5730
5731   /*
5732    * Read those bytes in the buffer from the small block file.
5733    */
5734   BlockChainStream_WriteAt(
5735     This->parentStorage->smallBlockDepotChain,
5736     offsetOfBlockInDepot,
5737     sizeof(DWORD),
5738     &buffer,
5739     &bytesWritten);
5740 }
5741
5742 /******************************************************************************
5743  *      SmallBlockChainStream_FreeBlock
5744  *
5745  * Flag small block 'blockIndex' as free in the small block depot.
5746  */
5747 static void SmallBlockChainStream_FreeBlock(
5748   SmallBlockChainStream* This,
5749   ULONG                  blockIndex)
5750 {
5751   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5752 }
5753
5754 /******************************************************************************
5755  *      SmallBlockChainStream_GetNextFreeBlock
5756  *
5757  * Returns the index of a free small block. The small block depot will be
5758  * enlarged if necessary. The small block chain will also be enlarged if
5759  * necessary.
5760  */
5761 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5762   SmallBlockChainStream* This)
5763 {
5764   ULARGE_INTEGER offsetOfBlockInDepot;
5765   DWORD buffer;
5766   ULONG bytesRead;
5767   ULONG blockIndex = 0;
5768   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5769   HRESULT res = S_OK;
5770   ULONG smallBlocksPerBigBlock;
5771
5772   offsetOfBlockInDepot.u.HighPart = 0;
5773
5774   /*
5775    * Scan the small block depot for a free block
5776    */
5777   while (nextBlockIndex != BLOCK_UNUSED)
5778   {
5779     offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5780
5781     res = BlockChainStream_ReadAt(
5782                 This->parentStorage->smallBlockDepotChain,
5783                 offsetOfBlockInDepot,
5784                 sizeof(DWORD),
5785                 &buffer,
5786                 &bytesRead);
5787
5788     /*
5789      * If we run out of space for the small block depot, enlarge it
5790      */
5791     if (SUCCEEDED(res))
5792     {
5793       StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5794
5795       if (nextBlockIndex != BLOCK_UNUSED)
5796         blockIndex++;
5797     }
5798     else
5799     {
5800       ULONG count =
5801         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5802
5803       ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5804       ULONG nextBlock, newsbdIndex;
5805       BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5806
5807       nextBlock = sbdIndex;
5808       while (nextBlock != BLOCK_END_OF_CHAIN)
5809       {
5810         sbdIndex = nextBlock;
5811         StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5812       }
5813
5814       newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5815       if (sbdIndex != BLOCK_END_OF_CHAIN)
5816         StorageImpl_SetNextBlockInChain(
5817           This->parentStorage,
5818           sbdIndex,
5819           newsbdIndex);
5820
5821       StorageImpl_SetNextBlockInChain(
5822         This->parentStorage,
5823         newsbdIndex,
5824         BLOCK_END_OF_CHAIN);
5825
5826       /*
5827        * Initialize all the small blocks to free
5828        */
5829       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5830       StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5831
5832       if (count == 0)
5833       {
5834         /*
5835          * We have just created the small block depot.
5836          */
5837         DirEntry rootEntry;
5838         ULONG sbStartIndex;
5839
5840         /*
5841          * Save it in the header
5842          */
5843         This->parentStorage->smallBlockDepotStart = newsbdIndex;
5844         StorageImpl_SaveFileHeader(This->parentStorage);
5845
5846         /*
5847          * And allocate the first big block that will contain small blocks
5848          */
5849         sbStartIndex =
5850           StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5851
5852         StorageImpl_SetNextBlockInChain(
5853           This->parentStorage,
5854           sbStartIndex,
5855           BLOCK_END_OF_CHAIN);
5856
5857         StorageImpl_ReadDirEntry(
5858           This->parentStorage,
5859           This->parentStorage->base.storageDirEntry,
5860           &rootEntry);
5861
5862         rootEntry.startingBlock = sbStartIndex;
5863         rootEntry.size.u.HighPart = 0;
5864         rootEntry.size.u.LowPart  = This->parentStorage->bigBlockSize;
5865
5866         StorageImpl_WriteDirEntry(
5867           This->parentStorage,
5868           This->parentStorage->base.storageDirEntry,
5869           &rootEntry);
5870       }
5871       else
5872         StorageImpl_SaveFileHeader(This->parentStorage);
5873     }
5874   }
5875
5876   smallBlocksPerBigBlock =
5877     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5878
5879   /*
5880    * Verify if we have to allocate big blocks to contain small blocks
5881    */
5882   if (blockIndex % smallBlocksPerBigBlock == 0)
5883   {
5884     DirEntry rootEntry;
5885     ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5886
5887     StorageImpl_ReadDirEntry(
5888       This->parentStorage,
5889       This->parentStorage->base.storageDirEntry,
5890       &rootEntry);
5891
5892     if (rootEntry.size.u.LowPart <
5893        (blocksRequired * This->parentStorage->bigBlockSize))
5894     {
5895       rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5896
5897       BlockChainStream_SetSize(
5898         This->parentStorage->smallBlockRootChain,
5899         rootEntry.size);
5900
5901       StorageImpl_WriteDirEntry(
5902         This->parentStorage,
5903         This->parentStorage->base.storageDirEntry,
5904         &rootEntry);
5905     }
5906   }
5907
5908   return blockIndex;
5909 }
5910
5911 /******************************************************************************
5912  *      SmallBlockChainStream_ReadAt
5913  *
5914  * Reads a specified number of bytes from this chain at the specified offset.
5915  * bytesRead may be NULL.
5916  * Failure will be returned if the specified number of bytes has not been read.
5917  */
5918 HRESULT SmallBlockChainStream_ReadAt(
5919   SmallBlockChainStream* This,
5920   ULARGE_INTEGER         offset,
5921   ULONG                  size,
5922   void*                  buffer,
5923   ULONG*                 bytesRead)
5924 {
5925   HRESULT rc = S_OK;
5926   ULARGE_INTEGER offsetInBigBlockFile;
5927   ULONG blockNoInSequence =
5928     offset.u.LowPart / This->parentStorage->smallBlockSize;
5929
5930   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5931   ULONG bytesToReadInBuffer;
5932   ULONG blockIndex;
5933   ULONG bytesReadFromBigBlockFile;
5934   BYTE* bufferWalker;
5935
5936   /*
5937    * This should never happen on a small block file.
5938    */
5939   assert(offset.u.HighPart==0);
5940
5941   /*
5942    * Find the first block in the stream that contains part of the buffer.
5943    */
5944   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5945
5946   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5947   {
5948     rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5949     if(FAILED(rc))
5950       return rc;
5951     blockNoInSequence--;
5952   }
5953
5954   /*
5955    * Start reading the buffer.
5956    */
5957   *bytesRead   = 0;
5958   bufferWalker = buffer;
5959
5960   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5961   {
5962     /*
5963      * Calculate how many bytes we can copy from this small block.
5964      */
5965     bytesToReadInBuffer =
5966       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5967
5968     /*
5969      * Calculate the offset of the small block in the small block file.
5970      */
5971     offsetInBigBlockFile.u.HighPart  = 0;
5972     offsetInBigBlockFile.u.LowPart   =
5973       blockIndex * This->parentStorage->smallBlockSize;
5974
5975     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5976
5977     /*
5978      * Read those bytes in the buffer from the small block file.
5979      * The small block has already been identified so it shouldn't fail
5980      * unless the file is corrupt.
5981      */
5982     rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5983       offsetInBigBlockFile,
5984       bytesToReadInBuffer,
5985       bufferWalker,
5986       &bytesReadFromBigBlockFile);
5987
5988     if (FAILED(rc))
5989       return rc;
5990
5991     /*
5992      * Step to the next big block.
5993      */
5994     rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5995     if(FAILED(rc))
5996       return STG_E_DOCFILECORRUPT;
5997
5998     bufferWalker += bytesReadFromBigBlockFile;
5999     size         -= bytesReadFromBigBlockFile;
6000     *bytesRead   += bytesReadFromBigBlockFile;
6001     offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6002   }
6003
6004   return (size == 0) ? S_OK : STG_E_READFAULT;
6005 }
6006
6007 /******************************************************************************
6008  *       SmallBlockChainStream_WriteAt
6009  *
6010  * Writes the specified number of bytes to this chain at the specified offset.
6011  * Will fail if not all specified number of bytes have been written.
6012  */
6013 HRESULT SmallBlockChainStream_WriteAt(
6014   SmallBlockChainStream* This,
6015   ULARGE_INTEGER offset,
6016   ULONG          size,
6017   const void*    buffer,
6018   ULONG*         bytesWritten)
6019 {
6020   ULARGE_INTEGER offsetInBigBlockFile;
6021   ULONG blockNoInSequence =
6022     offset.u.LowPart / This->parentStorage->smallBlockSize;
6023
6024   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6025   ULONG bytesToWriteInBuffer;
6026   ULONG blockIndex;
6027   ULONG bytesWrittenToBigBlockFile;
6028   const BYTE* bufferWalker;
6029   HRESULT res;
6030
6031   /*
6032    * This should never happen on a small block file.
6033    */
6034   assert(offset.u.HighPart==0);
6035
6036   /*
6037    * Find the first block in the stream that contains part of the buffer.
6038    */
6039   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6040
6041   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
6042   {
6043     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6044       return STG_E_DOCFILECORRUPT;
6045     blockNoInSequence--;
6046   }
6047
6048   /*
6049    * Start writing the buffer.
6050    */
6051   *bytesWritten   = 0;
6052   bufferWalker = buffer;
6053   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6054   {
6055     /*
6056      * Calculate how many bytes we can copy to this small block.
6057      */
6058     bytesToWriteInBuffer =
6059       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6060
6061     /*
6062      * Calculate the offset of the small block in the small block file.
6063      */
6064     offsetInBigBlockFile.u.HighPart  = 0;
6065     offsetInBigBlockFile.u.LowPart   =
6066       blockIndex * This->parentStorage->smallBlockSize;
6067
6068     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
6069
6070     /*
6071      * Write those bytes in the buffer to the small block file.
6072      */
6073     res = BlockChainStream_WriteAt(
6074       This->parentStorage->smallBlockRootChain,
6075       offsetInBigBlockFile,
6076       bytesToWriteInBuffer,
6077       bufferWalker,
6078       &bytesWrittenToBigBlockFile);
6079     if (FAILED(res))
6080       return res;
6081
6082     /*
6083      * Step to the next big block.
6084      */
6085     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6086                                                         &blockIndex)))
6087       return FALSE;
6088     bufferWalker  += bytesWrittenToBigBlockFile;
6089     size          -= bytesWrittenToBigBlockFile;
6090     *bytesWritten += bytesWrittenToBigBlockFile;
6091     offsetInBlock  = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6092   }
6093
6094   return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6095 }
6096
6097 /******************************************************************************
6098  *       SmallBlockChainStream_Shrink
6099  *
6100  * Shrinks this chain in the small block depot.
6101  */
6102 static BOOL SmallBlockChainStream_Shrink(
6103   SmallBlockChainStream* This,
6104   ULARGE_INTEGER newSize)
6105 {
6106   ULONG blockIndex, extraBlock;
6107   ULONG numBlocks;
6108   ULONG count = 0;
6109
6110   numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6111
6112   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6113     numBlocks++;
6114
6115   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6116
6117   /*
6118    * Go to the new end of chain
6119    */
6120   while (count < numBlocks)
6121   {
6122     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6123                                                         &blockIndex)))
6124       return FALSE;
6125     count++;
6126   }
6127
6128   /*
6129    * If the count is 0, we have a special case, the head of the chain was
6130    * just freed.
6131    */
6132   if (count == 0)
6133   {
6134     DirEntry chainEntry;
6135
6136     StorageImpl_ReadDirEntry(This->parentStorage,
6137                              This->ownerDirEntry,
6138                              &chainEntry);
6139
6140     chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6141
6142     StorageImpl_WriteDirEntry(This->parentStorage,
6143                               This->ownerDirEntry,
6144                               &chainEntry);
6145
6146     /*
6147      * We start freeing the chain at the head block.
6148      */
6149     extraBlock = blockIndex;
6150   }
6151   else
6152   {
6153     /* Get the next block before marking the new end */
6154     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6155                                                         &extraBlock)))
6156       return FALSE;
6157
6158     /* Mark the new end of chain */
6159     SmallBlockChainStream_SetNextBlockInChain(
6160       This,
6161       blockIndex,
6162       BLOCK_END_OF_CHAIN);
6163   }
6164
6165   /*
6166    * Mark the extra blocks as free
6167    */
6168   while (extraBlock != BLOCK_END_OF_CHAIN)
6169   {
6170     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6171                                                         &blockIndex)))
6172       return FALSE;
6173     SmallBlockChainStream_FreeBlock(This, extraBlock);
6174     extraBlock = blockIndex;
6175   }
6176
6177   return TRUE;
6178 }
6179
6180 /******************************************************************************
6181  *      SmallBlockChainStream_Enlarge
6182  *
6183  * Grows this chain in the small block depot.
6184  */
6185 static BOOL SmallBlockChainStream_Enlarge(
6186   SmallBlockChainStream* This,
6187   ULARGE_INTEGER newSize)
6188 {
6189   ULONG blockIndex, currentBlock;
6190   ULONG newNumBlocks;
6191   ULONG oldNumBlocks = 0;
6192
6193   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6194
6195   /*
6196    * Empty chain. Create the head.
6197    */
6198   if (blockIndex == BLOCK_END_OF_CHAIN)
6199   {
6200     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6201     SmallBlockChainStream_SetNextBlockInChain(
6202         This,
6203         blockIndex,
6204         BLOCK_END_OF_CHAIN);
6205
6206     if (This->headOfStreamPlaceHolder != NULL)
6207     {
6208       *(This->headOfStreamPlaceHolder) = blockIndex;
6209     }
6210     else
6211     {
6212       DirEntry chainEntry;
6213
6214       StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6215                                    &chainEntry);
6216
6217       chainEntry.startingBlock = blockIndex;
6218
6219       StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6220                                   &chainEntry);
6221     }
6222   }
6223
6224   currentBlock = blockIndex;
6225
6226   /*
6227    * Figure out how many blocks are needed to contain this stream
6228    */
6229   newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6230
6231   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6232     newNumBlocks++;
6233
6234   /*
6235    * Go to the current end of chain
6236    */
6237   while (blockIndex != BLOCK_END_OF_CHAIN)
6238   {
6239     oldNumBlocks++;
6240     currentBlock = blockIndex;
6241     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6242       return FALSE;
6243   }
6244
6245   /*
6246    * Add new blocks to the chain
6247    */
6248   while (oldNumBlocks < newNumBlocks)
6249   {
6250     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6251     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6252
6253     SmallBlockChainStream_SetNextBlockInChain(
6254       This,
6255       blockIndex,
6256       BLOCK_END_OF_CHAIN);
6257
6258     currentBlock = blockIndex;
6259     oldNumBlocks++;
6260   }
6261
6262   return TRUE;
6263 }
6264
6265 /******************************************************************************
6266  *      SmallBlockChainStream_SetSize
6267  *
6268  * Sets the size of this stream.
6269  * The file will grow if we grow the chain.
6270  *
6271  * TODO: Free the actual blocks in the file when we shrink the chain.
6272  *       Currently, the blocks are still in the file. So the file size
6273  *       doesn't shrink even if we shrink streams.
6274  */
6275 BOOL SmallBlockChainStream_SetSize(
6276                 SmallBlockChainStream* This,
6277                 ULARGE_INTEGER    newSize)
6278 {
6279   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6280
6281   if (newSize.u.LowPart == size.u.LowPart)
6282     return TRUE;
6283
6284   if (newSize.u.LowPart < size.u.LowPart)
6285   {
6286     SmallBlockChainStream_Shrink(This, newSize);
6287   }
6288   else
6289   {
6290     SmallBlockChainStream_Enlarge(This, newSize);
6291   }
6292
6293   return TRUE;
6294 }
6295
6296 /******************************************************************************
6297  *       SmallBlockChainStream_GetCount
6298  *
6299  * Returns the number of small blocks that comprises this chain.
6300  * This is not the size of the stream as the last block may not be full!
6301  *
6302  */
6303 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6304 {
6305     ULONG blockIndex;
6306     ULONG count = 0;
6307
6308     blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6309
6310     while(blockIndex != BLOCK_END_OF_CHAIN)
6311     {
6312         count++;
6313
6314         if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6315                         blockIndex, &blockIndex)))
6316             return 0;
6317     }
6318
6319     return count;
6320 }
6321
6322 /******************************************************************************
6323  *      SmallBlockChainStream_GetSize
6324  *
6325  * Returns the size of this chain.
6326  */
6327 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6328 {
6329   DirEntry chainEntry;
6330
6331   if(This->headOfStreamPlaceHolder != NULL)
6332   {
6333     ULARGE_INTEGER result;
6334     result.u.HighPart = 0;
6335
6336     result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6337         This->parentStorage->smallBlockSize;
6338
6339     return result;
6340   }
6341
6342   StorageImpl_ReadDirEntry(
6343     This->parentStorage,
6344     This->ownerDirEntry,
6345     &chainEntry);
6346
6347   return chainEntry.size;
6348 }
6349
6350 /******************************************************************************
6351  *    StgCreateDocfile  [OLE32.@]
6352  * Creates a new compound file storage object
6353  *
6354  * PARAMS
6355  *  pwcsName  [ I] Unicode string with filename (can be relative or NULL)
6356  *  grfMode   [ I] Access mode for opening the new storage object (see STGM_ constants)
6357  *  reserved  [ ?] unused?, usually 0
6358  *  ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6359  *
6360  * RETURNS
6361  *  S_OK if the file was successfully created
6362  *  some STG_E_ value if error
6363  * NOTES
6364  *  if pwcsName is NULL, create file with new unique name
6365  *  the function can returns
6366  *  STG_S_CONVERTED if the specified file was successfully converted to storage format
6367  *  (unrealized now)
6368  */
6369 HRESULT WINAPI StgCreateDocfile(
6370   LPCOLESTR pwcsName,
6371   DWORD       grfMode,
6372   DWORD       reserved,
6373   IStorage  **ppstgOpen)
6374 {
6375   StorageBaseImpl* newStorage = 0;
6376   HANDLE       hFile      = INVALID_HANDLE_VALUE;
6377   HRESULT        hr         = STG_E_INVALIDFLAG;
6378   DWORD          shareMode;
6379   DWORD          accessMode;
6380   DWORD          creationMode;
6381   DWORD          fileAttributes;
6382   WCHAR          tempFileName[MAX_PATH];
6383
6384   TRACE("(%s, %x, %d, %p)\n",
6385         debugstr_w(pwcsName), grfMode,
6386         reserved, ppstgOpen);
6387
6388   if (ppstgOpen == 0)
6389     return STG_E_INVALIDPOINTER;
6390   if (reserved != 0)
6391     return STG_E_INVALIDPARAMETER;
6392
6393   /* if no share mode given then DENY_NONE is the default */
6394   if (STGM_SHARE_MODE(grfMode) == 0)
6395       grfMode |= STGM_SHARE_DENY_NONE;
6396
6397   if ( FAILED( validateSTGM(grfMode) ))
6398     goto end;
6399
6400   /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6401   switch(STGM_ACCESS_MODE(grfMode))
6402   {
6403   case STGM_WRITE:
6404   case STGM_READWRITE:
6405     break;
6406   default:
6407     goto end;
6408   }
6409
6410   /* in direct mode, can only use SHARE_EXCLUSIVE */
6411   if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6412     goto end;
6413
6414   /* but in transacted mode, any share mode is valid */
6415
6416   /*
6417    * Generate a unique name.
6418    */
6419   if (pwcsName == 0)
6420   {
6421     WCHAR tempPath[MAX_PATH];
6422     static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6423
6424     memset(tempPath, 0, sizeof(tempPath));
6425     memset(tempFileName, 0, sizeof(tempFileName));
6426
6427     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6428       tempPath[0] = '.';
6429
6430     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6431       pwcsName = tempFileName;
6432     else
6433     {
6434       hr = STG_E_INSUFFICIENTMEMORY;
6435       goto end;
6436     }
6437
6438     creationMode = TRUNCATE_EXISTING;
6439   }
6440   else
6441   {
6442     creationMode = GetCreationModeFromSTGM(grfMode);
6443   }
6444
6445   /*
6446    * Interpret the STGM value grfMode
6447    */
6448   shareMode    = FILE_SHARE_READ | FILE_SHARE_WRITE;
6449   accessMode   = GetAccessModeFromSTGM(grfMode);
6450
6451   if (grfMode & STGM_DELETEONRELEASE)
6452     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6453   else
6454     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6455
6456   if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6457       FIXME("Storage share mode not implemented.\n");
6458
6459   if (grfMode & STGM_TRANSACTED)
6460     FIXME("Transacted mode not implemented.\n");
6461
6462   *ppstgOpen = 0;
6463
6464   hFile = CreateFileW(pwcsName,
6465                         accessMode,
6466                         shareMode,
6467                         NULL,
6468                         creationMode,
6469                         fileAttributes,
6470                         0);
6471
6472   if (hFile == INVALID_HANDLE_VALUE)
6473   {
6474     if(GetLastError() == ERROR_FILE_EXISTS)
6475       hr = STG_E_FILEALREADYEXISTS;
6476     else
6477       hr = E_FAIL;
6478     goto end;
6479   }
6480
6481   /*
6482    * Allocate and initialize the new IStorage32object.
6483    */
6484   hr = Storage_Construct(
6485          hFile,
6486         pwcsName,
6487          NULL,
6488          grfMode,
6489          TRUE,
6490          TRUE,
6491          &newStorage);
6492
6493   if (FAILED(hr))
6494   {
6495     goto end;
6496   }
6497
6498   /*
6499    * Get an "out" pointer for the caller.
6500    */
6501   *ppstgOpen = (IStorage*)newStorage;
6502
6503 end:
6504   TRACE("<-- %p  r = %08x\n", *ppstgOpen, hr);
6505
6506   return hr;
6507 }
6508
6509 /******************************************************************************
6510  *              StgCreateStorageEx        [OLE32.@]
6511  */
6512 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6513 {
6514     TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6515           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6516
6517     if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6518     {
6519         ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6520         return STG_E_INVALIDPARAMETER;  
6521     }
6522
6523     if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6524     {
6525         ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6526         return STG_E_INVALIDPARAMETER;  
6527     }
6528
6529     if (stgfmt == STGFMT_FILE)
6530     {
6531         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
6532         return STG_E_INVALIDPARAMETER;
6533     }
6534
6535     if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6536     {
6537         FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6538         return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen); 
6539     }
6540
6541     ERR("Invalid stgfmt argument\n");
6542     return STG_E_INVALIDPARAMETER;
6543 }
6544
6545 /******************************************************************************
6546  *              StgCreatePropSetStg       [OLE32.@]
6547  */
6548 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6549  IPropertySetStorage **ppPropSetStg)
6550 {
6551     HRESULT hr;
6552
6553     TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6554     if (reserved)
6555         hr = STG_E_INVALIDPARAMETER;
6556     else
6557         hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6558          (void**)ppPropSetStg);
6559     return hr;
6560 }
6561
6562 /******************************************************************************
6563  *              StgOpenStorageEx      [OLE32.@]
6564  */
6565 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6566 {
6567     TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6568           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6569
6570     if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6571     {
6572         ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6573         return STG_E_INVALIDPARAMETER;  
6574     }
6575
6576     switch (stgfmt)
6577     {
6578     case STGFMT_FILE:
6579         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
6580         return STG_E_INVALIDPARAMETER;
6581         
6582     case STGFMT_STORAGE:
6583         break;
6584
6585     case STGFMT_DOCFILE:
6586         if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6587         {
6588             ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6589             return STG_E_INVALIDPARAMETER;  
6590         }
6591         FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6592         break;
6593
6594     case STGFMT_ANY:
6595         WARN("STGFMT_ANY assuming storage\n");
6596         break;
6597
6598     default:
6599         return STG_E_INVALIDPARAMETER;
6600     }
6601
6602     return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6603 }
6604
6605
6606 /******************************************************************************
6607  *              StgOpenStorage        [OLE32.@]
6608  */
6609 HRESULT WINAPI StgOpenStorage(
6610   const OLECHAR *pwcsName,
6611   IStorage      *pstgPriority,
6612   DWORD          grfMode,
6613   SNB            snbExclude,
6614   DWORD          reserved,
6615   IStorage     **ppstgOpen)
6616 {
6617   StorageBaseImpl* newStorage = 0;
6618   HRESULT        hr = S_OK;
6619   HANDLE         hFile = 0;
6620   DWORD          shareMode;
6621   DWORD          accessMode;
6622
6623   TRACE("(%s, %p, %x, %p, %d, %p)\n",
6624         debugstr_w(pwcsName), pstgPriority, grfMode,
6625         snbExclude, reserved, ppstgOpen);
6626
6627   if (pwcsName == 0)
6628   {
6629     hr = STG_E_INVALIDNAME;
6630     goto end;
6631   }
6632
6633   if (ppstgOpen == 0)
6634   {
6635     hr = STG_E_INVALIDPOINTER;
6636     goto end;
6637   }
6638
6639   if (reserved)
6640   {
6641     hr = STG_E_INVALIDPARAMETER;
6642     goto end;
6643   }
6644
6645   if (grfMode & STGM_PRIORITY)
6646   {
6647     if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6648       return STG_E_INVALIDFLAG;
6649     if (grfMode & STGM_DELETEONRELEASE)
6650       return STG_E_INVALIDFUNCTION;
6651     if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6652       return STG_E_INVALIDFLAG;
6653     grfMode &= ~0xf0; /* remove the existing sharing mode */
6654     grfMode |= STGM_SHARE_DENY_NONE;
6655
6656     /* STGM_PRIORITY stops other IStorage objects on the same file from
6657      * committing until the STGM_PRIORITY IStorage is closed. it also
6658      * stops non-transacted mode StgOpenStorage calls with write access from
6659      * succeeding. obviously, both of these cannot be achieved through just
6660      * file share flags */
6661     FIXME("STGM_PRIORITY mode not implemented correctly\n");
6662   }
6663
6664   /*
6665    * Validate the sharing mode
6666    */
6667   if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6668     switch(STGM_SHARE_MODE(grfMode))
6669     {
6670       case STGM_SHARE_EXCLUSIVE:
6671       case STGM_SHARE_DENY_WRITE:
6672         break;
6673       default:
6674         hr = STG_E_INVALIDFLAG;
6675         goto end;
6676     }
6677
6678   if ( FAILED( validateSTGM(grfMode) ) ||
6679        (grfMode&STGM_CREATE))
6680   {
6681     hr = STG_E_INVALIDFLAG;
6682     goto end;
6683   }
6684
6685   /* shared reading requires transacted mode */
6686   if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6687       STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6688      !(grfMode&STGM_TRANSACTED) )
6689   {
6690     hr = STG_E_INVALIDFLAG;
6691     goto end;
6692   }
6693
6694   /*
6695    * Interpret the STGM value grfMode
6696    */
6697   shareMode    = GetShareModeFromSTGM(grfMode);
6698   accessMode   = GetAccessModeFromSTGM(grfMode);
6699
6700   *ppstgOpen = 0;
6701
6702   hFile = CreateFileW( pwcsName,
6703                        accessMode,
6704                        shareMode,
6705                        NULL,
6706                        OPEN_EXISTING,
6707                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6708                        0);
6709
6710   if (hFile==INVALID_HANDLE_VALUE)
6711   {
6712     DWORD last_error = GetLastError();
6713
6714     hr = E_FAIL;
6715
6716     switch (last_error)
6717     {
6718       case ERROR_FILE_NOT_FOUND:
6719         hr = STG_E_FILENOTFOUND;
6720         break;
6721
6722       case ERROR_PATH_NOT_FOUND:
6723         hr = STG_E_PATHNOTFOUND;
6724         break;
6725
6726       case ERROR_ACCESS_DENIED:
6727       case ERROR_WRITE_PROTECT:
6728         hr = STG_E_ACCESSDENIED;
6729         break;
6730
6731       case ERROR_SHARING_VIOLATION:
6732         hr = STG_E_SHAREVIOLATION;
6733         break;
6734
6735       default:
6736         hr = E_FAIL;
6737     }
6738
6739     goto end;
6740   }
6741
6742   /*
6743    * Refuse to open the file if it's too small to be a structured storage file
6744    * FIXME: verify the file when reading instead of here
6745    */
6746   if (GetFileSize(hFile, NULL) < 0x100)
6747   {
6748     CloseHandle(hFile);
6749     hr = STG_E_FILEALREADYEXISTS;
6750     goto end;
6751   }
6752
6753   /*
6754    * Allocate and initialize the new IStorage32object.
6755    */
6756   hr = Storage_Construct(
6757          hFile,
6758          pwcsName,
6759          NULL,
6760          grfMode,
6761          TRUE,
6762          FALSE,
6763          &newStorage);
6764
6765   if (FAILED(hr))
6766   {
6767     /*
6768      * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6769      */
6770     if(hr == STG_E_INVALIDHEADER)
6771         hr = STG_E_FILEALREADYEXISTS;
6772     goto end;
6773   }
6774
6775   /*
6776    * Get an "out" pointer for the caller.
6777    */
6778   *ppstgOpen = (IStorage*)newStorage;
6779
6780 end:
6781   TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6782   return hr;
6783 }
6784
6785 /******************************************************************************
6786  *    StgCreateDocfileOnILockBytes    [OLE32.@]
6787  */
6788 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6789       ILockBytes *plkbyt,
6790       DWORD grfMode,
6791       DWORD reserved,
6792       IStorage** ppstgOpen)
6793 {
6794   StorageBaseImpl* newStorage = 0;
6795   HRESULT        hr         = S_OK;
6796
6797   if ((ppstgOpen == 0) || (plkbyt == 0))
6798     return STG_E_INVALIDPOINTER;
6799
6800   /*
6801    * Allocate and initialize the new IStorage object.
6802    */
6803   hr = Storage_Construct(
6804          0,
6805         0,
6806          plkbyt,
6807          grfMode,
6808          FALSE,
6809          TRUE,
6810          &newStorage);
6811
6812   if (FAILED(hr))
6813   {
6814     return hr;
6815   }
6816
6817   /*
6818    * Get an "out" pointer for the caller.
6819    */
6820   *ppstgOpen = (IStorage*)newStorage;
6821
6822   return hr;
6823 }
6824
6825 /******************************************************************************
6826  *    StgOpenStorageOnILockBytes    [OLE32.@]
6827  */
6828 HRESULT WINAPI StgOpenStorageOnILockBytes(
6829       ILockBytes *plkbyt,
6830       IStorage *pstgPriority,
6831       DWORD grfMode,
6832       SNB snbExclude,
6833       DWORD reserved,
6834       IStorage **ppstgOpen)
6835 {
6836   StorageBaseImpl* newStorage = 0;
6837   HRESULT        hr = S_OK;
6838
6839   if ((plkbyt == 0) || (ppstgOpen == 0))
6840     return STG_E_INVALIDPOINTER;
6841
6842   if ( FAILED( validateSTGM(grfMode) ))
6843     return STG_E_INVALIDFLAG;
6844
6845   *ppstgOpen = 0;
6846
6847   /*
6848    * Allocate and initialize the new IStorage object.
6849    */
6850   hr = Storage_Construct(
6851          0,
6852          0,
6853          plkbyt,
6854          grfMode,
6855          FALSE,
6856          FALSE,
6857          &newStorage);
6858
6859   if (FAILED(hr))
6860   {
6861     return hr;
6862   }
6863
6864   /*
6865    * Get an "out" pointer for the caller.
6866    */
6867   *ppstgOpen = (IStorage*)newStorage;
6868
6869   return hr;
6870 }
6871
6872 /******************************************************************************
6873  *              StgSetTimes [ole32.@]
6874  *              StgSetTimes [OLE32.@]
6875  *
6876  *
6877  */
6878 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6879                            FILETIME const *patime, FILETIME const *pmtime)
6880 {
6881   IStorage *stg = NULL;
6882   HRESULT r;
6883  
6884   TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6885
6886   r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6887                      0, 0, &stg);
6888   if( SUCCEEDED(r) )
6889   {
6890     r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6891     IStorage_Release(stg);
6892   }
6893
6894   return r;
6895 }
6896
6897 /******************************************************************************
6898  *              StgIsStorageILockBytes        [OLE32.@]
6899  *
6900  * Determines if the ILockBytes contains a storage object.
6901  */
6902 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6903 {
6904   BYTE sig[8];
6905   ULARGE_INTEGER offset;
6906
6907   offset.u.HighPart = 0;
6908   offset.u.LowPart  = 0;
6909
6910   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6911
6912   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6913     return S_OK;
6914
6915   return S_FALSE;
6916 }
6917
6918 /******************************************************************************
6919  *              WriteClassStg        [OLE32.@]
6920  *
6921  * This method will store the specified CLSID in the specified storage object
6922  */
6923 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6924 {
6925   HRESULT hRes;
6926
6927   if(!pStg)
6928     return E_INVALIDARG;
6929
6930   if(!rclsid)
6931     return STG_E_INVALIDPOINTER;
6932
6933   hRes = IStorage_SetClass(pStg, rclsid);
6934
6935   return hRes;
6936 }
6937
6938 /***********************************************************************
6939  *    ReadClassStg (OLE32.@)
6940  *
6941  * This method reads the CLSID previously written to a storage object with
6942  * the WriteClassStg.
6943  *
6944  * PARAMS
6945  *  pstg    [I] IStorage pointer
6946  *  pclsid  [O] Pointer to where the CLSID is written
6947  *
6948  * RETURNS
6949  *  Success: S_OK.
6950  *  Failure: HRESULT code.
6951  */
6952 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6953
6954     STATSTG pstatstg;
6955     HRESULT hRes;
6956
6957     TRACE("(%p, %p)\n", pstg, pclsid);
6958
6959     if(!pstg || !pclsid)
6960         return E_INVALIDARG;
6961
6962    /*
6963     * read a STATSTG structure (contains the clsid) from the storage
6964     */
6965     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6966
6967     if(SUCCEEDED(hRes))
6968         *pclsid=pstatstg.clsid;
6969
6970     return hRes;
6971 }
6972
6973 /***********************************************************************
6974  *    OleLoadFromStream (OLE32.@)
6975  *
6976  * This function loads an object from stream
6977  */
6978 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6979 {
6980     CLSID       clsid;
6981     HRESULT     res;
6982     LPPERSISTSTREAM     xstm;
6983
6984     TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6985
6986     res=ReadClassStm(pStm,&clsid);
6987     if (FAILED(res))
6988         return res;
6989     res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6990     if (FAILED(res))
6991         return res;
6992     res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6993     if (FAILED(res)) {
6994         IUnknown_Release((IUnknown*)*ppvObj);
6995         return res;
6996     }
6997     res=IPersistStream_Load(xstm,pStm);
6998     IPersistStream_Release(xstm);
6999     /* FIXME: all refcounts ok at this point? I think they should be:
7000      *          pStm    : unchanged
7001      *          ppvObj  : 1
7002      *          xstm    : 0 (released)
7003      */
7004     return res;
7005 }
7006
7007 /***********************************************************************
7008  *    OleSaveToStream (OLE32.@)
7009  *
7010  * This function saves an object with the IPersistStream interface on it
7011  * to the specified stream.
7012  */
7013 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7014 {
7015
7016     CLSID clsid;
7017     HRESULT res;
7018
7019     TRACE("(%p,%p)\n",pPStm,pStm);
7020
7021     res=IPersistStream_GetClassID(pPStm,&clsid);
7022
7023     if (SUCCEEDED(res)){
7024
7025         res=WriteClassStm(pStm,&clsid);
7026
7027         if (SUCCEEDED(res))
7028
7029             res=IPersistStream_Save(pPStm,pStm,TRUE);
7030     }
7031
7032     TRACE("Finished Save\n");
7033     return res;
7034 }
7035
7036 /****************************************************************************
7037  * This method validate a STGM parameter that can contain the values below
7038  *
7039  * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7040  * The stgm values contained in 0xffff0000 are bitmasks.
7041  *
7042  * STGM_DIRECT               0x00000000
7043  * STGM_TRANSACTED           0x00010000
7044  * STGM_SIMPLE               0x08000000
7045  *
7046  * STGM_READ                 0x00000000
7047  * STGM_WRITE                0x00000001
7048  * STGM_READWRITE            0x00000002
7049  *
7050  * STGM_SHARE_DENY_NONE      0x00000040
7051  * STGM_SHARE_DENY_READ      0x00000030
7052  * STGM_SHARE_DENY_WRITE     0x00000020
7053  * STGM_SHARE_EXCLUSIVE      0x00000010
7054  *
7055  * STGM_PRIORITY             0x00040000
7056  * STGM_DELETEONRELEASE      0x04000000
7057  *
7058  * STGM_CREATE               0x00001000
7059  * STGM_CONVERT              0x00020000
7060  * STGM_FAILIFTHERE          0x00000000
7061  *
7062  * STGM_NOSCRATCH            0x00100000
7063  * STGM_NOSNAPSHOT           0x00200000
7064  */
7065 static HRESULT validateSTGM(DWORD stgm)
7066 {
7067   DWORD access = STGM_ACCESS_MODE(stgm);
7068   DWORD share  = STGM_SHARE_MODE(stgm);
7069   DWORD create = STGM_CREATE_MODE(stgm);
7070
7071   if (stgm&~STGM_KNOWN_FLAGS)
7072   {
7073     ERR("unknown flags %08x\n", stgm);
7074     return E_FAIL;
7075   }
7076
7077   switch (access)
7078   {
7079   case STGM_READ:
7080   case STGM_WRITE:
7081   case STGM_READWRITE:
7082     break;
7083   default:
7084     return E_FAIL;
7085   }
7086
7087   switch (share)
7088   {
7089   case STGM_SHARE_DENY_NONE:
7090   case STGM_SHARE_DENY_READ:
7091   case STGM_SHARE_DENY_WRITE:
7092   case STGM_SHARE_EXCLUSIVE:
7093     break;
7094   default:
7095     return E_FAIL;
7096   }
7097
7098   switch (create)
7099   {
7100   case STGM_CREATE:
7101   case STGM_FAILIFTHERE:
7102     break;
7103   default:
7104     return E_FAIL;
7105   }
7106
7107   /*
7108    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7109    */
7110   if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7111       return E_FAIL;
7112
7113   /*
7114    * STGM_CREATE | STGM_CONVERT
7115    * if both are false, STGM_FAILIFTHERE is set to TRUE
7116    */
7117   if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7118     return E_FAIL;
7119
7120   /*
7121    * STGM_NOSCRATCH requires STGM_TRANSACTED
7122    */
7123   if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7124     return E_FAIL;
7125
7126   /*
7127    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7128    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7129    */
7130   if ( (stgm & STGM_NOSNAPSHOT) &&
7131         (!(stgm & STGM_TRANSACTED) ||
7132          share == STGM_SHARE_EXCLUSIVE ||
7133          share == STGM_SHARE_DENY_WRITE) )
7134     return E_FAIL;
7135
7136   return S_OK;
7137 }
7138
7139 /****************************************************************************
7140  *      GetShareModeFromSTGM
7141  *
7142  * This method will return a share mode flag from a STGM value.
7143  * The STGM value is assumed valid.
7144  */
7145 static DWORD GetShareModeFromSTGM(DWORD stgm)
7146 {
7147   switch (STGM_SHARE_MODE(stgm))
7148   {
7149   case STGM_SHARE_DENY_NONE:
7150     return FILE_SHARE_READ | FILE_SHARE_WRITE;
7151   case STGM_SHARE_DENY_READ:
7152     return FILE_SHARE_WRITE;
7153   case STGM_SHARE_DENY_WRITE:
7154     return FILE_SHARE_READ;
7155   case STGM_SHARE_EXCLUSIVE:
7156     return 0;
7157   }
7158   ERR("Invalid share mode!\n");
7159   assert(0);
7160   return 0;
7161 }
7162
7163 /****************************************************************************
7164  *      GetAccessModeFromSTGM
7165  *
7166  * This method will return an access mode flag from a STGM value.
7167  * The STGM value is assumed valid.
7168  */
7169 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7170 {
7171   switch (STGM_ACCESS_MODE(stgm))
7172   {
7173   case STGM_READ:
7174     return GENERIC_READ;
7175   case STGM_WRITE:
7176   case STGM_READWRITE:
7177     return GENERIC_READ | GENERIC_WRITE;
7178   }
7179   ERR("Invalid access mode!\n");
7180   assert(0);
7181   return 0;
7182 }
7183
7184 /****************************************************************************
7185  *      GetCreationModeFromSTGM
7186  *
7187  * This method will return a creation mode flag from a STGM value.
7188  * The STGM value is assumed valid.
7189  */
7190 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7191 {
7192   switch(STGM_CREATE_MODE(stgm))
7193   {
7194   case STGM_CREATE:
7195     return CREATE_ALWAYS;
7196   case STGM_CONVERT:
7197     FIXME("STGM_CONVERT not implemented!\n");
7198     return CREATE_NEW;
7199   case STGM_FAILIFTHERE:
7200     return CREATE_NEW;
7201   }
7202   ERR("Invalid create mode!\n");
7203   assert(0);
7204   return 0;
7205 }
7206
7207
7208 /*************************************************************************
7209  * OLECONVERT_LoadOLE10 [Internal]
7210  *
7211  * Loads the OLE10 STREAM to memory
7212  *
7213  * PARAMS
7214  *     pOleStream   [I] The OLESTREAM
7215  *     pData        [I] Data Structure for the OLESTREAM Data
7216  *
7217  * RETURNS
7218  *     Success:  S_OK
7219  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
7220  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7221  *
7222  * NOTES
7223  *     This function is used by OleConvertOLESTREAMToIStorage only.
7224  *
7225  *     Memory allocated for pData must be freed by the caller
7226  */
7227 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7228 {
7229         DWORD dwSize;
7230         HRESULT hRes = S_OK;
7231         int nTryCnt=0;
7232         int max_try = 6;
7233
7234         pData->pData = NULL;
7235         pData->pstrOleObjFileName = NULL;
7236
7237         for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7238         {
7239         /* Get the OleID */
7240         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7241         if(dwSize != sizeof(pData->dwOleID))
7242         {
7243                 hRes = CONVERT10_E_OLESTREAM_GET;
7244         }
7245         else if(pData->dwOleID != OLESTREAM_ID)
7246         {
7247                 hRes = CONVERT10_E_OLESTREAM_FMT;
7248         }
7249                 else
7250                 {
7251                         hRes = S_OK;
7252                         break;
7253                 }
7254         }
7255
7256         if(hRes == S_OK)
7257         {
7258                 /* Get the TypeID... more info needed for this field */
7259                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7260                 if(dwSize != sizeof(pData->dwTypeID))
7261                 {
7262                         hRes = CONVERT10_E_OLESTREAM_GET;
7263                 }
7264         }
7265         if(hRes == S_OK)
7266         {
7267                 if(pData->dwTypeID != 0)
7268                 {
7269                         /* Get the length of the OleTypeName */
7270                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7271                         if(dwSize != sizeof(pData->dwOleTypeNameLength))
7272                         {
7273                                 hRes = CONVERT10_E_OLESTREAM_GET;
7274                         }
7275
7276                         if(hRes == S_OK)
7277                         {
7278                                 if(pData->dwOleTypeNameLength > 0)
7279                                 {
7280                                         /* Get the OleTypeName */
7281                                         dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7282                                         if(dwSize != pData->dwOleTypeNameLength)
7283                                         {
7284                                                 hRes = CONVERT10_E_OLESTREAM_GET;
7285                                         }
7286                                 }
7287                         }
7288                         if(bStrem1)
7289                         {
7290                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7291                                 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7292                                 {
7293                                         hRes = CONVERT10_E_OLESTREAM_GET;
7294                                 }
7295                         if(hRes == S_OK)
7296                         {
7297                                         if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7298                                                 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7299                                         pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7300                                         if(pData->pstrOleObjFileName)
7301                                         {
7302                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7303                                                 if(dwSize != pData->dwOleObjFileNameLength)
7304                                                 {
7305                                                         hRes = CONVERT10_E_OLESTREAM_GET;
7306                                                 }
7307                                         }
7308                                         else
7309                                                 hRes = CONVERT10_E_OLESTREAM_GET;
7310                                 }
7311                         }
7312                         else
7313                         {
7314                                 /* Get the Width of the Metafile */
7315                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7316                                 if(dwSize != sizeof(pData->dwMetaFileWidth))
7317                                 {
7318                                         hRes = CONVERT10_E_OLESTREAM_GET;
7319                                 }
7320                         if(hRes == S_OK)
7321                         {
7322                                 /* Get the Height of the Metafile */
7323                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7324                                 if(dwSize != sizeof(pData->dwMetaFileHeight))
7325                                 {
7326                                         hRes = CONVERT10_E_OLESTREAM_GET;
7327                                 }
7328                         }
7329                         }
7330                         if(hRes == S_OK)
7331                         {
7332                                 /* Get the Length of the Data */
7333                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7334                                 if(dwSize != sizeof(pData->dwDataLength))
7335                                 {
7336                                         hRes = CONVERT10_E_OLESTREAM_GET;
7337                                 }
7338                         }
7339
7340                         if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7341                         {
7342                                 if(!bStrem1) /* if it is a second OLE stream data */
7343                                 {
7344                                         pData->dwDataLength -= 8;
7345                                         dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7346                                         if(dwSize != sizeof(pData->strUnknown))
7347                                         {
7348                                                 hRes = CONVERT10_E_OLESTREAM_GET;
7349                                         }
7350                                 }
7351                         }
7352                         if(hRes == S_OK)
7353                         {
7354                                 if(pData->dwDataLength > 0)
7355                                 {
7356                                         pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7357
7358                                         /* Get Data (ex. IStorage, Metafile, or BMP) */
7359                                         if(pData->pData)
7360                                         {
7361                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7362                                                 if(dwSize != pData->dwDataLength)
7363                                                 {
7364                                                         hRes = CONVERT10_E_OLESTREAM_GET;
7365                                                 }
7366                                         }
7367                                         else
7368                                         {
7369                                                 hRes = CONVERT10_E_OLESTREAM_GET;
7370                                         }
7371                                 }
7372                         }
7373                 }
7374         }
7375         return hRes;
7376 }
7377
7378 /*************************************************************************
7379  * OLECONVERT_SaveOLE10 [Internal]
7380  *
7381  * Saves the OLE10 STREAM From memory
7382  *
7383  * PARAMS
7384  *     pData        [I] Data Structure for the OLESTREAM Data
7385  *     pOleStream   [I] The OLESTREAM to save
7386  *
7387  * RETURNS
7388  *     Success:  S_OK
7389  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
7390  *
7391  * NOTES
7392  *     This function is used by OleConvertIStorageToOLESTREAM only.
7393  *
7394  */
7395 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7396 {
7397     DWORD dwSize;
7398     HRESULT hRes = S_OK;
7399
7400
7401    /* Set the OleID */
7402     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7403     if(dwSize != sizeof(pData->dwOleID))
7404     {
7405         hRes = CONVERT10_E_OLESTREAM_PUT;
7406     }
7407
7408     if(hRes == S_OK)
7409     {
7410         /* Set the TypeID */
7411         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7412         if(dwSize != sizeof(pData->dwTypeID))
7413         {
7414             hRes = CONVERT10_E_OLESTREAM_PUT;
7415         }
7416     }
7417
7418     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7419     {
7420         /* Set the Length of the OleTypeName */
7421         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7422         if(dwSize != sizeof(pData->dwOleTypeNameLength))
7423         {
7424             hRes = CONVERT10_E_OLESTREAM_PUT;
7425         }
7426
7427         if(hRes == S_OK)
7428         {
7429             if(pData->dwOleTypeNameLength > 0)
7430             {
7431                 /* Set the OleTypeName */
7432                 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7433                 if(dwSize != pData->dwOleTypeNameLength)
7434                 {
7435                     hRes = CONVERT10_E_OLESTREAM_PUT;
7436                 }
7437             }
7438         }
7439
7440         if(hRes == S_OK)
7441         {
7442             /* Set the width of the Metafile */
7443             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7444             if(dwSize != sizeof(pData->dwMetaFileWidth))
7445             {
7446                 hRes = CONVERT10_E_OLESTREAM_PUT;
7447             }
7448         }
7449
7450         if(hRes == S_OK)
7451         {
7452             /* Set the height of the Metafile */
7453             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7454             if(dwSize != sizeof(pData->dwMetaFileHeight))
7455             {
7456                 hRes = CONVERT10_E_OLESTREAM_PUT;
7457             }
7458         }
7459
7460         if(hRes == S_OK)
7461         {
7462             /* Set the length of the Data */
7463             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7464             if(dwSize != sizeof(pData->dwDataLength))
7465             {
7466                 hRes = CONVERT10_E_OLESTREAM_PUT;
7467             }
7468         }
7469
7470         if(hRes == S_OK)
7471         {
7472             if(pData->dwDataLength > 0)
7473             {
7474                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7475                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
7476                 if(dwSize != pData->dwDataLength)
7477                 {
7478                     hRes = CONVERT10_E_OLESTREAM_PUT;
7479                 }
7480             }
7481         }
7482     }
7483     return hRes;
7484 }
7485
7486 /*************************************************************************
7487  * OLECONVERT_GetOLE20FromOLE10[Internal]
7488  *
7489  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7490  * opens it, and copies the content to the dest IStorage for
7491  * OleConvertOLESTREAMToIStorage
7492  *
7493  *
7494  * PARAMS
7495  *     pDestStorage  [I] The IStorage to copy the data to
7496  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
7497  *     nBufferLength [I] The size of the buffer
7498  *
7499  * RETURNS
7500  *     Nothing
7501  *
7502  * NOTES
7503  *
7504  *
7505  */
7506 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7507 {
7508     HRESULT hRes;
7509     HANDLE hFile;
7510     IStorage *pTempStorage;
7511     DWORD dwNumOfBytesWritten;
7512     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7513     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7514
7515     /* Create a temp File */
7516     GetTempPathW(MAX_PATH, wstrTempDir);
7517     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7518     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7519
7520     if(hFile != INVALID_HANDLE_VALUE)
7521     {
7522         /* Write IStorage Data to File */
7523         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7524         CloseHandle(hFile);
7525
7526         /* Open and copy temp storage to the Dest Storage */
7527         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7528         if(hRes == S_OK)
7529         {
7530             hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7531             IStorage_Release(pTempStorage);
7532         }
7533         DeleteFileW(wstrTempFile);
7534     }
7535 }
7536
7537
7538 /*************************************************************************
7539  * OLECONVERT_WriteOLE20ToBuffer [Internal]
7540  *
7541  * Saves the OLE10 STREAM From memory
7542  *
7543  * PARAMS
7544  *     pStorage  [I] The Src IStorage to copy
7545  *     pData     [I] The Dest Memory to write to.
7546  *
7547  * RETURNS
7548  *     The size in bytes allocated for pData
7549  *
7550  * NOTES
7551  *     Memory allocated for pData must be freed by the caller
7552  *
7553  *     Used by OleConvertIStorageToOLESTREAM only.
7554  *
7555  */
7556 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7557 {
7558     HANDLE hFile;
7559     HRESULT hRes;
7560     DWORD nDataLength = 0;
7561     IStorage *pTempStorage;
7562     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7563     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7564
7565     *pData = NULL;
7566
7567     /* Create temp Storage */
7568     GetTempPathW(MAX_PATH, wstrTempDir);
7569     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7570     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7571
7572     if(hRes == S_OK)
7573     {
7574         /* Copy Src Storage to the Temp Storage */
7575         IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7576         IStorage_Release(pTempStorage);
7577
7578         /* Open Temp Storage as a file and copy to memory */
7579         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7580         if(hFile != INVALID_HANDLE_VALUE)
7581         {
7582             nDataLength = GetFileSize(hFile, NULL);
7583             *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7584             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7585             CloseHandle(hFile);
7586         }
7587         DeleteFileW(wstrTempFile);
7588     }
7589     return nDataLength;
7590 }
7591
7592 /*************************************************************************
7593  * OLECONVERT_CreateOleStream [Internal]
7594  *
7595  * Creates the "\001OLE" stream in the IStorage if necessary.
7596  *
7597  * PARAMS
7598  *     pStorage     [I] Dest storage to create the stream in
7599  *
7600  * RETURNS
7601  *     Nothing
7602  *
7603  * NOTES
7604  *     This function is used by OleConvertOLESTREAMToIStorage only.
7605  *
7606  *     This stream is still unknown, MS Word seems to have extra data
7607  *     but since the data is stored in the OLESTREAM there should be
7608  *     no need to recreate the stream.  If the stream is manually
7609  *     deleted it will create it with this default data.
7610  *
7611  */
7612 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7613 {
7614     HRESULT hRes;
7615     IStream *pStream;
7616     static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7617     BYTE pOleStreamHeader [] =
7618     {
7619         0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7620         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7621         0x00, 0x00, 0x00, 0x00
7622     };
7623
7624     /* Create stream if not present */
7625     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7626         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7627
7628     if(hRes == S_OK)
7629     {
7630         /* Write default Data */
7631         hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7632         IStream_Release(pStream);
7633     }
7634 }
7635
7636 /* write a string to a stream, preceded by its length */
7637 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7638 {
7639     HRESULT r;
7640     LPSTR str;
7641     DWORD len = 0;
7642
7643     if( string )
7644         len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7645     r = IStream_Write( stm, &len, sizeof(len), NULL);
7646     if( FAILED( r ) )
7647         return r;
7648     if(len == 0)
7649         return r;
7650     str = CoTaskMemAlloc( len );
7651     WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7652     r = IStream_Write( stm, str, len, NULL);
7653     CoTaskMemFree( str );
7654     return r;
7655 }
7656
7657 /* read a string preceded by its length from a stream */
7658 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7659 {
7660     HRESULT r;
7661     DWORD len, count = 0;
7662     LPSTR str;
7663     LPWSTR wstr;
7664
7665     r = IStream_Read( stm, &len, sizeof(len), &count );
7666     if( FAILED( r ) )
7667         return r;
7668     if( count != sizeof(len) )
7669         return E_OUTOFMEMORY;
7670
7671     TRACE("%d bytes\n",len);
7672     
7673     str = CoTaskMemAlloc( len );
7674     if( !str )
7675         return E_OUTOFMEMORY;
7676     count = 0;
7677     r = IStream_Read( stm, str, len, &count );
7678     if( FAILED( r ) )
7679         return r;
7680     if( count != len )
7681     {
7682         CoTaskMemFree( str );
7683         return E_OUTOFMEMORY;
7684     }
7685
7686     TRACE("Read string %s\n",debugstr_an(str,len));
7687
7688     len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7689     wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7690     if( wstr )
7691          MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7692     CoTaskMemFree( str );
7693
7694     *string = wstr;
7695
7696     return r;
7697 }
7698
7699
7700 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7701     LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7702 {
7703     IStream *pstm;
7704     HRESULT r = S_OK;
7705     static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7706
7707     static const BYTE unknown1[12] =
7708        { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7709          0xFF, 0xFF, 0xFF, 0xFF};
7710     static const BYTE unknown2[16] =
7711        { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7712          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7713
7714     TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7715            debugstr_w(lpszUserType), debugstr_w(szClipName),
7716            debugstr_w(szProgIDName));
7717
7718     /*  Create a CompObj stream */
7719     r = IStorage_CreateStream(pstg, szwStreamName,
7720         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7721     if( FAILED (r) )
7722         return r;
7723
7724     /* Write CompObj Structure to stream */
7725     r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7726
7727     if( SUCCEEDED( r ) )
7728         r = WriteClassStm( pstm, clsid );
7729
7730     if( SUCCEEDED( r ) )
7731         r = STREAM_WriteString( pstm, lpszUserType );
7732     if( SUCCEEDED( r ) )
7733         r = STREAM_WriteString( pstm, szClipName );
7734     if( SUCCEEDED( r ) )
7735         r = STREAM_WriteString( pstm, szProgIDName );
7736     if( SUCCEEDED( r ) )
7737         r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7738
7739     IStream_Release( pstm );
7740
7741     return r;
7742 }
7743
7744 /***********************************************************************
7745  *               WriteFmtUserTypeStg (OLE32.@)
7746  */
7747 HRESULT WINAPI WriteFmtUserTypeStg(
7748           LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7749 {
7750     HRESULT r;
7751     WCHAR szwClipName[0x40];
7752     CLSID clsid = CLSID_NULL;
7753     LPWSTR wstrProgID = NULL;
7754     DWORD n;
7755
7756     TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7757
7758     /* get the clipboard format name */
7759     n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7760     szwClipName[n]=0;
7761
7762     TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7763
7764     /* FIXME: There's room to save a CLSID and its ProgID, but
7765        the CLSID is not looked up in the registry and in all the
7766        tests I wrote it was CLSID_NULL.  Where does it come from?
7767     */
7768
7769     /* get the real program ID.  This may fail, but that's fine */
7770     ProgIDFromCLSID(&clsid, &wstrProgID);
7771
7772     TRACE("progid is %s\n",debugstr_w(wstrProgID));
7773
7774     r = STORAGE_WriteCompObj( pstg, &clsid, 
7775                               lpszUserType, szwClipName, wstrProgID );
7776
7777     CoTaskMemFree(wstrProgID);
7778
7779     return r;
7780 }
7781
7782
7783 /******************************************************************************
7784  *              ReadFmtUserTypeStg        [OLE32.@]
7785  */
7786 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7787 {
7788     HRESULT r;
7789     IStream *stm = 0;
7790     static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7791     unsigned char unknown1[12];
7792     unsigned char unknown2[16];
7793     DWORD count;
7794     LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7795     CLSID clsid;
7796
7797     TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7798
7799     r = IStorage_OpenStream( pstg, szCompObj, NULL, 
7800                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7801     if( FAILED ( r ) )
7802     {
7803         WARN("Failed to open stream r = %08x\n", r);
7804         return r;
7805     }
7806
7807     /* read the various parts of the structure */
7808     r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7809     if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7810         goto end;
7811     r = ReadClassStm( stm, &clsid );
7812     if( FAILED( r ) )
7813         goto end;
7814
7815     r = STREAM_ReadString( stm, &szCLSIDName );
7816     if( FAILED( r ) )
7817         goto end;
7818
7819     r = STREAM_ReadString( stm, &szOleTypeName );
7820     if( FAILED( r ) )
7821         goto end;
7822
7823     r = STREAM_ReadString( stm, &szProgIDName );
7824     if( FAILED( r ) )
7825         goto end;
7826
7827     r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7828     if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7829         goto end;
7830
7831     /* ok, success... now we just need to store what we found */
7832     if( pcf )
7833         *pcf = RegisterClipboardFormatW( szOleTypeName );
7834     CoTaskMemFree( szOleTypeName );
7835
7836     if( lplpszUserType )
7837         *lplpszUserType = szCLSIDName;
7838     CoTaskMemFree( szProgIDName );
7839
7840 end:
7841     IStream_Release( stm );
7842
7843     return r;
7844 }
7845
7846
7847 /*************************************************************************
7848  * OLECONVERT_CreateCompObjStream [Internal]
7849  *
7850  * Creates a "\001CompObj" is the destination IStorage if necessary.
7851  *
7852  * PARAMS
7853  *     pStorage       [I] The dest IStorage to create the CompObj Stream
7854  *                        if necessary.
7855  *     strOleTypeName [I] The ProgID
7856  *
7857  * RETURNS
7858  *     Success:  S_OK
7859  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7860  *
7861  * NOTES
7862  *     This function is used by OleConvertOLESTREAMToIStorage only.
7863  *
7864  *     The stream data is stored in the OLESTREAM and there should be
7865  *     no need to recreate the stream.  If the stream is manually
7866  *     deleted it will attempt to create it by querying the registry.
7867  *
7868  *
7869  */
7870 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7871 {
7872     IStream *pStream;
7873     HRESULT hStorageRes, hRes = S_OK;
7874     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7875     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7876     WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7877
7878     BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7879     BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7880
7881     /* Initialize the CompObj structure */
7882     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7883     memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7884     memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7885
7886
7887     /*  Create a CompObj stream if it doesn't exist */
7888     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7889         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7890     if(hStorageRes == S_OK)
7891     {
7892         /* copy the OleTypeName to the compobj struct */
7893         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7894         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7895
7896         /* copy the OleTypeName to the compobj struct */
7897         /* Note: in the test made, these were Identical      */
7898         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7899         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7900
7901         /* Get the CLSID */
7902         MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7903                              bufferW, OLESTREAM_MAX_STR_LEN );
7904         hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7905
7906         if(hRes == S_OK)
7907         {
7908             HKEY hKey;
7909             LONG hErr;
7910             /* Get the CLSID Default Name from the Registry */
7911             hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7912             if(hErr == ERROR_SUCCESS)
7913             {
7914                 char strTemp[OLESTREAM_MAX_STR_LEN];
7915                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7916                 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7917                 if(hErr == ERROR_SUCCESS)
7918                 {
7919                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
7920                 }
7921                 RegCloseKey(hKey);
7922             }
7923         }
7924
7925         /* Write CompObj Structure to stream */
7926         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7927
7928         WriteClassStm(pStream,&(IStorageCompObj.clsid));
7929
7930         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7931         if(IStorageCompObj.dwCLSIDNameLength > 0)
7932         {
7933             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7934         }
7935         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7936         if(IStorageCompObj.dwOleTypeNameLength > 0)
7937         {
7938             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7939         }
7940         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7941         if(IStorageCompObj.dwProgIDNameLength > 0)
7942         {
7943             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7944         }
7945         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7946         IStream_Release(pStream);
7947     }
7948     return hRes;
7949 }
7950
7951
7952 /*************************************************************************
7953  * OLECONVERT_CreateOlePresStream[Internal]
7954  *
7955  * Creates the "\002OlePres000" Stream with the Metafile data
7956  *
7957  * PARAMS
7958  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
7959  *     dwExtentX    [I] Width of the Metafile
7960  *     dwExtentY    [I] Height of the Metafile
7961  *     pData        [I] Metafile data
7962  *     dwDataLength [I] Size of the Metafile data
7963  *
7964  * RETURNS
7965  *     Success:  S_OK
7966  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
7967  *
7968  * NOTES
7969  *     This function is used by OleConvertOLESTREAMToIStorage only.
7970  *
7971  */
7972 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7973 {
7974     HRESULT hRes;
7975     IStream *pStream;
7976     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7977     BYTE pOlePresStreamHeader [] =
7978     {
7979         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7980         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7981         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7982         0x00, 0x00, 0x00, 0x00
7983     };
7984
7985     BYTE pOlePresStreamHeaderEmpty [] =
7986     {
7987         0x00, 0x00, 0x00, 0x00,
7988         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7989         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7990         0x00, 0x00, 0x00, 0x00
7991     };
7992
7993     /* Create the OlePres000 Stream */
7994     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7995         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7996
7997     if(hRes == S_OK)
7998     {
7999         DWORD nHeaderSize;
8000         OLECONVERT_ISTORAGE_OLEPRES OlePres;
8001
8002         memset(&OlePres, 0, sizeof(OlePres));
8003         /* Do we have any metafile data to save */
8004         if(dwDataLength > 0)
8005         {
8006             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8007             nHeaderSize = sizeof(pOlePresStreamHeader);
8008         }
8009         else
8010         {
8011             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8012             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8013         }
8014         /* Set width and height of the metafile */
8015         OlePres.dwExtentX = dwExtentX;
8016         OlePres.dwExtentY = -dwExtentY;
8017
8018         /* Set Data and Length */
8019         if(dwDataLength > sizeof(METAFILEPICT16))
8020         {
8021             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8022             OlePres.pData = &(pData[8]);
8023         }
8024         /* Save OlePres000 Data to Stream */
8025         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8026         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8027         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8028         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8029         if(OlePres.dwSize > 0)
8030         {
8031             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8032         }
8033         IStream_Release(pStream);
8034     }
8035 }
8036
8037 /*************************************************************************
8038  * OLECONVERT_CreateOle10NativeStream [Internal]
8039  *
8040  * Creates the "\001Ole10Native" Stream (should contain a BMP)
8041  *
8042  * PARAMS
8043  *     pStorage     [I] Dest storage to create the stream in
8044  *     pData        [I] Ole10 Native Data (ex. bmp)
8045  *     dwDataLength [I] Size of the Ole10 Native Data
8046  *
8047  * RETURNS
8048  *     Nothing
8049  *
8050  * NOTES
8051  *     This function is used by OleConvertOLESTREAMToIStorage only.
8052  *
8053  *     Might need to verify the data and return appropriate error message
8054  *
8055  */
8056 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8057 {
8058     HRESULT hRes;
8059     IStream *pStream;
8060     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8061
8062     /* Create the Ole10Native Stream */
8063     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8064         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8065
8066     if(hRes == S_OK)
8067     {
8068         /* Write info to stream */
8069         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8070         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8071         IStream_Release(pStream);
8072     }
8073
8074 }
8075
8076 /*************************************************************************
8077  * OLECONVERT_GetOLE10ProgID [Internal]
8078  *
8079  * Finds the ProgID (or OleTypeID) from the IStorage
8080  *
8081  * PARAMS
8082  *     pStorage        [I] The Src IStorage to get the ProgID
8083  *     strProgID       [I] the ProgID string to get
8084  *     dwSize          [I] the size of the string
8085  *
8086  * RETURNS
8087  *     Success:  S_OK
8088  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8089  *
8090  * NOTES
8091  *     This function is used by OleConvertIStorageToOLESTREAM only.
8092  *
8093  *
8094  */
8095 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8096 {
8097     HRESULT hRes;
8098     IStream *pStream;
8099     LARGE_INTEGER iSeekPos;
8100     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8101     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8102
8103     /* Open the CompObj Stream */
8104     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8105         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8106     if(hRes == S_OK)
8107     {
8108
8109         /*Get the OleType from the CompObj Stream */
8110         iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8111         iSeekPos.u.HighPart = 0;
8112
8113         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8114         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8115         iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8116         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8117         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8118         iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8119         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8120
8121         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8122         if(*dwSize > 0)
8123         {
8124             IStream_Read(pStream, strProgID, *dwSize, NULL);
8125         }
8126         IStream_Release(pStream);
8127     }
8128     else
8129     {
8130         STATSTG stat;
8131         LPOLESTR wstrProgID;
8132
8133         /* Get the OleType from the registry */
8134         REFCLSID clsid = &(stat.clsid);
8135         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8136         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8137         if(hRes == S_OK)
8138         {
8139             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8140         }
8141
8142     }
8143     return hRes;
8144 }
8145
8146 /*************************************************************************
8147  * OLECONVERT_GetOle10PresData [Internal]
8148  *
8149  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8150  *
8151  * PARAMS
8152  *     pStorage     [I] Src IStroage
8153  *     pOleStream   [I] Dest OleStream Mem Struct
8154  *
8155  * RETURNS
8156  *     Nothing
8157  *
8158  * NOTES
8159  *     This function is used by OleConvertIStorageToOLESTREAM only.
8160  *
8161  *     Memory allocated for pData must be freed by the caller
8162  *
8163  *
8164  */
8165 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8166 {
8167
8168     HRESULT hRes;
8169     IStream *pStream;
8170     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8171
8172     /* Initialize Default data for OLESTREAM */
8173     pOleStreamData[0].dwOleID = OLESTREAM_ID;
8174     pOleStreamData[0].dwTypeID = 2;
8175     pOleStreamData[1].dwOleID = OLESTREAM_ID;
8176     pOleStreamData[1].dwTypeID = 0;
8177     pOleStreamData[0].dwMetaFileWidth = 0;
8178     pOleStreamData[0].dwMetaFileHeight = 0;
8179     pOleStreamData[0].pData = NULL;
8180     pOleStreamData[1].pData = NULL;
8181
8182     /* Open Ole10Native Stream */
8183     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8184         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8185     if(hRes == S_OK)
8186     {
8187
8188         /* Read Size and Data */
8189         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8190         if(pOleStreamData->dwDataLength > 0)
8191         {
8192             pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8193             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8194         }
8195         IStream_Release(pStream);
8196     }
8197
8198 }
8199
8200
8201 /*************************************************************************
8202  * OLECONVERT_GetOle20PresData[Internal]
8203  *
8204  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8205  *
8206  * PARAMS
8207  *     pStorage         [I] Src IStroage
8208  *     pOleStreamData   [I] Dest OleStream Mem Struct
8209  *
8210  * RETURNS
8211  *     Nothing
8212  *
8213  * NOTES
8214  *     This function is used by OleConvertIStorageToOLESTREAM only.
8215  *
8216  *     Memory allocated for pData must be freed by the caller
8217  */
8218 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8219 {
8220     HRESULT hRes;
8221     IStream *pStream;
8222     OLECONVERT_ISTORAGE_OLEPRES olePress;
8223     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8224
8225     /* Initialize Default data for OLESTREAM */
8226     pOleStreamData[0].dwOleID = OLESTREAM_ID;
8227     pOleStreamData[0].dwTypeID = 2;
8228     pOleStreamData[0].dwMetaFileWidth = 0;
8229     pOleStreamData[0].dwMetaFileHeight = 0;
8230     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8231     pOleStreamData[1].dwOleID = OLESTREAM_ID;
8232     pOleStreamData[1].dwTypeID = 0;
8233     pOleStreamData[1].dwOleTypeNameLength = 0;
8234     pOleStreamData[1].strOleTypeName[0] = 0;
8235     pOleStreamData[1].dwMetaFileWidth = 0;
8236     pOleStreamData[1].dwMetaFileHeight = 0;
8237     pOleStreamData[1].pData = NULL;
8238     pOleStreamData[1].dwDataLength = 0;
8239
8240
8241     /* Open OlePress000 stream */
8242     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8243         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8244     if(hRes == S_OK)
8245     {
8246         LARGE_INTEGER iSeekPos;
8247         METAFILEPICT16 MetaFilePict;
8248         static const char strMetafilePictName[] = "METAFILEPICT";
8249
8250         /* Set the TypeID for a Metafile */
8251         pOleStreamData[1].dwTypeID = 5;
8252
8253         /* Set the OleTypeName to Metafile */
8254         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8255         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8256
8257         iSeekPos.u.HighPart = 0;
8258         iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8259
8260         /* Get Presentation Data */
8261         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8262         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8263         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8264         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8265
8266         /*Set width and Height */
8267         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8268         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8269         if(olePress.dwSize > 0)
8270         {
8271             /* Set Length */
8272             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
8273
8274             /* Set MetaFilePict struct */
8275             MetaFilePict.mm = 8;
8276             MetaFilePict.xExt = olePress.dwExtentX;
8277             MetaFilePict.yExt = olePress.dwExtentY;
8278             MetaFilePict.hMF = 0;
8279
8280             /* Get Metafile Data */
8281             pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8282             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8283             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8284         }
8285         IStream_Release(pStream);
8286     }
8287 }
8288
8289 /*************************************************************************
8290  * OleConvertOLESTREAMToIStorage [OLE32.@]
8291  *
8292  * Read info on MSDN
8293  *
8294  * TODO
8295  *      DVTARGETDEVICE parameter is not handled
8296  *      Still unsure of some mem fields for OLE 10 Stream
8297  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8298  *      and "\001OLE" streams
8299  *
8300  */
8301 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8302     LPOLESTREAM pOleStream,
8303     LPSTORAGE pstg,
8304     const DVTARGETDEVICE* ptd)
8305 {
8306     int i;
8307     HRESULT hRes=S_OK;
8308     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8309
8310     TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8311
8312     memset(pOleStreamData, 0, sizeof(pOleStreamData));
8313
8314     if(ptd != NULL)
8315     {
8316         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8317     }
8318
8319     if(pstg == NULL || pOleStream == NULL)
8320     {
8321         hRes = E_INVALIDARG;
8322     }
8323
8324     if(hRes == S_OK)
8325     {
8326         /* Load the OLESTREAM to Memory */
8327         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8328     }
8329
8330     if(hRes == S_OK)
8331     {
8332         /* Load the OLESTREAM to Memory (part 2)*/
8333         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8334     }
8335
8336     if(hRes == S_OK)
8337     {
8338
8339         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8340         {
8341             /* Do we have the IStorage Data in the OLESTREAM */
8342             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8343             {
8344                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8345                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8346             }
8347             else
8348             {
8349                 /* It must be an original OLE 1.0 source */
8350                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8351             }
8352         }
8353         else
8354         {
8355             /* It must be an original OLE 1.0 source */
8356             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8357         }
8358
8359         /* Create CompObj Stream if necessary */
8360         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8361         if(hRes == S_OK)
8362         {
8363             /*Create the Ole Stream if necessary */
8364             OLECONVERT_CreateOleStream(pstg);
8365         }
8366     }
8367
8368
8369     /* Free allocated memory */
8370     for(i=0; i < 2; i++)
8371     {
8372         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8373         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8374         pOleStreamData[i].pstrOleObjFileName = NULL;
8375     }
8376     return hRes;
8377 }
8378
8379 /*************************************************************************
8380  * OleConvertIStorageToOLESTREAM [OLE32.@]
8381  *
8382  * Read info on MSDN
8383  *
8384  * Read info on MSDN
8385  *
8386  * TODO
8387  *      Still unsure of some mem fields for OLE 10 Stream
8388  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8389  *      and "\001OLE" streams.
8390  *
8391  */
8392 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8393     LPSTORAGE pstg,
8394     LPOLESTREAM pOleStream)
8395 {
8396     int i;
8397     HRESULT hRes = S_OK;
8398     IStream *pStream;
8399     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8400     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8401
8402     TRACE("%p %p\n", pstg, pOleStream);
8403
8404     memset(pOleStreamData, 0, sizeof(pOleStreamData));
8405
8406     if(pstg == NULL || pOleStream == NULL)
8407     {
8408         hRes = E_INVALIDARG;
8409     }
8410     if(hRes == S_OK)
8411     {
8412         /* Get the ProgID */
8413         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8414         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8415     }
8416     if(hRes == S_OK)
8417     {
8418         /* Was it originally Ole10 */
8419         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8420         if(hRes == S_OK)
8421         {
8422             IStream_Release(pStream);
8423             /* Get Presentation Data for Ole10Native */
8424             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8425         }
8426         else
8427         {
8428             /* Get Presentation Data (OLE20) */
8429             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8430         }
8431
8432         /* Save OLESTREAM */
8433         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8434         if(hRes == S_OK)
8435         {
8436             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8437         }
8438
8439     }
8440
8441     /* Free allocated memory */
8442     for(i=0; i < 2; i++)
8443     {
8444         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8445     }
8446
8447     return hRes;
8448 }
8449
8450 /***********************************************************************
8451  *              GetConvertStg (OLE32.@)
8452  */
8453 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8454     FIXME("unimplemented stub!\n");
8455     return E_FAIL;
8456 }
8457
8458 /******************************************************************************
8459  * StgIsStorageFile [OLE32.@]
8460  * Verify if the file contains a storage object
8461  *
8462  * PARAMS
8463  *  fn      [ I] Filename
8464  *
8465  * RETURNS
8466  *  S_OK    if file has magic bytes as a storage object
8467  *  S_FALSE if file is not storage
8468  */
8469 HRESULT WINAPI
8470 StgIsStorageFile(LPCOLESTR fn)
8471 {
8472         HANDLE          hf;
8473         BYTE            magic[8];
8474         DWORD           bytes_read;
8475
8476         TRACE("%s\n", debugstr_w(fn));
8477         hf = CreateFileW(fn, GENERIC_READ,
8478                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8479                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8480
8481         if (hf == INVALID_HANDLE_VALUE)
8482                 return STG_E_FILENOTFOUND;
8483
8484         if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8485         {
8486                 WARN(" unable to read file\n");
8487                 CloseHandle(hf);
8488                 return S_FALSE;
8489         }
8490
8491         CloseHandle(hf);
8492
8493         if (bytes_read != 8) {
8494                 WARN(" too short\n");
8495                 return S_FALSE;
8496         }
8497
8498         if (!memcmp(magic,STORAGE_magic,8)) {
8499                 WARN(" -> YES\n");
8500                 return S_OK;
8501         }
8502
8503         WARN(" -> Invalid header.\n");
8504         return S_FALSE;
8505 }
8506
8507 /***********************************************************************
8508  *              WriteClassStm (OLE32.@)
8509  *
8510  * Writes a CLSID to a stream.
8511  *
8512  * PARAMS
8513  *  pStm   [I] Stream to write to.
8514  *  rclsid [I] CLSID to write.
8515  *
8516  * RETURNS
8517  *  Success: S_OK.
8518  *  Failure: HRESULT code.
8519  */
8520 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8521 {
8522     TRACE("(%p,%p)\n",pStm,rclsid);
8523
8524     if (!pStm || !rclsid)
8525         return E_INVALIDARG;
8526
8527     return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8528 }
8529
8530 /***********************************************************************
8531  *              ReadClassStm (OLE32.@)
8532  *
8533  * Reads a CLSID from a stream.
8534  *
8535  * PARAMS
8536  *  pStm   [I] Stream to read from.
8537  *  rclsid [O] CLSID to read.
8538  *
8539  * RETURNS
8540  *  Success: S_OK.
8541  *  Failure: HRESULT code.
8542  */
8543 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8544 {
8545     ULONG nbByte;
8546     HRESULT res;
8547
8548     TRACE("(%p,%p)\n",pStm,pclsid);
8549
8550     if (!pStm || !pclsid)
8551         return E_INVALIDARG;
8552
8553     /* clear the output args */
8554     *pclsid = CLSID_NULL;
8555
8556     res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8557
8558     if (FAILED(res))
8559         return res;
8560
8561     if (nbByte != sizeof(CLSID))
8562         return STG_E_READFAULT;
8563     else
8564         return S_OK;
8565 }