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