Fixed some issues found by winapi_check.
[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   if (!(This->grfMode & STGM_WRITE))
418      return STG_E_ACCESSDENIED; 
419
420   /*
421    * If the caller is not interested in the number of bytes written,
422    * we use another buffer to avoid "if" statements in the code.
423    */
424   if (pcbWritten == 0)
425     pcbWritten = &bytesWritten;
426   
427   /*
428    * Initialize the out parameter
429    */
430   *pcbWritten = 0;
431
432   /*
433    * Do we have permission to write to this stream?
434    */
435   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
436     return STG_E_ACCESSDENIED;
437
438   if (cb == 0)
439   {
440     return S_OK;
441   }
442   else
443   {
444     newSize.s.HighPart = 0;
445     newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
446   }
447   
448   /*
449    * Verify if we need to grow the stream
450    */
451   if (newSize.s.LowPart > This->streamSize.s.LowPart)
452   {
453     /* grow stream */
454     IStream_SetSize(iface, newSize);
455   }
456   
457   /*
458    * Depending on the type of chain that was opened when the stream was constructed,
459    * we delegate the work to the method that readwrites to the block chains.
460    */
461   if (This->smallBlockChain!=0)
462   {
463     SmallBlockChainStream_WriteAt(This->smallBlockChain,
464                                   This->currentPosition,
465                                   cb,
466                                   pv,
467                                   pcbWritten);
468     
469   }
470   else if (This->bigBlockChain!=0)
471   {
472     BlockChainStream_WriteAt(This->bigBlockChain,
473                              This->currentPosition,
474                              cb,
475                              pv,
476                              pcbWritten);
477   }
478   else
479     assert(FALSE);
480   
481   /*
482    * Advance the position pointer for the number of positions written.
483    */
484   This->currentPosition.s.LowPart += *pcbWritten;
485   
486   return S_OK;
487 }
488
489 /***
490  * This method is part of the IStream interface.
491  *
492  * It will move the current stream pointer according to the parameters
493  * given.
494  *
495  * See the documentation of IStream for more info.
496  */        
497 HRESULT WINAPI StgStreamImpl_Seek( 
498                   IStream*      iface,
499                   LARGE_INTEGER   dlibMove,         /* [in] */ 
500                   DWORD           dwOrigin,         /* [in] */ 
501                   ULARGE_INTEGER* plibNewPosition) /* [out] */
502 {
503   StgStreamImpl* const This=(StgStreamImpl*)iface;
504
505   ULARGE_INTEGER newPosition;
506
507   TRACE("(%p, %ld, %ld, %p)\n",
508         iface, dlibMove.s.LowPart, dwOrigin, plibNewPosition);
509
510   /* 
511    * The caller is allowed to pass in NULL as the new position return value.
512    * If it happens, we assign it to a dynamic variable to avoid special cases
513    * in the code below.
514    */
515   if (plibNewPosition == 0)
516   {
517     plibNewPosition = &newPosition;
518   }
519
520   /*
521    * The file pointer is moved depending on the given "function"
522    * parameter.
523    */
524   switch (dwOrigin)
525   {
526     case STREAM_SEEK_SET:
527       plibNewPosition->s.HighPart = 0;
528       plibNewPosition->s.LowPart  = 0;
529       break;
530     case STREAM_SEEK_CUR:
531       *plibNewPosition = This->currentPosition;
532       break;
533     case STREAM_SEEK_END:
534       *plibNewPosition = This->streamSize;
535       break;
536     default:
537       return STG_E_INVALIDFUNCTION;
538   }
539
540 #if SIZEOF_LONG_LONG >= 8
541   plibNewPosition->QuadPart += dlibMove.QuadPart;
542 #else
543   /*
544    * do some multiword arithmetic:
545    *    treat HighPart as a signed value
546    *    treat LowPart as unsigned
547    *  NOTE: this stuff is two's complement specific!
548    */
549   if (dlibMove.s.HighPart < 0) { /* dlibMove is < 0 */
550       /* calculate the absolute value of dlibMove ... */
551       dlibMove.s.HighPart = -dlibMove.s.HighPart;
552       dlibMove.s.LowPart ^= -1;
553       /* ... and subtract with carry */
554       if (dlibMove.s.LowPart > plibNewPosition->s.LowPart) {
555           /* carry needed, This accounts for any underflows at [1]*/
556           plibNewPosition->s.HighPart -= 1; 
557       }
558       plibNewPosition->s.LowPart -= dlibMove.s.LowPart; /* [1] */
559       plibNewPosition->s.HighPart -= dlibMove.s.HighPart; 
560   } else {
561       /* add directly */
562       int initialLowPart = plibNewPosition->s.LowPart;
563       plibNewPosition->s.LowPart += dlibMove.s.LowPart;
564       if((plibNewPosition->s.LowPart < initialLowPart) ||
565          (plibNewPosition->s.LowPart < dlibMove.s.LowPart)) {
566           /* LowPart has rolled over => add the carry digit to HighPart */
567           plibNewPosition->s.HighPart++;
568       }
569       plibNewPosition->s.HighPart += dlibMove.s.HighPart; 
570   }
571   /*
572    * Check if we end-up before the beginning of the file. That should 
573    * trigger an error.
574    */
575   if (plibNewPosition->s.HighPart < 0) {
576       return STG_E_INVALIDPOINTER;
577   }
578
579     /*
580    * We currently don't support files with offsets of >32 bits.  
581    * Note that we have checked for a negative offset already
582      */
583   assert(plibNewPosition->s.HighPart <= 0);
584
585 #endif
586
587   /*
588    * tell the caller what we calculated
589    */
590   This->currentPosition = *plibNewPosition;
591  
592   return S_OK;
593 }
594
595 /***
596  * This method is part of the IStream interface.
597  *
598  * It will change the size of a stream.
599  *
600  * TODO: Switch from small blocks to big blocks and vice versa.
601  *
602  * See the documentation of IStream for more info.
603  */
604 HRESULT WINAPI StgStreamImpl_SetSize( 
605                                      IStream*      iface,
606                                      ULARGE_INTEGER  libNewSize)   /* [in] */ 
607 {
608   StgStreamImpl* const This=(StgStreamImpl*)iface;
609
610   StgProperty    curProperty;
611   BOOL         Success;
612
613   TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
614
615   /*
616    * As documented.
617    */
618   if (libNewSize.s.HighPart != 0)
619     return STG_E_INVALIDFUNCTION;
620
621   /*
622    * Do we have permission?
623    */
624   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
625     return STG_E_ACCESSDENIED;
626
627   if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
628     return S_OK;
629
630   /*
631    * This will happen if we're creating a stream
632    */
633   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
634   {
635     if (libNewSize.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
636     {
637       This->smallBlockChain = SmallBlockChainStream_Construct(
638                                     This->parentStorage->ancestorStorage,
639                                     This->ownerProperty);
640     }
641     else
642     {
643       This->bigBlockChain = BlockChainStream_Construct(
644                                 This->parentStorage->ancestorStorage,
645                                 NULL,
646                                 This->ownerProperty);
647     }
648   }
649
650   /*
651    * Read this stream's property to see if it's small blocks or big blocks
652    */
653   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
654                                        This->ownerProperty,
655                                        &curProperty); 
656   /*
657    * Determine if we have to switch from small to big blocks or vice versa
658    */  
659   if ( (This->smallBlockChain!=0) && 
660        (curProperty.size.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
661   {
662     if (libNewSize.s.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
663     {
664       /*
665        * Transform the small block chain into a big block chain
666        */
667       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
668                                 This->parentStorage->ancestorStorage,
669                                 &This->smallBlockChain);
670     }
671   }
672
673   if (This->smallBlockChain!=0)
674   {
675     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
676   }
677   else
678   {
679     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
680   }
681
682   /*
683    * Write to the property the new information about this stream
684    */
685   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
686                                        This->ownerProperty,
687                                        &curProperty);
688
689   curProperty.size.s.HighPart = libNewSize.s.HighPart;
690   curProperty.size.s.LowPart = libNewSize.s.LowPart;
691   
692   if (Success)
693   {
694     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
695                                 This->ownerProperty,
696                                 &curProperty);
697   }
698   
699   This->streamSize = libNewSize;
700   
701   return S_OK;
702 }
703         
704 /***
705  * This method is part of the IStream interface.
706  *
707  * It will copy the 'cb' Bytes to 'pstm' IStream.
708  *
709  * See the documentation of IStream for more info.
710  */
711 HRESULT WINAPI StgStreamImpl_CopyTo( 
712                                     IStream*      iface,
713                                     IStream*      pstm,         /* [unique][in] */ 
714                                     ULARGE_INTEGER  cb,           /* [in] */         
715                                     ULARGE_INTEGER* pcbRead,      /* [out] */        
716                                     ULARGE_INTEGER* pcbWritten)   /* [out] */        
717 {
718   HRESULT        hr = S_OK;
719   BYTE           tmpBuffer[128];
720   ULONG          bytesRead, bytesWritten, copySize;
721   ULARGE_INTEGER totalBytesRead;
722   ULARGE_INTEGER totalBytesWritten;
723
724   TRACE("(%p, %p, %ld, %p, %p)\n", 
725         iface, pstm, cb.s.LowPart, pcbRead, pcbWritten);
726
727   /*
728    * Sanity check
729    */
730   if ( pstm == 0 )
731     return STG_E_INVALIDPOINTER;
732
733   totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
734   totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
735
736   /*
737    * use stack to store data temporarly
738    * there is surely more performant way of doing it, for now this basic
739    * implementation will do the job
740    */
741   while ( cb.s.LowPart > 0 )
742   {
743     if ( cb.s.LowPart >= 128 )
744       copySize = 128;
745     else
746       copySize = cb.s.LowPart;
747     
748     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
749
750     totalBytesRead.s.LowPart += bytesRead;
751     
752     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
753
754     totalBytesWritten.s.LowPart += bytesWritten;
755
756     /*
757      * Check that read & write operations were succesfull
758      */
759     if (bytesRead != bytesWritten)
760     {
761       hr = STG_E_MEDIUMFULL;
762       break;
763     }
764     
765     if (bytesRead!=copySize)
766       cb.s.LowPart = 0;
767     else
768       cb.s.LowPart -= bytesRead;
769   }
770
771   /*
772    * Update number of bytes read and written
773    */
774   if (pcbRead)
775   {
776     pcbRead->s.LowPart = totalBytesRead.s.LowPart;
777     pcbRead->s.HighPart = totalBytesRead.s.HighPart;
778   }
779
780   if (pcbWritten)
781   {
782     pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
783     pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
784   }
785   return hr;
786 }
787
788 /***
789  * This method is part of the IStream interface.
790  *
791  * For streams contained in structured storages, this method
792  * does nothing. This is what the documentation tells us.
793  *
794  * See the documentation of IStream for more info.
795  */        
796 HRESULT WINAPI StgStreamImpl_Commit( 
797                   IStream*      iface,
798                   DWORD           grfCommitFlags)  /* [in] */ 
799 {
800   return S_OK;
801 }
802
803 /***
804  * This method is part of the IStream interface.
805  *
806  * For streams contained in structured storages, this method
807  * does nothing. This is what the documentation tells us.
808  *
809  * See the documentation of IStream for more info.
810  */        
811 HRESULT WINAPI StgStreamImpl_Revert( 
812                   IStream* iface)
813 {
814   return S_OK;
815 }
816
817 HRESULT WINAPI StgStreamImpl_LockRegion( 
818                                         IStream*     iface,
819                                         ULARGE_INTEGER libOffset,   /* [in] */ 
820                                         ULARGE_INTEGER cb,          /* [in] */ 
821                                         DWORD          dwLockType)  /* [in] */ 
822 {
823   FIXME("not implemented!\n");
824   return E_NOTIMPL;
825 }
826
827 HRESULT WINAPI StgStreamImpl_UnlockRegion( 
828                                           IStream*     iface,
829                                           ULARGE_INTEGER libOffset,   /* [in] */ 
830                                           ULARGE_INTEGER cb,          /* [in] */ 
831                                           DWORD          dwLockType)  /* [in] */ 
832 {
833   FIXME("not implemented!\n");
834   return E_NOTIMPL;
835 }
836
837 /***
838  * This method is part of the IStream interface.
839  *
840  * This method returns information about the current
841  * stream.
842  *
843  * See the documentation of IStream for more info.
844  */        
845 HRESULT WINAPI StgStreamImpl_Stat( 
846                   IStream*     iface,
847                   STATSTG*       pstatstg,     /* [out] */
848                   DWORD          grfStatFlag)  /* [in] */ 
849 {
850   StgStreamImpl* const This=(StgStreamImpl*)iface;
851
852   StgProperty    curProperty;
853   BOOL         readSucessful;
854   
855   /*
856    * Read the information from the property.
857    */
858   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
859                                              This->ownerProperty,
860                                              &curProperty);
861   
862   if (readSucessful)
863   {
864     StorageUtl_CopyPropertyToSTATSTG(pstatstg, 
865                                      &curProperty, 
866                                      grfStatFlag);
867
868     pstatstg->grfMode = This->grfMode;
869     
870     return S_OK;
871   }
872   
873   return E_FAIL;
874 }
875         
876 HRESULT WINAPI StgStreamImpl_Clone( 
877                                    IStream*     iface,
878                                    IStream**    ppstm) /* [out] */ 
879 {
880   FIXME("not implemented!\n");
881   return E_NOTIMPL;
882 }