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, ¤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 */ 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 ¤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 */ 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