kernel32: Add the directory the executable was loaded from to the module search path...
[wine] / dlls / ole32 / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winerror.h"
39 #include "winreg.h"
40 #include "winternl.h"
41 #include "wine/debug.h"
42
43 #include "storage32.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
46
47
48 /***
49  * This is the destructor of the StgStreamImpl class.
50  *
51  * This method will clean-up all the resources used-up by the given StgStreamImpl
52  * class. The pointer passed-in to this function will be freed and will not
53  * be valid anymore.
54  */
55 static void StgStreamImpl_Destroy(StgStreamImpl* This)
56 {
57   TRACE("(%p)\n", This);
58
59   /*
60    * Release the reference we are holding on the parent storage.
61    * IStorage_Release((IStorage*)This->parentStorage);
62    *
63    * No, don't do this. Some apps call IStorage_Release without
64    * calling IStream_Release first. If we grab a reference the
65    * file is not closed, and the app fails when it tries to
66    * reopen the file (Easy-PC, for example). Just inform the
67    * storage that we have closed the stream
68    */
69
70   if(This->parentStorage) {
71
72     StorageBaseImpl_RemoveStream(This->parentStorage, This);
73
74   }
75
76   This->parentStorage = 0;
77
78   /*
79    * Make sure we clean-up the block chain stream objects that we were using.
80    */
81   if (This->bigBlockChain != 0)
82   {
83     BlockChainStream_Destroy(This->bigBlockChain);
84     This->bigBlockChain = 0;
85   }
86
87   if (This->smallBlockChain != 0)
88   {
89     SmallBlockChainStream_Destroy(This->smallBlockChain);
90     This->smallBlockChain = 0;
91   }
92
93   /*
94    * Finally, free the memory used-up by the class.
95    */
96   HeapFree(GetProcessHeap(), 0, This);
97 }
98
99 /***
100  * This implements the IUnknown method QueryInterface for this
101  * class
102  */
103 static HRESULT WINAPI StgStreamImpl_QueryInterface(
104                   IStream*     iface,
105                   REFIID         riid,        /* [in] */
106                   void**         ppvObject)   /* [iid_is][out] */
107 {
108   StgStreamImpl* const This=(StgStreamImpl*)iface;
109
110   /*
111    * Perform a sanity check on the parameters.
112    */
113   if (ppvObject==0)
114     return E_INVALIDARG;
115
116   /*
117    * Initialize the return parameter.
118    */
119   *ppvObject = 0;
120
121   /*
122    * Compare the riid with the interface IDs implemented by this object.
123    */
124   if (IsEqualIID(&IID_IUnknown, riid) ||
125       IsEqualIID(&IID_IPersist, riid) ||
126       IsEqualIID(&IID_IPersistStream, riid) ||
127       IsEqualIID(&IID_ISequentialStream, riid) ||
128       IsEqualIID(&IID_IStream, riid))
129   {
130     *ppvObject = (IStream*)This;
131   }
132
133   /*
134    * Check that we obtained an interface.
135    */
136   if ((*ppvObject)==0)
137     return E_NOINTERFACE;
138
139   /*
140    * Query Interface always increases the reference count by one when it is
141    * successful
142    */
143   IStream_AddRef(iface);
144
145   return S_OK;
146 }
147
148 /***
149  * This implements the IUnknown method AddRef for this
150  * class
151  */
152 static ULONG WINAPI StgStreamImpl_AddRef(
153                 IStream* iface)
154 {
155   StgStreamImpl* const This=(StgStreamImpl*)iface;
156   return InterlockedIncrement(&This->ref);
157 }
158
159 /***
160  * This implements the IUnknown method Release for this
161  * class
162  */
163 static ULONG WINAPI StgStreamImpl_Release(
164                 IStream* iface)
165 {
166   StgStreamImpl* const This=(StgStreamImpl*)iface;
167
168   ULONG ref;
169
170   ref = InterlockedDecrement(&This->ref);
171
172   /*
173    * If the reference count goes down to 0, perform suicide.
174    */
175   if (ref==0)
176   {
177     StgStreamImpl_Destroy(This);
178   }
179
180   return ref;
181 }
182
183 /***
184  * This method will open the block chain pointed by the property
185  * that describes the stream.
186  * If the stream's size is null, no chain is opened.
187  */
188 static void StgStreamImpl_OpenBlockChain(
189         StgStreamImpl* This)
190 {
191   StgProperty    curProperty;
192   BOOL         readSuccessful;
193
194   /*
195    * Make sure no old object is left over.
196    */
197   if (This->smallBlockChain != 0)
198   {
199     SmallBlockChainStream_Destroy(This->smallBlockChain);
200     This->smallBlockChain = 0;
201   }
202
203   if (This->bigBlockChain != 0)
204   {
205     BlockChainStream_Destroy(This->bigBlockChain);
206     This->bigBlockChain = 0;
207   }
208
209   /*
210    * Read the information from the property.
211    */
212   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
213                                              This->ownerProperty,
214                                              &curProperty);
215
216   if (readSuccessful)
217   {
218     This->streamSize = curProperty.size;
219
220     /*
221      * This code supports only streams that are <32 bits in size.
222      */
223     assert(This->streamSize.u.HighPart == 0);
224
225     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
226     {
227       assert( (This->streamSize.u.HighPart == 0) && (This->streamSize.u.LowPart == 0) );
228     }
229     else
230     {
231       if ( (This->streamSize.u.HighPart == 0) &&
232            (This->streamSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
233       {
234         This->smallBlockChain = SmallBlockChainStream_Construct(
235                                                                 This->parentStorage->ancestorStorage,
236                                                                 This->ownerProperty);
237       }
238       else
239       {
240         This->bigBlockChain = BlockChainStream_Construct(
241                                                          This->parentStorage->ancestorStorage,
242                                                          NULL,
243                                                          This->ownerProperty);
244       }
245     }
246   }
247 }
248
249 /***
250  * This method is part of the ISequentialStream interface.
251  *
252  * It reads a block of information from the stream at the current
253  * position. It then moves the current position at the end of the
254  * read block
255  *
256  * See the documentation of ISequentialStream for more info.
257  */
258 static HRESULT WINAPI StgStreamImpl_Read(
259                   IStream*     iface,
260                   void*          pv,        /* [length_is][size_is][out] */
261                   ULONG          cb,        /* [in] */
262                   ULONG*         pcbRead)   /* [out] */
263 {
264   StgStreamImpl* const This=(StgStreamImpl*)iface;
265
266   ULONG bytesReadBuffer;
267   ULONG bytesToReadFromBuffer;
268   HRESULT res;
269
270   TRACE("(%p, %p, %d, %p)\n",
271         iface, pv, cb, pcbRead);
272
273   if (!This->parentStorage)
274   {
275     WARN("storage reverted\n");
276     return STG_E_REVERTED;
277   }
278
279   /*
280    * If the caller is not interested in the number of bytes read,
281    * we use another buffer to avoid "if" statements in the code.
282    */
283   if (pcbRead==0)
284     pcbRead = &bytesReadBuffer;
285
286   /*
287    * Using the known size of the stream, calculate the number of bytes
288    * to read from the block chain
289    */
290   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
291
292   /*
293    * Depending on the type of chain that was opened when the stream was constructed,
294    * we delegate the work to the method that reads the block chains.
295    */
296   if (This->smallBlockChain!=0)
297   {
298     res = SmallBlockChainStream_ReadAt(This->smallBlockChain,
299                                  This->currentPosition,
300                                  bytesToReadFromBuffer,
301                                  pv,
302                                  pcbRead);
303
304   }
305   else if (This->bigBlockChain!=0)
306   {
307     res = BlockChainStream_ReadAt(This->bigBlockChain,
308                  This->currentPosition,
309                  bytesToReadFromBuffer,
310                  pv,
311                  pcbRead);
312   }
313   else
314   {
315     /*
316      * Small and big block chains are both NULL. This case will happen
317      * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
318      */
319
320     *pcbRead = 0;
321     res = S_OK;
322     goto end;
323   }
324
325   if (SUCCEEDED(res))
326   {
327       /*
328        * We should always be able to read the proper amount of data from the
329        * chain.
330        */
331       assert(bytesToReadFromBuffer == *pcbRead);
332
333       /*
334        * Advance the pointer for the number of positions read.
335        */
336       This->currentPosition.u.LowPart += *pcbRead;
337   }
338
339 end:
340   TRACE("<-- %08x\n", res);
341   return res;
342 }
343
344 /***
345  * This method is part of the ISequentialStream interface.
346  *
347  * It writes a block of information to the stream at the current
348  * position. It then moves the current position at the end of the
349  * written block. If the stream is too small to fit the block,
350  * the stream is grown to fit.
351  *
352  * See the documentation of ISequentialStream for more info.
353  */
354 static HRESULT WINAPI StgStreamImpl_Write(
355                   IStream*     iface,
356                   const void*    pv,          /* [size_is][in] */
357                   ULONG          cb,          /* [in] */
358                   ULONG*         pcbWritten)  /* [out] */
359 {
360   StgStreamImpl* const This=(StgStreamImpl*)iface;
361
362   ULARGE_INTEGER newSize;
363   ULONG bytesWritten = 0;
364   HRESULT res;
365
366   TRACE("(%p, %p, %d, %p)\n",
367         iface, pv, cb, pcbWritten);
368
369   /*
370    * Do we have permission to write to this stream?
371    */
372   switch(STGM_ACCESS_MODE(This->grfMode))
373   {
374   case STGM_WRITE:
375   case STGM_READWRITE:
376       break;
377   default:
378       WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode));
379       return STG_E_ACCESSDENIED;
380   }
381
382   if (!pv)
383     return STG_E_INVALIDPOINTER;
384
385   if (!This->parentStorage)
386   {
387     WARN("storage reverted\n");
388     return STG_E_REVERTED;
389   }
390  
391   /*
392    * If the caller is not interested in the number of bytes written,
393    * we use another buffer to avoid "if" statements in the code.
394    */
395   if (pcbWritten == 0)
396     pcbWritten = &bytesWritten;
397
398   /*
399    * Initialize the out parameter
400    */
401   *pcbWritten = 0;
402
403   if (cb == 0)
404   {
405     TRACE("<-- S_OK, written 0\n");
406     return S_OK;
407   }
408   else
409   {
410     newSize.u.HighPart = 0;
411     newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
412   }
413
414   /*
415    * Verify if we need to grow the stream
416    */
417   if (newSize.u.LowPart > This->streamSize.u.LowPart)
418   {
419     /* grow stream */
420     res = IStream_SetSize(iface, newSize);
421     if (FAILED(res))
422       return res;
423   }
424
425   /*
426    * Depending on the type of chain that was opened when the stream was constructed,
427    * we delegate the work to the method that readwrites to the block chains.
428    */
429   if (This->smallBlockChain!=0)
430   {
431     res = SmallBlockChainStream_WriteAt(This->smallBlockChain,
432                                   This->currentPosition,
433                                   cb,
434                                   pv,
435                                   pcbWritten);
436
437   }
438   else if (This->bigBlockChain!=0)
439   {
440     res = BlockChainStream_WriteAt(This->bigBlockChain,
441                              This->currentPosition,
442                              cb,
443                              pv,
444                              pcbWritten);
445   }
446   else
447   {
448     /* this should never happen because the IStream_SetSize call above will
449      * make sure a big or small block chain is created */
450     assert(FALSE);
451     res = 0;
452   }
453
454   /*
455    * Advance the position pointer for the number of positions written.
456    */
457   This->currentPosition.u.LowPart += *pcbWritten;
458
459   TRACE("<-- S_OK, written %u\n", *pcbWritten);
460   return res;
461 }
462
463 /***
464  * This method is part of the IStream interface.
465  *
466  * It will move the current stream pointer according to the parameters
467  * given.
468  *
469  * See the documentation of IStream for more info.
470  */
471 static HRESULT WINAPI StgStreamImpl_Seek(
472                   IStream*      iface,
473                   LARGE_INTEGER   dlibMove,         /* [in] */
474                   DWORD           dwOrigin,         /* [in] */
475                   ULARGE_INTEGER* plibNewPosition) /* [out] */
476 {
477   StgStreamImpl* const This=(StgStreamImpl*)iface;
478
479   ULARGE_INTEGER newPosition;
480
481   TRACE("(%p, %d, %d, %p)\n",
482         iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
483
484   /*
485    * fail if the stream has no parent (as does windows)
486    */
487
488   if (!This->parentStorage)
489   {
490     WARN("storage reverted\n");
491     return STG_E_REVERTED;
492   }
493
494   /*
495    * The caller is allowed to pass in NULL as the new position return value.
496    * If it happens, we assign it to a dynamic variable to avoid special cases
497    * in the code below.
498    */
499   if (plibNewPosition == 0)
500   {
501     plibNewPosition = &newPosition;
502   }
503
504   /*
505    * The file pointer is moved depending on the given "function"
506    * parameter.
507    */
508   switch (dwOrigin)
509   {
510     case STREAM_SEEK_SET:
511       plibNewPosition->u.HighPart = 0;
512       plibNewPosition->u.LowPart  = 0;
513       break;
514     case STREAM_SEEK_CUR:
515       *plibNewPosition = This->currentPosition;
516       break;
517     case STREAM_SEEK_END:
518       *plibNewPosition = This->streamSize;
519       break;
520     default:
521       WARN("invalid dwOrigin %d\n", dwOrigin);
522       return STG_E_INVALIDFUNCTION;
523   }
524
525   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
526
527   /*
528    * tell the caller what we calculated
529    */
530   This->currentPosition = *plibNewPosition;
531
532   return S_OK;
533 }
534
535 /***
536  * This method is part of the IStream interface.
537  *
538  * It will change the size of a stream.
539  *
540  * TODO: Switch from small blocks to big blocks and vice versa.
541  *
542  * See the documentation of IStream for more info.
543  */
544 static HRESULT WINAPI StgStreamImpl_SetSize(
545                                      IStream*      iface,
546                                      ULARGE_INTEGER  libNewSize)   /* [in] */
547 {
548   StgStreamImpl* const This=(StgStreamImpl*)iface;
549
550   StgProperty    curProperty;
551   BOOL         Success;
552
553   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
554
555   if(!This->parentStorage)
556   {
557     WARN("storage reverted\n");
558     return STG_E_REVERTED;
559   }
560
561   /*
562    * As documented.
563    */
564   if (libNewSize.u.HighPart != 0)
565   {
566     WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize.u.HighPart);
567     return STG_E_INVALIDFUNCTION;
568   }
569
570   /*
571    * Do we have permission?
572    */
573   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
574   {
575     WARN("access denied\n");
576     return STG_E_ACCESSDENIED;
577   }
578
579   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
580     return S_OK;
581
582   /*
583    * This will happen if we're creating a stream
584    */
585   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
586   {
587     if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
588     {
589       This->smallBlockChain = SmallBlockChainStream_Construct(
590                                     This->parentStorage->ancestorStorage,
591                                     This->ownerProperty);
592     }
593     else
594     {
595       This->bigBlockChain = BlockChainStream_Construct(
596                                 This->parentStorage->ancestorStorage,
597                                 NULL,
598                                 This->ownerProperty);
599     }
600   }
601
602   /*
603    * Read this stream's property to see if it's small blocks or big blocks
604    */
605   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
606                                        This->ownerProperty,
607                                        &curProperty);
608   /*
609    * Determine if we have to switch from small to big blocks or vice versa
610    */
611   if ( (This->smallBlockChain!=0) &&
612        (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
613   {
614     if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
615     {
616       /*
617        * Transform the small block chain into a big block chain
618        */
619       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
620                                 This->parentStorage->ancestorStorage,
621                                 &This->smallBlockChain);
622     }
623   }
624
625   if (This->smallBlockChain!=0)
626   {
627     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
628   }
629   else
630   {
631     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
632   }
633
634   /*
635    * Write the new information about this stream to the property
636    */
637   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
638                                        This->ownerProperty,
639                                        &curProperty);
640
641   curProperty.size.u.HighPart = libNewSize.u.HighPart;
642   curProperty.size.u.LowPart = libNewSize.u.LowPart;
643
644   if (Success)
645   {
646     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
647                                 This->ownerProperty,
648                                 &curProperty);
649   }
650
651   This->streamSize = libNewSize;
652
653   return S_OK;
654 }
655
656 /***
657  * This method is part of the IStream interface.
658  *
659  * It will copy the 'cb' Bytes to 'pstm' IStream.
660  *
661  * See the documentation of IStream for more info.
662  */
663 static HRESULT WINAPI StgStreamImpl_CopyTo(
664                                     IStream*      iface,
665                                     IStream*      pstm,         /* [unique][in] */
666                                     ULARGE_INTEGER  cb,           /* [in] */
667                                     ULARGE_INTEGER* pcbRead,      /* [out] */
668                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
669 {
670   StgStreamImpl* const This=(StgStreamImpl*)iface;
671   HRESULT        hr = S_OK;
672   BYTE           tmpBuffer[128];
673   ULONG          bytesRead, bytesWritten, copySize;
674   ULARGE_INTEGER totalBytesRead;
675   ULARGE_INTEGER totalBytesWritten;
676
677   TRACE("(%p, %p, %d, %p, %p)\n",
678         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
679
680   /*
681    * Sanity check
682    */
683
684   if (!This->parentStorage)
685   {
686     WARN("storage reverted\n");
687     return STG_E_REVERTED;
688   }
689
690   if ( pstm == 0 )
691     return STG_E_INVALIDPOINTER;
692
693   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
694   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
695
696   /*
697    * use stack to store data temporarily
698    * there is surely a more performant way of doing it, for now this basic
699    * implementation will do the job
700    */
701   while ( cb.u.LowPart > 0 )
702   {
703     if ( cb.u.LowPart >= 128 )
704       copySize = 128;
705     else
706       copySize = cb.u.LowPart;
707
708     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
709
710     totalBytesRead.u.LowPart += bytesRead;
711
712     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
713
714     totalBytesWritten.u.LowPart += bytesWritten;
715
716     /*
717      * Check that read & write operations were successful
718      */
719     if (bytesRead != bytesWritten)
720     {
721       hr = STG_E_MEDIUMFULL;
722       WARN("medium full\n");
723       break;
724     }
725
726     if (bytesRead!=copySize)
727       cb.u.LowPart = 0;
728     else
729       cb.u.LowPart -= bytesRead;
730   }
731
732   /*
733    * Update number of bytes read and written
734    */
735   if (pcbRead)
736   {
737     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
738     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
739   }
740
741   if (pcbWritten)
742   {
743     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
744     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
745   }
746   return hr;
747 }
748
749 /***
750  * This method is part of the IStream interface.
751  *
752  * For streams contained in structured storages, this method
753  * does nothing. This is what the documentation tells us.
754  *
755  * See the documentation of IStream for more info.
756  */
757 static HRESULT WINAPI StgStreamImpl_Commit(
758                   IStream*      iface,
759                   DWORD           grfCommitFlags)  /* [in] */
760 {
761   StgStreamImpl* const This=(StgStreamImpl*)iface;
762
763   if (!This->parentStorage)
764   {
765     WARN("storage reverted\n");
766     return STG_E_REVERTED;
767   }
768
769   return S_OK;
770 }
771
772 /***
773  * This method is part of the IStream interface.
774  *
775  * For streams contained in structured storages, this method
776  * does nothing. This is what the documentation tells us.
777  *
778  * See the documentation of IStream for more info.
779  */
780 static HRESULT WINAPI StgStreamImpl_Revert(
781                   IStream* iface)
782 {
783   return S_OK;
784 }
785
786 static HRESULT WINAPI StgStreamImpl_LockRegion(
787                                         IStream*     iface,
788                                         ULARGE_INTEGER libOffset,   /* [in] */
789                                         ULARGE_INTEGER cb,          /* [in] */
790                                         DWORD          dwLockType)  /* [in] */
791 {
792   StgStreamImpl* const This=(StgStreamImpl*)iface;
793
794   if (!This->parentStorage)
795   {
796     WARN("storage reverted\n");
797     return STG_E_REVERTED;
798   }
799
800   FIXME("not implemented!\n");
801   return E_NOTIMPL;
802 }
803
804 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
805                                           IStream*     iface,
806                                           ULARGE_INTEGER libOffset,   /* [in] */
807                                           ULARGE_INTEGER cb,          /* [in] */
808                                           DWORD          dwLockType)  /* [in] */
809 {
810   StgStreamImpl* const This=(StgStreamImpl*)iface;
811
812   if (!This->parentStorage)
813   {
814     WARN("storage reverted\n");
815     return STG_E_REVERTED;
816   }
817
818   FIXME("not implemented!\n");
819   return E_NOTIMPL;
820 }
821
822 /***
823  * This method is part of the IStream interface.
824  *
825  * This method returns information about the current
826  * stream.
827  *
828  * See the documentation of IStream for more info.
829  */
830 static HRESULT WINAPI StgStreamImpl_Stat(
831                   IStream*     iface,
832                   STATSTG*       pstatstg,     /* [out] */
833                   DWORD          grfStatFlag)  /* [in] */
834 {
835   StgStreamImpl* const This=(StgStreamImpl*)iface;
836
837   StgProperty    curProperty;
838   BOOL         readSuccessful;
839
840   TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
841
842   /*
843    * if stream has no parent, return STG_E_REVERTED
844    */
845
846   if (!This->parentStorage)
847   {
848     WARN("storage reverted\n");
849     return STG_E_REVERTED;
850   }
851
852   /*
853    * Read the information from the property.
854    */
855   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
856                                              This->ownerProperty,
857                                              &curProperty);
858
859   if (readSuccessful)
860   {
861     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
862                                      &curProperty,
863                                      grfStatFlag);
864
865     pstatstg->grfMode = This->grfMode;
866
867     return S_OK;
868   }
869
870   WARN("failed to read properties\n");
871   return E_FAIL;
872 }
873
874 /***
875  * This method is part of the IStream interface.
876  *
877  * This method returns a clone of the interface that allows for
878  * another seek pointer
879  *
880  * See the documentation of IStream for more info.
881  *
882  * I am not totally sure what I am doing here but I presume that this
883  * should be basically as simple as creating a new stream with the same
884  * parent etc and positioning its seek cursor.
885  */
886 static HRESULT WINAPI StgStreamImpl_Clone(
887                                    IStream*     iface,
888                                    IStream**    ppstm) /* [out] */
889 {
890   StgStreamImpl* const This=(StgStreamImpl*)iface;
891   HRESULT hres;
892   StgStreamImpl* new_stream;
893   LARGE_INTEGER seek_pos;
894
895   TRACE("%p %p\n", This, ppstm);
896
897   /*
898    * Sanity check
899    */
900
901   if (!This->parentStorage)
902     return STG_E_REVERTED;
903
904   if ( ppstm == 0 )
905     return STG_E_INVALIDPOINTER;
906
907   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
908
909   if (!new_stream)
910     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
911
912   *ppstm = (IStream*) new_stream;
913   IStream_AddRef(*ppstm);
914
915   seek_pos.QuadPart = This->currentPosition.QuadPart;
916
917   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
918
919   assert (SUCCEEDED(hres));
920
921   return S_OK;
922 }
923
924 /*
925  * Virtual function table for the StgStreamImpl class.
926  */
927 static const IStreamVtbl StgStreamImpl_Vtbl =
928 {
929     StgStreamImpl_QueryInterface,
930     StgStreamImpl_AddRef,
931     StgStreamImpl_Release,
932     StgStreamImpl_Read,
933     StgStreamImpl_Write,
934     StgStreamImpl_Seek,
935     StgStreamImpl_SetSize,
936     StgStreamImpl_CopyTo,
937     StgStreamImpl_Commit,
938     StgStreamImpl_Revert,
939     StgStreamImpl_LockRegion,
940     StgStreamImpl_UnlockRegion,
941     StgStreamImpl_Stat,
942     StgStreamImpl_Clone
943 };
944
945 /******************************************************************************
946 ** StgStreamImpl implementation
947 */
948
949 /***
950  * This is the constructor for the StgStreamImpl class.
951  *
952  * Params:
953  *    parentStorage - Pointer to the storage that contains the stream to open
954  *    ownerProperty - Index of the property that points to this stream.
955  */
956 StgStreamImpl* StgStreamImpl_Construct(
957                 StorageBaseImpl* parentStorage,
958     DWORD            grfMode,
959     ULONG            ownerProperty)
960 {
961   StgStreamImpl* newStream;
962
963   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
964
965   if (newStream!=0)
966   {
967     /*
968      * Set-up the virtual function table and reference count.
969      */
970     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
971     newStream->ref       = 0;
972
973     newStream->parentStorage = parentStorage;
974
975     /*
976      * We want to nail-down the reference to the storage in case the
977      * stream out-lives the storage in the client application.
978      *
979      * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
980      *
981      * No, don't do this. Some apps call IStorage_Release without
982      * calling IStream_Release first. If we grab a reference the
983      * file is not closed, and the app fails when it tries to
984      * reopen the file (Easy-PC, for example)
985      */
986
987     newStream->grfMode = grfMode;
988     newStream->ownerProperty = ownerProperty;
989
990     /*
991      * Start the stream at the beginning.
992      */
993     newStream->currentPosition.u.HighPart = 0;
994     newStream->currentPosition.u.LowPart = 0;
995
996     /*
997      * Initialize the rest of the data.
998      */
999     newStream->streamSize.u.HighPart = 0;
1000     newStream->streamSize.u.LowPart  = 0;
1001     newStream->bigBlockChain       = 0;
1002     newStream->smallBlockChain     = 0;
1003
1004     /*
1005      * Read the size from the property and determine if the blocks forming
1006      * this stream are large or small.
1007      */
1008     StgStreamImpl_OpenBlockChain(newStream);
1009
1010     /* add us to the storage's list of active streams */
1011     StorageBaseImpl_AddStream(parentStorage, newStream);
1012   }
1013
1014   return newStream;
1015 }