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 */
StgStreamImpl_QueryInterface(IStream * iface,REFIID riid,void ** ppvObject)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 */
StgStreamImpl_AddRef(IStream * iface)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 */
StgStreamImpl_Release(IStream * iface)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 */
StgStreamImpl_Read(IStream * iface,void * pv,ULONG cb,ULONG * pcbRead)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 */
StgStreamImpl_Write(IStream * iface,const void * pv,ULONG cb,ULONG * pcbWritten)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 */
StgStreamImpl_Seek(IStream * iface,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)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, ¤tEntry);
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 */
StgStreamImpl_SetSize(IStream * iface,ULARGE_INTEGER libNewSize)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 */
StgStreamImpl_CopyTo(IStream * iface,IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pcbRead,ULARGE_INTEGER * pcbWritten)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 */
StgStreamImpl_Commit(IStream * iface,DWORD grfCommitFlags)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 */
StgStreamImpl_Revert(IStream * iface)490 static HRESULT WINAPI StgStreamImpl_Revert(
491 IStream* iface)
492 {
493 return S_OK;
494 }
495
StgStreamImpl_LockRegion(IStream * iface,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)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
StgStreamImpl_UnlockRegion(IStream * iface,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)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 */
StgStreamImpl_Stat(IStream * iface,STATSTG * pstatstg,DWORD grfStatFlag)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 ¤tEntry);
568
569 if (SUCCEEDED(hr))
570 {
571 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
572 pstatstg,
573 ¤tEntry,
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 */
StgStreamImpl_Clone(IStream * iface,IStream ** ppstm)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 */
StgStreamImpl_Construct(StorageBaseImpl * parentStorage,DWORD grfMode,DirRef dirEntry)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