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