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