Release 1.5.29.
[wine] / dlls / ole32 / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winerror.h"
39 #include "winternl.h"
40 #include "wine/debug.h"
41
42 #include "storage32.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45
46 /***
47  * This implements the IUnknown method QueryInterface for this
48  * class
49  */
50 static HRESULT WINAPI StgStreamImpl_QueryInterface(
51                   IStream*     iface,
52                   REFIID         riid,        /* [in] */
53                   void**         ppvObject)   /* [iid_is][out] */
54 {
55   StgStreamImpl* This = impl_from_IStream(iface);
56
57   if (ppvObject==0)
58     return E_INVALIDARG;
59
60   *ppvObject = 0;
61
62   if (IsEqualIID(&IID_IUnknown, riid) ||
63       IsEqualIID(&IID_ISequentialStream, riid) ||
64       IsEqualIID(&IID_IStream, riid))
65   {
66     *ppvObject = &This->IStream_iface;
67   }
68   else
69     return E_NOINTERFACE;
70
71   IStream_AddRef(iface);
72
73   return S_OK;
74 }
75
76 /***
77  * This implements the IUnknown method AddRef for this
78  * class
79  */
80 static ULONG WINAPI StgStreamImpl_AddRef(
81                 IStream* iface)
82 {
83   StgStreamImpl* This = impl_from_IStream(iface);
84   return InterlockedIncrement(&This->ref);
85 }
86
87 /***
88  * This implements the IUnknown method Release for this
89  * class
90  */
91 static ULONG WINAPI StgStreamImpl_Release(
92                 IStream* iface)
93 {
94   StgStreamImpl* This = impl_from_IStream(iface);
95   ULONG ref = InterlockedDecrement(&This->ref);
96
97   if (!ref)
98   {
99     TRACE("(%p)\n", This);
100
101     /*
102      * Release the reference we are holding on the parent storage.
103      * IStorage_Release(&This->parentStorage->IStorage_iface);
104      *
105      * No, don't do this. Some apps call IStorage_Release without
106      * calling IStream_Release first. If we grab a reference the
107      * file is not closed, and the app fails when it tries to
108      * reopen the file (Easy-PC, for example). Just inform the
109      * storage that we have closed the stream
110      */
111
112     if (This->parentStorage)
113       StorageBaseImpl_RemoveStream(This->parentStorage, This);
114     This->parentStorage = 0;
115     HeapFree(GetProcessHeap(), 0, This);
116   }
117
118   return ref;
119 }
120
121 /***
122  * This method is part of the ISequentialStream interface.
123  *
124  * It reads a block of information from the stream at the current
125  * position. It then moves the current position at the end of the
126  * read block
127  *
128  * See the documentation of ISequentialStream for more info.
129  */
130 static HRESULT WINAPI StgStreamImpl_Read(
131                   IStream*     iface,
132                   void*          pv,        /* [length_is][size_is][out] */
133                   ULONG          cb,        /* [in] */
134                   ULONG*         pcbRead)   /* [out] */
135 {
136   StgStreamImpl* This = impl_from_IStream(iface);
137
138   ULONG bytesReadBuffer;
139   HRESULT res;
140
141   TRACE("(%p, %p, %d, %p)\n",
142         iface, pv, cb, pcbRead);
143
144   if (!This->parentStorage)
145   {
146     WARN("storage reverted\n");
147     return STG_E_REVERTED;
148   }
149
150   /*
151    * If the caller is not interested in the number of bytes read,
152    * we use another buffer to avoid "if" statements in the code.
153    */
154   if (pcbRead==0)
155     pcbRead = &bytesReadBuffer;
156
157   res = StorageBaseImpl_StreamReadAt(This->parentStorage,
158                                      This->dirEntry,
159                                      This->currentPosition,
160                                      cb,
161                                      pv,
162                                      pcbRead);
163
164   if (SUCCEEDED(res))
165   {
166     /*
167      * Advance the pointer for the number of positions read.
168      */
169     This->currentPosition.u.LowPart += *pcbRead;
170   }
171
172   TRACE("<-- %08x\n", res);
173   return res;
174 }
175
176 /***
177  * This method is part of the ISequentialStream interface.
178  *
179  * It writes a block of information to the stream at the current
180  * position. It then moves the current position at the end of the
181  * written block. If the stream is too small to fit the block,
182  * the stream is grown to fit.
183  *
184  * See the documentation of ISequentialStream for more info.
185  */
186 static HRESULT WINAPI StgStreamImpl_Write(
187                   IStream*     iface,
188                   const void*    pv,          /* [size_is][in] */
189                   ULONG          cb,          /* [in] */
190                   ULONG*         pcbWritten)  /* [out] */
191 {
192   StgStreamImpl* This = impl_from_IStream(iface);
193
194   ULONG bytesWritten = 0;
195   HRESULT res;
196
197   TRACE("(%p, %p, %d, %p)\n",
198         iface, pv, cb, pcbWritten);
199
200   /*
201    * Do we have permission to write to this stream?
202    */
203   switch(STGM_ACCESS_MODE(This->grfMode))
204   {
205   case STGM_WRITE:
206   case STGM_READWRITE:
207       break;
208   default:
209       WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode));
210       return STG_E_ACCESSDENIED;
211   }
212
213   if (!pv)
214     return STG_E_INVALIDPOINTER;
215
216   if (!This->parentStorage)
217   {
218     WARN("storage reverted\n");
219     return STG_E_REVERTED;
220   }
221  
222   /*
223    * If the caller is not interested in the number of bytes written,
224    * we use another buffer to avoid "if" statements in the code.
225    */
226   if (pcbWritten == 0)
227     pcbWritten = &bytesWritten;
228
229   /*
230    * Initialize the out parameter
231    */
232   *pcbWritten = 0;
233
234   if (cb == 0)
235   {
236     TRACE("<-- S_OK, written 0\n");
237     return S_OK;
238   }
239
240   res = StorageBaseImpl_StreamWriteAt(This->parentStorage,
241                                       This->dirEntry,
242                                       This->currentPosition,
243                                       cb,
244                                       pv,
245                                       pcbWritten);
246
247   /*
248    * Advance the position pointer for the number of positions written.
249    */
250   This->currentPosition.u.LowPart += *pcbWritten;
251
252   if (SUCCEEDED(res))
253     res = StorageBaseImpl_Flush(This->parentStorage);
254
255   TRACE("<-- S_OK, written %u\n", *pcbWritten);
256   return res;
257 }
258
259 /***
260  * This method is part of the IStream interface.
261  *
262  * It will move the current stream pointer according to the parameters
263  * given.
264  *
265  * See the documentation of IStream for more info.
266  */
267 static HRESULT WINAPI StgStreamImpl_Seek(
268                   IStream*      iface,
269                   LARGE_INTEGER   dlibMove,         /* [in] */
270                   DWORD           dwOrigin,         /* [in] */
271                   ULARGE_INTEGER* plibNewPosition) /* [out] */
272 {
273   StgStreamImpl* This = impl_from_IStream(iface);
274
275   ULARGE_INTEGER newPosition;
276   DirEntry currentEntry;
277   HRESULT hr;
278
279   TRACE("(%p, %d, %d, %p)\n",
280         iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
281
282   /*
283    * fail if the stream has no parent (as does windows)
284    */
285
286   if (!This->parentStorage)
287   {
288     WARN("storage reverted\n");
289     return STG_E_REVERTED;
290   }
291
292   /*
293    * The caller is allowed to pass in NULL as the new position return value.
294    * If it happens, we assign it to a dynamic variable to avoid special cases
295    * in the code below.
296    */
297   if (plibNewPosition == 0)
298   {
299     plibNewPosition = &newPosition;
300   }
301
302   /*
303    * The file pointer is moved depending on the given "function"
304    * parameter.
305    */
306   switch (dwOrigin)
307   {
308     case STREAM_SEEK_SET:
309       plibNewPosition->u.HighPart = 0;
310       plibNewPosition->u.LowPart  = 0;
311       break;
312     case STREAM_SEEK_CUR:
313       *plibNewPosition = This->currentPosition;
314       break;
315     case STREAM_SEEK_END:
316       hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, &currentEntry);
317       if (FAILED(hr)) return hr;
318       *plibNewPosition = currentEntry.size;
319       break;
320     default:
321       WARN("invalid dwOrigin %d\n", dwOrigin);
322       return STG_E_INVALIDFUNCTION;
323   }
324
325   plibNewPosition->QuadPart += dlibMove.QuadPart;
326
327   /*
328    * tell the caller what we calculated
329    */
330   This->currentPosition = *plibNewPosition;
331
332   return S_OK;
333 }
334
335 /***
336  * This method is part of the IStream interface.
337  *
338  * It will change the size of a stream.
339  *
340  * See the documentation of IStream for more info.
341  */
342 static HRESULT WINAPI StgStreamImpl_SetSize(
343                                      IStream*      iface,
344                                      ULARGE_INTEGER  libNewSize)   /* [in] */
345 {
346   StgStreamImpl* This = impl_from_IStream(iface);
347
348   HRESULT      hr;
349
350   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
351
352   if(!This->parentStorage)
353   {
354     WARN("storage reverted\n");
355     return STG_E_REVERTED;
356   }
357
358   /*
359    * As documented.
360    */
361   if (libNewSize.u.HighPart != 0)
362   {
363     WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize.u.HighPart);
364     return STG_E_INVALIDFUNCTION;
365   }
366
367   /*
368    * Do we have permission?
369    */
370   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
371   {
372     WARN("access denied\n");
373     return STG_E_ACCESSDENIED;
374   }
375
376   hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize);
377
378   if (SUCCEEDED(hr))
379     hr = StorageBaseImpl_Flush(This->parentStorage);
380
381   return hr;
382 }
383
384 /***
385  * This method is part of the IStream interface.
386  *
387  * It will copy the 'cb' Bytes to 'pstm' IStream.
388  *
389  * See the documentation of IStream for more info.
390  */
391 static HRESULT WINAPI StgStreamImpl_CopyTo(
392                                     IStream*      iface,
393                                     IStream*      pstm,         /* [unique][in] */
394                                     ULARGE_INTEGER  cb,           /* [in] */
395                                     ULARGE_INTEGER* pcbRead,      /* [out] */
396                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
397 {
398   StgStreamImpl* This = impl_from_IStream(iface);
399   HRESULT        hr = S_OK;
400   BYTE           tmpBuffer[128];
401   ULONG          bytesRead, bytesWritten, copySize;
402   ULARGE_INTEGER totalBytesRead;
403   ULARGE_INTEGER totalBytesWritten;
404
405   TRACE("(%p, %p, %d, %p, %p)\n",
406         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
407
408   /*
409    * Sanity check
410    */
411
412   if (!This->parentStorage)
413   {
414     WARN("storage reverted\n");
415     return STG_E_REVERTED;
416   }
417
418   if ( pstm == 0 )
419     return STG_E_INVALIDPOINTER;
420
421   totalBytesRead.QuadPart = 0;
422   totalBytesWritten.QuadPart = 0;
423
424   while ( cb.QuadPart > 0 )
425   {
426     if ( cb.QuadPart >= sizeof(tmpBuffer) )
427       copySize = sizeof(tmpBuffer);
428     else
429       copySize = cb.u.LowPart;
430
431     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
432
433     totalBytesRead.QuadPart += bytesRead;
434
435     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
436
437     totalBytesWritten.QuadPart += bytesWritten;
438
439     /*
440      * Check that read & write operations were successful
441      */
442     if (bytesRead != bytesWritten)
443     {
444       hr = STG_E_MEDIUMFULL;
445       WARN("medium full\n");
446       break;
447     }
448
449     if (bytesRead!=copySize)
450       cb.QuadPart = 0;
451     else
452       cb.QuadPart -= bytesRead;
453   }
454
455   if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
456   if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
457
458   return hr;
459 }
460
461 /***
462  * This method is part of the IStream interface.
463  *
464  * For streams contained in structured storages, this method
465  * does nothing. This is what the documentation tells us.
466  *
467  * See the documentation of IStream for more info.
468  */
469 static HRESULT WINAPI StgStreamImpl_Commit(
470                   IStream*      iface,
471                   DWORD           grfCommitFlags)  /* [in] */
472 {
473   StgStreamImpl* This = impl_from_IStream(iface);
474
475   if (!This->parentStorage)
476   {
477     WARN("storage reverted\n");
478     return STG_E_REVERTED;
479   }
480
481   return StorageBaseImpl_Flush(This->parentStorage);
482 }
483
484 /***
485  * This method is part of the IStream interface.
486  *
487  * For streams contained in structured storages, this method
488  * does nothing. This is what the documentation tells us.
489  *
490  * See the documentation of IStream for more info.
491  */
492 static HRESULT WINAPI StgStreamImpl_Revert(
493                   IStream* iface)
494 {
495   return S_OK;
496 }
497
498 static HRESULT WINAPI StgStreamImpl_LockRegion(
499                                         IStream*     iface,
500                                         ULARGE_INTEGER libOffset,   /* [in] */
501                                         ULARGE_INTEGER cb,          /* [in] */
502                                         DWORD          dwLockType)  /* [in] */
503 {
504   StgStreamImpl* This = impl_from_IStream(iface);
505
506   if (!This->parentStorage)
507   {
508     WARN("storage reverted\n");
509     return STG_E_REVERTED;
510   }
511
512   FIXME("not implemented!\n");
513   return E_NOTIMPL;
514 }
515
516 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
517                                           IStream*     iface,
518                                           ULARGE_INTEGER libOffset,   /* [in] */
519                                           ULARGE_INTEGER cb,          /* [in] */
520                                           DWORD          dwLockType)  /* [in] */
521 {
522   StgStreamImpl* This = impl_from_IStream(iface);
523
524   if (!This->parentStorage)
525   {
526     WARN("storage reverted\n");
527     return STG_E_REVERTED;
528   }
529
530   FIXME("not implemented!\n");
531   return E_NOTIMPL;
532 }
533
534 /***
535  * This method is part of the IStream interface.
536  *
537  * This method returns information about the current
538  * stream.
539  *
540  * See the documentation of IStream for more info.
541  */
542 static HRESULT WINAPI StgStreamImpl_Stat(
543                   IStream*     iface,
544                   STATSTG*       pstatstg,     /* [out] */
545                   DWORD          grfStatFlag)  /* [in] */
546 {
547   StgStreamImpl* This = impl_from_IStream(iface);
548
549   DirEntry     currentEntry;
550   HRESULT      hr;
551
552   TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
553
554   /*
555    * if stream has no parent, return STG_E_REVERTED
556    */
557
558   if (!This->parentStorage)
559   {
560     WARN("storage reverted\n");
561     return STG_E_REVERTED;
562   }
563
564   /*
565    * Read the information from the directory entry.
566    */
567   hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
568                                              This->dirEntry,
569                                              &currentEntry);
570
571   if (SUCCEEDED(hr))
572   {
573     StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
574                      pstatstg,
575                                      &currentEntry,
576                                      grfStatFlag);
577
578     pstatstg->grfMode = This->grfMode;
579
580     /* In simple create mode cbSize is the current pos */
581     if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create)
582       pstatstg->cbSize = This->currentPosition;
583
584     return S_OK;
585   }
586
587   WARN("failed to read entry\n");
588   return hr;
589 }
590
591 /***
592  * This method is part of the IStream interface.
593  *
594  * This method returns a clone of the interface that allows for
595  * another seek pointer
596  *
597  * See the documentation of IStream for more info.
598  *
599  * I am not totally sure what I am doing here but I presume that this
600  * should be basically as simple as creating a new stream with the same
601  * parent etc and positioning its seek cursor.
602  */
603 static HRESULT WINAPI StgStreamImpl_Clone(
604                                    IStream*     iface,
605                                    IStream**    ppstm) /* [out] */
606 {
607   StgStreamImpl* This = impl_from_IStream(iface);
608   HRESULT hres;
609   StgStreamImpl* new_stream;
610   LARGE_INTEGER seek_pos;
611
612   TRACE("%p %p\n", This, ppstm);
613
614   /*
615    * Sanity check
616    */
617
618   if (!This->parentStorage)
619     return STG_E_REVERTED;
620
621   if ( ppstm == 0 )
622     return STG_E_INVALIDPOINTER;
623
624   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry);
625
626   if (!new_stream)
627     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
628
629   *ppstm = &new_stream->IStream_iface;
630   IStream_AddRef(*ppstm);
631
632   seek_pos.QuadPart = This->currentPosition.QuadPart;
633
634   hres = IStream_Seek(*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
635
636   assert (SUCCEEDED(hres));
637
638   return S_OK;
639 }
640
641 /*
642  * Virtual function table for the StgStreamImpl class.
643  */
644 static const IStreamVtbl StgStreamVtbl =
645 {
646     StgStreamImpl_QueryInterface,
647     StgStreamImpl_AddRef,
648     StgStreamImpl_Release,
649     StgStreamImpl_Read,
650     StgStreamImpl_Write,
651     StgStreamImpl_Seek,
652     StgStreamImpl_SetSize,
653     StgStreamImpl_CopyTo,
654     StgStreamImpl_Commit,
655     StgStreamImpl_Revert,
656     StgStreamImpl_LockRegion,
657     StgStreamImpl_UnlockRegion,
658     StgStreamImpl_Stat,
659     StgStreamImpl_Clone
660 };
661
662 /******************************************************************************
663 ** StgStreamImpl implementation
664 */
665
666 /***
667  * This is the constructor for the StgStreamImpl class.
668  *
669  * Params:
670  *    parentStorage - Pointer to the storage that contains the stream to open
671  *    dirEntry      - Index of the directory entry that points to this stream.
672  */
673 StgStreamImpl* StgStreamImpl_Construct(
674                 StorageBaseImpl* parentStorage,
675     DWORD            grfMode,
676     DirRef           dirEntry)
677 {
678   StgStreamImpl* newStream;
679
680   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
681
682   if (newStream)
683   {
684     /*
685      * Set-up the virtual function table and reference count.
686      */
687     newStream->IStream_iface.lpVtbl = &StgStreamVtbl;
688     newStream->ref       = 0;
689
690     newStream->parentStorage = parentStorage;
691
692     /*
693      * We want to nail-down the reference to the storage in case the
694      * stream out-lives the storage in the client application.
695      *
696      * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
697      *
698      * No, don't do this. Some apps call IStorage_Release without
699      * calling IStream_Release first. If we grab a reference the
700      * file is not closed, and the app fails when it tries to
701      * reopen the file (Easy-PC, for example)
702      */
703
704     newStream->grfMode = grfMode;
705     newStream->dirEntry = dirEntry;
706
707     /*
708      * Start the stream at the beginning.
709      */
710     newStream->currentPosition.u.HighPart = 0;
711     newStream->currentPosition.u.LowPart = 0;
712
713     /* add us to the storage's list of active streams */
714     StorageBaseImpl_AddStream(parentStorage, newStream);
715   }
716
717   return newStream;
718 }