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