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