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