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