Do not call ExitThread() on server communication errors.
[wine] / ole / 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 "wine/obj_storage.h"
19
20 #include "storage32.h"
21
22
23 /*
24  * Virtual function table for the StgStreamImpl class.
25  */
26 static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
27 {
28     StgStreamImpl_QueryInterface,
29     StgStreamImpl_AddRef,
30     StgStreamImpl_Release,
31     StgStreamImpl_Read,
32     StgStreamImpl_Write,
33     StgStreamImpl_Seek,
34     StgStreamImpl_SetSize,
35     StgStreamImpl_CopyTo,
36     StgStreamImpl_Commit,
37     StgStreamImpl_Revert,
38     StgStreamImpl_LockRegion,
39     StgStreamImpl_UnlockRegion,
40     StgStreamImpl_Stat,
41     StgStreamImpl_Clone
42 };
43
44 /******************************************************************************
45 ** StgStreamImpl implementation
46 */
47
48 /***
49  * This is the constructor for the StgStreamImpl class.
50  *
51  * Params:
52  *    parentStorage - Pointer to the storage that contains the stream to open
53  *    ownerProperty - Index of the property that points to this stream.
54  */
55 StgStreamImpl* StgStreamImpl_Construct(
56                 StorageBaseImpl* parentStorage,
57                 ULONG              ownerProperty)
58 {
59   StgStreamImpl* newStream;
60
61   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
62   
63   if (newStream!=0)
64   {
65     /*
66      * Set-up the virtual function table and reference count.
67      */
68     newStream->lpvtbl = &StgStreamImpl_Vtbl;
69     newStream->ref    = 0;
70     
71     /*
72      * We want to nail-down the reference to the storage in case the
73      * stream out-lives the storage in the client application.
74      */
75     newStream->parentStorage = parentStorage;
76     IStorage_AddRef((IStorage*)newStream->parentStorage);
77     
78     newStream->ownerProperty = ownerProperty;
79     
80     /*
81      * Start the stream at the begining.
82      */
83     newStream->currentPosition.HighPart = 0;
84     newStream->currentPosition.LowPart = 0;
85     
86     /*
87      * Initialize the rest of the data.
88      */
89     newStream->streamSize.HighPart = 0;
90     newStream->streamSize.LowPart  = 0;
91     newStream->bigBlockChain       = 0;
92     newStream->smallBlockChain     = 0;
93     
94     /*
95      * Read the size from the property and determine if the blocks forming
96      * this stream are large or small.
97      */
98     StgStreamImpl_OpenBlockChain(newStream);
99   }
100   
101   return newStream;
102 }
103
104 /***
105  * This is the destructor of the StgStreamImpl class.
106  *
107  * This method will clean-up all the resources used-up by the given StgStreamImpl 
108  * class. The pointer passed-in to this function will be freed and will not
109  * be valid anymore.
110  */
111 void StgStreamImpl_Destroy(StgStreamImpl* This)
112 {
113   /*
114    * Release the reference we are holding on the parent storage.
115    */
116   IStorage_Release((IStorage*)This->parentStorage);
117   This->parentStorage = 0;
118
119   /*
120    * Make sure we clean-up the block chain stream objects that we were using.
121    */
122   if (This->bigBlockChain != 0)
123   {
124     BlockChainStream_Destroy(This->bigBlockChain);
125     This->bigBlockChain = 0;
126   }
127
128   if (This->smallBlockChain != 0)
129   {
130     SmallBlockChainStream_Destroy(This->smallBlockChain);
131     This->smallBlockChain = 0;
132   }
133
134   /*
135    * Finally, free the memory used-up by the class.
136    */
137   HeapFree(GetProcessHeap(), 0, This);  
138 }
139
140 /***
141  * This implements the IUnknown method QueryInterface for this
142  * class
143  */
144 HRESULT WINAPI StgStreamImpl_QueryInterface(
145                   IStream*     iface,
146                   REFIID         riid,        /* [in] */          
147                   void**         ppvObject)   /* [iid_is][out] */ 
148 {
149   StgStreamImpl* const This=(StgStreamImpl*)iface;
150
151   /*
152    * Perform a sanity check on the parameters.
153    */
154   if (ppvObject==0)
155     return E_INVALIDARG;
156   
157   /*
158    * Initialize the return parameter.
159    */
160   *ppvObject = 0;
161   
162   /*
163    * Compare the riid with the interface IDs implemented by this object.
164    */
165   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
166   {
167     *ppvObject = (IStream*)This;
168   }
169   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStream)) == 0) 
170   {
171     *ppvObject = (IStream*)This;
172   }
173   
174   /*
175    * Check that we obtained an interface.
176    */
177   if ((*ppvObject)==0)
178     return E_NOINTERFACE;
179   
180   /*
181    * Query Interface always increases the reference count by one when it is
182    * successful
183    */
184   StgStreamImpl_AddRef(iface);
185   
186   return S_OK;;
187 }
188
189 /***
190  * This implements the IUnknown method AddRef for this
191  * class
192  */
193 ULONG WINAPI StgStreamImpl_AddRef(
194                 IStream* iface)
195 {
196   StgStreamImpl* const This=(StgStreamImpl*)iface;
197
198   This->ref++;
199   
200   return This->ref;
201 }
202
203 /***
204  * This implements the IUnknown method Release for this
205  * class
206  */
207 ULONG WINAPI StgStreamImpl_Release(
208                 IStream* iface)
209 {
210   StgStreamImpl* const This=(StgStreamImpl*)iface;
211
212   ULONG newRef;
213   
214   This->ref--;
215   
216   newRef = This->ref;
217   
218   /*
219    * If the reference count goes down to 0, perform suicide.
220    */
221   if (newRef==0)
222   {
223     StgStreamImpl_Destroy(This);
224   }
225   
226   return newRef;
227 }
228
229 /***
230  * This method will open the block chain pointed by the property
231  * that describes the stream.
232  * If the stream's size is null, no chain is opened.
233  */
234 void StgStreamImpl_OpenBlockChain(
235         StgStreamImpl* This)
236 {
237   StgProperty    curProperty;
238   BOOL         readSucessful;
239
240   /*
241    * Make sure no old object is staying behind.
242    */
243   if (This->smallBlockChain != 0)
244   {
245     SmallBlockChainStream_Destroy(This->smallBlockChain);
246     This->smallBlockChain = 0;
247   }
248
249   if (This->bigBlockChain != 0)
250   {
251     BlockChainStream_Destroy(This->bigBlockChain);
252     This->bigBlockChain = 0;
253   }
254
255   /*
256    * Read the information from the property.
257    */
258   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
259                                              This->ownerProperty,
260                                              &curProperty);
261   
262   if (readSucessful)
263   {
264     This->streamSize = curProperty.size;
265     
266     /*
267      * This code supports only streams that are <32 bits in size.
268      */
269     assert(This->streamSize.HighPart == 0);
270     
271     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
272     {
273       assert( (This->streamSize.HighPart == 0) && (This->streamSize.LowPart == 0) );
274     }
275     else
276     {
277       if ( (This->streamSize.HighPart == 0) &&
278            (This->streamSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
279       {
280         This->smallBlockChain = SmallBlockChainStream_Construct(
281                                                                 This->parentStorage->ancestorStorage,   
282                                                                 This->ownerProperty);
283       }
284       else
285       {
286         This->bigBlockChain = BlockChainStream_Construct(
287                                                          This->parentStorage->ancestorStorage,
288                                                          NULL,
289                                                          This->ownerProperty);
290       }
291     }
292   }
293 }
294
295 /***
296  * This method is part of the ISequentialStream interface.
297  *
298  * If reads a block of information from the stream at the current
299  * position. It then moves the current position at the end of the
300  * read block
301  *
302  * See the documentation of ISequentialStream for more info.
303  */
304 HRESULT WINAPI StgStreamImpl_Read( 
305                   IStream*     iface,
306                   void*          pv,        /* [length_is][size_is][out] */
307                   ULONG          cb,        /* [in] */                     
308                   ULONG*         pcbRead)   /* [out] */                    
309 {
310   StgStreamImpl* const This=(StgStreamImpl*)iface;
311
312   ULONG bytesReadBuffer;
313   ULONG bytesToReadFromBuffer;
314   
315   /* 
316    * If the caller is not interested in the nubmer of bytes read,
317    * we use another buffer to avoid "if" statements in the code.
318    */
319   if (pcbRead==0)
320     pcbRead = &bytesReadBuffer;
321   
322   /*
323    * Using the known size of the stream, calculate the number of bytes
324    * to read from the block chain
325    */
326   bytesToReadFromBuffer = MIN( This->streamSize.LowPart - This->currentPosition.LowPart, cb);
327   
328   /*
329    * Depending on the type of chain that was opened when the stream was constructed,
330    * we delegate the work to the method that read the block chains.
331    */
332   if (This->smallBlockChain!=0)
333   {
334     SmallBlockChainStream_ReadAt(This->smallBlockChain,
335                                  This->currentPosition,
336                                  bytesToReadFromBuffer,
337                                  pv,
338                                  pcbRead);
339     
340   }
341   else if (This->bigBlockChain!=0)
342   {
343     BlockChainStream_ReadAt(This->bigBlockChain,
344                             This->currentPosition,
345                             bytesToReadFromBuffer,
346                             pv,
347                             pcbRead);
348   }
349   else
350     assert(FALSE);
351
352   /*
353    * We should always be able to read the proper amount of data from the
354    * chain.
355    */
356   assert(bytesToReadFromBuffer == *pcbRead);
357
358   /*
359    * Advance the pointer for the number of positions read.
360    */
361   This->currentPosition.LowPart += *pcbRead;
362   
363   /*
364    * The function returns S_OK if the buffer was filled completely
365    * it returns S_FALSE if the end of the stream is reached before the
366    * buffer is filled
367    */
368   if(*pcbRead == cb)
369     return S_OK;
370   
371   return S_FALSE;
372 }
373         
374 /***
375  * This method is part of the ISequentialStream interface.
376  *
377  * It writes a block of information to the stream at the current
378  * position. It then moves the current position at the end of the
379  * written block. If the stream is too small to fit the block,
380  * the stream is grown to fit.
381  *
382  * See the documentation of ISequentialStream for more info.
383  */
384 HRESULT WINAPI StgStreamImpl_Write(
385                   IStream*     iface,
386                   const void*    pv,          /* [size_is][in] */ 
387                   ULONG          cb,          /* [in] */          
388                   ULONG*         pcbWritten)  /* [out] */         
389 {
390   StgStreamImpl* const This=(StgStreamImpl*)iface;
391
392   ULARGE_INTEGER newSize;
393   ULONG bytesWritten = 0;
394   
395   /*
396    * If the caller is not interested in the number of bytes written,
397    * we use another buffer to avoid "if" statements in the code.
398    */
399   if (pcbWritten == 0)
400     pcbWritten = &bytesWritten;
401   
402   if (cb == 0)
403   {
404     return S_OK;
405   }
406   else
407   {
408     newSize.HighPart = 0;
409     newSize.LowPart = This->currentPosition.LowPart + cb;
410   }
411   
412   /*
413    * Verify if we need to grow the stream
414    */
415   if (newSize.LowPart > This->streamSize.LowPart)
416   {
417     /* grow stream */
418     StgStreamImpl_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     SmallBlockChainStream_WriteAt(This->smallBlockChain,
428                                   This->currentPosition,
429                                   cb,
430                                   pv,
431                                   pcbWritten);
432     
433   }
434   else if (This->bigBlockChain!=0)
435   {
436     BlockChainStream_WriteAt(This->bigBlockChain,
437                              This->currentPosition,
438                              cb,
439                              pv,
440                              pcbWritten);
441   }
442   else
443     assert(FALSE);
444   
445   /*
446    * Advance the position pointer for the number of positions written.
447    */
448   This->currentPosition.LowPart += *pcbWritten;
449   
450   return S_OK;
451 }
452
453 /***
454  * This method is part of the IStream interface.
455  *
456  * It will move the current stream pointer according to the parameters
457  * given.
458  *
459  * See the documentation of IStream for more info.
460  */        
461 HRESULT WINAPI StgStreamImpl_Seek( 
462                   IStream*      iface,
463                   LARGE_INTEGER   dlibMove,         /* [in] */ 
464                   DWORD           dwOrigin,         /* [in] */ 
465                   ULARGE_INTEGER* plibNewPosition) /* [out] */
466 {
467   StgStreamImpl* const This=(StgStreamImpl*)iface;
468
469   ULARGE_INTEGER newPosition;
470
471   /* 
472    * The caller is allowed to pass in NULL as the new position return value.
473    * If it happens, we assign it to a dynamic variable to avoid special cases
474    * in the code below.
475    */
476   if (plibNewPosition == 0)
477   {
478     plibNewPosition = &newPosition;
479   }
480
481   /*
482    * The file pointer is moved depending on the given "function"
483    * parameter.
484    */
485   switch (dwOrigin)
486   {
487     case STREAM_SEEK_SET:
488       plibNewPosition->HighPart = 0;
489       plibNewPosition->LowPart  = 0;
490       break;
491     case STREAM_SEEK_CUR:
492       *plibNewPosition = This->currentPosition;
493       break;
494     case STREAM_SEEK_END:
495       *plibNewPosition = This->streamSize;
496       break;
497     default:
498       return STG_E_INVALIDFUNCTION;
499   }
500
501   /*
502    * We don't support files with offsets of 64 bits.
503    */
504   assert(dlibMove.HighPart == 0);
505
506   /*
507    * Check if we end-up before the beginning of the file. That should trigger an
508    * error.
509    */
510   if ( (dlibMove.LowPart<0) && (plibNewPosition->LowPart < (ULONG)(-dlibMove.LowPart)) )
511   {
512     /*
513      * I don't know what error to send there.
514      */
515     return E_FAIL;
516   }
517
518   /*
519    * Move the actual file pointer
520    * If the file pointer ends-up after the end of the stream, the next Write operation will
521    * make the file larger. This is how it is documented.
522    */
523   plibNewPosition->LowPart += dlibMove.LowPart;
524   This->currentPosition = *plibNewPosition;
525  
526   return S_OK;
527 }
528
529 /***
530  * This method is part of the IStream interface.
531  *
532  * It will change the size of a stream.
533  *
534  * TODO: Switch from small blocks to big blocks and vice versa.
535  *
536  * See the documentation of IStream for more info.
537  */
538 HRESULT WINAPI StgStreamImpl_SetSize( 
539                                      IStream*      iface,
540                                      ULARGE_INTEGER  libNewSize)   /* [in] */ 
541 {
542   StgStreamImpl* const This=(StgStreamImpl*)iface;
543
544   StgProperty    curProperty;
545   BOOL         Success;
546
547   /*
548    * As documented.
549    */
550   if (libNewSize.HighPart != 0)
551     return STG_E_INVALIDFUNCTION;
552   
553   if (This->streamSize.LowPart == libNewSize.LowPart)
554     return S_OK;
555
556   /*
557    * This will happen if we're creating a stream
558    */
559   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
560   {
561     if (libNewSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
562     {
563       This->smallBlockChain = SmallBlockChainStream_Construct(
564                                     This->parentStorage->ancestorStorage,
565                                     This->ownerProperty);
566     }
567     else
568     {
569       This->bigBlockChain = BlockChainStream_Construct(
570                                 This->parentStorage->ancestorStorage,
571                                 NULL,
572                                 This->ownerProperty);
573     }
574   }
575
576   /*
577    * Read this stream's property to see if it's small blocks or big blocks
578    */
579   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
580                                        This->ownerProperty,
581                                        &curProperty); 
582   /*
583    * Determine if we have to switch from small to big blocks or vice versa
584    */
585   
586   if (curProperty.size.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
587   {
588     if (libNewSize.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
589     {
590       /*
591        * Transform the small block chain into a big block chain
592        */
593       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
594                                 This->parentStorage->ancestorStorage,
595                                 &This->smallBlockChain);
596     }
597   }
598
599   if (This->smallBlockChain!=0)
600   {
601     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
602   }
603   else
604   {
605     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
606   }
607
608   /*
609    * Write to the property the new information about this stream
610    */
611   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
612                                        This->ownerProperty,
613                                        &curProperty);
614
615   curProperty.size.HighPart = libNewSize.HighPart;
616   curProperty.size.LowPart = libNewSize.LowPart;
617   
618   if (Success)
619   {
620     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
621                                 This->ownerProperty,
622                                 &curProperty);
623   }
624   
625   This->streamSize = libNewSize;
626   
627   return S_OK;
628 }
629         
630 HRESULT WINAPI StgStreamImpl_CopyTo( 
631                                     IStream*      iface,
632                                     IStream*      pstm,         /* [unique][in] */ 
633                                     ULARGE_INTEGER  cb,           /* [in] */         
634                                     ULARGE_INTEGER* pcbRead,      /* [out] */        
635                                     ULARGE_INTEGER* pcbWritten)   /* [out] */        
636 {
637   return E_NOTIMPL;
638 }
639
640 /***
641  * This method is part of the IStream interface.
642  *
643  * For streams contained in structured storages, this method
644  * does nothing. This is what the documentation tells us.
645  *
646  * See the documentation of IStream for more info.
647  */        
648 HRESULT WINAPI StgStreamImpl_Commit( 
649                   IStream*      iface,
650                   DWORD           grfCommitFlags)  /* [in] */ 
651 {
652   return S_OK;
653 }
654
655 /***
656  * This method is part of the IStream interface.
657  *
658  * For streams contained in structured storages, this method
659  * does nothing. This is what the documentation tells us.
660  *
661  * See the documentation of IStream for more info.
662  */        
663 HRESULT WINAPI StgStreamImpl_Revert( 
664                   IStream* iface)
665 {
666   return S_OK;
667 }
668
669 HRESULT WINAPI StgStreamImpl_LockRegion( 
670                                         IStream*     iface,
671                                         ULARGE_INTEGER libOffset,   /* [in] */ 
672                                         ULARGE_INTEGER cb,          /* [in] */ 
673                                         DWORD          dwLockType)  /* [in] */ 
674 {
675   return E_NOTIMPL;
676 }
677
678 HRESULT WINAPI StgStreamImpl_UnlockRegion( 
679                                           IStream*     iface,
680                                           ULARGE_INTEGER libOffset,   /* [in] */ 
681                                           ULARGE_INTEGER cb,          /* [in] */ 
682                                           DWORD          dwLockType)  /* [in] */ 
683 {
684   return E_NOTIMPL;
685 }
686
687 /***
688  * This method is part of the IStream interface.
689  *
690  * This method returns information about the current
691  * stream.
692  *
693  * See the documentation of IStream for more info.
694  */        
695 HRESULT WINAPI StgStreamImpl_Stat( 
696                   IStream*     iface,
697                   STATSTG*       pstatstg,     /* [out] */
698                   DWORD          grfStatFlag)  /* [in] */ 
699 {
700   StgStreamImpl* const This=(StgStreamImpl*)iface;
701
702   StgProperty    curProperty;
703   BOOL         readSucessful;
704   
705   /*
706    * Read the information from the property.
707    */
708   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
709                                              This->ownerProperty,
710                                              &curProperty);
711   
712   if (readSucessful)
713   {
714     StorageUtl_CopyPropertyToSTATSTG(pstatstg, 
715                                      &curProperty, 
716                                      grfStatFlag);
717     
718     return S_OK;
719   }
720   
721   return E_FAIL;
722 }
723         
724 HRESULT WINAPI StgStreamImpl_Clone( 
725                                    IStream*     iface,
726                                    IStream**    ppstm) /* [out] */ 
727 {
728   return E_NOTIMPL;
729 }