1 /* 2 * Compound Storage (32 bit version) 3 * Storage implementation 4 * 5 * This file contains the compound file implementation 6 * of the storage interface. 7 * 8 * Copyright 1999 Francis Beaudet 9 * Copyright 1999 Sylvain St-Germain 10 * Copyright 1999 Thuy Nguyen 11 * Copyright 2005 Mike McCormack 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Lesser General Public 15 * License as published by the Free Software Foundation; either 16 * version 2.1 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 26 * 27 * NOTES 28 * The compound file implementation of IStorage used for create 29 * and manage substorages and streams within a storage object 30 * residing in a compound file object. 31 */ 32 33 #include "precomp.h" 34 #include "storage32.h" 35 36 #include <wine/wingdi16.h> 37 38 WINE_DEFAULT_DEBUG_CHANNEL(storage); 39 40 41 /* 42 * These are signatures to detect the type of Document file. 43 */ 44 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1}; 45 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d}; 46 47 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl; 48 49 50 /**************************************************************************** 51 * StorageInternalImpl definitions. 52 * 53 * Definition of the implementation structure for the IStorage interface. 54 * This one implements the IStorage interface for storage that are 55 * inside another storage. 56 */ 57 typedef struct StorageInternalImpl 58 { 59 struct StorageBaseImpl base; 60 61 /* 62 * Entry in the parent's stream tracking list 63 */ 64 struct list ParentListEntry; 65 66 StorageBaseImpl *parentStorage; 67 } StorageInternalImpl; 68 69 static const IStorageVtbl StorageInternalImpl_Vtbl; 70 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef); 71 72 typedef struct TransactedDirEntry 73 { 74 /* If applicable, a reference to the original DirEntry in the transacted 75 * parent. If this is a newly-created entry, DIRENTRY_NULL. */ 76 DirRef transactedParentEntry; 77 78 /* True if this entry is being used. */ 79 BOOL inuse; 80 81 /* True if data is up to date. */ 82 BOOL read; 83 84 /* True if this entry has been modified. */ 85 BOOL dirty; 86 87 /* True if this entry's stream has been modified. */ 88 BOOL stream_dirty; 89 90 /* True if this entry has been deleted in the transacted storage, but the 91 * delete has not yet been committed. */ 92 BOOL deleted; 93 94 /* If this entry's stream has been modified, a reference to where the stream 95 * is stored in the snapshot file. */ 96 DirRef stream_entry; 97 98 /* This directory entry's data, including any changes that have been made. */ 99 DirEntry data; 100 101 /* A reference to the parent of this node. This is only valid while we are 102 * committing changes. */ 103 DirRef parent; 104 105 /* A reference to a newly-created entry in the transacted parent. This is 106 * always equal to transactedParentEntry except when committing changes. */ 107 DirRef newTransactedParentEntry; 108 } TransactedDirEntry; 109 110 111 /**************************************************************************** 112 * Transacted storage object. 113 */ 114 typedef struct TransactedSnapshotImpl 115 { 116 struct StorageBaseImpl base; 117 118 /* 119 * Modified streams are temporarily saved to the scratch file. 120 */ 121 StorageBaseImpl *scratch; 122 123 /* The directory structure is kept here, so that we can track how these 124 * entries relate to those in the parent storage. */ 125 TransactedDirEntry *entries; 126 ULONG entries_size; 127 ULONG firstFreeEntry; 128 129 /* 130 * Changes are committed to the transacted parent. 131 */ 132 StorageBaseImpl *transactedParent; 133 134 /* The transaction signature from when we last committed */ 135 ULONG lastTransactionSig; 136 } TransactedSnapshotImpl; 137 138 static const IStorageVtbl TransactedSnapshotImpl_Vtbl; 139 static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**); 140 141 typedef struct TransactedSharedImpl 142 { 143 struct StorageBaseImpl base; 144 145 /* 146 * Snapshot and uncommitted changes go here. 147 */ 148 TransactedSnapshotImpl *scratch; 149 150 /* 151 * Changes are committed to the transacted parent. 152 */ 153 StorageBaseImpl *transactedParent; 154 155 /* The transaction signature from when we last committed */ 156 ULONG lastTransactionSig; 157 } TransactedSharedImpl; 158 159 160 /**************************************************************************** 161 * BlockChainStream definitions. 162 * 163 * The BlockChainStream class is a utility class that is used to create an 164 * abstraction of the big block chains in the storage file. 165 */ 166 167 struct BlockChainRun 168 { 169 /* This represents a range of blocks that happen reside in consecutive sectors. */ 170 ULONG firstSector; 171 ULONG firstOffset; 172 ULONG lastOffset; 173 }; 174 175 typedef struct BlockChainBlock 176 { 177 ULONG index; 178 ULONG sector; 179 BOOL read; 180 BOOL dirty; 181 BYTE data[MAX_BIG_BLOCK_SIZE]; 182 } BlockChainBlock; 183 184 struct BlockChainStream 185 { 186 StorageImpl* parentStorage; 187 ULONG* headOfStreamPlaceHolder; 188 DirRef ownerDirEntry; 189 struct BlockChainRun* indexCache; 190 ULONG indexCacheLen; 191 ULONG indexCacheSize; 192 BlockChainBlock cachedBlocks[2]; 193 ULONG blockToEvict; 194 ULONG tailIndex; 195 ULONG numBlocks; 196 }; 197 198 /* Returns the number of blocks that comprises this chain. 199 * This is not the size of the stream as the last block may not be full! 200 */ 201 static inline ULONG BlockChainStream_GetCount(BlockChainStream* This) 202 { 203 return This->numBlocks; 204 } 205 206 static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); 207 static void BlockChainStream_Destroy(BlockChainStream*); 208 static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); 209 static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); 210 static HRESULT BlockChainStream_Flush(BlockChainStream*); 211 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*); 212 static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER); 213 214 215 /**************************************************************************** 216 * SmallBlockChainStream definitions. 217 * 218 * The SmallBlockChainStream class is a utility class that is used to create an 219 * abstraction of the small block chains in the storage file. 220 */ 221 222 struct SmallBlockChainStream 223 { 224 StorageImpl* parentStorage; 225 DirRef ownerDirEntry; 226 ULONG* headOfStreamPlaceHolder; 227 }; 228 229 static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef); 230 static void SmallBlockChainStream_Destroy(SmallBlockChainStream*); 231 static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*); 232 static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*); 233 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*); 234 static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER); 235 236 237 /************************************************************************ 238 * STGM Functions 239 ***********************************************************************/ 240 241 /************************************************************************ 242 * This method validates an STGM parameter that can contain the values below 243 * 244 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values. 245 * The stgm values contained in 0xffff0000 are bitmasks. 246 * 247 * STGM_DIRECT 0x00000000 248 * STGM_TRANSACTED 0x00010000 249 * STGM_SIMPLE 0x08000000 250 * 251 * STGM_READ 0x00000000 252 * STGM_WRITE 0x00000001 253 * STGM_READWRITE 0x00000002 254 * 255 * STGM_SHARE_DENY_NONE 0x00000040 256 * STGM_SHARE_DENY_READ 0x00000030 257 * STGM_SHARE_DENY_WRITE 0x00000020 258 * STGM_SHARE_EXCLUSIVE 0x00000010 259 * 260 * STGM_PRIORITY 0x00040000 261 * STGM_DELETEONRELEASE 0x04000000 262 * 263 * STGM_CREATE 0x00001000 264 * STGM_CONVERT 0x00020000 265 * STGM_FAILIFTHERE 0x00000000 266 * 267 * STGM_NOSCRATCH 0x00100000 268 * STGM_NOSNAPSHOT 0x00200000 269 */ 270 static HRESULT validateSTGM(DWORD stgm) 271 { 272 DWORD access = STGM_ACCESS_MODE(stgm); 273 DWORD share = STGM_SHARE_MODE(stgm); 274 DWORD create = STGM_CREATE_MODE(stgm); 275 276 if (stgm&~STGM_KNOWN_FLAGS) 277 { 278 ERR("unknown flags %08x\n", stgm); 279 return E_FAIL; 280 } 281 282 switch (access) 283 { 284 case STGM_READ: 285 case STGM_WRITE: 286 case STGM_READWRITE: 287 break; 288 default: 289 return E_FAIL; 290 } 291 292 switch (share) 293 { 294 case STGM_SHARE_DENY_NONE: 295 case STGM_SHARE_DENY_READ: 296 case STGM_SHARE_DENY_WRITE: 297 case STGM_SHARE_EXCLUSIVE: 298 break; 299 case 0: 300 if (!(stgm & STGM_TRANSACTED)) 301 return E_FAIL; 302 break; 303 default: 304 return E_FAIL; 305 } 306 307 switch (create) 308 { 309 case STGM_CREATE: 310 case STGM_FAILIFTHERE: 311 break; 312 default: 313 return E_FAIL; 314 } 315 316 /* 317 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE 318 */ 319 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) ) 320 return E_FAIL; 321 322 /* 323 * STGM_CREATE | STGM_CONVERT 324 * if both are false, STGM_FAILIFTHERE is set to TRUE 325 */ 326 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) ) 327 return E_FAIL; 328 329 /* 330 * STGM_NOSCRATCH requires STGM_TRANSACTED 331 */ 332 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) ) 333 return E_FAIL; 334 335 /* 336 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and 337 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE` 338 */ 339 if ( (stgm & STGM_NOSNAPSHOT) && 340 (!(stgm & STGM_TRANSACTED) || 341 share == STGM_SHARE_EXCLUSIVE || 342 share == STGM_SHARE_DENY_WRITE) ) 343 return E_FAIL; 344 345 return S_OK; 346 } 347 348 /************************************************************************ 349 * GetShareModeFromSTGM 350 * 351 * This method will return a share mode flag from a STGM value. 352 * The STGM value is assumed valid. 353 */ 354 static DWORD GetShareModeFromSTGM(DWORD stgm) 355 { 356 switch (STGM_SHARE_MODE(stgm)) 357 { 358 case 0: 359 assert(stgm & STGM_TRANSACTED); 360 /* fall-through */ 361 case STGM_SHARE_DENY_NONE: 362 return FILE_SHARE_READ | FILE_SHARE_WRITE; 363 case STGM_SHARE_DENY_READ: 364 return FILE_SHARE_WRITE; 365 case STGM_SHARE_DENY_WRITE: 366 case STGM_SHARE_EXCLUSIVE: 367 return FILE_SHARE_READ; 368 } 369 ERR("Invalid share mode!\n"); 370 assert(0); 371 return 0; 372 } 373 374 /************************************************************************ 375 * GetAccessModeFromSTGM 376 * 377 * This method will return an access mode flag from a STGM value. 378 * The STGM value is assumed valid. 379 */ 380 static DWORD GetAccessModeFromSTGM(DWORD stgm) 381 { 382 switch (STGM_ACCESS_MODE(stgm)) 383 { 384 case STGM_READ: 385 return GENERIC_READ; 386 case STGM_WRITE: 387 case STGM_READWRITE: 388 return GENERIC_READ | GENERIC_WRITE; 389 } 390 ERR("Invalid access mode!\n"); 391 assert(0); 392 return 0; 393 } 394 395 /************************************************************************ 396 * GetCreationModeFromSTGM 397 * 398 * This method will return a creation mode flag from a STGM value. 399 * The STGM value is assumed valid. 400 */ 401 static DWORD GetCreationModeFromSTGM(DWORD stgm) 402 { 403 switch(STGM_CREATE_MODE(stgm)) 404 { 405 case STGM_CREATE: 406 return CREATE_ALWAYS; 407 case STGM_CONVERT: 408 FIXME("STGM_CONVERT not implemented!\n"); 409 return CREATE_NEW; 410 case STGM_FAILIFTHERE: 411 return CREATE_NEW; 412 } 413 ERR("Invalid create mode!\n"); 414 assert(0); 415 return 0; 416 } 417 418 419 /************************************************************************ 420 * IDirectWriterLock implementation 421 ***********************************************************************/ 422 423 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface ) 424 { 425 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface); 426 } 427 428 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj) 429 { 430 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 431 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj); 432 } 433 434 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface) 435 { 436 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 437 return IStorage_AddRef(&This->IStorage_iface); 438 } 439 440 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface) 441 { 442 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 443 return IStorage_Release(&This->IStorage_iface); 444 } 445 446 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout) 447 { 448 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 449 FIXME("(%p)->(%d): stub\n", This, timeout); 450 return E_NOTIMPL; 451 } 452 453 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface) 454 { 455 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 456 FIXME("(%p): stub\n", This); 457 return E_NOTIMPL; 458 } 459 460 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface) 461 { 462 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface); 463 FIXME("(%p): stub\n", This); 464 return E_NOTIMPL; 465 } 466 467 static const IDirectWriterLockVtbl DirectWriterLockVtbl = 468 { 469 directwriterlock_QueryInterface, 470 directwriterlock_AddRef, 471 directwriterlock_Release, 472 directwriterlock_WaitForWriteAccess, 473 directwriterlock_ReleaseWriteAccess, 474 directwriterlock_HaveWriteAccess 475 }; 476 477 478 /************************************************************************ 479 * StorageBaseImpl implementation : Tree helper functions 480 ***********************************************************************/ 481 482 /**************************************************************************** 483 * 484 * Internal Method 485 * 486 * Case insensitive comparison of DirEntry.name by first considering 487 * their size. 488 * 489 * Returns <0 when name1 < name2 490 * >0 when name1 > name2 491 * 0 when name1 == name2 492 */ 493 static LONG entryNameCmp( 494 const OLECHAR *name1, 495 const OLECHAR *name2) 496 { 497 LONG diff = lstrlenW(name1) - lstrlenW(name2); 498 499 while (diff == 0 && *name1 != 0) 500 { 501 /* 502 * We compare the string themselves only when they are of the same length 503 */ 504 diff = toupperW(*name1++) - toupperW(*name2++); 505 } 506 507 return diff; 508 } 509 510 /**************************************************************************** 511 * 512 * Internal Method 513 * 514 * Find and read the element of a storage with the given name. 515 */ 516 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry, 517 const OLECHAR *name, DirEntry *data) 518 { 519 DirRef currentEntry; 520 521 /* Read the storage entry to find the root of the tree. */ 522 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data); 523 524 currentEntry = data->dirRootEntry; 525 526 while (currentEntry != DIRENTRY_NULL) 527 { 528 LONG cmp; 529 530 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data); 531 532 cmp = entryNameCmp(name, data->name); 533 534 if (cmp == 0) 535 /* found it */ 536 break; 537 538 else if (cmp < 0) 539 currentEntry = data->leftChild; 540 541 else if (cmp > 0) 542 currentEntry = data->rightChild; 543 } 544 545 return currentEntry; 546 } 547 548 /**************************************************************************** 549 * 550 * Internal Method 551 * 552 * Find and read the binary tree parent of the element with the given name. 553 * 554 * If there is no such element, find a place where it could be inserted and 555 * return STG_E_FILENOTFOUND. 556 */ 557 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry, 558 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry, 559 ULONG *relation) 560 { 561 DirRef childEntry; 562 DirEntry childData; 563 564 /* Read the storage entry to find the root of the tree. */ 565 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData); 566 567 *parentEntry = storageEntry; 568 *relation = DIRENTRY_RELATION_DIR; 569 570 childEntry = parentData->dirRootEntry; 571 572 while (childEntry != DIRENTRY_NULL) 573 { 574 LONG cmp; 575 576 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData); 577 578 cmp = entryNameCmp(childName, childData.name); 579 580 if (cmp == 0) 581 /* found it */ 582 break; 583 584 else if (cmp < 0) 585 { 586 *parentData = childData; 587 *parentEntry = childEntry; 588 *relation = DIRENTRY_RELATION_PREVIOUS; 589 590 childEntry = parentData->leftChild; 591 } 592 593 else if (cmp > 0) 594 { 595 *parentData = childData; 596 *parentEntry = childEntry; 597 *relation = DIRENTRY_RELATION_NEXT; 598 599 childEntry = parentData->rightChild; 600 } 601 } 602 603 if (childEntry == DIRENTRY_NULL) 604 return STG_E_FILENOTFOUND; 605 else 606 return S_OK; 607 } 608 609 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target) 610 { 611 switch (relation) 612 { 613 case DIRENTRY_RELATION_PREVIOUS: 614 entry->leftChild = new_target; 615 break; 616 case DIRENTRY_RELATION_NEXT: 617 entry->rightChild = new_target; 618 break; 619 case DIRENTRY_RELATION_DIR: 620 entry->dirRootEntry = new_target; 621 break; 622 default: 623 assert(0); 624 } 625 } 626 627 /**************************************************************************** 628 * 629 * Internal Method 630 * 631 * Add a directory entry to a storage 632 */ 633 static HRESULT insertIntoTree( 634 StorageBaseImpl *This, 635 DirRef parentStorageIndex, 636 DirRef newEntryIndex) 637 { 638 DirEntry currentEntry; 639 DirEntry newEntry; 640 641 /* 642 * Read the inserted entry 643 */ 644 StorageBaseImpl_ReadDirEntry(This, 645 newEntryIndex, 646 &newEntry); 647 648 /* 649 * Read the storage entry 650 */ 651 StorageBaseImpl_ReadDirEntry(This, 652 parentStorageIndex, 653 ¤tEntry); 654 655 if (currentEntry.dirRootEntry != DIRENTRY_NULL) 656 { 657 /* 658 * The root storage contains some element, therefore, start the research 659 * for the appropriate location. 660 */ 661 BOOL found = FALSE; 662 DirRef current, next, previous, currentEntryId; 663 664 /* 665 * Keep a reference to the root of the storage's element tree 666 */ 667 currentEntryId = currentEntry.dirRootEntry; 668 669 /* 670 * Read 671 */ 672 StorageBaseImpl_ReadDirEntry(This, 673 currentEntry.dirRootEntry, 674 ¤tEntry); 675 676 previous = currentEntry.leftChild; 677 next = currentEntry.rightChild; 678 current = currentEntryId; 679 680 while (!found) 681 { 682 LONG diff = entryNameCmp( newEntry.name, currentEntry.name); 683 684 if (diff < 0) 685 { 686 if (previous != DIRENTRY_NULL) 687 { 688 StorageBaseImpl_ReadDirEntry(This, 689 previous, 690 ¤tEntry); 691 current = previous; 692 } 693 else 694 { 695 currentEntry.leftChild = newEntryIndex; 696 StorageBaseImpl_WriteDirEntry(This, 697 current, 698 ¤tEntry); 699 found = TRUE; 700 } 701 } 702 else if (diff > 0) 703 { 704 if (next != DIRENTRY_NULL) 705 { 706 StorageBaseImpl_ReadDirEntry(This, 707 next, 708 ¤tEntry); 709 current = next; 710 } 711 else 712 { 713 currentEntry.rightChild = newEntryIndex; 714 StorageBaseImpl_WriteDirEntry(This, 715 current, 716 ¤tEntry); 717 found = TRUE; 718 } 719 } 720 else 721 { 722 /* 723 * Trying to insert an item with the same name in the 724 * subtree structure. 725 */ 726 return STG_E_FILEALREADYEXISTS; 727 } 728 729 previous = currentEntry.leftChild; 730 next = currentEntry.rightChild; 731 } 732 } 733 else 734 { 735 /* 736 * The storage is empty, make the new entry the root of its element tree 737 */ 738 currentEntry.dirRootEntry = newEntryIndex; 739 StorageBaseImpl_WriteDirEntry(This, 740 parentStorageIndex, 741 ¤tEntry); 742 } 743 744 return S_OK; 745 } 746 747 /************************************************************************* 748 * 749 * Internal Method 750 * 751 * This method removes a directory entry from its parent storage tree without 752 * freeing any resources attached to it. 753 */ 754 static HRESULT removeFromTree( 755 StorageBaseImpl *This, 756 DirRef parentStorageIndex, 757 DirRef deletedIndex) 758 { 759 DirEntry entryToDelete; 760 DirEntry parentEntry; 761 DirRef parentEntryRef; 762 ULONG typeOfRelation; 763 HRESULT hr; 764 765 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete); 766 767 if (hr != S_OK) 768 return hr; 769 770 /* 771 * Find the element that links to the one we want to delete. 772 */ 773 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name, 774 &parentEntry, &parentEntryRef, &typeOfRelation); 775 776 if (hr != S_OK) 777 return hr; 778 779 if (entryToDelete.leftChild != DIRENTRY_NULL) 780 { 781 /* 782 * Replace the deleted entry with its left child 783 */ 784 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild); 785 786 hr = StorageBaseImpl_WriteDirEntry( 787 This, 788 parentEntryRef, 789 &parentEntry); 790 if(FAILED(hr)) 791 { 792 return hr; 793 } 794 795 if (entryToDelete.rightChild != DIRENTRY_NULL) 796 { 797 /* 798 * We need to reinsert the right child somewhere. We already know it and 799 * its children are greater than everything in the left tree, so we 800 * insert it at the rightmost point in the left tree. 801 */ 802 DirRef newRightChildParent = entryToDelete.leftChild; 803 DirEntry newRightChildParentEntry; 804 805 do 806 { 807 hr = StorageBaseImpl_ReadDirEntry( 808 This, 809 newRightChildParent, 810 &newRightChildParentEntry); 811 if (FAILED(hr)) 812 { 813 return hr; 814 } 815 816 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL) 817 newRightChildParent = newRightChildParentEntry.rightChild; 818 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL); 819 820 newRightChildParentEntry.rightChild = entryToDelete.rightChild; 821 822 hr = StorageBaseImpl_WriteDirEntry( 823 This, 824 newRightChildParent, 825 &newRightChildParentEntry); 826 if (FAILED(hr)) 827 { 828 return hr; 829 } 830 } 831 } 832 else 833 { 834 /* 835 * Replace the deleted entry with its right child 836 */ 837 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild); 838 839 hr = StorageBaseImpl_WriteDirEntry( 840 This, 841 parentEntryRef, 842 &parentEntry); 843 if(FAILED(hr)) 844 { 845 return hr; 846 } 847 } 848 849 return hr; 850 } 851 852 853 /************************************************************************ 854 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements 855 ***********************************************************************/ 856 857 /* 858 * IEnumSTATSTGImpl definitions. 859 * 860 * Definition of the implementation structure for the IEnumSTATSTGImpl interface. 861 * This class allows iterating through the content of a storage and finding 862 * specific items inside it. 863 */ 864 struct IEnumSTATSTGImpl 865 { 866 IEnumSTATSTG IEnumSTATSTG_iface; 867 868 LONG ref; /* Reference count */ 869 StorageBaseImpl* parentStorage; /* Reference to the parent storage */ 870 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */ 871 872 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */ 873 }; 874 875 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface) 876 { 877 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface); 878 } 879 880 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This) 881 { 882 IStorage_Release(&This->parentStorage->IStorage_iface); 883 HeapFree(GetProcessHeap(), 0, This); 884 } 885 886 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface( 887 IEnumSTATSTG* iface, 888 REFIID riid, 889 void** ppvObject) 890 { 891 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 892 893 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); 894 895 if (ppvObject==0) 896 return E_INVALIDARG; 897 898 *ppvObject = 0; 899 900 if (IsEqualGUID(&IID_IUnknown, riid) || 901 IsEqualGUID(&IID_IEnumSTATSTG, riid)) 902 { 903 *ppvObject = &This->IEnumSTATSTG_iface; 904 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface); 905 TRACE("<-- %p\n", *ppvObject); 906 return S_OK; 907 } 908 909 TRACE("<-- E_NOINTERFACE\n"); 910 return E_NOINTERFACE; 911 } 912 913 static ULONG WINAPI IEnumSTATSTGImpl_AddRef( 914 IEnumSTATSTG* iface) 915 { 916 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 917 return InterlockedIncrement(&This->ref); 918 } 919 920 static ULONG WINAPI IEnumSTATSTGImpl_Release( 921 IEnumSTATSTG* iface) 922 { 923 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 924 925 ULONG newRef; 926 927 newRef = InterlockedDecrement(&This->ref); 928 929 if (newRef==0) 930 { 931 IEnumSTATSTGImpl_Destroy(This); 932 } 933 934 return newRef; 935 } 936 937 static HRESULT IEnumSTATSTGImpl_GetNextRef( 938 IEnumSTATSTGImpl* This, 939 DirRef *ref) 940 { 941 DirRef result = DIRENTRY_NULL; 942 DirRef searchNode; 943 DirEntry entry; 944 HRESULT hr; 945 WCHAR result_name[DIRENTRY_NAME_MAX_LEN]; 946 947 TRACE("%p,%p\n", This, ref); 948 949 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, 950 This->parentStorage->storageDirEntry, &entry); 951 searchNode = entry.dirRootEntry; 952 953 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL) 954 { 955 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry); 956 957 if (SUCCEEDED(hr)) 958 { 959 LONG diff = entryNameCmp( entry.name, This->name); 960 961 if (diff <= 0) 962 { 963 searchNode = entry.rightChild; 964 } 965 else 966 { 967 result = searchNode; 968 memcpy(result_name, entry.name, sizeof(result_name)); 969 searchNode = entry.leftChild; 970 } 971 } 972 } 973 974 if (SUCCEEDED(hr)) 975 { 976 *ref = result; 977 if (result != DIRENTRY_NULL) 978 memcpy(This->name, result_name, sizeof(result_name)); 979 } 980 981 TRACE("<-- %08x\n", hr); 982 return hr; 983 } 984 985 static HRESULT WINAPI IEnumSTATSTGImpl_Next( 986 IEnumSTATSTG* iface, 987 ULONG celt, 988 STATSTG* rgelt, 989 ULONG* pceltFetched) 990 { 991 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 992 993 DirEntry currentEntry; 994 STATSTG* currentReturnStruct = rgelt; 995 ULONG objectFetched = 0; 996 DirRef currentSearchNode; 997 HRESULT hr=S_OK; 998 999 TRACE("%p,%u,%p,%p\n", iface, celt, rgelt, pceltFetched); 1000 1001 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) ) 1002 return E_INVALIDARG; 1003 1004 if (This->parentStorage->reverted) 1005 { 1006 TRACE("<-- STG_E_REVERTED\n"); 1007 return STG_E_REVERTED; 1008 } 1009 1010 /* 1011 * To avoid the special case, get another pointer to a ULONG value if 1012 * the caller didn't supply one. 1013 */ 1014 if (pceltFetched==0) 1015 pceltFetched = &objectFetched; 1016 1017 /* 1018 * Start the iteration, we will iterate until we hit the end of the 1019 * linked list or until we hit the number of items to iterate through 1020 */ 1021 *pceltFetched = 0; 1022 1023 while ( *pceltFetched < celt ) 1024 { 1025 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); 1026 1027 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) 1028 { 1029 memset(currentReturnStruct, 0, sizeof(*currentReturnStruct)); 1030 break; 1031 } 1032 1033 /* 1034 * Read the entry from the storage. 1035 */ 1036 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, 1037 currentSearchNode, 1038 ¤tEntry); 1039 if (FAILED(hr)) break; 1040 1041 /* 1042 * Copy the information to the return buffer. 1043 */ 1044 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage, 1045 currentReturnStruct, 1046 ¤tEntry, 1047 STATFLAG_DEFAULT); 1048 1049 /* 1050 * Step to the next item in the iteration 1051 */ 1052 (*pceltFetched)++; 1053 currentReturnStruct++; 1054 } 1055 1056 if (SUCCEEDED(hr) && *pceltFetched != celt) 1057 hr = S_FALSE; 1058 1059 TRACE("<-- %08x (asked %u, got %u)\n", hr, celt, *pceltFetched); 1060 return hr; 1061 } 1062 1063 1064 static HRESULT WINAPI IEnumSTATSTGImpl_Skip( 1065 IEnumSTATSTG* iface, 1066 ULONG celt) 1067 { 1068 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 1069 1070 ULONG objectFetched = 0; 1071 DirRef currentSearchNode; 1072 HRESULT hr=S_OK; 1073 1074 TRACE("%p,%u\n", iface, celt); 1075 1076 if (This->parentStorage->reverted) 1077 { 1078 TRACE("<-- STG_E_REVERTED\n"); 1079 return STG_E_REVERTED; 1080 } 1081 1082 while ( (objectFetched < celt) ) 1083 { 1084 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode); 1085 1086 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL) 1087 break; 1088 1089 objectFetched++; 1090 } 1091 1092 if (SUCCEEDED(hr) && objectFetched != celt) 1093 return S_FALSE; 1094 1095 TRACE("<-- %08x\n", hr); 1096 return hr; 1097 } 1098 1099 static HRESULT WINAPI IEnumSTATSTGImpl_Reset( 1100 IEnumSTATSTG* iface) 1101 { 1102 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 1103 1104 TRACE("%p\n", iface); 1105 1106 if (This->parentStorage->reverted) 1107 { 1108 TRACE("<-- STG_E_REVERTED\n"); 1109 return STG_E_REVERTED; 1110 } 1111 1112 This->name[0] = 0; 1113 1114 return S_OK; 1115 } 1116 1117 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef); 1118 1119 static HRESULT WINAPI IEnumSTATSTGImpl_Clone( 1120 IEnumSTATSTG* iface, 1121 IEnumSTATSTG** ppenum) 1122 { 1123 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface); 1124 IEnumSTATSTGImpl* newClone; 1125 1126 TRACE("%p,%p\n", iface, ppenum); 1127 1128 if (This->parentStorage->reverted) 1129 { 1130 TRACE("<-- STG_E_REVERTED\n"); 1131 return STG_E_REVERTED; 1132 } 1133 1134 if (ppenum==0) 1135 return E_INVALIDARG; 1136 1137 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage, 1138 This->storageDirEntry); 1139 if (!newClone) 1140 { 1141 *ppenum = NULL; 1142 return E_OUTOFMEMORY; 1143 } 1144 1145 /* 1146 * The new clone enumeration must point to the same current node as 1147 * the old one. 1148 */ 1149 memcpy(newClone->name, This->name, sizeof(newClone->name)); 1150 1151 *ppenum = &newClone->IEnumSTATSTG_iface; 1152 1153 return S_OK; 1154 } 1155 1156 /* 1157 * Virtual function table for the IEnumSTATSTGImpl class. 1158 */ 1159 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl = 1160 { 1161 IEnumSTATSTGImpl_QueryInterface, 1162 IEnumSTATSTGImpl_AddRef, 1163 IEnumSTATSTGImpl_Release, 1164 IEnumSTATSTGImpl_Next, 1165 IEnumSTATSTGImpl_Skip, 1166 IEnumSTATSTGImpl_Reset, 1167 IEnumSTATSTGImpl_Clone 1168 }; 1169 1170 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct( 1171 StorageBaseImpl* parentStorage, 1172 DirRef storageDirEntry) 1173 { 1174 IEnumSTATSTGImpl* newEnumeration; 1175 1176 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl)); 1177 1178 if (newEnumeration) 1179 { 1180 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl; 1181 newEnumeration->ref = 1; 1182 newEnumeration->name[0] = 0; 1183 1184 /* 1185 * We want to nail-down the reference to the storage in case the 1186 * enumeration out-lives the storage in the client application. 1187 */ 1188 newEnumeration->parentStorage = parentStorage; 1189 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface); 1190 1191 newEnumeration->storageDirEntry = storageDirEntry; 1192 } 1193 1194 return newEnumeration; 1195 } 1196 1197 1198 /************************************************************************ 1199 * StorageBaseImpl implementation 1200 ***********************************************************************/ 1201 1202 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface ) 1203 { 1204 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface); 1205 } 1206 1207 /************************************************************************ 1208 * StorageBaseImpl_QueryInterface (IUnknown) 1209 * 1210 * This method implements the common QueryInterface for all IStorage 1211 * implementations contained in this file. 1212 * 1213 * See Windows documentation for more details on IUnknown methods. 1214 */ 1215 static HRESULT WINAPI StorageBaseImpl_QueryInterface( 1216 IStorage* iface, 1217 REFIID riid, 1218 void** ppvObject) 1219 { 1220 StorageBaseImpl *This = impl_from_IStorage(iface); 1221 1222 TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject); 1223 1224 if (!ppvObject) 1225 return E_INVALIDARG; 1226 1227 *ppvObject = 0; 1228 1229 if (IsEqualGUID(&IID_IUnknown, riid) || 1230 IsEqualGUID(&IID_IStorage, riid)) 1231 { 1232 *ppvObject = &This->IStorage_iface; 1233 } 1234 else if (IsEqualGUID(&IID_IPropertySetStorage, riid)) 1235 { 1236 *ppvObject = &This->IPropertySetStorage_iface; 1237 } 1238 /* locking interface is reported for writer only */ 1239 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer) 1240 { 1241 *ppvObject = &This->IDirectWriterLock_iface; 1242 } 1243 else 1244 { 1245 TRACE("<-- E_NOINTERFACE\n"); 1246 return E_NOINTERFACE; 1247 } 1248 1249 IStorage_AddRef(iface); 1250 TRACE("<-- %p\n", *ppvObject); 1251 return S_OK; 1252 } 1253 1254 /************************************************************************ 1255 * StorageBaseImpl_AddRef (IUnknown) 1256 * 1257 * This method implements the common AddRef for all IStorage 1258 * implementations contained in this file. 1259 * 1260 * See Windows documentation for more details on IUnknown methods. 1261 */ 1262 static ULONG WINAPI StorageBaseImpl_AddRef( 1263 IStorage* iface) 1264 { 1265 StorageBaseImpl *This = impl_from_IStorage(iface); 1266 ULONG ref = InterlockedIncrement(&This->ref); 1267 1268 TRACE("(%p) AddRef to %d\n", This, ref); 1269 1270 return ref; 1271 } 1272 1273 /************************************************************************ 1274 * StorageBaseImpl_Release (IUnknown) 1275 * 1276 * This method implements the common Release for all IStorage 1277 * implementations contained in this file. 1278 * 1279 * See Windows documentation for more details on IUnknown methods. 1280 */ 1281 static ULONG WINAPI StorageBaseImpl_Release( 1282 IStorage* iface) 1283 { 1284 StorageBaseImpl *This = impl_from_IStorage(iface); 1285 1286 ULONG ref = InterlockedDecrement(&This->ref); 1287 1288 TRACE("(%p) ReleaseRef to %d\n", This, ref); 1289 1290 if (ref == 0) 1291 { 1292 /* 1293 * Since we are using a system of base-classes, we want to call the 1294 * destructor of the appropriate derived class. To do this, we are 1295 * using virtual functions to implement the destructor. 1296 */ 1297 StorageBaseImpl_Destroy(This); 1298 } 1299 1300 return ref; 1301 } 1302 1303 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, 1304 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 1305 SNB snbExclude, IStorage *pstgDest); 1306 1307 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This, 1308 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 1309 SNB snbExclude, IStorage *pstgDest) 1310 { 1311 DirEntry data; 1312 HRESULT hr; 1313 BOOL skip = FALSE; 1314 IStorage *pstgTmp; 1315 IStream *pstrChild, *pstrTmp; 1316 STATSTG strStat; 1317 1318 if (srcEntry == DIRENTRY_NULL) 1319 return S_OK; 1320 1321 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); 1322 1323 if (FAILED(hr)) 1324 return hr; 1325 1326 if ( snbExclude ) 1327 { 1328 WCHAR **snb = snbExclude; 1329 1330 while ( *snb != NULL && !skip ) 1331 { 1332 if ( lstrcmpW(data.name, *snb) == 0 ) 1333 skip = TRUE; 1334 ++snb; 1335 } 1336 } 1337 1338 if (!skip) 1339 { 1340 if (data.stgType == STGTY_STORAGE && !skip_storage) 1341 { 1342 /* 1343 * create a new storage in destination storage 1344 */ 1345 hr = IStorage_CreateStorage( pstgDest, data.name, 1346 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 1347 0, 0, 1348 &pstgTmp ); 1349 1350 /* 1351 * if it already exist, don't create a new one use this one 1352 */ 1353 if (hr == STG_E_FILEALREADYEXISTS) 1354 { 1355 hr = IStorage_OpenStorage( pstgDest, data.name, NULL, 1356 STGM_WRITE|STGM_SHARE_EXCLUSIVE, 1357 NULL, 0, &pstgTmp ); 1358 } 1359 1360 if (SUCCEEDED(hr)) 1361 { 1362 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage, 1363 skip_stream, NULL, pstgTmp ); 1364 1365 IStorage_Release(pstgTmp); 1366 } 1367 } 1368 else if (data.stgType == STGTY_STREAM && !skip_stream) 1369 { 1370 /* 1371 * create a new stream in destination storage. If the stream already 1372 * exist, it will be deleted and a new one will be created. 1373 */ 1374 hr = IStorage_CreateStream( pstgDest, data.name, 1375 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 1376 0, 0, &pstrTmp ); 1377 1378 /* 1379 * open child stream storage. This operation must succeed even if the 1380 * stream is already open, so we use internal functions to do it. 1381 */ 1382 if (hr == S_OK) 1383 { 1384 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry); 1385 1386 if (streamimpl) 1387 { 1388 pstrChild = &streamimpl->IStream_iface; 1389 if (pstrChild) 1390 IStream_AddRef(pstrChild); 1391 } 1392 else 1393 { 1394 pstrChild = NULL; 1395 hr = E_OUTOFMEMORY; 1396 } 1397 } 1398 1399 if (hr == S_OK) 1400 { 1401 /* 1402 * Get the size of the source stream 1403 */ 1404 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME ); 1405 1406 /* 1407 * Set the size of the destination stream. 1408 */ 1409 IStream_SetSize(pstrTmp, strStat.cbSize); 1410 1411 /* 1412 * do the copy 1413 */ 1414 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize, 1415 NULL, NULL ); 1416 1417 IStream_Release( pstrChild ); 1418 } 1419 1420 IStream_Release( pstrTmp ); 1421 } 1422 } 1423 1424 /* copy siblings */ 1425 if (SUCCEEDED(hr)) 1426 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage, 1427 skip_stream, snbExclude, pstgDest ); 1428 1429 if (SUCCEEDED(hr)) 1430 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage, 1431 skip_stream, snbExclude, pstgDest ); 1432 1433 TRACE("<-- %08x\n", hr); 1434 return hr; 1435 } 1436 1437 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry) 1438 { 1439 StgStreamImpl *strm; 1440 1441 TRACE("%p,%d\n", stg, streamEntry); 1442 1443 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry) 1444 { 1445 if (strm->dirEntry == streamEntry) 1446 { 1447 return TRUE; 1448 } 1449 } 1450 1451 return FALSE; 1452 } 1453 1454 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry) 1455 { 1456 StorageInternalImpl *childstg; 1457 1458 TRACE("%p,%d\n", stg, storageEntry); 1459 1460 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry) 1461 { 1462 if (childstg->base.storageDirEntry == storageEntry) 1463 { 1464 return TRUE; 1465 } 1466 } 1467 1468 return FALSE; 1469 } 1470 1471 /************************************************************************ 1472 * StorageBaseImpl_OpenStream (IStorage) 1473 * 1474 * This method will open the specified stream object from the current storage. 1475 * 1476 * See Windows documentation for more details on IStorage methods. 1477 */ 1478 static HRESULT WINAPI StorageBaseImpl_OpenStream( 1479 IStorage* iface, 1480 const OLECHAR* pwcsName, /* [string][in] */ 1481 void* reserved1, /* [unique][in] */ 1482 DWORD grfMode, /* [in] */ 1483 DWORD reserved2, /* [in] */ 1484 IStream** ppstm) /* [out] */ 1485 { 1486 StorageBaseImpl *This = impl_from_IStorage(iface); 1487 StgStreamImpl* newStream; 1488 DirEntry currentEntry; 1489 DirRef streamEntryRef; 1490 HRESULT res = STG_E_UNKNOWN; 1491 1492 TRACE("(%p, %s, %p, %x, %d, %p)\n", 1493 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm); 1494 1495 if ( (pwcsName==NULL) || (ppstm==0) ) 1496 { 1497 res = E_INVALIDARG; 1498 goto end; 1499 } 1500 1501 *ppstm = NULL; 1502 1503 if ( FAILED( validateSTGM(grfMode) ) || 1504 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 1505 { 1506 res = STG_E_INVALIDFLAG; 1507 goto end; 1508 } 1509 1510 /* 1511 * As documented. 1512 */ 1513 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) ) 1514 { 1515 res = STG_E_INVALIDFUNCTION; 1516 goto end; 1517 } 1518 1519 if (This->reverted) 1520 { 1521 res = STG_E_REVERTED; 1522 goto end; 1523 } 1524 1525 /* 1526 * Check that we're compatible with the parent's storage mode, but 1527 * only if we are not in transacted mode 1528 */ 1529 if(!(This->openFlags & STGM_TRANSACTED)) { 1530 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 1531 { 1532 res = STG_E_INVALIDFLAG; 1533 goto end; 1534 } 1535 } 1536 1537 /* 1538 * Search for the element with the given name 1539 */ 1540 streamEntryRef = findElement( 1541 This, 1542 This->storageDirEntry, 1543 pwcsName, 1544 ¤tEntry); 1545 1546 /* 1547 * If it was found, construct the stream object and return a pointer to it. 1548 */ 1549 if ( (streamEntryRef!=DIRENTRY_NULL) && 1550 (currentEntry.stgType==STGTY_STREAM) ) 1551 { 1552 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef)) 1553 { 1554 /* A single stream cannot be opened a second time. */ 1555 res = STG_E_ACCESSDENIED; 1556 goto end; 1557 } 1558 1559 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef); 1560 1561 if (newStream) 1562 { 1563 newStream->grfMode = grfMode; 1564 *ppstm = &newStream->IStream_iface; 1565 1566 IStream_AddRef(*ppstm); 1567 1568 res = S_OK; 1569 goto end; 1570 } 1571 1572 res = E_OUTOFMEMORY; 1573 goto end; 1574 } 1575 1576 res = STG_E_FILENOTFOUND; 1577 1578 end: 1579 if (res == S_OK) 1580 TRACE("<-- IStream %p\n", *ppstm); 1581 TRACE("<-- %08x\n", res); 1582 return res; 1583 } 1584 1585 /************************************************************************ 1586 * StorageBaseImpl_OpenStorage (IStorage) 1587 * 1588 * This method will open a new storage object from the current storage. 1589 * 1590 * See Windows documentation for more details on IStorage methods. 1591 */ 1592 static HRESULT WINAPI StorageBaseImpl_OpenStorage( 1593 IStorage* iface, 1594 const OLECHAR* pwcsName, /* [string][unique][in] */ 1595 IStorage* pstgPriority, /* [unique][in] */ 1596 DWORD grfMode, /* [in] */ 1597 SNB snbExclude, /* [unique][in] */ 1598 DWORD reserved, /* [in] */ 1599 IStorage** ppstg) /* [out] */ 1600 { 1601 StorageBaseImpl *This = impl_from_IStorage(iface); 1602 StorageInternalImpl* newStorage; 1603 StorageBaseImpl* newTransactedStorage; 1604 DirEntry currentEntry; 1605 DirRef storageEntryRef; 1606 HRESULT res = STG_E_UNKNOWN; 1607 1608 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n", 1609 iface, debugstr_w(pwcsName), pstgPriority, 1610 grfMode, snbExclude, reserved, ppstg); 1611 1612 if ((pwcsName==NULL) || (ppstg==0) ) 1613 { 1614 res = E_INVALIDARG; 1615 goto end; 1616 } 1617 1618 if (This->openFlags & STGM_SIMPLE) 1619 { 1620 res = STG_E_INVALIDFUNCTION; 1621 goto end; 1622 } 1623 1624 /* as documented */ 1625 if (snbExclude != NULL) 1626 { 1627 res = STG_E_INVALIDPARAMETER; 1628 goto end; 1629 } 1630 1631 if ( FAILED( validateSTGM(grfMode) )) 1632 { 1633 res = STG_E_INVALIDFLAG; 1634 goto end; 1635 } 1636 1637 /* 1638 * As documented. 1639 */ 1640 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE || 1641 (grfMode & STGM_DELETEONRELEASE) || 1642 (grfMode & STGM_PRIORITY) ) 1643 { 1644 res = STG_E_INVALIDFUNCTION; 1645 goto end; 1646 } 1647 1648 if (This->reverted) 1649 return STG_E_REVERTED; 1650 1651 /* 1652 * Check that we're compatible with the parent's storage mode, 1653 * but only if we are not transacted 1654 */ 1655 if(!(This->openFlags & STGM_TRANSACTED)) { 1656 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 1657 { 1658 res = STG_E_ACCESSDENIED; 1659 goto end; 1660 } 1661 } 1662 1663 *ppstg = NULL; 1664 1665 storageEntryRef = findElement( 1666 This, 1667 This->storageDirEntry, 1668 pwcsName, 1669 ¤tEntry); 1670 1671 if ( (storageEntryRef!=DIRENTRY_NULL) && 1672 (currentEntry.stgType==STGTY_STORAGE) ) 1673 { 1674 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef)) 1675 { 1676 /* A single storage cannot be opened a second time. */ 1677 res = STG_E_ACCESSDENIED; 1678 goto end; 1679 } 1680 1681 newStorage = StorageInternalImpl_Construct( 1682 This, 1683 grfMode, 1684 storageEntryRef); 1685 1686 if (newStorage != 0) 1687 { 1688 if (grfMode & STGM_TRANSACTED) 1689 { 1690 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage); 1691 1692 if (FAILED(res)) 1693 { 1694 HeapFree(GetProcessHeap(), 0, newStorage); 1695 goto end; 1696 } 1697 1698 *ppstg = &newTransactedStorage->IStorage_iface; 1699 } 1700 else 1701 { 1702 *ppstg = &newStorage->base.IStorage_iface; 1703 } 1704 1705 list_add_tail(&This->storageHead, &newStorage->ParentListEntry); 1706 1707 res = S_OK; 1708 goto end; 1709 } 1710 1711 res = STG_E_INSUFFICIENTMEMORY; 1712 goto end; 1713 } 1714 1715 res = STG_E_FILENOTFOUND; 1716 1717 end: 1718 TRACE("<-- %08x\n", res); 1719 return res; 1720 } 1721 1722 /************************************************************************ 1723 * StorageBaseImpl_EnumElements (IStorage) 1724 * 1725 * This method will create an enumerator object that can be used to 1726 * retrieve information about all the elements in the storage object. 1727 * 1728 * See Windows documentation for more details on IStorage methods. 1729 */ 1730 static HRESULT WINAPI StorageBaseImpl_EnumElements( 1731 IStorage* iface, 1732 DWORD reserved1, /* [in] */ 1733 void* reserved2, /* [size_is][unique][in] */ 1734 DWORD reserved3, /* [in] */ 1735 IEnumSTATSTG** ppenum) /* [out] */ 1736 { 1737 StorageBaseImpl *This = impl_from_IStorage(iface); 1738 IEnumSTATSTGImpl* newEnum; 1739 1740 TRACE("(%p, %d, %p, %d, %p)\n", 1741 iface, reserved1, reserved2, reserved3, ppenum); 1742 1743 if (!ppenum) 1744 return E_INVALIDARG; 1745 1746 if (This->reverted) 1747 return STG_E_REVERTED; 1748 1749 newEnum = IEnumSTATSTGImpl_Construct( 1750 This, 1751 This->storageDirEntry); 1752 1753 if (newEnum) 1754 { 1755 *ppenum = &newEnum->IEnumSTATSTG_iface; 1756 return S_OK; 1757 } 1758 1759 return E_OUTOFMEMORY; 1760 } 1761 1762 /************************************************************************ 1763 * StorageBaseImpl_Stat (IStorage) 1764 * 1765 * This method will retrieve information about this storage object. 1766 * 1767 * See Windows documentation for more details on IStorage methods. 1768 */ 1769 static HRESULT WINAPI StorageBaseImpl_Stat( 1770 IStorage* iface, 1771 STATSTG* pstatstg, /* [out] */ 1772 DWORD grfStatFlag) /* [in] */ 1773 { 1774 StorageBaseImpl *This = impl_from_IStorage(iface); 1775 DirEntry currentEntry; 1776 HRESULT res = STG_E_UNKNOWN; 1777 1778 TRACE("(%p, %p, %x)\n", 1779 iface, pstatstg, grfStatFlag); 1780 1781 if (!pstatstg) 1782 { 1783 res = E_INVALIDARG; 1784 goto end; 1785 } 1786 1787 if (This->reverted) 1788 { 1789 res = STG_E_REVERTED; 1790 goto end; 1791 } 1792 1793 res = StorageBaseImpl_ReadDirEntry( 1794 This, 1795 This->storageDirEntry, 1796 ¤tEntry); 1797 1798 if (SUCCEEDED(res)) 1799 { 1800 StorageUtl_CopyDirEntryToSTATSTG( 1801 This, 1802 pstatstg, 1803 ¤tEntry, 1804 grfStatFlag); 1805 1806 pstatstg->grfMode = This->openFlags; 1807 pstatstg->grfStateBits = This->stateBits; 1808 } 1809 1810 end: 1811 if (res == S_OK) 1812 { 1813 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits); 1814 } 1815 TRACE("<-- %08x\n", res); 1816 return res; 1817 } 1818 1819 /************************************************************************ 1820 * StorageBaseImpl_RenameElement (IStorage) 1821 * 1822 * This method will rename the specified element. 1823 * 1824 * See Windows documentation for more details on IStorage methods. 1825 */ 1826 static HRESULT WINAPI StorageBaseImpl_RenameElement( 1827 IStorage* iface, 1828 const OLECHAR* pwcsOldName, /* [in] */ 1829 const OLECHAR* pwcsNewName) /* [in] */ 1830 { 1831 StorageBaseImpl *This = impl_from_IStorage(iface); 1832 DirEntry currentEntry; 1833 DirRef currentEntryRef; 1834 1835 TRACE("(%p, %s, %s)\n", 1836 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName)); 1837 1838 if (This->reverted) 1839 return STG_E_REVERTED; 1840 1841 currentEntryRef = findElement(This, 1842 This->storageDirEntry, 1843 pwcsNewName, 1844 ¤tEntry); 1845 1846 if (currentEntryRef != DIRENTRY_NULL) 1847 { 1848 /* 1849 * There is already an element with the new name 1850 */ 1851 return STG_E_FILEALREADYEXISTS; 1852 } 1853 1854 /* 1855 * Search for the old element name 1856 */ 1857 currentEntryRef = findElement(This, 1858 This->storageDirEntry, 1859 pwcsOldName, 1860 ¤tEntry); 1861 1862 if (currentEntryRef != DIRENTRY_NULL) 1863 { 1864 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) || 1865 StorageBaseImpl_IsStorageOpen(This, currentEntryRef)) 1866 { 1867 WARN("Element is already open; cannot rename.\n"); 1868 return STG_E_ACCESSDENIED; 1869 } 1870 1871 /* Remove the element from its current position in the tree */ 1872 removeFromTree(This, This->storageDirEntry, 1873 currentEntryRef); 1874 1875 /* Change the name of the element */ 1876 strcpyW(currentEntry.name, pwcsNewName); 1877 1878 /* Delete any sibling links */ 1879 currentEntry.leftChild = DIRENTRY_NULL; 1880 currentEntry.rightChild = DIRENTRY_NULL; 1881 1882 StorageBaseImpl_WriteDirEntry(This, currentEntryRef, 1883 ¤tEntry); 1884 1885 /* Insert the element in a new position in the tree */ 1886 insertIntoTree(This, This->storageDirEntry, 1887 currentEntryRef); 1888 } 1889 else 1890 { 1891 /* 1892 * There is no element with the old name 1893 */ 1894 return STG_E_FILENOTFOUND; 1895 } 1896 1897 return StorageBaseImpl_Flush(This); 1898 } 1899 1900 /************************************************************************ 1901 * StorageBaseImpl_CreateStream (IStorage) 1902 * 1903 * This method will create a stream object within this storage 1904 * 1905 * See Windows documentation for more details on IStorage methods. 1906 */ 1907 static HRESULT WINAPI StorageBaseImpl_CreateStream( 1908 IStorage* iface, 1909 const OLECHAR* pwcsName, /* [string][in] */ 1910 DWORD grfMode, /* [in] */ 1911 DWORD reserved1, /* [in] */ 1912 DWORD reserved2, /* [in] */ 1913 IStream** ppstm) /* [out] */ 1914 { 1915 StorageBaseImpl *This = impl_from_IStorage(iface); 1916 StgStreamImpl* newStream; 1917 DirEntry currentEntry, newStreamEntry; 1918 DirRef currentEntryRef, newStreamEntryRef; 1919 HRESULT hr; 1920 1921 TRACE("(%p, %s, %x, %d, %d, %p)\n", 1922 iface, debugstr_w(pwcsName), grfMode, 1923 reserved1, reserved2, ppstm); 1924 1925 if (ppstm == 0) 1926 return STG_E_INVALIDPOINTER; 1927 1928 if (pwcsName == 0) 1929 return STG_E_INVALIDNAME; 1930 1931 if (reserved1 || reserved2) 1932 return STG_E_INVALIDPARAMETER; 1933 1934 if ( FAILED( validateSTGM(grfMode) )) 1935 return STG_E_INVALIDFLAG; 1936 1937 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 1938 return STG_E_INVALIDFLAG; 1939 1940 if (This->reverted) 1941 return STG_E_REVERTED; 1942 1943 /* 1944 * As documented. 1945 */ 1946 if ((grfMode & STGM_DELETEONRELEASE) || 1947 (grfMode & STGM_TRANSACTED)) 1948 return STG_E_INVALIDFUNCTION; 1949 1950 /* 1951 * Don't worry about permissions in transacted mode, as we can always write 1952 * changes; we just can't always commit them. 1953 */ 1954 if(!(This->openFlags & STGM_TRANSACTED)) { 1955 /* Can't create a stream on read-only storage */ 1956 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) 1957 return STG_E_ACCESSDENIED; 1958 1959 /* Can't create a stream with greater access than the parent. */ 1960 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 1961 return STG_E_ACCESSDENIED; 1962 } 1963 1964 if(This->openFlags & STGM_SIMPLE) 1965 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG; 1966 1967 *ppstm = 0; 1968 1969 currentEntryRef = findElement(This, 1970 This->storageDirEntry, 1971 pwcsName, 1972 ¤tEntry); 1973 1974 if (currentEntryRef != DIRENTRY_NULL) 1975 { 1976 /* 1977 * An element with this name already exists 1978 */ 1979 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE) 1980 { 1981 IStorage_DestroyElement(iface, pwcsName); 1982 } 1983 else 1984 return STG_E_FILEALREADYEXISTS; 1985 } 1986 1987 /* 1988 * memset the empty entry 1989 */ 1990 memset(&newStreamEntry, 0, sizeof(DirEntry)); 1991 1992 newStreamEntry.sizeOfNameString = 1993 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR); 1994 1995 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) 1996 return STG_E_INVALIDNAME; 1997 1998 strcpyW(newStreamEntry.name, pwcsName); 1999 2000 newStreamEntry.stgType = STGTY_STREAM; 2001 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN; 2002 newStreamEntry.size.u.LowPart = 0; 2003 newStreamEntry.size.u.HighPart = 0; 2004 2005 newStreamEntry.leftChild = DIRENTRY_NULL; 2006 newStreamEntry.rightChild = DIRENTRY_NULL; 2007 newStreamEntry.dirRootEntry = DIRENTRY_NULL; 2008 2009 /* call CoFileTime to get the current time 2010 newStreamEntry.ctime 2011 newStreamEntry.mtime 2012 */ 2013 2014 /* newStreamEntry.clsid */ 2015 2016 /* 2017 * Create an entry with the new data 2018 */ 2019 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef); 2020 if (FAILED(hr)) 2021 return hr; 2022 2023 /* 2024 * Insert the new entry in the parent storage's tree. 2025 */ 2026 hr = insertIntoTree( 2027 This, 2028 This->storageDirEntry, 2029 newStreamEntryRef); 2030 if (FAILED(hr)) 2031 { 2032 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef); 2033 return hr; 2034 } 2035 2036 /* 2037 * Open the stream to return it. 2038 */ 2039 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef); 2040 2041 if (newStream) 2042 { 2043 *ppstm = &newStream->IStream_iface; 2044 IStream_AddRef(*ppstm); 2045 } 2046 else 2047 { 2048 return STG_E_INSUFFICIENTMEMORY; 2049 } 2050 2051 return StorageBaseImpl_Flush(This); 2052 } 2053 2054 /************************************************************************ 2055 * StorageBaseImpl_SetClass (IStorage) 2056 * 2057 * This method will write the specified CLSID in the directory entry of this 2058 * storage. 2059 * 2060 * See Windows documentation for more details on IStorage methods. 2061 */ 2062 static HRESULT WINAPI StorageBaseImpl_SetClass( 2063 IStorage* iface, 2064 REFCLSID clsid) /* [in] */ 2065 { 2066 StorageBaseImpl *This = impl_from_IStorage(iface); 2067 HRESULT hRes; 2068 DirEntry currentEntry; 2069 2070 TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid)); 2071 2072 if (This->reverted) 2073 return STG_E_REVERTED; 2074 2075 hRes = StorageBaseImpl_ReadDirEntry(This, 2076 This->storageDirEntry, 2077 ¤tEntry); 2078 if (SUCCEEDED(hRes)) 2079 { 2080 currentEntry.clsid = *clsid; 2081 2082 hRes = StorageBaseImpl_WriteDirEntry(This, 2083 This->storageDirEntry, 2084 ¤tEntry); 2085 } 2086 2087 if (SUCCEEDED(hRes)) 2088 hRes = StorageBaseImpl_Flush(This); 2089 2090 return hRes; 2091 } 2092 2093 /************************************************************************ 2094 * StorageBaseImpl_CreateStorage (IStorage) 2095 * 2096 * This method will create the storage object within the provided storage. 2097 * 2098 * See Windows documentation for more details on IStorage methods. 2099 */ 2100 static HRESULT WINAPI StorageBaseImpl_CreateStorage( 2101 IStorage* iface, 2102 const OLECHAR *pwcsName, /* [string][in] */ 2103 DWORD grfMode, /* [in] */ 2104 DWORD reserved1, /* [in] */ 2105 DWORD reserved2, /* [in] */ 2106 IStorage **ppstg) /* [out] */ 2107 { 2108 StorageBaseImpl* This = impl_from_IStorage(iface); 2109 2110 DirEntry currentEntry; 2111 DirEntry newEntry; 2112 DirRef currentEntryRef; 2113 DirRef newEntryRef; 2114 HRESULT hr; 2115 2116 TRACE("(%p, %s, %x, %d, %d, %p)\n", 2117 iface, debugstr_w(pwcsName), grfMode, 2118 reserved1, reserved2, ppstg); 2119 2120 if (ppstg == 0) 2121 return STG_E_INVALIDPOINTER; 2122 2123 if (This->openFlags & STGM_SIMPLE) 2124 { 2125 return STG_E_INVALIDFUNCTION; 2126 } 2127 2128 if (pwcsName == 0) 2129 return STG_E_INVALIDNAME; 2130 2131 *ppstg = NULL; 2132 2133 if ( FAILED( validateSTGM(grfMode) ) || 2134 (grfMode & STGM_DELETEONRELEASE) ) 2135 { 2136 WARN("bad grfMode: 0x%x\n", grfMode); 2137 return STG_E_INVALIDFLAG; 2138 } 2139 2140 if (This->reverted) 2141 return STG_E_REVERTED; 2142 2143 /* 2144 * Check that we're compatible with the parent's storage mode 2145 */ 2146 if ( !(This->openFlags & STGM_TRANSACTED) && 2147 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) ) 2148 { 2149 WARN("access denied\n"); 2150 return STG_E_ACCESSDENIED; 2151 } 2152 2153 currentEntryRef = findElement(This, 2154 This->storageDirEntry, 2155 pwcsName, 2156 ¤tEntry); 2157 2158 if (currentEntryRef != DIRENTRY_NULL) 2159 { 2160 /* 2161 * An element with this name already exists 2162 */ 2163 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE && 2164 ((This->openFlags & STGM_TRANSACTED) || 2165 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)) 2166 { 2167 hr = IStorage_DestroyElement(iface, pwcsName); 2168 if (FAILED(hr)) 2169 return hr; 2170 } 2171 else 2172 { 2173 WARN("file already exists\n"); 2174 return STG_E_FILEALREADYEXISTS; 2175 } 2176 } 2177 else if (!(This->openFlags & STGM_TRANSACTED) && 2178 STGM_ACCESS_MODE(This->openFlags) == STGM_READ) 2179 { 2180 WARN("read-only storage\n"); 2181 return STG_E_ACCESSDENIED; 2182 } 2183 2184 memset(&newEntry, 0, sizeof(DirEntry)); 2185 2186 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR); 2187 2188 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN) 2189 { 2190 FIXME("name too long\n"); 2191 return STG_E_INVALIDNAME; 2192 } 2193 2194 strcpyW(newEntry.name, pwcsName); 2195 2196 newEntry.stgType = STGTY_STORAGE; 2197 newEntry.startingBlock = BLOCK_END_OF_CHAIN; 2198 newEntry.size.u.LowPart = 0; 2199 newEntry.size.u.HighPart = 0; 2200 2201 newEntry.leftChild = DIRENTRY_NULL; 2202 newEntry.rightChild = DIRENTRY_NULL; 2203 newEntry.dirRootEntry = DIRENTRY_NULL; 2204 2205 /* call CoFileTime to get the current time 2206 newEntry.ctime 2207 newEntry.mtime 2208 */ 2209 2210 /* newEntry.clsid */ 2211 2212 /* 2213 * Create a new directory entry for the storage 2214 */ 2215 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef); 2216 if (FAILED(hr)) 2217 return hr; 2218 2219 /* 2220 * Insert the new directory entry into the parent storage's tree 2221 */ 2222 hr = insertIntoTree( 2223 This, 2224 This->storageDirEntry, 2225 newEntryRef); 2226 if (FAILED(hr)) 2227 { 2228 StorageBaseImpl_DestroyDirEntry(This, newEntryRef); 2229 return hr; 2230 } 2231 2232 /* 2233 * Open it to get a pointer to return. 2234 */ 2235 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg); 2236 2237 if( (hr != S_OK) || (*ppstg == NULL)) 2238 { 2239 return hr; 2240 } 2241 2242 if (SUCCEEDED(hr)) 2243 hr = StorageBaseImpl_Flush(This); 2244 2245 return S_OK; 2246 } 2247 2248 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This, 2249 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream, 2250 SNB snbExclude, IStorage *pstgDest) 2251 { 2252 DirEntry data; 2253 HRESULT hr; 2254 2255 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data ); 2256 2257 if (SUCCEEDED(hr)) 2258 hr = IStorage_SetClass( pstgDest, &data.clsid ); 2259 2260 if (SUCCEEDED(hr)) 2261 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage, 2262 skip_stream, snbExclude, pstgDest ); 2263 2264 TRACE("<-- %08x\n", hr); 2265 return hr; 2266 } 2267 2268 /************************************************************************* 2269 * CopyTo (IStorage) 2270 */ 2271 static HRESULT WINAPI StorageBaseImpl_CopyTo( 2272 IStorage* iface, 2273 DWORD ciidExclude, /* [in] */ 2274 const IID* rgiidExclude, /* [size_is][unique][in] */ 2275 SNB snbExclude, /* [unique][in] */ 2276 IStorage* pstgDest) /* [unique][in] */ 2277 { 2278 StorageBaseImpl *This = impl_from_IStorage(iface); 2279 2280 BOOL skip_storage = FALSE, skip_stream = FALSE; 2281 DWORD i; 2282 2283 TRACE("(%p, %d, %p, %p, %p)\n", 2284 iface, ciidExclude, rgiidExclude, 2285 snbExclude, pstgDest); 2286 2287 if ( pstgDest == 0 ) 2288 return STG_E_INVALIDPOINTER; 2289 2290 for(i = 0; i < ciidExclude; ++i) 2291 { 2292 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i])) 2293 skip_storage = TRUE; 2294 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i])) 2295 skip_stream = TRUE; 2296 else 2297 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i])); 2298 } 2299 2300 if (!skip_storage) 2301 { 2302 /* Give up early if it looks like this would be infinitely recursive. 2303 * Oddly enough, this includes some cases that aren't really recursive, like 2304 * copying to a transacted child. */ 2305 IStorage *pstgDestAncestor = pstgDest; 2306 IStorage *pstgDestAncestorChild = NULL; 2307 2308 /* Go up the chain from the destination until we find the source storage. */ 2309 while (pstgDestAncestor != iface) { 2310 pstgDestAncestorChild = pstgDest; 2311 2312 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl) 2313 { 2314 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor; 2315 2316 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface; 2317 } 2318 else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl) 2319 { 2320 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor; 2321 2322 pstgDestAncestor = &internal->parentStorage->IStorage_iface; 2323 } 2324 else 2325 break; 2326 } 2327 2328 if (pstgDestAncestor == iface) 2329 { 2330 BOOL fail = TRUE; 2331 2332 if (pstgDestAncestorChild && snbExclude) 2333 { 2334 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild; 2335 DirEntry data; 2336 WCHAR **snb = snbExclude; 2337 2338 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data); 2339 2340 while ( *snb != NULL && fail ) 2341 { 2342 if ( lstrcmpW(data.name, *snb) == 0 ) 2343 fail = FALSE; 2344 ++snb; 2345 } 2346 } 2347 2348 if (fail) 2349 return STG_E_ACCESSDENIED; 2350 } 2351 } 2352 2353 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry, 2354 skip_storage, skip_stream, snbExclude, pstgDest ); 2355 } 2356 2357 /************************************************************************* 2358 * MoveElementTo (IStorage) 2359 */ 2360 static HRESULT WINAPI StorageBaseImpl_MoveElementTo( 2361 IStorage* iface, 2362 const OLECHAR *pwcsName, /* [string][in] */ 2363 IStorage *pstgDest, /* [unique][in] */ 2364 const OLECHAR *pwcsNewName,/* [string][in] */ 2365 DWORD grfFlags) /* [in] */ 2366 { 2367 FIXME("(%p %s %p %s %u): stub\n", iface, 2368 debugstr_w(pwcsName), pstgDest, 2369 debugstr_w(pwcsNewName), grfFlags); 2370 return E_NOTIMPL; 2371 } 2372 2373 /************************************************************************* 2374 * Commit (IStorage) 2375 * 2376 * Ensures that any changes made to a storage object open in transacted mode 2377 * are reflected in the parent storage 2378 * 2379 * In a non-transacted mode, this ensures all cached writes are completed. 2380 */ 2381 static HRESULT WINAPI StorageBaseImpl_Commit( 2382 IStorage* iface, 2383 DWORD grfCommitFlags)/* [in] */ 2384 { 2385 StorageBaseImpl* This = impl_from_IStorage(iface); 2386 TRACE("(%p %d)\n", iface, grfCommitFlags); 2387 return StorageBaseImpl_Flush(This); 2388 } 2389 2390 /************************************************************************* 2391 * Revert (IStorage) 2392 * 2393 * Discard all changes that have been made since the last commit operation 2394 */ 2395 static HRESULT WINAPI StorageBaseImpl_Revert( 2396 IStorage* iface) 2397 { 2398 TRACE("(%p)\n", iface); 2399 return S_OK; 2400 } 2401 2402 /********************************************************************* 2403 * 2404 * Internal helper function for StorageBaseImpl_DestroyElement() 2405 * 2406 * Delete the contents of a storage entry. 2407 * 2408 */ 2409 static HRESULT deleteStorageContents( 2410 StorageBaseImpl *parentStorage, 2411 DirRef indexToDelete, 2412 DirEntry entryDataToDelete) 2413 { 2414 IEnumSTATSTG *elements = 0; 2415 IStorage *childStorage = 0; 2416 STATSTG currentElement; 2417 HRESULT hr; 2418 HRESULT destroyHr = S_OK; 2419 StorageInternalImpl *stg, *stg2; 2420 2421 TRACE("%p,%d\n", parentStorage, indexToDelete); 2422 2423 /* Invalidate any open storage objects. */ 2424 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry) 2425 { 2426 if (stg->base.storageDirEntry == indexToDelete) 2427 { 2428 StorageBaseImpl_Invalidate(&stg->base); 2429 } 2430 } 2431 2432 /* 2433 * Open the storage and enumerate it 2434 */ 2435 hr = IStorage_OpenStorage( 2436 &parentStorage->IStorage_iface, 2437 entryDataToDelete.name, 2438 0, 2439 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 2440 0, 2441 0, 2442 &childStorage); 2443 2444 if (hr != S_OK) 2445 { 2446 TRACE("<-- %08x\n", hr); 2447 return hr; 2448 } 2449 2450 /* 2451 * Enumerate the elements 2452 */ 2453 hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements); 2454 if (FAILED(hr)) 2455 { 2456 IStorage_Release(childStorage); 2457 TRACE("<-- %08x\n", hr); 2458 return hr; 2459 } 2460 2461 do 2462 { 2463 /* 2464 * Obtain the next element 2465 */ 2466 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL); 2467 if (hr==S_OK) 2468 { 2469 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName); 2470 2471 CoTaskMemFree(currentElement.pwcsName); 2472 } 2473 2474 /* 2475 * We need to Reset the enumeration every time because we delete elements 2476 * and the enumeration could be invalid 2477 */ 2478 IEnumSTATSTG_Reset(elements); 2479 2480 } while ((hr == S_OK) && (destroyHr == S_OK)); 2481 2482 IStorage_Release(childStorage); 2483 IEnumSTATSTG_Release(elements); 2484 2485 TRACE("%08x\n", hr); 2486 return destroyHr; 2487 } 2488 2489 /********************************************************************* 2490 * 2491 * Internal helper function for StorageBaseImpl_DestroyElement() 2492 * 2493 * Perform the deletion of a stream's data 2494 * 2495 */ 2496 static HRESULT deleteStreamContents( 2497 StorageBaseImpl *parentStorage, 2498 DirRef indexToDelete, 2499 DirEntry entryDataToDelete) 2500 { 2501 IStream *pis; 2502 HRESULT hr; 2503 ULARGE_INTEGER size; 2504 StgStreamImpl *strm, *strm2; 2505 2506 /* Invalidate any open stream objects. */ 2507 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry) 2508 { 2509 if (strm->dirEntry == indexToDelete) 2510 { 2511 TRACE("Stream deleted %p\n", strm); 2512 strm->parentStorage = NULL; 2513 list_remove(&strm->StrmListEntry); 2514 } 2515 } 2516 2517 size.u.HighPart = 0; 2518 size.u.LowPart = 0; 2519 2520 hr = IStorage_OpenStream(&parentStorage->IStorage_iface, 2521 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis); 2522 2523 if (hr!=S_OK) 2524 { 2525 TRACE("<-- %08x\n", hr); 2526 return(hr); 2527 } 2528 2529 /* 2530 * Zap the stream 2531 */ 2532 hr = IStream_SetSize(pis, size); 2533 2534 if(hr != S_OK) 2535 { 2536 TRACE("<-- %08x\n", hr); 2537 return hr; 2538 } 2539 2540 /* 2541 * Release the stream object. 2542 */ 2543 IStream_Release(pis); 2544 TRACE("<-- %08x\n", hr); 2545 return S_OK; 2546 } 2547 2548 /************************************************************************* 2549 * DestroyElement (IStorage) 2550 * 2551 * Strategy: This implementation is built this way for simplicity not for speed. 2552 * I always delete the topmost element of the enumeration and adjust 2553 * the deleted element pointer all the time. This takes longer to 2554 * do but allows reinvoking DestroyElement whenever we encounter a 2555 * storage object. The optimisation resides in the usage of another 2556 * enumeration strategy that would give all the leaves of a storage 2557 * first. (postfix order) 2558 */ 2559 static HRESULT WINAPI StorageBaseImpl_DestroyElement( 2560 IStorage* iface, 2561 const OLECHAR *pwcsName)/* [string][in] */ 2562 { 2563 StorageBaseImpl *This = impl_from_IStorage(iface); 2564 2565 HRESULT hr = S_OK; 2566 DirEntry entryToDelete; 2567 DirRef entryToDeleteRef; 2568 2569 TRACE("(%p, %s)\n", 2570 iface, debugstr_w(pwcsName)); 2571 2572 if (pwcsName==NULL) 2573 return STG_E_INVALIDPOINTER; 2574 2575 if (This->reverted) 2576 return STG_E_REVERTED; 2577 2578 if ( !(This->openFlags & STGM_TRANSACTED) && 2579 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ ) 2580 return STG_E_ACCESSDENIED; 2581 2582 entryToDeleteRef = findElement( 2583 This, 2584 This->storageDirEntry, 2585 pwcsName, 2586 &entryToDelete); 2587 2588 if ( entryToDeleteRef == DIRENTRY_NULL ) 2589 { 2590 TRACE("<-- STG_E_FILENOTFOUND\n"); 2591 return STG_E_FILENOTFOUND; 2592 } 2593 2594 if ( entryToDelete.stgType == STGTY_STORAGE ) 2595 { 2596 hr = deleteStorageContents( 2597 This, 2598 entryToDeleteRef, 2599 entryToDelete); 2600 } 2601 else if ( entryToDelete.stgType == STGTY_STREAM ) 2602 { 2603 hr = deleteStreamContents( 2604 This, 2605 entryToDeleteRef, 2606 entryToDelete); 2607 } 2608 2609 if (hr!=S_OK) 2610 { 2611 TRACE("<-- %08x\n", hr); 2612 return hr; 2613 } 2614 2615 /* 2616 * Remove the entry from its parent storage 2617 */ 2618 hr = removeFromTree( 2619 This, 2620 This->storageDirEntry, 2621 entryToDeleteRef); 2622 2623 /* 2624 * Invalidate the entry 2625 */ 2626 if (SUCCEEDED(hr)) 2627 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef); 2628 2629 if (SUCCEEDED(hr)) 2630 hr = StorageBaseImpl_Flush(This); 2631 2632 TRACE("<-- %08x\n", hr); 2633 return hr; 2634 } 2635 2636 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg) 2637 { 2638 struct list *cur, *cur2; 2639 StgStreamImpl *strm=NULL; 2640 StorageInternalImpl *childstg=NULL; 2641 2642 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) { 2643 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry); 2644 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev); 2645 strm->parentStorage = NULL; 2646 list_remove(cur); 2647 } 2648 2649 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) { 2650 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry); 2651 StorageBaseImpl_Invalidate( &childstg->base ); 2652 } 2653 2654 if (stg->transactedChild) 2655 { 2656 StorageBaseImpl_Invalidate(stg->transactedChild); 2657 2658 stg->transactedChild = NULL; 2659 } 2660 } 2661 2662 /****************************************************************************** 2663 * SetElementTimes (IStorage) 2664 */ 2665 static HRESULT WINAPI StorageBaseImpl_SetElementTimes( 2666 IStorage* iface, 2667 const OLECHAR *pwcsName,/* [string][in] */ 2668 const FILETIME *pctime, /* [in] */ 2669 const FILETIME *patime, /* [in] */ 2670 const FILETIME *pmtime) /* [in] */ 2671 { 2672 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName)); 2673 return S_OK; 2674 } 2675 2676 /****************************************************************************** 2677 * SetStateBits (IStorage) 2678 */ 2679 static HRESULT WINAPI StorageBaseImpl_SetStateBits( 2680 IStorage* iface, 2681 DWORD grfStateBits,/* [in] */ 2682 DWORD grfMask) /* [in] */ 2683 { 2684 StorageBaseImpl *This = impl_from_IStorage(iface); 2685 2686 if (This->reverted) 2687 return STG_E_REVERTED; 2688 2689 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask); 2690 return S_OK; 2691 } 2692 2693 /****************************************************************************** 2694 * Internal stream list handlers 2695 */ 2696 2697 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) 2698 { 2699 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm); 2700 list_add_tail(&stg->strmHead,&strm->StrmListEntry); 2701 } 2702 2703 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) 2704 { 2705 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm); 2706 list_remove(&(strm->StrmListEntry)); 2707 } 2708 2709 static HRESULT StorageBaseImpl_CopyStream( 2710 StorageBaseImpl *dst, DirRef dst_entry, 2711 StorageBaseImpl *src, DirRef src_entry) 2712 { 2713 HRESULT hr; 2714 BYTE data[4096]; 2715 DirEntry srcdata; 2716 ULARGE_INTEGER bytes_copied; 2717 ULONG bytestocopy, bytesread, byteswritten; 2718 2719 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata); 2720 2721 if (SUCCEEDED(hr)) 2722 { 2723 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size); 2724 2725 bytes_copied.QuadPart = 0; 2726 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr)) 2727 { 2728 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart); 2729 2730 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy, 2731 data, &bytesread); 2732 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT; 2733 2734 if (SUCCEEDED(hr)) 2735 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy, 2736 data, &byteswritten); 2737 if (SUCCEEDED(hr)) 2738 { 2739 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT; 2740 bytes_copied.QuadPart += byteswritten; 2741 } 2742 } 2743 } 2744 2745 return hr; 2746 } 2747 2748 static HRESULT StorageBaseImpl_DupStorageTree( 2749 StorageBaseImpl *dst, DirRef *dst_entry, 2750 StorageBaseImpl *src, DirRef src_entry) 2751 { 2752 HRESULT hr; 2753 DirEntry data; 2754 BOOL has_stream=FALSE; 2755 2756 if (src_entry == DIRENTRY_NULL) 2757 { 2758 *dst_entry = DIRENTRY_NULL; 2759 return S_OK; 2760 } 2761 2762 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data); 2763 if (SUCCEEDED(hr)) 2764 { 2765 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0); 2766 data.startingBlock = BLOCK_END_OF_CHAIN; 2767 data.size.QuadPart = 0; 2768 2769 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild); 2770 } 2771 2772 if (SUCCEEDED(hr)) 2773 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild); 2774 2775 if (SUCCEEDED(hr)) 2776 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry); 2777 2778 if (SUCCEEDED(hr)) 2779 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry); 2780 2781 if (SUCCEEDED(hr) && has_stream) 2782 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry); 2783 2784 return hr; 2785 } 2786 2787 static HRESULT StorageBaseImpl_CopyStorageTree( 2788 StorageBaseImpl *dst, DirRef dst_entry, 2789 StorageBaseImpl *src, DirRef src_entry) 2790 { 2791 HRESULT hr; 2792 DirEntry src_data, dst_data; 2793 DirRef new_root_entry; 2794 2795 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data); 2796 2797 if (SUCCEEDED(hr)) 2798 { 2799 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry); 2800 } 2801 2802 if (SUCCEEDED(hr)) 2803 { 2804 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data); 2805 dst_data.clsid = src_data.clsid; 2806 dst_data.ctime = src_data.ctime; 2807 dst_data.mtime = src_data.mtime; 2808 dst_data.dirRootEntry = new_root_entry; 2809 } 2810 2811 if (SUCCEEDED(hr)) 2812 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data); 2813 2814 return hr; 2815 } 2816 2817 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings) 2818 { 2819 HRESULT hr; 2820 DirEntry data; 2821 ULARGE_INTEGER zero; 2822 2823 if (entry == DIRENTRY_NULL) 2824 return S_OK; 2825 2826 zero.QuadPart = 0; 2827 2828 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data); 2829 2830 if (SUCCEEDED(hr) && include_siblings) 2831 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE); 2832 2833 if (SUCCEEDED(hr) && include_siblings) 2834 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE); 2835 2836 if (SUCCEEDED(hr)) 2837 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE); 2838 2839 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM) 2840 hr = StorageBaseImpl_StreamSetSize(This, entry, zero); 2841 2842 if (SUCCEEDED(hr)) 2843 hr = StorageBaseImpl_DestroyDirEntry(This, entry); 2844 2845 return hr; 2846 } 2847 2848 2849 /************************************************************************ 2850 * StorageImpl implementation 2851 ***********************************************************************/ 2852 2853 static HRESULT StorageImpl_ReadAt(StorageImpl* This, 2854 ULARGE_INTEGER offset, 2855 void* buffer, 2856 ULONG size, 2857 ULONG* bytesRead) 2858 { 2859 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead); 2860 } 2861 2862 static HRESULT StorageImpl_WriteAt(StorageImpl* This, 2863 ULARGE_INTEGER offset, 2864 const void* buffer, 2865 const ULONG size, 2866 ULONG* bytesWritten) 2867 { 2868 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten); 2869 } 2870 2871 /****************************************************************************** 2872 * StorageImpl_LoadFileHeader 2873 * 2874 * This method will read in the file header 2875 */ 2876 static HRESULT StorageImpl_LoadFileHeader( 2877 StorageImpl* This) 2878 { 2879 HRESULT hr; 2880 BYTE headerBigBlock[HEADER_SIZE]; 2881 int index; 2882 ULARGE_INTEGER offset; 2883 DWORD bytes_read; 2884 2885 TRACE("\n"); 2886 /* 2887 * Get a pointer to the big block of data containing the header. 2888 */ 2889 offset.u.HighPart = 0; 2890 offset.u.LowPart = 0; 2891 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); 2892 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) 2893 hr = STG_E_FILENOTFOUND; 2894 2895 /* 2896 * Extract the information from the header. 2897 */ 2898 if (SUCCEEDED(hr)) 2899 { 2900 /* 2901 * Check for the "magic number" signature and return an error if it is not 2902 * found. 2903 */ 2904 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0) 2905 { 2906 return STG_E_OLDFORMAT; 2907 } 2908 2909 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0) 2910 { 2911 return STG_E_INVALIDHEADER; 2912 } 2913 2914 StorageUtl_ReadWord( 2915 headerBigBlock, 2916 OFFSET_BIGBLOCKSIZEBITS, 2917 &This->bigBlockSizeBits); 2918 2919 StorageUtl_ReadWord( 2920 headerBigBlock, 2921 OFFSET_SMALLBLOCKSIZEBITS, 2922 &This->smallBlockSizeBits); 2923 2924 StorageUtl_ReadDWord( 2925 headerBigBlock, 2926 OFFSET_BBDEPOTCOUNT, 2927 &This->bigBlockDepotCount); 2928 2929 StorageUtl_ReadDWord( 2930 headerBigBlock, 2931 OFFSET_ROOTSTARTBLOCK, 2932 &This->rootStartBlock); 2933 2934 StorageUtl_ReadDWord( 2935 headerBigBlock, 2936 OFFSET_TRANSACTIONSIG, 2937 &This->transactionSig); 2938 2939 StorageUtl_ReadDWord( 2940 headerBigBlock, 2941 OFFSET_SMALLBLOCKLIMIT, 2942 &This->smallBlockLimit); 2943 2944 StorageUtl_ReadDWord( 2945 headerBigBlock, 2946 OFFSET_SBDEPOTSTART, 2947 &This->smallBlockDepotStart); 2948 2949 StorageUtl_ReadDWord( 2950 headerBigBlock, 2951 OFFSET_EXTBBDEPOTSTART, 2952 &This->extBigBlockDepotStart); 2953 2954 StorageUtl_ReadDWord( 2955 headerBigBlock, 2956 OFFSET_EXTBBDEPOTCOUNT, 2957 &This->extBigBlockDepotCount); 2958 2959 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) 2960 { 2961 StorageUtl_ReadDWord( 2962 headerBigBlock, 2963 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), 2964 &(This->bigBlockDepotStart[index])); 2965 } 2966 2967 /* 2968 * Make the bitwise arithmetic to get the size of the blocks in bytes. 2969 */ 2970 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits; 2971 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits; 2972 2973 /* 2974 * Right now, the code is making some assumptions about the size of the 2975 * blocks, just make sure they are what we're expecting. 2976 */ 2977 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) || 2978 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE || 2979 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK) 2980 { 2981 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n", 2982 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit); 2983 hr = STG_E_INVALIDHEADER; 2984 } 2985 else 2986 hr = S_OK; 2987 } 2988 2989 return hr; 2990 } 2991 2992 /****************************************************************************** 2993 * StorageImpl_SaveFileHeader 2994 * 2995 * This method will save to the file the header 2996 */ 2997 static void StorageImpl_SaveFileHeader( 2998 StorageImpl* This) 2999 { 3000 BYTE headerBigBlock[HEADER_SIZE]; 3001 int index; 3002 HRESULT hr; 3003 ULARGE_INTEGER offset; 3004 DWORD bytes_read, bytes_written; 3005 DWORD major_version, dirsectorcount; 3006 3007 /* 3008 * Get a pointer to the big block of data containing the header. 3009 */ 3010 offset.u.HighPart = 0; 3011 offset.u.LowPart = 0; 3012 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read); 3013 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE) 3014 hr = STG_E_FILENOTFOUND; 3015 3016 if (This->bigBlockSizeBits == 0x9) 3017 major_version = 3; 3018 else if (This->bigBlockSizeBits == 0xc) 3019 major_version = 4; 3020 else 3021 { 3022 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits); 3023 major_version = 4; 3024 } 3025 3026 /* 3027 * If the block read failed, the file is probably new. 3028 */ 3029 if (FAILED(hr)) 3030 { 3031 /* 3032 * Initialize for all unknown fields. 3033 */ 3034 memset(headerBigBlock, 0, HEADER_SIZE); 3035 3036 /* 3037 * Initialize the magic number. 3038 */ 3039 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic)); 3040 } 3041 3042 /* 3043 * Write the information to the header. 3044 */ 3045 StorageUtl_WriteWord( 3046 headerBigBlock, 3047 OFFSET_MINORVERSION, 3048 0x3e); 3049 3050 StorageUtl_WriteWord( 3051 headerBigBlock, 3052 OFFSET_MAJORVERSION, 3053 major_version); 3054 3055 StorageUtl_WriteWord( 3056 headerBigBlock, 3057 OFFSET_BYTEORDERMARKER, 3058 (WORD)-2); 3059 3060 StorageUtl_WriteWord( 3061 headerBigBlock, 3062 OFFSET_BIGBLOCKSIZEBITS, 3063 This->bigBlockSizeBits); 3064 3065 StorageUtl_WriteWord( 3066 headerBigBlock, 3067 OFFSET_SMALLBLOCKSIZEBITS, 3068 This->smallBlockSizeBits); 3069 3070 if (major_version >= 4) 3071 { 3072 if (This->rootBlockChain) 3073 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain); 3074 else 3075 /* This file is being created, and it will start out with one block. */ 3076 dirsectorcount = 1; 3077 } 3078 else 3079 /* This field must be 0 in versions older than 4 */ 3080 dirsectorcount = 0; 3081 3082 StorageUtl_WriteDWord( 3083 headerBigBlock, 3084 OFFSET_DIRSECTORCOUNT, 3085 dirsectorcount); 3086 3087 StorageUtl_WriteDWord( 3088 headerBigBlock, 3089 OFFSET_BBDEPOTCOUNT, 3090 This->bigBlockDepotCount); 3091 3092 StorageUtl_WriteDWord( 3093 headerBigBlock, 3094 OFFSET_ROOTSTARTBLOCK, 3095 This->rootStartBlock); 3096 3097 StorageUtl_WriteDWord( 3098 headerBigBlock, 3099 OFFSET_TRANSACTIONSIG, 3100 This->transactionSig); 3101 3102 StorageUtl_WriteDWord( 3103 headerBigBlock, 3104 OFFSET_SMALLBLOCKLIMIT, 3105 This->smallBlockLimit); 3106 3107 StorageUtl_WriteDWord( 3108 headerBigBlock, 3109 OFFSET_SBDEPOTSTART, 3110 This->smallBlockDepotStart); 3111 3112 StorageUtl_WriteDWord( 3113 headerBigBlock, 3114 OFFSET_SBDEPOTCOUNT, 3115 This->smallBlockDepotChain ? 3116 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0); 3117 3118 StorageUtl_WriteDWord( 3119 headerBigBlock, 3120 OFFSET_EXTBBDEPOTSTART, 3121 This->extBigBlockDepotStart); 3122 3123 StorageUtl_WriteDWord( 3124 headerBigBlock, 3125 OFFSET_EXTBBDEPOTCOUNT, 3126 This->extBigBlockDepotCount); 3127 3128 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++) 3129 { 3130 StorageUtl_WriteDWord( 3131 headerBigBlock, 3132 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index), 3133 (This->bigBlockDepotStart[index])); 3134 } 3135 3136 /* 3137 * Write the big block back to the file. 3138 */ 3139 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written); 3140 } 3141 3142 3143 /************************************************************************ 3144 * StorageImpl implementation : DirEntry methods 3145 ***********************************************************************/ 3146 3147 /****************************************************************************** 3148 * StorageImpl_ReadRawDirEntry 3149 * 3150 * This method will read the raw data from a directory entry in the file. 3151 * 3152 * buffer must be RAW_DIRENTRY_SIZE bytes long. 3153 */ 3154 static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer) 3155 { 3156 ULARGE_INTEGER offset; 3157 HRESULT hr; 3158 ULONG bytesRead; 3159 3160 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; 3161 3162 hr = BlockChainStream_ReadAt( 3163 This->rootBlockChain, 3164 offset, 3165 RAW_DIRENTRY_SIZE, 3166 buffer, 3167 &bytesRead); 3168 3169 if (bytesRead != RAW_DIRENTRY_SIZE) 3170 return STG_E_READFAULT; 3171 3172 return hr; 3173 } 3174 3175 /****************************************************************************** 3176 * StorageImpl_WriteRawDirEntry 3177 * 3178 * This method will write the raw data from a directory entry in the file. 3179 * 3180 * buffer must be RAW_DIRENTRY_SIZE bytes long. 3181 */ 3182 static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer) 3183 { 3184 ULARGE_INTEGER offset; 3185 ULONG bytesRead; 3186 3187 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE; 3188 3189 return BlockChainStream_WriteAt( 3190 This->rootBlockChain, 3191 offset, 3192 RAW_DIRENTRY_SIZE, 3193 buffer, 3194 &bytesRead); 3195 } 3196 3197 /*************************************************************************** 3198 * 3199 * Internal Method 3200 * 3201 * Mark a directory entry in the file as free. 3202 */ 3203 static HRESULT StorageImpl_DestroyDirEntry( 3204 StorageBaseImpl *base, 3205 DirRef index) 3206 { 3207 BYTE emptyData[RAW_DIRENTRY_SIZE]; 3208 StorageImpl *storage = (StorageImpl*)base; 3209 3210 memset(emptyData, 0, RAW_DIRENTRY_SIZE); 3211 3212 return StorageImpl_WriteRawDirEntry(storage, index, emptyData); 3213 } 3214 3215 /****************************************************************************** 3216 * UpdateRawDirEntry 3217 * 3218 * Update raw directory entry data from the fields in newData. 3219 * 3220 * buffer must be RAW_DIRENTRY_SIZE bytes long. 3221 */ 3222 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData) 3223 { 3224 memset(buffer, 0, RAW_DIRENTRY_SIZE); 3225 3226 memcpy( 3227 buffer + OFFSET_PS_NAME, 3228 newData->name, 3229 DIRENTRY_NAME_BUFFER_LEN ); 3230 3231 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1); 3232 3233 StorageUtl_WriteWord( 3234 buffer, 3235 OFFSET_PS_NAMELENGTH, 3236 newData->sizeOfNameString); 3237 3238 StorageUtl_WriteDWord( 3239 buffer, 3240 OFFSET_PS_LEFTCHILD, 3241 newData->leftChild); 3242 3243 StorageUtl_WriteDWord( 3244 buffer, 3245 OFFSET_PS_RIGHTCHILD, 3246 newData->rightChild); 3247 3248 StorageUtl_WriteDWord( 3249 buffer, 3250 OFFSET_PS_DIRROOT, 3251 newData->dirRootEntry); 3252 3253 StorageUtl_WriteGUID( 3254 buffer, 3255 OFFSET_PS_GUID, 3256 &newData->clsid); 3257 3258 StorageUtl_WriteDWord( 3259 buffer, 3260 OFFSET_PS_CTIMELOW, 3261 newData->ctime.dwLowDateTime); 3262 3263 StorageUtl_WriteDWord( 3264 buffer, 3265 OFFSET_PS_CTIMEHIGH, 3266 newData->ctime.dwHighDateTime); 3267 3268 StorageUtl_WriteDWord( 3269 buffer, 3270 OFFSET_PS_MTIMELOW, 3271 newData->mtime.dwLowDateTime); 3272 3273 StorageUtl_WriteDWord( 3274 buffer, 3275 OFFSET_PS_MTIMEHIGH, 3276 newData->ctime.dwHighDateTime); 3277 3278 StorageUtl_WriteDWord( 3279 buffer, 3280 OFFSET_PS_STARTBLOCK, 3281 newData->startingBlock); 3282 3283 StorageUtl_WriteDWord( 3284 buffer, 3285 OFFSET_PS_SIZE, 3286 newData->size.u.LowPart); 3287 3288 StorageUtl_WriteDWord( 3289 buffer, 3290 OFFSET_PS_SIZE_HIGH, 3291 newData->size.u.HighPart); 3292 } 3293 3294 /*************************************************************************** 3295 * 3296 * Internal Method 3297 * 3298 * Reserve a directory entry in the file and initialize it. 3299 */ 3300 static HRESULT StorageImpl_CreateDirEntry( 3301 StorageBaseImpl *base, 3302 const DirEntry *newData, 3303 DirRef *index) 3304 { 3305 StorageImpl *storage = (StorageImpl*)base; 3306 ULONG currentEntryIndex = 0; 3307 ULONG newEntryIndex = DIRENTRY_NULL; 3308 HRESULT hr = S_OK; 3309 BYTE currentData[RAW_DIRENTRY_SIZE]; 3310 WORD sizeOfNameString; 3311 3312 do 3313 { 3314 hr = StorageImpl_ReadRawDirEntry(storage, 3315 currentEntryIndex, 3316 currentData); 3317 3318 if (SUCCEEDED(hr)) 3319 { 3320 StorageUtl_ReadWord( 3321 currentData, 3322 OFFSET_PS_NAMELENGTH, 3323 &sizeOfNameString); 3324 3325 if (sizeOfNameString == 0) 3326 { 3327 /* 3328 * The entry exists and is available, we found it. 3329 */ 3330 newEntryIndex = currentEntryIndex; 3331 } 3332 } 3333 else 3334 { 3335 /* 3336 * We exhausted the directory entries, we will create more space below 3337 */ 3338 newEntryIndex = currentEntryIndex; 3339 } 3340 currentEntryIndex++; 3341 3342 } while (newEntryIndex == DIRENTRY_NULL); 3343 3344 /* 3345 * grow the directory stream 3346 */ 3347 if (FAILED(hr)) 3348 { 3349 BYTE emptyData[RAW_DIRENTRY_SIZE]; 3350 ULARGE_INTEGER newSize; 3351 ULONG entryIndex; 3352 ULONG lastEntry = 0; 3353 ULONG blockCount = 0; 3354 3355 /* 3356 * obtain the new count of blocks in the directory stream 3357 */ 3358 blockCount = BlockChainStream_GetCount( 3359 storage->rootBlockChain)+1; 3360 3361 /* 3362 * initialize the size used by the directory stream 3363 */ 3364 newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount; 3365 3366 /* 3367 * add a block to the directory stream 3368 */ 3369 BlockChainStream_SetSize(storage->rootBlockChain, newSize); 3370 3371 /* 3372 * memset the empty entry in order to initialize the unused newly 3373 * created entries 3374 */ 3375 memset(emptyData, 0, RAW_DIRENTRY_SIZE); 3376 3377 /* 3378 * initialize them 3379 */ 3380 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount; 3381 3382 for( 3383 entryIndex = newEntryIndex + 1; 3384 entryIndex < lastEntry; 3385 entryIndex++) 3386 { 3387 StorageImpl_WriteRawDirEntry( 3388 storage, 3389 entryIndex, 3390 emptyData); 3391 } 3392 3393 StorageImpl_SaveFileHeader(storage); 3394 } 3395 3396 UpdateRawDirEntry(currentData, newData); 3397 3398 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData); 3399 3400 if (SUCCEEDED(hr)) 3401 *index = newEntryIndex; 3402 3403 return hr; 3404 } 3405 3406 /****************************************************************************** 3407 * StorageImpl_ReadDirEntry 3408 * 3409 * This method will read the specified directory entry. 3410 */ 3411 static HRESULT StorageImpl_ReadDirEntry( 3412 StorageImpl* This, 3413 DirRef index, 3414 DirEntry* buffer) 3415 { 3416 BYTE currentEntry[RAW_DIRENTRY_SIZE]; 3417 HRESULT readRes; 3418 3419 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry); 3420 3421 if (SUCCEEDED(readRes)) 3422 { 3423 memset(buffer->name, 0, sizeof(buffer->name)); 3424 memcpy( 3425 buffer->name, 3426 (WCHAR *)currentEntry+OFFSET_PS_NAME, 3427 DIRENTRY_NAME_BUFFER_LEN ); 3428 TRACE("storage name: %s\n", debugstr_w(buffer->name)); 3429 3430 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1); 3431 3432 StorageUtl_ReadWord( 3433 currentEntry, 3434 OFFSET_PS_NAMELENGTH, 3435 &buffer->sizeOfNameString); 3436 3437 StorageUtl_ReadDWord( 3438 currentEntry, 3439 OFFSET_PS_LEFTCHILD, 3440 &buffer->leftChild); 3441 3442 StorageUtl_ReadDWord( 3443 currentEntry, 3444 OFFSET_PS_RIGHTCHILD, 3445 &buffer->rightChild); 3446 3447 StorageUtl_ReadDWord( 3448 currentEntry, 3449 OFFSET_PS_DIRROOT, 3450 &buffer->dirRootEntry); 3451 3452 StorageUtl_ReadGUID( 3453 currentEntry, 3454 OFFSET_PS_GUID, 3455 &buffer->clsid); 3456 3457 StorageUtl_ReadDWord( 3458 currentEntry, 3459 OFFSET_PS_CTIMELOW, 3460 &buffer->ctime.dwLowDateTime); 3461 3462 StorageUtl_ReadDWord( 3463 currentEntry, 3464 OFFSET_PS_CTIMEHIGH, 3465 &buffer->ctime.dwHighDateTime); 3466 3467 StorageUtl_ReadDWord( 3468 currentEntry, 3469 OFFSET_PS_MTIMELOW, 3470 &buffer->mtime.dwLowDateTime); 3471 3472 StorageUtl_ReadDWord( 3473 currentEntry, 3474 OFFSET_PS_MTIMEHIGH, 3475 &buffer->mtime.dwHighDateTime); 3476 3477 StorageUtl_ReadDWord( 3478 currentEntry, 3479 OFFSET_PS_STARTBLOCK, 3480 &buffer->startingBlock); 3481 3482 StorageUtl_ReadDWord( 3483 currentEntry, 3484 OFFSET_PS_SIZE, 3485 &buffer->size.u.LowPart); 3486 3487 if (This->bigBlockSize < 4096) 3488 { 3489 /* Version 3 files may have junk in the high part of size. */ 3490 buffer->size.u.HighPart = 0; 3491 } 3492 else 3493 { 3494 StorageUtl_ReadDWord( 3495 currentEntry, 3496 OFFSET_PS_SIZE_HIGH, 3497 &buffer->size.u.HighPart); 3498 } 3499 } 3500 3501 return readRes; 3502 } 3503 3504 /********************************************************************* 3505 * Write the specified directory entry to the file 3506 */ 3507 static HRESULT StorageImpl_WriteDirEntry( 3508 StorageImpl* This, 3509 DirRef index, 3510 const DirEntry* buffer) 3511 { 3512 BYTE currentEntry[RAW_DIRENTRY_SIZE]; 3513 3514 UpdateRawDirEntry(currentEntry, buffer); 3515 3516 return StorageImpl_WriteRawDirEntry(This, index, currentEntry); 3517 } 3518 3519 3520 /************************************************************************ 3521 * StorageImpl implementation : Block methods 3522 ***********************************************************************/ 3523 3524 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index) 3525 { 3526 return (ULONGLONG)(index+1) * This->bigBlockSize; 3527 } 3528 3529 static HRESULT StorageImpl_ReadBigBlock( 3530 StorageImpl* This, 3531 ULONG blockIndex, 3532 void* buffer, 3533 ULONG* out_read) 3534 { 3535 ULARGE_INTEGER ulOffset; 3536 DWORD read=0; 3537 HRESULT hr; 3538 3539 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 3540 3541 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read); 3542 3543 if (SUCCEEDED(hr) && read < This->bigBlockSize) 3544 { 3545 /* File ends during this block; fill the rest with 0's. */ 3546 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read); 3547 } 3548 3549 if (out_read) *out_read = read; 3550 3551 return hr; 3552 } 3553 3554 static BOOL StorageImpl_ReadDWordFromBigBlock( 3555 StorageImpl* This, 3556 ULONG blockIndex, 3557 ULONG offset, 3558 DWORD* value) 3559 { 3560 ULARGE_INTEGER ulOffset; 3561 DWORD read; 3562 DWORD tmp; 3563 3564 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 3565 ulOffset.QuadPart += offset; 3566 3567 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read); 3568 *value = lendian32toh(tmp); 3569 return (read == sizeof(DWORD)); 3570 } 3571 3572 static BOOL StorageImpl_WriteBigBlock( 3573 StorageImpl* This, 3574 ULONG blockIndex, 3575 const void* buffer) 3576 { 3577 ULARGE_INTEGER ulOffset; 3578 DWORD wrote; 3579 3580 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 3581 3582 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote); 3583 return (wrote == This->bigBlockSize); 3584 } 3585 3586 static BOOL StorageImpl_WriteDWordToBigBlock( 3587 StorageImpl* This, 3588 ULONG blockIndex, 3589 ULONG offset, 3590 DWORD value) 3591 { 3592 ULARGE_INTEGER ulOffset; 3593 DWORD wrote; 3594 3595 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex); 3596 ulOffset.QuadPart += offset; 3597 3598 value = htole32(value); 3599 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote); 3600 return (wrote == sizeof(DWORD)); 3601 } 3602 3603 /****************************************************************************** 3604 * Storage32Impl_SmallBlocksToBigBlocks 3605 * 3606 * This method will convert a small block chain to a big block chain. 3607 * The small block chain will be destroyed. 3608 */ 3609 static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks( 3610 StorageImpl* This, 3611 SmallBlockChainStream** ppsbChain) 3612 { 3613 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN; 3614 ULARGE_INTEGER size, offset; 3615 ULONG cbRead, cbWritten; 3616 ULARGE_INTEGER cbTotalRead; 3617 DirRef streamEntryRef; 3618 HRESULT resWrite = S_OK; 3619 HRESULT resRead; 3620 DirEntry streamEntry; 3621 BYTE *buffer; 3622 BlockChainStream *bbTempChain = NULL; 3623 BlockChainStream *bigBlockChain = NULL; 3624 3625 /* 3626 * Create a temporary big block chain that doesn't have 3627 * an associated directory entry. This temporary chain will be 3628 * used to copy data from small blocks to big blocks. 3629 */ 3630 bbTempChain = BlockChainStream_Construct(This, 3631 &bbHeadOfChain, 3632 DIRENTRY_NULL); 3633 if(!bbTempChain) return NULL; 3634 /* 3635 * Grow the big block chain. 3636 */ 3637 size = SmallBlockChainStream_GetSize(*ppsbChain); 3638 BlockChainStream_SetSize(bbTempChain, size); 3639 3640 /* 3641 * Copy the contents of the small block chain to the big block chain 3642 * by small block size increments. 3643 */ 3644 offset.u.LowPart = 0; 3645 offset.u.HighPart = 0; 3646 cbTotalRead.QuadPart = 0; 3647 3648 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE); 3649 do 3650 { 3651 resRead = SmallBlockChainStream_ReadAt(*ppsbChain, 3652 offset, 3653 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart), 3654 buffer, 3655 &cbRead); 3656 if (FAILED(resRead)) 3657 break; 3658 3659 if (cbRead > 0) 3660 { 3661 cbTotalRead.QuadPart += cbRead; 3662 3663 resWrite = BlockChainStream_WriteAt(bbTempChain, 3664 offset, 3665 cbRead, 3666 buffer, 3667 &cbWritten); 3668 3669 if (FAILED(resWrite)) 3670 break; 3671 3672 offset.u.LowPart += cbRead; 3673 } 3674 else 3675 { 3676 resRead = STG_E_READFAULT; 3677 break; 3678 } 3679 } while (cbTotalRead.QuadPart < size.QuadPart); 3680 HeapFree(GetProcessHeap(),0,buffer); 3681 3682 size.u.HighPart = 0; 3683 size.u.LowPart = 0; 3684 3685 if (FAILED(resRead) || FAILED(resWrite)) 3686 { 3687 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite); 3688 BlockChainStream_SetSize(bbTempChain, size); 3689 BlockChainStream_Destroy(bbTempChain); 3690 return NULL; 3691 } 3692 3693 /* 3694 * Destroy the small block chain. 3695 */ 3696 streamEntryRef = (*ppsbChain)->ownerDirEntry; 3697 SmallBlockChainStream_SetSize(*ppsbChain, size); 3698 SmallBlockChainStream_Destroy(*ppsbChain); 3699 *ppsbChain = 0; 3700 3701 /* 3702 * Change the directory entry. This chain is now a big block chain 3703 * and it doesn't reside in the small blocks chain anymore. 3704 */ 3705 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); 3706 3707 streamEntry.startingBlock = bbHeadOfChain; 3708 3709 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); 3710 3711 /* 3712 * Destroy the temporary entryless big block chain. 3713 * Create a new big block chain associated with this entry. 3714 */ 3715 BlockChainStream_Destroy(bbTempChain); 3716 bigBlockChain = BlockChainStream_Construct(This, 3717 NULL, 3718 streamEntryRef); 3719 3720 return bigBlockChain; 3721 } 3722 3723 /****************************************************************************** 3724 * Storage32Impl_BigBlocksToSmallBlocks 3725 * 3726 * This method will convert a big block chain to a small block chain. 3727 * The big block chain will be destroyed on success. 3728 */ 3729 static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( 3730 StorageImpl* This, 3731 BlockChainStream** ppbbChain, 3732 ULARGE_INTEGER newSize) 3733 { 3734 ULARGE_INTEGER size, offset, cbTotalRead; 3735 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN; 3736 DirRef streamEntryRef; 3737 HRESULT resWrite = S_OK, resRead = S_OK; 3738 DirEntry streamEntry; 3739 BYTE* buffer; 3740 SmallBlockChainStream* sbTempChain; 3741 3742 TRACE("%p %p\n", This, ppbbChain); 3743 3744 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain, 3745 DIRENTRY_NULL); 3746 3747 if(!sbTempChain) 3748 return NULL; 3749 3750 SmallBlockChainStream_SetSize(sbTempChain, newSize); 3751 size = BlockChainStream_GetSize(*ppbbChain); 3752 size.QuadPart = min(size.QuadPart, newSize.QuadPart); 3753 3754 offset.u.HighPart = 0; 3755 offset.u.LowPart = 0; 3756 cbTotalRead.QuadPart = 0; 3757 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize); 3758 while(cbTotalRead.QuadPart < size.QuadPart) 3759 { 3760 resRead = BlockChainStream_ReadAt(*ppbbChain, offset, 3761 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart), 3762 buffer, &cbRead); 3763 3764 if(FAILED(resRead)) 3765 break; 3766 3767 if(cbRead > 0) 3768 { 3769 cbTotalRead.QuadPart += cbRead; 3770 3771 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset, 3772 cbRead, buffer, &cbWritten); 3773 3774 if(FAILED(resWrite)) 3775 break; 3776 3777 offset.u.LowPart += cbRead; 3778 } 3779 else 3780 { 3781 resRead = STG_E_READFAULT; 3782 break; 3783 } 3784 } 3785 HeapFree(GetProcessHeap(), 0, buffer); 3786 3787 size.u.HighPart = 0; 3788 size.u.LowPart = 0; 3789 3790 if(FAILED(resRead) || FAILED(resWrite)) 3791 { 3792 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite); 3793 SmallBlockChainStream_SetSize(sbTempChain, size); 3794 SmallBlockChainStream_Destroy(sbTempChain); 3795 return NULL; 3796 } 3797 3798 /* destroy the original big block chain */ 3799 streamEntryRef = (*ppbbChain)->ownerDirEntry; 3800 BlockChainStream_SetSize(*ppbbChain, size); 3801 BlockChainStream_Destroy(*ppbbChain); 3802 *ppbbChain = NULL; 3803 3804 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry); 3805 streamEntry.startingBlock = sbHeadOfChain; 3806 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry); 3807 3808 SmallBlockChainStream_Destroy(sbTempChain); 3809 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); 3810 } 3811 3812 /****************************************************************************** 3813 * Storage32Impl_AddBlockDepot 3814 * 3815 * This will create a depot block, essentially it is a block initialized 3816 * to BLOCK_UNUSEDs. 3817 */ 3818 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex) 3819 { 3820 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE]; 3821 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1; 3822 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); 3823 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot; 3824 3825 /* 3826 * Initialize blocks as free 3827 */ 3828 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize); 3829 3830 /* Reserve the range lock sector */ 3831 if (depotIndex == rangeLockDepot) 3832 { 3833 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN; 3834 } 3835 3836 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer); 3837 } 3838 3839 /****************************************************************************** 3840 * Storage32Impl_GetExtDepotBlock 3841 * 3842 * Returns the index of the block that corresponds to the specified depot 3843 * index. This method is only for depot indexes equal or greater than 3844 * COUNT_BBDEPOTINHEADER. 3845 */ 3846 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex) 3847 { 3848 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1; 3849 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 3850 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock; 3851 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock; 3852 ULONG blockIndex = BLOCK_UNUSED; 3853 ULONG extBlockIndex; 3854 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 3855 int index, num_blocks; 3856 3857 assert(depotIndex >= COUNT_BBDEPOTINHEADER); 3858 3859 if (extBlockCount >= This->extBigBlockDepotCount) 3860 return BLOCK_UNUSED; 3861 3862 if (This->indexExtBlockDepotCached != extBlockCount) 3863 { 3864 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount]; 3865 3866 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL); 3867 3868 num_blocks = This->bigBlockSize / 4; 3869 3870 for (index = 0; index < num_blocks; index++) 3871 { 3872 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex); 3873 This->extBlockDepotCached[index] = blockIndex; 3874 } 3875 3876 This->indexExtBlockDepotCached = extBlockCount; 3877 } 3878 3879 blockIndex = This->extBlockDepotCached[extBlockOffset]; 3880 3881 return blockIndex; 3882 } 3883 3884 /****************************************************************************** 3885 * Storage32Impl_SetExtDepotBlock 3886 * 3887 * Associates the specified block index to the specified depot index. 3888 * This method is only for depot indexes equal or greater than 3889 * COUNT_BBDEPOTINHEADER. 3890 */ 3891 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex) 3892 { 3893 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1; 3894 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 3895 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock; 3896 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock; 3897 ULONG extBlockIndex; 3898 3899 assert(depotIndex >= COUNT_BBDEPOTINHEADER); 3900 3901 assert(extBlockCount < This->extBigBlockDepotCount); 3902 3903 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount]; 3904 3905 if (extBlockIndex != BLOCK_UNUSED) 3906 { 3907 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex, 3908 extBlockOffset * sizeof(ULONG), 3909 blockIndex); 3910 } 3911 3912 if (This->indexExtBlockDepotCached == extBlockCount) 3913 { 3914 This->extBlockDepotCached[extBlockOffset] = blockIndex; 3915 } 3916 } 3917 3918 /****************************************************************************** 3919 * Storage32Impl_AddExtBlockDepot 3920 * 3921 * Creates an extended depot block. 3922 */ 3923 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This) 3924 { 3925 ULONG numExtBlocks = This->extBigBlockDepotCount; 3926 ULONG nextExtBlock = This->extBigBlockDepotStart; 3927 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 3928 ULONG index = BLOCK_UNUSED; 3929 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG); 3930 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG); 3931 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1; 3932 3933 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) * 3934 blocksPerDepotBlock; 3935 3936 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN)) 3937 { 3938 /* 3939 * The first extended block. 3940 */ 3941 This->extBigBlockDepotStart = index; 3942 } 3943 else 3944 { 3945 /* 3946 * Find the last existing extended block. 3947 */ 3948 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1]; 3949 3950 /* 3951 * Add the new extended block to the chain. 3952 */ 3953 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset, 3954 index); 3955 } 3956 3957 /* 3958 * Initialize this block. 3959 */ 3960 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize); 3961 StorageImpl_WriteBigBlock(This, index, depotBuffer); 3962 3963 /* Add the block to our cache. */ 3964 if (This->extBigBlockDepotLocationsSize == numExtBlocks) 3965 { 3966 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2; 3967 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size); 3968 3969 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize); 3970 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); 3971 3972 This->extBigBlockDepotLocations = new_cache; 3973 This->extBigBlockDepotLocationsSize = new_cache_size; 3974 } 3975 This->extBigBlockDepotLocations[numExtBlocks] = index; 3976 3977 return index; 3978 } 3979 3980 /************************************************************************ 3981 * StorageImpl_GetNextBlockInChain 3982 * 3983 * This method will retrieve the block index of the next big block in 3984 * in the chain. 3985 * 3986 * Params: This - Pointer to the Storage object. 3987 * blockIndex - Index of the block to retrieve the chain 3988 * for. 3989 * nextBlockIndex - receives the return value. 3990 * 3991 * Returns: This method returns the index of the next block in the chain. 3992 * It will return the constants: 3993 * BLOCK_SPECIAL - If the block given was not part of a 3994 * chain. 3995 * BLOCK_END_OF_CHAIN - If the block given was the last in 3996 * a chain. 3997 * BLOCK_UNUSED - If the block given was not past of a chain 3998 * and is available. 3999 * BLOCK_EXTBBDEPOT - This block is part of the extended 4000 * big block depot. 4001 * 4002 * See Windows documentation for more details on IStorage methods. 4003 */ 4004 static HRESULT StorageImpl_GetNextBlockInChain( 4005 StorageImpl* This, 4006 ULONG blockIndex, 4007 ULONG* nextBlockIndex) 4008 { 4009 ULONG offsetInDepot = blockIndex * sizeof (ULONG); 4010 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize; 4011 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize; 4012 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 4013 ULONG read; 4014 ULONG depotBlockIndexPos; 4015 int index, num_blocks; 4016 4017 *nextBlockIndex = BLOCK_SPECIAL; 4018 4019 if(depotBlockCount >= This->bigBlockDepotCount) 4020 { 4021 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount, 4022 This->bigBlockDepotCount); 4023 return STG_E_READFAULT; 4024 } 4025 4026 /* 4027 * Cache the currently accessed depot block. 4028 */ 4029 if (depotBlockCount != This->indexBlockDepotCached) 4030 { 4031 This->indexBlockDepotCached = depotBlockCount; 4032 4033 if (depotBlockCount < COUNT_BBDEPOTINHEADER) 4034 { 4035 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; 4036 } 4037 else 4038 { 4039 /* 4040 * We have to look in the extended depot. 4041 */ 4042 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); 4043 } 4044 4045 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); 4046 4047 if (!read) 4048 return STG_E_READFAULT; 4049 4050 num_blocks = This->bigBlockSize / 4; 4051 4052 for (index = 0; index < num_blocks; index++) 4053 { 4054 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex); 4055 This->blockDepotCached[index] = *nextBlockIndex; 4056 } 4057 } 4058 4059 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)]; 4060 4061 return S_OK; 4062 } 4063 4064 /****************************************************************************** 4065 * Storage32Impl_GetNextExtendedBlock 4066 * 4067 * Given an extended block this method will return the next extended block. 4068 * 4069 * NOTES: 4070 * The last ULONG of an extended block is the block index of the next 4071 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the 4072 * depot. 4073 * 4074 * Return values: 4075 * - The index of the next extended block 4076 * - BLOCK_UNUSED: there is no next extended block. 4077 * - Any other return values denotes failure. 4078 */ 4079 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex) 4080 { 4081 ULONG nextBlockIndex = BLOCK_SPECIAL; 4082 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG); 4083 4084 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset, 4085 &nextBlockIndex); 4086 4087 return nextBlockIndex; 4088 } 4089 4090 /****************************************************************************** 4091 * StorageImpl_SetNextBlockInChain 4092 * 4093 * This method will write the index of the specified block's next block 4094 * in the big block depot. 4095 * 4096 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain 4097 * do the following 4098 * 4099 * StorageImpl_SetNextBlockInChain(This, 3, 1); 4100 * StorageImpl_SetNextBlockInChain(This, 1, 7); 4101 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN); 4102 * 4103 */ 4104 static void StorageImpl_SetNextBlockInChain( 4105 StorageImpl* This, 4106 ULONG blockIndex, 4107 ULONG nextBlock) 4108 { 4109 ULONG offsetInDepot = blockIndex * sizeof (ULONG); 4110 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize; 4111 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize; 4112 ULONG depotBlockIndexPos; 4113 4114 assert(depotBlockCount < This->bigBlockDepotCount); 4115 assert(blockIndex != nextBlock); 4116 4117 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1) 4118 /* This should never happen (storage file format spec forbids it), but 4119 * older versions of Wine may have generated broken files. We don't want to 4120 * assert and potentially lose data, but we do want to know if this ever 4121 * happens in a newly-created file. */ 4122 ERR("Using range lock page\n"); 4123 4124 if (depotBlockCount < COUNT_BBDEPOTINHEADER) 4125 { 4126 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount]; 4127 } 4128 else 4129 { 4130 /* 4131 * We have to look in the extended depot. 4132 */ 4133 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount); 4134 } 4135 4136 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset, 4137 nextBlock); 4138 /* 4139 * Update the cached block depot, if necessary. 4140 */ 4141 if (depotBlockCount == This->indexBlockDepotCached) 4142 { 4143 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock; 4144 } 4145 } 4146 4147 /****************************************************************************** 4148 * StorageImpl_GetNextFreeBigBlock 4149 * 4150 * Returns the index of the next free big block. 4151 * If the big block depot is filled, this method will enlarge it. 4152 * 4153 */ 4154 static ULONG StorageImpl_GetNextFreeBigBlock( 4155 StorageImpl* This) 4156 { 4157 ULONG depotBlockIndexPos; 4158 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE]; 4159 ULONG depotBlockOffset; 4160 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG); 4161 ULONG nextBlockIndex = BLOCK_SPECIAL; 4162 int depotIndex = 0; 4163 ULONG freeBlock = BLOCK_UNUSED; 4164 ULONG read; 4165 ULARGE_INTEGER neededSize; 4166 STATSTG statstg; 4167 4168 depotIndex = This->prevFreeBlock / blocksPerDepot; 4169 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG); 4170 4171 /* 4172 * Scan the entire big block depot until we find a block marked free 4173 */ 4174 while (nextBlockIndex != BLOCK_UNUSED) 4175 { 4176 if (depotIndex < COUNT_BBDEPOTINHEADER) 4177 { 4178 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex]; 4179 4180 /* 4181 * Grow the primary depot. 4182 */ 4183 if (depotBlockIndexPos == BLOCK_UNUSED) 4184 { 4185 depotBlockIndexPos = depotIndex*blocksPerDepot; 4186 4187 /* 4188 * Add a block depot. 4189 */ 4190 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex); 4191 This->bigBlockDepotCount++; 4192 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos; 4193 4194 /* 4195 * Flag it as a block depot. 4196 */ 4197 StorageImpl_SetNextBlockInChain(This, 4198 depotBlockIndexPos, 4199 BLOCK_SPECIAL); 4200 4201 /* Save new header information. 4202 */ 4203 StorageImpl_SaveFileHeader(This); 4204 } 4205 } 4206 else 4207 { 4208 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex); 4209 4210 if (depotBlockIndexPos == BLOCK_UNUSED) 4211 { 4212 /* 4213 * Grow the extended depot. 4214 */ 4215 ULONG extIndex = BLOCK_UNUSED; 4216 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER; 4217 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1); 4218 4219 if (extBlockOffset == 0) 4220 { 4221 /* We need an extended block. 4222 */ 4223 extIndex = Storage32Impl_AddExtBlockDepot(This); 4224 This->extBigBlockDepotCount++; 4225 depotBlockIndexPos = extIndex + 1; 4226 } 4227 else 4228 depotBlockIndexPos = depotIndex * blocksPerDepot; 4229 4230 /* 4231 * Add a block depot and mark it in the extended block. 4232 */ 4233 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex); 4234 This->bigBlockDepotCount++; 4235 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos); 4236 4237 /* Flag the block depot. 4238 */ 4239 StorageImpl_SetNextBlockInChain(This, 4240 depotBlockIndexPos, 4241 BLOCK_SPECIAL); 4242 4243 /* If necessary, flag the extended depot block. 4244 */ 4245 if (extIndex != BLOCK_UNUSED) 4246 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT); 4247 4248 /* Save header information. 4249 */ 4250 StorageImpl_SaveFileHeader(This); 4251 } 4252 } 4253 4254 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read); 4255 4256 if (read) 4257 { 4258 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) && 4259 ( nextBlockIndex != BLOCK_UNUSED)) 4260 { 4261 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex); 4262 4263 if (nextBlockIndex == BLOCK_UNUSED) 4264 { 4265 freeBlock = (depotIndex * blocksPerDepot) + 4266 (depotBlockOffset/sizeof(ULONG)); 4267 } 4268 4269 depotBlockOffset += sizeof(ULONG); 4270 } 4271 } 4272 4273 depotIndex++; 4274 depotBlockOffset = 0; 4275 } 4276 4277 /* 4278 * make sure that the block physically exists before using it 4279 */ 4280 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize; 4281 4282 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME); 4283 4284 if (neededSize.QuadPart > statstg.cbSize.QuadPart) 4285 ILockBytes_SetSize(This->lockBytes, neededSize); 4286 4287 This->prevFreeBlock = freeBlock; 4288 4289 return freeBlock; 4290 } 4291 4292 /****************************************************************************** 4293 * StorageImpl_FreeBigBlock 4294 * 4295 * This method will flag the specified block as free in the big block depot. 4296 */ 4297 static void StorageImpl_FreeBigBlock( 4298 StorageImpl* This, 4299 ULONG blockIndex) 4300 { 4301 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); 4302 4303 if (blockIndex < This->prevFreeBlock) 4304 This->prevFreeBlock = blockIndex; 4305 } 4306 4307 4308 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base, 4309 DirRef index, const DirEntry *data) 4310 { 4311 StorageImpl *This = (StorageImpl*)base; 4312 return StorageImpl_WriteDirEntry(This, index, data); 4313 } 4314 4315 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base, 4316 DirRef index, DirEntry *data) 4317 { 4318 StorageImpl *This = (StorageImpl*)base; 4319 return StorageImpl_ReadDirEntry(This, index, data); 4320 } 4321 4322 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This) 4323 { 4324 int i; 4325 4326 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 4327 { 4328 if (!This->blockChainCache[i]) 4329 { 4330 return &This->blockChainCache[i]; 4331 } 4332 } 4333 4334 i = This->blockChainToEvict; 4335 4336 BlockChainStream_Destroy(This->blockChainCache[i]); 4337 This->blockChainCache[i] = NULL; 4338 4339 This->blockChainToEvict++; 4340 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) 4341 This->blockChainToEvict = 0; 4342 4343 return &This->blockChainCache[i]; 4344 } 4345 4346 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This, 4347 DirRef index) 4348 { 4349 int i, free_index=-1; 4350 4351 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 4352 { 4353 if (!This->blockChainCache[i]) 4354 { 4355 if (free_index == -1) free_index = i; 4356 } 4357 else if (This->blockChainCache[i]->ownerDirEntry == index) 4358 { 4359 return &This->blockChainCache[i]; 4360 } 4361 } 4362 4363 if (free_index == -1) 4364 { 4365 free_index = This->blockChainToEvict; 4366 4367 BlockChainStream_Destroy(This->blockChainCache[free_index]); 4368 This->blockChainCache[free_index] = NULL; 4369 4370 This->blockChainToEvict++; 4371 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE) 4372 This->blockChainToEvict = 0; 4373 } 4374 4375 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index); 4376 return &This->blockChainCache[free_index]; 4377 } 4378 4379 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index) 4380 { 4381 int i; 4382 4383 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 4384 { 4385 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index) 4386 { 4387 BlockChainStream_Destroy(This->blockChainCache[i]); 4388 This->blockChainCache[i] = NULL; 4389 return; 4390 } 4391 } 4392 } 4393 4394 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index, 4395 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 4396 { 4397 StorageImpl *This = (StorageImpl*)base; 4398 DirEntry data; 4399 HRESULT hr; 4400 ULONG bytesToRead; 4401 4402 hr = StorageImpl_ReadDirEntry(This, index, &data); 4403 if (FAILED(hr)) return hr; 4404 4405 if (data.size.QuadPart == 0) 4406 { 4407 *bytesRead = 0; 4408 return S_OK; 4409 } 4410 4411 if (offset.QuadPart + size > data.size.QuadPart) 4412 { 4413 bytesToRead = data.size.QuadPart - offset.QuadPart; 4414 } 4415 else 4416 { 4417 bytesToRead = size; 4418 } 4419 4420 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 4421 { 4422 SmallBlockChainStream *stream; 4423 4424 stream = SmallBlockChainStream_Construct(This, NULL, index); 4425 if (!stream) return E_OUTOFMEMORY; 4426 4427 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead); 4428 4429 SmallBlockChainStream_Destroy(stream); 4430 4431 return hr; 4432 } 4433 else 4434 { 4435 BlockChainStream *stream = NULL; 4436 4437 stream = *StorageImpl_GetCachedBlockChainStream(This, index); 4438 if (!stream) return E_OUTOFMEMORY; 4439 4440 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead); 4441 4442 return hr; 4443 } 4444 } 4445 4446 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index, 4447 ULARGE_INTEGER newsize) 4448 { 4449 StorageImpl *This = (StorageImpl*)base; 4450 DirEntry data; 4451 HRESULT hr; 4452 SmallBlockChainStream *smallblock=NULL; 4453 BlockChainStream **pbigblock=NULL, *bigblock=NULL; 4454 4455 hr = StorageImpl_ReadDirEntry(This, index, &data); 4456 if (FAILED(hr)) return hr; 4457 4458 /* In simple mode keep the stream size above the small block limit */ 4459 if (This->base.openFlags & STGM_SIMPLE) 4460 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK); 4461 4462 if (data.size.QuadPart == newsize.QuadPart) 4463 return S_OK; 4464 4465 /* Create a block chain object of the appropriate type */ 4466 if (data.size.QuadPart == 0) 4467 { 4468 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 4469 { 4470 smallblock = SmallBlockChainStream_Construct(This, NULL, index); 4471 if (!smallblock) return E_OUTOFMEMORY; 4472 } 4473 else 4474 { 4475 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index); 4476 bigblock = *pbigblock; 4477 if (!bigblock) return E_OUTOFMEMORY; 4478 } 4479 } 4480 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 4481 { 4482 smallblock = SmallBlockChainStream_Construct(This, NULL, index); 4483 if (!smallblock) return E_OUTOFMEMORY; 4484 } 4485 else 4486 { 4487 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index); 4488 bigblock = *pbigblock; 4489 if (!bigblock) return E_OUTOFMEMORY; 4490 } 4491 4492 /* Change the block chain type if necessary. */ 4493 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK) 4494 { 4495 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock); 4496 if (!bigblock) 4497 { 4498 SmallBlockChainStream_Destroy(smallblock); 4499 return E_FAIL; 4500 } 4501 4502 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This); 4503 *pbigblock = bigblock; 4504 } 4505 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 4506 { 4507 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize); 4508 if (!smallblock) 4509 return E_FAIL; 4510 } 4511 4512 /* Set the size of the block chain. */ 4513 if (smallblock) 4514 { 4515 SmallBlockChainStream_SetSize(smallblock, newsize); 4516 SmallBlockChainStream_Destroy(smallblock); 4517 } 4518 else 4519 { 4520 BlockChainStream_SetSize(bigblock, newsize); 4521 } 4522 4523 /* Set the size in the directory entry. */ 4524 hr = StorageImpl_ReadDirEntry(This, index, &data); 4525 if (SUCCEEDED(hr)) 4526 { 4527 data.size = newsize; 4528 4529 hr = StorageImpl_WriteDirEntry(This, index, &data); 4530 } 4531 return hr; 4532 } 4533 4534 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index, 4535 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 4536 { 4537 StorageImpl *This = (StorageImpl*)base; 4538 DirEntry data; 4539 HRESULT hr; 4540 ULARGE_INTEGER newSize; 4541 4542 hr = StorageImpl_ReadDirEntry(This, index, &data); 4543 if (FAILED(hr)) return hr; 4544 4545 /* Grow the stream if necessary */ 4546 newSize.QuadPart = offset.QuadPart + size; 4547 4548 if (newSize.QuadPart > data.size.QuadPart) 4549 { 4550 hr = StorageImpl_StreamSetSize(base, index, newSize); 4551 if (FAILED(hr)) 4552 return hr; 4553 4554 hr = StorageImpl_ReadDirEntry(This, index, &data); 4555 if (FAILED(hr)) return hr; 4556 } 4557 4558 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK) 4559 { 4560 SmallBlockChainStream *stream; 4561 4562 stream = SmallBlockChainStream_Construct(This, NULL, index); 4563 if (!stream) return E_OUTOFMEMORY; 4564 4565 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); 4566 4567 SmallBlockChainStream_Destroy(stream); 4568 4569 return hr; 4570 } 4571 else 4572 { 4573 BlockChainStream *stream; 4574 4575 stream = *StorageImpl_GetCachedBlockChainStream(This, index); 4576 if (!stream) return E_OUTOFMEMORY; 4577 4578 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten); 4579 } 4580 } 4581 4582 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst, 4583 DirRef src) 4584 { 4585 StorageImpl *This = (StorageImpl*)base; 4586 DirEntry dst_data, src_data; 4587 HRESULT hr; 4588 4589 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data); 4590 4591 if (SUCCEEDED(hr)) 4592 hr = StorageImpl_ReadDirEntry(This, src, &src_data); 4593 4594 if (SUCCEEDED(hr)) 4595 { 4596 StorageImpl_DeleteCachedBlockChainStream(This, src); 4597 dst_data.startingBlock = src_data.startingBlock; 4598 dst_data.size = src_data.size; 4599 4600 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data); 4601 } 4602 4603 return hr; 4604 } 4605 4606 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create) 4607 { 4608 HRESULT hr=S_OK; 4609 DirEntry currentEntry; 4610 DirRef currentEntryRef; 4611 BlockChainStream *blockChainStream; 4612 4613 if (create) 4614 { 4615 ULARGE_INTEGER size; 4616 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE]; 4617 4618 /* Discard any existing data. */ 4619 size.QuadPart = 0; 4620 ILockBytes_SetSize(This->lockBytes, size); 4621 4622 /* 4623 * Initialize all header variables: 4624 * - The big block depot consists of one block and it is at block 0 4625 * - The directory table starts at block 1 4626 * - There is no small block depot 4627 */ 4628 memset( This->bigBlockDepotStart, 4629 BLOCK_UNUSED, 4630 sizeof(This->bigBlockDepotStart)); 4631 4632 This->bigBlockDepotCount = 1; 4633 This->bigBlockDepotStart[0] = 0; 4634 This->rootStartBlock = 1; 4635 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK; 4636 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN; 4637 if (This->bigBlockSize == 4096) 4638 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS; 4639 else 4640 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS; 4641 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS; 4642 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN; 4643 This->extBigBlockDepotCount = 0; 4644 4645 StorageImpl_SaveFileHeader(This); 4646 4647 /* 4648 * Add one block for the big block depot and one block for the directory table 4649 */ 4650 size.u.HighPart = 0; 4651 size.u.LowPart = This->bigBlockSize * 3; 4652 ILockBytes_SetSize(This->lockBytes, size); 4653 4654 /* 4655 * Initialize the big block depot 4656 */ 4657 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize); 4658 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL); 4659 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN); 4660 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer); 4661 } 4662 else 4663 { 4664 /* 4665 * Load the header for the file. 4666 */ 4667 hr = StorageImpl_LoadFileHeader(This); 4668 4669 if (FAILED(hr)) 4670 { 4671 return hr; 4672 } 4673 } 4674 4675 /* 4676 * There is no block depot cached yet. 4677 */ 4678 This->indexBlockDepotCached = 0xFFFFFFFF; 4679 This->indexExtBlockDepotCached = 0xFFFFFFFF; 4680 4681 /* 4682 * Start searching for free blocks with block 0. 4683 */ 4684 This->prevFreeBlock = 0; 4685 4686 This->firstFreeSmallBlock = 0; 4687 4688 /* Read the extended big block depot locations. */ 4689 if (This->extBigBlockDepotCount != 0) 4690 { 4691 ULONG current_block = This->extBigBlockDepotStart; 4692 ULONG cache_size = This->extBigBlockDepotCount * 2; 4693 ULONG i; 4694 4695 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size); 4696 if (!This->extBigBlockDepotLocations) 4697 { 4698 return E_OUTOFMEMORY; 4699 } 4700 4701 This->extBigBlockDepotLocationsSize = cache_size; 4702 4703 for (i=0; i<This->extBigBlockDepotCount; i++) 4704 { 4705 if (current_block == BLOCK_END_OF_CHAIN) 4706 { 4707 WARN("File has too few extended big block depot blocks.\n"); 4708 return STG_E_DOCFILECORRUPT; 4709 } 4710 This->extBigBlockDepotLocations[i] = current_block; 4711 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block); 4712 } 4713 } 4714 else 4715 { 4716 This->extBigBlockDepotLocations = NULL; 4717 This->extBigBlockDepotLocationsSize = 0; 4718 } 4719 4720 /* 4721 * Create the block chain abstractions. 4722 */ 4723 if(!(blockChainStream = 4724 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL))) 4725 { 4726 return STG_E_READFAULT; 4727 } 4728 if (!new_object) 4729 BlockChainStream_Destroy(This->rootBlockChain); 4730 This->rootBlockChain = blockChainStream; 4731 4732 if(!(blockChainStream = 4733 BlockChainStream_Construct(This, &This->smallBlockDepotStart, 4734 DIRENTRY_NULL))) 4735 { 4736 return STG_E_READFAULT; 4737 } 4738 if (!new_object) 4739 BlockChainStream_Destroy(This->smallBlockDepotChain); 4740 This->smallBlockDepotChain = blockChainStream; 4741 4742 /* 4743 * Write the root storage entry (memory only) 4744 */ 4745 if (create) 4746 { 4747 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0}; 4748 DirEntry rootEntry; 4749 /* 4750 * Initialize the directory table 4751 */ 4752 memset(&rootEntry, 0, sizeof(rootEntry)); 4753 strcpyW(rootEntry.name, rootentryW); 4754 rootEntry.sizeOfNameString = sizeof(rootentryW); 4755 rootEntry.stgType = STGTY_ROOT; 4756 rootEntry.leftChild = DIRENTRY_NULL; 4757 rootEntry.rightChild = DIRENTRY_NULL; 4758 rootEntry.dirRootEntry = DIRENTRY_NULL; 4759 rootEntry.startingBlock = BLOCK_END_OF_CHAIN; 4760 rootEntry.size.u.HighPart = 0; 4761 rootEntry.size.u.LowPart = 0; 4762 4763 StorageImpl_WriteDirEntry(This, 0, &rootEntry); 4764 } 4765 4766 /* 4767 * Find the ID of the root storage. 4768 */ 4769 currentEntryRef = 0; 4770 4771 do 4772 { 4773 hr = StorageImpl_ReadDirEntry( 4774 This, 4775 currentEntryRef, 4776 ¤tEntry); 4777 4778 if (SUCCEEDED(hr)) 4779 { 4780 if ( (currentEntry.sizeOfNameString != 0 ) && 4781 (currentEntry.stgType == STGTY_ROOT) ) 4782 { 4783 This->base.storageDirEntry = currentEntryRef; 4784 } 4785 } 4786 4787 currentEntryRef++; 4788 4789 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) ); 4790 4791 if (FAILED(hr)) 4792 { 4793 return STG_E_READFAULT; 4794 } 4795 4796 /* 4797 * Create the block chain abstraction for the small block root chain. 4798 */ 4799 if(!(blockChainStream = 4800 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry))) 4801 { 4802 return STG_E_READFAULT; 4803 } 4804 if (!new_object) 4805 BlockChainStream_Destroy(This->smallBlockRootChain); 4806 This->smallBlockRootChain = blockChainStream; 4807 4808 if (!new_object) 4809 { 4810 int i; 4811 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 4812 { 4813 BlockChainStream_Destroy(This->blockChainCache[i]); 4814 This->blockChainCache[i] = NULL; 4815 } 4816 } 4817 4818 return hr; 4819 } 4820 4821 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base, 4822 ULONG* result, BOOL refresh) 4823 { 4824 StorageImpl *This = (StorageImpl*)base; 4825 HRESULT hr=S_OK; 4826 DWORD oldTransactionSig = This->transactionSig; 4827 4828 if (refresh) 4829 { 4830 ULARGE_INTEGER offset; 4831 ULONG bytes_read; 4832 BYTE data[4]; 4833 4834 offset.u.HighPart = 0; 4835 offset.u.LowPart = OFFSET_TRANSACTIONSIG; 4836 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read); 4837 4838 if (SUCCEEDED(hr)) 4839 { 4840 StorageUtl_ReadDWord(data, 0, &This->transactionSig); 4841 4842 if (oldTransactionSig != This->transactionSig) 4843 { 4844 /* Someone else wrote to this, so toss all cached information. */ 4845 TRACE("signature changed\n"); 4846 4847 hr = StorageImpl_Refresh(This, FALSE, FALSE); 4848 } 4849 4850 if (FAILED(hr)) 4851 This->transactionSig = oldTransactionSig; 4852 } 4853 } 4854 4855 *result = This->transactionSig; 4856 4857 return hr; 4858 } 4859 4860 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base, 4861 ULONG value) 4862 { 4863 StorageImpl *This = (StorageImpl*)base; 4864 4865 This->transactionSig = value; 4866 StorageImpl_SaveFileHeader(This); 4867 4868 return S_OK; 4869 } 4870 4871 static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset, 4872 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) 4873 { 4874 if ((dwLockType & This->locks_supported) == 0) 4875 { 4876 if (supported) *supported = FALSE; 4877 return S_OK; 4878 } 4879 4880 if (supported) *supported = TRUE; 4881 return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); 4882 } 4883 4884 static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset, 4885 ULARGE_INTEGER cb, DWORD dwLockType) 4886 { 4887 if ((dwLockType & This->locks_supported) == 0) 4888 return S_OK; 4889 4890 return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType); 4891 } 4892 4893 /* Internal function */ 4894 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, 4895 ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) 4896 { 4897 HRESULT hr; 4898 int delay = 0; 4899 DWORD start_time = GetTickCount(); 4900 DWORD last_sanity_check = start_time; 4901 ULARGE_INTEGER sanity_offset, sanity_cb; 4902 4903 sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST; 4904 sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1; 4905 4906 do 4907 { 4908 hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported); 4909 4910 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) 4911 { 4912 DWORD current_time = GetTickCount(); 4913 if (current_time - start_time >= 20000) 4914 { 4915 /* timeout */ 4916 break; 4917 } 4918 if (current_time - last_sanity_check >= 500) 4919 { 4920 /* Any storage implementation with the file open in a 4921 * shared mode should not lock these bytes for writing. However, 4922 * some programs (LibreOffice Writer) will keep ALL bytes locked 4923 * when opening in exclusive mode. We can use a read lock to 4924 * detect this case early, and not hang a full 20 seconds. 4925 * 4926 * This can collide with another attempt to open the file in 4927 * exclusive mode, but it's unlikely, and someone would fail anyway. */ 4928 hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL); 4929 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) 4930 break; 4931 if (SUCCEEDED(hr)) 4932 { 4933 StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ); 4934 hr = STG_E_ACCESSDENIED; 4935 } 4936 4937 last_sanity_check = current_time; 4938 } 4939 Sleep(delay); 4940 if (delay < 150) delay++; 4941 } 4942 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION); 4943 4944 return hr; 4945 } 4946 4947 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write) 4948 { 4949 StorageImpl *This = (StorageImpl*)base; 4950 HRESULT hr; 4951 ULARGE_INTEGER offset, cb; 4952 4953 if (write) 4954 { 4955 /* Synchronous grab of second priority range, the commit lock, and the 4956 * lock-checking lock. */ 4957 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST; 4958 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1; 4959 } 4960 else 4961 { 4962 offset.QuadPart = RANGELOCK_COMMIT; 4963 cb.QuadPart = 1; 4964 } 4965 4966 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL); 4967 4968 return hr; 4969 } 4970 4971 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) 4972 { 4973 StorageImpl *This = (StorageImpl*)base; 4974 HRESULT hr; 4975 ULARGE_INTEGER offset, cb; 4976 4977 if (write) 4978 { 4979 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST; 4980 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1; 4981 } 4982 else 4983 { 4984 offset.QuadPart = RANGELOCK_COMMIT; 4985 cb.QuadPart = 1; 4986 } 4987 4988 hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); 4989 4990 return hr; 4991 } 4992 4993 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 4994 { 4995 StorageImpl *This = (StorageImpl*) iface; 4996 STATSTG statstg; 4997 HRESULT hr; 4998 4999 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0); 5000 5001 *result = statstg.pwcsName; 5002 5003 return hr; 5004 } 5005 5006 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start, 5007 ULONG end, HRESULT fail_hr) 5008 { 5009 HRESULT hr; 5010 ULARGE_INTEGER offset, cb; 5011 5012 offset.QuadPart = start; 5013 cb.QuadPart = 1 + end - start; 5014 5015 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); 5016 if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); 5017 5018 if (FAILED(hr)) 5019 return fail_hr; 5020 else 5021 return S_OK; 5022 } 5023 5024 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end) 5025 { 5026 HRESULT hr=S_OK; 5027 int i, j; 5028 ULARGE_INTEGER offset, cb; 5029 5030 cb.QuadPart = 1; 5031 5032 for (i=start; i<=end; i++) 5033 { 5034 offset.QuadPart = i; 5035 hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); 5036 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION) 5037 break; 5038 } 5039 5040 if (SUCCEEDED(hr)) 5041 { 5042 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++) 5043 { 5044 if (This->locked_bytes[j] == 0) 5045 { 5046 This->locked_bytes[j] = i; 5047 break; 5048 } 5049 } 5050 } 5051 5052 return hr; 5053 } 5054 5055 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) 5056 { 5057 HRESULT hr; 5058 ULARGE_INTEGER offset; 5059 ULARGE_INTEGER cb; 5060 DWORD share_mode = STGM_SHARE_MODE(openFlags); 5061 BOOL supported; 5062 5063 if (openFlags & STGM_NOSNAPSHOT) 5064 { 5065 /* STGM_NOSNAPSHOT implies deny write */ 5066 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE; 5067 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE; 5068 } 5069 5070 /* Wrap all other locking inside a single lock so we can check ranges safely */ 5071 offset.QuadPart = RANGELOCK_CHECKLOCKS; 5072 cb.QuadPart = 1; 5073 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported); 5074 5075 /* If the ILockBytes doesn't support locking that's ok. */ 5076 if (!supported) return S_OK; 5077 else if (FAILED(hr)) return hr; 5078 5079 hr = S_OK; 5080 5081 /* First check for any conflicting locks. */ 5082 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY) 5083 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION); 5084 5085 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) 5086 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION); 5087 5088 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) 5089 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION); 5090 5091 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE)) 5092 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION); 5093 5094 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE)) 5095 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION); 5096 5097 if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE) 5098 { 5099 hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION); 5100 5101 if (SUCCEEDED(hr)) 5102 hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION); 5103 } 5104 5105 /* Then grab our locks. */ 5106 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY) 5107 { 5108 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST); 5109 if (SUCCEEDED(hr)) 5110 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST); 5111 } 5112 5113 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE)) 5114 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST); 5115 5116 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ)) 5117 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST); 5118 5119 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE)) 5120 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST); 5121 5122 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE)) 5123 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST); 5124 5125 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT) 5126 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST); 5127 5128 offset.QuadPart = RANGELOCK_CHECKLOCKS; 5129 cb.QuadPart = 1; 5130 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); 5131 5132 return hr; 5133 } 5134 5135 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage) 5136 { 5137 StorageImpl *This = (StorageImpl*)storage; 5138 int i; 5139 HRESULT hr; 5140 TRACE("(%p)\n", This); 5141 5142 hr = BlockChainStream_Flush(This->smallBlockRootChain); 5143 5144 if (SUCCEEDED(hr)) 5145 hr = BlockChainStream_Flush(This->rootBlockChain); 5146 5147 if (SUCCEEDED(hr)) 5148 hr = BlockChainStream_Flush(This->smallBlockDepotChain); 5149 5150 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++) 5151 if (This->blockChainCache[i]) 5152 hr = BlockChainStream_Flush(This->blockChainCache[i]); 5153 5154 if (SUCCEEDED(hr)) 5155 hr = ILockBytes_Flush(This->lockBytes); 5156 5157 return hr; 5158 } 5159 5160 static void StorageImpl_Invalidate(StorageBaseImpl* iface) 5161 { 5162 StorageImpl *This = (StorageImpl*) iface; 5163 5164 StorageBaseImpl_DeleteAll(&This->base); 5165 5166 This->base.reverted = TRUE; 5167 } 5168 5169 static void StorageImpl_Destroy(StorageBaseImpl* iface) 5170 { 5171 StorageImpl *This = (StorageImpl*) iface; 5172 int i; 5173 TRACE("(%p)\n", This); 5174 5175 StorageImpl_Flush(iface); 5176 5177 StorageImpl_Invalidate(iface); 5178 5179 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations); 5180 5181 BlockChainStream_Destroy(This->smallBlockRootChain); 5182 BlockChainStream_Destroy(This->rootBlockChain); 5183 BlockChainStream_Destroy(This->smallBlockDepotChain); 5184 5185 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++) 5186 BlockChainStream_Destroy(This->blockChainCache[i]); 5187 5188 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++) 5189 { 5190 ULARGE_INTEGER offset, cb; 5191 cb.QuadPart = 1; 5192 if (This->locked_bytes[i] != 0) 5193 { 5194 offset.QuadPart = This->locked_bytes[i]; 5195 StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); 5196 } 5197 } 5198 5199 if (This->lockBytes) 5200 ILockBytes_Release(This->lockBytes); 5201 HeapFree(GetProcessHeap(), 0, This); 5202 } 5203 5204 5205 static const StorageBaseImplVtbl StorageImpl_BaseVtbl = 5206 { 5207 StorageImpl_Destroy, 5208 StorageImpl_Invalidate, 5209 StorageImpl_Flush, 5210 StorageImpl_GetFilename, 5211 StorageImpl_CreateDirEntry, 5212 StorageImpl_BaseWriteDirEntry, 5213 StorageImpl_BaseReadDirEntry, 5214 StorageImpl_DestroyDirEntry, 5215 StorageImpl_StreamReadAt, 5216 StorageImpl_StreamWriteAt, 5217 StorageImpl_StreamSetSize, 5218 StorageImpl_StreamLink, 5219 StorageImpl_GetTransactionSig, 5220 StorageImpl_SetTransactionSig, 5221 StorageImpl_LockTransaction, 5222 StorageImpl_UnlockTransaction 5223 }; 5224 5225 5226 /* 5227 * Virtual function table for the IStorageBaseImpl class. 5228 */ 5229 static const IStorageVtbl StorageImpl_Vtbl = 5230 { 5231 StorageBaseImpl_QueryInterface, 5232 StorageBaseImpl_AddRef, 5233 StorageBaseImpl_Release, 5234 StorageBaseImpl_CreateStream, 5235 StorageBaseImpl_OpenStream, 5236 StorageBaseImpl_CreateStorage, 5237 StorageBaseImpl_OpenStorage, 5238 StorageBaseImpl_CopyTo, 5239 StorageBaseImpl_MoveElementTo, 5240 StorageBaseImpl_Commit, 5241 StorageBaseImpl_Revert, 5242 StorageBaseImpl_EnumElements, 5243 StorageBaseImpl_DestroyElement, 5244 StorageBaseImpl_RenameElement, 5245 StorageBaseImpl_SetElementTimes, 5246 StorageBaseImpl_SetClass, 5247 StorageBaseImpl_SetStateBits, 5248 StorageBaseImpl_Stat 5249 }; 5250 5251 static HRESULT StorageImpl_Construct( 5252 HANDLE hFile, 5253 LPCOLESTR pwcsName, 5254 ILockBytes* pLkbyt, 5255 DWORD openFlags, 5256 BOOL fileBased, 5257 BOOL create, 5258 ULONG sector_size, 5259 StorageImpl** result) 5260 { 5261 StorageImpl* This; 5262 HRESULT hr = S_OK; 5263 STATSTG stat; 5264 5265 if ( FAILED( validateSTGM(openFlags) )) 5266 return STG_E_INVALIDFLAG; 5267 5268 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl)); 5269 if (!This) 5270 return E_OUTOFMEMORY; 5271 5272 memset(This, 0, sizeof(StorageImpl)); 5273 5274 list_init(&This->base.strmHead); 5275 5276 list_init(&This->base.storageHead); 5277 5278 This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl; 5279 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl; 5280 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl; 5281 This->base.baseVtbl = &StorageImpl_BaseVtbl; 5282 This->base.openFlags = (openFlags & ~STGM_CREATE); 5283 This->base.ref = 1; 5284 This->base.create = create; 5285 5286 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE)) 5287 This->base.lockingrole = SWMR_Writer; 5288 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE)) 5289 This->base.lockingrole = SWMR_Reader; 5290 else 5291 This->base.lockingrole = SWMR_None; 5292 5293 This->base.reverted = FALSE; 5294 5295 /* 5296 * Initialize the big block cache. 5297 */ 5298 This->bigBlockSize = sector_size; 5299 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE; 5300 if (hFile) 5301 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes); 5302 else 5303 { 5304 This->lockBytes = pLkbyt; 5305 ILockBytes_AddRef(pLkbyt); 5306 } 5307 5308 if (SUCCEEDED(hr)) 5309 hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME); 5310 5311 if (SUCCEEDED(hr)) 5312 { 5313 This->locks_supported = stat.grfLocksSupported; 5314 if (!hFile) 5315 /* Don't try to use wine-internal locking flag with custom ILockBytes */ 5316 This->locks_supported &= ~WINE_LOCK_READ; 5317 5318 hr = StorageImpl_GrabLocks(This, openFlags); 5319 } 5320 5321 if (SUCCEEDED(hr)) 5322 hr = StorageImpl_Refresh(This, TRUE, create); 5323 5324 if (FAILED(hr)) 5325 { 5326 IStorage_Release(&This->base.IStorage_iface); 5327 *result = NULL; 5328 } 5329 else 5330 { 5331 StorageImpl_Flush(&This->base); 5332 *result = This; 5333 } 5334 5335 return hr; 5336 } 5337 5338 5339 /************************************************************************ 5340 * StorageInternalImpl implementation 5341 ***********************************************************************/ 5342 5343 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base ) 5344 { 5345 StorageInternalImpl* This = (StorageInternalImpl*) base; 5346 5347 if (!This->base.reverted) 5348 { 5349 TRACE("Storage invalidated (stg=%p)\n", This); 5350 5351 This->base.reverted = TRUE; 5352 5353 This->parentStorage = NULL; 5354 5355 StorageBaseImpl_DeleteAll(&This->base); 5356 5357 list_remove(&This->ParentListEntry); 5358 } 5359 } 5360 5361 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface) 5362 { 5363 StorageInternalImpl* This = (StorageInternalImpl*) iface; 5364 5365 StorageInternalImpl_Invalidate(&This->base); 5366 5367 HeapFree(GetProcessHeap(), 0, This); 5368 } 5369 5370 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface) 5371 { 5372 StorageInternalImpl* This = (StorageInternalImpl*) iface; 5373 5374 return StorageBaseImpl_Flush(This->parentStorage); 5375 } 5376 5377 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 5378 { 5379 StorageInternalImpl* This = (StorageInternalImpl*) iface; 5380 5381 return StorageBaseImpl_GetFilename(This->parentStorage, result); 5382 } 5383 5384 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base, 5385 const DirEntry *newData, DirRef *index) 5386 { 5387 StorageInternalImpl* This = (StorageInternalImpl*) base; 5388 5389 return StorageBaseImpl_CreateDirEntry(This->parentStorage, 5390 newData, index); 5391 } 5392 5393 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base, 5394 DirRef index, const DirEntry *data) 5395 { 5396 StorageInternalImpl* This = (StorageInternalImpl*) base; 5397 5398 return StorageBaseImpl_WriteDirEntry(This->parentStorage, 5399 index, data); 5400 } 5401 5402 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base, 5403 DirRef index, DirEntry *data) 5404 { 5405 StorageInternalImpl* This = (StorageInternalImpl*) base; 5406 5407 return StorageBaseImpl_ReadDirEntry(This->parentStorage, 5408 index, data); 5409 } 5410 5411 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base, 5412 DirRef index) 5413 { 5414 StorageInternalImpl* This = (StorageInternalImpl*) base; 5415 5416 return StorageBaseImpl_DestroyDirEntry(This->parentStorage, 5417 index); 5418 } 5419 5420 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base, 5421 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 5422 { 5423 StorageInternalImpl* This = (StorageInternalImpl*) base; 5424 5425 return StorageBaseImpl_StreamReadAt(This->parentStorage, 5426 index, offset, size, buffer, bytesRead); 5427 } 5428 5429 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base, 5430 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 5431 { 5432 StorageInternalImpl* This = (StorageInternalImpl*) base; 5433 5434 return StorageBaseImpl_StreamWriteAt(This->parentStorage, 5435 index, offset, size, buffer, bytesWritten); 5436 } 5437 5438 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base, 5439 DirRef index, ULARGE_INTEGER newsize) 5440 { 5441 StorageInternalImpl* This = (StorageInternalImpl*) base; 5442 5443 return StorageBaseImpl_StreamSetSize(This->parentStorage, 5444 index, newsize); 5445 } 5446 5447 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base, 5448 DirRef dst, DirRef src) 5449 { 5450 StorageInternalImpl* This = (StorageInternalImpl*) base; 5451 5452 return StorageBaseImpl_StreamLink(This->parentStorage, 5453 dst, src); 5454 } 5455 5456 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base, 5457 ULONG* result, BOOL refresh) 5458 { 5459 return E_NOTIMPL; 5460 } 5461 5462 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base, 5463 ULONG value) 5464 { 5465 return E_NOTIMPL; 5466 } 5467 5468 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write) 5469 { 5470 return E_NOTIMPL; 5471 } 5472 5473 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) 5474 { 5475 return E_NOTIMPL; 5476 } 5477 5478 /****************************************************************************** 5479 ** 5480 ** StorageInternalImpl_Commit 5481 ** 5482 */ 5483 static HRESULT WINAPI StorageInternalImpl_Commit( 5484 IStorage* iface, 5485 DWORD grfCommitFlags) /* [in] */ 5486 { 5487 StorageBaseImpl* This = impl_from_IStorage(iface); 5488 TRACE("(%p,%x)\n", iface, grfCommitFlags); 5489 return StorageBaseImpl_Flush(This); 5490 } 5491 5492 /****************************************************************************** 5493 ** 5494 ** StorageInternalImpl_Revert 5495 ** 5496 */ 5497 static HRESULT WINAPI StorageInternalImpl_Revert( 5498 IStorage* iface) 5499 { 5500 FIXME("(%p): stub\n", iface); 5501 return S_OK; 5502 } 5503 5504 /* 5505 * Virtual function table for the StorageInternalImpl class. 5506 */ 5507 static const IStorageVtbl StorageInternalImpl_Vtbl = 5508 { 5509 StorageBaseImpl_QueryInterface, 5510 StorageBaseImpl_AddRef, 5511 StorageBaseImpl_Release, 5512 StorageBaseImpl_CreateStream, 5513 StorageBaseImpl_OpenStream, 5514 StorageBaseImpl_CreateStorage, 5515 StorageBaseImpl_OpenStorage, 5516 StorageBaseImpl_CopyTo, 5517 StorageBaseImpl_MoveElementTo, 5518 StorageInternalImpl_Commit, 5519 StorageInternalImpl_Revert, 5520 StorageBaseImpl_EnumElements, 5521 StorageBaseImpl_DestroyElement, 5522 StorageBaseImpl_RenameElement, 5523 StorageBaseImpl_SetElementTimes, 5524 StorageBaseImpl_SetClass, 5525 StorageBaseImpl_SetStateBits, 5526 StorageBaseImpl_Stat 5527 }; 5528 5529 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl = 5530 { 5531 StorageInternalImpl_Destroy, 5532 StorageInternalImpl_Invalidate, 5533 StorageInternalImpl_Flush, 5534 StorageInternalImpl_GetFilename, 5535 StorageInternalImpl_CreateDirEntry, 5536 StorageInternalImpl_WriteDirEntry, 5537 StorageInternalImpl_ReadDirEntry, 5538 StorageInternalImpl_DestroyDirEntry, 5539 StorageInternalImpl_StreamReadAt, 5540 StorageInternalImpl_StreamWriteAt, 5541 StorageInternalImpl_StreamSetSize, 5542 StorageInternalImpl_StreamLink, 5543 StorageInternalImpl_GetTransactionSig, 5544 StorageInternalImpl_SetTransactionSig, 5545 StorageInternalImpl_LockTransaction, 5546 StorageInternalImpl_UnlockTransaction 5547 }; 5548 5549 static StorageInternalImpl* StorageInternalImpl_Construct( 5550 StorageBaseImpl* parentStorage, 5551 DWORD openFlags, 5552 DirRef storageDirEntry) 5553 { 5554 StorageInternalImpl* newStorage; 5555 5556 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl)); 5557 5558 if (newStorage!=0) 5559 { 5560 list_init(&newStorage->base.strmHead); 5561 5562 list_init(&newStorage->base.storageHead); 5563 5564 /* 5565 * Initialize the virtual function table. 5566 */ 5567 newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl; 5568 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl; 5569 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl; 5570 newStorage->base.openFlags = (openFlags & ~STGM_CREATE); 5571 5572 newStorage->base.reverted = FALSE; 5573 5574 newStorage->base.ref = 1; 5575 5576 newStorage->parentStorage = parentStorage; 5577 5578 /* 5579 * Keep a reference to the directory entry of this storage 5580 */ 5581 newStorage->base.storageDirEntry = storageDirEntry; 5582 5583 newStorage->base.create = FALSE; 5584 5585 return newStorage; 5586 } 5587 5588 return 0; 5589 } 5590 5591 5592 /************************************************************************ 5593 * TransactedSnapshotImpl implementation 5594 ***********************************************************************/ 5595 5596 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This) 5597 { 5598 DirRef result=This->firstFreeEntry; 5599 5600 while (result < This->entries_size && This->entries[result].inuse) 5601 result++; 5602 5603 if (result == This->entries_size) 5604 { 5605 ULONG new_size = This->entries_size * 2; 5606 TransactedDirEntry *new_entries; 5607 5608 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size); 5609 if (!new_entries) return DIRENTRY_NULL; 5610 5611 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size); 5612 HeapFree(GetProcessHeap(), 0, This->entries); 5613 5614 This->entries = new_entries; 5615 This->entries_size = new_size; 5616 } 5617 5618 This->entries[result].inuse = TRUE; 5619 5620 This->firstFreeEntry = result+1; 5621 5622 return result; 5623 } 5624 5625 static DirRef TransactedSnapshotImpl_CreateStubEntry( 5626 TransactedSnapshotImpl *This, DirRef parentEntryRef) 5627 { 5628 DirRef stubEntryRef; 5629 TransactedDirEntry *entry; 5630 5631 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This); 5632 5633 if (stubEntryRef != DIRENTRY_NULL) 5634 { 5635 entry = &This->entries[stubEntryRef]; 5636 5637 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef; 5638 5639 entry->read = FALSE; 5640 } 5641 5642 return stubEntryRef; 5643 } 5644 5645 static HRESULT TransactedSnapshotImpl_EnsureReadEntry( 5646 TransactedSnapshotImpl *This, DirRef entry) 5647 { 5648 HRESULT hr=S_OK; 5649 DirEntry data; 5650 5651 if (!This->entries[entry].read) 5652 { 5653 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, 5654 This->entries[entry].transactedParentEntry, 5655 &data); 5656 5657 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL) 5658 { 5659 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild); 5660 5661 if (data.leftChild == DIRENTRY_NULL) 5662 hr = E_OUTOFMEMORY; 5663 } 5664 5665 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL) 5666 { 5667 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild); 5668 5669 if (data.rightChild == DIRENTRY_NULL) 5670 hr = E_OUTOFMEMORY; 5671 } 5672 5673 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL) 5674 { 5675 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry); 5676 5677 if (data.dirRootEntry == DIRENTRY_NULL) 5678 hr = E_OUTOFMEMORY; 5679 } 5680 5681 if (SUCCEEDED(hr)) 5682 { 5683 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry)); 5684 This->entries[entry].read = TRUE; 5685 } 5686 } 5687 5688 return hr; 5689 } 5690 5691 static HRESULT TransactedSnapshotImpl_MakeStreamDirty( 5692 TransactedSnapshotImpl *This, DirRef entry) 5693 { 5694 HRESULT hr = S_OK; 5695 5696 if (!This->entries[entry].stream_dirty) 5697 { 5698 DirEntry new_entrydata; 5699 5700 memset(&new_entrydata, 0, sizeof(DirEntry)); 5701 new_entrydata.name[0] = 'S'; 5702 new_entrydata.sizeOfNameString = 1; 5703 new_entrydata.stgType = STGTY_STREAM; 5704 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN; 5705 new_entrydata.leftChild = DIRENTRY_NULL; 5706 new_entrydata.rightChild = DIRENTRY_NULL; 5707 new_entrydata.dirRootEntry = DIRENTRY_NULL; 5708 5709 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata, 5710 &This->entries[entry].stream_entry); 5711 5712 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL) 5713 { 5714 hr = StorageBaseImpl_CopyStream( 5715 This->scratch, This->entries[entry].stream_entry, 5716 This->transactedParent, This->entries[entry].transactedParentEntry); 5717 5718 if (FAILED(hr)) 5719 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry); 5720 } 5721 5722 if (SUCCEEDED(hr)) 5723 This->entries[entry].stream_dirty = TRUE; 5724 5725 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL) 5726 { 5727 /* Since this entry is modified, and we aren't using its stream data, we 5728 * no longer care about the original entry. */ 5729 DirRef delete_ref; 5730 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry); 5731 5732 if (delete_ref != DIRENTRY_NULL) 5733 This->entries[delete_ref].deleted = TRUE; 5734 5735 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL; 5736 } 5737 } 5738 5739 return hr; 5740 } 5741 5742 /* Find the first entry in a depth-first traversal. */ 5743 static DirRef TransactedSnapshotImpl_FindFirstChild( 5744 TransactedSnapshotImpl* This, DirRef parent) 5745 { 5746 DirRef cursor, prev; 5747 TransactedDirEntry *entry; 5748 5749 cursor = parent; 5750 entry = &This->entries[cursor]; 5751 while (entry->read) 5752 { 5753 if (entry->data.leftChild != DIRENTRY_NULL) 5754 { 5755 prev = cursor; 5756 cursor = entry->data.leftChild; 5757 entry = &This->entries[cursor]; 5758 entry->parent = prev; 5759 } 5760 else if (entry->data.rightChild != DIRENTRY_NULL) 5761 { 5762 prev = cursor; 5763 cursor = entry->data.rightChild; 5764 entry = &This->entries[cursor]; 5765 entry->parent = prev; 5766 } 5767 else if (entry->data.dirRootEntry != DIRENTRY_NULL) 5768 { 5769 prev = cursor; 5770 cursor = entry->data.dirRootEntry; 5771 entry = &This->entries[cursor]; 5772 entry->parent = prev; 5773 } 5774 else 5775 break; 5776 } 5777 5778 return cursor; 5779 } 5780 5781 /* Find the next entry in a depth-first traversal. */ 5782 static DirRef TransactedSnapshotImpl_FindNextChild( 5783 TransactedSnapshotImpl* This, DirRef current) 5784 { 5785 DirRef parent; 5786 TransactedDirEntry *parent_entry; 5787 5788 parent = This->entries[current].parent; 5789 parent_entry = &This->entries[parent]; 5790 5791 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current) 5792 { 5793 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL) 5794 { 5795 This->entries[parent_entry->data.rightChild].parent = parent; 5796 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild); 5797 } 5798 5799 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL) 5800 { 5801 This->entries[parent_entry->data.dirRootEntry].parent = parent; 5802 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry); 5803 } 5804 } 5805 5806 return parent; 5807 } 5808 5809 /* Return TRUE if we've made a copy of this entry for committing to the parent. */ 5810 static inline BOOL TransactedSnapshotImpl_MadeCopy( 5811 TransactedSnapshotImpl* This, DirRef entry) 5812 { 5813 return entry != DIRENTRY_NULL && 5814 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry; 5815 } 5816 5817 /* Destroy the entries created by CopyTree. */ 5818 static void TransactedSnapshotImpl_DestroyTemporaryCopy( 5819 TransactedSnapshotImpl* This, DirRef stop) 5820 { 5821 DirRef cursor; 5822 TransactedDirEntry *entry; 5823 ULARGE_INTEGER zero; 5824 5825 zero.QuadPart = 0; 5826 5827 if (!This->entries[This->base.storageDirEntry].read) 5828 return; 5829 5830 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry; 5831 5832 if (cursor == DIRENTRY_NULL) 5833 return; 5834 5835 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor); 5836 5837 while (cursor != DIRENTRY_NULL && cursor != stop) 5838 { 5839 if (TransactedSnapshotImpl_MadeCopy(This, cursor)) 5840 { 5841 entry = &This->entries[cursor]; 5842 5843 if (entry->stream_dirty) 5844 StorageBaseImpl_StreamSetSize(This->transactedParent, 5845 entry->newTransactedParentEntry, zero); 5846 5847 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 5848 entry->newTransactedParentEntry); 5849 5850 entry->newTransactedParentEntry = entry->transactedParentEntry; 5851 } 5852 5853 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 5854 } 5855 } 5856 5857 /* Make a copy of our edited tree that we can use in the parent. */ 5858 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This) 5859 { 5860 DirRef cursor; 5861 TransactedDirEntry *entry; 5862 HRESULT hr = S_OK; 5863 5864 cursor = This->base.storageDirEntry; 5865 entry = &This->entries[cursor]; 5866 entry->parent = DIRENTRY_NULL; 5867 entry->newTransactedParentEntry = entry->transactedParentEntry; 5868 5869 if (entry->data.dirRootEntry == DIRENTRY_NULL) 5870 return S_OK; 5871 5872 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL; 5873 5874 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry); 5875 entry = &This->entries[cursor]; 5876 5877 while (cursor != DIRENTRY_NULL) 5878 { 5879 /* Make a copy of this entry in the transacted parent. */ 5880 if (!entry->read || 5881 (!entry->dirty && !entry->stream_dirty && 5882 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) && 5883 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) && 5884 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry))) 5885 entry->newTransactedParentEntry = entry->transactedParentEntry; 5886 else 5887 { 5888 DirEntry newData; 5889 5890 memcpy(&newData, &entry->data, sizeof(DirEntry)); 5891 5892 newData.size.QuadPart = 0; 5893 newData.startingBlock = BLOCK_END_OF_CHAIN; 5894 5895 if (newData.leftChild != DIRENTRY_NULL) 5896 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry; 5897 5898 if (newData.rightChild != DIRENTRY_NULL) 5899 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry; 5900 5901 if (newData.dirRootEntry != DIRENTRY_NULL) 5902 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry; 5903 5904 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData, 5905 &entry->newTransactedParentEntry); 5906 if (FAILED(hr)) 5907 { 5908 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); 5909 return hr; 5910 } 5911 5912 if (entry->stream_dirty) 5913 { 5914 hr = StorageBaseImpl_CopyStream( 5915 This->transactedParent, entry->newTransactedParentEntry, 5916 This->scratch, entry->stream_entry); 5917 } 5918 else if (entry->data.size.QuadPart) 5919 { 5920 hr = StorageBaseImpl_StreamLink( 5921 This->transactedParent, entry->newTransactedParentEntry, 5922 entry->transactedParentEntry); 5923 } 5924 5925 if (FAILED(hr)) 5926 { 5927 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 5928 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor); 5929 return hr; 5930 } 5931 } 5932 5933 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor); 5934 entry = &This->entries[cursor]; 5935 } 5936 5937 return hr; 5938 } 5939 5940 static HRESULT WINAPI TransactedSnapshotImpl_Commit( 5941 IStorage* iface, 5942 DWORD grfCommitFlags) /* [in] */ 5943 { 5944 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface); 5945 TransactedDirEntry *root_entry; 5946 DirRef i, dir_root_ref; 5947 DirEntry data; 5948 ULARGE_INTEGER zero; 5949 HRESULT hr; 5950 ULONG transactionSig; 5951 5952 zero.QuadPart = 0; 5953 5954 TRACE("(%p,%x)\n", iface, grfCommitFlags); 5955 5956 /* Cannot commit a read-only transacted storage */ 5957 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ ) 5958 return STG_E_ACCESSDENIED; 5959 5960 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE); 5961 if (hr == E_NOTIMPL) hr = S_OK; 5962 if (SUCCEEDED(hr)) 5963 { 5964 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE); 5965 if (SUCCEEDED(hr)) 5966 { 5967 if (transactionSig != This->lastTransactionSig) 5968 { 5969 ERR("file was externally modified\n"); 5970 hr = STG_E_NOTCURRENT; 5971 } 5972 5973 if (SUCCEEDED(hr)) 5974 { 5975 This->lastTransactionSig = transactionSig+1; 5976 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig); 5977 } 5978 } 5979 else if (hr == E_NOTIMPL) 5980 hr = S_OK; 5981 5982 if (FAILED(hr)) goto end; 5983 5984 /* To prevent data loss, we create the new structure in the file before we 5985 * delete the old one, so that in case of errors the old data is intact. We 5986 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be 5987 * needed in the rare situation where we have just enough free disk space to 5988 * overwrite the existing data. */ 5989 5990 root_entry = &This->entries[This->base.storageDirEntry]; 5991 5992 if (!root_entry->read) 5993 goto end; 5994 5995 hr = TransactedSnapshotImpl_CopyTree(This); 5996 if (FAILED(hr)) goto end; 5997 5998 if (root_entry->data.dirRootEntry == DIRENTRY_NULL) 5999 dir_root_ref = DIRENTRY_NULL; 6000 else 6001 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry; 6002 6003 hr = StorageBaseImpl_Flush(This->transactedParent); 6004 6005 /* Update the storage to use the new data in one step. */ 6006 if (SUCCEEDED(hr)) 6007 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, 6008 root_entry->transactedParentEntry, &data); 6009 6010 if (SUCCEEDED(hr)) 6011 { 6012 data.dirRootEntry = dir_root_ref; 6013 data.clsid = root_entry->data.clsid; 6014 data.ctime = root_entry->data.ctime; 6015 data.mtime = root_entry->data.mtime; 6016 6017 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, 6018 root_entry->transactedParentEntry, &data); 6019 } 6020 6021 /* Try to flush after updating the root storage, but if the flush fails, keep 6022 * going, on the theory that it'll either succeed later or the subsequent 6023 * writes will fail. */ 6024 StorageBaseImpl_Flush(This->transactedParent); 6025 6026 if (SUCCEEDED(hr)) 6027 { 6028 /* Destroy the old now-orphaned data. */ 6029 for (i=0; i<This->entries_size; i++) 6030 { 6031 TransactedDirEntry *entry = &This->entries[i]; 6032 if (entry->inuse) 6033 { 6034 if (entry->deleted) 6035 { 6036 StorageBaseImpl_StreamSetSize(This->transactedParent, 6037 entry->transactedParentEntry, zero); 6038 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 6039 entry->transactedParentEntry); 6040 memset(entry, 0, sizeof(TransactedDirEntry)); 6041 This->firstFreeEntry = min(i, This->firstFreeEntry); 6042 } 6043 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry) 6044 { 6045 if (entry->transactedParentEntry != DIRENTRY_NULL) 6046 StorageBaseImpl_DestroyDirEntry(This->transactedParent, 6047 entry->transactedParentEntry); 6048 if (entry->stream_dirty) 6049 { 6050 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero); 6051 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry); 6052 entry->stream_dirty = FALSE; 6053 } 6054 entry->dirty = FALSE; 6055 entry->transactedParentEntry = entry->newTransactedParentEntry; 6056 } 6057 } 6058 } 6059 } 6060 else 6061 { 6062 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL); 6063 } 6064 6065 if (SUCCEEDED(hr)) 6066 hr = StorageBaseImpl_Flush(This->transactedParent); 6067 end: 6068 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE); 6069 } 6070 6071 TRACE("<-- %08x\n", hr); 6072 return hr; 6073 } 6074 6075 static HRESULT WINAPI TransactedSnapshotImpl_Revert( 6076 IStorage* iface) 6077 { 6078 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface); 6079 ULARGE_INTEGER zero; 6080 ULONG i; 6081 6082 TRACE("(%p)\n", iface); 6083 6084 /* Destroy the open objects. */ 6085 StorageBaseImpl_DeleteAll(&This->base); 6086 6087 /* Clear out the scratch file. */ 6088 zero.QuadPart = 0; 6089 for (i=0; i<This->entries_size; i++) 6090 { 6091 if (This->entries[i].stream_dirty) 6092 { 6093 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry, 6094 zero); 6095 6096 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry); 6097 } 6098 } 6099 6100 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size); 6101 6102 This->firstFreeEntry = 0; 6103 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry); 6104 6105 return S_OK; 6106 } 6107 6108 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This) 6109 { 6110 if (!This->reverted) 6111 { 6112 TRACE("Storage invalidated (stg=%p)\n", This); 6113 6114 This->reverted = TRUE; 6115 6116 StorageBaseImpl_DeleteAll(This); 6117 } 6118 } 6119 6120 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) 6121 { 6122 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 6123 6124 IStorage_Revert(&This->base.IStorage_iface); 6125 IStorage_Release(&This->transactedParent->IStorage_iface); 6126 IStorage_Release(&This->scratch->IStorage_iface); 6127 HeapFree(GetProcessHeap(), 0, This->entries); 6128 HeapFree(GetProcessHeap(), 0, This); 6129 } 6130 6131 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface) 6132 { 6133 /* We only need to flush when committing. */ 6134 return S_OK; 6135 } 6136 6137 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 6138 { 6139 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface; 6140 6141 return StorageBaseImpl_GetFilename(This->transactedParent, result); 6142 } 6143 6144 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base, 6145 const DirEntry *newData, DirRef *index) 6146 { 6147 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6148 DirRef new_ref; 6149 TransactedDirEntry *new_entry; 6150 6151 new_ref = TransactedSnapshotImpl_FindFreeEntry(This); 6152 if (new_ref == DIRENTRY_NULL) 6153 return E_OUTOFMEMORY; 6154 6155 new_entry = &This->entries[new_ref]; 6156 6157 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL; 6158 new_entry->read = TRUE; 6159 new_entry->dirty = TRUE; 6160 memcpy(&new_entry->data, newData, sizeof(DirEntry)); 6161 6162 *index = new_ref; 6163 6164 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index); 6165 6166 return S_OK; 6167 } 6168 6169 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base, 6170 DirRef index, const DirEntry *data) 6171 { 6172 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6173 HRESULT hr; 6174 6175 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); 6176 6177 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 6178 if (FAILED(hr)) 6179 { 6180 TRACE("<-- %08x\n", hr); 6181 return hr; 6182 } 6183 6184 memcpy(&This->entries[index].data, data, sizeof(DirEntry)); 6185 6186 if (index != This->base.storageDirEntry) 6187 { 6188 This->entries[index].dirty = TRUE; 6189 6190 if (data->size.QuadPart == 0 && 6191 This->entries[index].transactedParentEntry != DIRENTRY_NULL) 6192 { 6193 /* Since this entry is modified, and we aren't using its stream data, we 6194 * no longer care about the original entry. */ 6195 DirRef delete_ref; 6196 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry); 6197 6198 if (delete_ref != DIRENTRY_NULL) 6199 This->entries[delete_ref].deleted = TRUE; 6200 6201 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; 6202 } 6203 } 6204 TRACE("<-- S_OK\n"); 6205 return S_OK; 6206 } 6207 6208 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, 6209 DirRef index, DirEntry *data) 6210 { 6211 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6212 HRESULT hr; 6213 6214 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 6215 if (FAILED(hr)) 6216 { 6217 TRACE("<-- %08x\n", hr); 6218 return hr; 6219 } 6220 6221 memcpy(data, &This->entries[index].data, sizeof(DirEntry)); 6222 6223 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry); 6224 6225 return S_OK; 6226 } 6227 6228 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, 6229 DirRef index) 6230 { 6231 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6232 6233 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL || 6234 This->entries[index].data.size.QuadPart != 0) 6235 { 6236 /* If we deleted this entry while it has stream data. We must have left the 6237 * data because some other entry is using it, and we need to leave the 6238 * original entry alone. */ 6239 memset(&This->entries[index], 0, sizeof(TransactedDirEntry)); 6240 This->firstFreeEntry = min(index, This->firstFreeEntry); 6241 } 6242 else 6243 { 6244 This->entries[index].deleted = TRUE; 6245 } 6246 6247 return S_OK; 6248 } 6249 6250 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base, 6251 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 6252 { 6253 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6254 6255 if (This->entries[index].stream_dirty) 6256 { 6257 return StorageBaseImpl_StreamReadAt(This->scratch, 6258 This->entries[index].stream_entry, offset, size, buffer, bytesRead); 6259 } 6260 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL) 6261 { 6262 /* This stream doesn't live in the parent, and we haven't allocated storage 6263 * for it yet */ 6264 *bytesRead = 0; 6265 return S_OK; 6266 } 6267 else 6268 { 6269 return StorageBaseImpl_StreamReadAt(This->transactedParent, 6270 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead); 6271 } 6272 } 6273 6274 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base, 6275 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 6276 { 6277 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6278 HRESULT hr; 6279 6280 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 6281 if (FAILED(hr)) 6282 { 6283 TRACE("<-- %08x\n", hr); 6284 return hr; 6285 } 6286 6287 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); 6288 if (FAILED(hr)) 6289 { 6290 TRACE("<-- %08x\n", hr); 6291 return hr; 6292 } 6293 6294 hr = StorageBaseImpl_StreamWriteAt(This->scratch, 6295 This->entries[index].stream_entry, offset, size, buffer, bytesWritten); 6296 6297 if (SUCCEEDED(hr) && size != 0) 6298 This->entries[index].data.size.QuadPart = max( 6299 This->entries[index].data.size.QuadPart, 6300 offset.QuadPart + size); 6301 6302 TRACE("<-- %08x\n", hr); 6303 return hr; 6304 } 6305 6306 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, 6307 DirRef index, ULARGE_INTEGER newsize) 6308 { 6309 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6310 HRESULT hr; 6311 6312 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index); 6313 if (FAILED(hr)) 6314 { 6315 TRACE("<-- %08x\n", hr); 6316 return hr; 6317 } 6318 6319 if (This->entries[index].data.size.QuadPart == newsize.QuadPart) 6320 return S_OK; 6321 6322 if (newsize.QuadPart == 0) 6323 { 6324 /* Destroy any parent references or entries in the scratch file. */ 6325 if (This->entries[index].stream_dirty) 6326 { 6327 ULARGE_INTEGER zero; 6328 zero.QuadPart = 0; 6329 StorageBaseImpl_StreamSetSize(This->scratch, 6330 This->entries[index].stream_entry, zero); 6331 StorageBaseImpl_DestroyDirEntry(This->scratch, 6332 This->entries[index].stream_entry); 6333 This->entries[index].stream_dirty = FALSE; 6334 } 6335 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL) 6336 { 6337 DirRef delete_ref; 6338 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry); 6339 6340 if (delete_ref != DIRENTRY_NULL) 6341 This->entries[delete_ref].deleted = TRUE; 6342 6343 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL; 6344 } 6345 } 6346 else 6347 { 6348 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index); 6349 if (FAILED(hr)) return hr; 6350 6351 hr = StorageBaseImpl_StreamSetSize(This->scratch, 6352 This->entries[index].stream_entry, newsize); 6353 } 6354 6355 if (SUCCEEDED(hr)) 6356 This->entries[index].data.size = newsize; 6357 6358 TRACE("<-- %08x\n", hr); 6359 return hr; 6360 } 6361 6362 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base, 6363 DirRef dst, DirRef src) 6364 { 6365 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; 6366 HRESULT hr; 6367 TransactedDirEntry *dst_entry, *src_entry; 6368 6369 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src); 6370 if (FAILED(hr)) 6371 { 6372 TRACE("<-- %08x\n", hr); 6373 return hr; 6374 } 6375 6376 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst); 6377 if (FAILED(hr)) 6378 { 6379 TRACE("<-- %08x\n", hr); 6380 return hr; 6381 } 6382 6383 dst_entry = &This->entries[dst]; 6384 src_entry = &This->entries[src]; 6385 6386 dst_entry->stream_dirty = src_entry->stream_dirty; 6387 dst_entry->stream_entry = src_entry->stream_entry; 6388 dst_entry->transactedParentEntry = src_entry->transactedParentEntry; 6389 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry; 6390 dst_entry->data.size = src_entry->data.size; 6391 6392 return S_OK; 6393 } 6394 6395 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base, 6396 ULONG* result, BOOL refresh) 6397 { 6398 return E_NOTIMPL; 6399 } 6400 6401 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base, 6402 ULONG value) 6403 { 6404 return E_NOTIMPL; 6405 } 6406 6407 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write) 6408 { 6409 return E_NOTIMPL; 6410 } 6411 6412 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) 6413 { 6414 return E_NOTIMPL; 6415 } 6416 6417 static const IStorageVtbl TransactedSnapshotImpl_Vtbl = 6418 { 6419 StorageBaseImpl_QueryInterface, 6420 StorageBaseImpl_AddRef, 6421 StorageBaseImpl_Release, 6422 StorageBaseImpl_CreateStream, 6423 StorageBaseImpl_OpenStream, 6424 StorageBaseImpl_CreateStorage, 6425 StorageBaseImpl_OpenStorage, 6426 StorageBaseImpl_CopyTo, 6427 StorageBaseImpl_MoveElementTo, 6428 TransactedSnapshotImpl_Commit, 6429 TransactedSnapshotImpl_Revert, 6430 StorageBaseImpl_EnumElements, 6431 StorageBaseImpl_DestroyElement, 6432 StorageBaseImpl_RenameElement, 6433 StorageBaseImpl_SetElementTimes, 6434 StorageBaseImpl_SetClass, 6435 StorageBaseImpl_SetStateBits, 6436 StorageBaseImpl_Stat 6437 }; 6438 6439 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl = 6440 { 6441 TransactedSnapshotImpl_Destroy, 6442 TransactedSnapshotImpl_Invalidate, 6443 TransactedSnapshotImpl_Flush, 6444 TransactedSnapshotImpl_GetFilename, 6445 TransactedSnapshotImpl_CreateDirEntry, 6446 TransactedSnapshotImpl_WriteDirEntry, 6447 TransactedSnapshotImpl_ReadDirEntry, 6448 TransactedSnapshotImpl_DestroyDirEntry, 6449 TransactedSnapshotImpl_StreamReadAt, 6450 TransactedSnapshotImpl_StreamWriteAt, 6451 TransactedSnapshotImpl_StreamSetSize, 6452 TransactedSnapshotImpl_StreamLink, 6453 TransactedSnapshotImpl_GetTransactionSig, 6454 TransactedSnapshotImpl_SetTransactionSig, 6455 TransactedSnapshotImpl_LockTransaction, 6456 TransactedSnapshotImpl_UnlockTransaction 6457 }; 6458 6459 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, 6460 TransactedSnapshotImpl** result) 6461 { 6462 HRESULT hr; 6463 6464 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl)); 6465 if (*result) 6466 { 6467 IStorage *scratch; 6468 6469 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl; 6470 6471 /* This is OK because the property set storage functions use the IStorage functions. */ 6472 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl; 6473 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl; 6474 6475 list_init(&(*result)->base.strmHead); 6476 6477 list_init(&(*result)->base.storageHead); 6478 6479 (*result)->base.ref = 1; 6480 6481 (*result)->base.openFlags = parentStorage->openFlags; 6482 6483 /* This cannot fail, except with E_NOTIMPL in which case we don't care */ 6484 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE); 6485 6486 /* Create a new temporary storage to act as the scratch file. */ 6487 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE, 6488 0, &scratch); 6489 (*result)->scratch = impl_from_IStorage(scratch); 6490 6491 if (SUCCEEDED(hr)) 6492 { 6493 ULONG num_entries = 20; 6494 6495 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries); 6496 (*result)->entries_size = num_entries; 6497 (*result)->firstFreeEntry = 0; 6498 6499 if ((*result)->entries) 6500 { 6501 /* parentStorage already has 1 reference, which we take over here. */ 6502 (*result)->transactedParent = parentStorage; 6503 6504 parentStorage->transactedChild = &(*result)->base; 6505 6506 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry); 6507 } 6508 else 6509 { 6510 IStorage_Release(scratch); 6511 6512 hr = E_OUTOFMEMORY; 6513 } 6514 } 6515 6516 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result); 6517 6518 return hr; 6519 } 6520 else 6521 return E_OUTOFMEMORY; 6522 } 6523 6524 6525 /************************************************************************ 6526 * TransactedSharedImpl implementation 6527 ***********************************************************************/ 6528 6529 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This) 6530 { 6531 if (!This->reverted) 6532 { 6533 TRACE("Storage invalidated (stg=%p)\n", This); 6534 6535 This->reverted = TRUE; 6536 6537 StorageBaseImpl_DeleteAll(This); 6538 } 6539 } 6540 6541 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface) 6542 { 6543 TransactedSharedImpl* This = (TransactedSharedImpl*) iface; 6544 6545 TransactedSharedImpl_Invalidate(&This->base); 6546 IStorage_Release(&This->transactedParent->IStorage_iface); 6547 IStorage_Release(&This->scratch->base.IStorage_iface); 6548 HeapFree(GetProcessHeap(), 0, This); 6549 } 6550 6551 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface) 6552 { 6553 /* We only need to flush when committing. */ 6554 return S_OK; 6555 } 6556 6557 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result) 6558 { 6559 TransactedSharedImpl* This = (TransactedSharedImpl*) iface; 6560 6561 return StorageBaseImpl_GetFilename(This->transactedParent, result); 6562 } 6563 6564 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base, 6565 const DirEntry *newData, DirRef *index) 6566 { 6567 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6568 6569 return StorageBaseImpl_CreateDirEntry(&This->scratch->base, 6570 newData, index); 6571 } 6572 6573 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base, 6574 DirRef index, const DirEntry *data) 6575 { 6576 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6577 6578 return StorageBaseImpl_WriteDirEntry(&This->scratch->base, 6579 index, data); 6580 } 6581 6582 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base, 6583 DirRef index, DirEntry *data) 6584 { 6585 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6586 6587 return StorageBaseImpl_ReadDirEntry(&This->scratch->base, 6588 index, data); 6589 } 6590 6591 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base, 6592 DirRef index) 6593 { 6594 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6595 6596 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base, 6597 index); 6598 } 6599 6600 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base, 6601 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 6602 { 6603 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6604 6605 return StorageBaseImpl_StreamReadAt(&This->scratch->base, 6606 index, offset, size, buffer, bytesRead); 6607 } 6608 6609 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base, 6610 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 6611 { 6612 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6613 6614 return StorageBaseImpl_StreamWriteAt(&This->scratch->base, 6615 index, offset, size, buffer, bytesWritten); 6616 } 6617 6618 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base, 6619 DirRef index, ULARGE_INTEGER newsize) 6620 { 6621 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6622 6623 return StorageBaseImpl_StreamSetSize(&This->scratch->base, 6624 index, newsize); 6625 } 6626 6627 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base, 6628 DirRef dst, DirRef src) 6629 { 6630 TransactedSharedImpl* This = (TransactedSharedImpl*) base; 6631 6632 return StorageBaseImpl_StreamLink(&This->scratch->base, 6633 dst, src); 6634 } 6635 6636 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base, 6637 ULONG* result, BOOL refresh) 6638 { 6639 return E_NOTIMPL; 6640 } 6641 6642 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base, 6643 ULONG value) 6644 { 6645 return E_NOTIMPL; 6646 } 6647 6648 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write) 6649 { 6650 return E_NOTIMPL; 6651 } 6652 6653 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) 6654 { 6655 return E_NOTIMPL; 6656 } 6657 6658 static HRESULT WINAPI TransactedSharedImpl_Commit( 6659 IStorage* iface, 6660 DWORD grfCommitFlags) /* [in] */ 6661 { 6662 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface); 6663 DirRef new_storage_ref, prev_storage_ref; 6664 DirEntry src_data, dst_data; 6665 HRESULT hr; 6666 ULONG transactionSig; 6667 6668 TRACE("(%p,%x)\n", iface, grfCommitFlags); 6669 6670 /* Cannot commit a read-only transacted storage */ 6671 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ ) 6672 return STG_E_ACCESSDENIED; 6673 6674 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE); 6675 if (hr == E_NOTIMPL) hr = S_OK; 6676 if (SUCCEEDED(hr)) 6677 { 6678 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE); 6679 if (SUCCEEDED(hr)) 6680 { 6681 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig) 6682 hr = STG_E_NOTCURRENT; 6683 6684 if (SUCCEEDED(hr)) 6685 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1); 6686 } 6687 else if (hr == E_NOTIMPL) 6688 hr = S_OK; 6689 6690 if (SUCCEEDED(hr)) 6691 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data); 6692 6693 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */ 6694 if (SUCCEEDED(hr)) 6695 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry); 6696 6697 if (SUCCEEDED(hr)) 6698 hr = StorageBaseImpl_Flush(This->transactedParent); 6699 6700 if (SUCCEEDED(hr)) 6701 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data); 6702 6703 if (SUCCEEDED(hr)) 6704 { 6705 prev_storage_ref = dst_data.dirRootEntry; 6706 dst_data.dirRootEntry = new_storage_ref; 6707 dst_data.clsid = src_data.clsid; 6708 dst_data.ctime = src_data.ctime; 6709 dst_data.mtime = src_data.mtime; 6710 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data); 6711 } 6712 6713 if (SUCCEEDED(hr)) 6714 { 6715 /* Try to flush after updating the root storage, but if the flush fails, keep 6716 * going, on the theory that it'll either succeed later or the subsequent 6717 * writes will fail. */ 6718 StorageBaseImpl_Flush(This->transactedParent); 6719 6720 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE); 6721 } 6722 6723 if (SUCCEEDED(hr)) 6724 hr = StorageBaseImpl_Flush(This->transactedParent); 6725 6726 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE); 6727 6728 if (SUCCEEDED(hr)) 6729 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT); 6730 6731 if (SUCCEEDED(hr)) 6732 { 6733 This->lastTransactionSig = transactionSig+1; 6734 } 6735 } 6736 TRACE("<-- %08x\n", hr); 6737 return hr; 6738 } 6739 6740 static HRESULT WINAPI TransactedSharedImpl_Revert( 6741 IStorage* iface) 6742 { 6743 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface); 6744 6745 TRACE("(%p)\n", iface); 6746 6747 /* Destroy the open objects. */ 6748 StorageBaseImpl_DeleteAll(&This->base); 6749 6750 return IStorage_Revert(&This->scratch->base.IStorage_iface); 6751 } 6752 6753 static const IStorageVtbl TransactedSharedImpl_Vtbl = 6754 { 6755 StorageBaseImpl_QueryInterface, 6756 StorageBaseImpl_AddRef, 6757 StorageBaseImpl_Release, 6758 StorageBaseImpl_CreateStream, 6759 StorageBaseImpl_OpenStream, 6760 StorageBaseImpl_CreateStorage, 6761 StorageBaseImpl_OpenStorage, 6762 StorageBaseImpl_CopyTo, 6763 StorageBaseImpl_MoveElementTo, 6764 TransactedSharedImpl_Commit, 6765 TransactedSharedImpl_Revert, 6766 StorageBaseImpl_EnumElements, 6767 StorageBaseImpl_DestroyElement, 6768 StorageBaseImpl_RenameElement, 6769 StorageBaseImpl_SetElementTimes, 6770 StorageBaseImpl_SetClass, 6771 StorageBaseImpl_SetStateBits, 6772 StorageBaseImpl_Stat 6773 }; 6774 6775 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl = 6776 { 6777 TransactedSharedImpl_Destroy, 6778 TransactedSharedImpl_Invalidate, 6779 TransactedSharedImpl_Flush, 6780 TransactedSharedImpl_GetFilename, 6781 TransactedSharedImpl_CreateDirEntry, 6782 TransactedSharedImpl_WriteDirEntry, 6783 TransactedSharedImpl_ReadDirEntry, 6784 TransactedSharedImpl_DestroyDirEntry, 6785 TransactedSharedImpl_StreamReadAt, 6786 TransactedSharedImpl_StreamWriteAt, 6787 TransactedSharedImpl_StreamSetSize, 6788 TransactedSharedImpl_StreamLink, 6789 TransactedSharedImpl_GetTransactionSig, 6790 TransactedSharedImpl_SetTransactionSig, 6791 TransactedSharedImpl_LockTransaction, 6792 TransactedSharedImpl_UnlockTransaction 6793 }; 6794 6795 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage, 6796 TransactedSharedImpl** result) 6797 { 6798 HRESULT hr; 6799 6800 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl)); 6801 if (*result) 6802 { 6803 IStorage *scratch; 6804 6805 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl; 6806 6807 /* This is OK because the property set storage functions use the IStorage functions. */ 6808 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl; 6809 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl; 6810 6811 list_init(&(*result)->base.strmHead); 6812 6813 list_init(&(*result)->base.storageHead); 6814 6815 (*result)->base.ref = 1; 6816 6817 (*result)->base.openFlags = parentStorage->openFlags; 6818 6819 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE); 6820 6821 if (SUCCEEDED(hr)) 6822 { 6823 STGOPTIONS stgo; 6824 6825 /* This cannot fail, except with E_NOTIMPL in which case we don't care */ 6826 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE); 6827 6828 stgo.usVersion = 1; 6829 stgo.reserved = 0; 6830 stgo.ulSectorSize = 4096; 6831 stgo.pwcsTemplateFile = NULL; 6832 6833 /* Create a new temporary storage to act as the scratch file. */ 6834 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED, 6835 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch); 6836 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch); 6837 6838 if (SUCCEEDED(hr)) 6839 { 6840 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry, 6841 parentStorage, parentStorage->storageDirEntry); 6842 6843 if (SUCCEEDED(hr)) 6844 { 6845 hr = IStorage_Commit(scratch, STGC_DEFAULT); 6846 6847 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry; 6848 (*result)->transactedParent = parentStorage; 6849 } 6850 6851 if (FAILED(hr)) 6852 IStorage_Release(scratch); 6853 } 6854 6855 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE); 6856 } 6857 6858 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result); 6859 6860 return hr; 6861 } 6862 else 6863 return E_OUTOFMEMORY; 6864 } 6865 6866 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage, 6867 BOOL toplevel, StorageBaseImpl** result) 6868 { 6869 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT; 6870 6871 if (parentStorage->openFlags & fixme_flags) 6872 { 6873 fixme_flags &= ~parentStorage->openFlags; 6874 FIXME("Unimplemented flags %x\n", parentStorage->openFlags); 6875 } 6876 6877 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) && 6878 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE && 6879 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE) 6880 { 6881 /* Need to create a temp file for the snapshot */ 6882 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result); 6883 } 6884 6885 return TransactedSnapshotImpl_Construct(parentStorage, 6886 (TransactedSnapshotImpl**)result); 6887 } 6888 6889 static HRESULT Storage_Construct( 6890 HANDLE hFile, 6891 LPCOLESTR pwcsName, 6892 ILockBytes* pLkbyt, 6893 DWORD openFlags, 6894 BOOL fileBased, 6895 BOOL create, 6896 ULONG sector_size, 6897 StorageBaseImpl** result) 6898 { 6899 StorageImpl *newStorage; 6900 StorageBaseImpl *newTransactedStorage; 6901 HRESULT hr; 6902 6903 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage); 6904 if (FAILED(hr)) goto end; 6905 6906 if (openFlags & STGM_TRANSACTED) 6907 { 6908 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage); 6909 if (FAILED(hr)) 6910 IStorage_Release(&newStorage->base.IStorage_iface); 6911 else 6912 *result = newTransactedStorage; 6913 } 6914 else 6915 *result = &newStorage->base; 6916 6917 end: 6918 return hr; 6919 } 6920 6921 6922 /************************************************************************ 6923 * StorageUtl helper functions 6924 ***********************************************************************/ 6925 6926 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value) 6927 { 6928 WORD tmp; 6929 6930 memcpy(&tmp, buffer+offset, sizeof(WORD)); 6931 *value = lendian16toh(tmp); 6932 } 6933 6934 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value) 6935 { 6936 value = htole16(value); 6937 memcpy(buffer+offset, &value, sizeof(WORD)); 6938 } 6939 6940 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value) 6941 { 6942 DWORD tmp; 6943 6944 memcpy(&tmp, buffer+offset, sizeof(DWORD)); 6945 *value = lendian32toh(tmp); 6946 } 6947 6948 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value) 6949 { 6950 value = htole32(value); 6951 memcpy(buffer+offset, &value, sizeof(DWORD)); 6952 } 6953 6954 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, 6955 ULARGE_INTEGER* value) 6956 { 6957 #ifdef WORDS_BIGENDIAN 6958 ULARGE_INTEGER tmp; 6959 6960 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER)); 6961 value->u.LowPart = htole32(tmp.u.HighPart); 6962 value->u.HighPart = htole32(tmp.u.LowPart); 6963 #else 6964 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER)); 6965 #endif 6966 } 6967 6968 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset, 6969 const ULARGE_INTEGER *value) 6970 { 6971 #ifdef WORDS_BIGENDIAN 6972 ULARGE_INTEGER tmp; 6973 6974 tmp.u.LowPart = htole32(value->u.HighPart); 6975 tmp.u.HighPart = htole32(value->u.LowPart); 6976 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER)); 6977 #else 6978 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER)); 6979 #endif 6980 } 6981 6982 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value) 6983 { 6984 StorageUtl_ReadDWord(buffer, offset, &(value->Data1)); 6985 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2)); 6986 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3)); 6987 6988 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4)); 6989 } 6990 6991 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value) 6992 { 6993 StorageUtl_WriteDWord(buffer, offset, value->Data1); 6994 StorageUtl_WriteWord(buffer, offset+4, value->Data2); 6995 StorageUtl_WriteWord(buffer, offset+6, value->Data3); 6996 6997 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4)); 6998 } 6999 7000 void StorageUtl_CopyDirEntryToSTATSTG( 7001 StorageBaseImpl* storage, 7002 STATSTG* destination, 7003 const DirEntry* source, 7004 int statFlags) 7005 { 7006 /* 7007 * The copy of the string occurs only when the flag is not set 7008 */ 7009 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT) 7010 { 7011 /* Use the filename for the root storage. */ 7012 destination->pwcsName = 0; 7013 StorageBaseImpl_GetFilename(storage, &destination->pwcsName); 7014 } 7015 else if( ((statFlags & STATFLAG_NONAME) != 0) || 7016 (source->name[0] == 0) ) 7017 { 7018 destination->pwcsName = 0; 7019 } 7020 else 7021 { 7022 destination->pwcsName = 7023 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR)); 7024 7025 strcpyW(destination->pwcsName, source->name); 7026 } 7027 7028 switch (source->stgType) 7029 { 7030 case STGTY_STORAGE: 7031 case STGTY_ROOT: 7032 destination->type = STGTY_STORAGE; 7033 break; 7034 case STGTY_STREAM: 7035 destination->type = STGTY_STREAM; 7036 break; 7037 default: 7038 destination->type = STGTY_STREAM; 7039 break; 7040 } 7041 7042 destination->cbSize = source->size; 7043 /* 7044 currentReturnStruct->mtime = {0}; TODO 7045 currentReturnStruct->ctime = {0}; 7046 currentReturnStruct->atime = {0}; 7047 */ 7048 destination->grfMode = 0; 7049 destination->grfLocksSupported = 0; 7050 destination->clsid = source->clsid; 7051 destination->grfStateBits = 0; 7052 destination->reserved = 0; 7053 } 7054 7055 7056 /************************************************************************ 7057 * BlockChainStream implementation 7058 ***********************************************************************/ 7059 7060 /****************************************************************************** 7061 * BlockChainStream_GetHeadOfChain 7062 * 7063 * Returns the head of this stream chain. 7064 * Some special chains don't have directory entries, their heads are kept in 7065 * This->headOfStreamPlaceHolder. 7066 * 7067 */ 7068 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This) 7069 { 7070 DirEntry chainEntry; 7071 HRESULT hr; 7072 7073 if (This->headOfStreamPlaceHolder != 0) 7074 return *(This->headOfStreamPlaceHolder); 7075 7076 if (This->ownerDirEntry != DIRENTRY_NULL) 7077 { 7078 hr = StorageImpl_ReadDirEntry( 7079 This->parentStorage, 7080 This->ownerDirEntry, 7081 &chainEntry); 7082 7083 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL) 7084 return chainEntry.startingBlock; 7085 } 7086 7087 return BLOCK_END_OF_CHAIN; 7088 } 7089 7090 /* Read and save the index of all blocks in this stream. */ 7091 static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This) 7092 { 7093 ULONG next_sector, next_offset; 7094 HRESULT hr; 7095 struct BlockChainRun *last_run; 7096 7097 if (This->indexCacheLen == 0) 7098 { 7099 last_run = NULL; 7100 next_offset = 0; 7101 next_sector = BlockChainStream_GetHeadOfChain(This); 7102 } 7103 else 7104 { 7105 last_run = &This->indexCache[This->indexCacheLen-1]; 7106 next_offset = last_run->lastOffset+1; 7107 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, 7108 last_run->firstSector + last_run->lastOffset - last_run->firstOffset, 7109 &next_sector); 7110 if (FAILED(hr)) return hr; 7111 } 7112 7113 while (next_sector != BLOCK_END_OF_CHAIN) 7114 { 7115 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset) 7116 { 7117 /* Add the current block to the cache. */ 7118 if (This->indexCacheSize == 0) 7119 { 7120 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16); 7121 if (!This->indexCache) return E_OUTOFMEMORY; 7122 This->indexCacheSize = 16; 7123 } 7124 else if (This->indexCacheSize == This->indexCacheLen) 7125 { 7126 struct BlockChainRun *new_cache; 7127 ULONG new_size; 7128 7129 new_size = This->indexCacheSize * 2; 7130 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size); 7131 if (!new_cache) return E_OUTOFMEMORY; 7132 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen); 7133 7134 HeapFree(GetProcessHeap(), 0, This->indexCache); 7135 This->indexCache = new_cache; 7136 This->indexCacheSize = new_size; 7137 } 7138 7139 This->indexCacheLen++; 7140 last_run = &This->indexCache[This->indexCacheLen-1]; 7141 last_run->firstSector = next_sector; 7142 last_run->firstOffset = next_offset; 7143 } 7144 7145 last_run->lastOffset = next_offset; 7146 7147 /* Find the next block. */ 7148 next_offset++; 7149 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector); 7150 if (FAILED(hr)) return hr; 7151 } 7152 7153 if (This->indexCacheLen) 7154 { 7155 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset; 7156 This->numBlocks = last_run->lastOffset+1; 7157 } 7158 else 7159 { 7160 This->tailIndex = BLOCK_END_OF_CHAIN; 7161 This->numBlocks = 0; 7162 } 7163 7164 return S_OK; 7165 } 7166 7167 /* Locate the nth block in this stream. */ 7168 static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) 7169 { 7170 ULONG min_offset = 0, max_offset = This->numBlocks-1; 7171 ULONG min_run = 0, max_run = This->indexCacheLen-1; 7172 7173 if (offset >= This->numBlocks) 7174 return BLOCK_END_OF_CHAIN; 7175 7176 while (min_run < max_run) 7177 { 7178 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset); 7179 if (offset < This->indexCache[run_to_check].firstOffset) 7180 { 7181 max_offset = This->indexCache[run_to_check].firstOffset-1; 7182 max_run = run_to_check-1; 7183 } 7184 else if (offset > This->indexCache[run_to_check].lastOffset) 7185 { 7186 min_offset = This->indexCache[run_to_check].lastOffset+1; 7187 min_run = run_to_check+1; 7188 } 7189 else 7190 /* Block is in this run. */ 7191 min_run = max_run = run_to_check; 7192 } 7193 7194 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; 7195 } 7196 7197 static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, 7198 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) 7199 { 7200 BlockChainBlock *result=NULL; 7201 int i; 7202 7203 for (i=0; i<2; i++) 7204 if (This->cachedBlocks[i].index == index) 7205 { 7206 *sector = This->cachedBlocks[i].sector; 7207 *block = &This->cachedBlocks[i]; 7208 return S_OK; 7209 } 7210 7211 *sector = BlockChainStream_GetSectorOfOffset(This, index); 7212 if (*sector == BLOCK_END_OF_CHAIN) 7213 return STG_E_DOCFILECORRUPT; 7214 7215 if (create) 7216 { 7217 if (This->cachedBlocks[0].index == 0xffffffff) 7218 result = &This->cachedBlocks[0]; 7219 else if (This->cachedBlocks[1].index == 0xffffffff) 7220 result = &This->cachedBlocks[1]; 7221 else 7222 { 7223 result = &This->cachedBlocks[This->blockToEvict++]; 7224 if (This->blockToEvict == 2) 7225 This->blockToEvict = 0; 7226 } 7227 7228 if (result->dirty) 7229 { 7230 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data)) 7231 return STG_E_WRITEFAULT; 7232 result->dirty = FALSE; 7233 } 7234 7235 result->read = FALSE; 7236 result->index = index; 7237 result->sector = *sector; 7238 } 7239 7240 *block = result; 7241 return S_OK; 7242 } 7243 7244 BlockChainStream* BlockChainStream_Construct( 7245 StorageImpl* parentStorage, 7246 ULONG* headOfStreamPlaceHolder, 7247 DirRef dirEntry) 7248 { 7249 BlockChainStream* newStream; 7250 7251 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream)); 7252 if(!newStream) 7253 return NULL; 7254 7255 newStream->parentStorage = parentStorage; 7256 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; 7257 newStream->ownerDirEntry = dirEntry; 7258 newStream->indexCache = NULL; 7259 newStream->indexCacheLen = 0; 7260 newStream->indexCacheSize = 0; 7261 newStream->cachedBlocks[0].index = 0xffffffff; 7262 newStream->cachedBlocks[0].dirty = FALSE; 7263 newStream->cachedBlocks[1].index = 0xffffffff; 7264 newStream->cachedBlocks[1].dirty = FALSE; 7265 newStream->blockToEvict = 0; 7266 7267 if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) 7268 { 7269 HeapFree(GetProcessHeap(), 0, newStream->indexCache); 7270 HeapFree(GetProcessHeap(), 0, newStream); 7271 return NULL; 7272 } 7273 7274 return newStream; 7275 } 7276 7277 HRESULT BlockChainStream_Flush(BlockChainStream* This) 7278 { 7279 int i; 7280 if (!This) return S_OK; 7281 for (i=0; i<2; i++) 7282 { 7283 if (This->cachedBlocks[i].dirty) 7284 { 7285 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data)) 7286 This->cachedBlocks[i].dirty = FALSE; 7287 else 7288 return STG_E_WRITEFAULT; 7289 } 7290 } 7291 return S_OK; 7292 } 7293 7294 void BlockChainStream_Destroy(BlockChainStream* This) 7295 { 7296 if (This) 7297 { 7298 BlockChainStream_Flush(This); 7299 HeapFree(GetProcessHeap(), 0, This->indexCache); 7300 } 7301 HeapFree(GetProcessHeap(), 0, This); 7302 } 7303 7304 /****************************************************************************** 7305 * BlockChainStream_Shrink 7306 * 7307 * Shrinks this chain in the big block depot. 7308 */ 7309 static BOOL BlockChainStream_Shrink(BlockChainStream* This, 7310 ULARGE_INTEGER newSize) 7311 { 7312 ULONG blockIndex; 7313 ULONG numBlocks; 7314 int i; 7315 7316 /* 7317 * Figure out how many blocks are needed to contain the new size 7318 */ 7319 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize; 7320 7321 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0) 7322 numBlocks++; 7323 7324 if (numBlocks) 7325 { 7326 /* 7327 * Go to the new end of chain 7328 */ 7329 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1); 7330 7331 /* Mark the new end of chain */ 7332 StorageImpl_SetNextBlockInChain( 7333 This->parentStorage, 7334 blockIndex, 7335 BLOCK_END_OF_CHAIN); 7336 7337 This->tailIndex = blockIndex; 7338 } 7339 else 7340 { 7341 if (This->headOfStreamPlaceHolder != 0) 7342 { 7343 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN; 7344 } 7345 else 7346 { 7347 DirEntry chainEntry; 7348 assert(This->ownerDirEntry != DIRENTRY_NULL); 7349 7350 StorageImpl_ReadDirEntry( 7351 This->parentStorage, 7352 This->ownerDirEntry, 7353 &chainEntry); 7354 7355 chainEntry.startingBlock = BLOCK_END_OF_CHAIN; 7356 7357 StorageImpl_WriteDirEntry( 7358 This->parentStorage, 7359 This->ownerDirEntry, 7360 &chainEntry); 7361 } 7362 7363 This->tailIndex = BLOCK_END_OF_CHAIN; 7364 } 7365 7366 This->numBlocks = numBlocks; 7367 7368 /* 7369 * Mark the extra blocks as free 7370 */ 7371 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks) 7372 { 7373 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1]; 7374 StorageImpl_FreeBigBlock(This->parentStorage, 7375 last_run->firstSector + last_run->lastOffset - last_run->firstOffset); 7376 if (last_run->lastOffset == last_run->firstOffset) 7377 This->indexCacheLen--; 7378 else 7379 last_run->lastOffset--; 7380 } 7381 7382 /* 7383 * Reset the last accessed block cache. 7384 */ 7385 for (i=0; i<2; i++) 7386 { 7387 if (This->cachedBlocks[i].index >= numBlocks) 7388 { 7389 This->cachedBlocks[i].index = 0xffffffff; 7390 This->cachedBlocks[i].dirty = FALSE; 7391 } 7392 } 7393 7394 return TRUE; 7395 } 7396 7397 /****************************************************************************** 7398 * BlockChainStream_Enlarge 7399 * 7400 * Grows this chain in the big block depot. 7401 */ 7402 static BOOL BlockChainStream_Enlarge(BlockChainStream* This, 7403 ULARGE_INTEGER newSize) 7404 { 7405 ULONG blockIndex, currentBlock; 7406 ULONG newNumBlocks; 7407 ULONG oldNumBlocks = 0; 7408 7409 blockIndex = BlockChainStream_GetHeadOfChain(This); 7410 7411 /* 7412 * Empty chain. Create the head. 7413 */ 7414 if (blockIndex == BLOCK_END_OF_CHAIN) 7415 { 7416 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage); 7417 StorageImpl_SetNextBlockInChain(This->parentStorage, 7418 blockIndex, 7419 BLOCK_END_OF_CHAIN); 7420 7421 if (This->headOfStreamPlaceHolder != 0) 7422 { 7423 *(This->headOfStreamPlaceHolder) = blockIndex; 7424 } 7425 else 7426 { 7427 DirEntry chainEntry; 7428 assert(This->ownerDirEntry != DIRENTRY_NULL); 7429 7430 StorageImpl_ReadDirEntry( 7431 This->parentStorage, 7432 This->ownerDirEntry, 7433 &chainEntry); 7434 7435 chainEntry.startingBlock = blockIndex; 7436 7437 StorageImpl_WriteDirEntry( 7438 This->parentStorage, 7439 This->ownerDirEntry, 7440 &chainEntry); 7441 } 7442 7443 This->tailIndex = blockIndex; 7444 This->numBlocks = 1; 7445 } 7446 7447 /* 7448 * Figure out how many blocks are needed to contain this stream 7449 */ 7450 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize; 7451 7452 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0) 7453 newNumBlocks++; 7454 7455 /* 7456 * Go to the current end of chain 7457 */ 7458 if (This->tailIndex == BLOCK_END_OF_CHAIN) 7459 { 7460 currentBlock = blockIndex; 7461 7462 while (blockIndex != BLOCK_END_OF_CHAIN) 7463 { 7464 This->numBlocks++; 7465 currentBlock = blockIndex; 7466 7467 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock, 7468 &blockIndex))) 7469 return FALSE; 7470 } 7471 7472 This->tailIndex = currentBlock; 7473 } 7474 7475 currentBlock = This->tailIndex; 7476 oldNumBlocks = This->numBlocks; 7477 7478 /* 7479 * Add new blocks to the chain 7480 */ 7481 if (oldNumBlocks < newNumBlocks) 7482 { 7483 while (oldNumBlocks < newNumBlocks) 7484 { 7485 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage); 7486 7487 StorageImpl_SetNextBlockInChain( 7488 This->parentStorage, 7489 currentBlock, 7490 blockIndex); 7491 7492 StorageImpl_SetNextBlockInChain( 7493 This->parentStorage, 7494 blockIndex, 7495 BLOCK_END_OF_CHAIN); 7496 7497 currentBlock = blockIndex; 7498 oldNumBlocks++; 7499 } 7500 7501 This->tailIndex = blockIndex; 7502 This->numBlocks = newNumBlocks; 7503 } 7504 7505 if (FAILED(BlockChainStream_UpdateIndexCache(This))) 7506 return FALSE; 7507 7508 return TRUE; 7509 } 7510 7511 7512 /****************************************************************************** 7513 * BlockChainStream_GetSize 7514 * 7515 * Returns the size of this chain. 7516 * Will return the block count if this chain doesn't have a directory entry. 7517 */ 7518 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This) 7519 { 7520 DirEntry chainEntry; 7521 7522 if(This->headOfStreamPlaceHolder == NULL) 7523 { 7524 /* 7525 * This chain has a directory entry so use the size value from there. 7526 */ 7527 StorageImpl_ReadDirEntry( 7528 This->parentStorage, 7529 This->ownerDirEntry, 7530 &chainEntry); 7531 7532 return chainEntry.size; 7533 } 7534 else 7535 { 7536 /* 7537 * this chain is a chain that does not have a directory entry, figure out the 7538 * size by making the product number of used blocks times the 7539 * size of them 7540 */ 7541 ULARGE_INTEGER result; 7542 result.QuadPart = 7543 (ULONGLONG)BlockChainStream_GetCount(This) * 7544 This->parentStorage->bigBlockSize; 7545 7546 return result; 7547 } 7548 } 7549 7550 /****************************************************************************** 7551 * BlockChainStream_SetSize 7552 * 7553 * Sets the size of this stream. The big block depot will be updated. 7554 * The file will grow if we grow the chain. 7555 * 7556 * TODO: Free the actual blocks in the file when we shrink the chain. 7557 * Currently, the blocks are still in the file. So the file size 7558 * doesn't shrink even if we shrink streams. 7559 */ 7560 BOOL BlockChainStream_SetSize( 7561 BlockChainStream* This, 7562 ULARGE_INTEGER newSize) 7563 { 7564 ULARGE_INTEGER size = BlockChainStream_GetSize(This); 7565 7566 if (newSize.QuadPart == size.QuadPart) 7567 return TRUE; 7568 7569 if (newSize.QuadPart < size.QuadPart) 7570 { 7571 BlockChainStream_Shrink(This, newSize); 7572 } 7573 else 7574 { 7575 BlockChainStream_Enlarge(This, newSize); 7576 } 7577 7578 return TRUE; 7579 } 7580 7581 /****************************************************************************** 7582 * BlockChainStream_ReadAt 7583 * 7584 * Reads a specified number of bytes from this chain at the specified offset. 7585 * bytesRead may be NULL. 7586 * Failure will be returned if the specified number of bytes has not been read. 7587 */ 7588 HRESULT BlockChainStream_ReadAt(BlockChainStream* This, 7589 ULARGE_INTEGER offset, 7590 ULONG size, 7591 void* buffer, 7592 ULONG* bytesRead) 7593 { 7594 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize; 7595 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize; 7596 ULONG bytesToReadInBuffer; 7597 ULONG blockIndex; 7598 BYTE* bufferWalker; 7599 ULARGE_INTEGER stream_size; 7600 HRESULT hr; 7601 BlockChainBlock *cachedBlock; 7602 7603 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); 7604 7605 /* 7606 * Find the first block in the stream that contains part of the buffer. 7607 */ 7608 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); 7609 7610 *bytesRead = 0; 7611 7612 stream_size = BlockChainStream_GetSize(This); 7613 if (stream_size.QuadPart > offset.QuadPart) 7614 size = min(stream_size.QuadPart - offset.QuadPart, size); 7615 else 7616 return S_OK; 7617 7618 /* 7619 * Start reading the buffer. 7620 */ 7621 bufferWalker = buffer; 7622 7623 while (size > 0) 7624 { 7625 ULARGE_INTEGER ulOffset; 7626 DWORD bytesReadAt; 7627 7628 /* 7629 * Calculate how many bytes we can copy from this big block. 7630 */ 7631 bytesToReadInBuffer = 7632 min(This->parentStorage->bigBlockSize - offsetInBlock, size); 7633 7634 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer); 7635 7636 if (FAILED(hr)) 7637 return hr; 7638 7639 if (!cachedBlock) 7640 { 7641 /* Not in cache, and we're going to read past the end of the block. */ 7642 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + 7643 offsetInBlock; 7644 7645 StorageImpl_ReadAt(This->parentStorage, 7646 ulOffset, 7647 bufferWalker, 7648 bytesToReadInBuffer, 7649 &bytesReadAt); 7650 } 7651 else 7652 { 7653 if (!cachedBlock->read) 7654 { 7655 ULONG read; 7656 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) 7657 return STG_E_READFAULT; 7658 7659 cachedBlock->read = TRUE; 7660 } 7661 7662 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer); 7663 bytesReadAt = bytesToReadInBuffer; 7664 } 7665 7666 blockNoInSequence++; 7667 bufferWalker += bytesReadAt; 7668 size -= bytesReadAt; 7669 *bytesRead += bytesReadAt; 7670 offsetInBlock = 0; /* There is no offset on the next block */ 7671 7672 if (bytesToReadInBuffer != bytesReadAt) 7673 break; 7674 } 7675 7676 return S_OK; 7677 } 7678 7679 /****************************************************************************** 7680 * BlockChainStream_WriteAt 7681 * 7682 * Writes the specified number of bytes to this chain at the specified offset. 7683 * Will fail if not all specified number of bytes have been written. 7684 */ 7685 HRESULT BlockChainStream_WriteAt(BlockChainStream* This, 7686 ULARGE_INTEGER offset, 7687 ULONG size, 7688 const void* buffer, 7689 ULONG* bytesWritten) 7690 { 7691 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize; 7692 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize; 7693 ULONG bytesToWrite; 7694 ULONG blockIndex; 7695 const BYTE* bufferWalker; 7696 HRESULT hr; 7697 BlockChainBlock *cachedBlock; 7698 7699 *bytesWritten = 0; 7700 bufferWalker = buffer; 7701 7702 while (size > 0) 7703 { 7704 ULARGE_INTEGER ulOffset; 7705 DWORD bytesWrittenAt; 7706 7707 /* 7708 * Calculate how many bytes we can copy to this big block. 7709 */ 7710 bytesToWrite = 7711 min(This->parentStorage->bigBlockSize - offsetInBlock, size); 7712 7713 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite); 7714 7715 /* BlockChainStream_SetSize should have already been called to ensure we have 7716 * enough blocks in the chain to write into */ 7717 if (FAILED(hr)) 7718 { 7719 ERR("not enough blocks in chain to write data\n"); 7720 return hr; 7721 } 7722 7723 if (!cachedBlock) 7724 { 7725 /* Not in cache, and we're going to write past the end of the block. */ 7726 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + 7727 offsetInBlock; 7728 7729 StorageImpl_WriteAt(This->parentStorage, 7730 ulOffset, 7731 bufferWalker, 7732 bytesToWrite, 7733 &bytesWrittenAt); 7734 } 7735 else 7736 { 7737 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize) 7738 { 7739 ULONG read; 7740 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read) 7741 return STG_E_READFAULT; 7742 } 7743 7744 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite); 7745 bytesWrittenAt = bytesToWrite; 7746 cachedBlock->read = TRUE; 7747 cachedBlock->dirty = TRUE; 7748 } 7749 7750 blockNoInSequence++; 7751 bufferWalker += bytesWrittenAt; 7752 size -= bytesWrittenAt; 7753 *bytesWritten += bytesWrittenAt; 7754 offsetInBlock = 0; /* There is no offset on the next block */ 7755 7756 if (bytesWrittenAt != bytesToWrite) 7757 break; 7758 } 7759 7760 return (size == 0) ? S_OK : STG_E_WRITEFAULT; 7761 } 7762 7763 7764 /************************************************************************ 7765 * SmallBlockChainStream implementation 7766 ***********************************************************************/ 7767 7768 SmallBlockChainStream* SmallBlockChainStream_Construct( 7769 StorageImpl* parentStorage, 7770 ULONG* headOfStreamPlaceHolder, 7771 DirRef dirEntry) 7772 { 7773 SmallBlockChainStream* newStream; 7774 7775 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream)); 7776 7777 newStream->parentStorage = parentStorage; 7778 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder; 7779 newStream->ownerDirEntry = dirEntry; 7780 7781 return newStream; 7782 } 7783 7784 void SmallBlockChainStream_Destroy( 7785 SmallBlockChainStream* This) 7786 { 7787 HeapFree(GetProcessHeap(), 0, This); 7788 } 7789 7790 /****************************************************************************** 7791 * SmallBlockChainStream_GetHeadOfChain 7792 * 7793 * Returns the head of this chain of small blocks. 7794 */ 7795 static ULONG SmallBlockChainStream_GetHeadOfChain( 7796 SmallBlockChainStream* This) 7797 { 7798 DirEntry chainEntry; 7799 HRESULT hr; 7800 7801 if (This->headOfStreamPlaceHolder != NULL) 7802 return *(This->headOfStreamPlaceHolder); 7803 7804 if (This->ownerDirEntry) 7805 { 7806 hr = StorageImpl_ReadDirEntry( 7807 This->parentStorage, 7808 This->ownerDirEntry, 7809 &chainEntry); 7810 7811 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL) 7812 return chainEntry.startingBlock; 7813 } 7814 7815 return BLOCK_END_OF_CHAIN; 7816 } 7817 7818 /****************************************************************************** 7819 * SmallBlockChainStream_GetNextBlockInChain 7820 * 7821 * Returns the index of the next small block in this chain. 7822 * 7823 * Return Values: 7824 * - BLOCK_END_OF_CHAIN: end of this chain 7825 * - BLOCK_UNUSED: small block 'blockIndex' is free 7826 */ 7827 static HRESULT SmallBlockChainStream_GetNextBlockInChain( 7828 SmallBlockChainStream* This, 7829 ULONG blockIndex, 7830 ULONG* nextBlockInChain) 7831 { 7832 ULARGE_INTEGER offsetOfBlockInDepot; 7833 DWORD buffer; 7834 ULONG bytesRead; 7835 HRESULT res; 7836 7837 *nextBlockInChain = BLOCK_END_OF_CHAIN; 7838 7839 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); 7840 7841 /* 7842 * Read those bytes in the buffer from the small block file. 7843 */ 7844 res = BlockChainStream_ReadAt( 7845 This->parentStorage->smallBlockDepotChain, 7846 offsetOfBlockInDepot, 7847 sizeof(DWORD), 7848 &buffer, 7849 &bytesRead); 7850 7851 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD)) 7852 res = STG_E_READFAULT; 7853 7854 if (SUCCEEDED(res)) 7855 { 7856 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain); 7857 return S_OK; 7858 } 7859 7860 return res; 7861 } 7862 7863 /****************************************************************************** 7864 * SmallBlockChainStream_SetNextBlockInChain 7865 * 7866 * Writes the index of the next block of the specified block in the small 7867 * block depot. 7868 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock. 7869 * To flag a block as free use BLOCK_UNUSED as nextBlock. 7870 */ 7871 static void SmallBlockChainStream_SetNextBlockInChain( 7872 SmallBlockChainStream* This, 7873 ULONG blockIndex, 7874 ULONG nextBlock) 7875 { 7876 ULARGE_INTEGER offsetOfBlockInDepot; 7877 DWORD buffer; 7878 ULONG bytesWritten; 7879 7880 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); 7881 7882 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock); 7883 7884 /* 7885 * Read those bytes in the buffer from the small block file. 7886 */ 7887 BlockChainStream_WriteAt( 7888 This->parentStorage->smallBlockDepotChain, 7889 offsetOfBlockInDepot, 7890 sizeof(DWORD), 7891 &buffer, 7892 &bytesWritten); 7893 } 7894 7895 /****************************************************************************** 7896 * SmallBlockChainStream_FreeBlock 7897 * 7898 * Flag small block 'blockIndex' as free in the small block depot. 7899 */ 7900 static void SmallBlockChainStream_FreeBlock( 7901 SmallBlockChainStream* This, 7902 ULONG blockIndex) 7903 { 7904 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED); 7905 } 7906 7907 /****************************************************************************** 7908 * SmallBlockChainStream_GetNextFreeBlock 7909 * 7910 * Returns the index of a free small block. The small block depot will be 7911 * enlarged if necessary. The small block chain will also be enlarged if 7912 * necessary. 7913 */ 7914 static ULONG SmallBlockChainStream_GetNextFreeBlock( 7915 SmallBlockChainStream* This) 7916 { 7917 ULARGE_INTEGER offsetOfBlockInDepot; 7918 DWORD buffer; 7919 ULONG bytesRead; 7920 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock; 7921 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN; 7922 HRESULT res = S_OK; 7923 ULONG smallBlocksPerBigBlock; 7924 DirEntry rootEntry; 7925 ULONG blocksRequired; 7926 ULARGE_INTEGER old_size, size_required; 7927 7928 offsetOfBlockInDepot.u.HighPart = 0; 7929 7930 /* 7931 * Scan the small block depot for a free block 7932 */ 7933 while (nextBlockIndex != BLOCK_UNUSED) 7934 { 7935 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG); 7936 7937 res = BlockChainStream_ReadAt( 7938 This->parentStorage->smallBlockDepotChain, 7939 offsetOfBlockInDepot, 7940 sizeof(DWORD), 7941 &buffer, 7942 &bytesRead); 7943 7944 /* 7945 * If we run out of space for the small block depot, enlarge it 7946 */ 7947 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD)) 7948 { 7949 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex); 7950 7951 if (nextBlockIndex != BLOCK_UNUSED) 7952 blockIndex++; 7953 } 7954 else 7955 { 7956 ULONG count = 7957 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain); 7958 7959 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE]; 7960 ULARGE_INTEGER newSize, offset; 7961 ULONG bytesWritten; 7962 7963 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize; 7964 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize); 7965 7966 /* 7967 * Initialize all the small blocks to free 7968 */ 7969 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize); 7970 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize; 7971 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain, 7972 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten); 7973 7974 StorageImpl_SaveFileHeader(This->parentStorage); 7975 } 7976 } 7977 7978 This->parentStorage->firstFreeSmallBlock = blockIndex+1; 7979 7980 smallBlocksPerBigBlock = 7981 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize; 7982 7983 /* 7984 * Verify if we have to allocate big blocks to contain small blocks 7985 */ 7986 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1; 7987 7988 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize; 7989 7990 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain); 7991 7992 if (size_required.QuadPart > old_size.QuadPart) 7993 { 7994 BlockChainStream_SetSize( 7995 This->parentStorage->smallBlockRootChain, 7996 size_required); 7997 7998 StorageImpl_ReadDirEntry( 7999 This->parentStorage, 8000 This->parentStorage->base.storageDirEntry, 8001 &rootEntry); 8002 8003 rootEntry.size = size_required; 8004 8005 StorageImpl_WriteDirEntry( 8006 This->parentStorage, 8007 This->parentStorage->base.storageDirEntry, 8008 &rootEntry); 8009 } 8010 8011 return blockIndex; 8012 } 8013 8014 /****************************************************************************** 8015 * SmallBlockChainStream_ReadAt 8016 * 8017 * Reads a specified number of bytes from this chain at the specified offset. 8018 * bytesRead may be NULL. 8019 * Failure will be returned if the specified number of bytes has not been read. 8020 */ 8021 HRESULT SmallBlockChainStream_ReadAt( 8022 SmallBlockChainStream* This, 8023 ULARGE_INTEGER offset, 8024 ULONG size, 8025 void* buffer, 8026 ULONG* bytesRead) 8027 { 8028 HRESULT rc = S_OK; 8029 ULARGE_INTEGER offsetInBigBlockFile; 8030 ULONG blockNoInSequence = 8031 offset.u.LowPart / This->parentStorage->smallBlockSize; 8032 8033 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize; 8034 ULONG bytesToReadInBuffer; 8035 ULONG blockIndex; 8036 ULONG bytesReadFromBigBlockFile; 8037 BYTE* bufferWalker; 8038 ULARGE_INTEGER stream_size; 8039 8040 /* 8041 * This should never happen on a small block file. 8042 */ 8043 assert(offset.u.HighPart==0); 8044 8045 *bytesRead = 0; 8046 8047 stream_size = SmallBlockChainStream_GetSize(This); 8048 if (stream_size.QuadPart > offset.QuadPart) 8049 size = min(stream_size.QuadPart - offset.QuadPart, size); 8050 else 8051 return S_OK; 8052 8053 /* 8054 * Find the first block in the stream that contains part of the buffer. 8055 */ 8056 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 8057 8058 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) 8059 { 8060 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); 8061 if(FAILED(rc)) 8062 return rc; 8063 blockNoInSequence--; 8064 } 8065 8066 /* 8067 * Start reading the buffer. 8068 */ 8069 bufferWalker = buffer; 8070 8071 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) 8072 { 8073 /* 8074 * Calculate how many bytes we can copy from this small block. 8075 */ 8076 bytesToReadInBuffer = 8077 min(This->parentStorage->smallBlockSize - offsetInBlock, size); 8078 8079 /* 8080 * Calculate the offset of the small block in the small block file. 8081 */ 8082 offsetInBigBlockFile.QuadPart = 8083 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; 8084 8085 offsetInBigBlockFile.QuadPart += offsetInBlock; 8086 8087 /* 8088 * Read those bytes in the buffer from the small block file. 8089 * The small block has already been identified so it shouldn't fail 8090 * unless the file is corrupt. 8091 */ 8092 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain, 8093 offsetInBigBlockFile, 8094 bytesToReadInBuffer, 8095 bufferWalker, 8096 &bytesReadFromBigBlockFile); 8097 8098 if (FAILED(rc)) 8099 return rc; 8100 8101 if (!bytesReadFromBigBlockFile) 8102 return STG_E_DOCFILECORRUPT; 8103 8104 /* 8105 * Step to the next big block. 8106 */ 8107 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); 8108 if(FAILED(rc)) 8109 return STG_E_DOCFILECORRUPT; 8110 8111 bufferWalker += bytesReadFromBigBlockFile; 8112 size -= bytesReadFromBigBlockFile; 8113 *bytesRead += bytesReadFromBigBlockFile; 8114 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize; 8115 } 8116 8117 return S_OK; 8118 } 8119 8120 /****************************************************************************** 8121 * SmallBlockChainStream_WriteAt 8122 * 8123 * Writes the specified number of bytes to this chain at the specified offset. 8124 * Will fail if not all specified number of bytes have been written. 8125 */ 8126 HRESULT SmallBlockChainStream_WriteAt( 8127 SmallBlockChainStream* This, 8128 ULARGE_INTEGER offset, 8129 ULONG size, 8130 const void* buffer, 8131 ULONG* bytesWritten) 8132 { 8133 ULARGE_INTEGER offsetInBigBlockFile; 8134 ULONG blockNoInSequence = 8135 offset.u.LowPart / This->parentStorage->smallBlockSize; 8136 8137 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize; 8138 ULONG bytesToWriteInBuffer; 8139 ULONG blockIndex; 8140 ULONG bytesWrittenToBigBlockFile; 8141 const BYTE* bufferWalker; 8142 HRESULT res; 8143 8144 /* 8145 * This should never happen on a small block file. 8146 */ 8147 assert(offset.u.HighPart==0); 8148 8149 /* 8150 * Find the first block in the stream that contains part of the buffer. 8151 */ 8152 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 8153 8154 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN)) 8155 { 8156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex))) 8157 return STG_E_DOCFILECORRUPT; 8158 blockNoInSequence--; 8159 } 8160 8161 /* 8162 * Start writing the buffer. 8163 */ 8164 *bytesWritten = 0; 8165 bufferWalker = buffer; 8166 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) 8167 { 8168 /* 8169 * Calculate how many bytes we can copy to this small block. 8170 */ 8171 bytesToWriteInBuffer = 8172 min(This->parentStorage->smallBlockSize - offsetInBlock, size); 8173 8174 /* 8175 * Calculate the offset of the small block in the small block file. 8176 */ 8177 offsetInBigBlockFile.QuadPart = 8178 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize; 8179 8180 offsetInBigBlockFile.QuadPart += offsetInBlock; 8181 8182 /* 8183 * Write those bytes in the buffer to the small block file. 8184 */ 8185 res = BlockChainStream_WriteAt( 8186 This->parentStorage->smallBlockRootChain, 8187 offsetInBigBlockFile, 8188 bytesToWriteInBuffer, 8189 bufferWalker, 8190 &bytesWrittenToBigBlockFile); 8191 if (FAILED(res)) 8192 return res; 8193 8194 /* 8195 * Step to the next big block. 8196 */ 8197 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex); 8198 if (FAILED(res)) 8199 return res; 8200 bufferWalker += bytesWrittenToBigBlockFile; 8201 size -= bytesWrittenToBigBlockFile; 8202 *bytesWritten += bytesWrittenToBigBlockFile; 8203 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize; 8204 } 8205 8206 return (size == 0) ? S_OK : STG_E_WRITEFAULT; 8207 } 8208 8209 /****************************************************************************** 8210 * SmallBlockChainStream_Shrink 8211 * 8212 * Shrinks this chain in the small block depot. 8213 */ 8214 static BOOL SmallBlockChainStream_Shrink( 8215 SmallBlockChainStream* This, 8216 ULARGE_INTEGER newSize) 8217 { 8218 ULONG blockIndex, extraBlock; 8219 ULONG numBlocks; 8220 ULONG count = 0; 8221 8222 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize; 8223 8224 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0) 8225 numBlocks++; 8226 8227 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 8228 8229 /* 8230 * Go to the new end of chain 8231 */ 8232 while (count < numBlocks) 8233 { 8234 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, 8235 &blockIndex))) 8236 return FALSE; 8237 count++; 8238 } 8239 8240 /* 8241 * If the count is 0, we have a special case, the head of the chain was 8242 * just freed. 8243 */ 8244 if (count == 0) 8245 { 8246 DirEntry chainEntry; 8247 8248 StorageImpl_ReadDirEntry(This->parentStorage, 8249 This->ownerDirEntry, 8250 &chainEntry); 8251 8252 chainEntry.startingBlock = BLOCK_END_OF_CHAIN; 8253 8254 StorageImpl_WriteDirEntry(This->parentStorage, 8255 This->ownerDirEntry, 8256 &chainEntry); 8257 8258 /* 8259 * We start freeing the chain at the head block. 8260 */ 8261 extraBlock = blockIndex; 8262 } 8263 else 8264 { 8265 /* Get the next block before marking the new end */ 8266 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, 8267 &extraBlock))) 8268 return FALSE; 8269 8270 /* Mark the new end of chain */ 8271 SmallBlockChainStream_SetNextBlockInChain( 8272 This, 8273 blockIndex, 8274 BLOCK_END_OF_CHAIN); 8275 } 8276 8277 /* 8278 * Mark the extra blocks as free 8279 */ 8280 while (extraBlock != BLOCK_END_OF_CHAIN) 8281 { 8282 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock, 8283 &blockIndex))) 8284 return FALSE; 8285 SmallBlockChainStream_FreeBlock(This, extraBlock); 8286 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock); 8287 extraBlock = blockIndex; 8288 } 8289 8290 return TRUE; 8291 } 8292 8293 /****************************************************************************** 8294 * SmallBlockChainStream_Enlarge 8295 * 8296 * Grows this chain in the small block depot. 8297 */ 8298 static BOOL SmallBlockChainStream_Enlarge( 8299 SmallBlockChainStream* This, 8300 ULARGE_INTEGER newSize) 8301 { 8302 ULONG blockIndex, currentBlock; 8303 ULONG newNumBlocks; 8304 ULONG oldNumBlocks = 0; 8305 8306 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 8307 8308 /* 8309 * Empty chain. Create the head. 8310 */ 8311 if (blockIndex == BLOCK_END_OF_CHAIN) 8312 { 8313 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); 8314 SmallBlockChainStream_SetNextBlockInChain( 8315 This, 8316 blockIndex, 8317 BLOCK_END_OF_CHAIN); 8318 8319 if (This->headOfStreamPlaceHolder != NULL) 8320 { 8321 *(This->headOfStreamPlaceHolder) = blockIndex; 8322 } 8323 else 8324 { 8325 DirEntry chainEntry; 8326 8327 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry, 8328 &chainEntry); 8329 8330 chainEntry.startingBlock = blockIndex; 8331 8332 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry, 8333 &chainEntry); 8334 } 8335 } 8336 8337 currentBlock = blockIndex; 8338 8339 /* 8340 * Figure out how many blocks are needed to contain this stream 8341 */ 8342 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize; 8343 8344 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0) 8345 newNumBlocks++; 8346 8347 /* 8348 * Go to the current end of chain 8349 */ 8350 while (blockIndex != BLOCK_END_OF_CHAIN) 8351 { 8352 oldNumBlocks++; 8353 currentBlock = blockIndex; 8354 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex))) 8355 return FALSE; 8356 } 8357 8358 /* 8359 * Add new blocks to the chain 8360 */ 8361 while (oldNumBlocks < newNumBlocks) 8362 { 8363 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This); 8364 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex); 8365 8366 SmallBlockChainStream_SetNextBlockInChain( 8367 This, 8368 blockIndex, 8369 BLOCK_END_OF_CHAIN); 8370 8371 currentBlock = blockIndex; 8372 oldNumBlocks++; 8373 } 8374 8375 return TRUE; 8376 } 8377 8378 /****************************************************************************** 8379 * SmallBlockChainStream_SetSize 8380 * 8381 * Sets the size of this stream. 8382 * The file will grow if we grow the chain. 8383 * 8384 * TODO: Free the actual blocks in the file when we shrink the chain. 8385 * Currently, the blocks are still in the file. So the file size 8386 * doesn't shrink even if we shrink streams. 8387 */ 8388 BOOL SmallBlockChainStream_SetSize( 8389 SmallBlockChainStream* This, 8390 ULARGE_INTEGER newSize) 8391 { 8392 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This); 8393 8394 if (newSize.u.LowPart == size.u.LowPart) 8395 return TRUE; 8396 8397 if (newSize.u.LowPart < size.u.LowPart) 8398 { 8399 SmallBlockChainStream_Shrink(This, newSize); 8400 } 8401 else 8402 { 8403 SmallBlockChainStream_Enlarge(This, newSize); 8404 } 8405 8406 return TRUE; 8407 } 8408 8409 /****************************************************************************** 8410 * SmallBlockChainStream_GetCount 8411 * 8412 * Returns the number of small blocks that comprises this chain. 8413 * This is not the size of the stream as the last block may not be full! 8414 * 8415 */ 8416 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This) 8417 { 8418 ULONG blockIndex; 8419 ULONG count = 0; 8420 8421 blockIndex = SmallBlockChainStream_GetHeadOfChain(This); 8422 8423 while(blockIndex != BLOCK_END_OF_CHAIN) 8424 { 8425 count++; 8426 8427 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, 8428 blockIndex, &blockIndex))) 8429 return 0; 8430 } 8431 8432 return count; 8433 } 8434 8435 /****************************************************************************** 8436 * SmallBlockChainStream_GetSize 8437 * 8438 * Returns the size of this chain. 8439 */ 8440 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This) 8441 { 8442 DirEntry chainEntry; 8443 8444 if(This->headOfStreamPlaceHolder != NULL) 8445 { 8446 ULARGE_INTEGER result; 8447 result.u.HighPart = 0; 8448 8449 result.u.LowPart = SmallBlockChainStream_GetCount(This) * 8450 This->parentStorage->smallBlockSize; 8451 8452 return result; 8453 } 8454 8455 StorageImpl_ReadDirEntry( 8456 This->parentStorage, 8457 This->ownerDirEntry, 8458 &chainEntry); 8459 8460 return chainEntry.size; 8461 } 8462 8463 8464 /************************************************************************ 8465 * Miscellaneous storage functions 8466 ***********************************************************************/ 8467 8468 static HRESULT create_storagefile( 8469 LPCOLESTR pwcsName, 8470 DWORD grfMode, 8471 DWORD grfAttrs, 8472 STGOPTIONS* pStgOptions, 8473 REFIID riid, 8474 void** ppstgOpen) 8475 { 8476 StorageBaseImpl* newStorage = 0; 8477 HANDLE hFile = INVALID_HANDLE_VALUE; 8478 HRESULT hr = STG_E_INVALIDFLAG; 8479 DWORD shareMode; 8480 DWORD accessMode; 8481 DWORD creationMode; 8482 DWORD fileAttributes; 8483 WCHAR tempFileName[MAX_PATH]; 8484 8485 if (ppstgOpen == 0) 8486 return STG_E_INVALIDPOINTER; 8487 8488 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE) 8489 return STG_E_INVALIDPARAMETER; 8490 8491 /* if no share mode given then DENY_NONE is the default */ 8492 if (STGM_SHARE_MODE(grfMode) == 0) 8493 grfMode |= STGM_SHARE_DENY_NONE; 8494 8495 if ( FAILED( validateSTGM(grfMode) )) 8496 goto end; 8497 8498 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */ 8499 switch(STGM_ACCESS_MODE(grfMode)) 8500 { 8501 case STGM_WRITE: 8502 case STGM_READWRITE: 8503 break; 8504 default: 8505 goto end; 8506 } 8507 8508 /* in direct mode, can only use SHARE_EXCLUSIVE */ 8509 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)) 8510 goto end; 8511 8512 /* but in transacted mode, any share mode is valid */ 8513 8514 /* 8515 * Generate a unique name. 8516 */ 8517 if (pwcsName == 0) 8518 { 8519 WCHAR tempPath[MAX_PATH]; 8520 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 }; 8521 8522 memset(tempPath, 0, sizeof(tempPath)); 8523 memset(tempFileName, 0, sizeof(tempFileName)); 8524 8525 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 ) 8526 tempPath[0] = '.'; 8527 8528 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0) 8529 pwcsName = tempFileName; 8530 else 8531 { 8532 hr = STG_E_INSUFFICIENTMEMORY; 8533 goto end; 8534 } 8535 8536 creationMode = TRUNCATE_EXISTING; 8537 } 8538 else 8539 { 8540 creationMode = GetCreationModeFromSTGM(grfMode); 8541 } 8542 8543 /* 8544 * Interpret the STGM value grfMode 8545 */ 8546 shareMode = GetShareModeFromSTGM(grfMode); 8547 accessMode = GetAccessModeFromSTGM(grfMode); 8548 8549 if (grfMode & STGM_DELETEONRELEASE) 8550 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE; 8551 else 8552 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; 8553 8554 *ppstgOpen = 0; 8555 8556 hFile = CreateFileW(pwcsName, 8557 accessMode, 8558 shareMode, 8559 NULL, 8560 creationMode, 8561 fileAttributes, 8562 0); 8563 8564 if (hFile == INVALID_HANDLE_VALUE) 8565 { 8566 if(GetLastError() == ERROR_FILE_EXISTS) 8567 hr = STG_E_FILEALREADYEXISTS; 8568 else 8569 hr = E_FAIL; 8570 goto end; 8571 } 8572 8573 /* 8574 * Allocate and initialize the new IStorage object. 8575 */ 8576 hr = Storage_Construct( 8577 hFile, 8578 pwcsName, 8579 NULL, 8580 grfMode, 8581 TRUE, 8582 TRUE, 8583 pStgOptions->ulSectorSize, 8584 &newStorage); 8585 8586 if (FAILED(hr)) 8587 { 8588 goto end; 8589 } 8590 8591 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen); 8592 IStorage_Release(&newStorage->IStorage_iface); 8593 8594 end: 8595 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr); 8596 8597 return hr; 8598 } 8599 8600 /****************************************************************************** 8601 * StgCreateDocfile [OLE32.@] 8602 * Creates a new compound file storage object 8603 * 8604 * PARAMS 8605 * pwcsName [ I] Unicode string with filename (can be relative or NULL) 8606 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants) 8607 * reserved [ ?] unused?, usually 0 8608 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject 8609 * 8610 * RETURNS 8611 * S_OK if the file was successfully created 8612 * some STG_E_ value if error 8613 * NOTES 8614 * if pwcsName is NULL, create file with new unique name 8615 * the function can returns 8616 * STG_S_CONVERTED if the specified file was successfully converted to storage format 8617 * (unrealized now) 8618 */ 8619 HRESULT WINAPI StgCreateDocfile( 8620 LPCOLESTR pwcsName, 8621 DWORD grfMode, 8622 DWORD reserved, 8623 IStorage **ppstgOpen) 8624 { 8625 STGOPTIONS stgoptions = {1, 0, 512}; 8626 8627 TRACE("(%s, %x, %d, %p)\n", 8628 debugstr_w(pwcsName), grfMode, 8629 reserved, ppstgOpen); 8630 8631 if (ppstgOpen == 0) 8632 return STG_E_INVALIDPOINTER; 8633 if (reserved != 0) 8634 return STG_E_INVALIDPARAMETER; 8635 8636 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen); 8637 } 8638 8639 /****************************************************************************** 8640 * StgCreateStorageEx [OLE32.@] 8641 */ 8642 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) 8643 { 8644 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName), 8645 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); 8646 8647 if (stgfmt != STGFMT_FILE && grfAttrs != 0) 8648 { 8649 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n"); 8650 return STG_E_INVALIDPARAMETER; 8651 } 8652 8653 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING) 8654 { 8655 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n"); 8656 return STG_E_INVALIDPARAMETER; 8657 } 8658 8659 if (stgfmt == STGFMT_FILE) 8660 { 8661 ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); 8662 return STG_E_INVALIDPARAMETER; 8663 } 8664 8665 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE) 8666 { 8667 STGOPTIONS defaultOptions = {1, 0, 512}; 8668 8669 if (!pStgOptions) pStgOptions = &defaultOptions; 8670 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen); 8671 } 8672 8673 8674 ERR("Invalid stgfmt argument\n"); 8675 return STG_E_INVALIDPARAMETER; 8676 } 8677 8678 /****************************************************************************** 8679 * StgCreatePropSetStg [OLE32.@] 8680 */ 8681 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved, 8682 IPropertySetStorage **propset) 8683 { 8684 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset); 8685 if (reserved) 8686 return STG_E_INVALIDPARAMETER; 8687 8688 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset); 8689 } 8690 8691 /****************************************************************************** 8692 * StgOpenStorageEx [OLE32.@] 8693 */ 8694 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen) 8695 { 8696 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName), 8697 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen); 8698 8699 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0) 8700 { 8701 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n"); 8702 return STG_E_INVALIDPARAMETER; 8703 } 8704 8705 switch (stgfmt) 8706 { 8707 case STGFMT_FILE: 8708 ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); 8709 return STG_E_INVALIDPARAMETER; 8710 8711 case STGFMT_STORAGE: 8712 break; 8713 8714 case STGFMT_DOCFILE: 8715 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING) 8716 { 8717 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n"); 8718 return STG_E_INVALIDPARAMETER; 8719 } 8720 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n"); 8721 break; 8722 8723 case STGFMT_ANY: 8724 WARN("STGFMT_ANY assuming storage\n"); 8725 break; 8726 8727 default: 8728 return STG_E_INVALIDPARAMETER; 8729 } 8730 8731 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen); 8732 } 8733 8734 8735 /****************************************************************************** 8736 * StgOpenStorage [OLE32.@] 8737 */ 8738 HRESULT WINAPI StgOpenStorage( 8739 const OLECHAR *pwcsName, 8740 IStorage *pstgPriority, 8741 DWORD grfMode, 8742 SNB snbExclude, 8743 DWORD reserved, 8744 IStorage **ppstgOpen) 8745 { 8746 StorageBaseImpl* newStorage = 0; 8747 HRESULT hr = S_OK; 8748 HANDLE hFile = 0; 8749 DWORD shareMode; 8750 DWORD accessMode; 8751 LPWSTR temp_name = NULL; 8752 8753 TRACE("(%s, %p, %x, %p, %d, %p)\n", 8754 debugstr_w(pwcsName), pstgPriority, grfMode, 8755 snbExclude, reserved, ppstgOpen); 8756 8757 if (pstgPriority) 8758 { 8759 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */ 8760 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name); 8761 if (FAILED(hr)) goto end; 8762 pwcsName = temp_name; 8763 TRACE("using filename %s\n", debugstr_w(temp_name)); 8764 } 8765 8766 if (pwcsName == 0) 8767 { 8768 hr = STG_E_INVALIDNAME; 8769 goto end; 8770 } 8771 8772 if (ppstgOpen == 0) 8773 { 8774 hr = STG_E_INVALIDPOINTER; 8775 goto end; 8776 } 8777 8778 if (reserved) 8779 { 8780 hr = STG_E_INVALIDPARAMETER; 8781 goto end; 8782 } 8783 8784 if (grfMode & STGM_PRIORITY) 8785 { 8786 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT)) 8787 return STG_E_INVALIDFLAG; 8788 if (grfMode & STGM_DELETEONRELEASE) 8789 return STG_E_INVALIDFUNCTION; 8790 if(STGM_ACCESS_MODE(grfMode) != STGM_READ) 8791 return STG_E_INVALIDFLAG; 8792 grfMode &= ~0xf0; /* remove the existing sharing mode */ 8793 grfMode |= STGM_SHARE_DENY_NONE; 8794 } 8795 8796 /* 8797 * Validate the sharing mode 8798 */ 8799 if (grfMode & STGM_DIRECT_SWMR) 8800 { 8801 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) && 8802 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE)) 8803 { 8804 hr = STG_E_INVALIDFLAG; 8805 goto end; 8806 } 8807 } 8808 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY))) 8809 switch(STGM_SHARE_MODE(grfMode)) 8810 { 8811 case STGM_SHARE_EXCLUSIVE: 8812 case STGM_SHARE_DENY_WRITE: 8813 break; 8814 default: 8815 hr = STG_E_INVALIDFLAG; 8816 goto end; 8817 } 8818 8819 if ( FAILED( validateSTGM(grfMode) ) || 8820 (grfMode&STGM_CREATE)) 8821 { 8822 hr = STG_E_INVALIDFLAG; 8823 goto end; 8824 } 8825 8826 /* shared reading requires transacted or single writer mode */ 8827 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE && 8828 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE && 8829 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR)) 8830 { 8831 hr = STG_E_INVALIDFLAG; 8832 goto end; 8833 } 8834 8835 /* 8836 * Interpret the STGM value grfMode 8837 */ 8838 shareMode = GetShareModeFromSTGM(grfMode); 8839 accessMode = GetAccessModeFromSTGM(grfMode); 8840 8841 *ppstgOpen = 0; 8842 8843 hFile = CreateFileW( pwcsName, 8844 accessMode, 8845 shareMode, 8846 NULL, 8847 OPEN_EXISTING, 8848 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 8849 0); 8850 8851 if (hFile==INVALID_HANDLE_VALUE) 8852 { 8853 DWORD last_error = GetLastError(); 8854 8855 hr = E_FAIL; 8856 8857 switch (last_error) 8858 { 8859 case ERROR_FILE_NOT_FOUND: 8860 hr = STG_E_FILENOTFOUND; 8861 break; 8862 8863 case ERROR_PATH_NOT_FOUND: 8864 hr = STG_E_PATHNOTFOUND; 8865 break; 8866 8867 case ERROR_ACCESS_DENIED: 8868 case ERROR_WRITE_PROTECT: 8869 hr = STG_E_ACCESSDENIED; 8870 break; 8871 8872 case ERROR_SHARING_VIOLATION: 8873 hr = STG_E_SHAREVIOLATION; 8874 break; 8875 8876 default: 8877 hr = E_FAIL; 8878 } 8879 8880 goto end; 8881 } 8882 8883 /* 8884 * Refuse to open the file if it's too small to be a structured storage file 8885 * FIXME: verify the file when reading instead of here 8886 */ 8887 if (GetFileSize(hFile, NULL) < HEADER_SIZE) 8888 { 8889 CloseHandle(hFile); 8890 hr = STG_E_FILEALREADYEXISTS; 8891 goto end; 8892 } 8893 8894 /* 8895 * Allocate and initialize the new IStorage object. 8896 */ 8897 hr = Storage_Construct( 8898 hFile, 8899 pwcsName, 8900 NULL, 8901 grfMode, 8902 TRUE, 8903 FALSE, 8904 512, 8905 &newStorage); 8906 8907 if (FAILED(hr)) 8908 { 8909 /* 8910 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS 8911 */ 8912 if(hr == STG_E_INVALIDHEADER) 8913 hr = STG_E_FILEALREADYEXISTS; 8914 goto end; 8915 } 8916 8917 *ppstgOpen = &newStorage->IStorage_iface; 8918 8919 end: 8920 CoTaskMemFree(temp_name); 8921 if (pstgPriority) IStorage_Release(pstgPriority); 8922 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL); 8923 return hr; 8924 } 8925 8926 /****************************************************************************** 8927 * StgCreateDocfileOnILockBytes [OLE32.@] 8928 */ 8929 HRESULT WINAPI StgCreateDocfileOnILockBytes( 8930 ILockBytes *plkbyt, 8931 DWORD grfMode, 8932 DWORD reserved, 8933 IStorage** ppstgOpen) 8934 { 8935 StorageBaseImpl* newStorage = 0; 8936 HRESULT hr = S_OK; 8937 8938 if ((ppstgOpen == 0) || (plkbyt == 0)) 8939 return STG_E_INVALIDPOINTER; 8940 8941 /* 8942 * Allocate and initialize the new IStorage object. 8943 */ 8944 hr = Storage_Construct( 8945 0, 8946 0, 8947 plkbyt, 8948 grfMode, 8949 FALSE, 8950 TRUE, 8951 512, 8952 &newStorage); 8953 8954 if (FAILED(hr)) 8955 { 8956 return hr; 8957 } 8958 8959 *ppstgOpen = &newStorage->IStorage_iface; 8960 8961 return hr; 8962 } 8963 8964 /****************************************************************************** 8965 * StgOpenStorageOnILockBytes [OLE32.@] 8966 */ 8967 HRESULT WINAPI StgOpenStorageOnILockBytes( 8968 ILockBytes *plkbyt, 8969 IStorage *pstgPriority, 8970 DWORD grfMode, 8971 SNB snbExclude, 8972 DWORD reserved, 8973 IStorage **ppstgOpen) 8974 { 8975 StorageBaseImpl* newStorage = 0; 8976 HRESULT hr = S_OK; 8977 8978 if ((plkbyt == 0) || (ppstgOpen == 0)) 8979 return STG_E_INVALIDPOINTER; 8980 8981 if ( FAILED( validateSTGM(grfMode) )) 8982 return STG_E_INVALIDFLAG; 8983 8984 *ppstgOpen = 0; 8985 8986 /* 8987 * Allocate and initialize the new IStorage object. 8988 */ 8989 hr = Storage_Construct( 8990 0, 8991 0, 8992 plkbyt, 8993 grfMode, 8994 FALSE, 8995 FALSE, 8996 512, 8997 &newStorage); 8998 8999 if (FAILED(hr)) 9000 { 9001 return hr; 9002 } 9003 9004 *ppstgOpen = &newStorage->IStorage_iface; 9005 9006 return hr; 9007 } 9008 9009 /****************************************************************************** 9010 * StgSetTimes [ole32.@] 9011 * StgSetTimes [OLE32.@] 9012 * 9013 * 9014 */ 9015 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime, 9016 FILETIME const *patime, FILETIME const *pmtime) 9017 { 9018 IStorage *stg = NULL; 9019 HRESULT r; 9020 9021 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime); 9022 9023 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE, 9024 0, 0, &stg); 9025 if( SUCCEEDED(r) ) 9026 { 9027 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime); 9028 IStorage_Release(stg); 9029 } 9030 9031 return r; 9032 } 9033 9034 /****************************************************************************** 9035 * StgIsStorageILockBytes [OLE32.@] 9036 * 9037 * Determines if the ILockBytes contains a storage object. 9038 */ 9039 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt) 9040 { 9041 BYTE sig[sizeof(STORAGE_magic)]; 9042 ULARGE_INTEGER offset; 9043 ULONG read = 0; 9044 9045 offset.u.HighPart = 0; 9046 offset.u.LowPart = 0; 9047 9048 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read); 9049 9050 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0) 9051 return S_OK; 9052 9053 return S_FALSE; 9054 } 9055 9056 /****************************************************************************** 9057 * WriteClassStg [OLE32.@] 9058 * 9059 * This method will store the specified CLSID in the specified storage object 9060 */ 9061 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid) 9062 { 9063 if(!pStg) 9064 return E_INVALIDARG; 9065 9066 if(!rclsid) 9067 return STG_E_INVALIDPOINTER; 9068 9069 return IStorage_SetClass(pStg, rclsid); 9070 } 9071 9072 /*********************************************************************** 9073 * ReadClassStg (OLE32.@) 9074 * 9075 * This method reads the CLSID previously written to a storage object with 9076 * the WriteClassStg. 9077 * 9078 * PARAMS 9079 * pstg [I] IStorage pointer 9080 * pclsid [O] Pointer to where the CLSID is written 9081 * 9082 * RETURNS 9083 * Success: S_OK. 9084 * Failure: HRESULT code. 9085 */ 9086 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){ 9087 9088 STATSTG pstatstg; 9089 HRESULT hRes; 9090 9091 TRACE("(%p, %p)\n", pstg, pclsid); 9092 9093 if(!pstg || !pclsid) 9094 return E_INVALIDARG; 9095 9096 /* 9097 * read a STATSTG structure (contains the clsid) from the storage 9098 */ 9099 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME); 9100 9101 if(SUCCEEDED(hRes)) 9102 *pclsid=pstatstg.clsid; 9103 9104 return hRes; 9105 } 9106 9107 /*********************************************************************** 9108 * OleLoadFromStream (OLE32.@) 9109 * 9110 * This function loads an object from stream 9111 */ 9112 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj) 9113 { 9114 CLSID clsid; 9115 HRESULT res; 9116 LPPERSISTSTREAM xstm; 9117 9118 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj); 9119 9120 res=ReadClassStm(pStm,&clsid); 9121 if (FAILED(res)) 9122 return res; 9123 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj); 9124 if (FAILED(res)) 9125 return res; 9126 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm); 9127 if (FAILED(res)) { 9128 IUnknown_Release((IUnknown*)*ppvObj); 9129 return res; 9130 } 9131 res=IPersistStream_Load(xstm,pStm); 9132 IPersistStream_Release(xstm); 9133 /* FIXME: all refcounts ok at this point? I think they should be: 9134 * pStm : unchanged 9135 * ppvObj : 1 9136 * xstm : 0 (released) 9137 */ 9138 return res; 9139 } 9140 9141 /*********************************************************************** 9142 * OleSaveToStream (OLE32.@) 9143 * 9144 * This function saves an object with the IPersistStream interface on it 9145 * to the specified stream. 9146 */ 9147 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm) 9148 { 9149 9150 CLSID clsid; 9151 HRESULT res; 9152 9153 TRACE("(%p,%p)\n",pPStm,pStm); 9154 9155 res=IPersistStream_GetClassID(pPStm,&clsid); 9156 9157 if (SUCCEEDED(res)){ 9158 9159 res=WriteClassStm(pStm,&clsid); 9160 9161 if (SUCCEEDED(res)) 9162 9163 res=IPersistStream_Save(pPStm,pStm,TRUE); 9164 } 9165 9166 TRACE("Finished Save\n"); 9167 return res; 9168 } 9169 9170 /************************************************************************* 9171 * STORAGE_CreateOleStream [Internal] 9172 * 9173 * Creates the "\001OLE" stream in the IStorage if necessary. 9174 * 9175 * PARAMS 9176 * storage [I] Dest storage to create the stream in 9177 * flags [I] flags to be set for newly created stream 9178 * 9179 * RETURNS 9180 * HRESULT return value 9181 * 9182 * NOTES 9183 * 9184 * This stream is still unknown, MS Word seems to have extra data 9185 * but since the data is stored in the OLESTREAM there should be 9186 * no need to recreate the stream. If the stream is manually 9187 * deleted it will create it with this default data. 9188 * 9189 */ 9190 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags) 9191 { 9192 static const WCHAR stream_1oleW[] = {1,'O','l','e',0}; 9193 static const DWORD version_magic = 0x02000001; 9194 IStream *stream; 9195 HRESULT hr; 9196 9197 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream); 9198 if (hr == S_OK) 9199 { 9200 struct empty_1ole_stream { 9201 DWORD version_magic; 9202 DWORD flags; 9203 DWORD update_options; 9204 DWORD reserved; 9205 DWORD mon_stream_size; 9206 }; 9207 struct empty_1ole_stream stream_data; 9208 9209 stream_data.version_magic = version_magic; 9210 stream_data.flags = flags; 9211 stream_data.update_options = 0; 9212 stream_data.reserved = 0; 9213 stream_data.mon_stream_size = 0; 9214 9215 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL); 9216 IStream_Release(stream); 9217 } 9218 9219 return hr; 9220 } 9221 9222 /* write a string to a stream, preceded by its length */ 9223 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string ) 9224 { 9225 HRESULT r; 9226 LPSTR str; 9227 DWORD len = 0; 9228 9229 if( string ) 9230 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL); 9231 r = IStream_Write( stm, &len, sizeof(len), NULL); 9232 if( FAILED( r ) ) 9233 return r; 9234 if(len == 0) 9235 return r; 9236 str = CoTaskMemAlloc( len ); 9237 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL); 9238 r = IStream_Write( stm, str, len, NULL); 9239 CoTaskMemFree( str ); 9240 return r; 9241 } 9242 9243 /* read a string preceded by its length from a stream */ 9244 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string ) 9245 { 9246 HRESULT r; 9247 DWORD len, count = 0; 9248 LPSTR str; 9249 LPWSTR wstr; 9250 9251 r = IStream_Read( stm, &len, sizeof(len), &count ); 9252 if( FAILED( r ) ) 9253 return r; 9254 if( count != sizeof(len) ) 9255 return E_OUTOFMEMORY; 9256 9257 TRACE("%d bytes\n",len); 9258 9259 str = CoTaskMemAlloc( len ); 9260 if( !str ) 9261 return E_OUTOFMEMORY; 9262 count = 0; 9263 r = IStream_Read( stm, str, len, &count ); 9264 if( FAILED( r ) ) 9265 { 9266 CoTaskMemFree( str ); 9267 return r; 9268 } 9269 if( count != len ) 9270 { 9271 CoTaskMemFree( str ); 9272 return E_OUTOFMEMORY; 9273 } 9274 9275 TRACE("Read string %s\n",debugstr_an(str,len)); 9276 9277 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 ); 9278 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) ); 9279 if( wstr ) 9280 { 9281 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len ); 9282 wstr[len] = 0; 9283 } 9284 CoTaskMemFree( str ); 9285 9286 *string = wstr; 9287 9288 return r; 9289 } 9290 9291 9292 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid, 9293 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName ) 9294 { 9295 IStream *pstm; 9296 HRESULT r = S_OK; 9297 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 9298 9299 static const BYTE unknown1[12] = 9300 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 9301 0xFF, 0xFF, 0xFF, 0xFF}; 9302 static const BYTE unknown2[16] = 9303 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 9304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 9305 9306 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid), 9307 debugstr_w(lpszUserType), debugstr_w(szClipName), 9308 debugstr_w(szProgIDName)); 9309 9310 /* Create a CompObj stream */ 9311 r = IStorage_CreateStream(pstg, szwStreamName, 9312 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm ); 9313 if( FAILED (r) ) 9314 return r; 9315 9316 /* Write CompObj Structure to stream */ 9317 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL); 9318 9319 if( SUCCEEDED( r ) ) 9320 r = WriteClassStm( pstm, clsid ); 9321 9322 if( SUCCEEDED( r ) ) 9323 r = STREAM_WriteString( pstm, lpszUserType ); 9324 if( SUCCEEDED( r ) ) 9325 r = STREAM_WriteString( pstm, szClipName ); 9326 if( SUCCEEDED( r ) ) 9327 r = STREAM_WriteString( pstm, szProgIDName ); 9328 if( SUCCEEDED( r ) ) 9329 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL); 9330 9331 IStream_Release( pstm ); 9332 9333 return r; 9334 } 9335 9336 /*********************************************************************** 9337 * WriteFmtUserTypeStg (OLE32.@) 9338 */ 9339 HRESULT WINAPI WriteFmtUserTypeStg( 9340 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType) 9341 { 9342 STATSTG stat; 9343 HRESULT r; 9344 WCHAR szwClipName[0x40]; 9345 CLSID clsid; 9346 LPWSTR wstrProgID = NULL; 9347 DWORD n; 9348 9349 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType)); 9350 9351 /* get the clipboard format name */ 9352 if( cf ) 9353 { 9354 n = GetClipboardFormatNameW( cf, szwClipName, 9355 sizeof(szwClipName)/sizeof(szwClipName[0]) ); 9356 szwClipName[n]=0; 9357 } 9358 9359 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName)); 9360 9361 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME); 9362 if(SUCCEEDED(r)) 9363 clsid = stat.clsid; 9364 else 9365 clsid = CLSID_NULL; 9366 9367 ProgIDFromCLSID(&clsid, &wstrProgID); 9368 9369 TRACE("progid is %s\n",debugstr_w(wstrProgID)); 9370 9371 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType, 9372 cf ? szwClipName : NULL, wstrProgID ); 9373 9374 CoTaskMemFree(wstrProgID); 9375 9376 return r; 9377 } 9378 9379 9380 /****************************************************************************** 9381 * ReadFmtUserTypeStg [OLE32.@] 9382 */ 9383 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType) 9384 { 9385 HRESULT r; 9386 IStream *stm = 0; 9387 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 }; 9388 unsigned char unknown1[12]; 9389 unsigned char unknown2[16]; 9390 DWORD count; 9391 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL; 9392 CLSID clsid; 9393 9394 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType); 9395 9396 r = IStorage_OpenStream( pstg, szCompObj, NULL, 9397 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); 9398 if( FAILED ( r ) ) 9399 { 9400 WARN("Failed to open stream r = %08x\n", r); 9401 return r; 9402 } 9403 9404 /* read the various parts of the structure */ 9405 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count ); 9406 if( FAILED( r ) || ( count != sizeof(unknown1) ) ) 9407 goto end; 9408 r = ReadClassStm( stm, &clsid ); 9409 if( FAILED( r ) ) 9410 goto end; 9411 9412 r = STREAM_ReadString( stm, &szCLSIDName ); 9413 if( FAILED( r ) ) 9414 goto end; 9415 9416 r = STREAM_ReadString( stm, &szOleTypeName ); 9417 if( FAILED( r ) ) 9418 goto end; 9419 9420 r = STREAM_ReadString( stm, &szProgIDName ); 9421 if( FAILED( r ) ) 9422 goto end; 9423 9424 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count ); 9425 if( FAILED( r ) || ( count != sizeof(unknown2) ) ) 9426 goto end; 9427 9428 /* ok, success... now we just need to store what we found */ 9429 if( pcf ) 9430 *pcf = RegisterClipboardFormatW( szOleTypeName ); 9431 9432 if( lplpszUserType ) 9433 { 9434 *lplpszUserType = szCLSIDName; 9435 szCLSIDName = NULL; 9436 } 9437 9438 end: 9439 CoTaskMemFree( szCLSIDName ); 9440 CoTaskMemFree( szOleTypeName ); 9441 CoTaskMemFree( szProgIDName ); 9442 IStream_Release( stm ); 9443 9444 return r; 9445 } 9446 9447 /****************************************************************************** 9448 * StgIsStorageFile [OLE32.@] 9449 * Verify if the file contains a storage object 9450 * 9451 * PARAMS 9452 * fn [ I] Filename 9453 * 9454 * RETURNS 9455 * S_OK if file has magic bytes as a storage object 9456 * S_FALSE if file is not storage 9457 */ 9458 HRESULT WINAPI 9459 StgIsStorageFile(LPCOLESTR fn) 9460 { 9461 HANDLE hf; 9462 BYTE magic[8]; 9463 DWORD bytes_read; 9464 9465 TRACE("%s\n", debugstr_w(fn)); 9466 hf = CreateFileW(fn, GENERIC_READ, 9467 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 9468 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 9469 9470 if (hf == INVALID_HANDLE_VALUE) 9471 return STG_E_FILENOTFOUND; 9472 9473 if (!ReadFile(hf, magic, 8, &bytes_read, NULL)) 9474 { 9475 WARN(" unable to read file\n"); 9476 CloseHandle(hf); 9477 return S_FALSE; 9478 } 9479 9480 CloseHandle(hf); 9481 9482 if (bytes_read != 8) { 9483 TRACE(" too short\n"); 9484 return S_FALSE; 9485 } 9486 9487 if (!memcmp(magic,STORAGE_magic,8)) { 9488 TRACE(" -> YES\n"); 9489 return S_OK; 9490 } 9491 9492 TRACE(" -> Invalid header.\n"); 9493 return S_FALSE; 9494 } 9495 9496 /*********************************************************************** 9497 * WriteClassStm (OLE32.@) 9498 * 9499 * Writes a CLSID to a stream. 9500 * 9501 * PARAMS 9502 * pStm [I] Stream to write to. 9503 * rclsid [I] CLSID to write. 9504 * 9505 * RETURNS 9506 * Success: S_OK. 9507 * Failure: HRESULT code. 9508 */ 9509 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid) 9510 { 9511 TRACE("(%p,%p)\n",pStm,rclsid); 9512 9513 if (!pStm || !rclsid) 9514 return E_INVALIDARG; 9515 9516 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL); 9517 } 9518 9519 /*********************************************************************** 9520 * ReadClassStm (OLE32.@) 9521 * 9522 * Reads a CLSID from a stream. 9523 * 9524 * PARAMS 9525 * pStm [I] Stream to read from. 9526 * rclsid [O] CLSID to read. 9527 * 9528 * RETURNS 9529 * Success: S_OK. 9530 * Failure: HRESULT code. 9531 */ 9532 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid) 9533 { 9534 ULONG nbByte; 9535 HRESULT res; 9536 9537 TRACE("(%p,%p)\n",pStm,pclsid); 9538 9539 if (!pStm || !pclsid) 9540 return E_INVALIDARG; 9541 9542 /* clear the output args */ 9543 *pclsid = CLSID_NULL; 9544 9545 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte); 9546 9547 if (FAILED(res)) 9548 return res; 9549 9550 if (nbByte != sizeof(CLSID)) 9551 return STG_E_READFAULT; 9552 else 9553 return S_OK; 9554 } 9555 9556 9557 /************************************************************************ 9558 * OleConvert Functions 9559 ***********************************************************************/ 9560 9561 #define OLESTREAM_ID 0x501 9562 #define OLESTREAM_MAX_STR_LEN 255 9563 9564 /* OLESTREAM memory structure to use for Get and Put Routines */ 9565 typedef struct 9566 { 9567 DWORD dwOleID; 9568 DWORD dwTypeID; 9569 DWORD dwOleTypeNameLength; 9570 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; 9571 CHAR *pstrOleObjFileName; 9572 DWORD dwOleObjFileNameLength; 9573 DWORD dwMetaFileWidth; 9574 DWORD dwMetaFileHeight; 9575 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */ 9576 DWORD dwDataLength; 9577 BYTE *pData; 9578 } OLECONVERT_OLESTREAM_DATA; 9579 9580 /* CompObj Stream structure */ 9581 typedef struct 9582 { 9583 BYTE byUnknown1[12]; 9584 CLSID clsid; 9585 DWORD dwCLSIDNameLength; 9586 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN]; 9587 DWORD dwOleTypeNameLength; 9588 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN]; 9589 DWORD dwProgIDNameLength; 9590 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN]; 9591 BYTE byUnknown2[16]; 9592 } OLECONVERT_ISTORAGE_COMPOBJ; 9593 9594 /* Ole Presentation Stream structure */ 9595 typedef struct 9596 { 9597 BYTE byUnknown1[28]; 9598 DWORD dwExtentX; 9599 DWORD dwExtentY; 9600 DWORD dwSize; 9601 BYTE *pData; 9602 } OLECONVERT_ISTORAGE_OLEPRES; 9603 9604 9605 /************************************************************************* 9606 * OLECONVERT_LoadOLE10 [Internal] 9607 * 9608 * Loads the OLE10 STREAM to memory 9609 * 9610 * PARAMS 9611 * pOleStream [I] The OLESTREAM 9612 * pData [I] Data Structure for the OLESTREAM Data 9613 * 9614 * RETURNS 9615 * Success: S_OK 9616 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get 9617 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid 9618 * 9619 * NOTES 9620 * This function is used by OleConvertOLESTREAMToIStorage only. 9621 * 9622 * Memory allocated for pData must be freed by the caller 9623 */ 9624 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1) 9625 { 9626 DWORD dwSize; 9627 HRESULT hRes = S_OK; 9628 int nTryCnt=0; 9629 int max_try = 6; 9630 9631 pData->pData = NULL; 9632 pData->pstrOleObjFileName = NULL; 9633 9634 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++) 9635 { 9636 /* Get the OleID */ 9637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); 9638 if(dwSize != sizeof(pData->dwOleID)) 9639 { 9640 hRes = CONVERT10_E_OLESTREAM_GET; 9641 } 9642 else if(pData->dwOleID != OLESTREAM_ID) 9643 { 9644 hRes = CONVERT10_E_OLESTREAM_FMT; 9645 } 9646 else 9647 { 9648 hRes = S_OK; 9649 break; 9650 } 9651 } 9652 9653 if(hRes == S_OK) 9654 { 9655 /* Get the TypeID... more info needed for this field */ 9656 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); 9657 if(dwSize != sizeof(pData->dwTypeID)) 9658 { 9659 hRes = CONVERT10_E_OLESTREAM_GET; 9660 } 9661 } 9662 if(hRes == S_OK) 9663 { 9664 if(pData->dwTypeID != 0) 9665 { 9666 /* Get the length of the OleTypeName */ 9667 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); 9668 if(dwSize != sizeof(pData->dwOleTypeNameLength)) 9669 { 9670 hRes = CONVERT10_E_OLESTREAM_GET; 9671 } 9672 9673 if(hRes == S_OK) 9674 { 9675 if(pData->dwOleTypeNameLength > 0) 9676 { 9677 /* Get the OleTypeName */ 9678 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); 9679 if(dwSize != pData->dwOleTypeNameLength) 9680 { 9681 hRes = CONVERT10_E_OLESTREAM_GET; 9682 } 9683 } 9684 } 9685 if(bStrem1) 9686 { 9687 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength)); 9688 if(dwSize != sizeof(pData->dwOleObjFileNameLength)) 9689 { 9690 hRes = CONVERT10_E_OLESTREAM_GET; 9691 } 9692 if(hRes == S_OK) 9693 { 9694 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */ 9695 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength); 9696 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength); 9697 if(pData->pstrOleObjFileName) 9698 { 9699 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength); 9700 if(dwSize != pData->dwOleObjFileNameLength) 9701 { 9702 hRes = CONVERT10_E_OLESTREAM_GET; 9703 } 9704 } 9705 else 9706 hRes = CONVERT10_E_OLESTREAM_GET; 9707 } 9708 } 9709 else 9710 { 9711 /* Get the Width of the Metafile */ 9712 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); 9713 if(dwSize != sizeof(pData->dwMetaFileWidth)) 9714 { 9715 hRes = CONVERT10_E_OLESTREAM_GET; 9716 } 9717 if(hRes == S_OK) 9718 { 9719 /* Get the Height of the Metafile */ 9720 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); 9721 if(dwSize != sizeof(pData->dwMetaFileHeight)) 9722 { 9723 hRes = CONVERT10_E_OLESTREAM_GET; 9724 } 9725 } 9726 } 9727 if(hRes == S_OK) 9728 { 9729 /* Get the Length of the Data */ 9730 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); 9731 if(dwSize != sizeof(pData->dwDataLength)) 9732 { 9733 hRes = CONVERT10_E_OLESTREAM_GET; 9734 } 9735 } 9736 9737 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */ 9738 { 9739 if(!bStrem1) /* if it is a second OLE stream data */ 9740 { 9741 pData->dwDataLength -= 8; 9742 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown)); 9743 if(dwSize != sizeof(pData->strUnknown)) 9744 { 9745 hRes = CONVERT10_E_OLESTREAM_GET; 9746 } 9747 } 9748 } 9749 if(hRes == S_OK) 9750 { 9751 if(pData->dwDataLength > 0) 9752 { 9753 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength); 9754 9755 /* Get Data (ex. IStorage, Metafile, or BMP) */ 9756 if(pData->pData) 9757 { 9758 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength); 9759 if(dwSize != pData->dwDataLength) 9760 { 9761 hRes = CONVERT10_E_OLESTREAM_GET; 9762 } 9763 } 9764 else 9765 { 9766 hRes = CONVERT10_E_OLESTREAM_GET; 9767 } 9768 } 9769 } 9770 } 9771 } 9772 return hRes; 9773 } 9774 9775 /************************************************************************* 9776 * OLECONVERT_SaveOLE10 [Internal] 9777 * 9778 * Saves the OLE10 STREAM From memory 9779 * 9780 * PARAMS 9781 * pData [I] Data Structure for the OLESTREAM Data 9782 * pOleStream [I] The OLESTREAM to save 9783 * 9784 * RETURNS 9785 * Success: S_OK 9786 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put 9787 * 9788 * NOTES 9789 * This function is used by OleConvertIStorageToOLESTREAM only. 9790 * 9791 */ 9792 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream) 9793 { 9794 DWORD dwSize; 9795 HRESULT hRes = S_OK; 9796 9797 9798 /* Set the OleID */ 9799 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID)); 9800 if(dwSize != sizeof(pData->dwOleID)) 9801 { 9802 hRes = CONVERT10_E_OLESTREAM_PUT; 9803 } 9804 9805 if(hRes == S_OK) 9806 { 9807 /* Set the TypeID */ 9808 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID)); 9809 if(dwSize != sizeof(pData->dwTypeID)) 9810 { 9811 hRes = CONVERT10_E_OLESTREAM_PUT; 9812 } 9813 } 9814 9815 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK) 9816 { 9817 /* Set the Length of the OleTypeName */ 9818 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength)); 9819 if(dwSize != sizeof(pData->dwOleTypeNameLength)) 9820 { 9821 hRes = CONVERT10_E_OLESTREAM_PUT; 9822 } 9823 9824 if(hRes == S_OK) 9825 { 9826 if(pData->dwOleTypeNameLength > 0) 9827 { 9828 /* Set the OleTypeName */ 9829 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength); 9830 if(dwSize != pData->dwOleTypeNameLength) 9831 { 9832 hRes = CONVERT10_E_OLESTREAM_PUT; 9833 } 9834 } 9835 } 9836 9837 if(hRes == S_OK) 9838 { 9839 /* Set the width of the Metafile */ 9840 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth)); 9841 if(dwSize != sizeof(pData->dwMetaFileWidth)) 9842 { 9843 hRes = CONVERT10_E_OLESTREAM_PUT; 9844 } 9845 } 9846 9847 if(hRes == S_OK) 9848 { 9849 /* Set the height of the Metafile */ 9850 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight)); 9851 if(dwSize != sizeof(pData->dwMetaFileHeight)) 9852 { 9853 hRes = CONVERT10_E_OLESTREAM_PUT; 9854 } 9855 } 9856 9857 if(hRes == S_OK) 9858 { 9859 /* Set the length of the Data */ 9860 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength)); 9861 if(dwSize != sizeof(pData->dwDataLength)) 9862 { 9863 hRes = CONVERT10_E_OLESTREAM_PUT; 9864 } 9865 } 9866 9867 if(hRes == S_OK) 9868 { 9869 if(pData->dwDataLength > 0) 9870 { 9871 /* Set the Data (eg. IStorage, Metafile, Bitmap) */ 9872 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength); 9873 if(dwSize != pData->dwDataLength) 9874 { 9875 hRes = CONVERT10_E_OLESTREAM_PUT; 9876 } 9877 } 9878 } 9879 } 9880 return hRes; 9881 } 9882 9883 /************************************************************************* 9884 * OLECONVERT_GetOLE20FromOLE10[Internal] 9885 * 9886 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk, 9887 * opens it, and copies the content to the dest IStorage for 9888 * OleConvertOLESTREAMToIStorage 9889 * 9890 * 9891 * PARAMS 9892 * pDestStorage [I] The IStorage to copy the data to 9893 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM 9894 * nBufferLength [I] The size of the buffer 9895 * 9896 * RETURNS 9897 * Nothing 9898 * 9899 * NOTES 9900 * 9901 * 9902 */ 9903 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength) 9904 { 9905 HRESULT hRes; 9906 HANDLE hFile; 9907 IStorage *pTempStorage; 9908 DWORD dwNumOfBytesWritten; 9909 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH]; 9910 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0}; 9911 9912 /* Create a temp File */ 9913 GetTempPathW(MAX_PATH, wstrTempDir); 9914 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile); 9915 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 9916 9917 if(hFile != INVALID_HANDLE_VALUE) 9918 { 9919 /* Write IStorage Data to File */ 9920 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL); 9921 CloseHandle(hFile); 9922 9923 /* Open and copy temp storage to the Dest Storage */ 9924 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage); 9925 if(hRes == S_OK) 9926 { 9927 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage); 9928 IStorage_Release(pTempStorage); 9929 } 9930 DeleteFileW(wstrTempFile); 9931 } 9932 } 9933 9934 9935 /************************************************************************* 9936 * OLECONVERT_WriteOLE20ToBuffer [Internal] 9937 * 9938 * Saves the OLE10 STREAM From memory 9939 * 9940 * PARAMS 9941 * pStorage [I] The Src IStorage to copy 9942 * pData [I] The Dest Memory to write to. 9943 * 9944 * RETURNS 9945 * The size in bytes allocated for pData 9946 * 9947 * NOTES 9948 * Memory allocated for pData must be freed by the caller 9949 * 9950 * Used by OleConvertIStorageToOLESTREAM only. 9951 * 9952 */ 9953 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData) 9954 { 9955 HANDLE hFile; 9956 HRESULT hRes; 9957 DWORD nDataLength = 0; 9958 IStorage *pTempStorage; 9959 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH]; 9960 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0}; 9961 9962 *pData = NULL; 9963 9964 /* Create temp Storage */ 9965 GetTempPathW(MAX_PATH, wstrTempDir); 9966 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile); 9967 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage); 9968 9969 if(hRes == S_OK) 9970 { 9971 /* Copy Src Storage to the Temp Storage */ 9972 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage); 9973 IStorage_Release(pTempStorage); 9974 9975 /* Open Temp Storage as a file and copy to memory */ 9976 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 9977 if(hFile != INVALID_HANDLE_VALUE) 9978 { 9979 nDataLength = GetFileSize(hFile, NULL); 9980 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength); 9981 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0); 9982 CloseHandle(hFile); 9983 } 9984 DeleteFileW(wstrTempFile); 9985 } 9986 return nDataLength; 9987 } 9988 9989 /************************************************************************* 9990 * OLECONVERT_CreateCompObjStream [Internal] 9991 * 9992 * Creates a "\001CompObj" is the destination IStorage if necessary. 9993 * 9994 * PARAMS 9995 * pStorage [I] The dest IStorage to create the CompObj Stream 9996 * if necessary. 9997 * strOleTypeName [I] The ProgID 9998 * 9999 * RETURNS 10000 * Success: S_OK 10001 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream 10002 * 10003 * NOTES 10004 * This function is used by OleConvertOLESTREAMToIStorage only. 10005 * 10006 * The stream data is stored in the OLESTREAM and there should be 10007 * no need to recreate the stream. If the stream is manually 10008 * deleted it will attempt to create it by querying the registry. 10009 * 10010 * 10011 */ 10012 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName) 10013 { 10014 IStream *pStream; 10015 HRESULT hStorageRes, hRes = S_OK; 10016 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj; 10017 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 10018 WCHAR bufferW[OLESTREAM_MAX_STR_LEN]; 10019 10020 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; 10021 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71}; 10022 10023 /* Initialize the CompObj structure */ 10024 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj)); 10025 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1)); 10026 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2)); 10027 10028 10029 /* Create a CompObj stream if it doesn't exist */ 10030 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName, 10031 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 10032 if(hStorageRes == S_OK) 10033 { 10034 /* copy the OleTypeName to the compobj struct */ 10035 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1; 10036 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName); 10037 10038 /* copy the OleTypeName to the compobj struct */ 10039 /* Note: in the test made, these were Identical */ 10040 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1; 10041 strcpy(IStorageCompObj.strProgIDName, strOleTypeName); 10042 10043 /* Get the CLSID */ 10044 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1, 10045 bufferW, OLESTREAM_MAX_STR_LEN ); 10046 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid)); 10047 10048 if(hRes == S_OK) 10049 { 10050 HKEY hKey; 10051 LONG hErr; 10052 /* Get the CLSID Default Name from the Registry */ 10053 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey); 10054 if(hErr == ERROR_SUCCESS) 10055 { 10056 char strTemp[OLESTREAM_MAX_STR_LEN]; 10057 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN; 10058 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength)); 10059 if(hErr == ERROR_SUCCESS) 10060 { 10061 strcpy(IStorageCompObj.strCLSIDName, strTemp); 10062 } 10063 RegCloseKey(hKey); 10064 } 10065 } 10066 10067 /* Write CompObj Structure to stream */ 10068 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL); 10069 10070 WriteClassStm(pStream,&(IStorageCompObj.clsid)); 10071 10072 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL); 10073 if(IStorageCompObj.dwCLSIDNameLength > 0) 10074 { 10075 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL); 10076 } 10077 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL); 10078 if(IStorageCompObj.dwOleTypeNameLength > 0) 10079 { 10080 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL); 10081 } 10082 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL); 10083 if(IStorageCompObj.dwProgIDNameLength > 0) 10084 { 10085 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL); 10086 } 10087 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL); 10088 IStream_Release(pStream); 10089 } 10090 return hRes; 10091 } 10092 10093 10094 /************************************************************************* 10095 * OLECONVERT_CreateOlePresStream[Internal] 10096 * 10097 * Creates the "\002OlePres000" Stream with the Metafile data 10098 * 10099 * PARAMS 10100 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in. 10101 * dwExtentX [I] Width of the Metafile 10102 * dwExtentY [I] Height of the Metafile 10103 * pData [I] Metafile data 10104 * dwDataLength [I] Size of the Metafile data 10105 * 10106 * RETURNS 10107 * Success: S_OK 10108 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put 10109 * 10110 * NOTES 10111 * This function is used by OleConvertOLESTREAMToIStorage only. 10112 * 10113 */ 10114 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength) 10115 { 10116 HRESULT hRes; 10117 IStream *pStream; 10118 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0}; 10119 BYTE pOlePresStreamHeader [] = 10120 { 10121 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 10122 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 10123 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 10124 0x00, 0x00, 0x00, 0x00 10125 }; 10126 10127 BYTE pOlePresStreamHeaderEmpty [] = 10128 { 10129 0x00, 0x00, 0x00, 0x00, 10130 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 10131 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 10132 0x00, 0x00, 0x00, 0x00 10133 }; 10134 10135 /* Create the OlePres000 Stream */ 10136 hRes = IStorage_CreateStream(pStorage, wstrStreamName, 10137 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 10138 10139 if(hRes == S_OK) 10140 { 10141 DWORD nHeaderSize; 10142 OLECONVERT_ISTORAGE_OLEPRES OlePres; 10143 10144 memset(&OlePres, 0, sizeof(OlePres)); 10145 /* Do we have any metafile data to save */ 10146 if(dwDataLength > 0) 10147 { 10148 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader)); 10149 nHeaderSize = sizeof(pOlePresStreamHeader); 10150 } 10151 else 10152 { 10153 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty)); 10154 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty); 10155 } 10156 /* Set width and height of the metafile */ 10157 OlePres.dwExtentX = dwExtentX; 10158 OlePres.dwExtentY = -dwExtentY; 10159 10160 /* Set Data and Length */ 10161 if(dwDataLength > sizeof(METAFILEPICT16)) 10162 { 10163 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16); 10164 OlePres.pData = &(pData[8]); 10165 } 10166 /* Save OlePres000 Data to Stream */ 10167 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL); 10168 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL); 10169 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL); 10170 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL); 10171 if(OlePres.dwSize > 0) 10172 { 10173 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL); 10174 } 10175 IStream_Release(pStream); 10176 } 10177 } 10178 10179 /************************************************************************* 10180 * OLECONVERT_CreateOle10NativeStream [Internal] 10181 * 10182 * Creates the "\001Ole10Native" Stream (should contain a BMP) 10183 * 10184 * PARAMS 10185 * pStorage [I] Dest storage to create the stream in 10186 * pData [I] Ole10 Native Data (ex. bmp) 10187 * dwDataLength [I] Size of the Ole10 Native Data 10188 * 10189 * RETURNS 10190 * Nothing 10191 * 10192 * NOTES 10193 * This function is used by OleConvertOLESTREAMToIStorage only. 10194 * 10195 * Might need to verify the data and return appropriate error message 10196 * 10197 */ 10198 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength) 10199 { 10200 HRESULT hRes; 10201 IStream *pStream; 10202 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 10203 10204 /* Create the Ole10Native Stream */ 10205 hRes = IStorage_CreateStream(pStorage, wstrStreamName, 10206 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream ); 10207 10208 if(hRes == S_OK) 10209 { 10210 /* Write info to stream */ 10211 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL); 10212 hRes = IStream_Write(pStream, pData, dwDataLength, NULL); 10213 IStream_Release(pStream); 10214 } 10215 10216 } 10217 10218 /************************************************************************* 10219 * OLECONVERT_GetOLE10ProgID [Internal] 10220 * 10221 * Finds the ProgID (or OleTypeID) from the IStorage 10222 * 10223 * PARAMS 10224 * pStorage [I] The Src IStorage to get the ProgID 10225 * strProgID [I] the ProgID string to get 10226 * dwSize [I] the size of the string 10227 * 10228 * RETURNS 10229 * Success: S_OK 10230 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream 10231 * 10232 * NOTES 10233 * This function is used by OleConvertIStorageToOLESTREAM only. 10234 * 10235 * 10236 */ 10237 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize) 10238 { 10239 HRESULT hRes; 10240 IStream *pStream; 10241 LARGE_INTEGER iSeekPos; 10242 OLECONVERT_ISTORAGE_COMPOBJ CompObj; 10243 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0}; 10244 10245 /* Open the CompObj Stream */ 10246 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 10247 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 10248 if(hRes == S_OK) 10249 { 10250 10251 /*Get the OleType from the CompObj Stream */ 10252 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid); 10253 iSeekPos.u.HighPart = 0; 10254 10255 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); 10256 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL); 10257 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength; 10258 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); 10259 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL); 10260 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength; 10261 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL); 10262 10263 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL); 10264 if(*dwSize > 0) 10265 { 10266 IStream_Read(pStream, strProgID, *dwSize, NULL); 10267 } 10268 IStream_Release(pStream); 10269 } 10270 else 10271 { 10272 STATSTG stat; 10273 LPOLESTR wstrProgID; 10274 10275 /* Get the OleType from the registry */ 10276 REFCLSID clsid = &(stat.clsid); 10277 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME); 10278 hRes = ProgIDFromCLSID(clsid, &wstrProgID); 10279 if(hRes == S_OK) 10280 { 10281 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE); 10282 CoTaskMemFree(wstrProgID); 10283 } 10284 10285 } 10286 return hRes; 10287 } 10288 10289 /************************************************************************* 10290 * OLECONVERT_GetOle10PresData [Internal] 10291 * 10292 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream 10293 * 10294 * PARAMS 10295 * pStorage [I] Src IStroage 10296 * pOleStream [I] Dest OleStream Mem Struct 10297 * 10298 * RETURNS 10299 * Nothing 10300 * 10301 * NOTES 10302 * This function is used by OleConvertIStorageToOLESTREAM only. 10303 * 10304 * Memory allocated for pData must be freed by the caller 10305 * 10306 * 10307 */ 10308 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) 10309 { 10310 10311 HRESULT hRes; 10312 IStream *pStream; 10313 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 10314 10315 /* Initialize Default data for OLESTREAM */ 10316 pOleStreamData[0].dwOleID = OLESTREAM_ID; 10317 pOleStreamData[0].dwTypeID = 2; 10318 pOleStreamData[1].dwOleID = OLESTREAM_ID; 10319 pOleStreamData[1].dwTypeID = 0; 10320 pOleStreamData[0].dwMetaFileWidth = 0; 10321 pOleStreamData[0].dwMetaFileHeight = 0; 10322 pOleStreamData[0].pData = NULL; 10323 pOleStreamData[1].pData = NULL; 10324 10325 /* Open Ole10Native Stream */ 10326 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 10327 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 10328 if(hRes == S_OK) 10329 { 10330 10331 /* Read Size and Data */ 10332 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL); 10333 if(pOleStreamData->dwDataLength > 0) 10334 { 10335 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength); 10336 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL); 10337 } 10338 IStream_Release(pStream); 10339 } 10340 10341 } 10342 10343 10344 /************************************************************************* 10345 * OLECONVERT_GetOle20PresData[Internal] 10346 * 10347 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream 10348 * 10349 * PARAMS 10350 * pStorage [I] Src IStroage 10351 * pOleStreamData [I] Dest OleStream Mem Struct 10352 * 10353 * RETURNS 10354 * Nothing 10355 * 10356 * NOTES 10357 * This function is used by OleConvertIStorageToOLESTREAM only. 10358 * 10359 * Memory allocated for pData must be freed by the caller 10360 */ 10361 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData) 10362 { 10363 HRESULT hRes; 10364 IStream *pStream; 10365 OLECONVERT_ISTORAGE_OLEPRES olePress; 10366 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0}; 10367 10368 /* Initialize Default data for OLESTREAM */ 10369 pOleStreamData[0].dwOleID = OLESTREAM_ID; 10370 pOleStreamData[0].dwTypeID = 2; 10371 pOleStreamData[0].dwMetaFileWidth = 0; 10372 pOleStreamData[0].dwMetaFileHeight = 0; 10373 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData)); 10374 pOleStreamData[1].dwOleID = OLESTREAM_ID; 10375 pOleStreamData[1].dwTypeID = 0; 10376 pOleStreamData[1].dwOleTypeNameLength = 0; 10377 pOleStreamData[1].strOleTypeName[0] = 0; 10378 pOleStreamData[1].dwMetaFileWidth = 0; 10379 pOleStreamData[1].dwMetaFileHeight = 0; 10380 pOleStreamData[1].pData = NULL; 10381 pOleStreamData[1].dwDataLength = 0; 10382 10383 10384 /* Open OlePress000 stream */ 10385 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL, 10386 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream ); 10387 if(hRes == S_OK) 10388 { 10389 LARGE_INTEGER iSeekPos; 10390 METAFILEPICT16 MetaFilePict; 10391 static const char strMetafilePictName[] = "METAFILEPICT"; 10392 10393 /* Set the TypeID for a Metafile */ 10394 pOleStreamData[1].dwTypeID = 5; 10395 10396 /* Set the OleTypeName to Metafile */ 10397 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1; 10398 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName); 10399 10400 iSeekPos.u.HighPart = 0; 10401 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1); 10402 10403 /* Get Presentation Data */ 10404 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL); 10405 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL); 10406 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL); 10407 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL); 10408 10409 /*Set width and Height */ 10410 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX; 10411 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY; 10412 if(olePress.dwSize > 0) 10413 { 10414 /* Set Length */ 10415 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16); 10416 10417 /* Set MetaFilePict struct */ 10418 MetaFilePict.mm = 8; 10419 MetaFilePict.xExt = olePress.dwExtentX; 10420 MetaFilePict.yExt = olePress.dwExtentY; 10421 MetaFilePict.hMF = 0; 10422 10423 /* Get Metafile Data */ 10424 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength); 10425 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict)); 10426 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL); 10427 } 10428 IStream_Release(pStream); 10429 } 10430 } 10431 10432 /************************************************************************* 10433 * OleConvertOLESTREAMToIStorage [OLE32.@] 10434 * 10435 * Read info on MSDN 10436 * 10437 * TODO 10438 * DVTARGETDEVICE parameter is not handled 10439 * Still unsure of some mem fields for OLE 10 Stream 10440 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", 10441 * and "\001OLE" streams 10442 * 10443 */ 10444 HRESULT WINAPI OleConvertOLESTREAMToIStorage ( 10445 LPOLESTREAM pOleStream, 10446 LPSTORAGE pstg, 10447 const DVTARGETDEVICE* ptd) 10448 { 10449 int i; 10450 HRESULT hRes=S_OK; 10451 OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; 10452 10453 TRACE("%p %p %p\n", pOleStream, pstg, ptd); 10454 10455 memset(pOleStreamData, 0, sizeof(pOleStreamData)); 10456 10457 if(ptd != NULL) 10458 { 10459 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n"); 10460 } 10461 10462 if(pstg == NULL || pOleStream == NULL) 10463 { 10464 hRes = E_INVALIDARG; 10465 } 10466 10467 if(hRes == S_OK) 10468 { 10469 /* Load the OLESTREAM to Memory */ 10470 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE); 10471 } 10472 10473 if(hRes == S_OK) 10474 { 10475 /* Load the OLESTREAM to Memory (part 2)*/ 10476 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE); 10477 } 10478 10479 if(hRes == S_OK) 10480 { 10481 10482 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic)) 10483 { 10484 /* Do we have the IStorage Data in the OLESTREAM */ 10485 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0) 10486 { 10487 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 10488 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength); 10489 } 10490 else 10491 { 10492 /* It must be an original OLE 1.0 source */ 10493 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 10494 } 10495 } 10496 else 10497 { 10498 /* It must be an original OLE 1.0 source */ 10499 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength); 10500 } 10501 10502 /* Create CompObj Stream if necessary */ 10503 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName); 10504 if(hRes == S_OK) 10505 { 10506 /*Create the Ole Stream if necessary */ 10507 STORAGE_CreateOleStream(pstg, 0); 10508 } 10509 } 10510 10511 10512 /* Free allocated memory */ 10513 for(i=0; i < 2; i++) 10514 { 10515 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); 10516 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName); 10517 pOleStreamData[i].pstrOleObjFileName = NULL; 10518 } 10519 return hRes; 10520 } 10521 10522 /************************************************************************* 10523 * OleConvertIStorageToOLESTREAM [OLE32.@] 10524 * 10525 * Read info on MSDN 10526 * 10527 * Read info on MSDN 10528 * 10529 * TODO 10530 * Still unsure of some mem fields for OLE 10 Stream 10531 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj", 10532 * and "\001OLE" streams. 10533 * 10534 */ 10535 HRESULT WINAPI OleConvertIStorageToOLESTREAM ( 10536 LPSTORAGE pstg, 10537 LPOLESTREAM pOleStream) 10538 { 10539 int i; 10540 HRESULT hRes = S_OK; 10541 IStream *pStream; 10542 OLECONVERT_OLESTREAM_DATA pOleStreamData[2]; 10543 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0}; 10544 10545 TRACE("%p %p\n", pstg, pOleStream); 10546 10547 memset(pOleStreamData, 0, sizeof(pOleStreamData)); 10548 10549 if(pstg == NULL || pOleStream == NULL) 10550 { 10551 hRes = E_INVALIDARG; 10552 } 10553 if(hRes == S_OK) 10554 { 10555 /* Get the ProgID */ 10556 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN; 10557 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength)); 10558 } 10559 if(hRes == S_OK) 10560 { 10561 /* Was it originally Ole10 */ 10562 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream); 10563 if(hRes == S_OK) 10564 { 10565 IStream_Release(pStream); 10566 /* Get Presentation Data for Ole10Native */ 10567 OLECONVERT_GetOle10PresData(pstg, pOleStreamData); 10568 } 10569 else 10570 { 10571 /* Get Presentation Data (OLE20) */ 10572 OLECONVERT_GetOle20PresData(pstg, pOleStreamData); 10573 } 10574 10575 /* Save OLESTREAM */ 10576 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream); 10577 if(hRes == S_OK) 10578 { 10579 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream); 10580 } 10581 10582 } 10583 10584 /* Free allocated memory */ 10585 for(i=0; i < 2; i++) 10586 { 10587 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData); 10588 } 10589 10590 return hRes; 10591 } 10592 10593 enum stream_1ole_flags { 10594 OleStream_LinkedObject = 0x00000001, 10595 OleStream_Convert = 0x00000004 10596 }; 10597 10598 /*********************************************************************** 10599 * GetConvertStg (OLE32.@) 10600 */ 10601 HRESULT WINAPI GetConvertStg(IStorage *stg) 10602 { 10603 static const WCHAR stream_1oleW[] = {1,'O','l','e',0}; 10604 static const DWORD version_magic = 0x02000001; 10605 DWORD header[2]; 10606 IStream *stream; 10607 HRESULT hr; 10608 10609 TRACE("%p\n", stg); 10610 10611 if (!stg) return E_INVALIDARG; 10612 10613 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream); 10614 if (FAILED(hr)) return hr; 10615 10616 hr = IStream_Read(stream, header, sizeof(header), NULL); 10617 IStream_Release(stream); 10618 if (FAILED(hr)) return hr; 10619 10620 if (header[0] != version_magic) 10621 { 10622 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]); 10623 return E_FAIL; 10624 } 10625 10626 return header[1] & OleStream_Convert ? S_OK : S_FALSE; 10627 } 10628 10629 /*********************************************************************** 10630 * SetConvertStg (OLE32.@) 10631 */ 10632 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert) 10633 { 10634 static const WCHAR stream_1oleW[] = {1,'O','l','e',0}; 10635 DWORD flags = convert ? OleStream_Convert : 0; 10636 IStream *stream; 10637 DWORD header[2]; 10638 HRESULT hr; 10639 10640 TRACE("(%p, %d)\n", storage, convert); 10641 10642 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream); 10643 if (FAILED(hr)) 10644 { 10645 if (hr != STG_E_FILENOTFOUND) 10646 return hr; 10647 10648 return STORAGE_CreateOleStream(storage, flags); 10649 } 10650 10651 hr = IStream_Read(stream, header, sizeof(header), NULL); 10652 if (FAILED(hr)) 10653 { 10654 IStream_Release(stream); 10655 return hr; 10656 } 10657 10658 /* update flag if differs */ 10659 if ((header[1] ^ flags) & OleStream_Convert) 10660 { 10661 LARGE_INTEGER pos = {{0}}; 10662 10663 if (header[1] & OleStream_Convert) 10664 flags = header[1] & ~OleStream_Convert; 10665 else 10666 flags = header[1] | OleStream_Convert; 10667 10668 pos.QuadPart = sizeof(DWORD); 10669 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); 10670 if (FAILED(hr)) 10671 { 10672 IStream_Release(stream); 10673 return hr; 10674 } 10675 10676 hr = IStream_Write(stream, &flags, sizeof(flags), NULL); 10677 } 10678 10679 IStream_Release(stream); 10680 return hr; 10681 } 10682