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, ¤tEntry); 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 ¤tEntry); 555 556 if (SUCCEEDED(hr)) 557 { 558 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, 559 pstatstg, 560 ¤tEntry, 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