kernel32: Allow to execute .cmd files.
[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     return STG_E_REVERTED;
490
491   /*
492    * The caller is allowed to pass in NULL as the new position return value.
493    * If it happens, we assign it to a dynamic variable to avoid special cases
494    * in the code below.
495    */
496   if (plibNewPosition == 0)
497   {
498     plibNewPosition = &newPosition;
499   }
500
501   /*
502    * The file pointer is moved depending on the given "function"
503    * parameter.
504    */
505   switch (dwOrigin)
506   {
507     case STREAM_SEEK_SET:
508       plibNewPosition->u.HighPart = 0;
509       plibNewPosition->u.LowPart  = 0;
510       break;
511     case STREAM_SEEK_CUR:
512       *plibNewPosition = This->currentPosition;
513       break;
514     case STREAM_SEEK_END:
515       *plibNewPosition = This->streamSize;
516       break;
517     default:
518       return STG_E_INVALIDFUNCTION;
519   }
520
521   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
522
523   /*
524    * tell the caller what we calculated
525    */
526   This->currentPosition = *plibNewPosition;
527
528   return S_OK;
529 }
530
531 /***
532  * This method is part of the IStream interface.
533  *
534  * It will change the size of a stream.
535  *
536  * TODO: Switch from small blocks to big blocks and vice versa.
537  *
538  * See the documentation of IStream for more info.
539  */
540 static HRESULT WINAPI StgStreamImpl_SetSize(
541                                      IStream*      iface,
542                                      ULARGE_INTEGER  libNewSize)   /* [in] */
543 {
544   StgStreamImpl* const This=(StgStreamImpl*)iface;
545
546   StgProperty    curProperty;
547   BOOL         Success;
548
549   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
550
551   if(!This->parentStorage)
552     return STG_E_REVERTED;
553
554   /*
555    * As documented.
556    */
557   if (libNewSize.u.HighPart != 0)
558     return STG_E_INVALIDFUNCTION;
559
560   /*
561    * Do we have permission?
562    */
563   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
564     return STG_E_ACCESSDENIED;
565
566   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
567     return S_OK;
568
569   /*
570    * This will happen if we're creating a stream
571    */
572   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
573   {
574     if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
575     {
576       This->smallBlockChain = SmallBlockChainStream_Construct(
577                                     This->parentStorage->ancestorStorage,
578                                     This->ownerProperty);
579     }
580     else
581     {
582       This->bigBlockChain = BlockChainStream_Construct(
583                                 This->parentStorage->ancestorStorage,
584                                 NULL,
585                                 This->ownerProperty);
586     }
587   }
588
589   /*
590    * Read this stream's property to see if it's small blocks or big blocks
591    */
592   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
593                                        This->ownerProperty,
594                                        &curProperty);
595   /*
596    * Determine if we have to switch from small to big blocks or vice versa
597    */
598   if ( (This->smallBlockChain!=0) &&
599        (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
600   {
601     if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
602     {
603       /*
604        * Transform the small block chain into a big block chain
605        */
606       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
607                                 This->parentStorage->ancestorStorage,
608                                 &This->smallBlockChain);
609     }
610   }
611
612   if (This->smallBlockChain!=0)
613   {
614     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
615   }
616   else
617   {
618     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
619   }
620
621   /*
622    * Write the new information about this stream to the property
623    */
624   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
625                                        This->ownerProperty,
626                                        &curProperty);
627
628   curProperty.size.u.HighPart = libNewSize.u.HighPart;
629   curProperty.size.u.LowPart = libNewSize.u.LowPart;
630
631   if (Success)
632   {
633     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
634                                 This->ownerProperty,
635                                 &curProperty);
636   }
637
638   This->streamSize = libNewSize;
639
640   return S_OK;
641 }
642
643 /***
644  * This method is part of the IStream interface.
645  *
646  * It will copy the 'cb' Bytes to 'pstm' IStream.
647  *
648  * See the documentation of IStream for more info.
649  */
650 static HRESULT WINAPI StgStreamImpl_CopyTo(
651                                     IStream*      iface,
652                                     IStream*      pstm,         /* [unique][in] */
653                                     ULARGE_INTEGER  cb,           /* [in] */
654                                     ULARGE_INTEGER* pcbRead,      /* [out] */
655                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
656 {
657   StgStreamImpl* const This=(StgStreamImpl*)iface;
658   HRESULT        hr = S_OK;
659   BYTE           tmpBuffer[128];
660   ULONG          bytesRead, bytesWritten, copySize;
661   ULARGE_INTEGER totalBytesRead;
662   ULARGE_INTEGER totalBytesWritten;
663
664   TRACE("(%p, %p, %d, %p, %p)\n",
665         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
666
667   /*
668    * Sanity check
669    */
670
671   if (!This->parentStorage)
672     return STG_E_REVERTED;
673
674   if ( pstm == 0 )
675     return STG_E_INVALIDPOINTER;
676
677   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
678   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
679
680   /*
681    * use stack to store data temporarily
682    * there is surely a more performant way of doing it, for now this basic
683    * implementation will do the job
684    */
685   while ( cb.u.LowPart > 0 )
686   {
687     if ( cb.u.LowPart >= 128 )
688       copySize = 128;
689     else
690       copySize = cb.u.LowPart;
691
692     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
693
694     totalBytesRead.u.LowPart += bytesRead;
695
696     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
697
698     totalBytesWritten.u.LowPart += bytesWritten;
699
700     /*
701      * Check that read & write operations were successful
702      */
703     if (bytesRead != bytesWritten)
704     {
705       hr = STG_E_MEDIUMFULL;
706       break;
707     }
708
709     if (bytesRead!=copySize)
710       cb.u.LowPart = 0;
711     else
712       cb.u.LowPart -= bytesRead;
713   }
714
715   /*
716    * Update number of bytes read and written
717    */
718   if (pcbRead)
719   {
720     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
721     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
722   }
723
724   if (pcbWritten)
725   {
726     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
727     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
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     return STG_E_REVERTED;
748
749   return S_OK;
750 }
751
752 /***
753  * This method is part of the IStream interface.
754  *
755  * For streams contained in structured storages, this method
756  * does nothing. This is what the documentation tells us.
757  *
758  * See the documentation of IStream for more info.
759  */
760 static HRESULT WINAPI StgStreamImpl_Revert(
761                   IStream* iface)
762 {
763   return S_OK;
764 }
765
766 static HRESULT WINAPI StgStreamImpl_LockRegion(
767                                         IStream*     iface,
768                                         ULARGE_INTEGER libOffset,   /* [in] */
769                                         ULARGE_INTEGER cb,          /* [in] */
770                                         DWORD          dwLockType)  /* [in] */
771 {
772   StgStreamImpl* const This=(StgStreamImpl*)iface;
773
774   if (!This->parentStorage)
775     return STG_E_REVERTED;
776
777   FIXME("not implemented!\n");
778   return E_NOTIMPL;
779 }
780
781 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
782                                           IStream*     iface,
783                                           ULARGE_INTEGER libOffset,   /* [in] */
784                                           ULARGE_INTEGER cb,          /* [in] */
785                                           DWORD          dwLockType)  /* [in] */
786 {
787   StgStreamImpl* const This=(StgStreamImpl*)iface;
788
789   if (!This->parentStorage)
790     return STG_E_REVERTED;
791
792   FIXME("not implemented!\n");
793   return E_NOTIMPL;
794 }
795
796 /***
797  * This method is part of the IStream interface.
798  *
799  * This method returns information about the current
800  * stream.
801  *
802  * See the documentation of IStream for more info.
803  */
804 static HRESULT WINAPI StgStreamImpl_Stat(
805                   IStream*     iface,
806                   STATSTG*       pstatstg,     /* [out] */
807                   DWORD          grfStatFlag)  /* [in] */
808 {
809   StgStreamImpl* const This=(StgStreamImpl*)iface;
810
811   StgProperty    curProperty;
812   BOOL         readSuccessful;
813
814   TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
815
816   /*
817    * if stream has no parent, return STG_E_REVERTED
818    */
819
820   if (!This->parentStorage)
821     return STG_E_REVERTED;
822
823   /*
824    * Read the information from the property.
825    */
826   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
827                                              This->ownerProperty,
828                                              &curProperty);
829
830   if (readSuccessful)
831   {
832     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
833                                      &curProperty,
834                                      grfStatFlag);
835
836     pstatstg->grfMode = This->grfMode;
837
838     return S_OK;
839   }
840
841   return E_FAIL;
842 }
843
844 /***
845  * This method is part of the IStream interface.
846  *
847  * This method returns a clone of the interface that allows for
848  * another seek pointer
849  *
850  * See the documentation of IStream for more info.
851  *
852  * I am not totally sure what I am doing here but I presume that this
853  * should be basically as simple as creating a new stream with the same
854  * parent etc and positioning its seek cursor.
855  */
856 static HRESULT WINAPI StgStreamImpl_Clone(
857                                    IStream*     iface,
858                                    IStream**    ppstm) /* [out] */
859 {
860   StgStreamImpl* const This=(StgStreamImpl*)iface;
861   HRESULT hres;
862   StgStreamImpl* new_stream;
863   LARGE_INTEGER seek_pos;
864
865   TRACE("%p %p\n", This, ppstm);
866
867   /*
868    * Sanity check
869    */
870
871   if (!This->parentStorage)
872     return STG_E_REVERTED;
873
874   if ( ppstm == 0 )
875     return STG_E_INVALIDPOINTER;
876
877   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
878
879   if (!new_stream)
880     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
881
882   *ppstm = (IStream*) new_stream;
883   IStream_AddRef(*ppstm);
884
885   seek_pos.QuadPart = This->currentPosition.QuadPart;
886
887   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
888
889   assert (SUCCEEDED(hres));
890
891   return S_OK;
892 }
893
894 /*
895  * Virtual function table for the StgStreamImpl class.
896  */
897 static const IStreamVtbl StgStreamImpl_Vtbl =
898 {
899     StgStreamImpl_QueryInterface,
900     StgStreamImpl_AddRef,
901     StgStreamImpl_Release,
902     StgStreamImpl_Read,
903     StgStreamImpl_Write,
904     StgStreamImpl_Seek,
905     StgStreamImpl_SetSize,
906     StgStreamImpl_CopyTo,
907     StgStreamImpl_Commit,
908     StgStreamImpl_Revert,
909     StgStreamImpl_LockRegion,
910     StgStreamImpl_UnlockRegion,
911     StgStreamImpl_Stat,
912     StgStreamImpl_Clone
913 };
914
915 /******************************************************************************
916 ** StgStreamImpl implementation
917 */
918
919 /***
920  * This is the constructor for the StgStreamImpl class.
921  *
922  * Params:
923  *    parentStorage - Pointer to the storage that contains the stream to open
924  *    ownerProperty - Index of the property that points to this stream.
925  */
926 StgStreamImpl* StgStreamImpl_Construct(
927                 StorageBaseImpl* parentStorage,
928     DWORD            grfMode,
929     ULONG            ownerProperty)
930 {
931   StgStreamImpl* newStream;
932
933   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
934
935   if (newStream!=0)
936   {
937     /*
938      * Set-up the virtual function table and reference count.
939      */
940     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
941     newStream->ref       = 0;
942
943     newStream->parentStorage = parentStorage;
944
945     /*
946      * We want to nail-down the reference to the storage in case the
947      * stream out-lives the storage in the client application.
948      *
949      * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
950      *
951      * No, don't do this. Some apps call IStorage_Release without
952      * calling IStream_Release first. If we grab a reference the
953      * file is not closed, and the app fails when it tries to
954      * reopen the file (Easy-PC, for example)
955      */
956
957     newStream->grfMode = grfMode;
958     newStream->ownerProperty = ownerProperty;
959
960     /*
961      * Start the stream at the beginning.
962      */
963     newStream->currentPosition.u.HighPart = 0;
964     newStream->currentPosition.u.LowPart = 0;
965
966     /*
967      * Initialize the rest of the data.
968      */
969     newStream->streamSize.u.HighPart = 0;
970     newStream->streamSize.u.LowPart  = 0;
971     newStream->bigBlockChain       = 0;
972     newStream->smallBlockChain     = 0;
973
974     /*
975      * Read the size from the property and determine if the blocks forming
976      * this stream are large or small.
977      */
978     StgStreamImpl_OpenBlockChain(newStream);
979
980     /* add us to the storage's list of active streams */
981     StorageBaseImpl_AddStream(parentStorage, newStream);
982   }
983
984   return newStream;
985 }