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