crypt32: Test decoding a big CRL.
[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         readSuccessful;
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   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
211                                              This->ownerProperty,
212                                              &curProperty);
213
214   if (readSuccessful)
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     res = BlockChainStream_ReadAt(This->bigBlockChain,
306                  This->currentPosition,
307                  bytesToReadFromBuffer,
308                  pv,
309                  pcbRead);
310   }
311   else
312   {
313     /*
314      * Small and big block chains are both NULL. This case will happen
315      * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
316      */
317
318     *pcbRead = 0;
319     res = S_OK;
320     goto end;
321   }
322
323   if (SUCCEEDED(res))
324   {
325       /*
326        * We should always be able to read the proper amount of data from the
327        * chain.
328        */
329       assert(bytesToReadFromBuffer == *pcbRead);
330
331       /*
332        * Advance the pointer for the number of positions read.
333        */
334       This->currentPosition.u.LowPart += *pcbRead;
335   }
336
337 end:
338   TRACE("<-- %08lx\n", res);
339   return res;
340 }
341
342 /***
343  * This method is part of the ISequentialStream interface.
344  *
345  * It writes a block of information to the stream at the current
346  * position. It then moves the current position at the end of the
347  * written block. If the stream is too small to fit the block,
348  * the stream is grown to fit.
349  *
350  * See the documentation of ISequentialStream for more info.
351  */
352 static HRESULT WINAPI StgStreamImpl_Write(
353                   IStream*     iface,
354                   const void*    pv,          /* [size_is][in] */
355                   ULONG          cb,          /* [in] */
356                   ULONG*         pcbWritten)  /* [out] */
357 {
358   StgStreamImpl* const This=(StgStreamImpl*)iface;
359
360   ULARGE_INTEGER newSize;
361   ULONG bytesWritten = 0;
362   HRESULT res;
363
364   TRACE("(%p, %p, %ld, %p)\n",
365         iface, pv, cb, pcbWritten);
366
367   /*
368    * Do we have permission to write to this stream?
369    */
370   switch(STGM_ACCESS_MODE(This->grfMode))
371   {
372   case STGM_WRITE:
373   case STGM_READWRITE:
374       break;
375   default:
376       WARN("access denied by flags: 0x%lx\n", STGM_ACCESS_MODE(This->grfMode));
377       return STG_E_ACCESSDENIED;
378   }
379
380   if (!pv)
381     return STG_E_INVALIDPOINTER;
382
383   if (!This->parentStorage)
384   {
385     WARN("storage reverted\n");
386     return STG_E_REVERTED;
387   }
388  
389   /*
390    * If the caller is not interested in the number of bytes written,
391    * we use another buffer to avoid "if" statements in the code.
392    */
393   if (pcbWritten == 0)
394     pcbWritten = &bytesWritten;
395
396   /*
397    * Initialize the out parameter
398    */
399   *pcbWritten = 0;
400
401   if (cb == 0)
402   {
403     TRACE("<-- S_OK, written 0\n");
404     return S_OK;
405   }
406   else
407   {
408     newSize.u.HighPart = 0;
409     newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
410   }
411
412   /*
413    * Verify if we need to grow the stream
414    */
415   if (newSize.u.LowPart > This->streamSize.u.LowPart)
416   {
417     /* grow stream */
418     IStream_SetSize(iface, newSize);
419   }
420
421   /*
422    * Depending on the type of chain that was opened when the stream was constructed,
423    * we delegate the work to the method that readwrites to the block chains.
424    */
425   if (This->smallBlockChain!=0)
426   {
427     res = SmallBlockChainStream_WriteAt(This->smallBlockChain,
428                                   This->currentPosition,
429                                   cb,
430                                   pv,
431                                   pcbWritten);
432
433   }
434   else if (This->bigBlockChain!=0)
435   {
436     res = BlockChainStream_WriteAt(This->bigBlockChain,
437                              This->currentPosition,
438                              cb,
439                              pv,
440                              pcbWritten);
441   }
442   else
443   {
444     /* this should never happen because the IStream_SetSize call above will
445      * make sure a big or small block chain is created */
446     assert(FALSE);
447     res = 0;
448   }
449
450   /*
451    * Advance the position pointer for the number of positions written.
452    */
453   This->currentPosition.u.LowPart += *pcbWritten;
454
455   TRACE("<-- S_OK, written %lu\n", *pcbWritten);
456   return res;
457 }
458
459 /***
460  * This method is part of the IStream interface.
461  *
462  * It will move the current stream pointer according to the parameters
463  * given.
464  *
465  * See the documentation of IStream for more info.
466  */
467 static HRESULT WINAPI StgStreamImpl_Seek(
468                   IStream*      iface,
469                   LARGE_INTEGER   dlibMove,         /* [in] */
470                   DWORD           dwOrigin,         /* [in] */
471                   ULARGE_INTEGER* plibNewPosition) /* [out] */
472 {
473   StgStreamImpl* const This=(StgStreamImpl*)iface;
474
475   ULARGE_INTEGER newPosition;
476
477   TRACE("(%p, %ld, %ld, %p)\n",
478         iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
479
480   /*
481    * fail if the stream has no parent (as does windows)
482    */
483
484   if (!This->parentStorage)
485     return STG_E_REVERTED;
486
487   /*
488    * The caller is allowed to pass in NULL as the new position return value.
489    * If it happens, we assign it to a dynamic variable to avoid special cases
490    * in the code below.
491    */
492   if (plibNewPosition == 0)
493   {
494     plibNewPosition = &newPosition;
495   }
496
497   /*
498    * The file pointer is moved depending on the given "function"
499    * parameter.
500    */
501   switch (dwOrigin)
502   {
503     case STREAM_SEEK_SET:
504       plibNewPosition->u.HighPart = 0;
505       plibNewPosition->u.LowPart  = 0;
506       break;
507     case STREAM_SEEK_CUR:
508       *plibNewPosition = This->currentPosition;
509       break;
510     case STREAM_SEEK_END:
511       *plibNewPosition = This->streamSize;
512       break;
513     default:
514       return STG_E_INVALIDFUNCTION;
515   }
516
517   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
518
519   /*
520    * tell the caller what we calculated
521    */
522   This->currentPosition = *plibNewPosition;
523
524   return S_OK;
525 }
526
527 /***
528  * This method is part of the IStream interface.
529  *
530  * It will change the size of a stream.
531  *
532  * TODO: Switch from small blocks to big blocks and vice versa.
533  *
534  * See the documentation of IStream for more info.
535  */
536 static HRESULT WINAPI StgStreamImpl_SetSize(
537                                      IStream*      iface,
538                                      ULARGE_INTEGER  libNewSize)   /* [in] */
539 {
540   StgStreamImpl* const This=(StgStreamImpl*)iface;
541
542   StgProperty    curProperty;
543   BOOL         Success;
544
545   TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
546
547   if(!This->parentStorage)
548     return STG_E_REVERTED;
549
550   /*
551    * As documented.
552    */
553   if (libNewSize.u.HighPart != 0)
554     return STG_E_INVALIDFUNCTION;
555
556   /*
557    * Do we have permission?
558    */
559   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
560     return STG_E_ACCESSDENIED;
561
562   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
563     return S_OK;
564
565   /*
566    * This will happen if we're creating a stream
567    */
568   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
569   {
570     if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
571     {
572       This->smallBlockChain = SmallBlockChainStream_Construct(
573                                     This->parentStorage->ancestorStorage,
574                                     This->ownerProperty);
575     }
576     else
577     {
578       This->bigBlockChain = BlockChainStream_Construct(
579                                 This->parentStorage->ancestorStorage,
580                                 NULL,
581                                 This->ownerProperty);
582     }
583   }
584
585   /*
586    * Read this stream's property to see if it's small blocks or big blocks
587    */
588   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
589                                        This->ownerProperty,
590                                        &curProperty);
591   /*
592    * Determine if we have to switch from small to big blocks or vice versa
593    */
594   if ( (This->smallBlockChain!=0) &&
595        (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
596   {
597     if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
598     {
599       /*
600        * Transform the small block chain into a big block chain
601        */
602       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
603                                 This->parentStorage->ancestorStorage,
604                                 &This->smallBlockChain);
605     }
606   }
607
608   if (This->smallBlockChain!=0)
609   {
610     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
611   }
612   else
613   {
614     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
615   }
616
617   /*
618    * Write the new information about this stream to the property
619    */
620   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
621                                        This->ownerProperty,
622                                        &curProperty);
623
624   curProperty.size.u.HighPart = libNewSize.u.HighPart;
625   curProperty.size.u.LowPart = libNewSize.u.LowPart;
626
627   if (Success)
628   {
629     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
630                                 This->ownerProperty,
631                                 &curProperty);
632   }
633
634   This->streamSize = libNewSize;
635
636   return S_OK;
637 }
638
639 /***
640  * This method is part of the IStream interface.
641  *
642  * It will copy the 'cb' Bytes to 'pstm' IStream.
643  *
644  * See the documentation of IStream for more info.
645  */
646 static HRESULT WINAPI StgStreamImpl_CopyTo(
647                                     IStream*      iface,
648                                     IStream*      pstm,         /* [unique][in] */
649                                     ULARGE_INTEGER  cb,           /* [in] */
650                                     ULARGE_INTEGER* pcbRead,      /* [out] */
651                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
652 {
653   StgStreamImpl* const This=(StgStreamImpl*)iface;
654   HRESULT        hr = S_OK;
655   BYTE           tmpBuffer[128];
656   ULONG          bytesRead, bytesWritten, copySize;
657   ULARGE_INTEGER totalBytesRead;
658   ULARGE_INTEGER totalBytesWritten;
659
660   TRACE("(%p, %p, %ld, %p, %p)\n",
661         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
662
663   /*
664    * Sanity check
665    */
666
667   if (!This->parentStorage)
668     return STG_E_REVERTED;
669
670   if ( pstm == 0 )
671     return STG_E_INVALIDPOINTER;
672
673   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
674   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
675
676   /*
677    * use stack to store data temporarily
678    * there is surely a more performant way of doing it, for now this basic
679    * implementation will do the job
680    */
681   while ( cb.u.LowPart > 0 )
682   {
683     if ( cb.u.LowPart >= 128 )
684       copySize = 128;
685     else
686       copySize = cb.u.LowPart;
687
688     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
689
690     totalBytesRead.u.LowPart += bytesRead;
691
692     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
693
694     totalBytesWritten.u.LowPart += bytesWritten;
695
696     /*
697      * Check that read & write operations were successful
698      */
699     if (bytesRead != bytesWritten)
700     {
701       hr = STG_E_MEDIUMFULL;
702       break;
703     }
704
705     if (bytesRead!=copySize)
706       cb.u.LowPart = 0;
707     else
708       cb.u.LowPart -= bytesRead;
709   }
710
711   /*
712    * Update number of bytes read and written
713    */
714   if (pcbRead)
715   {
716     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
717     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
718   }
719
720   if (pcbWritten)
721   {
722     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
723     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
724   }
725   return hr;
726 }
727
728 /***
729  * This method is part of the IStream interface.
730  *
731  * For streams contained in structured storages, this method
732  * does nothing. This is what the documentation tells us.
733  *
734  * See the documentation of IStream for more info.
735  */
736 static HRESULT WINAPI StgStreamImpl_Commit(
737                   IStream*      iface,
738                   DWORD           grfCommitFlags)  /* [in] */
739 {
740   StgStreamImpl* const This=(StgStreamImpl*)iface;
741
742   if (!This->parentStorage)
743     return STG_E_REVERTED;
744
745   return S_OK;
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_Revert(
757                   IStream* iface)
758 {
759   return S_OK;
760 }
761
762 static HRESULT WINAPI StgStreamImpl_LockRegion(
763                                         IStream*     iface,
764                                         ULARGE_INTEGER libOffset,   /* [in] */
765                                         ULARGE_INTEGER cb,          /* [in] */
766                                         DWORD          dwLockType)  /* [in] */
767 {
768   StgStreamImpl* const This=(StgStreamImpl*)iface;
769
770   if (!This->parentStorage)
771     return STG_E_REVERTED;
772
773   FIXME("not implemented!\n");
774   return E_NOTIMPL;
775 }
776
777 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
778                                           IStream*     iface,
779                                           ULARGE_INTEGER libOffset,   /* [in] */
780                                           ULARGE_INTEGER cb,          /* [in] */
781                                           DWORD          dwLockType)  /* [in] */
782 {
783   StgStreamImpl* const This=(StgStreamImpl*)iface;
784
785   if (!This->parentStorage)
786     return STG_E_REVERTED;
787
788   FIXME("not implemented!\n");
789   return E_NOTIMPL;
790 }
791
792 /***
793  * This method is part of the IStream interface.
794  *
795  * This method returns information about the current
796  * stream.
797  *
798  * See the documentation of IStream for more info.
799  */
800 static HRESULT WINAPI StgStreamImpl_Stat(
801                   IStream*     iface,
802                   STATSTG*       pstatstg,     /* [out] */
803                   DWORD          grfStatFlag)  /* [in] */
804 {
805   StgStreamImpl* const This=(StgStreamImpl*)iface;
806
807   StgProperty    curProperty;
808   BOOL         readSuccessful;
809
810   TRACE("%p %p %ld\n", This, pstatstg, grfStatFlag);
811
812   /*
813    * if stream has no parent, return STG_E_REVERTED
814    */
815
816   if (!This->parentStorage)
817     return STG_E_REVERTED;
818
819   /*
820    * Read the information from the property.
821    */
822   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
823                                              This->ownerProperty,
824                                              &curProperty);
825
826   if (readSuccessful)
827   {
828     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
829                                      &curProperty,
830                                      grfStatFlag);
831
832     pstatstg->grfMode = This->grfMode;
833
834     return S_OK;
835   }
836
837   return E_FAIL;
838 }
839
840 /***
841  * This method is part of the IStream interface.
842  *
843  * This method returns a clone of the interface that allows for
844  * another seek pointer
845  *
846  * See the documentation of IStream for more info.
847  *
848  * I am not totally sure what I am doing here but I presume that this
849  * should be basically as simple as creating a new stream with the same
850  * parent etc and positioning its seek cursor.
851  */
852 static HRESULT WINAPI StgStreamImpl_Clone(
853                                    IStream*     iface,
854                                    IStream**    ppstm) /* [out] */
855 {
856   StgStreamImpl* const This=(StgStreamImpl*)iface;
857   HRESULT hres;
858   StgStreamImpl* new_stream;
859   LARGE_INTEGER seek_pos;
860
861   TRACE("%p %p\n", This, ppstm);
862
863   /*
864    * Sanity check
865    */
866
867   if (!This->parentStorage)
868     return STG_E_REVERTED;
869
870   if ( ppstm == 0 )
871     return STG_E_INVALIDPOINTER;
872
873   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
874
875   if (!new_stream)
876     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
877
878   *ppstm = (IStream*) new_stream;
879   seek_pos.QuadPart = This->currentPosition.QuadPart;
880
881   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
882
883   assert (SUCCEEDED(hres));
884
885   return S_OK;
886 }
887
888 /*
889  * Virtual function table for the StgStreamImpl class.
890  */
891 static const IStreamVtbl StgStreamImpl_Vtbl =
892 {
893     StgStreamImpl_QueryInterface,
894     StgStreamImpl_AddRef,
895     StgStreamImpl_Release,
896     StgStreamImpl_Read,
897     StgStreamImpl_Write,
898     StgStreamImpl_Seek,
899     StgStreamImpl_SetSize,
900     StgStreamImpl_CopyTo,
901     StgStreamImpl_Commit,
902     StgStreamImpl_Revert,
903     StgStreamImpl_LockRegion,
904     StgStreamImpl_UnlockRegion,
905     StgStreamImpl_Stat,
906     StgStreamImpl_Clone
907 };
908
909 /******************************************************************************
910 ** StgStreamImpl implementation
911 */
912
913 /***
914  * This is the constructor for the StgStreamImpl class.
915  *
916  * Params:
917  *    parentStorage - Pointer to the storage that contains the stream to open
918  *    ownerProperty - Index of the property that points to this stream.
919  */
920 StgStreamImpl* StgStreamImpl_Construct(
921                 StorageBaseImpl* parentStorage,
922     DWORD            grfMode,
923     ULONG            ownerProperty)
924 {
925   StgStreamImpl* newStream;
926
927   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
928
929   if (newStream!=0)
930   {
931     /*
932      * Set-up the virtual function table and reference count.
933      */
934     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
935     newStream->ref       = 0;
936
937     newStream->parentStorage = parentStorage;
938
939     /*
940      * We want to nail-down the reference to the storage in case the
941      * stream out-lives the storage in the client application.
942      *
943      * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
944      *
945      * No, don't do this. Some apps call IStorage_Release without
946      * calling IStream_Release first. If we grab a reference the
947      * file is not closed, and the app fails when it tries to
948      * reopen the file (Easy-PC, for example)
949      */
950
951     newStream->grfMode = grfMode;
952     newStream->ownerProperty = ownerProperty;
953
954     /*
955      * Start the stream at the beginning.
956      */
957     newStream->currentPosition.u.HighPart = 0;
958     newStream->currentPosition.u.LowPart = 0;
959
960     /*
961      * Initialize the rest of the data.
962      */
963     newStream->streamSize.u.HighPart = 0;
964     newStream->streamSize.u.LowPart  = 0;
965     newStream->bigBlockChain       = 0;
966     newStream->smallBlockChain     = 0;
967
968     /*
969      * Read the size from the property and determine if the blocks forming
970      * this stream are large or small.
971      */
972     StgStreamImpl_OpenBlockChain(newStream);
973   }
974
975   return newStream;
976 }