oleaut32/tests: Avoid relying on system-dependent rounding.
[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.u.LowPart = totalBytesRead.u.HighPart = 0;
693   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
694
695   /*
696    * use stack to store data temporarily
697    * there is surely a more performant way of doing it, for now this basic
698    * implementation will do the job
699    */
700   while ( cb.u.LowPart > 0 )
701   {
702     if ( cb.u.LowPart >= 128 )
703       copySize = 128;
704     else
705       copySize = cb.u.LowPart;
706
707     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
708
709     totalBytesRead.u.LowPart += bytesRead;
710
711     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
712
713     totalBytesWritten.u.LowPart += bytesWritten;
714
715     /*
716      * Check that read & write operations were successful
717      */
718     if (bytesRead != bytesWritten)
719     {
720       hr = STG_E_MEDIUMFULL;
721       WARN("medium full\n");
722       break;
723     }
724
725     if (bytesRead!=copySize)
726       cb.u.LowPart = 0;
727     else
728       cb.u.LowPart -= bytesRead;
729   }
730
731   /*
732    * Update number of bytes read and written
733    */
734   if (pcbRead)
735   {
736     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
737     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
738   }
739
740   if (pcbWritten)
741   {
742     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
743     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
744   }
745   return hr;
746 }
747
748 /***
749  * This method is part of the IStream interface.
750  *
751  * For streams contained in structured storages, this method
752  * does nothing. This is what the documentation tells us.
753  *
754  * See the documentation of IStream for more info.
755  */
756 static HRESULT WINAPI StgStreamImpl_Commit(
757                   IStream*      iface,
758                   DWORD           grfCommitFlags)  /* [in] */
759 {
760   StgStreamImpl* const This=(StgStreamImpl*)iface;
761
762   if (!This->parentStorage)
763   {
764     WARN("storage reverted\n");
765     return STG_E_REVERTED;
766   }
767
768   return S_OK;
769 }
770
771 /***
772  * This method is part of the IStream interface.
773  *
774  * For streams contained in structured storages, this method
775  * does nothing. This is what the documentation tells us.
776  *
777  * See the documentation of IStream for more info.
778  */
779 static HRESULT WINAPI StgStreamImpl_Revert(
780                   IStream* iface)
781 {
782   return S_OK;
783 }
784
785 static HRESULT WINAPI StgStreamImpl_LockRegion(
786                                         IStream*     iface,
787                                         ULARGE_INTEGER libOffset,   /* [in] */
788                                         ULARGE_INTEGER cb,          /* [in] */
789                                         DWORD          dwLockType)  /* [in] */
790 {
791   StgStreamImpl* const This=(StgStreamImpl*)iface;
792
793   if (!This->parentStorage)
794   {
795     WARN("storage reverted\n");
796     return STG_E_REVERTED;
797   }
798
799   FIXME("not implemented!\n");
800   return E_NOTIMPL;
801 }
802
803 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
804                                           IStream*     iface,
805                                           ULARGE_INTEGER libOffset,   /* [in] */
806                                           ULARGE_INTEGER cb,          /* [in] */
807                                           DWORD          dwLockType)  /* [in] */
808 {
809   StgStreamImpl* const This=(StgStreamImpl*)iface;
810
811   if (!This->parentStorage)
812   {
813     WARN("storage reverted\n");
814     return STG_E_REVERTED;
815   }
816
817   FIXME("not implemented!\n");
818   return E_NOTIMPL;
819 }
820
821 /***
822  * This method is part of the IStream interface.
823  *
824  * This method returns information about the current
825  * stream.
826  *
827  * See the documentation of IStream for more info.
828  */
829 static HRESULT WINAPI StgStreamImpl_Stat(
830                   IStream*     iface,
831                   STATSTG*       pstatstg,     /* [out] */
832                   DWORD          grfStatFlag)  /* [in] */
833 {
834   StgStreamImpl* const This=(StgStreamImpl*)iface;
835
836   StgProperty    curProperty;
837   BOOL         readSuccessful;
838
839   TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
840
841   /*
842    * if stream has no parent, return STG_E_REVERTED
843    */
844
845   if (!This->parentStorage)
846   {
847     WARN("storage reverted\n");
848     return STG_E_REVERTED;
849   }
850
851   /*
852    * Read the information from the property.
853    */
854   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
855                                              This->ownerProperty,
856                                              &curProperty);
857
858   if (readSuccessful)
859   {
860     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
861                                      &curProperty,
862                                      grfStatFlag);
863
864     pstatstg->grfMode = This->grfMode;
865
866     return S_OK;
867   }
868
869   WARN("failed to read properties\n");
870   return E_FAIL;
871 }
872
873 /***
874  * This method is part of the IStream interface.
875  *
876  * This method returns a clone of the interface that allows for
877  * another seek pointer
878  *
879  * See the documentation of IStream for more info.
880  *
881  * I am not totally sure what I am doing here but I presume that this
882  * should be basically as simple as creating a new stream with the same
883  * parent etc and positioning its seek cursor.
884  */
885 static HRESULT WINAPI StgStreamImpl_Clone(
886                                    IStream*     iface,
887                                    IStream**    ppstm) /* [out] */
888 {
889   StgStreamImpl* const This=(StgStreamImpl*)iface;
890   HRESULT hres;
891   StgStreamImpl* new_stream;
892   LARGE_INTEGER seek_pos;
893
894   TRACE("%p %p\n", This, ppstm);
895
896   /*
897    * Sanity check
898    */
899
900   if (!This->parentStorage)
901     return STG_E_REVERTED;
902
903   if ( ppstm == 0 )
904     return STG_E_INVALIDPOINTER;
905
906   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
907
908   if (!new_stream)
909     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
910
911   *ppstm = (IStream*) new_stream;
912   IStream_AddRef(*ppstm);
913
914   seek_pos.QuadPart = This->currentPosition.QuadPart;
915
916   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
917
918   assert (SUCCEEDED(hres));
919
920   return S_OK;
921 }
922
923 /*
924  * Virtual function table for the StgStreamImpl class.
925  */
926 static const IStreamVtbl StgStreamImpl_Vtbl =
927 {
928     StgStreamImpl_QueryInterface,
929     StgStreamImpl_AddRef,
930     StgStreamImpl_Release,
931     StgStreamImpl_Read,
932     StgStreamImpl_Write,
933     StgStreamImpl_Seek,
934     StgStreamImpl_SetSize,
935     StgStreamImpl_CopyTo,
936     StgStreamImpl_Commit,
937     StgStreamImpl_Revert,
938     StgStreamImpl_LockRegion,
939     StgStreamImpl_UnlockRegion,
940     StgStreamImpl_Stat,
941     StgStreamImpl_Clone
942 };
943
944 /******************************************************************************
945 ** StgStreamImpl implementation
946 */
947
948 /***
949  * This is the constructor for the StgStreamImpl class.
950  *
951  * Params:
952  *    parentStorage - Pointer to the storage that contains the stream to open
953  *    ownerProperty - Index of the property that points to this stream.
954  */
955 StgStreamImpl* StgStreamImpl_Construct(
956                 StorageBaseImpl* parentStorage,
957     DWORD            grfMode,
958     ULONG            ownerProperty)
959 {
960   StgStreamImpl* newStream;
961
962   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
963
964   if (newStream!=0)
965   {
966     /*
967      * Set-up the virtual function table and reference count.
968      */
969     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
970     newStream->ref       = 0;
971
972     newStream->parentStorage = parentStorage;
973
974     /*
975      * We want to nail-down the reference to the storage in case the
976      * stream out-lives the storage in the client application.
977      *
978      * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
979      *
980      * No, don't do this. Some apps call IStorage_Release without
981      * calling IStream_Release first. If we grab a reference the
982      * file is not closed, and the app fails when it tries to
983      * reopen the file (Easy-PC, for example)
984      */
985
986     newStream->grfMode = grfMode;
987     newStream->ownerProperty = ownerProperty;
988
989     /*
990      * Start the stream at the beginning.
991      */
992     newStream->currentPosition.u.HighPart = 0;
993     newStream->currentPosition.u.LowPart = 0;
994
995     /*
996      * Initialize the rest of the data.
997      */
998     newStream->streamSize.u.HighPart = 0;
999     newStream->streamSize.u.LowPart  = 0;
1000     newStream->bigBlockChain       = 0;
1001     newStream->smallBlockChain     = 0;
1002
1003     /*
1004      * Read the size from the property and determine if the blocks forming
1005      * this stream are large or small.
1006      */
1007     StgStreamImpl_OpenBlockChain(newStream);
1008
1009     /* add us to the storage's list of active streams */
1010     StorageBaseImpl_AddStream(parentStorage, newStream);
1011   }
1012
1013   return newStream;
1014 }