storage32.c 286 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * Compound Storage (32 bit version)
 * Storage implementation
 *
 * This file contains the compound file implementation
 * of the storage interface.
 *
 * Copyright 1999 Francis Beaudet
 * Copyright 1999 Sylvain St-Germain
 * Copyright 1999 Thuy Nguyen
11
 * Copyright 2005 Mike McCormack
12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 28 29 30
 *
 * NOTES
 *  The compound file implementation of IStorage used for create
 *  and manage substorages and streams within a storage object
 *  residing in a compound file object.
31 32 33
 */

#include <assert.h>
34
#include <stdarg.h>
35
#include <stdio.h>
Patrik Stridvall's avatar
Patrik Stridvall committed
36
#include <stdlib.h>
37 38
#include <string.h>

39
#define COBJMACROS
40
#define NONAMELESSUNION
41

42 43
#include "windef.h"
#include "winbase.h"
44
#include "winnls.h"
45
#include "winuser.h"
46
#include "wine/unicode.h"
47
#include "wine/debug.h"
48 49

#include "storage32.h"
50
#include "ole2.h"      /* For Write/ReadClassStm */
51

52 53
#include "winreg.h"
#include "wine/wingdi16.h"
54
#include "compobj_private.h"
55

56
WINE_DEFAULT_DEBUG_CHANNEL(storage);
57

58

59 60 61 62 63 64
/*
 * These are signatures to detect the type of Document file.
 */
static const BYTE STORAGE_magic[8]    ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};

65
extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
66

67

68
/****************************************************************************
69
 * StorageInternalImpl definitions.
70
 *
71 72
 * Definition of the implementation structure for the IStorage interface.
 * This one implements the IStorage interface for storage that are
73 74
 * inside another storage.
 */
75
typedef struct StorageInternalImpl
76 77
{
  struct StorageBaseImpl base;
78

79
  /*
80
   * Entry in the parent's stream tracking list
81
   */
82
  struct list ParentListEntry;
83 84

  StorageBaseImpl *parentStorage;
85
} StorageInternalImpl;
86

87
static const IStorageVtbl StorageInternalImpl_Vtbl;
88
static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
89

90 91 92 93 94 95 96
typedef struct TransactedDirEntry
{
  /* If applicable, a reference to the original DirEntry in the transacted
   * parent. If this is a newly-created entry, DIRENTRY_NULL. */
  DirRef transactedParentEntry;

  /* True if this entry is being used. */
97
  BOOL inuse;
98 99

  /* True if data is up to date. */
100
  BOOL read;
101 102

  /* True if this entry has been modified. */
103
  BOOL dirty;
104 105

  /* True if this entry's stream has been modified. */
106
  BOOL stream_dirty;
107 108 109

  /* True if this entry has been deleted in the transacted storage, but the
   * delete has not yet been committed. */
110
  BOOL deleted;
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

  /* If this entry's stream has been modified, a reference to where the stream
   * is stored in the snapshot file. */
  DirRef stream_entry;

  /* This directory entry's data, including any changes that have been made. */
  DirEntry data;

  /* A reference to the parent of this node. This is only valid while we are
   * committing changes. */
  DirRef parent;

  /* A reference to a newly-created entry in the transacted parent. This is
   * always equal to transactedParentEntry except when committing changes. */
  DirRef newTransactedParentEntry;
} TransactedDirEntry;

128

129
/****************************************************************************
130
 * Transacted storage object.
131 132 133 134 135
 */
typedef struct TransactedSnapshotImpl
{
  struct StorageBaseImpl base;

136
  /*
137
   * Modified streams are temporarily saved to the scratch file.
138
   */
139 140 141 142 143 144 145
  StorageBaseImpl *scratch;

  /* The directory structure is kept here, so that we can track how these
   * entries relate to those in the parent storage. */
  TransactedDirEntry *entries;
  ULONG entries_size;
  ULONG firstFreeEntry;
146

147 148 149 150
  /*
   * Changes are committed to the transacted parent.
   */
  StorageBaseImpl *transactedParent;
151 152 153

  /* The transaction signature from when we last committed */
  ULONG lastTransactionSig;
154 155
} TransactedSnapshotImpl;

156 157 158
static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
typedef struct TransactedSharedImpl
{
  struct StorageBaseImpl base;

  /*
   * Snapshot and uncommitted changes go here.
   */
  TransactedSnapshotImpl *scratch;

  /*
   * Changes are committed to the transacted parent.
   */
  StorageBaseImpl *transactedParent;

  /* The transaction signature from when we last committed */
  ULONG lastTransactionSig;
} TransactedSharedImpl;

177

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/****************************************************************************
 * BlockChainStream definitions.
 *
 * The BlockChainStream class is a utility class that is used to create an
 * abstraction of the big block chains in the storage file.
 */

struct BlockChainRun
{
  /* This represents a range of blocks that happen reside in consecutive sectors. */
  ULONG firstSector;
  ULONG firstOffset;
  ULONG lastOffset;
};

typedef struct BlockChainBlock
{
  ULONG index;
  ULONG sector;
  BOOL  read;
  BOOL  dirty;
  BYTE data[MAX_BIG_BLOCK_SIZE];
} BlockChainBlock;

struct BlockChainStream
{
  StorageImpl* parentStorage;
  ULONG*       headOfStreamPlaceHolder;
  DirRef       ownerDirEntry;
  struct BlockChainRun* indexCache;
  ULONG        indexCacheLen;
  ULONG        indexCacheSize;
  BlockChainBlock cachedBlocks[2];
  ULONG        blockToEvict;
  ULONG        tailIndex;
  ULONG        numBlocks;
};

/* Returns the number of blocks that comprises this chain.
 * This is not the size of the stream as the last block may not be full!
 */
static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
{
  return This->numBlocks;
}

static BlockChainStream* BlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
static void BlockChainStream_Destroy(BlockChainStream*);
static HRESULT BlockChainStream_ReadAt(BlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
static HRESULT BlockChainStream_WriteAt(BlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
static HRESULT BlockChainStream_Flush(BlockChainStream*);
229
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
static BOOL BlockChainStream_SetSize(BlockChainStream*,ULARGE_INTEGER);


/****************************************************************************
 * SmallBlockChainStream definitions.
 *
 * The SmallBlockChainStream class is a utility class that is used to create an
 * abstraction of the small block chains in the storage file.
 */

struct SmallBlockChainStream
{
  StorageImpl* parentStorage;
  DirRef         ownerDirEntry;
  ULONG*         headOfStreamPlaceHolder;
};
246

247 248 249 250 251 252
static SmallBlockChainStream* SmallBlockChainStream_Construct(StorageImpl*,ULONG*,DirRef);
static void SmallBlockChainStream_Destroy(SmallBlockChainStream*);
static HRESULT SmallBlockChainStream_ReadAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,void*,ULONG*);
static HRESULT SmallBlockChainStream_WriteAt(SmallBlockChainStream*,ULARGE_INTEGER,ULONG,const void*,ULONG*);
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
static BOOL SmallBlockChainStream_SetSize(SmallBlockChainStream*,ULARGE_INTEGER);
253 254


255 256 257
/************************************************************************
 * STGM Functions
 ***********************************************************************/
258

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
/************************************************************************
 * This method validates an STGM parameter that can contain the values below
 *
 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
 * The stgm values contained in 0xffff0000 are bitmasks.
 *
 * STGM_DIRECT               0x00000000
 * STGM_TRANSACTED           0x00010000
 * STGM_SIMPLE               0x08000000
 *
 * STGM_READ                 0x00000000
 * STGM_WRITE                0x00000001
 * STGM_READWRITE            0x00000002
 *
 * STGM_SHARE_DENY_NONE      0x00000040
 * STGM_SHARE_DENY_READ      0x00000030
 * STGM_SHARE_DENY_WRITE     0x00000020
 * STGM_SHARE_EXCLUSIVE      0x00000010
 *
 * STGM_PRIORITY             0x00040000
 * STGM_DELETEONRELEASE      0x04000000
 *
 * STGM_CREATE               0x00001000
 * STGM_CONVERT              0x00020000
 * STGM_FAILIFTHERE          0x00000000
 *
 * STGM_NOSCRATCH            0x00100000
 * STGM_NOSNAPSHOT           0x00200000
287
 */
288 289 290 291 292
static HRESULT validateSTGM(DWORD stgm)
{
  DWORD access = STGM_ACCESS_MODE(stgm);
  DWORD share  = STGM_SHARE_MODE(stgm);
  DWORD create = STGM_CREATE_MODE(stgm);
293

294 295 296 297 298
  if (stgm&~STGM_KNOWN_FLAGS)
  {
    ERR("unknown flags %08x\n", stgm);
    return E_FAIL;
  }
299

300 301 302 303 304 305 306 307 308
  switch (access)
  {
  case STGM_READ:
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
    return E_FAIL;
  }
309

310 311 312 313 314 315 316 317 318 319 320 321 322 323
  switch (share)
  {
  case STGM_SHARE_DENY_NONE:
  case STGM_SHARE_DENY_READ:
  case STGM_SHARE_DENY_WRITE:
  case STGM_SHARE_EXCLUSIVE:
    break;
  case 0:
    if (!(stgm & STGM_TRANSACTED))
      return E_FAIL;
    break;
  default:
    return E_FAIL;
  }
324

325 326 327 328 329 330 331 332
  switch (create)
  {
  case STGM_CREATE:
  case STGM_FAILIFTHERE:
    break;
  default:
    return E_FAIL;
  }
333

334 335 336 337 338
  /*
   * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
   */
  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
      return E_FAIL;
339

340 341 342 343 344 345
  /*
   * STGM_CREATE | STGM_CONVERT
   * if both are false, STGM_FAILIFTHERE is set to TRUE
   */
  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
    return E_FAIL;
346

347 348 349 350 351
  /*
   * STGM_NOSCRATCH requires STGM_TRANSACTED
   */
  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
    return E_FAIL;
352

353 354 355 356 357 358 359 360 361
  /*
   * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
   * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
   */
  if ( (stgm & STGM_NOSNAPSHOT) &&
        (!(stgm & STGM_TRANSACTED) ||
         share == STGM_SHARE_EXCLUSIVE ||
         share == STGM_SHARE_DENY_WRITE) )
    return E_FAIL;
362

363 364
  return S_OK;
}
365

366 367
/************************************************************************
 *      GetShareModeFromSTGM
368
 *
369 370
 * This method will return a share mode flag from a STGM value.
 * The STGM value is assumed valid.
371
 */
372
static DWORD GetShareModeFromSTGM(DWORD stgm)
373
{
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
  switch (STGM_SHARE_MODE(stgm))
  {
  case 0:
    assert(stgm & STGM_TRANSACTED);
    /* fall-through */
  case STGM_SHARE_DENY_NONE:
    return FILE_SHARE_READ | FILE_SHARE_WRITE;
  case STGM_SHARE_DENY_READ:
    return FILE_SHARE_WRITE;
  case STGM_SHARE_DENY_WRITE:
  case STGM_SHARE_EXCLUSIVE:
    return FILE_SHARE_READ;
  }
  ERR("Invalid share mode!\n");
  assert(0);
  return 0;
}
391

392 393 394 395 396 397 398
/************************************************************************
 *      GetAccessModeFromSTGM
 *
 * This method will return an access mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetAccessModeFromSTGM(DWORD stgm)
399
{
400 401 402 403 404 405 406 407 408 409 410
  switch (STGM_ACCESS_MODE(stgm))
  {
  case STGM_READ:
    return GENERIC_READ;
  case STGM_WRITE:
  case STGM_READWRITE:
    return GENERIC_READ | GENERIC_WRITE;
  }
  ERR("Invalid access mode!\n");
  assert(0);
  return 0;
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
/************************************************************************
 *      GetCreationModeFromSTGM
 *
 * This method will return a creation mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetCreationModeFromSTGM(DWORD stgm)
{
  switch(STGM_CREATE_MODE(stgm))
  {
  case STGM_CREATE:
    return CREATE_ALWAYS;
  case STGM_CONVERT:
    FIXME("STGM_CONVERT not implemented!\n");
    return CREATE_NEW;
  case STGM_FAILIFTHERE:
    return CREATE_NEW;
  }
  ERR("Invalid create mode!\n");
  assert(0);
  return 0;
}
435 436


437
/************************************************************************
438 439
 * IDirectWriterLock implementation
 ***********************************************************************/
440

441
static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
442
{
443
    return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
444
}
445

446
static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
447
{
448 449
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
450 451
}

452
static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
453
{
454 455
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  return IStorage_AddRef(&This->IStorage_iface);
456
}
457

458
static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
459
{
460 461 462
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  return IStorage_Release(&This->IStorage_iface);
}
463

464 465 466 467 468 469
static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
{
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  FIXME("(%p)->(%d): stub\n", This, timeout);
  return E_NOTIMPL;
}
470

471 472 473 474 475 476
static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
{
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  FIXME("(%p): stub\n", This);
  return E_NOTIMPL;
}
477

478 479 480 481 482 483
static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
{
  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
  FIXME("(%p): stub\n", This);
  return E_NOTIMPL;
}
484

485 486 487 488 489 490 491 492 493
static const IDirectWriterLockVtbl DirectWriterLockVtbl =
{
  directwriterlock_QueryInterface,
  directwriterlock_AddRef,
  directwriterlock_Release,
  directwriterlock_WaitForWriteAccess,
  directwriterlock_ReleaseWriteAccess,
  directwriterlock_HaveWriteAccess
};
494

495

496
/************************************************************************
497 498 499 500
 * StorageBaseImpl implementation : Tree helper functions
 ***********************************************************************/

/****************************************************************************
501
 *
502
 * Internal Method
503
 *
504 505 506 507 508 509
 * Case insensitive comparison of DirEntry.name by first considering
 * their size.
 *
 * Returns <0 when name1 < name2
 *         >0 when name1 > name2
 *          0 when name1 == name2
510
 */
511 512 513
static LONG entryNameCmp(
    const OLECHAR *name1,
    const OLECHAR *name2)
514
{
515
  LONG diff      = lstrlenW(name1) - lstrlenW(name2);
516

517 518 519 520 521 522 523
  while (diff == 0 && *name1 != 0)
  {
    /*
     * We compare the string themselves only when they are of the same length
     */
    diff = toupperW(*name1++) - toupperW(*name2++);
  }
524

525
  return diff;
526
}
527

528
/****************************************************************************
529
 *
530
 * Internal Method
531
 *
532
 * Find and read the element of a storage with the given name.
533
 */
534 535
static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
    const OLECHAR *name, DirEntry *data)
536
{
537
  DirRef currentEntry;
538

539 540
  /* Read the storage entry to find the root of the tree. */
  StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
541

542
  currentEntry = data->dirRootEntry;
543

544
  while (currentEntry != DIRENTRY_NULL)
545
  {
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
    LONG cmp;

    StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);

    cmp = entryNameCmp(name, data->name);

    if (cmp == 0)
      /* found it */
      break;

    else if (cmp < 0)
      currentEntry = data->leftChild;

    else if (cmp > 0)
      currentEntry = data->rightChild;
561
  }
562

563
  return currentEntry;
564 565
}

566
/****************************************************************************
567
 *
568
 * Internal Method
569
 *
570 571 572 573
 * Find and read the binary tree parent of the element with the given name.
 *
 * If there is no such element, find a place where it could be inserted and
 * return STG_E_FILENOTFOUND.
574
 */
575 576 577
static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
    const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
    ULONG *relation)
578
{
579 580
  DirRef childEntry;
  DirEntry childData;
581

582 583
  /* Read the storage entry to find the root of the tree. */
  StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
584

585 586
  *parentEntry = storageEntry;
  *relation = DIRENTRY_RELATION_DIR;
587

588
  childEntry = parentData->dirRootEntry;
589

590
  while (childEntry != DIRENTRY_NULL)
Alexandre Julliard's avatar
Alexandre Julliard committed
591
  {
592
    LONG cmp;
593

594
    StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
595

596
    cmp = entryNameCmp(childName, childData.name);
597

598 599 600 601 602
    if (cmp == 0)
      /* found it */
      break;

    else if (cmp < 0)
603
    {
604 605 606
      *parentData = childData;
      *parentEntry = childEntry;
      *relation = DIRENTRY_RELATION_PREVIOUS;
607

608 609
      childEntry = parentData->leftChild;
    }
610

611
    else if (cmp > 0)
612
    {
613 614 615
      *parentData = childData;
      *parentEntry = childEntry;
      *relation = DIRENTRY_RELATION_NEXT;
616

617
      childEntry = parentData->rightChild;
618 619
    }
  }
620

621 622 623 624 625
  if (childEntry == DIRENTRY_NULL)
    return STG_E_FILENOTFOUND;
  else
    return S_OK;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
626

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
{
  switch (relation)
  {
    case DIRENTRY_RELATION_PREVIOUS:
      entry->leftChild = new_target;
      break;
    case DIRENTRY_RELATION_NEXT:
      entry->rightChild = new_target;
      break;
    case DIRENTRY_RELATION_DIR:
      entry->dirRootEntry = new_target;
      break;
    default:
      assert(0);
  }
643 644
}

645
/****************************************************************************
646
 *
647
 * Internal Method
648
 *
649
 * Add a directory entry to a storage
650
 */
651 652 653 654
static HRESULT insertIntoTree(
  StorageBaseImpl *This,
  DirRef        parentStorageIndex,
  DirRef        newEntryIndex)
655
{
656 657
  DirEntry currentEntry;
  DirEntry newEntry;
658 659

  /*
660
   * Read the inserted entry
661
   */
662 663 664
  StorageBaseImpl_ReadDirEntry(This,
                               newEntryIndex,
                               &newEntry);
665

666
  /*
667
   * Read the storage entry
668
   */
669 670 671
  StorageBaseImpl_ReadDirEntry(This,
                               parentStorageIndex,
                               &currentEntry);
672

673 674 675 676 677 678 679 680
  if (currentEntry.dirRootEntry != DIRENTRY_NULL)
  {
    /*
     * The root storage contains some element, therefore, start the research
     * for the appropriate location.
     */
    BOOL found = FALSE;
    DirRef current, next, previous, currentEntryId;
681

682 683 684 685
    /*
     * Keep a reference to the root of the storage's element tree
     */
    currentEntryId = currentEntry.dirRootEntry;
686

687 688 689 690 691 692
    /*
     * Read
     */
    StorageBaseImpl_ReadDirEntry(This,
                                 currentEntry.dirRootEntry,
                                 &currentEntry);
693

694 695 696
    previous = currentEntry.leftChild;
    next     = currentEntry.rightChild;
    current  = currentEntryId;
697

698
    while (!found)
699
    {
700
      LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
701

702 703 704
      if (diff < 0)
      {
        if (previous != DIRENTRY_NULL)
705
        {
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
          StorageBaseImpl_ReadDirEntry(This,
                                       previous,
                                       &currentEntry);
          current = previous;
        }
        else
        {
          currentEntry.leftChild = newEntryIndex;
          StorageBaseImpl_WriteDirEntry(This,
                                        current,
                                        &currentEntry);
          found = TRUE;
        }
      }
      else if (diff > 0)
      {
        if (next != DIRENTRY_NULL)
        {
          StorageBaseImpl_ReadDirEntry(This,
                                       next,
                                       &currentEntry);
          current = next;
        }
        else
        {
          currentEntry.rightChild = newEntryIndex;
          StorageBaseImpl_WriteDirEntry(This,
                                        current,
                                        &currentEntry);
          found = TRUE;
736 737 738 739
        }
      }
      else
      {
740 741 742 743 744
	/*
	 * Trying to insert an item with the same name in the
	 * subtree structure.
	 */
	return STG_E_FILEALREADYEXISTS;
745
      }
746

747 748
      previous = currentEntry.leftChild;
      next     = currentEntry.rightChild;
749
    }
750 751 752 753 754 755 756 757 758 759
  }
  else
  {
    /*
     * The storage is empty, make the new entry the root of its element tree
     */
    currentEntry.dirRootEntry = newEntryIndex;
    StorageBaseImpl_WriteDirEntry(This,
                                  parentStorageIndex,
                                  &currentEntry);
760
  }
761

762
  return S_OK;
763 764
}

765
/*************************************************************************
766
 *
767
 * Internal Method
768
 *
769 770
 * This method removes a directory entry from its parent storage tree without
 * freeing any resources attached to it.
771
 */
772 773 774 775
static HRESULT removeFromTree(
  StorageBaseImpl *This,
  DirRef        parentStorageIndex,
  DirRef        deletedIndex)
776
{
777 778 779 780 781
  DirEntry   entryToDelete;
  DirEntry   parentEntry;
  DirRef parentEntryRef;
  ULONG typeOfRelation;
  HRESULT hr;
782

783
  hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
784

785 786
  if (hr != S_OK)
    return hr;
787

788 789 790 791 792
  /*
   * Find the element that links to the one we want to delete.
   */
  hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
    &parentEntry, &parentEntryRef, &typeOfRelation);
793

794 795
  if (hr != S_OK)
    return hr;
796

797
  if (entryToDelete.leftChild != DIRENTRY_NULL)
798
  {
799 800 801 802
    /*
     * Replace the deleted entry with its left child
     */
    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
803

804 805 806 807 808 809 810 811
    hr = StorageBaseImpl_WriteDirEntry(
            This,
            parentEntryRef,
            &parentEntry);
    if(FAILED(hr))
    {
      return hr;
    }
812

813 814 815 816 817 818 819 820 821
    if (entryToDelete.rightChild != DIRENTRY_NULL)
    {
      /*
       * We need to reinsert the right child somewhere. We already know it and
       * its children are greater than everything in the left tree, so we
       * insert it at the rightmost point in the left tree.
       */
      DirRef newRightChildParent = entryToDelete.leftChild;
      DirEntry newRightChildParentEntry;
822

823 824 825 826 827 828 829 830 831 832
      do
      {
        hr = StorageBaseImpl_ReadDirEntry(
                This,
                newRightChildParent,
                &newRightChildParentEntry);
        if (FAILED(hr))
        {
          return hr;
        }
833

834 835 836
        if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
          newRightChildParent = newRightChildParentEntry.rightChild;
      } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
837

838
      newRightChildParentEntry.rightChild = entryToDelete.rightChild;
839

840 841 842 843 844 845 846 847 848 849 850
      hr = StorageBaseImpl_WriteDirEntry(
              This,
              newRightChildParent,
              &newRightChildParentEntry);
      if (FAILED(hr))
      {
        return hr;
      }
    }
  }
  else
851
  {
852 853 854 855
    /*
     * Replace the deleted entry with its right child
     */
    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
856

857 858 859 860 861 862 863 864
    hr = StorageBaseImpl_WriteDirEntry(
            This,
            parentEntryRef,
            &parentEntry);
    if(FAILED(hr))
    {
      return hr;
    }
865
  }
866

867
  return hr;
868 869
}

870

871
/************************************************************************
872 873 874 875 876
 * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
 ***********************************************************************/

/*
 * IEnumSTATSTGImpl definitions.
877
 *
878
 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
879
 * This class allows iterating through the content of a storage and finding
880
 * specific items inside it.
881
 */
882
struct IEnumSTATSTGImpl
883
{
884
  IEnumSTATSTG   IEnumSTATSTG_iface;
885

886 887 888
  LONG           ref;                   /* Reference count */
  StorageBaseImpl* parentStorage;         /* Reference to the parent storage */
  DirRef         storageDirEntry;     /* Directory entry of the storage to enumerate */
889

890 891
  WCHAR	         name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
};
892

893 894 895 896
static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
{
  return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
}
897

898 899 900 901 902
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
{
  IStorage_Release(&This->parentStorage->IStorage_iface);
  HeapFree(GetProcessHeap(), 0, This);
}
903

904 905 906 907 908 909
static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
  IEnumSTATSTG*     iface,
  REFIID            riid,
  void**            ppvObject)
{
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
910

911 912
  if (ppvObject==0)
    return E_INVALIDARG;
913

914
  *ppvObject = 0;
915

916 917
  if (IsEqualGUID(&IID_IUnknown, riid) ||
      IsEqualGUID(&IID_IEnumSTATSTG, riid))
918
  {
919 920 921
    *ppvObject = &This->IEnumSTATSTG_iface;
    IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
    return S_OK;
922 923
  }

924
  return E_NOINTERFACE;
925 926
}

927 928
static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
  IEnumSTATSTG* iface)
929
{
930 931 932
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
  return InterlockedIncrement(&This->ref);
}
933

934 935 936 937
static ULONG   WINAPI IEnumSTATSTGImpl_Release(
  IEnumSTATSTG* iface)
{
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
938

939
  ULONG newRef;
940

941
  newRef = InterlockedDecrement(&This->ref);
942

943 944 945
  if (newRef==0)
  {
    IEnumSTATSTGImpl_Destroy(This);
946
  }
947

948 949
  return newRef;
}
950

951 952 953 954 955 956 957 958 959
static HRESULT IEnumSTATSTGImpl_GetNextRef(
  IEnumSTATSTGImpl* This,
  DirRef *ref)
{
  DirRef result = DIRENTRY_NULL;
  DirRef searchNode;
  DirEntry entry;
  HRESULT hr;
  WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
960

961 962 963
  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
    This->parentStorage->storageDirEntry, &entry);
  searchNode = entry.dirRootEntry;
964

965
  while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
966
  {
967 968 969
    hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);

    if (SUCCEEDED(hr))
970
    {
971 972 973 974 975 976 977 978 979 980 981 982
      LONG diff = entryNameCmp( entry.name, This->name);

      if (diff <= 0)
      {
        searchNode = entry.rightChild;
      }
      else
      {
        result = searchNode;
        memcpy(result_name, entry.name, sizeof(result_name));
        searchNode = entry.leftChild;
      }
983
    }
984 985
  }

986 987 988 989 990 991
  if (SUCCEEDED(hr))
  {
    *ref = result;
    if (result != DIRENTRY_NULL)
      memcpy(This->name, result_name, sizeof(result_name));
  }
992

993 994
  return hr;
}
995

996 997 998 999 1000 1001 1002
static HRESULT WINAPI IEnumSTATSTGImpl_Next(
  IEnumSTATSTG* iface,
  ULONG             celt,
  STATSTG*          rgelt,
  ULONG*            pceltFetched)
{
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1003

1004 1005 1006 1007 1008
  DirEntry    currentEntry;
  STATSTG*    currentReturnStruct = rgelt;
  ULONG       objectFetched       = 0;
  DirRef      currentSearchNode;
  HRESULT     hr=S_OK;
1009

1010 1011
  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
    return E_INVALIDARG;
1012

1013 1014
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;
1015 1016

  /*
1017 1018
   * To avoid the special case, get another pointer to a ULONG value if
   * the caller didn't supply one.
1019
   */
1020 1021
  if (pceltFetched==0)
    pceltFetched = &objectFetched;
1022

1023
  /*
1024 1025
   * Start the iteration, we will iterate until we hit the end of the
   * linked list or until we hit the number of items to iterate through
1026
   */
1027 1028 1029
  *pceltFetched = 0;

  while ( *pceltFetched < celt )
1030
  {
1031
    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1032

1033 1034
    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
      break;
1035

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
    /*
     * Read the entry from the storage.
     */
    StorageBaseImpl_ReadDirEntry(This->parentStorage,
      currentSearchNode,
      &currentEntry);

    /*
     * Copy the information to the return buffer.
     */
    StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
      currentReturnStruct,
      &currentEntry,
      STATFLAG_DEFAULT);

    /*
     * Step to the next item in the iteration
     */
    (*pceltFetched)++;
    currentReturnStruct++;
1056 1057
  }

1058 1059 1060 1061
  if (SUCCEEDED(hr) && *pceltFetched != celt)
    hr = S_FALSE;

  return hr;
1062 1063
}

1064 1065 1066 1067

static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
  IEnumSTATSTG* iface,
  ULONG             celt)
1068
{
1069
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1070

1071 1072 1073
  ULONG       objectFetched = 0;
  DirRef      currentSearchNode;
  HRESULT     hr=S_OK;
1074

1075
  if (This->parentStorage->reverted)
1076 1077
    return STG_E_REVERTED;

1078
  while ( (objectFetched < celt) )
1079
  {
1080
    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1081

1082 1083
    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
      break;
1084

1085 1086
    objectFetched++;
  }
1087

1088 1089
  if (SUCCEEDED(hr) && objectFetched != celt)
    return S_FALSE;
1090

1091 1092 1093 1094 1095
  return hr;
}

static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
  IEnumSTATSTG* iface)
1096
{
1097
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
1098

1099 1100
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;
1101

1102
  This->name[0] = 0;
1103

1104 1105
  return S_OK;
}
1106

1107
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
1108

1109 1110 1111 1112 1113 1114
static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
  IEnumSTATSTG* iface,
  IEnumSTATSTG**    ppenum)
{
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
  IEnumSTATSTGImpl* newClone;
1115

1116 1117
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;
1118

1119 1120 1121 1122 1123 1124
  if (ppenum==0)
    return E_INVALIDARG;

  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
               This->storageDirEntry);
  if (!newClone)
1125
  {
1126 1127
    *ppenum = NULL;
    return E_OUTOFMEMORY;
1128
  }
1129

1130
  /*
1131 1132
   * The new clone enumeration must point to the same current node as
   * the old one.
1133
   */
1134
  memcpy(newClone->name, This->name, sizeof(newClone->name));
1135

1136
  *ppenum = &newClone->IEnumSTATSTG_iface;
1137

1138 1139
  return S_OK;
}
1140

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
/*
 * Virtual function table for the IEnumSTATSTGImpl class.
 */
static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
{
    IEnumSTATSTGImpl_QueryInterface,
    IEnumSTATSTGImpl_AddRef,
    IEnumSTATSTGImpl_Release,
    IEnumSTATSTGImpl_Next,
    IEnumSTATSTGImpl_Skip,
    IEnumSTATSTGImpl_Reset,
    IEnumSTATSTGImpl_Clone
};
1154

1155 1156 1157 1158 1159
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
  StorageBaseImpl* parentStorage,
  DirRef         storageDirEntry)
{
  IEnumSTATSTGImpl* newEnumeration;
1160

1161 1162 1163
  newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));

  if (newEnumeration)
1164
  {
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
    newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
    newEnumeration->ref = 1;
    newEnumeration->name[0] = 0;

    /*
     * We want to nail-down the reference to the storage in case the
     * enumeration out-lives the storage in the client application.
     */
    newEnumeration->parentStorage = parentStorage;
    IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);

    newEnumeration->storageDirEntry = storageDirEntry;
1177
  }
1178

1179 1180
  return newEnumeration;
}
1181 1182


1183 1184 1185
/************************************************************************
 * StorageBaseImpl implementation
 ***********************************************************************/
1186

1187 1188 1189 1190
static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
{
    return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
}
1191

1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
/************************************************************************
 * StorageBaseImpl_QueryInterface (IUnknown)
 *
 * This method implements the common QueryInterface for all IStorage
 * implementations contained in this file.
 *
 * See Windows documentation for more details on IUnknown methods.
 */
static HRESULT WINAPI StorageBaseImpl_QueryInterface(
  IStorage*        iface,
  REFIID             riid,
  void**             ppvObject)
{
  StorageBaseImpl *This = impl_from_IStorage(iface);
1206

1207 1208
  if (!ppvObject)
    return E_INVALIDARG;
1209

1210 1211 1212 1213
  *ppvObject = 0;

  if (IsEqualGUID(&IID_IUnknown, riid) ||
      IsEqualGUID(&IID_IStorage, riid))
1214
  {
1215
    *ppvObject = &This->IStorage_iface;
1216
  }
1217
  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1218
  {
1219 1220 1221 1222 1223 1224
    *ppvObject = &This->IPropertySetStorage_iface;
  }
  /* locking interface is reported for writer only */
  else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
  {
    *ppvObject = &This->IDirectWriterLock_iface;
1225
  }
1226 1227
  else
    return E_NOINTERFACE;
1228

1229
  IStorage_AddRef(iface);
1230

1231 1232 1233
  return S_OK;
}

1234 1235
/************************************************************************
 * StorageBaseImpl_AddRef (IUnknown)
1236
 *
1237 1238
 * This method implements the common AddRef for all IStorage
 * implementations contained in this file.
1239
 *
1240
 * See Windows documentation for more details on IUnknown methods.
1241
 */
1242 1243
static ULONG WINAPI StorageBaseImpl_AddRef(
            IStorage* iface)
1244
{
1245 1246
  StorageBaseImpl *This = impl_from_IStorage(iface);
  ULONG ref = InterlockedIncrement(&This->ref);
1247

1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
  TRACE("(%p) AddRef to %d\n", This, ref);

  return ref;
}

/************************************************************************
 * StorageBaseImpl_Release (IUnknown)
 *
 * This method implements the common Release for all IStorage
 * implementations contained in this file.
 *
 * See Windows documentation for more details on IUnknown methods.
 */
static ULONG WINAPI StorageBaseImpl_Release(
      IStorage* iface)
{
  StorageBaseImpl *This = impl_from_IStorage(iface);

  ULONG ref = InterlockedDecrement(&This->ref);

  TRACE("(%p) ReleaseRef to %d\n", This, ref);

  if (ref == 0)
1271
  {
1272 1273 1274 1275 1276 1277 1278
    /*
     * Since we are using a system of base-classes, we want to call the
     * destructor of the appropriate derived class. To do this, we are
     * using virtual functions to implement the destructor.
     */
    StorageBaseImpl_Destroy(This);
  }
1279

1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
  return ref;
}

static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
    SNB snbExclude, IStorage *pstgDest);

static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
    SNB snbExclude, IStorage *pstgDest)
{
  DirEntry data;
  HRESULT hr;
  BOOL skip = FALSE;
  IStorage *pstgTmp;
  IStream *pstrChild, *pstrTmp;
  STATSTG strStat;

  if (srcEntry == DIRENTRY_NULL)
    return S_OK;

  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );

  if (FAILED(hr))
    return hr;

  if ( snbExclude )
  {
    WCHAR **snb = snbExclude;

    while ( *snb != NULL && !skip )
1311
    {
1312 1313 1314 1315 1316
      if ( lstrcmpW(data.name, *snb) == 0 )
        skip = TRUE;
      ++snb;
    }
  }
1317

1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
  if (!skip)
  {
    if (data.stgType == STGTY_STORAGE && !skip_storage)
    {
      /*
       * create a new storage in destination storage
       */
      hr = IStorage_CreateStorage( pstgDest, data.name,
                                   STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                   0, 0,
                                   &pstgTmp );

      /*
       * if it already exist, don't create a new one use this one
       */
      if (hr == STG_E_FILEALREADYEXISTS)
1334
      {
1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
        hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
                                   STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                   NULL, 0, &pstgTmp );
      }

      if (SUCCEEDED(hr))
      {
        hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
                                                 skip_stream, NULL, pstgTmp );

        IStorage_Release(pstgTmp);
1346 1347
      }
    }
1348
    else if (data.stgType == STGTY_STREAM && !skip_stream)
1349 1350
    {
      /*
1351 1352
       * create a new stream in destination storage. If the stream already
       * exist, it will be deleted and a new one will be created.
1353
       */
1354 1355 1356
      hr = IStorage_CreateStream( pstgDest, data.name,
                                  STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                  0, 0, &pstrTmp );
1357

1358 1359 1360 1361 1362 1363 1364
      /*
       * open child stream storage. This operation must succeed even if the
       * stream is already open, so we use internal functions to do it.
       */
      if (hr == S_OK)
      {
        StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1365

1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
        if (streamimpl)
        {
          pstrChild = &streamimpl->IStream_iface;
          if (pstrChild)
            IStream_AddRef(pstrChild);
        }
        else
        {
          pstrChild = NULL;
          hr = E_OUTOFMEMORY;
        }
      }
1378

1379 1380 1381 1382 1383 1384
      if (hr == S_OK)
      {
        /*
         * Get the size of the source stream
         */
        IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1385

1386 1387 1388 1389
        /*
         * Set the size of the destination stream.
         */
        IStream_SetSize(pstrTmp, strStat.cbSize);
1390

1391 1392 1393 1394 1395
        /*
         * do the copy
         */
        hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
                             NULL, NULL );
1396

1397 1398
        IStream_Release( pstrChild );
      }
1399

1400
      IStream_Release( pstrTmp );
1401 1402 1403
    }
  }

1404 1405 1406 1407
  /* copy siblings */
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
                                           skip_stream, snbExclude, pstgDest );
1408 1409

  if (SUCCEEDED(hr))
1410 1411
    hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
                                           skip_stream, snbExclude, pstgDest );
1412 1413

  return hr;
1414 1415
}

1416
static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1417
{
1418
  StgStreamImpl *strm;
1419

1420 1421 1422 1423 1424 1425 1426
  LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
  {
    if (strm->dirEntry == streamEntry)
    {
      return TRUE;
    }
  }
1427

1428
  return FALSE;
1429 1430
}

1431
static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1432
{
1433
  StorageInternalImpl *childstg;
1434

1435
  LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1436
  {
1437 1438 1439 1440
    if (childstg->base.storageDirEntry == storageEntry)
    {
      return TRUE;
    }
1441 1442
  }

1443
  return FALSE;
1444 1445
}

1446 1447
/************************************************************************
 * StorageBaseImpl_OpenStream (IStorage)
1448
 *
1449
 * This method will open the specified stream object from the current storage.
1450
 *
1451
 * See Windows documentation for more details on IStorage methods.
1452
 */
1453 1454 1455 1456 1457 1458 1459
static HRESULT WINAPI StorageBaseImpl_OpenStream(
  IStorage*        iface,
  const OLECHAR*   pwcsName,  /* [string][in] */
  void*            reserved1, /* [unique][in] */
  DWORD            grfMode,   /* [in]  */
  DWORD            reserved2, /* [in]  */
  IStream**        ppstm)     /* [out] */
1460
{
1461 1462 1463 1464 1465
  StorageBaseImpl *This = impl_from_IStorage(iface);
  StgStreamImpl*    newStream;
  DirEntry          currentEntry;
  DirRef            streamEntryRef;
  HRESULT           res = STG_E_UNKNOWN;
1466

1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
  TRACE("(%p, %s, %p, %x, %d, %p)\n",
	iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);

  if ( (pwcsName==NULL) || (ppstm==0) )
  {
    res = E_INVALIDARG;
    goto end;
  }

  *ppstm = NULL;

  if ( FAILED( validateSTGM(grfMode) ) ||
       STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
1484 1485

  /*
1486
   * As documented.
1487
   */
1488 1489 1490 1491 1492
  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
1493

1494
  if (This->reverted)
1495
  {
1496 1497 1498
    res = STG_E_REVERTED;
    goto end;
  }
1499

1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510
  /*
   * Check that we're compatible with the parent's storage mode, but
   * only if we are not in transacted mode
   */
  if(!(This->openFlags & STGM_TRANSACTED)) {
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
    {
      res = STG_E_INVALIDFLAG;
      goto end;
    }
  }
1511

1512 1513 1514 1515 1516 1517 1518 1519
  /*
   * Search for the element with the given name
   */
  streamEntryRef = findElement(
    This,
    This->storageDirEntry,
    pwcsName,
    &currentEntry);
1520

1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532
  /*
   * If it was found, construct the stream object and return a pointer to it.
   */
  if ( (streamEntryRef!=DIRENTRY_NULL) &&
       (currentEntry.stgType==STGTY_STREAM) )
  {
    if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
    {
      /* A single stream cannot be opened a second time. */
      res = STG_E_ACCESSDENIED;
      goto end;
    }
1533

1534 1535 1536
    newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);

    if (newStream)
1537
    {
1538 1539
      newStream->grfMode = grfMode;
      *ppstm = &newStream->IStream_iface;
1540

1541
      IStream_AddRef(*ppstm);
1542

1543 1544
      res = S_OK;
      goto end;
1545
    }
1546 1547 1548

    res = E_OUTOFMEMORY;
    goto end;
1549
  }
1550

1551 1552 1553 1554 1555 1556 1557
  res = STG_E_FILENOTFOUND;

end:
  if (res == S_OK)
    TRACE("<-- IStream %p\n", *ppstm);
  TRACE("<-- %08x\n", res);
  return res;
1558 1559
}

1560 1561
/************************************************************************
 * StorageBaseImpl_OpenStorage (IStorage)
1562
 *
1563
 * This method will open a new storage object from the current storage.
1564
 *
1565
 * See Windows documentation for more details on IStorage methods.
1566
 */
1567 1568 1569 1570 1571 1572 1573 1574
static HRESULT WINAPI StorageBaseImpl_OpenStorage(
  IStorage*        iface,
  const OLECHAR*   pwcsName,      /* [string][unique][in] */
  IStorage*        pstgPriority,  /* [unique][in] */
  DWORD            grfMode,       /* [in] */
  SNB              snbExclude,    /* [unique][in] */
  DWORD            reserved,      /* [in] */
  IStorage**       ppstg)         /* [out] */
1575
{
1576 1577 1578 1579 1580 1581
  StorageBaseImpl *This = impl_from_IStorage(iface);
  StorageInternalImpl*   newStorage;
  StorageBaseImpl*       newTransactedStorage;
  DirEntry               currentEntry;
  DirRef                 storageEntryRef;
  HRESULT                res = STG_E_UNKNOWN;
1582

1583 1584 1585
  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
	iface, debugstr_w(pwcsName), pstgPriority,
	grfMode, snbExclude, reserved, ppstg);
1586

1587 1588 1589 1590 1591
  if ((pwcsName==NULL) || (ppstg==0) )
  {
    res = E_INVALIDARG;
    goto end;
  }
1592

1593
  if (This->openFlags & STGM_SIMPLE)
1594
  {
1595 1596 1597
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
1598

1599 1600 1601 1602 1603 1604
  /* as documented */
  if (snbExclude != NULL)
  {
    res = STG_E_INVALIDPARAMETER;
    goto end;
  }
1605

1606 1607 1608 1609 1610
  if ( FAILED( validateSTGM(grfMode) ))
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
1611

1612 1613 1614 1615 1616 1617 1618 1619 1620
  /*
   * As documented.
   */
  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
        (grfMode & STGM_DELETEONRELEASE) ||
        (grfMode & STGM_PRIORITY) )
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
1621 1622
  }

1623 1624
  if (This->reverted)
    return STG_E_REVERTED;
1625

1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636
  /*
   * Check that we're compatible with the parent's storage mode,
   * but only if we are not transacted
   */
  if(!(This->openFlags & STGM_TRANSACTED)) {
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
    {
      res = STG_E_ACCESSDENIED;
      goto end;
    }
  }
1637

1638
  *ppstg = NULL;
1639

1640 1641 1642 1643 1644
  storageEntryRef = findElement(
                         This,
                         This->storageDirEntry,
                         pwcsName,
                         &currentEntry);
1645

1646 1647
  if ( (storageEntryRef!=DIRENTRY_NULL) &&
       (currentEntry.stgType==STGTY_STORAGE) )
1648
  {
1649 1650 1651 1652 1653 1654
    if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
    {
      /* A single storage cannot be opened a second time. */
      res = STG_E_ACCESSDENIED;
      goto end;
    }
1655

1656 1657 1658 1659
    newStorage = StorageInternalImpl_Construct(
                   This,
                   grfMode,
                   storageEntryRef);
1660

1661
    if (newStorage != 0)
1662
    {
1663 1664 1665
      if (grfMode & STGM_TRANSACTED)
      {
        res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1666

1667 1668 1669 1670 1671
        if (FAILED(res))
        {
          HeapFree(GetProcessHeap(), 0, newStorage);
          goto end;
        }
1672

1673 1674 1675 1676 1677 1678
        *ppstg = &newTransactedStorage->IStorage_iface;
      }
      else
      {
        *ppstg = &newStorage->base.IStorage_iface;
      }
1679

1680 1681 1682 1683
      list_add_tail(&This->storageHead, &newStorage->ParentListEntry);

      res = S_OK;
      goto end;
1684
    }
1685 1686 1687

    res = STG_E_INSUFFICIENTMEMORY;
    goto end;
1688 1689
  }

1690 1691 1692 1693 1694
  res = STG_E_FILENOTFOUND;

end:
  TRACE("<-- %08x\n", res);
  return res;
1695 1696
}

1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716
/************************************************************************
 * StorageBaseImpl_EnumElements (IStorage)
 *
 * This method will create an enumerator object that can be used to
 * retrieve information about all the elements in the storage object.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT WINAPI StorageBaseImpl_EnumElements(
  IStorage*       iface,
  DWORD           reserved1, /* [in] */
  void*           reserved2, /* [size_is][unique][in] */
  DWORD           reserved3, /* [in] */
  IEnumSTATSTG**  ppenum)    /* [out] */
{
  StorageBaseImpl *This = impl_from_IStorage(iface);
  IEnumSTATSTGImpl* newEnum;

  TRACE("(%p, %d, %p, %d, %p)\n",
	iface, reserved1, reserved2, reserved3, ppenum);
1717

1718 1719
  if (!ppenum)
    return E_INVALIDARG;
1720

1721 1722
  if (This->reverted)
    return STG_E_REVERTED;
1723

1724 1725 1726 1727 1728 1729 1730
  newEnum = IEnumSTATSTGImpl_Construct(
              This,
              This->storageDirEntry);

  if (newEnum)
  {
    *ppenum = &newEnum->IEnumSTATSTG_iface;
1731
    return S_OK;
1732
  }
1733

1734 1735
  return E_OUTOFMEMORY;
}
1736

1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
/************************************************************************
 * StorageBaseImpl_Stat (IStorage)
 *
 * This method will retrieve information about this storage object.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT WINAPI StorageBaseImpl_Stat(
  IStorage*        iface,
  STATSTG*         pstatstg,     /* [out] */
  DWORD            grfStatFlag)  /* [in] */
{
  StorageBaseImpl *This = impl_from_IStorage(iface);
  DirEntry       currentEntry;
  HRESULT        res = STG_E_UNKNOWN;
1752

1753 1754
  TRACE("(%p, %p, %x)\n",
	iface, pstatstg, grfStatFlag);
1755

1756 1757 1758 1759
  if (!pstatstg)
  {
    res = E_INVALIDARG;
    goto end;
1760
  }
1761

1762
  if (This->reverted)
1763
  {
1764 1765 1766
    res = STG_E_REVERTED;
    goto end;
  }
1767

1768 1769 1770 1771
  res = StorageBaseImpl_ReadDirEntry(
                    This,
                    This->storageDirEntry,
                    &currentEntry);
1772

1773 1774 1775 1776 1777 1778 1779
  if (SUCCEEDED(res))
  {
    StorageUtl_CopyDirEntryToSTATSTG(
      This,
      pstatstg,
      &currentEntry,
      grfStatFlag);
1780

1781 1782 1783
    pstatstg->grfMode = This->openFlags;
    pstatstg->grfStateBits = This->stateBits;
  }
1784

1785 1786 1787 1788 1789 1790 1791 1792
end:
  if (res == S_OK)
  {
    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);
  }
  TRACE("<-- %08x\n", res);
  return res;
}
1793

1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
/************************************************************************
 * StorageBaseImpl_RenameElement (IStorage)
 *
 * This method will rename the specified element.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT WINAPI StorageBaseImpl_RenameElement(
            IStorage*        iface,
            const OLECHAR*   pwcsOldName,  /* [in] */
            const OLECHAR*   pwcsNewName)  /* [in] */
{
  StorageBaseImpl *This = impl_from_IStorage(iface);
  DirEntry          currentEntry;
  DirRef            currentEntryRef;
1809

1810 1811
  TRACE("(%p, %s, %s)\n",
	iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1812

1813 1814
  if (This->reverted)
    return STG_E_REVERTED;
1815

1816 1817 1818 1819
  currentEntryRef = findElement(This,
                                   This->storageDirEntry,
                                   pwcsNewName,
                                   &currentEntry);
1820

1821 1822 1823 1824 1825 1826
  if (currentEntryRef != DIRENTRY_NULL)
  {
    /*
     * There is already an element with the new name
     */
    return STG_E_FILEALREADYEXISTS;
1827 1828
  }

1829 1830 1831 1832 1833 1834 1835
  /*
   * Search for the old element name
   */
  currentEntryRef = findElement(This,
                                   This->storageDirEntry,
                                   pwcsOldName,
                                   &currentEntry);
1836

1837 1838 1839 1840 1841 1842 1843 1844
  if (currentEntryRef != DIRENTRY_NULL)
  {
    if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
        StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
    {
      WARN("Element is already open; cannot rename.\n");
      return STG_E_ACCESSDENIED;
    }
1845

1846 1847 1848
    /* Remove the element from its current position in the tree */
    removeFromTree(This, This->storageDirEntry,
        currentEntryRef);
1849

1850 1851
    /* Change the name of the element */
    strcpyW(currentEntry.name, pwcsNewName);
1852

1853 1854 1855
    /* Delete any sibling links */
    currentEntry.leftChild = DIRENTRY_NULL;
    currentEntry.rightChild = DIRENTRY_NULL;
1856

1857 1858
    StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
        &currentEntry);
1859

1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870
    /* Insert the element in a new position in the tree */
    insertIntoTree(This, This->storageDirEntry,
        currentEntryRef);
  }
  else
  {
    /*
     * There is no element with the old name
     */
    return STG_E_FILENOTFOUND;
  }
1871

1872
  return StorageBaseImpl_Flush(This);
1873 1874
}

1875 1876 1877 1878 1879 1880
/************************************************************************
 * StorageBaseImpl_CreateStream (IStorage)
 *
 * This method will create a stream object within this storage
 *
 * See Windows documentation for more details on IStorage methods.
1881
 */
1882 1883 1884 1885 1886 1887 1888
static HRESULT WINAPI StorageBaseImpl_CreateStream(
            IStorage*        iface,
            const OLECHAR*   pwcsName,  /* [string][in] */
            DWORD            grfMode,   /* [in] */
            DWORD            reserved1, /* [in] */
            DWORD            reserved2, /* [in] */
            IStream**        ppstm)     /* [out] */
1889
{
1890
  StorageBaseImpl *This = impl_from_IStorage(iface);
1891 1892 1893 1894
  StgStreamImpl*    newStream;
  DirEntry          currentEntry, newStreamEntry;
  DirRef            currentEntryRef, newStreamEntryRef;
  HRESULT hr;
1895

1896 1897 1898
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
	iface, debugstr_w(pwcsName), grfMode,
	reserved1, reserved2, ppstm);
1899

1900
  if (ppstm == 0)
1901 1902
    return STG_E_INVALIDPOINTER;

1903 1904
  if (pwcsName == 0)
    return STG_E_INVALIDNAME;
1905

1906 1907
  if (reserved1 || reserved2)
    return STG_E_INVALIDPARAMETER;
1908

1909 1910
  if ( FAILED( validateSTGM(grfMode) ))
    return STG_E_INVALIDFLAG;
1911

1912 1913
  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
    return STG_E_INVALIDFLAG;
1914

1915 1916
  if (This->reverted)
    return STG_E_REVERTED;
1917

1918 1919 1920 1921 1922 1923
  /*
   * As documented.
   */
  if ((grfMode & STGM_DELETEONRELEASE) ||
      (grfMode & STGM_TRANSACTED))
    return STG_E_INVALIDFUNCTION;
1924

1925 1926 1927 1928 1929 1930 1931 1932
  /*
   * Don't worry about permissions in transacted mode, as we can always write
   * changes; we just can't always commit them.
   */
  if(!(This->openFlags & STGM_TRANSACTED)) {
    /* Can't create a stream on read-only storage */
    if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
      return STG_E_ACCESSDENIED;
1933

1934 1935 1936 1937
    /* Can't create a stream with greater access than the parent. */
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
      return STG_E_ACCESSDENIED;
  }
1938

1939 1940
  if(This->openFlags & STGM_SIMPLE)
    if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1941

1942
  *ppstm = 0;
1943

1944 1945 1946 1947
  currentEntryRef = findElement(This,
                                   This->storageDirEntry,
                                   pwcsName,
                                   &currentEntry);
1948

1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960
  if (currentEntryRef != DIRENTRY_NULL)
  {
    /*
     * An element with this name already exists
     */
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
    {
      IStorage_DestroyElement(iface, pwcsName);
    }
    else
      return STG_E_FILEALREADYEXISTS;
  }
1961

1962 1963 1964 1965
  /*
   * memset the empty entry
   */
  memset(&newStreamEntry, 0, sizeof(DirEntry));
1966

1967 1968
  newStreamEntry.sizeOfNameString =
      ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1969

1970 1971
  if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
    return STG_E_INVALIDNAME;
1972

1973
  strcpyW(newStreamEntry.name, pwcsName);
1974

1975 1976 1977 1978
  newStreamEntry.stgType       = STGTY_STREAM;
  newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
  newStreamEntry.size.u.LowPart  = 0;
  newStreamEntry.size.u.HighPart = 0;
1979

1980 1981 1982
  newStreamEntry.leftChild        = DIRENTRY_NULL;
  newStreamEntry.rightChild       = DIRENTRY_NULL;
  newStreamEntry.dirRootEntry     = DIRENTRY_NULL;
1983

1984 1985 1986 1987
  /* call CoFileTime to get the current time
  newStreamEntry.ctime
  newStreamEntry.mtime
  */
1988

1989
  /*  newStreamEntry.clsid */
1990

1991 1992 1993 1994 1995 1996
  /*
   * Create an entry with the new data
   */
  hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
  if (FAILED(hr))
    return hr;
1997

1998 1999 2000 2001
  /*
   * Insert the new entry in the parent storage's tree.
   */
  hr = insertIntoTree(
2002
    This,
2003
    This->storageDirEntry,
2004 2005
    newStreamEntryRef);
  if (FAILED(hr))
2006
  {
2007 2008
    StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
    return hr;
2009 2010
  }

2011 2012 2013 2014 2015 2016
  /*
   * Open the stream to return it.
   */
  newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);

  if (newStream)
2017
  {
2018 2019
    *ppstm = &newStream->IStream_iface;
    IStream_AddRef(*ppstm);
2020
  }
2021
  else
2022
  {
2023
    return STG_E_INSUFFICIENTMEMORY;
2024 2025
  }

2026 2027
  return StorageBaseImpl_Flush(This);
}
2028

2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
/************************************************************************
 * StorageBaseImpl_SetClass (IStorage)
 *
 * This method will write the specified CLSID in the directory entry of this
 * storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT WINAPI StorageBaseImpl_SetClass(
  IStorage*        iface,
  REFCLSID         clsid) /* [in] */
{
  StorageBaseImpl *This = impl_from_IStorage(iface);
  HRESULT hRes;
  DirEntry currentEntry;
2044

2045
  TRACE("(%p, %p)\n", iface, clsid);
2046

2047 2048
  if (This->reverted)
    return STG_E_REVERTED;
2049

2050 2051 2052 2053 2054 2055
  hRes = StorageBaseImpl_ReadDirEntry(This,
                                      This->storageDirEntry,
                                      &currentEntry);
  if (SUCCEEDED(hRes))
  {
    currentEntry.clsid = *clsid;
2056

2057 2058 2059 2060
    hRes = StorageBaseImpl_WriteDirEntry(This,
                                         This->storageDirEntry,
                                         &currentEntry);
  }
2061

2062 2063
  if (SUCCEEDED(hRes))
    hRes = StorageBaseImpl_Flush(This);
2064

2065
  return hRes;
2066 2067
}

2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081
/************************************************************************
 * StorageBaseImpl_CreateStorage (IStorage)
 *
 * This method will create the storage object within the provided storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT WINAPI StorageBaseImpl_CreateStorage(
  IStorage*      iface,
  const OLECHAR  *pwcsName, /* [string][in] */
  DWORD            grfMode,   /* [in] */
  DWORD            reserved1, /* [in] */
  DWORD            reserved2, /* [in] */
  IStorage       **ppstg)   /* [out] */
2082
{
2083
  StorageBaseImpl* This = impl_from_IStorage(iface);
2084

2085 2086 2087 2088 2089
  DirEntry         currentEntry;
  DirEntry         newEntry;
  DirRef           currentEntryRef;
  DirRef           newEntryRef;
  HRESULT          hr;
2090

2091 2092 2093 2094 2095 2096 2097 2098
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
	iface, debugstr_w(pwcsName), grfMode,
	reserved1, reserved2, ppstg);

  if (ppstg == 0)
    return STG_E_INVALIDPOINTER;

  if (This->openFlags & STGM_SIMPLE)
2099
  {
2100
    return STG_E_INVALIDFUNCTION;
2101 2102
  }

2103 2104
  if (pwcsName == 0)
    return STG_E_INVALIDNAME;
2105

2106
  *ppstg = NULL;
2107

2108 2109
  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode & STGM_DELETEONRELEASE) )
2110
  {
2111 2112
    WARN("bad grfMode: 0x%x\n", grfMode);
    return STG_E_INVALIDFLAG;
2113 2114
  }

2115 2116
  if (This->reverted)
    return STG_E_REVERTED;
2117

2118 2119 2120 2121 2122 2123 2124 2125
  /*
   * Check that we're compatible with the parent's storage mode
   */
  if ( !(This->openFlags & STGM_TRANSACTED) &&
       STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
  {
    WARN("access denied\n");
    return STG_E_ACCESSDENIED;
2126
  }
2127

2128 2129 2130 2131
  currentEntryRef = findElement(This,
                                   This->storageDirEntry,
                                   pwcsName,
                                   &currentEntry);
2132

2133
  if (currentEntryRef != DIRENTRY_NULL)
2134
  {
2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
    /*
     * An element with this name already exists
     */
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
        ((This->openFlags & STGM_TRANSACTED) ||
         STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
    {
      hr = IStorage_DestroyElement(iface, pwcsName);
      if (FAILED(hr))
        return hr;
    }
    else
    {
      WARN("file already exists\n");
      return STG_E_FILEALREADYEXISTS;
    }
  }
  else if (!(This->openFlags & STGM_TRANSACTED) &&
           STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
  {
    WARN("read-only storage\n");
    return STG_E_ACCESSDENIED;
2157
  }
2158

2159
  memset(&newEntry, 0, sizeof(DirEntry));
2160

2161
  newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2162

2163
  if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2164
  {
2165 2166
    FIXME("name too long\n");
    return STG_E_INVALIDNAME;
2167
  }
2168

2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186
  strcpyW(newEntry.name, pwcsName);

  newEntry.stgType       = STGTY_STORAGE;
  newEntry.startingBlock = BLOCK_END_OF_CHAIN;
  newEntry.size.u.LowPart  = 0;
  newEntry.size.u.HighPart = 0;

  newEntry.leftChild        = DIRENTRY_NULL;
  newEntry.rightChild       = DIRENTRY_NULL;
  newEntry.dirRootEntry     = DIRENTRY_NULL;

  /* call CoFileTime to get the current time
  newEntry.ctime
  newEntry.mtime
  */

  /*  newEntry.clsid */

2187
  /*
2188
   * Create a new directory entry for the storage
2189
   */
2190 2191 2192
  hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
  if (FAILED(hr))
    return hr;
2193

2194 2195 2196 2197 2198 2199 2200 2201
  /*
   * Insert the new directory entry into the parent storage's tree
   */
  hr = insertIntoTree(
    This,
    This->storageDirEntry,
    newEntryRef);
  if (FAILED(hr))
2202
  {
2203
    StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2204 2205 2206
    return hr;
  }

2207
  /*
2208
   * Open it to get a pointer to return.
2209
   */
2210
  hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2211

2212
  if( (hr != S_OK) || (*ppstg == NULL))
2213
  {
2214 2215
    return hr;
  }
2216

2217 2218
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_Flush(This);
2219

2220 2221
  return S_OK;
}
2222

2223 2224 2225 2226 2227 2228
static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
    SNB snbExclude, IStorage *pstgDest)
{
  DirEntry data;
  HRESULT hr;
2229

2230
  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2231

2232 2233 2234 2235 2236 2237 2238 2239
  if (SUCCEEDED(hr))
    hr = IStorage_SetClass( pstgDest, &data.clsid );

  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
      skip_stream, snbExclude, pstgDest );

  return hr;
2240 2241
}

2242 2243
/*************************************************************************
 * CopyTo (IStorage)
2244
 */
2245 2246 2247 2248 2249 2250
static HRESULT WINAPI StorageBaseImpl_CopyTo(
  IStorage*   iface,
  DWORD       ciidExclude,  /* [in] */
  const IID*  rgiidExclude, /* [size_is][unique][in] */
  SNB         snbExclude,   /* [unique][in] */
  IStorage*   pstgDest)     /* [unique][in] */
2251
{
2252
  StorageBaseImpl *This = impl_from_IStorage(iface);
2253

2254 2255
  BOOL         skip_storage = FALSE, skip_stream = FALSE;
  DWORD        i;
2256

2257 2258 2259
  TRACE("(%p, %d, %p, %p, %p)\n",
	iface, ciidExclude, rgiidExclude,
	snbExclude, pstgDest);
2260

2261 2262
  if ( pstgDest == 0 )
    return STG_E_INVALIDPOINTER;
2263

2264
  for(i = 0; i < ciidExclude; ++i)
2265
  {
2266 2267 2268 2269 2270 2271
    if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
        skip_storage = TRUE;
    else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
        skip_stream = TRUE;
    else
        WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2272 2273
  }

2274
  if (!skip_storage)
2275
  {
2276 2277 2278 2279 2280
    /* Give up early if it looks like this would be infinitely recursive.
     * Oddly enough, this includes some cases that aren't really recursive, like
     * copying to a transacted child. */
    IStorage *pstgDestAncestor = pstgDest;
    IStorage *pstgDestAncestorChild = NULL;
2281

2282 2283 2284
    /* Go up the chain from the destination until we find the source storage. */
    while (pstgDestAncestor != iface) {
      pstgDestAncestorChild = pstgDest;
2285

2286 2287 2288
      if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
      {
        TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
2289

2290 2291 2292 2293 2294
        pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
      }
      else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
      {
        StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2295

2296 2297 2298 2299
        pstgDestAncestor = &internal->parentStorage->IStorage_iface;
      }
      else
        break;
2300
    }
2301

2302
    if (pstgDestAncestor == iface)
2303
    {
2304
      BOOL fail = TRUE;
2305

2306
      if (pstgDestAncestorChild && snbExclude)
2307
      {
2308 2309 2310
        StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
        DirEntry data;
        WCHAR **snb = snbExclude;
2311

2312
        StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2313

2314 2315 2316 2317 2318 2319
        while ( *snb != NULL && fail )
        {
          if ( lstrcmpW(data.name, *snb) == 0 )
            fail = FALSE;
          ++snb;
        }
2320
      }
2321

2322 2323
      if (fail)
        return STG_E_ACCESSDENIED;
2324
    }
2325 2326
  }

2327 2328
  return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
    skip_storage, skip_stream, snbExclude, pstgDest );
2329 2330
}

2331 2332
/*************************************************************************
 * MoveElementTo (IStorage)
2333
 */
2334
static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
2335
  IStorage*     iface,
2336 2337 2338 2339
  const OLECHAR *pwcsName,   /* [string][in] */
  IStorage      *pstgDest,   /* [unique][in] */
  const OLECHAR *pwcsNewName,/* [string][in] */
  DWORD           grfFlags)    /* [in] */
2340
{
2341 2342 2343 2344
  FIXME("(%p %s %p %s %u): stub\n", iface,
         debugstr_w(pwcsName), pstgDest,
         debugstr_w(pwcsNewName), grfFlags);
  return E_NOTIMPL;
2345 2346
}

2347 2348 2349 2350 2351 2352 2353
/*************************************************************************
 * Commit (IStorage)
 *
 * Ensures that any changes made to a storage object open in transacted mode
 * are reflected in the parent storage
 *
 * In a non-transacted mode, this ensures all cached writes are completed.
2354
 */
2355
static HRESULT WINAPI StorageBaseImpl_Commit(
2356
  IStorage*   iface,
2357
  DWORD         grfCommitFlags)/* [in] */
2358
{
2359 2360 2361
  StorageBaseImpl* This = impl_from_IStorage(iface);
  TRACE("(%p %d)\n", iface, grfCommitFlags);
  return StorageBaseImpl_Flush(This);
2362 2363
}

2364 2365 2366 2367 2368 2369 2370
/*************************************************************************
 * Revert (IStorage)
 *
 * Discard all changes that have been made since the last commit operation
 */
static HRESULT WINAPI StorageBaseImpl_Revert(
  IStorage* iface)
2371
{
2372 2373
  TRACE("(%p)\n", iface);
  return S_OK;
2374 2375
}

2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386
/*********************************************************************
 *
 * Internal helper function for StorageBaseImpl_DestroyElement()
 *
 * Delete the contents of a storage entry.
 *
 */
static HRESULT deleteStorageContents(
  StorageBaseImpl *parentStorage,
  DirRef       indexToDelete,
  DirEntry     entryDataToDelete)
2387
{
2388 2389 2390 2391 2392 2393
  IEnumSTATSTG *elements     = 0;
  IStorage   *childStorage = 0;
  STATSTG      currentElement;
  HRESULT      hr;
  HRESULT      destroyHr = S_OK;
  StorageInternalImpl *stg, *stg2;
2394

2395 2396
  /* Invalidate any open storage objects. */
  LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2397
  {
2398
    if (stg->base.storageDirEntry == indexToDelete)
2399
    {
2400
      StorageBaseImpl_Invalidate(&stg->base);
2401 2402 2403
    }
  }

2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414
  /*
   * Open the storage and enumerate it
   */
  hr = IStorage_OpenStorage(
        &parentStorage->IStorage_iface,
        entryDataToDelete.name,
        0,
        STGM_WRITE | STGM_SHARE_EXCLUSIVE,
        0,
        0,
        &childStorage);
2415

2416 2417 2418 2419
  if (hr != S_OK)
  {
    return hr;
  }
2420

2421 2422 2423
  /*
   * Enumerate the elements
   */
2424 2425 2426 2427 2428 2429
  hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
  if (FAILED(hr))
  {
    IStorage_Release(childStorage);
    return hr;
  }
2430

2431
  do
2432
  {
2433 2434 2435 2436 2437
    /*
     * Obtain the next element
     */
    hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
    if (hr==S_OK)
2438
    {
2439 2440 2441
      destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);

      CoTaskMemFree(currentElement.pwcsName);
2442 2443
    }

2444 2445 2446 2447 2448
    /*
     * We need to Reset the enumeration every time because we delete elements
     * and the enumeration could be invalid
     */
    IEnumSTATSTG_Reset(elements);
2449

2450
  } while ((hr == S_OK) && (destroyHr == S_OK));
2451

2452 2453
  IStorage_Release(childStorage);
  IEnumSTATSTG_Release(elements);
2454

2455
  return destroyHr;
2456 2457
}

2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
/*********************************************************************
 *
 * Internal helper function for StorageBaseImpl_DestroyElement()
 *
 * Perform the deletion of a stream's data
 *
 */
static HRESULT deleteStreamContents(
  StorageBaseImpl *parentStorage,
  DirRef        indexToDelete,
  DirEntry      entryDataToDelete)
2469
{
2470 2471 2472 2473
  IStream      *pis;
  HRESULT        hr;
  ULARGE_INTEGER size;
  StgStreamImpl *strm, *strm2;
2474

2475 2476
  /* Invalidate any open stream objects. */
  LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2477
  {
2478
    if (strm->dirEntry == indexToDelete)
2479
    {
2480 2481 2482
      TRACE("Stream deleted %p\n", strm);
      strm->parentStorage = NULL;
      list_remove(&strm->StrmListEntry);
2483 2484 2485
    }
  }

2486 2487
  size.u.HighPart = 0;
  size.u.LowPart = 0;
2488

2489 2490
  hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
        entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2491

2492
  if (hr!=S_OK)
2493
  {
2494
    return(hr);
2495 2496
  }

2497 2498 2499 2500
  /*
   * Zap the stream
   */
  hr = IStream_SetSize(pis, size);
2501

2502
  if(hr != S_OK)
2503 2504 2505 2506
  {
    return hr;
  }

2507 2508 2509 2510
  /*
   * Release the stream object.
   */
  IStream_Release(pis);
2511

2512
  return S_OK;
2513 2514
}

2515 2516 2517 2518 2519 2520
/*************************************************************************
 * DestroyElement (IStorage)
 *
 * Strategy: This implementation is built this way for simplicity not for speed.
 *          I always delete the topmost element of the enumeration and adjust
 *          the deleted element pointer all the time.  This takes longer to
2521
 *          do but allows reinvoking DestroyElement whenever we encounter a
2522 2523 2524 2525 2526 2527 2528
 *          storage object.  The optimisation resides in the usage of another
 *          enumeration strategy that would give all the leaves of a storage
 *          first. (postfix order)
 */
static HRESULT WINAPI StorageBaseImpl_DestroyElement(
  IStorage*     iface,
  const OLECHAR *pwcsName)/* [string][in] */
2529
{
2530
  StorageBaseImpl *This = impl_from_IStorage(iface);
2531

2532 2533 2534
  HRESULT           hr = S_OK;
  DirEntry          entryToDelete;
  DirRef            entryToDeleteRef;
2535

2536 2537
  TRACE("(%p, %s)\n",
	iface, debugstr_w(pwcsName));
2538

2539 2540
  if (pwcsName==NULL)
    return STG_E_INVALIDPOINTER;
2541

2542 2543
  if (This->reverted)
    return STG_E_REVERTED;
2544

2545 2546 2547
  if ( !(This->openFlags & STGM_TRANSACTED) &&
       STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
    return STG_E_ACCESSDENIED;
2548

2549 2550 2551 2552 2553 2554 2555
  entryToDeleteRef = findElement(
    This,
    This->storageDirEntry,
    pwcsName,
    &entryToDelete);

  if ( entryToDeleteRef == DIRENTRY_NULL )
2556
  {
2557
    return STG_E_FILENOTFOUND;
2558 2559
  }

2560
  if ( entryToDelete.stgType == STGTY_STORAGE )
2561
  {
2562 2563 2564 2565
    hr = deleteStorageContents(
           This,
           entryToDeleteRef,
           entryToDelete);
2566
  }
2567
  else if ( entryToDelete.stgType == STGTY_STREAM )
2568
  {
2569 2570 2571 2572
    hr = deleteStreamContents(
           This,
           entryToDeleteRef,
           entryToDelete);
2573 2574
  }

2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588
  if (hr!=S_OK)
    return hr;

  /*
   * Remove the entry from its parent storage
   */
  hr = removeFromTree(
        This,
        This->storageDirEntry,
        entryToDeleteRef);

  /*
   * Invalidate the entry
   */
2589
  if (SUCCEEDED(hr))
2590 2591 2592 2593
    StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);

  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_Flush(This);
2594 2595 2596 2597

  return hr;
}

2598
static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2599
{
2600 2601 2602
  struct list *cur, *cur2;
  StgStreamImpl *strm=NULL;
  StorageInternalImpl *childstg=NULL;
2603

2604 2605 2606 2607 2608 2609
  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
    strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
    TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
    strm->parentStorage = NULL;
    list_remove(cur);
  }
2610

2611 2612 2613
  LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
    childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
    StorageBaseImpl_Invalidate( &childstg->base );
2614 2615
  }

2616
  if (stg->transactedChild)
2617
  {
2618
    StorageBaseImpl_Invalidate(stg->transactedChild);
2619

2620
    stg->transactedChild = NULL;
2621
  }
2622
}
2623

2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635
/******************************************************************************
 * SetElementTimes (IStorage)
 */
static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
  IStorage*     iface,
  const OLECHAR *pwcsName,/* [string][in] */
  const FILETIME  *pctime,  /* [in] */
  const FILETIME  *patime,  /* [in] */
  const FILETIME  *pmtime)  /* [in] */
{
  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
  return S_OK;
2636 2637
}

2638 2639 2640 2641 2642 2643 2644
/******************************************************************************
 * SetStateBits (IStorage)
 */
static HRESULT WINAPI StorageBaseImpl_SetStateBits(
  IStorage*   iface,
  DWORD         grfStateBits,/* [in] */
  DWORD         grfMask)     /* [in] */
2645
{
2646
  StorageBaseImpl *This = impl_from_IStorage(iface);
2647

2648 2649
  if (This->reverted)
    return STG_E_REVERTED;
2650

2651 2652 2653
  This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
  return S_OK;
}
2654

2655 2656 2657
/******************************************************************************
 * Internal stream list handlers
 */
2658

2659 2660 2661 2662 2663
void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
{
  TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
  list_add_tail(&stg->strmHead,&strm->StrmListEntry);
}
2664

2665 2666 2667 2668
void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
{
  TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
  list_remove(&(strm->StrmListEntry));
2669 2670
}

2671 2672 2673
static HRESULT StorageBaseImpl_CopyStream(
  StorageBaseImpl *dst, DirRef dst_entry,
  StorageBaseImpl *src, DirRef src_entry)
2674
{
2675 2676 2677 2678 2679
  HRESULT hr;
  BYTE data[4096];
  DirEntry srcdata;
  ULARGE_INTEGER bytes_copied;
  ULONG bytestocopy, bytesread, byteswritten;
2680

2681
  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2682

2683 2684 2685
  if (SUCCEEDED(hr))
  {
    hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
2686

2687 2688
    bytes_copied.QuadPart = 0;
    while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2689
    {
2690
      bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2691

2692 2693 2694
      hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
        data, &bytesread);
      if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2695

2696 2697 2698 2699 2700 2701 2702
      if (SUCCEEDED(hr))
        hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
          data, &byteswritten);
      if (SUCCEEDED(hr))
      {
        if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
        bytes_copied.QuadPart += byteswritten;
2703
      }
2704 2705 2706 2707 2708 2709
    }
  }

  return hr;
}

2710 2711 2712
static HRESULT StorageBaseImpl_DupStorageTree(
  StorageBaseImpl *dst, DirRef *dst_entry,
  StorageBaseImpl *src, DirRef src_entry)
2713 2714
{
  HRESULT hr;
2715 2716
  DirEntry data;
  BOOL has_stream=FALSE;
2717

2718
  if (src_entry == DIRENTRY_NULL)
2719
  {
2720 2721
    *dst_entry = DIRENTRY_NULL;
    return S_OK;
2722
  }
2723 2724 2725

  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
  if (SUCCEEDED(hr))
2726
  {
2727 2728 2729 2730 2731
    has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
    data.startingBlock = BLOCK_END_OF_CHAIN;
    data.size.QuadPart = 0;

    hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2732
  }
2733

2734 2735
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2736

2737 2738 2739 2740 2741 2742 2743 2744
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);

  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);

  if (SUCCEEDED(hr) && has_stream)
    hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2745 2746 2747 2748

  return hr;
}

2749 2750 2751
static HRESULT StorageBaseImpl_CopyStorageTree(
  StorageBaseImpl *dst, DirRef dst_entry,
  StorageBaseImpl *src, DirRef src_entry)
2752 2753
{
  HRESULT hr;
2754 2755
  DirEntry src_data, dst_data;
  DirRef new_root_entry;
2756

2757 2758 2759
  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);

  if (SUCCEEDED(hr))
2760
  {
2761
    hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2762
  }
2763 2764

  if (SUCCEEDED(hr))
2765
  {
2766 2767 2768 2769 2770
    hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
    dst_data.clsid = src_data.clsid;
    dst_data.ctime = src_data.ctime;
    dst_data.mtime = src_data.mtime;
    dst_data.dirRootEntry = new_root_entry;
2771
  }
2772

2773 2774
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
2775 2776 2777 2778

  return hr;
}

2779
static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
2780 2781
{
  HRESULT hr;
2782 2783
  DirEntry data;
  ULARGE_INTEGER zero;
2784

2785 2786
  if (entry == DIRENTRY_NULL)
    return S_OK;
2787

2788
  zero.QuadPart = 0;
2789

2790
  hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
2791

2792 2793
  if (SUCCEEDED(hr) && include_siblings)
    hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
2794

2795 2796
  if (SUCCEEDED(hr) && include_siblings)
    hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
2797

2798 2799
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
2800

2801 2802
  if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
    hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
2803

2804 2805 2806 2807
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_DestroyDirEntry(This, entry);

  return hr;
2808 2809
}

2810 2811 2812 2813 2814 2815 2816 2817 2818 2819

/************************************************************************
 * StorageImpl implementation
 ***********************************************************************/

static HRESULT StorageImpl_ReadAt(StorageImpl* This,
  ULARGE_INTEGER offset,
  void*          buffer,
  ULONG          size,
  ULONG*         bytesRead)
2820
{
2821
    return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2822 2823
}

2824 2825 2826 2827 2828
static HRESULT StorageImpl_WriteAt(StorageImpl* This,
  ULARGE_INTEGER offset,
  const void*    buffer,
  const ULONG    size,
  ULONG*         bytesWritten)
2829
{
2830 2831
    return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
}
2832

2833 2834 2835 2836
/******************************************************************************
 *      StorageImpl_LoadFileHeader
 *
 * This method will read in the file header
2837
 */
2838 2839
static HRESULT StorageImpl_LoadFileHeader(
          StorageImpl* This)
2840
{
2841 2842 2843 2844 2845
  HRESULT hr;
  BYTE    headerBigBlock[HEADER_SIZE];
  int     index;
  ULARGE_INTEGER offset;
  DWORD bytes_read;
2846

2847 2848 2849 2850 2851 2852 2853 2854 2855
  TRACE("\n");
  /*
   * Get a pointer to the big block of data containing the header.
   */
  offset.u.HighPart = 0;
  offset.u.LowPart = 0;
  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
    hr = STG_E_FILENOTFOUND;
2856

2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869
  /*
   * Extract the information from the header.
   */
  if (SUCCEEDED(hr))
  {
    /*
     * Check for the "magic number" signature and return an error if it is not
     * found.
     */
    if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
    {
      return STG_E_OLDFORMAT;
    }
2870

2871
    if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2872
    {
2873 2874
      return STG_E_INVALIDHEADER;
    }
2875

2876 2877 2878 2879
    StorageUtl_ReadWord(
      headerBigBlock,
      OFFSET_BIGBLOCKSIZEBITS,
      &This->bigBlockSizeBits);
2880

2881 2882 2883 2884
    StorageUtl_ReadWord(
      headerBigBlock,
      OFFSET_SMALLBLOCKSIZEBITS,
      &This->smallBlockSizeBits);
2885

2886 2887 2888 2889
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_BBDEPOTCOUNT,
      &This->bigBlockDepotCount);
2890

2891 2892 2893 2894
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_ROOTSTARTBLOCK,
      &This->rootStartBlock);
2895

2896 2897 2898 2899
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_TRANSACTIONSIG,
      &This->transactionSig);
2900

2901 2902 2903 2904
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_SMALLBLOCKLIMIT,
      &This->smallBlockLimit);
2905

2906 2907 2908 2909
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_SBDEPOTSTART,
      &This->smallBlockDepotStart);
2910

2911 2912 2913 2914
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_EXTBBDEPOTSTART,
      &This->extBigBlockDepotStart);
2915

2916 2917 2918 2919
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_EXTBBDEPOTCOUNT,
      &This->extBigBlockDepotCount);
2920

2921
    for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2922
    {
2923 2924 2925 2926
      StorageUtl_ReadDWord(
        headerBigBlock,
        OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
        &(This->bigBlockDepotStart[index]));
2927 2928
    }

2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945
    /*
     * Make the bitwise arithmetic to get the size of the blocks in bytes.
     */
    This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
    This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;

    /*
     * Right now, the code is making some assumptions about the size of the
     * blocks, just make sure they are what we're expecting.
     */
    if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
	This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
	This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
    {
	FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
	    This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
	hr = STG_E_INVALIDHEADER;
2946
    }
2947 2948 2949
    else
	hr = S_OK;
  }
2950

2951
  return hr;
2952 2953
}

2954 2955 2956 2957 2958 2959 2960
/******************************************************************************
 *      StorageImpl_SaveFileHeader
 *
 * This method will save to the file the header
 */
static void StorageImpl_SaveFileHeader(
          StorageImpl* This)
2961
{
2962 2963 2964 2965 2966 2967
  BYTE   headerBigBlock[HEADER_SIZE];
  int    index;
  HRESULT hr;
  ULARGE_INTEGER offset;
  DWORD bytes_read, bytes_written;
  DWORD major_version, dirsectorcount;
2968

2969 2970 2971 2972 2973 2974 2975 2976
  /*
   * Get a pointer to the big block of data containing the header.
   */
  offset.u.HighPart = 0;
  offset.u.LowPart = 0;
  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
    hr = STG_E_FILENOTFOUND;
2977

2978 2979 2980 2981 2982 2983 2984 2985 2986
  if (This->bigBlockSizeBits == 0x9)
    major_version = 3;
  else if (This->bigBlockSizeBits == 0xc)
    major_version = 4;
  else
  {
    ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
    major_version = 4;
  }
2987

2988 2989 2990 2991 2992 2993 2994 2995 2996
  /*
   * If the block read failed, the file is probably new.
   */
  if (FAILED(hr))
  {
    /*
     * Initialize for all unknown fields.
     */
    memset(headerBigBlock, 0, HEADER_SIZE);
2997

2998 2999 3000 3001 3002
    /*
     * Initialize the magic number.
     */
    memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
  }
3003

3004 3005 3006 3007 3008 3009 3010
  /*
   * Write the information to the header.
   */
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_MINORVERSION,
    0x3e);
3011

3012 3013 3014 3015
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_MAJORVERSION,
    major_version);
3016

3017 3018 3019 3020
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_BYTEORDERMARKER,
    (WORD)-2);
3021

3022 3023 3024 3025
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_BIGBLOCKSIZEBITS,
    This->bigBlockSizeBits);
3026

3027 3028 3029 3030
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_SMALLBLOCKSIZEBITS,
    This->smallBlockSizeBits);
3031

3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042
  if (major_version >= 4)
  {
    if (This->rootBlockChain)
      dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
    else
      /* This file is being created, and it will start out with one block. */
      dirsectorcount = 1;
  }
  else
    /* This field must be 0 in versions older than 4 */
    dirsectorcount = 0;
3043

3044 3045 3046 3047
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_DIRSECTORCOUNT,
    dirsectorcount);
3048

3049 3050 3051 3052
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_BBDEPOTCOUNT,
    This->bigBlockDepotCount);
3053

3054 3055 3056 3057
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_ROOTSTARTBLOCK,
    This->rootStartBlock);
3058

3059 3060 3061 3062
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_TRANSACTIONSIG,
    This->transactionSig);
3063

3064 3065 3066 3067
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SMALLBLOCKLIMIT,
    This->smallBlockLimit);
3068

3069 3070 3071 3072
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTSTART,
    This->smallBlockDepotStart);
3073

3074 3075 3076 3077 3078
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTCOUNT,
    This->smallBlockDepotChain ?
     BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3079

3080 3081 3082 3083
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_EXTBBDEPOTSTART,
    This->extBigBlockDepotStart);
3084

3085 3086 3087 3088 3089 3090
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_EXTBBDEPOTCOUNT,
    This->extBigBlockDepotCount);

  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3091
  {
3092 3093 3094 3095 3096
    StorageUtl_WriteDWord(
      headerBigBlock,
      OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
      (This->bigBlockDepotStart[index]));
  }
3097

3098 3099 3100 3101 3102
  /*
   * Write the big block back to the file.
   */
  StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
}
3103

3104

3105 3106 3107
/************************************************************************
 * StorageImpl implementation : DirEntry methods
 ***********************************************************************/
3108

3109 3110 3111 3112 3113 3114 3115
/******************************************************************************
 *      StorageImpl_ReadRawDirEntry
 *
 * This method will read the raw data from a directory entry in the file.
 *
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
 */
3116
static HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3117 3118 3119 3120
{
  ULARGE_INTEGER offset;
  HRESULT hr;
  ULONG bytesRead;
3121

3122
  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3123

3124 3125 3126 3127 3128 3129
  hr = BlockChainStream_ReadAt(
                    This->rootBlockChain,
                    offset,
                    RAW_DIRENTRY_SIZE,
                    buffer,
                    &bytesRead);
3130

3131 3132
  if (bytesRead != RAW_DIRENTRY_SIZE)
    return STG_E_READFAULT;
3133

3134 3135
  return hr;
}
3136

3137 3138 3139 3140 3141 3142 3143
/******************************************************************************
 *      StorageImpl_WriteRawDirEntry
 *
 * This method will write the raw data from a directory entry in the file.
 *
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
 */
3144
static HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3145 3146 3147
{
  ULARGE_INTEGER offset;
  ULONG bytesRead;
3148

3149
  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3150

3151 3152 3153 3154 3155 3156 3157
  return BlockChainStream_WriteAt(
                    This->rootBlockChain,
                    offset,
                    RAW_DIRENTRY_SIZE,
                    buffer,
                    &bytesRead);
}
3158

3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170
/***************************************************************************
 *
 * Internal Method
 *
 * Mark a directory entry in the file as free.
 */
static HRESULT StorageImpl_DestroyDirEntry(
  StorageBaseImpl *base,
  DirRef index)
{
  BYTE emptyData[RAW_DIRENTRY_SIZE];
  StorageImpl *storage = (StorageImpl*)base;
3171

3172
  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3173

3174 3175
  return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
}
3176

3177 3178 3179 3180 3181 3182 3183
/******************************************************************************
 *      UpdateRawDirEntry
 *
 * Update raw directory entry data from the fields in newData.
 *
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
 */
3184
static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3185 3186
{
  memset(buffer, 0, RAW_DIRENTRY_SIZE);
3187

3188 3189 3190 3191
  memcpy(
    buffer + OFFSET_PS_NAME,
    newData->name,
    DIRENTRY_NAME_BUFFER_LEN );
3192

3193
  memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3194

3195 3196 3197 3198
  StorageUtl_WriteWord(
    buffer,
      OFFSET_PS_NAMELENGTH,
      newData->sizeOfNameString);
3199

3200 3201 3202 3203
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_LEFTCHILD,
      newData->leftChild);
3204

3205 3206 3207 3208
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_RIGHTCHILD,
      newData->rightChild);
3209

3210 3211 3212 3213
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_DIRROOT,
      newData->dirRootEntry);
3214

3215 3216 3217 3218
  StorageUtl_WriteGUID(
    buffer,
      OFFSET_PS_GUID,
      &newData->clsid);
3219

3220 3221 3222 3223
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_CTIMELOW,
      newData->ctime.dwLowDateTime);
3224

3225 3226 3227 3228
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_CTIMEHIGH,
      newData->ctime.dwHighDateTime);
3229

3230 3231 3232 3233
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_MTIMELOW,
      newData->mtime.dwLowDateTime);
3234

3235 3236 3237 3238
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_MTIMEHIGH,
      newData->ctime.dwHighDateTime);
3239

3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253
  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_STARTBLOCK,
      newData->startingBlock);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_SIZE,
      newData->size.u.LowPart);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_SIZE_HIGH,
      newData->size.u.HighPart);
3254 3255
}

3256 3257 3258 3259 3260 3261 3262 3263 3264 3265
/***************************************************************************
 *
 * Internal Method
 *
 * Reserve a directory entry in the file and initialize it.
 */
static HRESULT StorageImpl_CreateDirEntry(
  StorageBaseImpl *base,
  const DirEntry *newData,
  DirRef *index)
3266
{
3267 3268 3269 3270 3271 3272
  StorageImpl *storage = (StorageImpl*)base;
  ULONG       currentEntryIndex    = 0;
  ULONG       newEntryIndex        = DIRENTRY_NULL;
  HRESULT hr = S_OK;
  BYTE currentData[RAW_DIRENTRY_SIZE];
  WORD sizeOfNameString;
3273

3274 3275 3276 3277 3278
  do
  {
    hr = StorageImpl_ReadRawDirEntry(storage,
                                     currentEntryIndex,
                                     currentData);
3279

3280 3281 3282 3283 3284 3285
    if (SUCCEEDED(hr))
    {
      StorageUtl_ReadWord(
        currentData,
        OFFSET_PS_NAMELENGTH,
        &sizeOfNameString);
3286

3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302
      if (sizeOfNameString == 0)
      {
        /*
         * The entry exists and is available, we found it.
         */
        newEntryIndex = currentEntryIndex;
      }
    }
    else
    {
      /*
       * We exhausted the directory entries, we will create more space below
       */
      newEntryIndex = currentEntryIndex;
    }
    currentEntryIndex++;
3303

3304
  } while (newEntryIndex == DIRENTRY_NULL);
3305 3306

  /*
3307
   * grow the directory stream
3308
   */
3309 3310
  if (FAILED(hr))
  {
3311 3312 3313 3314 3315
    BYTE           emptyData[RAW_DIRENTRY_SIZE];
    ULARGE_INTEGER newSize;
    ULONG          entryIndex;
    ULONG          lastEntry     = 0;
    ULONG          blockCount    = 0;
3316

3317 3318 3319 3320 3321
    /*
     * obtain the new count of blocks in the directory stream
     */
    blockCount = BlockChainStream_GetCount(
                   storage->rootBlockChain)+1;
3322

3323 3324 3325 3326
    /*
     * initialize the size used by the directory stream
     */
    newSize.QuadPart  = (ULONGLONG)storage->bigBlockSize * blockCount;
3327

3328 3329 3330 3331
    /*
     * add a block to the directory stream
     */
    BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3332

3333 3334 3335 3336 3337
    /*
     * memset the empty entry in order to initialize the unused newly
     * created entries
     */
    memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3338

3339 3340 3341 3342
    /*
     * initialize them
     */
    lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3343

3344 3345 3346 3347
    for(
      entryIndex = newEntryIndex + 1;
      entryIndex < lastEntry;
      entryIndex++)
3348
    {
3349 3350 3351 3352
      StorageImpl_WriteRawDirEntry(
        storage,
        entryIndex,
        emptyData);
3353
    }
3354

3355 3356
    StorageImpl_SaveFileHeader(storage);
  }
3357

3358
  UpdateRawDirEntry(currentData, newData);
3359

3360
  hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3361 3362

  if (SUCCEEDED(hr))
3363
    *index = newEntryIndex;
3364

3365
  return hr;
3366 3367
}

3368
/******************************************************************************
3369
 *      StorageImpl_ReadDirEntry
3370
 *
3371
 * This method will read the specified directory entry.
3372
 */
3373
static HRESULT StorageImpl_ReadDirEntry(
3374 3375 3376
  StorageImpl* This,
  DirRef         index,
  DirEntry*      buffer)
3377
{
3378 3379
  BYTE           currentEntry[RAW_DIRENTRY_SIZE];
  HRESULT        readRes;
3380

3381
  readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3382

3383
  if (SUCCEEDED(readRes))
3384
  {
3385 3386 3387 3388 3389 3390
    memset(buffer->name, 0, sizeof(buffer->name));
    memcpy(
      buffer->name,
      (WCHAR *)currentEntry+OFFSET_PS_NAME,
      DIRENTRY_NAME_BUFFER_LEN );
    TRACE("storage name: %s\n", debugstr_w(buffer->name));
3391

3392
    memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3393

3394 3395 3396 3397
    StorageUtl_ReadWord(
      currentEntry,
      OFFSET_PS_NAMELENGTH,
      &buffer->sizeOfNameString);
3398

3399 3400 3401 3402
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_LEFTCHILD,
      &buffer->leftChild);
3403

3404 3405 3406 3407
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_RIGHTCHILD,
      &buffer->rightChild);
3408

3409 3410 3411 3412
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_DIRROOT,
      &buffer->dirRootEntry);
3413

3414 3415 3416 3417
    StorageUtl_ReadGUID(
      currentEntry,
      OFFSET_PS_GUID,
      &buffer->clsid);
3418

3419 3420 3421 3422
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_CTIMELOW,
      &buffer->ctime.dwLowDateTime);
3423

3424 3425 3426 3427
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_CTIMEHIGH,
      &buffer->ctime.dwHighDateTime);
3428

3429 3430 3431 3432
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_MTIMELOW,
      &buffer->mtime.dwLowDateTime);
3433

3434 3435 3436 3437
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_MTIMEHIGH,
      &buffer->mtime.dwHighDateTime);
3438

3439 3440 3441 3442
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_STARTBLOCK,
      &buffer->startingBlock);
3443

3444 3445 3446 3447
    StorageUtl_ReadDWord(
      currentEntry,
      OFFSET_PS_SIZE,
      &buffer->size.u.LowPart);
3448

3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
    if (This->bigBlockSize < 4096)
    {
      /* Version 3 files may have junk in the high part of size. */
      buffer->size.u.HighPart = 0;
    }
    else
    {
      StorageUtl_ReadDWord(
        currentEntry,
        OFFSET_PS_SIZE_HIGH,
        &buffer->size.u.HighPart);
    }
3461 3462
  }

3463 3464
  return readRes;
}
3465

3466 3467 3468
/*********************************************************************
 * Write the specified directory entry to the file
 */
3469
static HRESULT StorageImpl_WriteDirEntry(
3470 3471 3472 3473 3474
  StorageImpl*          This,
  DirRef                index,
  const DirEntry*       buffer)
{
  BYTE currentEntry[RAW_DIRENTRY_SIZE];
3475

3476
  UpdateRawDirEntry(currentEntry, buffer);
3477

3478 3479
  return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
}
3480

3481 3482 3483 3484 3485 3486 3487 3488

/************************************************************************
 * StorageImpl implementation : Block methods
 ***********************************************************************/

static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
{
    return (ULONGLONG)(index+1) * This->bigBlockSize;
3489 3490
}

3491 3492 3493 3494 3495
static HRESULT StorageImpl_ReadBigBlock(
  StorageImpl* This,
  ULONG          blockIndex,
  void*          buffer,
  ULONG*         out_read)
3496
{
3497 3498 3499
  ULARGE_INTEGER ulOffset;
  DWORD  read=0;
  HRESULT hr;
3500

3501
  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3502

3503 3504 3505
  hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);

  if (SUCCEEDED(hr) &&  read < This->bigBlockSize)
3506
  {
3507 3508
    /* File ends during this block; fill the rest with 0's. */
    memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3509 3510
  }

3511 3512 3513
  if (out_read) *out_read = read;

  return hr;
3514 3515
}

3516 3517 3518 3519 3520
static BOOL StorageImpl_ReadDWordFromBigBlock(
  StorageImpl*  This,
  ULONG         blockIndex,
  ULONG         offset,
  DWORD*        value)
3521
{
3522 3523 3524
  ULARGE_INTEGER ulOffset;
  DWORD  read;
  DWORD  tmp;
3525

3526 3527
  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
  ulOffset.QuadPart += offset;
3528

3529 3530 3531 3532
  StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
  *value = lendian32toh(tmp);
  return (read == sizeof(DWORD));
}
3533

3534 3535 3536 3537 3538 3539 3540
static BOOL StorageImpl_WriteBigBlock(
  StorageImpl*  This,
  ULONG         blockIndex,
  const void*   buffer)
{
  ULARGE_INTEGER ulOffset;
  DWORD  wrote;
3541

3542
  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3543

3544 3545 3546
  StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
  return (wrote == This->bigBlockSize);
}
3547

3548 3549 3550 3551 3552 3553 3554 3555
static BOOL StorageImpl_WriteDWordToBigBlock(
  StorageImpl* This,
  ULONG         blockIndex,
  ULONG         offset,
  DWORD         value)
{
  ULARGE_INTEGER ulOffset;
  DWORD  wrote;
3556

3557 3558
  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
  ulOffset.QuadPart += offset;
3559

3560 3561 3562
  value = htole32(value);
  StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
  return (wrote == sizeof(DWORD));
3563 3564 3565
}

/******************************************************************************
3566
 *              Storage32Impl_SmallBlocksToBigBlocks
3567
 *
3568 3569
 * This method will convert a small block chain to a big block chain.
 * The small block chain will be destroyed.
3570
 */
3571
static BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3572 3573
                      StorageImpl* This,
                      SmallBlockChainStream** ppsbChain)
3574
{
3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585
  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
  ULARGE_INTEGER size, offset;
  ULONG cbRead, cbWritten;
  ULARGE_INTEGER cbTotalRead;
  DirRef streamEntryRef;
  HRESULT resWrite = S_OK;
  HRESULT resRead;
  DirEntry streamEntry;
  BYTE *buffer;
  BlockChainStream *bbTempChain = NULL;
  BlockChainStream *bigBlockChain = NULL;
3586

3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600
  /*
   * Create a temporary big block chain that doesn't have
   * an associated directory entry. This temporary chain will be
   * used to copy data from small blocks to big blocks.
   */
  bbTempChain = BlockChainStream_Construct(This,
                                           &bbHeadOfChain,
                                           DIRENTRY_NULL);
  if(!bbTempChain) return NULL;
  /*
   * Grow the big block chain.
   */
  size = SmallBlockChainStream_GetSize(*ppsbChain);
  BlockChainStream_SetSize(bbTempChain, size);
3601

3602 3603 3604 3605 3606 3607 3608
  /*
   * Copy the contents of the small block chain to the big block chain
   * by small block size increments.
   */
  offset.u.LowPart = 0;
  offset.u.HighPart = 0;
  cbTotalRead.QuadPart = 0;
3609

3610 3611
  buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
  do
3612
  {
3613 3614 3615 3616 3617 3618 3619
    resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
                                           offset,
                                           min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
                                           buffer,
                                           &cbRead);
    if (FAILED(resRead))
        break;
3620

3621 3622 3623
    if (cbRead > 0)
    {
        cbTotalRead.QuadPart += cbRead;
3624

3625 3626 3627 3628 3629
        resWrite = BlockChainStream_WriteAt(bbTempChain,
                                            offset,
                                            cbRead,
                                            buffer,
                                            &cbWritten);
3630

3631 3632
        if (FAILED(resWrite))
            break;
3633

3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647
        offset.u.LowPart += cbRead;
    }
    else
    {
        resRead = STG_E_READFAULT;
        break;
    }
  } while (cbTotalRead.QuadPart < size.QuadPart);
  HeapFree(GetProcessHeap(),0,buffer);

  size.u.HighPart = 0;
  size.u.LowPart  = 0;

  if (FAILED(resRead) || FAILED(resWrite))
3648
  {
3649 3650 3651 3652
    ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
    BlockChainStream_SetSize(bbTempChain, size);
    BlockChainStream_Destroy(bbTempChain);
    return NULL;
3653 3654
  }

3655 3656 3657 3658 3659 3660 3661
  /*
   * Destroy the small block chain.
   */
  streamEntryRef = (*ppsbChain)->ownerDirEntry;
  SmallBlockChainStream_SetSize(*ppsbChain, size);
  SmallBlockChainStream_Destroy(*ppsbChain);
  *ppsbChain = 0;
3662 3663

  /*
3664 3665
   * Change the directory entry. This chain is now a big block chain
   * and it doesn't reside in the small blocks chain anymore.
3666
   */
3667
  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3668

3669
  streamEntry.startingBlock = bbHeadOfChain;
3670

3671
  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3672

3673 3674 3675 3676 3677 3678 3679 3680
  /*
   * Destroy the temporary entryless big block chain.
   * Create a new big block chain associated with this entry.
   */
  BlockChainStream_Destroy(bbTempChain);
  bigBlockChain = BlockChainStream_Construct(This,
                                             NULL,
                                             streamEntryRef);
3681

3682
  return bigBlockChain;
3683 3684
}

3685
/******************************************************************************
3686
 *              Storage32Impl_BigBlocksToSmallBlocks
3687
 *
3688 3689
 * This method will convert a big block chain to a small block chain.
 * The big block chain will be destroyed on success.
3690
 */
3691
static SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3692 3693 3694
                           StorageImpl* This,
                           BlockChainStream** ppbbChain,
                           ULARGE_INTEGER newSize)
3695
{
3696 3697 3698 3699 3700 3701 3702
    ULARGE_INTEGER size, offset, cbTotalRead;
    ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
    DirRef streamEntryRef;
    HRESULT resWrite = S_OK, resRead = S_OK;
    DirEntry streamEntry;
    BYTE* buffer;
    SmallBlockChainStream* sbTempChain;
3703

3704
    TRACE("%p %p\n", This, ppbbChain);
3705

3706 3707
    sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
            DIRENTRY_NULL);
3708

3709 3710
    if(!sbTempChain)
        return NULL;
3711

3712 3713 3714
    SmallBlockChainStream_SetSize(sbTempChain, newSize);
    size = BlockChainStream_GetSize(*ppbbChain);
    size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3715

3716 3717 3718 3719 3720
    offset.u.HighPart = 0;
    offset.u.LowPart = 0;
    cbTotalRead.QuadPart = 0;
    buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
    while(cbTotalRead.QuadPart < size.QuadPart)
3721
    {
3722 3723 3724
        resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
                min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
                buffer, &cbRead);
3725

3726 3727
        if(FAILED(resRead))
            break;
3728

3729 3730 3731
        if(cbRead > 0)
        {
            cbTotalRead.QuadPart += cbRead;
3732

3733 3734
            resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
                    cbRead, buffer, &cbWritten);
3735

3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752
            if(FAILED(resWrite))
                break;

            offset.u.LowPart += cbRead;
        }
        else
        {
            resRead = STG_E_READFAULT;
            break;
        }
    }
    HeapFree(GetProcessHeap(), 0, buffer);

    size.u.HighPart = 0;
    size.u.LowPart = 0;

    if(FAILED(resRead) || FAILED(resWrite))
3753
    {
3754 3755 3756 3757
        ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
        SmallBlockChainStream_SetSize(sbTempChain, size);
        SmallBlockChainStream_Destroy(sbTempChain);
        return NULL;
3758 3759
    }

3760 3761 3762 3763 3764
    /* destroy the original big block chain */
    streamEntryRef = (*ppbbChain)->ownerDirEntry;
    BlockChainStream_SetSize(*ppbbChain, size);
    BlockChainStream_Destroy(*ppbbChain);
    *ppbbChain = NULL;
3765

3766 3767 3768 3769 3770 3771
    StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
    streamEntry.startingBlock = sbHeadOfChain;
    StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);

    SmallBlockChainStream_Destroy(sbTempChain);
    return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3772 3773 3774
}

/******************************************************************************
3775
 *      Storage32Impl_AddBlockDepot
3776
 *
3777 3778
 * This will create a depot block, essentially it is a block initialized
 * to BLOCK_UNUSEDs.
3779
 */
3780
static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3781
{
3782 3783 3784 3785
  BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
  ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
  ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3786

3787 3788 3789 3790
  /*
   * Initialize blocks as free
   */
  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3791

3792 3793 3794 3795 3796 3797 3798
  /* Reserve the range lock sector */
  if (depotIndex == rangeLockDepot)
  {
    ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
  }

  StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3799 3800 3801
}

/******************************************************************************
3802
 *      Storage32Impl_GetExtDepotBlock
3803
 *
3804 3805 3806
 * Returns the index of the block that corresponds to the specified depot
 * index. This method is only for depot indexes equal or greater than
 * COUNT_BBDEPOTINHEADER.
3807
 */
3808
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3809
{
3810 3811 3812 3813 3814 3815 3816 3817
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
  ULONG blockIndex             = BLOCK_UNUSED;
  ULONG extBlockIndex;
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
  int index, num_blocks;
3818

3819
  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3820

3821 3822
  if (extBlockCount >= This->extBigBlockDepotCount)
    return BLOCK_UNUSED;
3823

3824
  if (This->indexExtBlockDepotCached != extBlockCount)
3825
  {
3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838
    extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];

    StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);

    num_blocks = This->bigBlockSize / 4;

    for (index = 0; index < num_blocks; index++)
    {
      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
      This->extBlockDepotCached[index] = blockIndex;
    }

    This->indexExtBlockDepotCached = extBlockCount;
3839
  }
3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867

  blockIndex = This->extBlockDepotCached[extBlockOffset];

  return blockIndex;
}

/******************************************************************************
 *      Storage32Impl_SetExtDepotBlock
 *
 * Associates the specified block index to the specified depot index.
 * This method is only for depot indexes equal or greater than
 * COUNT_BBDEPOTINHEADER.
 */
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
{
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
  ULONG extBlockIndex;

  assert(depotIndex >= COUNT_BBDEPOTINHEADER);

  assert(extBlockCount < This->extBigBlockDepotCount);

  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];

  if (extBlockIndex != BLOCK_UNUSED)
3868
  {
3869 3870 3871
    StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
                        extBlockOffset * sizeof(ULONG),
                        blockIndex);
3872
  }
3873

3874
  if (This->indexExtBlockDepotCached == extBlockCount)
3875
  {
3876
    This->extBlockDepotCached[extBlockOffset] = blockIndex;
3877
  }
3878 3879 3880
}

/******************************************************************************
3881
 *      Storage32Impl_AddExtBlockDepot
3882
 *
3883
 * Creates an extended depot block.
3884
 */
3885
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3886
{
3887 3888 3889 3890 3891 3892 3893
  ULONG numExtBlocks           = This->extBigBlockDepotCount;
  ULONG nextExtBlock           = This->extBigBlockDepotStart;
  BYTE  depotBuffer[MAX_BIG_BLOCK_SIZE];
  ULONG index                  = BLOCK_UNUSED;
  ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
  ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
  ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3894

3895 3896
  index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
          blocksPerDepotBlock;
3897

3898
  if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3899 3900
  {
    /*
3901
     * The first extended block.
3902
     */
3903 3904 3905 3906 3907 3908 3909 3910
    This->extBigBlockDepotStart = index;
  }
  else
  {
    /*
     * Find the last existing extended block.
     */
    nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3911

3912 3913 3914 3915 3916 3917
    /*
     * Add the new extended block to the chain.
     */
    StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
                                     index);
  }
3918

3919 3920 3921 3922 3923
  /*
   * Initialize this block.
   */
  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
  StorageImpl_WriteBigBlock(This, index, depotBuffer);
3924

3925 3926 3927 3928 3929
  /* Add the block to our cache. */
  if (This->extBigBlockDepotLocationsSize == numExtBlocks)
  {
    ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
    ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3930

3931 3932
    memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
    HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3933

3934 3935 3936 3937
    This->extBigBlockDepotLocations = new_cache;
    This->extBigBlockDepotLocationsSize = new_cache_size;
  }
  This->extBigBlockDepotLocations[numExtBlocks] = index;
3938

3939 3940
  return index;
}
3941

3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977
/************************************************************************
 * StorageImpl_GetNextBlockInChain
 *
 * This method will retrieve the block index of the next big block in
 * in the chain.
 *
 * Params:  This       - Pointer to the Storage object.
 *          blockIndex - Index of the block to retrieve the chain
 *                       for.
 *          nextBlockIndex - receives the return value.
 *
 * Returns: This method returns the index of the next block in the chain.
 *          It will return the constants:
 *              BLOCK_SPECIAL - If the block given was not part of a
 *                              chain.
 *              BLOCK_END_OF_CHAIN - If the block given was the last in
 *                                   a chain.
 *              BLOCK_UNUSED - If the block given was not past of a chain
 *                             and is available.
 *              BLOCK_EXTBBDEPOT - This block is part of the extended
 *                                 big block depot.
 *
 * See Windows documentation for more details on IStorage methods.
 */
static HRESULT StorageImpl_GetNextBlockInChain(
  StorageImpl* This,
  ULONG        blockIndex,
  ULONG*       nextBlockIndex)
{
  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
  ULONG read;
  ULONG depotBlockIndexPos;
  int index, num_blocks;
3978

3979
  *nextBlockIndex   = BLOCK_SPECIAL;
3980

3981 3982 3983 3984 3985 3986
  if(depotBlockCount >= This->bigBlockDepotCount)
  {
    WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
	 This->bigBlockDepotCount);
    return STG_E_READFAULT;
  }
3987

3988 3989 3990 3991 3992 3993 3994 3995
  /*
   * Cache the currently accessed depot block.
   */
  if (depotBlockCount != This->indexBlockDepotCached)
  {
    This->indexBlockDepotCached = depotBlockCount;

    if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3996
    {
3997 3998 3999 4000 4001 4002 4003 4004
      depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
    }
    else
    {
      /*
       * We have to look in the extended depot.
       */
      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4005
    }
4006

4007
    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4008

4009 4010 4011 4012 4013 4014
    if (!read)
      return STG_E_READFAULT;

    num_blocks = This->bigBlockSize / 4;

    for (index = 0; index < num_blocks; index++)
Alexandre Julliard's avatar
Alexandre Julliard committed
4015
    {
4016 4017
      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
      This->blockDepotCached[index] = *nextBlockIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
4018
    }
4019
  }
4020

4021 4022 4023
  *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];

  return S_OK;
4024 4025 4026
}

/******************************************************************************
4027
 *      Storage32Impl_GetNextExtendedBlock
4028
 *
4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039
 * Given an extended block this method will return the next extended block.
 *
 * NOTES:
 * The last ULONG of an extended block is the block index of the next
 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
 * depot.
 *
 * Return values:
 *    - The index of the next extended block
 *    - BLOCK_UNUSED: there is no next extended block.
 *    - Any other return values denotes failure.
4040
 */
4041
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
4042
{
4043 4044
  ULONG nextBlockIndex   = BLOCK_SPECIAL;
  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4045

4046 4047
  StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
                        &nextBlockIndex);
4048

4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086
  return nextBlockIndex;
}

/******************************************************************************
 *      StorageImpl_SetNextBlockInChain
 *
 * This method will write the index of the specified block's next block
 * in the big block depot.
 *
 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
 *              do the following
 *
 * StorageImpl_SetNextBlockInChain(This, 3, 1);
 * StorageImpl_SetNextBlockInChain(This, 1, 7);
 * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
 *
 */
static void StorageImpl_SetNextBlockInChain(
          StorageImpl* This,
          ULONG          blockIndex,
          ULONG          nextBlock)
{
  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
  ULONG depotBlockIndexPos;

  assert(depotBlockCount < This->bigBlockDepotCount);
  assert(blockIndex != nextBlock);

  if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
    /* This should never happen (storage file format spec forbids it), but
     * older versions of Wine may have generated broken files. We don't want to
     * assert and potentially lose data, but we do want to know if this ever
     * happens in a newly-created file. */
    ERR("Using range lock page\n");

  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4087
  {
4088
    depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4089
  }
4090
  else
4091 4092
  {
    /*
4093
     * We have to look in the extended depot.
4094
     */
4095
    depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4096
  }
4097

4098 4099
  StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
                        nextBlock);
4100
  /*
4101
   * Update the cached block depot, if necessary.
4102
   */
4103 4104 4105 4106 4107
  if (depotBlockCount == This->indexBlockDepotCached)
  {
    This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
  }
}
4108

4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128
/******************************************************************************
 *      StorageImpl_GetNextFreeBigBlock
 *
 * Returns the index of the next free big block.
 * If the big block depot is filled, this method will enlarge it.
 *
 */
static ULONG StorageImpl_GetNextFreeBigBlock(
  StorageImpl* This)
{
  ULONG depotBlockIndexPos;
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
  ULONG depotBlockOffset;
  ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
  ULONG nextBlockIndex    = BLOCK_SPECIAL;
  int   depotIndex        = 0;
  ULONG freeBlock         = BLOCK_UNUSED;
  ULONG read;
  ULARGE_INTEGER neededSize;
  STATSTG statstg;
4129

4130 4131
  depotIndex = This->prevFreeBlock / blocksPerDepot;
  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4132

4133 4134 4135 4136 4137 4138 4139 4140
  /*
   * Scan the entire big block depot until we find a block marked free
   */
  while (nextBlockIndex != BLOCK_UNUSED)
  {
    if (depotIndex < COUNT_BBDEPOTINHEADER)
    {
      depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4141

4142 4143 4144 4145 4146 4147
      /*
       * Grow the primary depot.
       */
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        depotBlockIndexPos = depotIndex*blocksPerDepot;
4148

4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167
        /*
         * Add a block depot.
         */
        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
        This->bigBlockDepotCount++;
        This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;

        /*
         * Flag it as a block depot.
         */
        StorageImpl_SetNextBlockInChain(This,
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);

        /* Save new header information.
         */
        StorageImpl_SaveFileHeader(This);
      }
    }
4168
    else
4169 4170
    {
      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4171

4172 4173 4174 4175 4176 4177 4178 4179
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        /*
         * Grow the extended depot.
         */
        ULONG extIndex       = BLOCK_UNUSED;
        ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
        ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4180

4181 4182 4183 4184 4185 4186 4187 4188 4189 4190
        if (extBlockOffset == 0)
        {
          /* We need an extended block.
           */
          extIndex = Storage32Impl_AddExtBlockDepot(This);
          This->extBigBlockDepotCount++;
          depotBlockIndexPos = extIndex + 1;
        }
        else
          depotBlockIndexPos = depotIndex * blocksPerDepot;
4191

4192 4193 4194 4195 4196 4197
        /*
         * Add a block depot and mark it in the extended block.
         */
        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
        This->bigBlockDepotCount++;
        Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4198

4199 4200 4201 4202 4203
        /* Flag the block depot.
         */
        StorageImpl_SetNextBlockInChain(This,
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);
4204

4205 4206 4207 4208
        /* If necessary, flag the extended depot block.
         */
        if (extIndex != BLOCK_UNUSED)
          StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
4209

4210 4211 4212 4213 4214
        /* Save header information.
         */
        StorageImpl_SaveFileHeader(This);
      }
    }
4215

4216
    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4217

4218 4219 4220 4221 4222 4223
    if (read)
    {
      while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
              ( nextBlockIndex != BLOCK_UNUSED))
      {
        StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4224

4225 4226 4227 4228 4229
        if (nextBlockIndex == BLOCK_UNUSED)
        {
          freeBlock = (depotIndex * blocksPerDepot) +
                      (depotBlockOffset/sizeof(ULONG));
        }
4230

4231 4232 4233 4234 4235 4236
        depotBlockOffset += sizeof(ULONG);
      }
    }

    depotIndex++;
    depotBlockOffset = 0;
4237
  }
4238

4239
  /*
4240
   * make sure that the block physically exists before using it
4241
   */
4242
  neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4243

4244
  ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4245

4246 4247
  if (neededSize.QuadPart > statstg.cbSize.QuadPart)
    ILockBytes_SetSize(This->lockBytes, neededSize);
4248

4249
  This->prevFreeBlock = freeBlock;
4250

4251
  return freeBlock;
4252 4253
}

4254
/******************************************************************************
4255
 *      StorageImpl_FreeBigBlock
4256
 *
4257
 * This method will flag the specified block as free in the big block depot.
4258
 */
4259 4260 4261
static void StorageImpl_FreeBigBlock(
  StorageImpl* This,
  ULONG          blockIndex)
4262
{
4263
  StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4264

4265 4266
  if (blockIndex < This->prevFreeBlock)
    This->prevFreeBlock = blockIndex;
4267 4268
}

4269

4270 4271 4272 4273 4274 4275
static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
{
  StorageImpl *This = (StorageImpl*)base;
  return StorageImpl_WriteDirEntry(This, index, data);
}
4276

4277 4278 4279 4280 4281 4282
static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
{
  StorageImpl *This = (StorageImpl*)base;
  return StorageImpl_ReadDirEntry(This, index, data);
}
4283

4284 4285 4286
static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
{
  int i;
4287

4288 4289 4290 4291 4292 4293 4294
  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
  {
    if (!This->blockChainCache[i])
    {
      return &This->blockChainCache[i];
    }
  }
4295

4296
  i = This->blockChainToEvict;
4297

4298 4299
  BlockChainStream_Destroy(This->blockChainCache[i]);
  This->blockChainCache[i] = NULL;
4300

4301 4302 4303
  This->blockChainToEvict++;
  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
    This->blockChainToEvict = 0;
4304

4305 4306
  return &This->blockChainCache[i];
}
4307

4308 4309 4310 4311
static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
    DirRef index)
{
  int i, free_index=-1;
4312

4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323
  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
  {
    if (!This->blockChainCache[i])
    {
      if (free_index == -1) free_index = i;
    }
    else if (This->blockChainCache[i]->ownerDirEntry == index)
    {
      return &This->blockChainCache[i];
    }
  }
4324

4325 4326 4327
  if (free_index == -1)
  {
    free_index = This->blockChainToEvict;
4328

4329 4330
    BlockChainStream_Destroy(This->blockChainCache[free_index]);
    This->blockChainCache[free_index] = NULL;
4331

4332 4333 4334 4335
    This->blockChainToEvict++;
    if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
      This->blockChainToEvict = 0;
  }
4336

4337 4338
  This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
  return &This->blockChainCache[free_index];
4339 4340
}

4341
static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
4342
{
4343
  int i;
Alexandre Julliard's avatar
Alexandre Julliard committed
4344

4345
  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4346
  {
4347 4348 4349 4350 4351 4352 4353 4354
    if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
    {
      BlockChainStream_Destroy(This->blockChainCache[i]);
      This->blockChainCache[i] = NULL;
      return;
    }
  }
}
4355

4356 4357 4358 4359 4360 4361 4362
static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
  ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
  StorageImpl *This = (StorageImpl*)base;
  DirEntry data;
  HRESULT hr;
  ULONG bytesToRead;
4363

4364 4365
  hr = StorageImpl_ReadDirEntry(This, index, &data);
  if (FAILED(hr)) return hr;
4366

4367 4368 4369 4370 4371
  if (data.size.QuadPart == 0)
  {
    *bytesRead = 0;
    return S_OK;
  }
4372

4373 4374 4375 4376 4377 4378 4379 4380
  if (offset.QuadPart + size > data.size.QuadPart)
  {
    bytesToRead = data.size.QuadPart - offset.QuadPart;
  }
  else
  {
    bytesToRead = size;
  }
4381

4382 4383 4384
  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
    SmallBlockChainStream *stream;
4385

4386 4387
    stream = SmallBlockChainStream_Construct(This, NULL, index);
    if (!stream) return E_OUTOFMEMORY;
4388

4389
    hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4390

4391
    SmallBlockChainStream_Destroy(stream);
4392

4393 4394 4395 4396 4397
    return hr;
  }
  else
  {
    BlockChainStream *stream = NULL;
4398

4399 4400
    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
    if (!stream) return E_OUTOFMEMORY;
4401

4402
    hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4403

4404
    return hr;
4405 4406 4407
  }
}

4408 4409
static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
  ULARGE_INTEGER newsize)
4410
{
4411 4412 4413 4414 4415
  StorageImpl *This = (StorageImpl*)base;
  DirEntry data;
  HRESULT hr;
  SmallBlockChainStream *smallblock=NULL;
  BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4416

4417 4418
  hr = StorageImpl_ReadDirEntry(This, index, &data);
  if (FAILED(hr)) return hr;
4419

4420 4421 4422
  /* In simple mode keep the stream size above the small block limit */
  if (This->base.openFlags & STGM_SIMPLE)
    newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4423

4424 4425
  if (data.size.QuadPart == newsize.QuadPart)
    return S_OK;
4426

4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452
  /* Create a block chain object of the appropriate type */
  if (data.size.QuadPart == 0)
  {
    if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
    {
      smallblock = SmallBlockChainStream_Construct(This, NULL, index);
      if (!smallblock) return E_OUTOFMEMORY;
    }
    else
    {
      pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
      bigblock = *pbigblock;
      if (!bigblock) return E_OUTOFMEMORY;
    }
  }
  else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
    smallblock = SmallBlockChainStream_Construct(This, NULL, index);
    if (!smallblock) return E_OUTOFMEMORY;
  }
  else
  {
    pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
    bigblock = *pbigblock;
    if (!bigblock) return E_OUTOFMEMORY;
  }
4453

4454 4455 4456 4457 4458 4459 4460 4461 4462
  /* Change the block chain type if necessary. */
  if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
  {
    bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
    if (!bigblock)
    {
      SmallBlockChainStream_Destroy(smallblock);
      return E_FAIL;
    }
4463

4464 4465 4466 4467
    pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
    *pbigblock = bigblock;
  }
  else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4468
  {
4469 4470 4471
    smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
    if (!smallblock)
      return E_FAIL;
4472 4473
  }

4474 4475 4476 4477 4478 4479 4480 4481 4482 4483
  /* Set the size of the block chain. */
  if (smallblock)
  {
    SmallBlockChainStream_SetSize(smallblock, newsize);
    SmallBlockChainStream_Destroy(smallblock);
  }
  else
  {
    BlockChainStream_SetSize(bigblock, newsize);
  }
4484

4485 4486 4487 4488 4489 4490 4491 4492
  /* Set the size in the directory entry. */
  hr = StorageImpl_ReadDirEntry(This, index, &data);
  if (SUCCEEDED(hr))
  {
    data.size = newsize;

    hr = StorageImpl_WriteDirEntry(This, index, &data);
  }
4493
  return hr;
4494
}
4495

4496 4497
static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
  ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4498
{
4499 4500 4501 4502
  StorageImpl *This = (StorageImpl*)base;
  DirEntry data;
  HRESULT hr;
  ULARGE_INTEGER newSize;
4503

4504 4505
  hr = StorageImpl_ReadDirEntry(This, index, &data);
  if (FAILED(hr)) return hr;
4506

4507 4508
  /* Grow the stream if necessary */
  newSize.QuadPart = offset.QuadPart + size;
4509

4510 4511 4512 4513 4514
  if (newSize.QuadPart > data.size.QuadPart)
  {
    hr = StorageImpl_StreamSetSize(base, index, newSize);
    if (FAILED(hr))
      return hr;
4515

4516 4517 4518
    hr = StorageImpl_ReadDirEntry(This, index, &data);
    if (FAILED(hr)) return hr;
  }
4519

4520 4521 4522
  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
    SmallBlockChainStream *stream;
4523

4524 4525
    stream = SmallBlockChainStream_Construct(This, NULL, index);
    if (!stream) return E_OUTOFMEMORY;
4526

4527
    hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
4528

4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541
    SmallBlockChainStream_Destroy(stream);

    return hr;
  }
  else
  {
    BlockChainStream *stream;

    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
    if (!stream) return E_OUTOFMEMORY;

    return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
  }
4542 4543
}

4544 4545
static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
  DirRef src)
4546
{
4547 4548 4549
  StorageImpl *This = (StorageImpl*)base;
  DirEntry dst_data, src_data;
  HRESULT hr;
4550

4551
  hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4552

4553 4554
  if (SUCCEEDED(hr))
    hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4555

4556
  if (SUCCEEDED(hr))
4557
  {
4558 4559 4560
    StorageImpl_DeleteCachedBlockChainStream(This, src);
    dst_data.startingBlock = src_data.startingBlock;
    dst_data.size = src_data.size;
4561

4562 4563
    hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
  }
4564

4565 4566
  return hr;
}
4567

4568 4569 4570 4571 4572 4573
static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
{
  HRESULT hr=S_OK;
  DirEntry currentEntry;
  DirRef      currentEntryRef;
  BlockChainStream *blockChainStream;
4574

4575
  if (create)
4576
  {
4577 4578
    ULARGE_INTEGER size;
    BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4579

4580 4581 4582
    /* Discard any existing data. */
    size.QuadPart = 0;
    ILockBytes_SetSize(This->lockBytes, size);
4583

4584 4585 4586 4587 4588 4589 4590 4591 4592
    /*
     * Initialize all header variables:
     * - The big block depot consists of one block and it is at block 0
     * - The directory table starts at block 1
     * - There is no small block depot
     */
    memset( This->bigBlockDepotStart,
            BLOCK_UNUSED,
            sizeof(This->bigBlockDepotStart));
4593

4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605
    This->bigBlockDepotCount    = 1;
    This->bigBlockDepotStart[0] = 0;
    This->rootStartBlock        = 1;
    This->smallBlockLimit       = LIMIT_TO_USE_SMALL_BLOCK;
    This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
    if (This->bigBlockSize == 4096)
      This->bigBlockSizeBits    = MAX_BIG_BLOCK_SIZE_BITS;
    else
      This->bigBlockSizeBits    = MIN_BIG_BLOCK_SIZE_BITS;
    This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
    This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
    This->extBigBlockDepotCount = 0;
4606

4607
    StorageImpl_SaveFileHeader(This);
4608

4609 4610 4611 4612 4613 4614
    /*
     * Add one block for the big block depot and one block for the directory table
     */
    size.u.HighPart = 0;
    size.u.LowPart  = This->bigBlockSize * 3;
    ILockBytes_SetSize(This->lockBytes, size);
4615

4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629
    /*
     * Initialize the big block depot
     */
    memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
    StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
    StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
    StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
  }
  else
  {
    /*
     * Load the header for the file.
     */
    hr = StorageImpl_LoadFileHeader(This);
4630

4631
    if (FAILED(hr))
4632
    {
4633 4634 4635
      return hr;
    }
  }
4636

4637 4638 4639 4640 4641
  /*
   * There is no block depot cached yet.
   */
  This->indexBlockDepotCached = 0xFFFFFFFF;
  This->indexExtBlockDepotCached = 0xFFFFFFFF;
4642

4643 4644 4645 4646
  /*
   * Start searching for free blocks with block 0.
   */
  This->prevFreeBlock = 0;
4647

4648
  This->firstFreeSmallBlock = 0;
4649

4650 4651 4652 4653 4654 4655
  /* Read the extended big block depot locations. */
  if (This->extBigBlockDepotCount != 0)
  {
    ULONG current_block = This->extBigBlockDepotStart;
    ULONG cache_size = This->extBigBlockDepotCount * 2;
    ULONG i;
4656

4657 4658 4659 4660
    This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
    if (!This->extBigBlockDepotLocations)
    {
      return E_OUTOFMEMORY;
4661
    }
4662

4663
    This->extBigBlockDepotLocationsSize = cache_size;
4664

4665
    for (i=0; i<This->extBigBlockDepotCount; i++)
4666
    {
4667 4668 4669 4670 4671 4672 4673
      if (current_block == BLOCK_END_OF_CHAIN)
      {
        WARN("File has too few extended big block depot blocks.\n");
        return STG_E_DOCFILECORRUPT;
      }
      This->extBigBlockDepotLocations[i] = current_block;
      current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4674
    }
4675 4676 4677 4678 4679 4680
  }
  else
  {
    This->extBigBlockDepotLocations = NULL;
    This->extBigBlockDepotLocationsSize = 0;
  }
4681

4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692
  /*
   * Create the block chain abstractions.
   */
  if(!(blockChainStream =
       BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
  {
    return STG_E_READFAULT;
  }
  if (!new_object)
    BlockChainStream_Destroy(This->rootBlockChain);
  This->rootBlockChain = blockChainStream;
4693

4694 4695 4696 4697 4698 4699 4700 4701 4702
  if(!(blockChainStream =
       BlockChainStream_Construct(This, &This->smallBlockDepotStart,
				  DIRENTRY_NULL)))
  {
    return STG_E_READFAULT;
  }
  if (!new_object)
    BlockChainStream_Destroy(This->smallBlockDepotChain);
  This->smallBlockDepotChain = blockChainStream;
4703

4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723
  /*
   * Write the root storage entry (memory only)
   */
  if (create)
  {
    static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
    DirEntry rootEntry;
    /*
     * Initialize the directory table
     */
    memset(&rootEntry, 0, sizeof(rootEntry));
    strcpyW(rootEntry.name, rootentryW);
    rootEntry.sizeOfNameString = sizeof(rootentryW);
    rootEntry.stgType          = STGTY_ROOT;
    rootEntry.leftChild        = DIRENTRY_NULL;
    rootEntry.rightChild       = DIRENTRY_NULL;
    rootEntry.dirRootEntry     = DIRENTRY_NULL;
    rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
    rootEntry.size.u.HighPart  = 0;
    rootEntry.size.u.LowPart   = 0;
4724

4725 4726
    StorageImpl_WriteDirEntry(This, 0, &rootEntry);
  }
4727

4728 4729 4730 4731
  /*
   * Find the ID of the root storage.
   */
  currentEntryRef = 0;
4732

4733
  do
4734
  {
4735 4736 4737 4738
    hr = StorageImpl_ReadDirEntry(
                      This,
                      currentEntryRef,
                      &currentEntry);
4739

4740
    if (SUCCEEDED(hr))
4741
    {
4742 4743
      if ( (currentEntry.sizeOfNameString != 0 ) &&
           (currentEntry.stgType          == STGTY_ROOT) )
4744
      {
4745
        This->base.storageDirEntry = currentEntryRef;
4746
      }
4747 4748
    }

4749
    currentEntryRef++;
4750

4751
  } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4752

4753
  if (FAILED(hr))
4754
  {
4755
    return STG_E_READFAULT;
4756 4757
  }

4758 4759 4760 4761 4762
  /*
   * Create the block chain abstraction for the small block root chain.
   */
  if(!(blockChainStream =
       BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4763
  {
4764
    return STG_E_READFAULT;
4765
  }
4766 4767 4768
  if (!new_object)
    BlockChainStream_Destroy(This->smallBlockRootChain);
  This->smallBlockRootChain = blockChainStream;
4769

4770 4771 4772 4773 4774 4775 4776 4777 4778
  if (!new_object)
  {
    int i;
    for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
    {
      BlockChainStream_Destroy(This->blockChainCache[i]);
      This->blockChainCache[i] = NULL;
    }
  }
4779 4780 4781 4782

  return hr;
}

4783 4784
static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
  ULONG* result, BOOL refresh)
4785
{
4786 4787 4788
  StorageImpl *This = (StorageImpl*)base;
  HRESULT hr=S_OK;
  DWORD oldTransactionSig = This->transactionSig;
4789

4790
  if (refresh)
4791
  {
4792 4793 4794
    ULARGE_INTEGER offset;
    ULONG bytes_read;
    BYTE data[4];
4795

4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814
    offset.u.HighPart = 0;
    offset.u.LowPart = OFFSET_TRANSACTIONSIG;
    hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);

    if (SUCCEEDED(hr))
    {
      StorageUtl_ReadDWord(data, 0, &This->transactionSig);

      if (oldTransactionSig != This->transactionSig)
      {
        /* Someone else wrote to this, so toss all cached information. */
        TRACE("signature changed\n");

        hr = StorageImpl_Refresh(This, FALSE, FALSE);
      }

      if (FAILED(hr))
        This->transactionSig = oldTransactionSig;
    }
4815 4816
  }

4817
  *result = This->transactionSig;
4818 4819 4820 4821

  return hr;
}

4822 4823
static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
  ULONG value)
4824
{
4825
  StorageImpl *This = (StorageImpl*)base;
4826

4827 4828
  This->transactionSig = value;
  StorageImpl_SaveFileHeader(This);
4829

4830 4831
  return S_OK;
}
4832

4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854
static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset,
    ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
{
    if ((dwLockType & This->locks_supported) == 0)
    {
        if (supported) *supported = FALSE;
        return S_OK;
    }

    if (supported) *supported = TRUE;
    return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
}

static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset,
    ULARGE_INTEGER cb, DWORD dwLockType)
{
    if ((dwLockType & This->locks_supported) == 0)
        return S_OK;

    return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
}

4855 4856
/* Internal function */
static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
4857
    ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4858 4859 4860 4861 4862 4863
{
    HRESULT hr;
    int delay = 0;
    DWORD start_time = GetTickCount();
    DWORD last_sanity_check = start_time;
    ULARGE_INTEGER sanity_offset, sanity_cb;
4864

4865 4866
    sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
    sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
4867

4868 4869
    do
    {
4870
        hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4871

4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889
        if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
        {
            DWORD current_time = GetTickCount();
            if (current_time - start_time >= 20000)
            {
                /* timeout */
                break;
            }
            if (current_time - last_sanity_check >= 500)
            {
                /* Any storage implementation with the file open in a
                 * shared mode should not lock these bytes for writing. However,
                 * some programs (LibreOffice Writer) will keep ALL bytes locked
                 * when opening in exclusive mode. We can use a read lock to
                 * detect this case early, and not hang a full 20 seconds.
                 *
                 * This can collide with another attempt to open the file in
                 * exclusive mode, but it's unlikely, and someone would fail anyway. */
4890
                hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4891 4892 4893 4894
                if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
                    break;
                if (SUCCEEDED(hr))
                {
4895
                    StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4896 4897
                    hr = STG_E_ACCESSDENIED;
                }
4898

4899 4900 4901 4902 4903 4904
                last_sanity_check = current_time;
            }
            Sleep(delay);
            if (delay < 150) delay++;
        }
    } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4905

4906
    return hr;
4907 4908
}

4909
static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
4910
{
4911 4912 4913
  StorageImpl *This = (StorageImpl*)base;
  HRESULT hr;
  ULARGE_INTEGER offset, cb;
4914

4915
  if (write)
4916
  {
4917 4918 4919 4920 4921 4922 4923 4924 4925
    /* Synchronous grab of second priority range, the commit lock, and the
     * lock-checking lock. */
    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
  }
  else
  {
    offset.QuadPart = RANGELOCK_COMMIT;
    cb.QuadPart = 1;
4926 4927
  }

4928
  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4929

4930
  return hr;
4931 4932
}

4933
static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
4934
{
4935 4936 4937
  StorageImpl *This = (StorageImpl*)base;
  HRESULT hr;
  ULARGE_INTEGER offset, cb;
4938

4939
  if (write)
4940
  {
4941 4942 4943 4944 4945 4946 4947 4948
    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
  }
  else
  {
    offset.QuadPart = RANGELOCK_COMMIT;
    cb.QuadPart = 1;
  }
4949

4950
  hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4951

4952
  return hr;
4953 4954
}

4955
static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4956
{
4957 4958 4959
  StorageImpl *This = (StorageImpl*) iface;
  STATSTG statstg;
  HRESULT hr;
4960

4961
  hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
4962

4963
  *result = statstg.pwcsName;
4964

4965 4966
  return hr;
}
4967

4968 4969 4970 4971 4972
static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
    ULONG end, HRESULT fail_hr)
{
    HRESULT hr;
    ULARGE_INTEGER offset, cb;
4973

4974 4975
    offset.QuadPart = start;
    cb.QuadPart = 1 + end - start;
4976

4977 4978
    hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
    if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
4979

4980
    if (FAILED(hr))
4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996
        return fail_hr;
    else
        return S_OK;
}

static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
{
    HRESULT hr=S_OK;
    int i, j;
    ULARGE_INTEGER offset, cb;

    cb.QuadPart = 1;

    for (i=start; i<=end; i++)
    {
        offset.QuadPart = i;
4997
        hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
4998 4999
        if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
            break;
5000
    }
5001 5002

    if (SUCCEEDED(hr))
5003
    {
5004 5005 5006 5007 5008 5009 5010 5011
        for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
        {
            if (This->locked_bytes[j] == 0)
            {
                This->locked_bytes[j] = i;
                break;
            }
        }
5012
    }
5013

5014
    return hr;
5015 5016
}

5017
static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
5018
{
5019 5020 5021 5022
    HRESULT hr;
    ULARGE_INTEGER offset;
    ULARGE_INTEGER cb;
    DWORD share_mode = STGM_SHARE_MODE(openFlags);
5023
    BOOL supported;
5024

5025
    if (openFlags & STGM_NOSNAPSHOT)
5026
    {
5027 5028 5029
        /* STGM_NOSNAPSHOT implies deny write */
        if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
        else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5030 5031
    }

5032 5033 5034
    /* Wrap all other locking inside a single lock so we can check ranges safely */
    offset.QuadPart = RANGELOCK_CHECKLOCKS;
    cb.QuadPart = 1;
5035
    hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5036

5037
    /* If the ILockBytes doesn't support locking that's ok. */
5038
    if (!supported) return S_OK;
5039
    else if (FAILED(hr)) return hr;
5040

5041
    hr = S_OK;
5042

5043 5044 5045
    /* First check for any conflicting locks. */
    if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
        hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
5046

5047 5048
    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
5049

5050 5051
    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
5052

5053 5054 5055 5056 5057 5058
    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
        hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);

    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
        hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);

5059 5060 5061 5062 5063 5064 5065 5066
    if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
    {
        hr = StorageImpl_CheckLockRange(This, 0, RANGELOCK_CHECKLOCKS-1, STG_E_LOCKVIOLATION);

        if (SUCCEEDED(hr))
            hr = StorageImpl_CheckLockRange(This, RANGELOCK_CHECKLOCKS+1, RANGELOCK_LAST, STG_E_LOCKVIOLATION);
    }

5067 5068
    /* Then grab our locks. */
    if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5069
    {
5070 5071 5072
        hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
        if (SUCCEEDED(hr))
            hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
5073 5074
    }

5075 5076
    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
        hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
5077

5078 5079
    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
        hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
5080

5081 5082
    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
5083

5084 5085
    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
5086

5087 5088
    if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
        hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
5089

5090 5091
    offset.QuadPart = RANGELOCK_CHECKLOCKS;
    cb.QuadPart = 1;
5092
    StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5093

5094
    return hr;
5095 5096
}

5097
static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
5098
{
5099 5100 5101 5102
  StorageImpl *This = (StorageImpl*)storage;
  int i;
  HRESULT hr;
  TRACE("(%p)\n", This);
5103

5104
  hr = BlockChainStream_Flush(This->smallBlockRootChain);
5105

5106 5107
  if (SUCCEEDED(hr))
    hr = BlockChainStream_Flush(This->rootBlockChain);
5108

5109 5110
  if (SUCCEEDED(hr))
    hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5111

5112 5113 5114
  for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
    if (This->blockChainCache[i])
      hr = BlockChainStream_Flush(This->blockChainCache[i]);
5115

5116 5117
  if (SUCCEEDED(hr))
    hr = ILockBytes_Flush(This->lockBytes);
5118

5119 5120
  return hr;
}
5121

5122 5123 5124
static void StorageImpl_Invalidate(StorageBaseImpl* iface)
{
  StorageImpl *This = (StorageImpl*) iface;
5125

5126
  StorageBaseImpl_DeleteAll(&This->base);
5127

5128
  This->base.reverted = TRUE;
5129 5130
}

5131
static void StorageImpl_Destroy(StorageBaseImpl* iface)
5132
{
5133 5134 5135
  StorageImpl *This = (StorageImpl*) iface;
  int i;
  TRACE("(%p)\n", This);
5136

5137
  StorageImpl_Flush(iface);
5138

5139
  StorageImpl_Invalidate(iface);
5140

5141
  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5142

5143 5144 5145
  BlockChainStream_Destroy(This->smallBlockRootChain);
  BlockChainStream_Destroy(This->rootBlockChain);
  BlockChainStream_Destroy(This->smallBlockDepotChain);
5146

5147 5148 5149 5150
  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
    BlockChainStream_Destroy(This->blockChainCache[i]);

  for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
5151
  {
5152 5153 5154
    ULARGE_INTEGER offset, cb;
    cb.QuadPart = 1;
    if (This->locked_bytes[i] != 0)
5155
    {
5156
      offset.QuadPart = This->locked_bytes[i];
5157
      StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5158 5159
    }
  }
5160

5161 5162 5163 5164
  if (This->lockBytes)
    ILockBytes_Release(This->lockBytes);
  HeapFree(GetProcessHeap(), 0, This);
}
5165 5166


5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185
static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
{
  StorageImpl_Destroy,
  StorageImpl_Invalidate,
  StorageImpl_Flush,
  StorageImpl_GetFilename,
  StorageImpl_CreateDirEntry,
  StorageImpl_BaseWriteDirEntry,
  StorageImpl_BaseReadDirEntry,
  StorageImpl_DestroyDirEntry,
  StorageImpl_StreamReadAt,
  StorageImpl_StreamWriteAt,
  StorageImpl_StreamSetSize,
  StorageImpl_StreamLink,
  StorageImpl_GetTransactionSig,
  StorageImpl_SetTransactionSig,
  StorageImpl_LockTransaction,
  StorageImpl_UnlockTransaction
};
5186 5187


5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211
/*
 * Virtual function table for the IStorageBaseImpl class.
 */
static const IStorageVtbl StorageImpl_Vtbl =
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
    StorageBaseImpl_CreateStorage,
    StorageBaseImpl_OpenStorage,
    StorageBaseImpl_CopyTo,
    StorageBaseImpl_MoveElementTo,
    StorageBaseImpl_Commit,
    StorageBaseImpl_Revert,
    StorageBaseImpl_EnumElements,
    StorageBaseImpl_DestroyElement,
    StorageBaseImpl_RenameElement,
    StorageBaseImpl_SetElementTimes,
    StorageBaseImpl_SetClass,
    StorageBaseImpl_SetStateBits,
    StorageBaseImpl_Stat
};
5212

5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223
static HRESULT StorageImpl_Construct(
  HANDLE       hFile,
  LPCOLESTR    pwcsName,
  ILockBytes*  pLkbyt,
  DWORD        openFlags,
  BOOL         fileBased,
  BOOL         create,
  ULONG        sector_size,
  StorageImpl** result)
{
  StorageImpl* This;
5224 5225
  HRESULT hr = S_OK;
  STATSTG stat;
5226

5227 5228
  if ( FAILED( validateSTGM(openFlags) ))
    return STG_E_INVALIDFLAG;
5229

5230 5231 5232
  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
  if (!This)
    return E_OUTOFMEMORY;
5233

5234
  memset(This, 0, sizeof(StorageImpl));
5235

5236
  list_init(&This->base.strmHead);
5237

5238
  list_init(&This->base.storageHead);
5239

5240 5241 5242 5243 5244 5245 5246
  This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
  This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
  This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
  This->base.baseVtbl = &StorageImpl_BaseVtbl;
  This->base.openFlags = (openFlags & ~STGM_CREATE);
  This->base.ref = 1;
  This->base.create = create;
5247

5248 5249 5250 5251 5252 5253
  if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
    This->base.lockingrole = SWMR_Writer;
  else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
    This->base.lockingrole = SWMR_Reader;
  else
    This->base.lockingrole = SWMR_None;
5254

5255
  This->base.reverted = FALSE;
5256

5257 5258 5259 5260 5261 5262 5263 5264
  /*
   * Initialize the big block cache.
   */
  This->bigBlockSize   = sector_size;
  This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
  if (hFile)
    hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
  else
5265
  {
5266 5267 5268
    This->lockBytes = pLkbyt;
    ILockBytes_AddRef(pLkbyt);
  }
5269

5270
  if (SUCCEEDED(hr))
5271 5272 5273 5274 5275 5276 5277 5278 5279
    hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME);

  if (SUCCEEDED(hr))
  {
    This->locks_supported = stat.grfLocksSupported;
    if (!hFile)
        /* Don't try to use wine-internal locking flag with custom ILockBytes */
        This->locks_supported &= ~WINE_LOCK_READ;

5280
    hr = StorageImpl_GrabLocks(This, openFlags);
5281
  }
5282

5283 5284
  if (SUCCEEDED(hr))
    hr = StorageImpl_Refresh(This, TRUE, create);
5285

5286 5287 5288 5289 5290 5291 5292 5293 5294 5295
  if (FAILED(hr))
  {
    IStorage_Release(&This->base.IStorage_iface);
    *result = NULL;
  }
  else
  {
    StorageImpl_Flush(&This->base);
    *result = This;
  }
5296

5297 5298
  return hr;
}
5299

5300

5301 5302 5303
/************************************************************************
 * StorageInternalImpl implementation
 ***********************************************************************/
5304

5305 5306 5307
static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
{
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5308

5309 5310 5311
  if (!This->base.reverted)
  {
    TRACE("Storage invalidated (stg=%p)\n", This);
5312

5313
    This->base.reverted = TRUE;
5314

5315
    This->parentStorage = NULL;
5316

5317
    StorageBaseImpl_DeleteAll(&This->base);
5318

5319
    list_remove(&This->ParentListEntry);
5320
  }
5321 5322
}

5323
static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5324
{
5325
  StorageInternalImpl* This = (StorageInternalImpl*) iface;
5326

5327
  StorageInternalImpl_Invalidate(&This->base);
5328

5329
  HeapFree(GetProcessHeap(), 0, This);
5330 5331
}

5332
static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5333
{
5334
  StorageInternalImpl* This = (StorageInternalImpl*) iface;
5335

5336
  return StorageBaseImpl_Flush(This->parentStorage);
5337 5338
}

5339
static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5340
{
5341
  StorageInternalImpl* This = (StorageInternalImpl*) iface;
5342

5343
  return StorageBaseImpl_GetFilename(This->parentStorage, result);
5344 5345
}

5346 5347
static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
  const DirEntry *newData, DirRef *index)
5348
{
5349 5350 5351 5352
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_CreateDirEntry(This->parentStorage,
    newData, index);
5353 5354
}

5355 5356
static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
5357
{
5358
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5359

5360 5361
  return StorageBaseImpl_WriteDirEntry(This->parentStorage,
    index, data);
5362 5363
}

5364 5365
static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
5366
{
5367
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5368

5369 5370
  return StorageBaseImpl_ReadDirEntry(This->parentStorage,
    index, data);
5371 5372
}

5373 5374
static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
  DirRef index)
5375
{
5376
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5377

5378 5379 5380
  return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
    index);
}
5381

5382 5383 5384 5385
static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5386

5387 5388 5389
  return StorageBaseImpl_StreamReadAt(This->parentStorage,
    index, offset, size, buffer, bytesRead);
}
5390

5391 5392 5393 5394
static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5395

5396 5397 5398
  return StorageBaseImpl_StreamWriteAt(This->parentStorage,
    index, offset, size, buffer, bytesWritten);
}
5399

5400 5401 5402 5403
static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER newsize)
{
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5404

5405 5406
  return StorageBaseImpl_StreamSetSize(This->parentStorage,
    index, newsize);
5407 5408
}

5409 5410
static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
  DirRef dst, DirRef src)
5411
{
5412
  StorageInternalImpl* This = (StorageInternalImpl*) base;
5413

5414 5415 5416
  return StorageBaseImpl_StreamLink(This->parentStorage,
    dst, src);
}
5417

5418 5419 5420 5421 5422
static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
  ULONG* result, BOOL refresh)
{
  return E_NOTIMPL;
}
5423

5424 5425 5426 5427 5428
static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
  ULONG value)
{
  return E_NOTIMPL;
}
5429

5430 5431 5432
static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
{
  return E_NOTIMPL;
5433 5434
}

5435
static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5436
{
5437 5438
  return E_NOTIMPL;
}
5439

5440 5441 5442 5443 5444 5445 5446 5447
/******************************************************************************
**
** StorageInternalImpl_Commit
**
*/
static HRESULT WINAPI StorageInternalImpl_Commit(
  IStorage*            iface,
  DWORD                  grfCommitFlags)  /* [in] */
5448
{
5449 5450 5451
  StorageBaseImpl* This = impl_from_IStorage(iface);
  TRACE("(%p,%x)\n", iface, grfCommitFlags);
  return StorageBaseImpl_Flush(This);
5452 5453
}

5454 5455 5456 5457 5458 5459 5460
/******************************************************************************
**
** StorageInternalImpl_Revert
**
*/
static HRESULT WINAPI StorageInternalImpl_Revert(
  IStorage*            iface)
5461
{
5462
  FIXME("(%p): stub\n", iface);
5463 5464 5465
  return S_OK;
}

5466 5467 5468 5469
/*
 * Virtual function table for the StorageInternalImpl class.
 */
static const IStorageVtbl StorageInternalImpl_Vtbl =
5470 5471 5472 5473 5474 5475 5476 5477 5478 5479
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
    StorageBaseImpl_CreateStorage,
    StorageBaseImpl_OpenStorage,
    StorageBaseImpl_CopyTo,
    StorageBaseImpl_MoveElementTo,
5480 5481
    StorageInternalImpl_Commit,
    StorageInternalImpl_Revert,
5482 5483 5484 5485 5486 5487 5488 5489 5490
    StorageBaseImpl_EnumElements,
    StorageBaseImpl_DestroyElement,
    StorageBaseImpl_RenameElement,
    StorageBaseImpl_SetElementTimes,
    StorageBaseImpl_SetClass,
    StorageBaseImpl_SetStateBits,
    StorageBaseImpl_Stat
};

5491
static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5492
{
5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508
  StorageInternalImpl_Destroy,
  StorageInternalImpl_Invalidate,
  StorageInternalImpl_Flush,
  StorageInternalImpl_GetFilename,
  StorageInternalImpl_CreateDirEntry,
  StorageInternalImpl_WriteDirEntry,
  StorageInternalImpl_ReadDirEntry,
  StorageInternalImpl_DestroyDirEntry,
  StorageInternalImpl_StreamReadAt,
  StorageInternalImpl_StreamWriteAt,
  StorageInternalImpl_StreamSetSize,
  StorageInternalImpl_StreamLink,
  StorageInternalImpl_GetTransactionSig,
  StorageInternalImpl_SetTransactionSig,
  StorageInternalImpl_LockTransaction,
  StorageInternalImpl_UnlockTransaction
5509 5510
};

5511 5512 5513 5514
static StorageInternalImpl* StorageInternalImpl_Construct(
  StorageBaseImpl* parentStorage,
  DWORD        openFlags,
  DirRef       storageDirEntry)
5515
{
5516
  StorageInternalImpl* newStorage;
5517

5518 5519 5520
  newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));

  if (newStorage!=0)
5521
  {
5522
    list_init(&newStorage->base.strmHead);
5523

5524
    list_init(&newStorage->base.storageHead);
5525

5526 5527 5528 5529 5530 5531 5532
    /*
     * Initialize the virtual function table.
     */
    newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
    newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
    newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
    newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5533

5534
    newStorage->base.reverted = FALSE;
5535

5536
    newStorage->base.ref = 1;
5537

5538
    newStorage->parentStorage = parentStorage;
5539

5540 5541 5542 5543
    /*
     * Keep a reference to the directory entry of this storage
     */
    newStorage->base.storageDirEntry = storageDirEntry;
5544

5545
    newStorage->base.create = FALSE;
5546

5547 5548
    return newStorage;
  }
5549

5550 5551
  return 0;
}
5552

5553

5554 5555 5556
/************************************************************************
 * TransactedSnapshotImpl implementation
 ***********************************************************************/
5557

5558 5559 5560
static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
{
  DirRef result=This->firstFreeEntry;
5561

5562 5563
  while (result < This->entries_size && This->entries[result].inuse)
    result++;
5564

5565 5566 5567 5568
  if (result == This->entries_size)
  {
    ULONG new_size = This->entries_size * 2;
    TransactedDirEntry *new_entries;
5569

5570 5571
    new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
    if (!new_entries) return DIRENTRY_NULL;
5572

5573 5574 5575 5576 5577
    memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
    HeapFree(GetProcessHeap(), 0, This->entries);

    This->entries = new_entries;
    This->entries_size = new_size;
5578 5579
  }

5580
  This->entries[result].inuse = TRUE;
5581

5582
  This->firstFreeEntry = result+1;
5583

5584
  return result;
5585 5586
}

5587 5588
static DirRef TransactedSnapshotImpl_CreateStubEntry(
  TransactedSnapshotImpl *This, DirRef parentEntryRef)
5589
{
5590 5591
  DirRef stubEntryRef;
  TransactedDirEntry *entry;
5592

5593
  stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
5594

5595 5596 5597
  if (stubEntryRef != DIRENTRY_NULL)
  {
    entry = &This->entries[stubEntryRef];
5598

5599
    entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
5600

5601 5602 5603 5604
    entry->read = FALSE;
  }

  return stubEntryRef;
5605 5606
}

5607 5608
static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
  TransactedSnapshotImpl *This, DirRef entry)
5609
{
5610 5611
  HRESULT hr=S_OK;
  DirEntry data;
5612

5613 5614 5615 5616 5617
  if (!This->entries[entry].read)
  {
    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
        This->entries[entry].transactedParentEntry,
        &data);
5618

5619 5620 5621
    if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
    {
      data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
5622

5623 5624 5625
      if (data.leftChild == DIRENTRY_NULL)
        hr = E_OUTOFMEMORY;
    }
5626

5627 5628 5629
    if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
    {
      data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
5630

5631 5632 5633
      if (data.rightChild == DIRENTRY_NULL)
        hr = E_OUTOFMEMORY;
    }
5634

5635 5636 5637
    if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
    {
      data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
5638

5639 5640 5641
      if (data.dirRootEntry == DIRENTRY_NULL)
        hr = E_OUTOFMEMORY;
    }
5642

5643 5644 5645 5646 5647 5648
    if (SUCCEEDED(hr))
    {
      memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
      This->entries[entry].read = TRUE;
    }
  }
5649

5650
  return hr;
5651 5652
}

5653 5654
static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
  TransactedSnapshotImpl *This, DirRef entry)
5655
{
5656
  HRESULT hr = S_OK;
5657

5658 5659 5660
  if (!This->entries[entry].stream_dirty)
  {
    DirEntry new_entrydata;
5661

5662 5663 5664 5665 5666 5667 5668 5669
    memset(&new_entrydata, 0, sizeof(DirEntry));
    new_entrydata.name[0] = 'S';
    new_entrydata.sizeOfNameString = 1;
    new_entrydata.stgType = STGTY_STREAM;
    new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
    new_entrydata.leftChild = DIRENTRY_NULL;
    new_entrydata.rightChild = DIRENTRY_NULL;
    new_entrydata.dirRootEntry = DIRENTRY_NULL;
5670

5671 5672
    hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
      &This->entries[entry].stream_entry);
5673

5674 5675 5676 5677 5678
    if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
    {
      hr = StorageBaseImpl_CopyStream(
        This->scratch, This->entries[entry].stream_entry,
        This->transactedParent, This->entries[entry].transactedParentEntry);
5679

5680 5681 5682
      if (FAILED(hr))
        StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
    }
5683

5684 5685
    if (SUCCEEDED(hr))
      This->entries[entry].stream_dirty = TRUE;
5686

5687 5688 5689 5690 5691 5692
    if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
    {
      /* Since this entry is modified, and we aren't using its stream data, we
       * no longer care about the original entry. */
      DirRef delete_ref;
      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
5693

5694 5695
      if (delete_ref != DIRENTRY_NULL)
        This->entries[delete_ref].deleted = TRUE;
5696

5697 5698 5699 5700 5701
      This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }

  return hr;
5702 5703
}

5704 5705 5706
/* Find the first entry in a depth-first traversal. */
static DirRef TransactedSnapshotImpl_FindFirstChild(
  TransactedSnapshotImpl* This, DirRef parent)
5707
{
5708 5709
  DirRef cursor, prev;
  TransactedDirEntry *entry;
5710

5711 5712 5713 5714 5715
  cursor = parent;
  entry = &This->entries[cursor];
  while (entry->read)
  {
    if (entry->data.leftChild != DIRENTRY_NULL)
5716
    {
5717 5718 5719 5720
      prev = cursor;
      cursor = entry->data.leftChild;
      entry = &This->entries[cursor];
      entry->parent = prev;
5721
    }
5722
    else if (entry->data.rightChild != DIRENTRY_NULL)
5723
    {
5724 5725 5726 5727
      prev = cursor;
      cursor = entry->data.rightChild;
      entry = &This->entries[cursor];
      entry->parent = prev;
5728
    }
5729
    else if (entry->data.dirRootEntry != DIRENTRY_NULL)
5730
    {
5731 5732 5733 5734
      prev = cursor;
      cursor = entry->data.dirRootEntry;
      entry = &This->entries[cursor];
      entry->parent = prev;
5735
    }
5736 5737 5738
    else
      break;
  }
5739

5740 5741
  return cursor;
}
5742

5743 5744 5745 5746 5747 5748
/* Find the next entry in a depth-first traversal. */
static DirRef TransactedSnapshotImpl_FindNextChild(
  TransactedSnapshotImpl* This, DirRef current)
{
  DirRef parent;
  TransactedDirEntry *parent_entry;
5749

5750 5751
  parent = This->entries[current].parent;
  parent_entry = &This->entries[parent];
5752

5753 5754 5755
  if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
  {
    if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
5756
    {
5757 5758 5759 5760 5761 5762 5763 5764
      This->entries[parent_entry->data.rightChild].parent = parent;
      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
    }

    if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
    {
      This->entries[parent_entry->data.dirRootEntry].parent = parent;
      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5765 5766 5767
    }
  }

5768
  return parent;
5769 5770
}

5771 5772 5773
/* Return TRUE if we've made a copy of this entry for committing to the parent. */
static inline BOOL TransactedSnapshotImpl_MadeCopy(
  TransactedSnapshotImpl* This, DirRef entry)
5774
{
5775 5776 5777
  return entry != DIRENTRY_NULL &&
    This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
}
5778

5779 5780 5781 5782 5783 5784 5785
/* Destroy the entries created by CopyTree. */
static void TransactedSnapshotImpl_DestroyTemporaryCopy(
  TransactedSnapshotImpl* This, DirRef stop)
{
  DirRef cursor;
  TransactedDirEntry *entry;
  ULARGE_INTEGER zero;
5786

5787
  zero.QuadPart = 0;
5788

5789 5790
  if (!This->entries[This->base.storageDirEntry].read)
    return;
5791

5792
  cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5793

5794 5795
  if (cursor == DIRENTRY_NULL)
    return;
5796

5797
  cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5798

5799
  while (cursor != DIRENTRY_NULL && cursor != stop)
5800
  {
5801 5802 5803
    if (TransactedSnapshotImpl_MadeCopy(This, cursor))
    {
      entry = &This->entries[cursor];
5804

5805 5806 5807
      if (entry->stream_dirty)
        StorageBaseImpl_StreamSetSize(This->transactedParent,
          entry->newTransactedParentEntry, zero);
5808

5809 5810
      StorageBaseImpl_DestroyDirEntry(This->transactedParent,
        entry->newTransactedParentEntry);
5811

5812 5813
      entry->newTransactedParentEntry = entry->transactedParentEntry;
    }
5814

5815 5816 5817
    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
  }
}
5818

5819 5820 5821 5822 5823 5824
/* Make a copy of our edited tree that we can use in the parent. */
static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
{
  DirRef cursor;
  TransactedDirEntry *entry;
  HRESULT hr = S_OK;
5825

5826 5827 5828 5829
  cursor = This->base.storageDirEntry;
  entry = &This->entries[cursor];
  entry->parent = DIRENTRY_NULL;
  entry->newTransactedParentEntry = entry->transactedParentEntry;
5830

5831 5832
  if (entry->data.dirRootEntry == DIRENTRY_NULL)
    return S_OK;
5833

5834
  This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5835

5836 5837
  cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
  entry = &This->entries[cursor];
5838

5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850
  while (cursor != DIRENTRY_NULL)
  {
    /* Make a copy of this entry in the transacted parent. */
    if (!entry->read ||
        (!entry->dirty && !entry->stream_dirty &&
         !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
         !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
         !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
      entry->newTransactedParentEntry = entry->transactedParentEntry;
    else
    {
      DirEntry newData;
5851

5852
      memcpy(&newData, &entry->data, sizeof(DirEntry));
5853

5854 5855
      newData.size.QuadPart = 0;
      newData.startingBlock = BLOCK_END_OF_CHAIN;
5856

5857 5858
      if (newData.leftChild != DIRENTRY_NULL)
        newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5859

5860 5861
      if (newData.rightChild != DIRENTRY_NULL)
        newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5862

5863 5864 5865 5866 5867 5868 5869 5870 5871
      if (newData.dirRootEntry != DIRENTRY_NULL)
        newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;

      hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
        &entry->newTransactedParentEntry);
      if (FAILED(hr))
      {
        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
        return hr;
5872 5873
      }

5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885
      if (entry->stream_dirty)
      {
        hr = StorageBaseImpl_CopyStream(
          This->transactedParent, entry->newTransactedParentEntry,
          This->scratch, entry->stream_entry);
      }
      else if (entry->data.size.QuadPart)
      {
        hr = StorageBaseImpl_StreamLink(
          This->transactedParent, entry->newTransactedParentEntry,
          entry->transactedParentEntry);
      }
5886

5887 5888 5889 5890 5891 5892 5893
      if (FAILED(hr))
      {
        cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
        return hr;
      }
    }
5894

5895 5896
    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
    entry = &This->entries[cursor];
5897
  }
5898 5899

  return hr;
5900 5901
}

5902 5903 5904
static HRESULT WINAPI TransactedSnapshotImpl_Commit(
  IStorage*            iface,
  DWORD                  grfCommitFlags)  /* [in] */
5905
{
5906 5907 5908 5909 5910 5911 5912
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
  TransactedDirEntry *root_entry;
  DirRef i, dir_root_ref;
  DirEntry data;
  ULARGE_INTEGER zero;
  HRESULT hr;
  ULONG transactionSig;
5913

5914
  zero.QuadPart = 0;
5915

5916 5917 5918 5919 5920 5921 5922 5923 5924
  TRACE("(%p,%x)\n", iface, grfCommitFlags);

  /* Cannot commit a read-only transacted storage */
  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
    return STG_E_ACCESSDENIED;

  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
  if (hr == E_NOTIMPL) hr = S_OK;
  if (SUCCEEDED(hr))
5925
  {
5926 5927 5928 5929 5930 5931 5932 5933
    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
    if (SUCCEEDED(hr))
    {
      if (transactionSig != This->lastTransactionSig)
      {
        ERR("file was externally modified\n");
        hr = STG_E_NOTCURRENT;
      }
5934

5935 5936 5937 5938 5939 5940 5941 5942
      if (SUCCEEDED(hr))
      {
        This->lastTransactionSig = transactionSig+1;
        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
      }
    }
    else if (hr == E_NOTIMPL)
      hr = S_OK;
5943

5944
    if (FAILED(hr)) goto end;
5945

5946 5947 5948 5949 5950
    /* To prevent data loss, we create the new structure in the file before we
     * delete the old one, so that in case of errors the old data is intact. We
     * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
     * needed in the rare situation where we have just enough free disk space to
     * overwrite the existing data. */
5951

5952 5953 5954 5955 5956 5957 5958 5959 5960 5961
    root_entry = &This->entries[This->base.storageDirEntry];

    if (!root_entry->read)
      goto end;

    hr = TransactedSnapshotImpl_CopyTree(This);
    if (FAILED(hr)) goto end;

    if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
      dir_root_ref = DIRENTRY_NULL;
5962
    else
5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025
      dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;

    hr = StorageBaseImpl_Flush(This->transactedParent);

    /* Update the storage to use the new data in one step. */
    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
        root_entry->transactedParentEntry, &data);

    if (SUCCEEDED(hr))
    {
      data.dirRootEntry = dir_root_ref;
      data.clsid = root_entry->data.clsid;
      data.ctime = root_entry->data.ctime;
      data.mtime = root_entry->data.mtime;

      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
        root_entry->transactedParentEntry, &data);
    }

    /* Try to flush after updating the root storage, but if the flush fails, keep
     * going, on the theory that it'll either succeed later or the subsequent
     * writes will fail. */
    StorageBaseImpl_Flush(This->transactedParent);

    if (SUCCEEDED(hr))
    {
      /* Destroy the old now-orphaned data. */
      for (i=0; i<This->entries_size; i++)
      {
        TransactedDirEntry *entry = &This->entries[i];
        if (entry->inuse)
        {
          if (entry->deleted)
          {
            StorageBaseImpl_StreamSetSize(This->transactedParent,
              entry->transactedParentEntry, zero);
            StorageBaseImpl_DestroyDirEntry(This->transactedParent,
              entry->transactedParentEntry);
            memset(entry, 0, sizeof(TransactedDirEntry));
            This->firstFreeEntry = min(i, This->firstFreeEntry);
          }
          else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
          {
            if (entry->transactedParentEntry != DIRENTRY_NULL)
              StorageBaseImpl_DestroyDirEntry(This->transactedParent,
                entry->transactedParentEntry);
            if (entry->stream_dirty)
            {
              StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
              StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
              entry->stream_dirty = FALSE;
            }
            entry->dirty = FALSE;
            entry->transactedParentEntry = entry->newTransactedParentEntry;
          }
        }
      }
    }
    else
    {
      TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
    }
6026

6027 6028
    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_Flush(This->transactedParent);
6029
end:
6030 6031 6032
    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
  }

6033 6034 6035
  return hr;
}

6036 6037
static HRESULT WINAPI TransactedSnapshotImpl_Revert(
  IStorage*            iface)
6038
{
6039 6040 6041
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
  ULARGE_INTEGER zero;
  ULONG i;
6042

6043 6044 6045 6046 6047 6048 6049 6050
  TRACE("(%p)\n", iface);

  /* Destroy the open objects. */
  StorageBaseImpl_DeleteAll(&This->base);

  /* Clear out the scratch file. */
  zero.QuadPart = 0;
  for (i=0; i<This->entries_size; i++)
6051
  {
6052 6053 6054 6055
    if (This->entries[i].stream_dirty)
    {
      StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
        zero);
6056

6057 6058 6059
      StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
    }
  }
6060

6061
  memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
6062

6063 6064
  This->firstFreeEntry = 0;
  This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
6065

6066
  return S_OK;
6067 6068
}

6069
static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
6070
{
6071 6072 6073
  if (!This->reverted)
  {
    TRACE("Storage invalidated (stg=%p)\n", This);
6074

6075
    This->reverted = TRUE;
6076

6077 6078
    StorageBaseImpl_DeleteAll(This);
  }
6079 6080
}

6081
static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
6082
{
6083
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6084

6085 6086 6087 6088 6089
  IStorage_Revert(&This->base.IStorage_iface);
  IStorage_Release(&This->transactedParent->IStorage_iface);
  IStorage_Release(&This->scratch->IStorage_iface);
  HeapFree(GetProcessHeap(), 0, This->entries);
  HeapFree(GetProcessHeap(), 0, This);
6090 6091
}

6092
static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
6093
{
6094 6095
  /* We only need to flush when committing. */
  return S_OK;
6096 6097
}

6098
static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6099
{
6100
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
6101

6102
  return StorageBaseImpl_GetFilename(This->transactedParent, result);
6103 6104
}

6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130
static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
  const DirEntry *newData, DirRef *index)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  DirRef new_ref;
  TransactedDirEntry *new_entry;

  new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
  if (new_ref == DIRENTRY_NULL)
    return E_OUTOFMEMORY;

  new_entry = &This->entries[new_ref];

  new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
  new_entry->read = TRUE;
  new_entry->dirty = TRUE;
  memcpy(&new_entry->data, newData, sizeof(DirEntry));

  *index = new_ref;

  TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);

  return S_OK;
}

static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
6131 6132
  DirRef index, const DirEntry *data)
{
6133 6134
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
6135

6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162
  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);

  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
  if (FAILED(hr)) return hr;

  memcpy(&This->entries[index].data, data, sizeof(DirEntry));

  if (index != This->base.storageDirEntry)
  {
    This->entries[index].dirty = TRUE;

    if (data->size.QuadPart == 0 &&
        This->entries[index].transactedParentEntry != DIRENTRY_NULL)
    {
      /* Since this entry is modified, and we aren't using its stream data, we
       * no longer care about the original entry. */
      DirRef delete_ref;
      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);

      if (delete_ref != DIRENTRY_NULL)
        This->entries[delete_ref].deleted = TRUE;

      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }

  return S_OK;
6163 6164
}

6165
static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
6166 6167
  DirRef index, DirEntry *data)
{
6168 6169
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
6170

6171 6172 6173 6174 6175 6176 6177 6178
  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
  if (FAILED(hr)) return hr;

  memcpy(data, &This->entries[index].data, sizeof(DirEntry));

  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);

  return S_OK;
6179 6180
}

6181
static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
6182 6183
  DirRef index)
{
6184
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6185

6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200
  if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
      This->entries[index].data.size.QuadPart != 0)
  {
    /* If we deleted this entry while it has stream data. We must have left the
     * data because some other entry is using it, and we need to leave the
     * original entry alone. */
    memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
    This->firstFreeEntry = min(index, This->firstFreeEntry);
  }
  else
  {
    This->entries[index].deleted = TRUE;
  }

  return S_OK;
6201 6202
}

6203
static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
6204 6205
  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
6206
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
6207

6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224
  if (This->entries[index].stream_dirty)
  {
    return StorageBaseImpl_StreamReadAt(This->scratch,
        This->entries[index].stream_entry, offset, size, buffer, bytesRead);
  }
  else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
  {
    /* This stream doesn't live in the parent, and we haven't allocated storage
     * for it yet */
    *bytesRead = 0;
    return S_OK;
  }
  else
  {
    return StorageBaseImpl_StreamReadAt(This->transactedParent,
        This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
  }
6225 6226
}

6227
static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
6228 6229
  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
6230 6231
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
6232

6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247
  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
  if (FAILED(hr)) return hr;

  hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
  if (FAILED(hr)) return hr;

  hr = StorageBaseImpl_StreamWriteAt(This->scratch,
    This->entries[index].stream_entry, offset, size, buffer, bytesWritten);

  if (SUCCEEDED(hr) && size != 0)
    This->entries[index].data.size.QuadPart = max(
        This->entries[index].data.size.QuadPart,
        offset.QuadPart + size);

  return hr;
6248 6249
}

6250
static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
6251 6252
  DirRef index, ULARGE_INTEGER newsize)
{
6253 6254
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
6255

6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298
  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
  if (FAILED(hr)) return hr;

  if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
    return S_OK;

  if (newsize.QuadPart == 0)
  {
    /* Destroy any parent references or entries in the scratch file. */
    if (This->entries[index].stream_dirty)
    {
      ULARGE_INTEGER zero;
      zero.QuadPart = 0;
      StorageBaseImpl_StreamSetSize(This->scratch,
        This->entries[index].stream_entry, zero);
      StorageBaseImpl_DestroyDirEntry(This->scratch,
        This->entries[index].stream_entry);
      This->entries[index].stream_dirty = FALSE;
    }
    else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
    {
      DirRef delete_ref;
      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);

      if (delete_ref != DIRENTRY_NULL)
        This->entries[delete_ref].deleted = TRUE;

      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }
  else
  {
    hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
    if (FAILED(hr)) return hr;

    hr = StorageBaseImpl_StreamSetSize(This->scratch,
      This->entries[index].stream_entry, newsize);
  }

  if (SUCCEEDED(hr))
    This->entries[index].data.size = newsize;

  return hr;
6299 6300
}

6301
static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
6302 6303
  DirRef dst, DirRef src)
{
6304 6305 6306
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
  TransactedDirEntry *dst_entry, *src_entry;
6307

6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323
  hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
  if (FAILED(hr)) return hr;

  hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
  if (FAILED(hr)) return hr;

  dst_entry = &This->entries[dst];
  src_entry = &This->entries[src];

  dst_entry->stream_dirty = src_entry->stream_dirty;
  dst_entry->stream_entry = src_entry->stream_entry;
  dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
  dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
  dst_entry->data.size = src_entry->data.size;

  return S_OK;
6324 6325
}

6326
static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
6327 6328 6329 6330 6331
  ULONG* result, BOOL refresh)
{
  return E_NOTIMPL;
}

6332
static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
6333 6334 6335 6336 6337
  ULONG value)
{
  return E_NOTIMPL;
}

6338
static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6339 6340 6341 6342
{
  return E_NOTIMPL;
}

6343
static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6344 6345 6346 6347
{
  return E_NOTIMPL;
}

6348
static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
6349
{
6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
    StorageBaseImpl_CreateStorage,
    StorageBaseImpl_OpenStorage,
    StorageBaseImpl_CopyTo,
    StorageBaseImpl_MoveElementTo,
    TransactedSnapshotImpl_Commit,
    TransactedSnapshotImpl_Revert,
    StorageBaseImpl_EnumElements,
    StorageBaseImpl_DestroyElement,
    StorageBaseImpl_RenameElement,
    StorageBaseImpl_SetElementTimes,
    StorageBaseImpl_SetClass,
    StorageBaseImpl_SetStateBits,
    StorageBaseImpl_Stat
};
6369

6370
static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
6371
{
6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388
  TransactedSnapshotImpl_Destroy,
  TransactedSnapshotImpl_Invalidate,
  TransactedSnapshotImpl_Flush,
  TransactedSnapshotImpl_GetFilename,
  TransactedSnapshotImpl_CreateDirEntry,
  TransactedSnapshotImpl_WriteDirEntry,
  TransactedSnapshotImpl_ReadDirEntry,
  TransactedSnapshotImpl_DestroyDirEntry,
  TransactedSnapshotImpl_StreamReadAt,
  TransactedSnapshotImpl_StreamWriteAt,
  TransactedSnapshotImpl_StreamSetSize,
  TransactedSnapshotImpl_StreamLink,
  TransactedSnapshotImpl_GetTransactionSig,
  TransactedSnapshotImpl_SetTransactionSig,
  TransactedSnapshotImpl_LockTransaction,
  TransactedSnapshotImpl_UnlockTransaction
};
6389

6390 6391
static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
  TransactedSnapshotImpl** result)
6392
{
6393
  HRESULT hr;
6394

6395 6396
  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
  if (*result)
6397
  {
6398
    IStorage *scratch;
6399

6400
    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
6401

6402 6403 6404
    /* This is OK because the property set storage functions use the IStorage functions. */
    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
    (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
6405

6406
    list_init(&(*result)->base.strmHead);
6407

6408
    list_init(&(*result)->base.storageHead);
6409

6410
    (*result)->base.ref = 1;
6411

6412
    (*result)->base.openFlags = parentStorage->openFlags;
6413

6414 6415
    /* This cannot fail, except with E_NOTIMPL in which case we don't care */
    StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6416

6417 6418 6419 6420
    /* Create a new temporary storage to act as the scratch file. */
    hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
        0, &scratch);
    (*result)->scratch = impl_from_IStorage(scratch);
6421 6422 6423

    if (SUCCEEDED(hr))
    {
6424
        ULONG num_entries = 20;
6425

6426 6427 6428
        (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
        (*result)->entries_size = num_entries;
        (*result)->firstFreeEntry = 0;
6429

6430 6431 6432 6433
        if ((*result)->entries)
        {
            /* parentStorage already has 1 reference, which we take over here. */
            (*result)->transactedParent = parentStorage;
6434

6435
            parentStorage->transactedChild = &(*result)->base;
6436

6437 6438 6439 6440 6441
            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
        }
        else
        {
            IStorage_Release(scratch);
6442

6443 6444 6445
            hr = E_OUTOFMEMORY;
        }
    }
6446

6447
    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6448

6449 6450 6451 6452 6453
    return hr;
  }
  else
    return E_OUTOFMEMORY;
}
6454

6455

6456 6457 6458
/************************************************************************
 * TransactedSharedImpl implementation
 ***********************************************************************/
6459

6460 6461 6462
static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
{
  if (!This->reverted)
6463
  {
6464
    TRACE("Storage invalidated (stg=%p)\n", This);
6465

6466
    This->reverted = TRUE;
6467

6468
    StorageBaseImpl_DeleteAll(This);
6469
  }
6470
}
6471

6472 6473 6474
static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6475

6476 6477 6478 6479
  TransactedSharedImpl_Invalidate(&This->base);
  IStorage_Release(&This->transactedParent->IStorage_iface);
  IStorage_Release(&This->scratch->base.IStorage_iface);
  HeapFree(GetProcessHeap(), 0, This);
6480 6481
}

6482 6483 6484 6485 6486
static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
{
  /* We only need to flush when committing. */
  return S_OK;
}
6487

6488
static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6489
{
6490
  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
6491

6492 6493
  return StorageBaseImpl_GetFilename(This->transactedParent, result);
}
6494

6495 6496 6497 6498
static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
  const DirEntry *newData, DirRef *index)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6499

6500 6501 6502
  return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
    newData, index);
}
6503

6504 6505 6506 6507
static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6508

6509 6510 6511
  return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
    index, data);
}
6512

6513 6514 6515 6516
static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6517

6518 6519
  return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
    index, data);
6520
}
6521

6522 6523
static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
  DirRef index)
6524
{
6525
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6526

6527 6528 6529
  return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
    index);
}
6530

6531 6532 6533 6534
static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6535

6536 6537
  return StorageBaseImpl_StreamReadAt(&This->scratch->base,
    index, offset, size, buffer, bytesRead);
6538
}
6539

6540 6541
static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6542
{
6543
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6544

6545 6546 6547
  return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
    index, offset, size, buffer, bytesWritten);
}
6548

6549 6550 6551 6552
static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER newsize)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6553

6554 6555 6556
  return StorageBaseImpl_StreamSetSize(&This->scratch->base,
    index, newsize);
}
6557

6558 6559 6560 6561
static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
  DirRef dst, DirRef src)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
6562

6563 6564 6565
  return StorageBaseImpl_StreamLink(&This->scratch->base,
    dst, src);
}
6566

6567 6568 6569 6570
static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
  ULONG* result, BOOL refresh)
{
  return E_NOTIMPL;
6571 6572
}

6573 6574
static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
  ULONG value)
6575
{
6576 6577
  return E_NOTIMPL;
}
6578

6579 6580 6581 6582
static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
{
  return E_NOTIMPL;
}
6583

6584
static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6585
{
6586 6587
  return E_NOTIMPL;
}
6588

6589 6590 6591 6592 6593 6594 6595 6596 6597
static HRESULT WINAPI TransactedSharedImpl_Commit(
  IStorage*            iface,
  DWORD                  grfCommitFlags)  /* [in] */
{
  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
  DirRef new_storage_ref, prev_storage_ref;
  DirEntry src_data, dst_data;
  HRESULT hr;
  ULONG transactionSig;
6598

6599 6600 6601 6602 6603 6604 6605 6606 6607
  TRACE("(%p,%x)\n", iface, grfCommitFlags);

  /* Cannot commit a read-only transacted storage */
  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
    return STG_E_ACCESSDENIED;

  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
  if (hr == E_NOTIMPL) hr = S_OK;
  if (SUCCEEDED(hr))
6608
  {
6609 6610 6611 6612 6613
    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
    if (SUCCEEDED(hr))
    {
      if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
        hr = STG_E_NOTCURRENT;
6614

6615 6616 6617 6618 6619
      if (SUCCEEDED(hr))
        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
    }
    else if (hr == E_NOTIMPL)
      hr = S_OK;
6620

6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665
    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);

    /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);

    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_Flush(This->transactedParent);

    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);

    if (SUCCEEDED(hr))
    {
      prev_storage_ref = dst_data.dirRootEntry;
      dst_data.dirRootEntry = new_storage_ref;
      dst_data.clsid = src_data.clsid;
      dst_data.ctime = src_data.ctime;
      dst_data.mtime = src_data.mtime;
      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
    }

    if (SUCCEEDED(hr))
    {
      /* Try to flush after updating the root storage, but if the flush fails, keep
       * going, on the theory that it'll either succeed later or the subsequent
       * writes will fail. */
      StorageBaseImpl_Flush(This->transactedParent);

      hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
    }

    if (SUCCEEDED(hr))
      hr = StorageBaseImpl_Flush(This->transactedParent);

    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);

    if (SUCCEEDED(hr))
      hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);

    if (SUCCEEDED(hr))
    {
      This->lastTransactionSig = transactionSig+1;
    }
6666 6667
  }

6668
  return hr;
6669 6670
}

6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684
static HRESULT WINAPI TransactedSharedImpl_Revert(
  IStorage*            iface)
{
  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);

  TRACE("(%p)\n", iface);

  /* Destroy the open objects. */
  StorageBaseImpl_DeleteAll(&This->base);

  return IStorage_Revert(&This->scratch->base.IStorage_iface);
}

static const IStorageVtbl TransactedSharedImpl_Vtbl =
6685 6686 6687 6688 6689 6690
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
6691
    StorageBaseImpl_CreateStorage,
6692
    StorageBaseImpl_OpenStorage,
6693
    StorageBaseImpl_CopyTo,
6694
    StorageBaseImpl_MoveElementTo,
6695 6696
    TransactedSharedImpl_Commit,
    TransactedSharedImpl_Revert,
6697
    StorageBaseImpl_EnumElements,
6698
    StorageBaseImpl_DestroyElement,
6699
    StorageBaseImpl_RenameElement,
6700
    StorageBaseImpl_SetElementTimes,
6701
    StorageBaseImpl_SetClass,
6702
    StorageBaseImpl_SetStateBits,
6703 6704 6705
    StorageBaseImpl_Stat
};

6706
static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
6707
{
6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723
  TransactedSharedImpl_Destroy,
  TransactedSharedImpl_Invalidate,
  TransactedSharedImpl_Flush,
  TransactedSharedImpl_GetFilename,
  TransactedSharedImpl_CreateDirEntry,
  TransactedSharedImpl_WriteDirEntry,
  TransactedSharedImpl_ReadDirEntry,
  TransactedSharedImpl_DestroyDirEntry,
  TransactedSharedImpl_StreamReadAt,
  TransactedSharedImpl_StreamWriteAt,
  TransactedSharedImpl_StreamSetSize,
  TransactedSharedImpl_StreamLink,
  TransactedSharedImpl_GetTransactionSig,
  TransactedSharedImpl_SetTransactionSig,
  TransactedSharedImpl_LockTransaction,
  TransactedSharedImpl_UnlockTransaction
6724 6725
};

6726 6727 6728 6729
static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
  TransactedSharedImpl** result)
{
  HRESULT hr;
6730

6731 6732 6733 6734
  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
  if (*result)
  {
    IStorage *scratch;
6735

6736
    (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
6737

6738 6739 6740
    /* This is OK because the property set storage functions use the IStorage functions. */
    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
    (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
6741

6742
    list_init(&(*result)->base.strmHead);
6743

6744
    list_init(&(*result)->base.storageHead);
6745

6746
    (*result)->base.ref = 1;
6747

6748
    (*result)->base.openFlags = parentStorage->openFlags;
6749

6750
    hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
6751

6752 6753 6754
    if (SUCCEEDED(hr))
    {
      STGOPTIONS stgo;
6755

6756 6757
      /* This cannot fail, except with E_NOTIMPL in which case we don't care */
      StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
6758

6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805
      stgo.usVersion = 1;
      stgo.reserved = 0;
      stgo.ulSectorSize = 4096;
      stgo.pwcsTemplateFile = NULL;

      /* Create a new temporary storage to act as the scratch file. */
      hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
          STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
      (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);

      if (SUCCEEDED(hr))
      {
        hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
          parentStorage, parentStorage->storageDirEntry);

        if (SUCCEEDED(hr))
        {
          hr = IStorage_Commit(scratch, STGC_DEFAULT);

          (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
          (*result)->transactedParent = parentStorage;
        }

        if (FAILED(hr))
          IStorage_Release(scratch);
      }

      StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
    }

    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);

    return hr;
  }
  else
    return E_OUTOFMEMORY;
}

static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
  BOOL toplevel, StorageBaseImpl** result)
{
  static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;

  if (parentStorage->openFlags & fixme_flags)
  {
    fixme_flags &= ~parentStorage->openFlags;
    FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6806 6807
  }

6808 6809 6810 6811 6812 6813 6814 6815 6816 6817
  if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
  {
    /* Need to create a temp file for the snapshot */
    return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
  }

  return TransactedSnapshotImpl_Construct(parentStorage,
    (TransactedSnapshotImpl**)result);
6818 6819
}

6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855
static HRESULT Storage_Construct(
  HANDLE       hFile,
  LPCOLESTR    pwcsName,
  ILockBytes*  pLkbyt,
  DWORD        openFlags,
  BOOL         fileBased,
  BOOL         create,
  ULONG        sector_size,
  StorageBaseImpl** result)
{
  StorageImpl *newStorage;
  StorageBaseImpl *newTransactedStorage;
  HRESULT hr;

  hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
  if (FAILED(hr)) goto end;

  if (openFlags & STGM_TRANSACTED)
  {
    hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
    if (FAILED(hr))
      IStorage_Release(&newStorage->base.IStorage_iface);
    else
      *result = newTransactedStorage;
  }
  else
    *result = &newStorage->base;

end:
  return hr;
}


/************************************************************************
 * StorageUtl helper functions
 ***********************************************************************/
6856

6857
void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6858
{
6859 6860 6861
  WORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(WORD));
6862
  *value = lendian16toh(tmp);
6863 6864
}

6865
void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6866
{
6867
  value = htole16(value);
6868
  memcpy(buffer+offset, &value, sizeof(WORD));
6869 6870
}

6871
void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6872
{
6873 6874 6875
  DWORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(DWORD));
6876
  *value = lendian32toh(tmp);
6877 6878
}

6879
void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6880
{
6881
  value = htole32(value);
6882
  memcpy(buffer+offset, &value, sizeof(DWORD));
6883 6884
}

Juan Lang's avatar
Juan Lang committed
6885 6886 6887 6888 6889 6890 6891
void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
 ULARGE_INTEGER* value)
{
#ifdef WORDS_BIGENDIAN
    ULARGE_INTEGER tmp;

    memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6892 6893
    value->u.LowPart = htole32(tmp.u.HighPart);
    value->u.HighPart = htole32(tmp.u.LowPart);
Juan Lang's avatar
Juan Lang committed
6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904
#else
    memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
#endif
}

void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
 const ULARGE_INTEGER *value)
{
#ifdef WORDS_BIGENDIAN
    ULARGE_INTEGER tmp;

6905 6906
    tmp.u.LowPart = htole32(value->u.HighPart);
    tmp.u.HighPart = htole32(value->u.LowPart);
Juan Lang's avatar
Juan Lang committed
6907 6908 6909 6910 6911 6912
    memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
#else
    memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
#endif
}

6913
void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6914 6915 6916 6917 6918
{
  StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
  StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
  StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));

6919
  memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6920 6921
}

6922
void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6923 6924 6925 6926 6927
{
  StorageUtl_WriteDWord(buffer, offset,   value->Data1);
  StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
  StorageUtl_WriteWord(buffer,  offset+6, value->Data3);

6928
  memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6929 6930
}

6931
void StorageUtl_CopyDirEntryToSTATSTG(
6932
  StorageBaseImpl*      storage,
6933
  STATSTG*              destination,
6934
  const DirEntry*       source,
6935
  int                   statFlags)
6936 6937 6938 6939
{
  /*
   * The copy of the string occurs only when the flag is not set
   */
6940 6941 6942 6943 6944 6945 6946 6947
  if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
  {
    /* Use the filename for the root storage. */
    destination->pwcsName = 0;
    StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
  }
  else if( ((statFlags & STATFLAG_NONAME) != 0) ||
       (source->name[0] == 0) )
6948 6949 6950 6951 6952
  {
    destination->pwcsName = 0;
  }
  else
  {
6953
    destination->pwcsName =
6954
      CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6955

6956
    strcpyW(destination->pwcsName, source->name);
6957
  }
6958

6959
  switch (source->stgType)
6960
  {
6961 6962
    case STGTY_STORAGE:
    case STGTY_ROOT:
6963 6964
      destination->type = STGTY_STORAGE;
      break;
6965
    case STGTY_STREAM:
6966 6967 6968 6969
      destination->type = STGTY_STREAM;
      break;
    default:
      destination->type = STGTY_STREAM;
6970
      break;
6971 6972 6973
  }

  destination->cbSize            = source->size;
6974
/*
6975 6976
  currentReturnStruct->mtime     = {0}; TODO
  currentReturnStruct->ctime     = {0};
6977
  currentReturnStruct->atime     = {0};
6978 6979
*/
  destination->grfMode           = 0;
6980
  destination->grfLocksSupported = 0;
6981
  destination->clsid             = source->clsid;
6982 6983
  destination->grfStateBits      = 0;
  destination->reserved          = 0;
6984 6985
}

6986 6987 6988 6989 6990

/************************************************************************
 * BlockChainStream implementation
 ***********************************************************************/

6991
/******************************************************************************
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019
 *      BlockChainStream_GetHeadOfChain
 *
 * Returns the head of this stream chain.
 * Some special chains don't have directory entries, their heads are kept in
 * This->headOfStreamPlaceHolder.
 *
 */
static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
{
  DirEntry  chainEntry;
  HRESULT   hr;

  if (This->headOfStreamPlaceHolder != 0)
    return *(This->headOfStreamPlaceHolder);

  if (This->ownerDirEntry != DIRENTRY_NULL)
  {
    hr = StorageImpl_ReadDirEntry(
                      This->parentStorage,
                      This->ownerDirEntry,
                      &chainEntry);

    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
      return chainEntry.startingBlock;
  }

  return BLOCK_END_OF_CHAIN;
}
7020

7021
/* Read and save the index of all blocks in this stream. */
7022
static HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098
{
  ULONG  next_sector, next_offset;
  HRESULT hr;
  struct BlockChainRun *last_run;

  if (This->indexCacheLen == 0)
  {
    last_run = NULL;
    next_offset = 0;
    next_sector = BlockChainStream_GetHeadOfChain(This);
  }
  else
  {
    last_run = &This->indexCache[This->indexCacheLen-1];
    next_offset = last_run->lastOffset+1;
    hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
        last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
        &next_sector);
    if (FAILED(hr)) return hr;
  }

  while (next_sector != BLOCK_END_OF_CHAIN)
  {
    if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
    {
      /* Add the current block to the cache. */
      if (This->indexCacheSize == 0)
      {
        This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
        if (!This->indexCache) return E_OUTOFMEMORY;
        This->indexCacheSize = 16;
      }
      else if (This->indexCacheSize == This->indexCacheLen)
      {
        struct BlockChainRun *new_cache;
        ULONG new_size;

        new_size = This->indexCacheSize * 2;
        new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
        if (!new_cache) return E_OUTOFMEMORY;
        memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);

        HeapFree(GetProcessHeap(), 0, This->indexCache);
        This->indexCache = new_cache;
        This->indexCacheSize = new_size;
      }

      This->indexCacheLen++;
      last_run = &This->indexCache[This->indexCacheLen-1];
      last_run->firstSector = next_sector;
      last_run->firstOffset = next_offset;
    }

    last_run->lastOffset = next_offset;

    /* Find the next block. */
    next_offset++;
    hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
    if (FAILED(hr)) return hr;
  }

  if (This->indexCacheLen)
  {
    This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
    This->numBlocks = last_run->lastOffset+1;
  }
  else
  {
    This->tailIndex = BLOCK_END_OF_CHAIN;
    This->numBlocks = 0;
  }

  return S_OK;
}

/* Locate the nth block in this stream. */
7099
static ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127
{
  ULONG min_offset = 0, max_offset = This->numBlocks-1;
  ULONG min_run = 0, max_run = This->indexCacheLen-1;

  if (offset >= This->numBlocks)
    return BLOCK_END_OF_CHAIN;

  while (min_run < max_run)
  {
    ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
    if (offset < This->indexCache[run_to_check].firstOffset)
    {
      max_offset = This->indexCache[run_to_check].firstOffset-1;
      max_run = run_to_check-1;
    }
    else if (offset > This->indexCache[run_to_check].lastOffset)
    {
      min_offset = This->indexCache[run_to_check].lastOffset+1;
      min_run = run_to_check+1;
    }
    else
      /* Block is in this run. */
      min_run = max_run = run_to_check;
  }

  return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
}

7128
static HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162
    ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
{
  BlockChainBlock *result=NULL;
  int i;

  for (i=0; i<2; i++)
    if (This->cachedBlocks[i].index == index)
    {
      *sector = This->cachedBlocks[i].sector;
      *block = &This->cachedBlocks[i];
      return S_OK;
    }

  *sector = BlockChainStream_GetSectorOfOffset(This, index);
  if (*sector == BLOCK_END_OF_CHAIN)
    return STG_E_DOCFILECORRUPT;

  if (create)
  {
    if (This->cachedBlocks[0].index == 0xffffffff)
      result = &This->cachedBlocks[0];
    else if (This->cachedBlocks[1].index == 0xffffffff)
      result = &This->cachedBlocks[1];
    else
    {
      result = &This->cachedBlocks[This->blockToEvict++];
      if (This->blockToEvict == 2)
        This->blockToEvict = 0;
    }

    if (result->dirty)
    {
      if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
        return STG_E_WRITEFAULT;
7163
      result->dirty = FALSE;
7164 7165
    }

7166
    result->read = FALSE;
7167 7168 7169 7170 7171 7172 7173 7174
    result->index = index;
    result->sector = *sector;
  }

  *block = result;
  return S_OK;
}

7175
BlockChainStream* BlockChainStream_Construct(
7176
  StorageImpl* parentStorage,
7177
  ULONG*         headOfStreamPlaceHolder,
7178
  DirRef         dirEntry)
7179 7180 7181 7182 7183 7184 7185
{
  BlockChainStream* newStream;

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));

  newStream->parentStorage           = parentStorage;
  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7186
  newStream->ownerDirEntry           = dirEntry;
7187 7188 7189
  newStream->indexCache              = NULL;
  newStream->indexCacheLen           = 0;
  newStream->indexCacheSize          = 0;
7190
  newStream->cachedBlocks[0].index = 0xffffffff;
7191
  newStream->cachedBlocks[0].dirty = FALSE;
7192
  newStream->cachedBlocks[1].index = 0xffffffff;
7193
  newStream->cachedBlocks[1].dirty = FALSE;
7194
  newStream->blockToEvict          = 0;
Thuy Nguyen's avatar
Thuy Nguyen committed
7195

7196
  if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
Thuy Nguyen's avatar
Thuy Nguyen committed
7197
  {
7198 7199 7200
    HeapFree(GetProcessHeap(), 0, newStream->indexCache);
    HeapFree(GetProcessHeap(), 0, newStream);
    return NULL;
Thuy Nguyen's avatar
Thuy Nguyen committed
7201
  }
7202 7203 7204 7205

  return newStream;
}

7206 7207 7208 7209 7210 7211 7212 7213 7214
HRESULT BlockChainStream_Flush(BlockChainStream* This)
{
  int i;
  if (!This) return S_OK;
  for (i=0; i<2; i++)
  {
    if (This->cachedBlocks[i].dirty)
    {
      if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
7215
        This->cachedBlocks[i].dirty = FALSE;
7216 7217 7218 7219 7220 7221 7222
      else
        return STG_E_WRITEFAULT;
    }
  }
  return S_OK;
}

7223 7224
void BlockChainStream_Destroy(BlockChainStream* This)
{
7225
  if (This)
7226 7227
  {
    BlockChainStream_Flush(This);
7228
    HeapFree(GetProcessHeap(), 0, This->indexCache);
7229
  }
7230 7231 7232 7233
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
7234
 *      BlockChainStream_Shrink
7235
 *
7236
 * Shrinks this chain in the big block depot.
7237
 */
7238 7239
static BOOL BlockChainStream_Shrink(BlockChainStream* This,
                                    ULARGE_INTEGER    newSize)
7240 7241
{
  ULONG blockIndex;
7242 7243
  ULONG numBlocks;
  int i;
7244 7245

  /*
7246
   * Figure out how many blocks are needed to contain the new size
7247
   */
7248
  numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7249

7250 7251
  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
    numBlocks++;
7252

7253
  if (numBlocks)
7254
  {
7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266
    /*
     * Go to the new end of chain
     */
    blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);

    /* Mark the new end of chain */
    StorageImpl_SetNextBlockInChain(
      This->parentStorage,
      blockIndex,
      BLOCK_END_OF_CHAIN);

    This->tailIndex = blockIndex;
7267
  }
7268 7269 7270 7271 7272 7273 7274 7275 7276 7277
  else
  {
    if (This->headOfStreamPlaceHolder != 0)
    {
      *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
    }
    else
    {
      DirEntry chainEntry;
      assert(This->ownerDirEntry != DIRENTRY_NULL);
7278

7279 7280 7281 7282
      StorageImpl_ReadDirEntry(
        This->parentStorage,
        This->ownerDirEntry,
        &chainEntry);
7283

7284 7285 7286 7287 7288 7289 7290 7291 7292 7293
      chainEntry.startingBlock = BLOCK_END_OF_CHAIN;

      StorageImpl_WriteDirEntry(
        This->parentStorage,
        This->ownerDirEntry,
        &chainEntry);
    }

    This->tailIndex = BLOCK_END_OF_CHAIN;
  }
7294

Thuy Nguyen's avatar
Thuy Nguyen committed
7295 7296
  This->numBlocks = numBlocks;

7297 7298 7299
  /*
   * Mark the extra blocks as free
   */
7300
  while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7301
  {
7302 7303 7304 7305 7306 7307 7308
    struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
    StorageImpl_FreeBigBlock(This->parentStorage,
      last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
    if (last_run->lastOffset == last_run->firstOffset)
      This->indexCacheLen--;
    else
      last_run->lastOffset--;
7309 7310
  }

7311 7312 7313 7314 7315 7316 7317 7318
  /*
   * Reset the last accessed block cache.
   */
  for (i=0; i<2; i++)
  {
    if (This->cachedBlocks[i].index >= numBlocks)
    {
      This->cachedBlocks[i].index = 0xffffffff;
7319
      This->cachedBlocks[i].dirty = FALSE;
7320 7321 7322
    }
  }

7323 7324 7325 7326 7327 7328 7329 7330
  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_Enlarge
 *
 * Grows this chain in the big block depot.
 */
7331 7332
static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
                                     ULARGE_INTEGER    newSize)
7333 7334 7335 7336 7337 7338 7339 7340
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = BlockChainStream_GetHeadOfChain(This);

  /*
7341
   * Empty chain. Create the head.
7342 7343 7344
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
7345 7346
    blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
    StorageImpl_SetNextBlockInChain(This->parentStorage,
7347 7348 7349 7350 7351 7352 7353 7354 7355
                                      blockIndex,
                                      BLOCK_END_OF_CHAIN);

    if (This->headOfStreamPlaceHolder != 0)
    {
      *(This->headOfStreamPlaceHolder) = blockIndex;
    }
    else
    {
7356
      DirEntry chainEntry;
7357
      assert(This->ownerDirEntry != DIRENTRY_NULL);
7358

7359
      StorageImpl_ReadDirEntry(
7360
        This->parentStorage,
7361
        This->ownerDirEntry,
7362
        &chainEntry);
7363

7364
      chainEntry.startingBlock = blockIndex;
7365

7366
      StorageImpl_WriteDirEntry(
7367
        This->parentStorage,
7368
        This->ownerDirEntry,
7369
        &chainEntry);
Thuy Nguyen's avatar
Thuy Nguyen committed
7370
    }
7371

Thuy Nguyen's avatar
Thuy Nguyen committed
7372 7373 7374
    This->tailIndex = blockIndex;
    This->numBlocks = 1;
  }
7375 7376 7377 7378

  /*
   * Figure out how many blocks are needed to contain this stream
   */
7379
  newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7380

7381
  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7382 7383 7384 7385 7386
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
Thuy Nguyen's avatar
Thuy Nguyen committed
7387
  if (This->tailIndex == BLOCK_END_OF_CHAIN)
7388 7389 7390
  {
    currentBlock = blockIndex;

Thuy Nguyen's avatar
Thuy Nguyen committed
7391 7392 7393 7394 7395
    while (blockIndex != BLOCK_END_OF_CHAIN)
    {
      This->numBlocks++;
      currentBlock = blockIndex;

7396 7397 7398
      if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
						&blockIndex)))
	return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
7399 7400 7401
    }

    This->tailIndex = currentBlock;
7402 7403
  }

Thuy Nguyen's avatar
Thuy Nguyen committed
7404 7405 7406
  currentBlock = This->tailIndex;
  oldNumBlocks = This->numBlocks;

7407 7408 7409
  /*
   * Add new blocks to the chain
   */
7410
  if (oldNumBlocks < newNumBlocks)
7411
  {
7412 7413 7414
    while (oldNumBlocks < newNumBlocks)
    {
      blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7415

7416
      StorageImpl_SetNextBlockInChain(
7417 7418
	This->parentStorage,
	currentBlock,
7419
	blockIndex);
7420

7421
      StorageImpl_SetNextBlockInChain(
7422 7423
        This->parentStorage,
	blockIndex,
7424
	BLOCK_END_OF_CHAIN);
7425

7426 7427 7428
      currentBlock = blockIndex;
      oldNumBlocks++;
    }
7429

7430 7431 7432
    This->tailIndex = blockIndex;
    This->numBlocks = newNumBlocks;
  }
Thuy Nguyen's avatar
Thuy Nguyen committed
7433

7434 7435 7436
  if (FAILED(BlockChainStream_UpdateIndexCache(This)))
    return FALSE;

7437 7438 7439 7440 7441 7442 7443 7444
  return TRUE;
}


/******************************************************************************
 *      BlockChainStream_GetSize
 *
 * Returns the size of this chain.
7445
 * Will return the block count if this chain doesn't have a directory entry.
7446
 */
7447
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7448
{
7449
  DirEntry chainEntry;
7450 7451 7452

  if(This->headOfStreamPlaceHolder == NULL)
  {
7453
    /*
7454
     * This chain has a directory entry so use the size value from there.
7455
     */
7456
    StorageImpl_ReadDirEntry(
7457
      This->parentStorage,
7458
      This->ownerDirEntry,
7459
      &chainEntry);
7460

7461
    return chainEntry.size;
7462 7463 7464 7465
  }
  else
  {
    /*
7466
     * this chain is a chain that does not have a directory entry, figure out the
7467
     * size by making the product number of used blocks times the
7468 7469 7470
     * size of them
     */
    ULARGE_INTEGER result;
7471 7472
    result.QuadPart =
      (ULONGLONG)BlockChainStream_GetCount(This) *
7473 7474 7475 7476 7477 7478 7479
      This->parentStorage->bigBlockSize;

    return result;
  }
}

/******************************************************************************
7480
 *      BlockChainStream_SetSize
7481
 *
7482 7483 7484 7485 7486 7487
 * Sets the size of this stream. The big block depot will be updated.
 * The file will grow if we grow the chain.
 *
 * TODO: Free the actual blocks in the file when we shrink the chain.
 *       Currently, the blocks are still in the file. So the file size
 *       doesn't shrink even if we shrink streams.
7488
 */
7489 7490 7491
BOOL BlockChainStream_SetSize(
  BlockChainStream* This,
  ULARGE_INTEGER    newSize)
7492
{
7493
  ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7494

7495 7496
  if (newSize.QuadPart == size.QuadPart)
    return TRUE;
7497

7498
  if (newSize.QuadPart < size.QuadPart)
7499
  {
7500 7501 7502 7503 7504
    BlockChainStream_Shrink(This, newSize);
  }
  else
  {
    BlockChainStream_Enlarge(This, newSize);
7505 7506
  }

7507
  return TRUE;
7508 7509 7510
}

/******************************************************************************
7511
 *      BlockChainStream_ReadAt
7512
 *
7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755
 * Reads a specified number of bytes from this chain at the specified offset.
 * bytesRead may be NULL.
 * Failure will be returned if the specified number of bytes has not been read.
 */
HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
  ULARGE_INTEGER offset,
  ULONG          size,
  void*          buffer,
  ULONG*         bytesRead)
{
  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  BYTE* bufferWalker;
  ULARGE_INTEGER stream_size;
  HRESULT hr;
  BlockChainBlock *cachedBlock;

  TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);

  /*
   * Find the first block in the stream that contains part of the buffer.
   */
  blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);

  *bytesRead   = 0;

  stream_size = BlockChainStream_GetSize(This);
  if (stream_size.QuadPart > offset.QuadPart)
    size = min(stream_size.QuadPart - offset.QuadPart, size);
  else
    return S_OK;

  /*
   * Start reading the buffer.
   */
  bufferWalker = buffer;

  while (size > 0)
  {
    ULARGE_INTEGER ulOffset;
    DWORD bytesReadAt;

    /*
     * Calculate how many bytes we can copy from this big block.
     */
    bytesToReadInBuffer =
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);

    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);

    if (FAILED(hr))
      return hr;

    if (!cachedBlock)
    {
      /* Not in cache, and we're going to read past the end of the block. */
      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                               offsetInBlock;

      StorageImpl_ReadAt(This->parentStorage,
           ulOffset,
           bufferWalker,
           bytesToReadInBuffer,
           &bytesReadAt);
    }
    else
    {
      if (!cachedBlock->read)
      {
        ULONG read;
        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
          return STG_E_READFAULT;

        cachedBlock->read = TRUE;
      }

      memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
      bytesReadAt = bytesToReadInBuffer;
    }

    blockNoInSequence++;
    bufferWalker += bytesReadAt;
    size         -= bytesReadAt;
    *bytesRead   += bytesReadAt;
    offsetInBlock = 0;  /* There is no offset on the next block */

    if (bytesToReadInBuffer != bytesReadAt)
        break;
  }

  return S_OK;
}

/******************************************************************************
 *      BlockChainStream_WriteAt
 *
 * Writes the specified number of bytes to this chain at the specified offset.
 * Will fail if not all specified number of bytes have been written.
 */
HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
  ULARGE_INTEGER    offset,
  ULONG             size,
  const void*       buffer,
  ULONG*            bytesWritten)
{
  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
  ULONG bytesToWrite;
  ULONG blockIndex;
  const BYTE* bufferWalker;
  HRESULT hr;
  BlockChainBlock *cachedBlock;

  *bytesWritten   = 0;
  bufferWalker = buffer;

  while (size > 0)
  {
    ULARGE_INTEGER ulOffset;
    DWORD bytesWrittenAt;

    /*
     * Calculate how many bytes we can copy to this big block.
     */
    bytesToWrite =
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);

    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);

    /* BlockChainStream_SetSize should have already been called to ensure we have
     * enough blocks in the chain to write into */
    if (FAILED(hr))
    {
      ERR("not enough blocks in chain to write data\n");
      return hr;
    }

    if (!cachedBlock)
    {
      /* Not in cache, and we're going to write past the end of the block. */
      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                               offsetInBlock;

      StorageImpl_WriteAt(This->parentStorage,
           ulOffset,
           bufferWalker,
           bytesToWrite,
           &bytesWrittenAt);
    }
    else
    {
      if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
      {
        ULONG read;
        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
          return STG_E_READFAULT;
      }

      memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
      bytesWrittenAt = bytesToWrite;
      cachedBlock->read = TRUE;
      cachedBlock->dirty = TRUE;
    }

    blockNoInSequence++;
    bufferWalker  += bytesWrittenAt;
    size          -= bytesWrittenAt;
    *bytesWritten += bytesWrittenAt;
    offsetInBlock  = 0;      /* There is no offset on the next block */

    if (bytesWrittenAt != bytesToWrite)
      break;
  }

  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
}


/************************************************************************
 * SmallBlockChainStream implementation
 ***********************************************************************/

SmallBlockChainStream* SmallBlockChainStream_Construct(
  StorageImpl* parentStorage,
  ULONG*         headOfStreamPlaceHolder,
  DirRef         dirEntry)
{
  SmallBlockChainStream* newStream;

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));

  newStream->parentStorage      = parentStorage;
  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
  newStream->ownerDirEntry      = dirEntry;

  return newStream;
}

void SmallBlockChainStream_Destroy(
  SmallBlockChainStream* This)
{
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
 *      SmallBlockChainStream_GetHeadOfChain
 *
 * Returns the head of this chain of small blocks.
 */
static ULONG SmallBlockChainStream_GetHeadOfChain(
  SmallBlockChainStream* This)
{
  DirEntry  chainEntry;
  HRESULT   hr;

  if (This->headOfStreamPlaceHolder != NULL)
    return *(This->headOfStreamPlaceHolder);

  if (This->ownerDirEntry)
  {
    hr = StorageImpl_ReadDirEntry(
                      This->parentStorage,
                      This->ownerDirEntry,
                      &chainEntry);

    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
      return chainEntry.startingBlock;
  }

  return BLOCK_END_OF_CHAIN;
}

/******************************************************************************
 *      SmallBlockChainStream_GetNextBlockInChain
 *
 * Returns the index of the next small block in this chain.
 *
 * Return Values:
 *    - BLOCK_END_OF_CHAIN: end of this chain
 *    - BLOCK_UNUSED: small block 'blockIndex' is free
 */
7756
static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7757
  SmallBlockChainStream* This,
7758 7759
  ULONG                  blockIndex,
  ULONG*                 nextBlockInChain)
7760 7761 7762 7763
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesRead;
7764
  HRESULT res;
7765

7766 7767
  *nextBlockInChain = BLOCK_END_OF_CHAIN;

7768
  offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
7769 7770 7771 7772

  /*
   * Read those bytes in the buffer from the small block file.
   */
7773
  res = BlockChainStream_ReadAt(
7774 7775 7776 7777 7778 7779
              This->parentStorage->smallBlockDepotChain,
              offsetOfBlockInDepot,
              sizeof(DWORD),
              &buffer,
              &bytesRead);

7780 7781 7782
  if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
    res = STG_E_READFAULT;

7783
  if (SUCCEEDED(res))
7784
  {
7785
    StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7786
    return S_OK;
7787 7788
  }

7789
  return res;
7790 7791 7792 7793 7794 7795 7796 7797 7798 7799
}

/******************************************************************************
 *       SmallBlockChainStream_SetNextBlockInChain
 *
 * Writes the index of the next block of the specified block in the small
 * block depot.
 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
 * To flag a block as free use BLOCK_UNUSED as nextBlock.
 */
7800
static void SmallBlockChainStream_SetNextBlockInChain(
7801 7802 7803 7804 7805 7806 7807 7808
  SmallBlockChainStream* This,
  ULONG                  blockIndex,
  ULONG                  nextBlock)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesWritten;

7809
  offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
7810

7811
  StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828

  /*
   * Read those bytes in the buffer from the small block file.
   */
  BlockChainStream_WriteAt(
    This->parentStorage->smallBlockDepotChain,
    offsetOfBlockInDepot,
    sizeof(DWORD),
    &buffer,
    &bytesWritten);
}

/******************************************************************************
 *      SmallBlockChainStream_FreeBlock
 *
 * Flag small block 'blockIndex' as free in the small block depot.
 */
7829
static void SmallBlockChainStream_FreeBlock(
7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842
  SmallBlockChainStream* This,
  ULONG                  blockIndex)
{
  SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
}

/******************************************************************************
 *      SmallBlockChainStream_GetNextFreeBlock
 *
 * Returns the index of a free small block. The small block depot will be
 * enlarged if necessary. The small block chain will also be enlarged if
 * necessary.
 */
7843
static ULONG SmallBlockChainStream_GetNextFreeBlock(
7844 7845 7846 7847 7848
  SmallBlockChainStream* This)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD buffer;
  ULONG bytesRead;
7849
  ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7850
  ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7851
  HRESULT res = S_OK;
7852
  ULONG smallBlocksPerBigBlock;
7853 7854 7855
  DirEntry rootEntry;
  ULONG blocksRequired;
  ULARGE_INTEGER old_size, size_required;
7856

7857
  offsetOfBlockInDepot.u.HighPart = 0;
7858 7859 7860 7861 7862 7863

  /*
   * Scan the small block depot for a free block
   */
  while (nextBlockIndex != BLOCK_UNUSED)
  {
7864
    offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7865

7866
    res = BlockChainStream_ReadAt(
7867 7868 7869 7870 7871 7872 7873 7874 7875
                This->parentStorage->smallBlockDepotChain,
                offsetOfBlockInDepot,
                sizeof(DWORD),
                &buffer,
                &bytesRead);

    /*
     * If we run out of space for the small block depot, enlarge it
     */
7876
    if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7877
    {
7878
      StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7879 7880 7881 7882 7883 7884

      if (nextBlockIndex != BLOCK_UNUSED)
        blockIndex++;
    }
    else
    {
7885
      ULONG count =
7886 7887
        BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);

7888
      BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7889 7890
      ULARGE_INTEGER newSize, offset;
      ULONG bytesWritten;
7891

7892
      newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7893
      BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7894 7895 7896 7897 7898

      /*
       * Initialize all the small blocks to free
       */
      memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7899
      offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7900 7901
      BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
        offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7902

7903
      StorageImpl_SaveFileHeader(This->parentStorage);
7904 7905 7906
    }
  }

7907 7908
  This->parentStorage->firstFreeSmallBlock = blockIndex+1;

7909
  smallBlocksPerBigBlock =
7910 7911 7912 7913 7914
    This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;

  /*
   * Verify if we have to allocate big blocks to contain small blocks
   */
7915
  blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7916

7917
  size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7918

7919
  old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7920

7921 7922 7923 7924 7925
  if (size_required.QuadPart > old_size.QuadPart)
  {
    BlockChainStream_SetSize(
      This->parentStorage->smallBlockRootChain,
      size_required);
7926

7927 7928 7929 7930
    StorageImpl_ReadDirEntry(
      This->parentStorage,
      This->parentStorage->base.storageDirEntry,
      &rootEntry);
7931

7932
    rootEntry.size = size_required;
7933

7934 7935 7936 7937
    StorageImpl_WriteDirEntry(
      This->parentStorage,
      This->parentStorage->base.storageDirEntry,
      &rootEntry);
7938 7939 7940 7941 7942 7943 7944 7945 7946 7947
  }

  return blockIndex;
}

/******************************************************************************
 *      SmallBlockChainStream_ReadAt
 *
 * Reads a specified number of bytes from this chain at the specified offset.
 * bytesRead may be NULL.
7948
 * Failure will be returned if the specified number of bytes has not been read.
7949
 */
7950
HRESULT SmallBlockChainStream_ReadAt(
7951 7952 7953 7954 7955 7956
  SmallBlockChainStream* This,
  ULARGE_INTEGER         offset,
  ULONG                  size,
  void*                  buffer,
  ULONG*                 bytesRead)
{
7957
  HRESULT rc = S_OK;
7958
  ULARGE_INTEGER offsetInBigBlockFile;
7959
  ULONG blockNoInSequence =
7960
    offset.u.LowPart / This->parentStorage->smallBlockSize;
7961

7962
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7963 7964 7965 7966
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  ULONG bytesReadFromBigBlockFile;
  BYTE* bufferWalker;
7967
  ULARGE_INTEGER stream_size;
7968 7969 7970 7971

  /*
   * This should never happen on a small block file.
   */
7972
  assert(offset.u.HighPart==0);
7973

7974 7975 7976 7977 7978 7979 7980 7981
  *bytesRead   = 0;

  stream_size = SmallBlockChainStream_GetSize(This);
  if (stream_size.QuadPart > offset.QuadPart)
    size = min(stream_size.QuadPart - offset.QuadPart, size);
  else
    return S_OK;

7982 7983 7984 7985 7986 7987 7988
  /*
   * Find the first block in the stream that contains part of the buffer.
   */
  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
7989 7990 7991
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
      return rc;
7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004
    blockNoInSequence--;
  }

  /*
   * Start reading the buffer.
   */
  bufferWalker = buffer;

  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy from this small block.
     */
8005
    bytesToReadInBuffer =
8006
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8007 8008 8009 8010

    /*
     * Calculate the offset of the small block in the small block file.
     */
8011 8012
    offsetInBigBlockFile.QuadPart   =
      (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8013

8014
    offsetInBigBlockFile.QuadPart  += offsetInBlock;
8015 8016 8017

    /*
     * Read those bytes in the buffer from the small block file.
8018 8019
     * The small block has already been identified so it shouldn't fail
     * unless the file is corrupt.
8020
     */
8021
    rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
8022 8023 8024
      offsetInBigBlockFile,
      bytesToReadInBuffer,
      bufferWalker,
8025 8026 8027 8028
      &bytesReadFromBigBlockFile);

    if (FAILED(rc))
      return rc;
8029

8030 8031 8032
    if (!bytesReadFromBigBlockFile)
      return STG_E_DOCFILECORRUPT;

8033 8034 8035
    /*
     * Step to the next big block.
     */
8036 8037
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
8038
      return STG_E_DOCFILECORRUPT;
8039

8040 8041 8042 8043
    bufferWalker += bytesReadFromBigBlockFile;
    size         -= bytesReadFromBigBlockFile;
    *bytesRead   += bytesReadFromBigBlockFile;
    offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
8044 8045
  }

8046
  return S_OK;
8047 8048 8049 8050 8051 8052 8053 8054
}

/******************************************************************************
 *       SmallBlockChainStream_WriteAt
 *
 * Writes the specified number of bytes to this chain at the specified offset.
 * Will fail if not all specified number of bytes have been written.
 */
8055
HRESULT SmallBlockChainStream_WriteAt(
8056 8057 8058 8059 8060 8061 8062
  SmallBlockChainStream* This,
  ULARGE_INTEGER offset,
  ULONG          size,
  const void*    buffer,
  ULONG*         bytesWritten)
{
  ULARGE_INTEGER offsetInBigBlockFile;
8063
  ULONG blockNoInSequence =
8064
    offset.u.LowPart / This->parentStorage->smallBlockSize;
8065

8066
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
8067 8068
  ULONG bytesToWriteInBuffer;
  ULONG blockIndex;
8069
  ULONG bytesWrittenToBigBlockFile;
Eric Pouech's avatar
Eric Pouech committed
8070
  const BYTE* bufferWalker;
8071
  HRESULT res;
8072

8073 8074 8075
  /*
   * This should never happen on a small block file.
   */
8076
  assert(offset.u.HighPart==0);
8077

8078 8079 8080 8081
  /*
   * Find the first block in the stream that contains part of the buffer.
   */
  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8082

8083 8084
  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
8085
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
8086
      return STG_E_DOCFILECORRUPT;
8087 8088
    blockNoInSequence--;
  }
8089

8090 8091 8092 8093
  /*
   * Start writing the buffer.
   */
  *bytesWritten   = 0;
8094
  bufferWalker = buffer;
8095 8096 8097 8098 8099
  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy to this small block.
     */
8100
    bytesToWriteInBuffer =
8101
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
8102

8103 8104 8105
    /*
     * Calculate the offset of the small block in the small block file.
     */
8106 8107
    offsetInBigBlockFile.QuadPart   =
      (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
8108

8109
    offsetInBigBlockFile.QuadPart  += offsetInBlock;
8110

8111 8112 8113
    /*
     * Write those bytes in the buffer to the small block file.
     */
8114 8115
    res = BlockChainStream_WriteAt(
      This->parentStorage->smallBlockRootChain,
8116 8117 8118
      offsetInBigBlockFile,
      bytesToWriteInBuffer,
      bufferWalker,
8119
      &bytesWrittenToBigBlockFile);
8120 8121
    if (FAILED(res))
      return res;
8122

8123 8124 8125
    /*
     * Step to the next big block.
     */
Andrew Eikum's avatar
Andrew Eikum committed
8126 8127 8128
    res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if (FAILED(res))
      return res;
8129 8130 8131 8132
    bufferWalker  += bytesWrittenToBigBlockFile;
    size          -= bytesWrittenToBigBlockFile;
    *bytesWritten += bytesWrittenToBigBlockFile;
    offsetInBlock  = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
8133
  }
8134

8135
  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
8136 8137 8138 8139 8140
}

/******************************************************************************
 *       SmallBlockChainStream_Shrink
 *
8141
 * Shrinks this chain in the small block depot.
8142
 */
8143
static BOOL SmallBlockChainStream_Shrink(
8144 8145 8146 8147 8148
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, extraBlock;
  ULONG numBlocks;
8149
  ULONG count = 0;
8150

8151
  numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8152

8153
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8154 8155 8156 8157 8158 8159 8160 8161 8162
    numBlocks++;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
   * Go to the new end of chain
   */
  while (count < numBlocks)
  {
8163 8164 8165
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&blockIndex)))
      return FALSE;
8166 8167 8168
    count++;
  }

8169 8170
  /*
   * If the count is 0, we have a special case, the head of the chain was
8171
   * just freed.
8172 8173 8174
   */
  if (count == 0)
  {
8175
    DirEntry chainEntry;
8176

8177
    StorageImpl_ReadDirEntry(This->parentStorage,
8178
			     This->ownerDirEntry,
8179
			     &chainEntry);
8180

8181
    chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
8182

8183
    StorageImpl_WriteDirEntry(This->parentStorage,
8184
			      This->ownerDirEntry,
8185
			      &chainEntry);
8186 8187 8188 8189 8190 8191 8192 8193 8194

    /*
     * We start freeing the chain at the head block.
     */
    extraBlock = blockIndex;
  }
  else
  {
    /* Get the next block before marking the new end */
8195 8196 8197
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&extraBlock)))
      return FALSE;
8198 8199 8200

    /* Mark the new end of chain */
    SmallBlockChainStream_SetNextBlockInChain(
8201 8202
      This,
      blockIndex,
8203 8204
      BLOCK_END_OF_CHAIN);
  }
8205 8206 8207 8208 8209 8210

  /*
   * Mark the extra blocks as free
   */
  while (extraBlock != BLOCK_END_OF_CHAIN)
  {
8211 8212 8213
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
							&blockIndex)))
      return FALSE;
8214
    SmallBlockChainStream_FreeBlock(This, extraBlock);
8215
    This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8216 8217 8218
    extraBlock = blockIndex;
  }

8219
  return TRUE;
8220 8221 8222 8223 8224 8225 8226
}

/******************************************************************************
 *      SmallBlockChainStream_Enlarge
 *
 * Grows this chain in the small block depot.
 */
8227
static BOOL SmallBlockChainStream_Enlarge(
8228 8229 8230 8231 8232 8233 8234 8235 8236 8237
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
8238
   * Empty chain. Create the head.
8239 8240 8241
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
8242 8243 8244 8245 8246
    blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
    SmallBlockChainStream_SetNextBlockInChain(
        This,
        blockIndex,
        BLOCK_END_OF_CHAIN);
8247

8248 8249 8250 8251 8252 8253
    if (This->headOfStreamPlaceHolder != NULL)
    {
      *(This->headOfStreamPlaceHolder) = blockIndex;
    }
    else
    {
8254
      DirEntry chainEntry;
8255

8256
      StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8257
                                   &chainEntry);
8258

8259
      chainEntry.startingBlock = blockIndex;
8260

8261
      StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8262
                                  &chainEntry);
8263
    }
8264 8265 8266 8267 8268 8269 8270
  }

  currentBlock = blockIndex;

  /*
   * Figure out how many blocks are needed to contain this stream
   */
8271
  newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8272

8273
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8274 8275 8276 8277 8278 8279 8280 8281 8282
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
  while (blockIndex != BLOCK_END_OF_CHAIN)
  {
    oldNumBlocks++;
    currentBlock = blockIndex;
8283 8284
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
      return FALSE;
8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295
  }

  /*
   * Add new blocks to the chain
   */
  while (oldNumBlocks < newNumBlocks)
  {
    blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
    SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);

    SmallBlockChainStream_SetNextBlockInChain(
8296 8297
      This,
      blockIndex,
8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314
      BLOCK_END_OF_CHAIN);

    currentBlock = blockIndex;
    oldNumBlocks++;
  }

  return TRUE;
}

/******************************************************************************
 *      SmallBlockChainStream_SetSize
 *
 * Sets the size of this stream.
 * The file will grow if we grow the chain.
 *
 * TODO: Free the actual blocks in the file when we shrink the chain.
 *       Currently, the blocks are still in the file. So the file size
8315
 *       doesn't shrink even if we shrink streams.
8316
 */
8317
BOOL SmallBlockChainStream_SetSize(
8318 8319 8320 8321 8322
                SmallBlockChainStream* This,
                ULARGE_INTEGER    newSize)
{
  ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);

8323
  if (newSize.u.LowPart == size.u.LowPart)
8324 8325
    return TRUE;

8326
  if (newSize.u.LowPart < size.u.LowPart)
8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337
  {
    SmallBlockChainStream_Shrink(This, newSize);
  }
  else
  {
    SmallBlockChainStream_Enlarge(This, newSize);
  }

  return TRUE;
}

8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363
/******************************************************************************
 *       SmallBlockChainStream_GetCount
 *
 * Returns the number of small blocks that comprises this chain.
 * This is not the size of the stream as the last block may not be full!
 *
 */
static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
{
    ULONG blockIndex;
    ULONG count = 0;

    blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

    while(blockIndex != BLOCK_END_OF_CHAIN)
    {
        count++;

        if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
                        blockIndex, &blockIndex)))
            return 0;
    }

    return count;
}

8364 8365 8366 8367 8368
/******************************************************************************
 *      SmallBlockChainStream_GetSize
 *
 * Returns the size of this chain.
 */
8369
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8370
{
8371
  DirEntry chainEntry;
8372

8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383
  if(This->headOfStreamPlaceHolder != NULL)
  {
    ULARGE_INTEGER result;
    result.u.HighPart = 0;

    result.u.LowPart = SmallBlockChainStream_GetCount(This) *
        This->parentStorage->smallBlockSize;

    return result;
  }

8384
  StorageImpl_ReadDirEntry(
8385
    This->parentStorage,
8386
    This->ownerDirEntry,
8387
    &chainEntry);
8388

8389
  return chainEntry.size;
8390 8391
}

8392 8393 8394 8395 8396

/************************************************************************
 * Miscellaneous storage functions
 ***********************************************************************/

8397
static HRESULT create_storagefile(
8398
  LPCOLESTR pwcsName,
8399
  DWORD       grfMode,
8400 8401 8402 8403
  DWORD       grfAttrs,
  STGOPTIONS* pStgOptions,
  REFIID      riid,
  void**      ppstgOpen)
8404
{
8405
  StorageBaseImpl* newStorage = 0;
8406
  HANDLE       hFile      = INVALID_HANDLE_VALUE;
8407
  HRESULT        hr         = STG_E_INVALIDFLAG;
8408 8409 8410 8411
  DWORD          shareMode;
  DWORD          accessMode;
  DWORD          creationMode;
  DWORD          fileAttributes;
8412
  WCHAR          tempFileName[MAX_PATH];
8413

8414
  if (ppstgOpen == 0)
8415
    return STG_E_INVALIDPOINTER;
8416 8417

  if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8418
    return STG_E_INVALIDPARAMETER;
8419

8420 8421 8422 8423
  /* if no share mode given then DENY_NONE is the default */
  if (STGM_SHARE_MODE(grfMode) == 0)
      grfMode |= STGM_SHARE_DENY_NONE;

8424
  if ( FAILED( validateSTGM(grfMode) ))
8425
    goto end;
8426

8427
  /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8428 8429 8430 8431 8432 8433
  switch(STGM_ACCESS_MODE(grfMode))
  {
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
8434
    goto end;
8435
  }
8436

8437 8438
  /* in direct mode, can only use SHARE_EXCLUSIVE */
  if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8439
    goto end;
8440

8441 8442
  /* but in transacted mode, any share mode is valid */

8443 8444 8445 8446 8447 8448
  /*
   * Generate a unique name.
   */
  if (pwcsName == 0)
  {
    WCHAR tempPath[MAX_PATH];
8449
    static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8450 8451 8452 8453 8454 8455 8456 8457 8458 8459

    memset(tempPath, 0, sizeof(tempPath));
    memset(tempFileName, 0, sizeof(tempFileName));

    if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
      tempPath[0] = '.';

    if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
      pwcsName = tempFileName;
    else
8460 8461 8462 8463
    {
      hr = STG_E_INSUFFICIENTMEMORY;
      goto end;
    }
8464 8465 8466 8467 8468 8469

    creationMode = TRUNCATE_EXISTING;
  }
  else
  {
    creationMode = GetCreationModeFromSTGM(grfMode);
8470 8471
  }

8472
  /*
8473
   * Interpret the STGM value grfMode
8474
   */
8475
  shareMode    = GetShareModeFromSTGM(grfMode);
8476 8477 8478 8479 8480 8481 8482
  accessMode   = GetAccessModeFromSTGM(grfMode);

  if (grfMode & STGM_DELETEONRELEASE)
    fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
  else
    fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;

8483 8484
  *ppstgOpen = 0;

8485
  hFile = CreateFileW(pwcsName,
8486 8487
                        accessMode,
                        shareMode,
8488
                        NULL,
8489 8490
                        creationMode,
                        fileAttributes,
8491
                        0);
8492

8493
  if (hFile == INVALID_HANDLE_VALUE)
8494
  {
8495
    if(GetLastError() == ERROR_FILE_EXISTS)
8496 8497 8498 8499
      hr = STG_E_FILEALREADYEXISTS;
    else
      hr = E_FAIL;
    goto end;
8500 8501 8502
  }

  /*
8503
   * Allocate and initialize the new IStorage object.
8504
   */
8505
  hr = Storage_Construct(
8506
         hFile,
8507
        pwcsName,
8508 8509
         NULL,
         grfMode,
8510
         TRUE,
8511
         TRUE,
8512
         pStgOptions->ulSectorSize,
8513
         &newStorage);
8514

8515
  if (FAILED(hr))
8516
  {
8517
    goto end;
8518
  }
8519

8520 8521
  hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
  IStorage_Release(&newStorage->IStorage_iface);
8522

8523
end:
8524
  TRACE("<-- %p  r = %08x\n", *ppstgOpen, hr);
8525 8526 8527 8528

  return hr;
}

8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567
/******************************************************************************
 *    StgCreateDocfile  [OLE32.@]
 * Creates a new compound file storage object
 *
 * PARAMS
 *  pwcsName  [ I] Unicode string with filename (can be relative or NULL)
 *  grfMode   [ I] Access mode for opening the new storage object (see STGM_ constants)
 *  reserved  [ ?] unused?, usually 0
 *  ppstgOpen [IO] A pointer to IStorage pointer to the new onject
 *
 * RETURNS
 *  S_OK if the file was successfully created
 *  some STG_E_ value if error
 * NOTES
 *  if pwcsName is NULL, create file with new unique name
 *  the function can returns
 *  STG_S_CONVERTED if the specified file was successfully converted to storage format
 *  (unrealized now)
 */
HRESULT WINAPI StgCreateDocfile(
  LPCOLESTR pwcsName,
  DWORD       grfMode,
  DWORD       reserved,
  IStorage  **ppstgOpen)
{
  STGOPTIONS stgoptions = {1, 0, 512};

  TRACE("(%s, %x, %d, %p)\n",
	debugstr_w(pwcsName), grfMode,
	reserved, ppstgOpen);

  if (ppstgOpen == 0)
    return STG_E_INVALIDPOINTER;
  if (reserved != 0)
    return STG_E_INVALIDPARAMETER;

  return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
}

8568 8569 8570
/******************************************************************************
 *              StgCreateStorageEx        [OLE32.@]
 */
8571 8572
HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
8573
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8574
          grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8575 8576 8577 8578

    if (stgfmt != STGFMT_FILE && grfAttrs != 0)
    {
        ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8579
        return STG_E_INVALIDPARAMETER;
8580 8581
    }

8582
    if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8583 8584
    {
        ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8585
        return STG_E_INVALIDPARAMETER;
8586 8587 8588 8589
    }

    if (stgfmt == STGFMT_FILE)
    {
8590
        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8591 8592 8593 8594 8595
        return STG_E_INVALIDPARAMETER;
    }

    if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
    {
8596 8597 8598
        STGOPTIONS defaultOptions = {1, 0, 512};

        if (!pStgOptions) pStgOptions = &defaultOptions;
8599
        return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8600 8601
    }

8602

8603 8604
    ERR("Invalid stgfmt argument\n");
    return STG_E_INVALIDPARAMETER;
8605 8606
}

8607 8608 8609 8610
/******************************************************************************
 *              StgCreatePropSetStg       [OLE32.@]
 */
HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8611
 IPropertySetStorage **propset)
8612
{
8613
    TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8614
    if (reserved)
8615 8616 8617
        return STG_E_INVALIDPARAMETER;

    return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8618 8619
}

8620 8621 8622 8623 8624
/******************************************************************************
 *              StgOpenStorageEx      [OLE32.@]
 */
HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
8625
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8626 8627 8628 8629 8630
          grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);

    if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
    {
        ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8631
        return STG_E_INVALIDPARAMETER;
8632 8633
    }

8634
    switch (stgfmt)
8635
    {
8636
    case STGFMT_FILE:
8637
        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8638
        return STG_E_INVALIDPARAMETER;
8639

8640 8641
    case STGFMT_STORAGE:
        break;
8642

8643 8644 8645 8646
    case STGFMT_DOCFILE:
        if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
        {
            ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8647
            return STG_E_INVALIDPARAMETER;
8648
        }
8649
        FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8650 8651 8652 8653 8654 8655 8656 8657
        break;

    case STGFMT_ANY:
        WARN("STGFMT_ANY assuming storage\n");
        break;

    default:
        return STG_E_INVALIDPARAMETER;
8658 8659
    }

8660
    return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8661 8662 8663
}


8664
/******************************************************************************
8665
 *              StgOpenStorage        [OLE32.@]
8666
 */
8667 8668 8669
HRESULT WINAPI StgOpenStorage(
  const OLECHAR *pwcsName,
  IStorage      *pstgPriority,
8670 8671 8672 8673
  DWORD          grfMode,
  SNB            snbExclude,
  DWORD          reserved,
  IStorage     **ppstgOpen)
8674
{
8675
  StorageBaseImpl* newStorage = 0;
8676
  HRESULT        hr = S_OK;
8677
  HANDLE         hFile = 0;
8678 8679
  DWORD          shareMode;
  DWORD          accessMode;
8680
  LPWSTR         temp_name = NULL;
8681

8682
  TRACE("(%s, %p, %x, %p, %d, %p)\n",
8683 8684 8685
	debugstr_w(pwcsName), pstgPriority, grfMode,
	snbExclude, reserved, ppstgOpen);

8686 8687 8688 8689 8690 8691 8692 8693 8694
  if (pstgPriority)
  {
    /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
    hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
    if (FAILED(hr)) goto end;
    pwcsName = temp_name;
    TRACE("using filename %s\n", debugstr_w(temp_name));
  }

8695 8696 8697 8698 8699 8700 8701
  if (pwcsName == 0)
  {
    hr = STG_E_INVALIDNAME;
    goto end;
  }

  if (ppstgOpen == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
8702 8703 8704 8705
  {
    hr = STG_E_INVALIDPOINTER;
    goto end;
  }
8706

8707 8708 8709 8710 8711 8712
  if (reserved)
  {
    hr = STG_E_INVALIDPARAMETER;
    goto end;
  }

8713 8714
  if (grfMode & STGM_PRIORITY)
  {
8715 8716 8717 8718 8719 8720
    if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
      return STG_E_INVALIDFLAG;
    if (grfMode & STGM_DELETEONRELEASE)
      return STG_E_INVALIDFUNCTION;
    if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
      return STG_E_INVALIDFLAG;
8721
    grfMode &= ~0xf0; /* remove the existing sharing mode */
8722
    grfMode |= STGM_SHARE_DENY_NONE;
8723 8724
  }

8725 8726 8727
  /*
   * Validate the sharing mode
   */
8728 8729 8730 8731 8732 8733 8734 8735 8736 8737
  if (grfMode & STGM_DIRECT_SWMR)
  {
    if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
        (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
    {
      hr = STG_E_INVALIDFLAG;
      goto end;
    }
  }
  else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8738 8739 8740 8741 8742 8743 8744 8745 8746
    switch(STGM_SHARE_MODE(grfMode))
    {
      case STGM_SHARE_EXCLUSIVE:
      case STGM_SHARE_DENY_WRITE:
        break;
      default:
        hr = STG_E_INVALIDFLAG;
        goto end;
    }
8747 8748 8749

  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode&STGM_CREATE))
Alexandre Julliard's avatar
Alexandre Julliard committed
8750 8751 8752 8753
  {
    hr = STG_E_INVALIDFLAG;
    goto end;
  }
8754

8755
  /* shared reading requires transacted or single writer mode */
8756 8757
  if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
      STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8758
     !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8759 8760 8761 8762 8763
  {
    hr = STG_E_INVALIDFLAG;
    goto end;
  }

8764 8765 8766 8767 8768 8769
  /*
   * Interpret the STGM value grfMode
   */
  shareMode    = GetShareModeFromSTGM(grfMode);
  accessMode   = GetAccessModeFromSTGM(grfMode);

8770
  *ppstgOpen = 0;
8771

8772 8773 8774 8775 8776 8777 8778
  hFile = CreateFileW( pwcsName,
                       accessMode,
                       shareMode,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
                       0);
8779

8780
  if (hFile==INVALID_HANDLE_VALUE)
8781
  {
8782 8783
    DWORD last_error = GetLastError();

Alexandre Julliard's avatar
Alexandre Julliard committed
8784 8785
    hr = E_FAIL;

8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808
    switch (last_error)
    {
      case ERROR_FILE_NOT_FOUND:
        hr = STG_E_FILENOTFOUND;
        break;

      case ERROR_PATH_NOT_FOUND:
        hr = STG_E_PATHNOTFOUND;
        break;

      case ERROR_ACCESS_DENIED:
      case ERROR_WRITE_PROTECT:
        hr = STG_E_ACCESSDENIED;
        break;

      case ERROR_SHARING_VIOLATION:
        hr = STG_E_SHAREVIOLATION;
        break;

      default:
        hr = E_FAIL;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
8809
    goto end;
8810 8811
  }

8812 8813 8814 8815
  /*
   * Refuse to open the file if it's too small to be a structured storage file
   * FIXME: verify the file when reading instead of here
   */
8816
  if (GetFileSize(hFile, NULL) < 0x100)
8817 8818 8819 8820 8821
  {
    CloseHandle(hFile);
    hr = STG_E_FILEALREADYEXISTS;
    goto end;
  }
8822

8823
  /*
8824
   * Allocate and initialize the new IStorage object.
8825
   */
8826
  hr = Storage_Construct(
8827
         hFile,
8828
         pwcsName,
8829 8830
         NULL,
         grfMode,
8831
         TRUE,
8832
         FALSE,
8833
         512,
8834
         &newStorage);
8835

8836
  if (FAILED(hr))
8837
  {
8838 8839 8840 8841
    /*
     * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
     */
    if(hr == STG_E_INVALIDHEADER)
Alexandre Julliard's avatar
Alexandre Julliard committed
8842 8843
	hr = STG_E_FILEALREADYEXISTS;
    goto end;
8844
  }
8845

8846
  *ppstgOpen = &newStorage->IStorage_iface;
8847

Alexandre Julliard's avatar
Alexandre Julliard committed
8848
end:
8849 8850
  CoTaskMemFree(temp_name);
  if (pstgPriority) IStorage_Release(pstgPriority);
8851
  TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8852 8853 8854 8855
  return hr;
}

/******************************************************************************
8856
 *    StgCreateDocfileOnILockBytes    [OLE32.@]
8857 8858 8859 8860 8861 8862 8863
 */
HRESULT WINAPI StgCreateDocfileOnILockBytes(
      ILockBytes *plkbyt,
      DWORD grfMode,
      DWORD reserved,
      IStorage** ppstgOpen)
{
8864
  StorageBaseImpl* newStorage = 0;
8865 8866 8867 8868 8869 8870 8871 8872
  HRESULT        hr         = S_OK;

  if ((ppstgOpen == 0) || (plkbyt == 0))
    return STG_E_INVALIDPOINTER;

  /*
   * Allocate and initialize the new IStorage object.
   */
8873
  hr = Storage_Construct(
8874
         0,
8875
        0,
8876 8877
         plkbyt,
         grfMode,
8878
         FALSE,
8879
         TRUE,
8880
         512,
8881
         &newStorage);
8882 8883 8884 8885 8886 8887

  if (FAILED(hr))
  {
    return hr;
  }

8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193
  *ppstgOpen = &newStorage->IStorage_iface;

  return hr;
}

/******************************************************************************
 *    StgOpenStorageOnILockBytes    [OLE32.@]
 */
HRESULT WINAPI StgOpenStorageOnILockBytes(
      ILockBytes *plkbyt,
      IStorage *pstgPriority,
      DWORD grfMode,
      SNB snbExclude,
      DWORD reserved,
      IStorage **ppstgOpen)
{
  StorageBaseImpl* newStorage = 0;
  HRESULT        hr = S_OK;

  if ((plkbyt == 0) || (ppstgOpen == 0))
    return STG_E_INVALIDPOINTER;

  if ( FAILED( validateSTGM(grfMode) ))
    return STG_E_INVALIDFLAG;

  *ppstgOpen = 0;

  /*
   * Allocate and initialize the new IStorage object.
   */
  hr = Storage_Construct(
         0,
         0,
         plkbyt,
         grfMode,
         FALSE,
         FALSE,
         512,
         &newStorage);

  if (FAILED(hr))
  {
    return hr;
  }

  *ppstgOpen = &newStorage->IStorage_iface;

  return hr;
}

/******************************************************************************
 *              StgSetTimes [ole32.@]
 *              StgSetTimes [OLE32.@]
 *
 *
 */
HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
                           FILETIME const *patime, FILETIME const *pmtime)
{
  IStorage *stg = NULL;
  HRESULT r;

  TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);

  r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
                     0, 0, &stg);
  if( SUCCEEDED(r) )
  {
    r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
    IStorage_Release(stg);
  }

  return r;
}

/******************************************************************************
 *              StgIsStorageILockBytes        [OLE32.@]
 *
 * Determines if the ILockBytes contains a storage object.
 */
HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
{
  BYTE sig[sizeof(STORAGE_magic)];
  ULARGE_INTEGER offset;
  ULONG read = 0;

  offset.u.HighPart = 0;
  offset.u.LowPart  = 0;

  ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);

  if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
    return S_OK;

  return S_FALSE;
}

/******************************************************************************
 *              WriteClassStg        [OLE32.@]
 *
 * This method will store the specified CLSID in the specified storage object
 */
HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
{
  if(!pStg)
    return E_INVALIDARG;

  if(!rclsid)
    return STG_E_INVALIDPOINTER;

  return IStorage_SetClass(pStg, rclsid);
}

/***********************************************************************
 *    ReadClassStg (OLE32.@)
 *
 * This method reads the CLSID previously written to a storage object with
 * the WriteClassStg.
 *
 * PARAMS
 *  pstg    [I] IStorage pointer
 *  pclsid  [O] Pointer to where the CLSID is written
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){

    STATSTG pstatstg;
    HRESULT hRes;

    TRACE("(%p, %p)\n", pstg, pclsid);

    if(!pstg || !pclsid)
        return E_INVALIDARG;

   /*
    * read a STATSTG structure (contains the clsid) from the storage
    */
    hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);

    if(SUCCEEDED(hRes))
        *pclsid=pstatstg.clsid;

    return hRes;
}

/***********************************************************************
 *    OleLoadFromStream (OLE32.@)
 *
 * This function loads an object from stream
 */
HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
{
    CLSID	clsid;
    HRESULT	res;
    LPPERSISTSTREAM	xstm;

    TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);

    res=ReadClassStm(pStm,&clsid);
    if (FAILED(res))
	return res;
    res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
    if (FAILED(res))
	return res;
    res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
    if (FAILED(res)) {
	IUnknown_Release((IUnknown*)*ppvObj);
	return res;
    }
    res=IPersistStream_Load(xstm,pStm);
    IPersistStream_Release(xstm);
    /* FIXME: all refcounts ok at this point? I think they should be:
     * 		pStm	: unchanged
     *		ppvObj	: 1
     *		xstm	: 0 (released)
     */
    return res;
}

/***********************************************************************
 *    OleSaveToStream (OLE32.@)
 *
 * This function saves an object with the IPersistStream interface on it
 * to the specified stream.
 */
HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
{

    CLSID clsid;
    HRESULT res;

    TRACE("(%p,%p)\n",pPStm,pStm);

    res=IPersistStream_GetClassID(pPStm,&clsid);

    if (SUCCEEDED(res)){

        res=WriteClassStm(pStm,&clsid);

        if (SUCCEEDED(res))

            res=IPersistStream_Save(pPStm,pStm,TRUE);
    }

    TRACE("Finished Save\n");
    return res;
}

/*************************************************************************
 * STORAGE_CreateOleStream [Internal]
 *
 * Creates the "\001OLE" stream in the IStorage if necessary.
 *
 * PARAMS
 *     storage     [I] Dest storage to create the stream in
 *     flags       [I] flags to be set for newly created stream
 *
 * RETURNS
 *     HRESULT return value
 *
 * NOTES
 *
 *     This stream is still unknown, MS Word seems to have extra data
 *     but since the data is stored in the OLESTREAM there should be
 *     no need to recreate the stream.  If the stream is manually
 *     deleted it will create it with this default data.
 *
 */
HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
{
    static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
    static const DWORD version_magic = 0x02000001;
    IStream *stream;
    HRESULT hr;

    hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
    if (hr == S_OK)
    {
        struct empty_1ole_stream {
            DWORD version_magic;
            DWORD flags;
            DWORD update_options;
            DWORD reserved;
            DWORD mon_stream_size;
        };
        struct empty_1ole_stream stream_data;

        stream_data.version_magic = version_magic;
        stream_data.flags = flags;
        stream_data.update_options = 0;
        stream_data.reserved = 0;
        stream_data.mon_stream_size = 0;

        hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
        IStream_Release(stream);
    }

    return hr;
}

/* write a string to a stream, preceded by its length */
static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
{
    HRESULT r;
    LPSTR str;
    DWORD len = 0;

    if( string )
        len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
    r = IStream_Write( stm, &len, sizeof(len), NULL);
    if( FAILED( r ) )
        return r;
    if(len == 0)
        return r;
    str = CoTaskMemAlloc( len );
    WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
    r = IStream_Write( stm, str, len, NULL);
    CoTaskMemFree( str );
    return r;
}

/* read a string preceded by its length from a stream */
static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
{
    HRESULT r;
    DWORD len, count = 0;
    LPSTR str;
    LPWSTR wstr;

    r = IStream_Read( stm, &len, sizeof(len), &count );
    if( FAILED( r ) )
        return r;
    if( count != sizeof(len) )
        return E_OUTOFMEMORY;

    TRACE("%d bytes\n",len);

    str = CoTaskMemAlloc( len );
    if( !str )
        return E_OUTOFMEMORY;
    count = 0;
    r = IStream_Read( stm, str, len, &count );
    if( FAILED( r ) )
9194 9195
    {
        CoTaskMemFree( str );
9196
        return r;
9197
    }
9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215
    if( count != len )
    {
        CoTaskMemFree( str );
        return E_OUTOFMEMORY;
    }

    TRACE("Read string %s\n",debugstr_an(str,len));

    len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
    wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
    if( wstr )
    {
         MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
         wstr[len] = 0;
    }
    CoTaskMemFree( str );

    *string = wstr;
9216

9217
    return r;
9218 9219
}

9220 9221 9222

static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
    LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9223
{
9224 9225 9226
    IStream *pstm;
    HRESULT r = S_OK;
    static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9227

9228 9229 9230 9231 9232 9233
    static const BYTE unknown1[12] =
       { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
         0xFF, 0xFF, 0xFF, 0xFF};
    static const BYTE unknown2[16] =
       { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9234

9235 9236 9237
    TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
           debugstr_w(lpszUserType), debugstr_w(szClipName),
           debugstr_w(szProgIDName));
9238

9239 9240 9241 9242 9243
    /*  Create a CompObj stream */
    r = IStorage_CreateStream(pstg, szwStreamName,
        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
    if( FAILED (r) )
        return r;
9244

9245 9246
    /* Write CompObj Structure to stream */
    r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9247

9248 9249
    if( SUCCEEDED( r ) )
        r = WriteClassStm( pstm, clsid );
9250

9251 9252 9253 9254 9255 9256 9257 9258
    if( SUCCEEDED( r ) )
        r = STREAM_WriteString( pstm, lpszUserType );
    if( SUCCEEDED( r ) )
        r = STREAM_WriteString( pstm, szClipName );
    if( SUCCEEDED( r ) )
        r = STREAM_WriteString( pstm, szProgIDName );
    if( SUCCEEDED( r ) )
        r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9259

9260 9261 9262
    IStream_Release( pstm );

    return r;
9263 9264
}

9265 9266
/***********************************************************************
 *               WriteFmtUserTypeStg (OLE32.@)
Jim Aston's avatar
Jim Aston committed
9267
 */
9268 9269
HRESULT WINAPI WriteFmtUserTypeStg(
	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
Jim Aston's avatar
Jim Aston committed
9270
{
9271 9272 9273 9274 9275 9276
    STATSTG stat;
    HRESULT r;
    WCHAR szwClipName[0x40];
    CLSID clsid;
    LPWSTR wstrProgID = NULL;
    DWORD n;
Mike McCormack's avatar
Mike McCormack committed
9277

9278
    TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
Jim Aston's avatar
Jim Aston committed
9279

9280 9281 9282 9283 9284 9285 9286
    /* get the clipboard format name */
    if( cf )
    {
        n = GetClipboardFormatNameW( cf, szwClipName,
                sizeof(szwClipName)/sizeof(szwClipName[0]) );
        szwClipName[n]=0;
    }
9287

9288
    TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9289

9290 9291 9292 9293 9294
    r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
    if(SUCCEEDED(r))
        clsid = stat.clsid;
    else
        clsid = CLSID_NULL;
9295

9296
    ProgIDFromCLSID(&clsid, &wstrProgID);
9297

9298
    TRACE("progid is %s\n",debugstr_w(wstrProgID));
9299

9300 9301
    r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
            cf ? szwClipName : NULL, wstrProgID );
9302

9303
    CoTaskMemFree(wstrProgID);
9304

9305
    return r;
9306 9307
}

9308 9309 9310

/******************************************************************************
 *              ReadFmtUserTypeStg        [OLE32.@]
Noomen Hamza's avatar
Noomen Hamza committed
9311
 */
9312 9313 9314 9315 9316 9317 9318 9319 9320 9321
HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
{
    HRESULT r;
    IStream *stm = 0;
    static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
    unsigned char unknown1[12];
    unsigned char unknown2[16];
    DWORD count;
    LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
    CLSID clsid;
Noomen Hamza's avatar
Noomen Hamza committed
9322

9323
    TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9324

9325 9326 9327 9328 9329 9330 9331
    r = IStorage_OpenStream( pstg, szCompObj, NULL,
                    STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
    if( FAILED ( r ) )
    {
        WARN("Failed to open stream r = %08x\n", r);
        return r;
    }
9332

9333 9334 9335 9336 9337 9338 9339
    /* read the various parts of the structure */
    r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
    if( FAILED( r ) || ( count != sizeof(unknown1) ) )
        goto end;
    r = ReadClassStm( stm, &clsid );
    if( FAILED( r ) )
        goto end;
Noomen Hamza's avatar
Noomen Hamza committed
9340

9341 9342 9343
    r = STREAM_ReadString( stm, &szCLSIDName );
    if( FAILED( r ) )
        goto end;
Noomen Hamza's avatar
Noomen Hamza committed
9344

9345 9346 9347
    r = STREAM_ReadString( stm, &szOleTypeName );
    if( FAILED( r ) )
        goto end;
Noomen Hamza's avatar
Noomen Hamza committed
9348

9349 9350 9351
    r = STREAM_ReadString( stm, &szProgIDName );
    if( FAILED( r ) )
        goto end;
Noomen Hamza's avatar
Noomen Hamza committed
9352

9353 9354 9355
    r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
    if( FAILED( r ) || ( count != sizeof(unknown2) ) )
        goto end;
Noomen Hamza's avatar
Noomen Hamza committed
9356

9357 9358 9359
    /* ok, success... now we just need to store what we found */
    if( pcf )
        *pcf = RegisterClipboardFormatW( szOleTypeName );
Noomen Hamza's avatar
Noomen Hamza committed
9360

9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373
    if( lplpszUserType )
    {
        *lplpszUserType = szCLSIDName;
        szCLSIDName = NULL;
    }

end:
    CoTaskMemFree( szCLSIDName );
    CoTaskMemFree( szOleTypeName );
    CoTaskMemFree( szProgIDName );
    IStream_Release( stm );

    return r;
Noomen Hamza's avatar
Noomen Hamza committed
9374 9375
}

9376 9377 9378
/******************************************************************************
 * StgIsStorageFile [OLE32.@]
 * Verify if the file contains a storage object
Noomen Hamza's avatar
Noomen Hamza committed
9379
 *
9380 9381 9382 9383 9384 9385
 * PARAMS
 *  fn      [ I] Filename
 *
 * RETURNS
 *  S_OK    if file has magic bytes as a storage object
 *  S_FALSE if file is not storage
Noomen Hamza's avatar
Noomen Hamza committed
9386
 */
9387 9388
HRESULT WINAPI
StgIsStorageFile(LPCOLESTR fn)
Noomen Hamza's avatar
Noomen Hamza committed
9389
{
9390 9391 9392
	HANDLE		hf;
	BYTE		magic[8];
	DWORD		bytes_read;
Noomen Hamza's avatar
Noomen Hamza committed
9393

9394 9395 9396 9397
	TRACE("%s\n", debugstr_w(fn));
	hf = CreateFileW(fn, GENERIC_READ,
	                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
	                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
Noomen Hamza's avatar
Noomen Hamza committed
9398

9399 9400
	if (hf == INVALID_HANDLE_VALUE)
		return STG_E_FILENOTFOUND;
Noomen Hamza's avatar
Noomen Hamza committed
9401

9402 9403 9404 9405 9406 9407
	if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
	{
		WARN(" unable to read file\n");
		CloseHandle(hf);
		return S_FALSE;
	}
9408

9409
	CloseHandle(hf);
Noomen Hamza's avatar
Noomen Hamza committed
9410

9411 9412 9413 9414
	if (bytes_read != 8) {
		TRACE(" too short\n");
		return S_FALSE;
	}
Noomen Hamza's avatar
Noomen Hamza committed
9415

9416 9417 9418 9419
	if (!memcmp(magic,STORAGE_magic,8)) {
		TRACE(" -> YES\n");
		return S_OK;
	}
Noomen Hamza's avatar
Noomen Hamza committed
9420

9421 9422
	TRACE(" -> Invalid header.\n");
	return S_FALSE;
Noomen Hamza's avatar
Noomen Hamza committed
9423
}
9424

9425 9426
/***********************************************************************
 *		WriteClassStm (OLE32.@)
9427
 *
9428
 * Writes a CLSID to a stream.
9429
 *
9430 9431 9432
 * PARAMS
 *  pStm   [I] Stream to write to.
 *  rclsid [I] CLSID to write.
9433
 *
9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
{
    TRACE("(%p,%p)\n",pStm,rclsid);

    if (!pStm || !rclsid)
        return E_INVALIDARG;

    return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
}

/***********************************************************************
 *		ReadClassStm (OLE32.@)
9450
 *
9451
 * Reads a CLSID from a stream.
9452
 *
9453 9454 9455
 * PARAMS
 *  pStm   [I] Stream to read from.
 *  rclsid [O] CLSID to read.
9456
 *
9457 9458 9459
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
9460
 */
9461
HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9462
{
9463 9464
    ULONG nbByte;
    HRESULT res;
9465

9466
    TRACE("(%p,%p)\n",pStm,pclsid);
9467

9468 9469
    if (!pStm || !pclsid)
        return E_INVALIDARG;
9470

9471 9472
    /* clear the output args */
    *pclsid = CLSID_NULL;
9473

9474
    res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9475

9476 9477
    if (FAILED(res))
        return res;
9478

9479 9480 9481 9482 9483
    if (nbByte != sizeof(CLSID))
        return STG_E_READFAULT;
    else
        return S_OK;
}
9484

9485

9486 9487 9488
/************************************************************************
 * OleConvert Functions
 ***********************************************************************/
9489

9490 9491
#define OLESTREAM_ID 0x501
#define OLESTREAM_MAX_STR_LEN 255
9492

9493 9494
/* OLESTREAM memory structure to use for Get and Put Routines */
typedef struct
9495
{
9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507
    DWORD dwOleID;
    DWORD dwTypeID;
    DWORD dwOleTypeNameLength;
    CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
    CHAR  *pstrOleObjFileName;
    DWORD dwOleObjFileNameLength;
    DWORD dwMetaFileWidth;
    DWORD dwMetaFileHeight;
    CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
    DWORD dwDataLength;
    BYTE *pData;
} OLECONVERT_OLESTREAM_DATA;
9508

9509 9510
/* CompObj Stream structure */
typedef struct
9511
{
9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531
    BYTE byUnknown1[12];
    CLSID clsid;
    DWORD dwCLSIDNameLength;
    CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
    DWORD dwOleTypeNameLength;
    CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
    DWORD dwProgIDNameLength;
    CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
    BYTE byUnknown2[16];
} OLECONVERT_ISTORAGE_COMPOBJ;

/* Ole Presentation Stream structure */
typedef struct
{
    BYTE byUnknown1[28];
    DWORD dwExtentX;
    DWORD dwExtentY;
    DWORD dwSize;
    BYTE *pData;
} OLECONVERT_ISTORAGE_OLEPRES;
9532 9533 9534


/*************************************************************************
9535
 * OLECONVERT_LoadOLE10 [Internal]
9536
 *
9537
 * Loads the OLE10 STREAM to memory
9538 9539 9540 9541 9542 9543 9544 9545
 *
 * PARAMS
 *     pOleStream   [I] The OLESTREAM
 *     pData        [I] Data Structure for the OLESTREAM Data
 *
 * RETURNS
 *     Success:  S_OK
 *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
Austin English's avatar
Austin English committed
9546
 *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9547 9548 9549
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
9550
 *
9551 9552
 *     Memory allocated for pData must be freed by the caller
 */
9553
static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9554 9555 9556
{
	DWORD dwSize;
	HRESULT hRes = S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
9557 9558 9559
	int nTryCnt=0;
	int max_try = 6;

9560
	pData->pData = NULL;
9561
	pData->pstrOleObjFileName = NULL;
9562

Alexandre Julliard's avatar
Alexandre Julliard committed
9563 9564
	for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
	{
9565 9566 9567 9568 9569 9570 9571 9572 9573 9574
	/* Get the OleID */
	dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
	if(dwSize != sizeof(pData->dwOleID))
	{
		hRes = CONVERT10_E_OLESTREAM_GET;
	}
	else if(pData->dwOleID != OLESTREAM_ID)
	{
		hRes = CONVERT10_E_OLESTREAM_FMT;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
9575 9576 9577 9578 9579 9580
		else
		{
			hRes = S_OK;
			break;
		}
	}
9581 9582 9583

	if(hRes == S_OK)
	{
9584
		/* Get the TypeID... more info needed for this field */
9585 9586 9587 9588 9589 9590 9591 9592 9593 9594
		dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
		if(dwSize != sizeof(pData->dwTypeID))
		{
			hRes = CONVERT10_E_OLESTREAM_GET;
		}
	}
	if(hRes == S_OK)
	{
		if(pData->dwTypeID != 0)
		{
Francois Gouget's avatar
Francois Gouget committed
9595
			/* Get the length of the OleTypeName */
9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606
			dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
			if(dwSize != sizeof(pData->dwOleTypeNameLength))
			{
				hRes = CONVERT10_E_OLESTREAM_GET;
			}

			if(hRes == S_OK)
			{
				if(pData->dwOleTypeNameLength > 0)
				{
					/* Get the OleTypeName */
9607
					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9608 9609 9610 9611 9612 9613
					if(dwSize != pData->dwOleTypeNameLength)
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
9614 9615 9616 9617 9618 9619 9620
			if(bStrem1)
			{
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
				if(dwSize != sizeof(pData->dwOleObjFileNameLength))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
9621
			if(hRes == S_OK)
Alexandre Julliard's avatar
Alexandre Julliard committed
9622
			{
9623
					if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
Alexandre Julliard's avatar
Alexandre Julliard committed
9624
						pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9625
					pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
Alexandre Julliard's avatar
Alexandre Julliard committed
9626 9627
					if(pData->pstrOleObjFileName)
					{
9628
						dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
Alexandre Julliard's avatar
Alexandre Julliard committed
9629 9630 9631 9632 9633 9634 9635 9636 9637 9638
						if(dwSize != pData->dwOleObjFileNameLength)
						{
							hRes = CONVERT10_E_OLESTREAM_GET;
						}
					}
					else
						hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}
			else
9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654
			{
				/* Get the Width of the Metafile */
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
				if(dwSize != sizeof(pData->dwMetaFileWidth))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
			if(hRes == S_OK)
			{
				/* Get the Height of the Metafile */
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
				if(dwSize != sizeof(pData->dwMetaFileHeight))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
9655
			}
9656 9657
			if(hRes == S_OK)
			{
Francois Gouget's avatar
Francois Gouget committed
9658
				/* Get the Length of the Data */
9659 9660 9661 9662 9663 9664 9665
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
				if(dwSize != sizeof(pData->dwDataLength))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}

Austin English's avatar
Austin English committed
9666
			if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
Alexandre Julliard's avatar
Alexandre Julliard committed
9667
			{
9668
				if(!bStrem1) /* if it is a second OLE stream data */
Alexandre Julliard's avatar
Alexandre Julliard committed
9669 9670
				{
					pData->dwDataLength -= 8;
9671
					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
Alexandre Julliard's avatar
Alexandre Julliard committed
9672 9673 9674 9675 9676 9677
					if(dwSize != sizeof(pData->strUnknown))
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
9678 9679 9680 9681
			if(hRes == S_OK)
			{
				if(pData->dwDataLength > 0)
				{
9682
					pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9683 9684 9685 9686 9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 9704

					/* Get Data (ex. IStorage, Metafile, or BMP) */
					if(pData->pData)
					{
						dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
						if(dwSize != pData->dwDataLength)
						{
							hRes = CONVERT10_E_OLESTREAM_GET;
						}
					}
					else
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
		}
	}
	return hRes;
}

/*************************************************************************
9705
 * OLECONVERT_SaveOLE10 [Internal]
9706
 *
9707
 * Saves the OLE10 STREAM From memory
9708 9709
 *
 * PARAMS
9710 9711
 *     pData        [I] Data Structure for the OLESTREAM Data
 *     pOleStream   [I] The OLESTREAM to save
9712 9713
 *
 * RETURNS
9714 9715
 *     Success:  S_OK
 *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
9716 9717
 *
 * NOTES
9718
 *     This function is used by OleConvertIStorageToOLESTREAM only.
9719
 *
9720
 */
9721
static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9722
{
9723 9724
    DWORD dwSize;
    HRESULT hRes = S_OK;
9725 9726


9727 9728 9729
   /* Set the OleID */
    dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
    if(dwSize != sizeof(pData->dwOleID))
9730
    {
9731
        hRes = CONVERT10_E_OLESTREAM_PUT;
9732 9733
    }

9734
    if(hRes == S_OK)
9735
    {
9736 9737 9738 9739 9740 9741
        /* Set the TypeID */
        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
        if(dwSize != sizeof(pData->dwTypeID))
        {
            hRes = CONVERT10_E_OLESTREAM_PUT;
        }
9742
    }
9743

9744
    if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9745
    {
9746 9747 9748 9749 9750 9751
        /* Set the Length of the OleTypeName */
        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
        if(dwSize != sizeof(pData->dwOleTypeNameLength))
        {
            hRes = CONVERT10_E_OLESTREAM_PUT;
        }
9752

9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764
        if(hRes == S_OK)
        {
            if(pData->dwOleTypeNameLength > 0)
            {
                /* Set the OleTypeName */
                dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
                if(dwSize != pData->dwOleTypeNameLength)
                {
                    hRes = CONVERT10_E_OLESTREAM_PUT;
                }
            }
        }
9765

9766 9767 9768 9769 9770 9771 9772 9773 9774
        if(hRes == S_OK)
        {
            /* Set the width of the Metafile */
            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
            if(dwSize != sizeof(pData->dwMetaFileWidth))
            {
                hRes = CONVERT10_E_OLESTREAM_PUT;
            }
        }
9775

9776 9777 9778 9779 9780 9781 9782 9783 9784
        if(hRes == S_OK)
        {
            /* Set the height of the Metafile */
            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
            if(dwSize != sizeof(pData->dwMetaFileHeight))
            {
                hRes = CONVERT10_E_OLESTREAM_PUT;
            }
        }
9785

9786 9787 9788 9789 9790 9791 9792 9793 9794
        if(hRes == S_OK)
        {
            /* Set the length of the Data */
            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
            if(dwSize != sizeof(pData->dwDataLength))
            {
                hRes = CONVERT10_E_OLESTREAM_PUT;
            }
        }
9795

9796 9797 9798 9799 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809
        if(hRes == S_OK)
        {
            if(pData->dwDataLength > 0)
            {
                /* Set the Data (eg. IStorage, Metafile, Bitmap) */
                dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
                if(dwSize != pData->dwDataLength)
                {
                    hRes = CONVERT10_E_OLESTREAM_PUT;
                }
            }
        }
    }
    return hRes;
9810 9811
}

9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830
/*************************************************************************
 * OLECONVERT_GetOLE20FromOLE10[Internal]
 *
 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
 * opens it, and copies the content to the dest IStorage for
 * OleConvertOLESTREAMToIStorage
 *
 *
 * PARAMS
 *     pDestStorage  [I] The IStorage to copy the data to
 *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
 *     nBufferLength [I] The size of the buffer
 *
 * RETURNS
 *     Nothing
 *
 * NOTES
 *
 *
9831
 */
9832
static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9833
{
9834 9835 9836 9837 9838 9839
    HRESULT hRes;
    HANDLE hFile;
    IStorage *pTempStorage;
    DWORD dwNumOfBytesWritten;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9840

9841 9842 9843 9844
    /* Create a temp File */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
    hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9845

9846
    if(hFile != INVALID_HANDLE_VALUE)
9847
    {
9848 9849 9850
        /* Write IStorage Data to File */
        WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
        CloseHandle(hFile);
9851

9852 9853 9854 9855 9856 9857 9858 9859 9860 9861
        /* Open and copy temp storage to the Dest Storage */
        hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
        if(hRes == S_OK)
        {
            hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
            IStorage_Release(pTempStorage);
        }
        DeleteFileW(wstrTempFile);
    }
}
9862 9863


9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889
/*************************************************************************
 * OLECONVERT_WriteOLE20ToBuffer [Internal]
 *
 * Saves the OLE10 STREAM From memory
 *
 * PARAMS
 *     pStorage  [I] The Src IStorage to copy
 *     pData     [I] The Dest Memory to write to.
 *
 * RETURNS
 *     The size in bytes allocated for pData
 *
 * NOTES
 *     Memory allocated for pData must be freed by the caller
 *
 *     Used by OleConvertIStorageToOLESTREAM only.
 *
 */
static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
{
    HANDLE hFile;
    HRESULT hRes;
    DWORD nDataLength = 0;
    IStorage *pTempStorage;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9890

9891
    *pData = NULL;
9892

9893 9894 9895 9896
    /* Create temp Storage */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
    hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9897

9898
    if(hRes == S_OK)
9899
    {
9900 9901 9902
        /* Copy Src Storage to the Temp Storage */
        IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
        IStorage_Release(pTempStorage);
9903

9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915
        /* Open Temp Storage as a file and copy to memory */
        hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if(hFile != INVALID_HANDLE_VALUE)
        {
            nDataLength = GetFileSize(hFile, NULL);
            *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
            ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
            CloseHandle(hFile);
        }
        DeleteFileW(wstrTempFile);
    }
    return nDataLength;
9916 9917
}

9918
/*************************************************************************
9919
 * OLECONVERT_CreateCompObjStream [Internal]
9920 9921 9922 9923
 *
 * Creates a "\001CompObj" is the destination IStorage if necessary.
 *
 * PARAMS
9924
 *     pStorage       [I] The dest IStorage to create the CompObj Stream
9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935
 *                        if necessary.
 *     strOleTypeName [I] The ProgID
 *
 * RETURNS
 *     Success:  S_OK
 *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
 *
 *     The stream data is stored in the OLESTREAM and there should be
9936
 *     no need to recreate the stream.  If the stream is manually
9937 9938
 *     deleted it will attempt to create it by querying the registry.
 *
9939
 *
9940 9941 9942 9943 9944 9945
 */
HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
{
    IStream *pStream;
    HRESULT hStorageRes, hRes = S_OK;
    OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9946
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9947
    WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9948 9949 9950 9951 9952 9953

    BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
    BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};

    /* Initialize the CompObj structure */
    memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9954 9955
    memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
    memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9956 9957 9958


    /*  Create a CompObj stream if it doesn't exist */
9959
    hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9960 9961 9962 9963 9964 9965 9966 9967
        STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
    if(hStorageRes == S_OK)
    {
        /* copy the OleTypeName to the compobj struct */
        IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
        strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);

        /* copy the OleTypeName to the compobj struct */
9968
        /* Note: in the test made, these were Identical      */
9969 9970 9971 9972
        IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
        strcpy(IStorageCompObj.strProgIDName, strOleTypeName);

        /* Get the CLSID */
9973 9974 9975
        MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
                             bufferW, OLESTREAM_MAX_STR_LEN );
        hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9976

9977
        if(hRes == S_OK)
9978 9979 9980 9981
        {
            HKEY hKey;
            LONG hErr;
            /* Get the CLSID Default Name from the Registry */
9982
            hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9983 9984 9985 9986
            if(hErr == ERROR_SUCCESS)
            {
                char strTemp[OLESTREAM_MAX_STR_LEN];
                IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9987
                hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9988 9989 9990 9991 9992 9993 9994 9995
                if(hErr == ERROR_SUCCESS)
                {
                    strcpy(IStorageCompObj.strCLSIDName, strTemp);
                }
                RegCloseKey(hKey);
            }
        }

9996 9997 9998 9999 10000 10001 10002
        /* Write CompObj Structure to stream */
        hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);

        WriteClassStm(pStream,&(IStorageCompObj.clsid));

        hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
        if(IStorageCompObj.dwCLSIDNameLength > 0)
10003
        {
10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014
            hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
        }
        hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
        if(IStorageCompObj.dwOleTypeNameLength > 0)
        {
            hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
        }
        hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
        if(IStorageCompObj.dwProgIDNameLength > 0)
        {
            hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
10015
        }
10016
        hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
10017 10018 10019 10020 10021 10022 10023
        IStream_Release(pStream);
    }
    return hRes;
}


/*************************************************************************
10024
 * OLECONVERT_CreateOlePresStream[Internal]
10025 10026 10027 10028 10029 10030
 *
 * Creates the "\002OlePres000" Stream with the Metafile data
 *
 * PARAMS
 *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
 *     dwExtentX    [I] Width of the Metafile
10031
 *     dwExtentY    [I] Height of the Metafile
10032 10033 10034 10035 10036 10037 10038 10039 10040
 *     pData        [I] Metafile data
 *     dwDataLength [I] Size of the Metafile data
 *
 * RETURNS
 *     Success:  S_OK
 *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
10041
 *
10042
 */
10043
static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
10044 10045 10046
{
    HRESULT hRes;
    IStream *pStream;
10047
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10048
    BYTE pOlePresStreamHeader [] =
10049
    {
10050 10051
        0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10052 10053 10054 10055
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };

10056
    BYTE pOlePresStreamHeaderEmpty [] =
10057
    {
10058 10059
        0x00, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
10060 10061 10062
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };
10063

10064
    /* Create the OlePres000 Stream */
10065
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088
        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );

    if(hRes == S_OK)
    {
        DWORD nHeaderSize;
        OLECONVERT_ISTORAGE_OLEPRES OlePres;

        memset(&OlePres, 0, sizeof(OlePres));
        /* Do we have any metafile data to save */
        if(dwDataLength > 0)
        {
            memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
            nHeaderSize = sizeof(pOlePresStreamHeader);
        }
        else
        {
            memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
            nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
        }
        /* Set width and height of the metafile */
        OlePres.dwExtentX = dwExtentX;
        OlePres.dwExtentY = -dwExtentY;

Francois Gouget's avatar
Francois Gouget committed
10089
        /* Set Data and Length */
10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108
        if(dwDataLength > sizeof(METAFILEPICT16))
        {
            OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
            OlePres.pData = &(pData[8]);
        }
        /* Save OlePres000 Data to Stream */
        hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
        hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
        hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
        hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
        if(OlePres.dwSize > 0)
        {
            hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
        }
        IStream_Release(pStream);
    }
}

/*************************************************************************
10109
 * OLECONVERT_CreateOle10NativeStream [Internal]
10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124
 *
 * Creates the "\001Ole10Native" Stream (should contain a BMP)
 *
 * PARAMS
 *     pStorage     [I] Dest storage to create the stream in
 *     pData        [I] Ole10 Native Data (ex. bmp)
 *     dwDataLength [I] Size of the Ole10 Native Data
 *
 * RETURNS
 *     Nothing
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
 *
 *     Might need to verify the data and return appropriate error message
10125
 *
10126
 */
10127
static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
10128 10129 10130
{
    HRESULT hRes;
    IStream *pStream;
10131
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10132

10133
    /* Create the Ole10Native Stream */
10134
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147
        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );

    if(hRes == S_OK)
    {
        /* Write info to stream */
        hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
        hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
        IStream_Release(pStream);
    }

}

/*************************************************************************
10148
 * OLECONVERT_GetOLE10ProgID [Internal]
10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163
 *
 * Finds the ProgID (or OleTypeID) from the IStorage
 *
 * PARAMS
 *     pStorage        [I] The Src IStorage to get the ProgID
 *     strProgID       [I] the ProgID string to get
 *     dwSize          [I] the size of the string
 *
 * RETURNS
 *     Success:  S_OK
 *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
 *
 * NOTES
 *     This function is used by OleConvertIStorageToOLESTREAM only.
 *
10164
 *
10165
 */
10166
static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
10167 10168 10169 10170 10171
{
    HRESULT hRes;
    IStream *pStream;
    LARGE_INTEGER iSeekPos;
    OLECONVERT_ISTORAGE_COMPOBJ CompObj;
10172
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10173 10174

    /* Open the CompObj Stream */
10175
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10176 10177 10178 10179 10180
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {

        /*Get the OleType from the CompObj Stream */
10181 10182
        iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
        iSeekPos.u.HighPart = 0;
10183 10184 10185

        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
        IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
10186
        iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
10187 10188
        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
        IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
10189
        iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210
        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);

        IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
        if(*dwSize > 0)
        {
            IStream_Read(pStream, strProgID, *dwSize, NULL);
        }
        IStream_Release(pStream);
    }
    else
    {
        STATSTG stat;
        LPOLESTR wstrProgID;

        /* Get the OleType from the registry */
        REFCLSID clsid = &(stat.clsid);
        IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
        hRes = ProgIDFromCLSID(clsid, &wstrProgID);
        if(hRes == S_OK)
        {
            *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10211
            CoTaskMemFree(wstrProgID);
10212
        }
10213

10214 10215 10216 10217 10218
    }
    return hRes;
}

/*************************************************************************
10219
 * OLECONVERT_GetOle10PresData [Internal]
10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233
 *
 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
 *
 * PARAMS
 *     pStorage     [I] Src IStroage
 *     pOleStream   [I] Dest OleStream Mem Struct
 *
 * RETURNS
 *     Nothing
 *
 * NOTES
 *     This function is used by OleConvertIStorageToOLESTREAM only.
 *
 *     Memory allocated for pData must be freed by the caller
10234 10235
 *
 *
10236
 */
10237
static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10238 10239 10240 10241
{

    HRESULT hRes;
    IStream *pStream;
10242
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254

    /* Initialize Default data for OLESTREAM */
    pOleStreamData[0].dwOleID = OLESTREAM_ID;
    pOleStreamData[0].dwTypeID = 2;
    pOleStreamData[1].dwOleID = OLESTREAM_ID;
    pOleStreamData[1].dwTypeID = 0;
    pOleStreamData[0].dwMetaFileWidth = 0;
    pOleStreamData[0].dwMetaFileHeight = 0;
    pOleStreamData[0].pData = NULL;
    pOleStreamData[1].pData = NULL;

    /* Open Ole10Native Stream */
10255
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10256 10257 10258 10259 10260 10261 10262 10263
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {

        /* Read Size and Data */
        IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
        if(pOleStreamData->dwDataLength > 0)
        {
10264
            pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10265 10266 10267 10268 10269 10270 10271 10272 10273
            IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
        }
        IStream_Release(pStream);
    }

}


/*************************************************************************
10274
 * OLECONVERT_GetOle20PresData[Internal]
10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286
 *
 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
 *
 * PARAMS
 *     pStorage         [I] Src IStroage
 *     pOleStreamData   [I] Dest OleStream Mem Struct
 *
 * RETURNS
 *     Nothing
 *
 * NOTES
 *     This function is used by OleConvertIStorageToOLESTREAM only.
10287
 *
10288 10289
 *     Memory allocated for pData must be freed by the caller
 */
10290
static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10291 10292 10293 10294
{
    HRESULT hRes;
    IStream *pStream;
    OLECONVERT_ISTORAGE_OLEPRES olePress;
10295
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313

    /* Initialize Default data for OLESTREAM */
    pOleStreamData[0].dwOleID = OLESTREAM_ID;
    pOleStreamData[0].dwTypeID = 2;
    pOleStreamData[0].dwMetaFileWidth = 0;
    pOleStreamData[0].dwMetaFileHeight = 0;
    pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
    pOleStreamData[1].dwOleID = OLESTREAM_ID;
    pOleStreamData[1].dwTypeID = 0;
    pOleStreamData[1].dwOleTypeNameLength = 0;
    pOleStreamData[1].strOleTypeName[0] = 0;
    pOleStreamData[1].dwMetaFileWidth = 0;
    pOleStreamData[1].dwMetaFileHeight = 0;
    pOleStreamData[1].pData = NULL;
    pOleStreamData[1].dwDataLength = 0;


    /* Open OlePress000 stream */
10314
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10315 10316 10317 10318 10319
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {
        LARGE_INTEGER iSeekPos;
        METAFILEPICT16 MetaFilePict;
10320
        static const char strMetafilePictName[] = "METAFILEPICT";
10321 10322 10323 10324 10325 10326 10327 10328

        /* Set the TypeID for a Metafile */
        pOleStreamData[1].dwTypeID = 5;

        /* Set the OleTypeName to Metafile */
        pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
        strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);

10329 10330
        iSeekPos.u.HighPart = 0;
        iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352

        /* Get Presentation Data */
        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
        IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
        IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
        IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);

        /*Set width and Height */
        pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
        pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
        if(olePress.dwSize > 0)
        {
            /* Set Length */
            pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);

            /* Set MetaFilePict struct */
            MetaFilePict.mm = 8;
            MetaFilePict.xExt = olePress.dwExtentX;
            MetaFilePict.yExt = olePress.dwExtentY;
            MetaFilePict.hMF = 0;

            /* Get Metafile Data */
10353
            pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10354 10355 10356 10357 10358 10359 10360 10361
            memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
            IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
        }
        IStream_Release(pStream);
    }
}

/*************************************************************************
10362
 * OleConvertOLESTREAMToIStorage [OLE32.@]
10363 10364 10365 10366
 *
 * Read info on MSDN
 *
 * TODO
Austin English's avatar
Austin English committed
10367
 *      DVTARGETDEVICE parameter is not handled
10368 10369 10370
 *      Still unsure of some mem fields for OLE 10 Stream
 *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
 *      and "\001OLE" streams
10371
 *
10372 10373
 */
HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10374 10375
    LPOLESTREAM pOleStream,
    LPSTORAGE pstg,
10376 10377 10378 10379 10380 10381
    const DVTARGETDEVICE* ptd)
{
    int i;
    HRESULT hRes=S_OK;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];

10382 10383
    TRACE("%p %p %p\n", pOleStream, pstg, ptd);

10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398
    memset(pOleStreamData, 0, sizeof(pOleStreamData));

    if(ptd != NULL)
    {
        FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
    }

    if(pstg == NULL || pOleStream == NULL)
    {
        hRes = E_INVALIDARG;
    }

    if(hRes == S_OK)
    {
        /* Load the OLESTREAM to Memory */
Alexandre Julliard's avatar
Alexandre Julliard committed
10399
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10400 10401 10402 10403 10404
    }

    if(hRes == S_OK)
    {
        /* Load the OLESTREAM to Memory (part 2)*/
Alexandre Julliard's avatar
Alexandre Julliard committed
10405
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435
    }

    if(hRes == S_OK)
    {

        if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
        {
            /* Do we have the IStorage Data in the OLESTREAM */
            if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
            {
                OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
                OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
            }
            else
            {
                /* It must be an original OLE 1.0 source */
                OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
            }
        }
        else
        {
            /* It must be an original OLE 1.0 source */
            OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
        }

        /* Create CompObj Stream if necessary */
        hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
        if(hRes == S_OK)
        {
            /*Create the Ole Stream if necessary */
10436
            STORAGE_CreateOleStream(pstg, 0);
10437 10438 10439 10440 10441 10442 10443
        }
    }


    /* Free allocated memory */
    for(i=0; i < 2; i++)
    {
10444 10445 10446
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
        pOleStreamData[i].pstrOleObjFileName = NULL;
10447 10448 10449 10450 10451
    }
    return hRes;
}

/*************************************************************************
10452
 * OleConvertIStorageToOLESTREAM [OLE32.@]
10453 10454 10455 10456 10457 10458 10459 10460 10461
 *
 * Read info on MSDN
 *
 * Read info on MSDN
 *
 * TODO
 *      Still unsure of some mem fields for OLE 10 Stream
 *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
 *      and "\001OLE" streams.
10462
 *
10463 10464
 */
HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10465
    LPSTORAGE pstg,
10466 10467 10468 10469 10470 10471
    LPOLESTREAM pOleStream)
{
    int i;
    HRESULT hRes = S_OK;
    IStream *pStream;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10472
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10473

10474
    TRACE("%p %p\n", pstg, pOleStream);
10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489

    memset(pOleStreamData, 0, sizeof(pOleStreamData));

    if(pstg == NULL || pOleStream == NULL)
    {
        hRes = E_INVALIDARG;
    }
    if(hRes == S_OK)
    {
        /* Get the ProgID */
        pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
        hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
    }
    if(hRes == S_OK)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
10490
        /* Was it originally Ole10 */
10491
        hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10492 10493 10494
        if(hRes == S_OK)
        {
            IStream_Release(pStream);
Alexandre Julliard's avatar
Alexandre Julliard committed
10495
            /* Get Presentation Data for Ole10Native */
10496 10497 10498 10499
            OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
        }
        else
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
10500
            /* Get Presentation Data (OLE20) */
10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515
            OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
        }

        /* Save OLESTREAM */
        hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
        if(hRes == S_OK)
        {
            hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
        }

    }

    /* Free allocated memory */
    for(i=0; i < 2; i++)
    {
10516
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10517 10518 10519 10520
    }

    return hRes;
}
10521

10522 10523
enum stream_1ole_flags {
    OleStream_LinkedObject = 0x00000001,
10524
    OleStream_Convert      = 0x00000004
10525 10526
};

10527
/***********************************************************************
10528
 *		GetConvertStg (OLE32.@)
10529
 */
10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544
HRESULT WINAPI GetConvertStg(IStorage *stg)
{
    static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
    static const DWORD version_magic = 0x02000001;
    DWORD header[2];
    IStream *stream;
    HRESULT hr;

    TRACE("%p\n", stg);

    if (!stg) return E_INVALIDARG;

    hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
    if (FAILED(hr)) return hr;

10545
    hr = IStream_Read(stream, header, sizeof(header), NULL);
10546 10547 10548 10549 10550
    IStream_Release(stream);
    if (FAILED(hr)) return hr;

    if (header[0] != version_magic)
    {
10551
        ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10552 10553 10554 10555
        return E_FAIL;
    }

    return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10556
}
10557

10558 10559 10560 10561 10562
/***********************************************************************
 *		SetConvertStg (OLE32.@)
 */
HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
{
10563
    static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10564
    DWORD flags = convert ? OleStream_Convert : 0;
10565 10566
    IStream *stream;
    DWORD header[2];
10567 10568 10569 10570
    HRESULT hr;

    TRACE("(%p, %d)\n", storage, convert);

10571 10572
    hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
    if (FAILED(hr))
10573
    {
10574 10575 10576 10577 10578
        if (hr != STG_E_FILENOTFOUND)
            return hr;

        return STORAGE_CreateOleStream(storage, flags);
    }
10579

10580 10581 10582 10583 10584 10585
    hr = IStream_Read(stream, header, sizeof(header), NULL);
    if (FAILED(hr))
    {
        IStream_Release(stream);
        return hr;
    }
10586

10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598
    /* update flag if differs */
    if ((header[1] ^ flags) & OleStream_Convert)
    {
        LARGE_INTEGER pos = {{0}};

        if (header[1] & OleStream_Convert)
            flags = header[1] & ~OleStream_Convert;
        else
            flags = header[1] |  OleStream_Convert;

        pos.QuadPart = sizeof(DWORD);
        hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10599 10600 10601 10602 10603 10604
        if (FAILED(hr))
        {
            IStream_Release(stream);
            return hr;
        }

10605
        hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10606 10607
    }

10608
    IStream_Release(stream);
10609 10610
    return hr;
}