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