storage32.c 256 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 41
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
42

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

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

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

56
WINE_DEFAULT_DEBUG_CHANNEL(storage);
57

58 59 60 61
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
#define OLESTREAM_ID 0x501
#define OLESTREAM_MAX_STR_LEN 255

62 63 64 65 66 67
/*
 * 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};

68 69 70 71 72
static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
{
    return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
}

73 74 75 76 77 78 79 80 81 82
/****************************************************************************
 * Storage32InternalImpl definitions.
 *
 * Definition of the implementation structure for the IStorage32 interface.
 * This one implements the IStorage32 interface for storage that are
 * inside another storage.
 */
struct StorageInternalImpl
{
  struct StorageBaseImpl base;
83

84
  /*
85
   * Entry in the parent's stream tracking list
86
   */
87
  struct list ParentListEntry;
88 89

  StorageBaseImpl *parentStorage;
90 91 92
};
typedef struct StorageInternalImpl StorageInternalImpl;

93 94 95
static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
static const IStorageVtbl Storage32InternalImpl_Vtbl;

96
/* Method definitions for the Storage32InternalImpl class. */
97
static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
98
                                                          DWORD openFlags, DirRef storageDirEntry);
99
static void StorageImpl_Destroy(StorageBaseImpl* iface);
100
static void StorageImpl_Invalidate(StorageBaseImpl* iface);
101
static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
102
static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
103
static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
static void StorageImpl_SaveFileHeader(StorageImpl* This);

static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);

static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
static ULONG BlockChainStream_GetCount(BlockChainStream* This);

static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
119
static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
120 121 122 123
static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
    ULONG blockIndex, ULONG offset, DWORD value);
static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl*  This,
    ULONG blockIndex, ULONG offset, DWORD* value);
124

125
static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
126
static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
127

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
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. */
  int inuse;

  /* True if data is up to date. */
  int read;

  /* True if this entry has been modified. */
  int dirty;

  /* True if this entry's stream has been modified. */
  int stream_dirty;

  /* True if this entry has been deleted in the transacted storage, but the
   * delete has not yet been committed. */
  int deleted;

  /* 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;

166
/****************************************************************************
167
 * Transacted storage object.
168 169 170 171 172
 */
typedef struct TransactedSnapshotImpl
{
  struct StorageBaseImpl base;

173
  /*
174
   * Modified streams are temporarily saved to the scratch file.
175
   */
176 177 178 179 180 181 182
  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;
183

184 185 186 187 188 189 190 191 192
  /*
   * Changes are committed to the transacted parent.
   */
  StorageBaseImpl *transactedParent;
} TransactedSnapshotImpl;

/* Generic function to create a transacted wrapper for a direct storage object. */
static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);

193 194
/* OLESTREAM memory structure to use for Get and Put Routines */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
195
typedef struct
196 197 198 199 200
{
    DWORD dwOleID;
    DWORD dwTypeID;
    DWORD dwOleTypeNameLength;
    CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
Alexandre Julliard's avatar
Alexandre Julliard committed
201 202
    CHAR  *pstrOleObjFileName;
    DWORD dwOleObjFileNameLength;
203 204
    DWORD dwMetaFileWidth;
    DWORD dwMetaFileHeight;
Austin English's avatar
Austin English committed
205
    CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
    DWORD dwDataLength;
    BYTE *pData;
}OLECONVERT_OLESTREAM_DATA;

/* CompObj Stream structure */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
    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;


Austin English's avatar
Austin English committed
226
/* Ole Presentation Stream structure */
227 228 229 230 231 232
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
    BYTE byUnknown1[28];
    DWORD dwExtentX;
    DWORD dwExtentY;
233
    DWORD dwSize;
234 235 236 237 238
    BYTE *pData;
}OLECONVERT_ISTORAGE_OLEPRES;



239 240 241
/***********************************************************************
 * Forward declaration of internal functions used by the method DestroyElement
 */
242
static HRESULT deleteStorageContents(
243
  StorageBaseImpl *parentStorage,
244
  DirRef       indexToDelete,
245
  DirEntry     entryDataToDelete);
246

247
static HRESULT deleteStreamContents(
248
  StorageBaseImpl *parentStorage,
249
  DirRef        indexToDelete,
250
  DirEntry      entryDataToDelete);
251

252
static HRESULT removeFromTree(
253
  StorageBaseImpl *This,
254 255
  DirRef        parentStorageIndex,
  DirRef        deletedIndex);
256 257

/***********************************************************************
258
 * Declaration of the functions used to manipulate DirEntry
259 260
 */

261
static HRESULT insertIntoTree(
262
  StorageBaseImpl *This,
263 264
  DirRef        parentStorageIndex,
  DirRef        newEntryIndex);
265

266 267 268
static LONG entryNameCmp(
    const OLECHAR *name1,
    const OLECHAR *name2);
269

270
static DirRef findElement(
271
    StorageBaseImpl *storage,
272
    DirRef storageEntry,
273
    const OLECHAR *name,
274
    DirEntry *data);
275

276
static HRESULT findTreeParent(
277
    StorageBaseImpl *storage,
278
    DirRef storageEntry,
279
    const OLECHAR *childName,
280
    DirEntry *parentData,
281
    DirRef *parentEntry,
282 283
    ULONG *relation);

284 285 286
/***********************************************************************
 * Declaration of miscellaneous functions...
 */
287
static HRESULT validateSTGM(DWORD stgmValue);
288 289 290 291 292

static DWORD GetShareModeFromSTGM(DWORD stgm);
static DWORD GetAccessModeFromSTGM(DWORD stgm);
static DWORD GetCreationModeFromSTGM(DWORD stgm);

293
extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
294 295


296 297 298 299 300 301 302 303 304
/****************************************************************************
 * IEnumSTATSTGImpl definitions.
 *
 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
 * This class allows iterating through the content of a storage and to find
 * specific items inside it.
 */
struct IEnumSTATSTGImpl
{
305
  IEnumSTATSTG   IEnumSTATSTG_iface;
306 307

  LONG           ref;                   /* Reference count */
308
  StorageBaseImpl* parentStorage;         /* Reference to the parent storage */
309
  DirRef         storageDirEntry;     /* Directory entry of the storage to enumerate */
310

311
  WCHAR	         name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
312 313
};

314 315 316 317 318
static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
{
  return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
}

319

320
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
321 322
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);

323 324 325 326
/************************************************************************
** Block Functions
*/

327
static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
328
{
329
    return (index+1) * This->bigBlockSize;
330
}
331

332
/************************************************************************
Austin English's avatar
Austin English committed
333
** Storage32BaseImpl implementation
334
*/
335 336 337 338 339 340
static HRESULT StorageImpl_ReadAt(StorageImpl* This,
  ULARGE_INTEGER offset,
  void*          buffer,
  ULONG          size,
  ULONG*         bytesRead)
{
341
    return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
342 343 344 345
}

static HRESULT StorageImpl_WriteAt(StorageImpl* This,
  ULARGE_INTEGER offset,
346
  const void*    buffer,
347 348 349
  const ULONG    size,
  ULONG*         bytesWritten)
{
350
    return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
351
}
352 353 354 355 356 357

/************************************************************************
 * Storage32BaseImpl_QueryInterface (IUnknown)
 *
 * This method implements the common QueryInterface for all IStorage32
 * implementations contained in this file.
358
 *
359 360
 * See Windows documentation for more details on IUnknown methods.
 */
361
static HRESULT WINAPI StorageBaseImpl_QueryInterface(
362
  IStorage*        iface,
363 364 365
  REFIID             riid,
  void**             ppvObject)
{
366
  StorageBaseImpl *This = impl_from_IStorage(iface);
367

368
  if (!ppvObject)
369
    return E_INVALIDARG;
370

371
  *ppvObject = 0;
372

373 374
  if (IsEqualGUID(&IID_IUnknown, riid) ||
      IsEqualGUID(&IID_IStorage, riid))
375
  {
376
    *ppvObject = &This->IStorage_iface;
377
  }
378
  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
379
  {
380
    *ppvObject = &This->IPropertySetStorage_iface;
381
  }
382
  else
383
    return E_NOINTERFACE;
384

385
  IStorage_AddRef(iface);
386

387
  return S_OK;
388
}
389

390 391 392 393 394
/************************************************************************
 * Storage32BaseImpl_AddRef (IUnknown)
 *
 * This method implements the common AddRef for all IStorage32
 * implementations contained in this file.
395
 *
396 397
 * See Windows documentation for more details on IUnknown methods.
 */
398
static ULONG WINAPI StorageBaseImpl_AddRef(
399
            IStorage* iface)
400
{
401
  StorageBaseImpl *This = impl_from_IStorage(iface);
402 403
  ULONG ref = InterlockedIncrement(&This->ref);

404
  TRACE("(%p) AddRef to %d\n", This, ref);
405 406

  return ref;
407
}
408

409 410 411 412 413
/************************************************************************
 * Storage32BaseImpl_Release (IUnknown)
 *
 * This method implements the common Release for all IStorage32
 * implementations contained in this file.
414
 *
415 416
 * See Windows documentation for more details on IUnknown methods.
 */
417
static ULONG WINAPI StorageBaseImpl_Release(
418
      IStorage* iface)
419
{
420
  StorageBaseImpl *This = impl_from_IStorage(iface);
421

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

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

426
  if (ref == 0)
427 428
  {
    /*
429 430
     * 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
431 432
     * using virtual functions to implement the destructor.
     */
433
    StorageBaseImpl_Destroy(This);
434
  }
435

436
  return ref;
437 438 439 440 441 442 443 444 445
}

/************************************************************************
 * Storage32BaseImpl_OpenStream (IStorage)
 *
 * This method will open the specified stream object from the current storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
446
static HRESULT WINAPI StorageBaseImpl_OpenStream(
447 448
  IStorage*        iface,
  const OLECHAR*   pwcsName,  /* [string][in] */
449
  void*            reserved1, /* [unique][in] */
450 451 452
  DWORD            grfMode,   /* [in]  */
  DWORD            reserved2, /* [in]  */
  IStream**        ppstm)     /* [out] */
453
{
454
  StorageBaseImpl *This = impl_from_IStorage(iface);
455
  StgStreamImpl*    newStream;
456
  DirEntry          currentEntry;
457
  DirRef            streamEntryRef;
Alexandre Julliard's avatar
Alexandre Julliard committed
458
  HRESULT           res = STG_E_UNKNOWN;
459

460
  TRACE("(%p, %s, %p, %x, %d, %p)\n",
461 462
	iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);

463
  if ( (pwcsName==NULL) || (ppstm==0) )
Alexandre Julliard's avatar
Alexandre Julliard committed
464 465 466 467
  {
    res = E_INVALIDARG;
    goto end;
  }
468

Alexandre Julliard's avatar
Alexandre Julliard committed
469
  *ppstm = NULL;
470

471 472
  if ( FAILED( validateSTGM(grfMode) ) ||
       STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
Alexandre Julliard's avatar
Alexandre Julliard committed
473 474 475 476
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
477 478 479 480

  /*
   * As documented.
   */
481
  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
Alexandre Julliard's avatar
Alexandre Julliard committed
482 483 484 485
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
486

487
  if (This->reverted)
488 489 490 491 492
  {
    res = STG_E_REVERTED;
    goto end;
  }

493
  /*
494 495
   * Check that we're compatible with the parent's storage mode, but
   * only if we are not in transacted mode
496
   */
497
  if(!(This->openFlags & STGM_TRANSACTED)) {
498
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
499
    {
500
      res = STG_E_INVALIDFLAG;
501 502
      goto end;
    }
503 504
  }

505
  /*
506
   * Search for the element with the given name
507
   */
508
  streamEntryRef = findElement(
509
    This,
510
    This->storageDirEntry,
511
    pwcsName,
512
    &currentEntry);
513

514 515 516
  /*
   * If it was found, construct the stream object and return a pointer to it.
   */
517 518
  if ( (streamEntryRef!=DIRENTRY_NULL) &&
       (currentEntry.stgType==STGTY_STREAM) )
519
  {
520 521 522 523 524 525 526
    if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
    {
      /* A single stream cannot be opened a second time. */
      res = STG_E_ACCESSDENIED;
      goto end;
    }

527
    newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
528

529
    if (newStream)
530
    {
531
      newStream->grfMode = grfMode;
532
      *ppstm = &newStream->IStream_iface;
533

534
      IStream_AddRef(*ppstm);
535

Alexandre Julliard's avatar
Alexandre Julliard committed
536 537
      res = S_OK;
      goto end;
538
    }
539

Alexandre Julliard's avatar
Alexandre Julliard committed
540 541
    res = E_OUTOFMEMORY;
    goto end;
542
  }
543

Alexandre Julliard's avatar
Alexandre Julliard committed
544 545 546 547 548
  res = STG_E_FILENOTFOUND;

end:
  if (res == S_OK)
    TRACE("<-- IStream %p\n", *ppstm);
549
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
550
  return res;
551 552 553 554 555 556
}

/************************************************************************
 * Storage32BaseImpl_OpenStorage (IStorage)
 *
 * This method will open a new storage object from the current storage.
557
 *
558
 * See Windows documentation for more details on IStorage methods.
559
 */
560
static HRESULT WINAPI StorageBaseImpl_OpenStorage(
561
  IStorage*        iface,
562 563 564 565 566 567
  const OLECHAR*   pwcsName,      /* [string][unique][in] */
  IStorage*        pstgPriority,  /* [unique][in] */
  DWORD            grfMode,       /* [in] */
  SNB              snbExclude,    /* [unique][in] */
  DWORD            reserved,      /* [in] */
  IStorage**       ppstg)         /* [out] */
568
{
569
  StorageBaseImpl *This = impl_from_IStorage(iface);
570 571
  StorageInternalImpl*   newStorage;
  StorageBaseImpl*       newTransactedStorage;
572
  DirEntry               currentEntry;
573
  DirRef                 storageEntryRef;
Alexandre Julliard's avatar
Alexandre Julliard committed
574
  HRESULT                res = STG_E_UNKNOWN;
575

576
  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
577
	iface, debugstr_w(pwcsName), pstgPriority,
578
	grfMode, snbExclude, reserved, ppstg);
579

580
  if ((pwcsName==NULL) || (ppstg==0) )
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582 583 584
  {
    res = E_INVALIDARG;
    goto end;
  }
585

586 587 588 589 590 591
  if (This->openFlags & STGM_SIMPLE)
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }

Alexandre Julliard's avatar
Alexandre Julliard committed
592 593 594 595 596 597
  /* as documented */
  if (snbExclude != NULL)
  {
    res = STG_E_INVALIDPARAMETER;
    goto end;
  }
598

599
  if ( FAILED( validateSTGM(grfMode) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
600 601 602 603
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
604 605 606 607

  /*
   * As documented.
   */
608
  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
609 610
        (grfMode & STGM_DELETEONRELEASE) ||
        (grfMode & STGM_PRIORITY) )
Alexandre Julliard's avatar
Alexandre Julliard committed
611 612 613 614
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
615

616
  if (This->reverted)
617 618
    return STG_E_REVERTED;

619
  /*
620 621
   * Check that we're compatible with the parent's storage mode,
   * but only if we are not transacted
622
   */
623
  if(!(This->openFlags & STGM_TRANSACTED)) {
624
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
625 626 627 628
    {
      res = STG_E_ACCESSDENIED;
      goto end;
    }
629 630
  }

Alexandre Julliard's avatar
Alexandre Julliard committed
631
  *ppstg = NULL;
632

633
  storageEntryRef = findElement(
634
                         This,
635
                         This->storageDirEntry,
636
                         pwcsName,
637
                         &currentEntry);
638

639 640
  if ( (storageEntryRef!=DIRENTRY_NULL) &&
       (currentEntry.stgType==STGTY_STORAGE) )
641
  {
642 643 644 645 646 647 648
    if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
    {
      /* A single storage cannot be opened a second time. */
      res = STG_E_ACCESSDENIED;
      goto end;
    }

649
    newStorage = StorageInternalImpl_Construct(
650
                   This,
651
                   grfMode,
652
                   storageEntryRef);
653

654 655
    if (newStorage != 0)
    {
656 657 658 659 660 661 662 663 664 665
      if (grfMode & STGM_TRANSACTED)
      {
        res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);

        if (FAILED(res))
        {
          HeapFree(GetProcessHeap(), 0, newStorage);
          goto end;
        }

666
        *ppstg = &newTransactedStorage->IStorage_iface;
667 668 669
      }
      else
      {
670
        *ppstg = &newStorage->base.IStorage_iface;
671
      }
672

673 674
      list_add_tail(&This->storageHead, &newStorage->ParentListEntry);

Alexandre Julliard's avatar
Alexandre Julliard committed
675 676
      res = S_OK;
      goto end;
677
    }
678

Alexandre Julliard's avatar
Alexandre Julliard committed
679 680
    res = STG_E_INSUFFICIENTMEMORY;
    goto end;
681
  }
682

Alexandre Julliard's avatar
Alexandre Julliard committed
683 684 685
  res = STG_E_FILENOTFOUND;

end:
686
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
687
  return res;
688 689 690 691 692
}

/************************************************************************
 * Storage32BaseImpl_EnumElements (IStorage)
 *
693
 * This method will create an enumerator object that can be used to
694
 * retrieve information about all the elements in the storage object.
695
 *
696
 * See Windows documentation for more details on IStorage methods.
697
 */
698
static HRESULT WINAPI StorageBaseImpl_EnumElements(
699
  IStorage*       iface,
700 701 702 703
  DWORD           reserved1, /* [in] */
  void*           reserved2, /* [size_is][unique][in] */
  DWORD           reserved3, /* [in] */
  IEnumSTATSTG**  ppenum)    /* [out] */
704
{
705
  StorageBaseImpl *This = impl_from_IStorage(iface);
706 707
  IEnumSTATSTGImpl* newEnum;

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

711
  if (!ppenum)
712
    return E_INVALIDARG;
713

714
  if (This->reverted)
715 716
    return STG_E_REVERTED;

717
  newEnum = IEnumSTATSTGImpl_Construct(
718
              This,
719
              This->storageDirEntry);
720

721
  if (newEnum)
722
  {
723
    *ppenum = &newEnum->IEnumSTATSTG_iface;
724

725
    IEnumSTATSTG_AddRef(*ppenum);
726

727 728 729 730 731 732 733 734 735 736
    return S_OK;
  }

  return E_OUTOFMEMORY;
}

/************************************************************************
 * Storage32BaseImpl_Stat (IStorage)
 *
 * This method will retrieve information about this storage object.
737
 *
738
 * See Windows documentation for more details on IStorage methods.
739
 */
740
static HRESULT WINAPI StorageBaseImpl_Stat(
741
  IStorage*        iface,
742 743
  STATSTG*         pstatstg,     /* [out] */
  DWORD            grfStatFlag)  /* [in] */
744
{
745
  StorageBaseImpl *This = impl_from_IStorage(iface);
746
  DirEntry       currentEntry;
Alexandre Julliard's avatar
Alexandre Julliard committed
747
  HRESULT        res = STG_E_UNKNOWN;
748

749
  TRACE("(%p, %p, %x)\n",
750 751
	iface, pstatstg, grfStatFlag);

752
  if (!pstatstg)
Alexandre Julliard's avatar
Alexandre Julliard committed
753 754 755 756
  {
    res = E_INVALIDARG;
    goto end;
  }
757

758
  if (This->reverted)
759 760 761 762 763
  {
    res = STG_E_REVERTED;
    goto end;
  }

764 765
  res = StorageBaseImpl_ReadDirEntry(
                    This,
766
                    This->storageDirEntry,
767
                    &currentEntry);
768

769
  if (SUCCEEDED(res))
770
  {
771
    StorageUtl_CopyDirEntryToSTATSTG(
772
      This,
773
      pstatstg,
774
      &currentEntry,
775
      grfStatFlag);
776

777
    pstatstg->grfMode = This->openFlags;
778
    pstatstg->grfStateBits = This->stateBits;
779
  }
780

Alexandre Julliard's avatar
Alexandre Julliard committed
781 782 783
end:
  if (res == S_OK)
  {
784
    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);
Alexandre Julliard's avatar
Alexandre Julliard committed
785
  }
786
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
787
  return res;
788 789 790 791 792
}

/************************************************************************
 * Storage32BaseImpl_RenameElement (IStorage)
 *
793
 * This method will rename the specified element.
794 795 796
 *
 * See Windows documentation for more details on IStorage methods.
 */
797
static HRESULT WINAPI StorageBaseImpl_RenameElement(
798 799 800
            IStorage*        iface,
            const OLECHAR*   pwcsOldName,  /* [in] */
            const OLECHAR*   pwcsNewName)  /* [in] */
801
{
802
  StorageBaseImpl *This = impl_from_IStorage(iface);
803
  DirEntry          currentEntry;
804
  DirRef            currentEntryRef;
805

806
  TRACE("(%p, %s, %s)\n",
807 808
	iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));

809
  if (This->reverted)
810 811
    return STG_E_REVERTED;

812
  currentEntryRef = findElement(This,
813
                                   This->storageDirEntry,
814
                                   pwcsNewName,
815
                                   &currentEntry);
816

817
  if (currentEntryRef != DIRENTRY_NULL)
818 819
  {
    /*
820
     * There is already an element with the new name
821 822 823 824 825
     */
    return STG_E_FILEALREADYEXISTS;
  }

  /*
826
   * Search for the old element name
827
   */
828
  currentEntryRef = findElement(This,
829
                                   This->storageDirEntry,
830
                                   pwcsOldName,
831
                                   &currentEntry);
832

833
  if (currentEntryRef != DIRENTRY_NULL)
834
  {
835 836
    if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
        StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
837
    {
838
      WARN("Element is already open; cannot rename.\n");
839 840 841
      return STG_E_ACCESSDENIED;
    }

842
    /* Remove the element from its current position in the tree */
843
    removeFromTree(This, This->storageDirEntry,
844
        currentEntryRef);
845

846
    /* Change the name of the element */
847
    strcpyW(currentEntry.name, pwcsNewName);
848

849 850 851 852
    /* Delete any sibling links */
    currentEntry.leftChild = DIRENTRY_NULL;
    currentEntry.rightChild = DIRENTRY_NULL;

853
    StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
854
        &currentEntry);
855

856
    /* Insert the element in a new position in the tree */
857
    insertIntoTree(This, This->storageDirEntry,
858
        currentEntryRef);
859 860 861 862
  }
  else
  {
    /*
863
     * There is no element with the old name
864 865 866 867
     */
    return STG_E_FILENOTFOUND;
  }

868
  return StorageBaseImpl_Flush(This);
869 870 871 872 873
}

/************************************************************************
 * Storage32BaseImpl_CreateStream (IStorage)
 *
874
 * This method will create a stream object within this storage
875 876 877
 *
 * See Windows documentation for more details on IStorage methods.
 */
878
static HRESULT WINAPI StorageBaseImpl_CreateStream(
879 880
            IStorage*        iface,
            const OLECHAR*   pwcsName,  /* [string][in] */
881 882 883
            DWORD            grfMode,   /* [in] */
            DWORD            reserved1, /* [in] */
            DWORD            reserved2, /* [in] */
884
            IStream**        ppstm)     /* [out] */
885
{
886
  StorageBaseImpl *This = impl_from_IStorage(iface);
887
  StgStreamImpl*    newStream;
888
  DirEntry          currentEntry, newStreamEntry;
889
  DirRef            currentEntryRef, newStreamEntryRef;
890
  HRESULT hr;
891

892
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
893
	iface, debugstr_w(pwcsName), grfMode,
894 895
	reserved1, reserved2, ppstm);

896 897 898 899 900 901
  if (ppstm == 0)
    return STG_E_INVALIDPOINTER;

  if (pwcsName == 0)
    return STG_E_INVALIDNAME;

902 903 904
  if (reserved1 || reserved2)
    return STG_E_INVALIDPARAMETER;

905 906 907
  if ( FAILED( validateSTGM(grfMode) ))
    return STG_E_INVALIDFLAG;

908
  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
909 910
    return STG_E_INVALIDFLAG;

911
  if (This->reverted)
912 913
    return STG_E_REVERTED;

914 915 916
  /*
   * As documented.
   */
917 918
  if ((grfMode & STGM_DELETEONRELEASE) ||
      (grfMode & STGM_TRANSACTED))
919 920
    return STG_E_INVALIDFUNCTION;

921
  /*
922 923
   * Don't worry about permissions in transacted mode, as we can always write
   * changes; we just can't always commit them.
924
   */
925
  if(!(This->openFlags & STGM_TRANSACTED)) {
926 927 928 929 930
    /* Can't create a stream on read-only storage */
    if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
      return STG_E_ACCESSDENIED;

    /* Can't create a stream with greater access than the parent. */
931
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
932 933
      return STG_E_ACCESSDENIED;
  }
934

935
  if(This->openFlags & STGM_SIMPLE)
936 937
    if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;

938 939
  *ppstm = 0;

940
  currentEntryRef = findElement(This,
941
                                   This->storageDirEntry,
942
                                   pwcsName,
943
                                   &currentEntry);
944

945
  if (currentEntryRef != DIRENTRY_NULL)
946 947
  {
    /*
948
     * An element with this name already exists
949
     */
950
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
951
    {
952
      IStorage_DestroyElement(iface, pwcsName);
953
    }
954
    else
955 956 957
      return STG_E_FILEALREADYEXISTS;
  }

958
  /*
959
   * memset the empty entry
960
   */
961
  memset(&newStreamEntry, 0, sizeof(DirEntry));
962

963
  newStreamEntry.sizeOfNameString =
964
      ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
965

966
  if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
967 968
    return STG_E_INVALIDNAME;

969
  strcpyW(newStreamEntry.name, pwcsName);
970

971 972 973 974
  newStreamEntry.stgType       = STGTY_STREAM;
  newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
  newStreamEntry.size.u.LowPart  = 0;
  newStreamEntry.size.u.HighPart = 0;
975

976 977 978
  newStreamEntry.leftChild        = DIRENTRY_NULL;
  newStreamEntry.rightChild       = DIRENTRY_NULL;
  newStreamEntry.dirRootEntry     = DIRENTRY_NULL;
979

980
  /* call CoFileTime to get the current time
981 982
  newStreamEntry.ctime
  newStreamEntry.mtime
983 984
  */

985
  /*  newStreamEntry.clsid */
986 987

  /*
988
   * Create an entry with the new data
989
   */
990 991 992 993
  hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
  if (FAILED(hr))
    return hr;

994
  /*
995
   * Insert the new entry in the parent storage's tree.
996
   */
997
  hr = insertIntoTree(
998
    This,
999
    This->storageDirEntry,
1000
    newStreamEntryRef);
1001 1002 1003 1004 1005
  if (FAILED(hr))
  {
    StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
    return hr;
  }
1006

1007
  /*
1008 1009
   * Open the stream to return it.
   */
1010
  newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1011

1012
  if (newStream)
1013
  {
1014
    *ppstm = &newStream->IStream_iface;
1015
    IStream_AddRef(*ppstm);
1016 1017 1018 1019 1020 1021
  }
  else
  {
    return STG_E_INSUFFICIENTMEMORY;
  }

1022
  return StorageBaseImpl_Flush(This);
1023 1024 1025 1026 1027
}

/************************************************************************
 * Storage32BaseImpl_SetClass (IStorage)
 *
1028
 * This method will write the specified CLSID in the directory entry of this
1029 1030 1031 1032
 * storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
1033
static HRESULT WINAPI StorageBaseImpl_SetClass(
1034
  IStorage*        iface,
1035
  REFCLSID         clsid) /* [in] */
1036
{
1037
  StorageBaseImpl *This = impl_from_IStorage(iface);
1038
  HRESULT hRes;
1039
  DirEntry currentEntry;
1040

1041
  TRACE("(%p, %p)\n", iface, clsid);
1042

1043
  if (This->reverted)
1044 1045
    return STG_E_REVERTED;

1046 1047 1048
  hRes = StorageBaseImpl_ReadDirEntry(This,
                                      This->storageDirEntry,
                                      &currentEntry);
1049
  if (SUCCEEDED(hRes))
1050
  {
1051
    currentEntry.clsid = *clsid;
1052

1053 1054 1055
    hRes = StorageBaseImpl_WriteDirEntry(This,
                                         This->storageDirEntry,
                                         &currentEntry);
1056 1057
  }

1058 1059 1060
  if (SUCCEEDED(hRes))
    hRes = StorageBaseImpl_Flush(This);

1061 1062 1063 1064 1065 1066
  return hRes;
}

/************************************************************************
** Storage32Impl implementation
*/
1067

1068
/************************************************************************
1069
 * Storage32BaseImpl_CreateStorage (IStorage)
1070 1071 1072 1073 1074
 *
 * This method will create the storage object within the provided storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
1075
static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1076
  IStorage*      iface,
1077 1078 1079 1080 1081
  const OLECHAR  *pwcsName, /* [string][in] */
  DWORD            grfMode,   /* [in] */
  DWORD            reserved1, /* [in] */
  DWORD            reserved2, /* [in] */
  IStorage       **ppstg)   /* [out] */
1082
{
1083
  StorageBaseImpl* This = impl_from_IStorage(iface);
1084

1085 1086
  DirEntry         currentEntry;
  DirEntry         newEntry;
1087 1088
  DirRef           currentEntryRef;
  DirRef           newEntryRef;
1089 1090
  HRESULT          hr;

1091
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
1092
	iface, debugstr_w(pwcsName), grfMode,
1093
	reserved1, reserved2, ppstg);
1094

1095 1096 1097
  if (ppstg == 0)
    return STG_E_INVALIDPOINTER;

1098 1099 1100 1101 1102
  if (This->openFlags & STGM_SIMPLE)
  {
    return STG_E_INVALIDFUNCTION;
  }

1103 1104 1105
  if (pwcsName == 0)
    return STG_E_INVALIDNAME;

1106 1107
  *ppstg = NULL;

1108 1109
  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode & STGM_DELETEONRELEASE) )
1110
  {
1111
    WARN("bad grfMode: 0x%x\n", grfMode);
1112
    return STG_E_INVALIDFLAG;
1113
  }
1114

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

1118 1119 1120
  /*
   * Check that we're compatible with the parent's storage mode
   */
1121 1122
  if ( !(This->openFlags & STGM_TRANSACTED) &&
       STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1123 1124
  {
    WARN("access denied\n");
1125
    return STG_E_ACCESSDENIED;
1126
  }
1127

1128
  currentEntryRef = findElement(This,
1129
                                   This->storageDirEntry,
1130
                                   pwcsName,
1131
                                   &currentEntry);
1132

1133
  if (currentEntryRef != DIRENTRY_NULL)
1134 1135
  {
    /*
1136
     * An element with this name already exists
1137
     */
1138
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1139 1140
        ((This->openFlags & STGM_TRANSACTED) ||
         STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1141 1142 1143 1144 1145
    {
      hr = IStorage_DestroyElement(iface, pwcsName);
      if (FAILED(hr))
        return hr;
    }
1146
    else
1147 1148
    {
      WARN("file already exists\n");
1149
      return STG_E_FILEALREADYEXISTS;
1150
    }
1151
  }
1152 1153
  else if (!(This->openFlags & STGM_TRANSACTED) &&
           STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1154 1155 1156 1157
  {
    WARN("read-only storage\n");
    return STG_E_ACCESSDENIED;
  }
1158

1159
  memset(&newEntry, 0, sizeof(DirEntry));
1160

1161
  newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1162

1163
  if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1164 1165
  {
    FIXME("name too long\n");
1166
    return STG_E_INVALIDNAME;
1167
  }
1168

1169
  strcpyW(newEntry.name, pwcsName);
1170

1171 1172 1173 1174
  newEntry.stgType       = STGTY_STORAGE;
  newEntry.startingBlock = BLOCK_END_OF_CHAIN;
  newEntry.size.u.LowPart  = 0;
  newEntry.size.u.HighPart = 0;
1175

1176 1177 1178
  newEntry.leftChild        = DIRENTRY_NULL;
  newEntry.rightChild       = DIRENTRY_NULL;
  newEntry.dirRootEntry     = DIRENTRY_NULL;
1179

1180
  /* call CoFileTime to get the current time
1181 1182
  newEntry.ctime
  newEntry.mtime
1183 1184
  */

1185
  /*  newEntry.clsid */
1186

1187
  /*
1188
   * Create a new directory entry for the storage
1189
   */
1190 1191 1192
  hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
  if (FAILED(hr))
    return hr;
1193

1194
  /*
1195
   * Insert the new directory entry into the parent storage's tree
1196
   */
1197
  hr = insertIntoTree(
1198
    This,
1199
    This->storageDirEntry,
1200
    newEntryRef);
1201 1202 1203 1204 1205
  if (FAILED(hr))
  {
    StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
    return hr;
  }
1206

1207
  /*
1208 1209
   * Open it to get a pointer to return.
   */
1210
  hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1211 1212 1213 1214 1215 1216

  if( (hr != S_OK) || (*ppstg == NULL))
  {
    return hr;
  }

1217 1218
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_Flush(This);
1219

1220 1221 1222 1223 1224 1225 1226 1227
  return S_OK;
}


/***************************************************************************
 *
 * Internal Method
 *
1228
 * Reserve a directory entry in the file and initialize it.
1229
 */
1230 1231
static HRESULT StorageImpl_CreateDirEntry(
  StorageBaseImpl *base,
1232
  const DirEntry *newData,
1233
  DirRef *index)
1234
{
1235
  StorageImpl *storage = (StorageImpl*)base;
1236 1237
  ULONG       currentEntryIndex    = 0;
  ULONG       newEntryIndex        = DIRENTRY_NULL;
1238
  HRESULT hr = S_OK;
1239
  BYTE currentData[RAW_DIRENTRY_SIZE];
1240
  WORD sizeOfNameString;
1241 1242 1243

  do
  {
1244
    hr = StorageImpl_ReadRawDirEntry(storage,
1245
                                     currentEntryIndex,
1246
                                     currentData);
1247

1248
    if (SUCCEEDED(hr))
1249
    {
1250 1251 1252 1253 1254 1255
      StorageUtl_ReadWord(
        currentData,
        OFFSET_PS_NAMELENGTH,
        &sizeOfNameString);

      if (sizeOfNameString == 0)
1256
      {
1257
        /*
1258
         * The entry exists and is available, we found it.
1259
         */
1260
        newEntryIndex = currentEntryIndex;
1261 1262 1263 1264 1265
      }
    }
    else
    {
      /*
1266
       * We exhausted the directory entries, we will create more space below
1267
       */
1268
      newEntryIndex = currentEntryIndex;
1269
    }
1270
    currentEntryIndex++;
1271

1272
  } while (newEntryIndex == DIRENTRY_NULL);
1273

1274
  /*
1275
   * grow the directory stream
1276
   */
1277
  if (FAILED(hr))
1278
  {
1279
    BYTE           emptyData[RAW_DIRENTRY_SIZE];
1280
    ULARGE_INTEGER newSize;
1281 1282
    ULONG          entryIndex;
    ULONG          lastEntry     = 0;
1283 1284
    ULONG          blockCount    = 0;

1285
    /*
1286
     * obtain the new count of blocks in the directory stream
1287 1288
     */
    blockCount = BlockChainStream_GetCount(
1289
                   storage->rootBlockChain)+1;
1290

1291
    /*
1292
     * initialize the size used by the directory stream
1293
     */
1294 1295
    newSize.u.HighPart = 0;
    newSize.u.LowPart  = storage->bigBlockSize * blockCount;
1296

1297
    /*
1298
     * add a block to the directory stream
1299
     */
1300
    BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1301

1302
    /*
1303 1304
     * memset the empty entry in order to initialize the unused newly
     * created entries
1305
     */
1306
    memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1307

1308
    /*
1309 1310
     * initialize them
     */
1311
    lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1312

1313
    for(
1314 1315 1316
      entryIndex = newEntryIndex + 1;
      entryIndex < lastEntry;
      entryIndex++)
1317
    {
1318
      StorageImpl_WriteRawDirEntry(
1319
        storage,
1320
        entryIndex,
1321
        emptyData);
1322
    }
1323 1324

    StorageImpl_SaveFileHeader(storage);
1325 1326
  }

1327 1328
  UpdateRawDirEntry(currentData, newData);

1329
  hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1330 1331

  if (SUCCEEDED(hr))
1332
    *index = newEntryIndex;
1333 1334

  return hr;
1335 1336
}

1337 1338 1339 1340 1341 1342
/***************************************************************************
 *
 * Internal Method
 *
 * Mark a directory entry in the file as free.
 */
1343 1344
static HRESULT StorageImpl_DestroyDirEntry(
  StorageBaseImpl *base,
1345
  DirRef index)
1346
{
1347
  BYTE emptyData[RAW_DIRENTRY_SIZE];
1348
  StorageImpl *storage = (StorageImpl*)base;
1349

1350
  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1351

1352
  return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1353 1354 1355
}


1356 1357 1358 1359
/****************************************************************************
 *
 * Internal Method
 *
1360
 * Case insensitive comparison of DirEntry.name by first considering
1361 1362
 * their size.
 *
1363 1364 1365
 * Returns <0 when name1 < name2
 *         >0 when name1 > name2
 *          0 when name1 == name2
1366
 */
1367 1368 1369
static LONG entryNameCmp(
    const OLECHAR *name1,
    const OLECHAR *name2)
1370
{
1371
  LONG diff      = lstrlenW(name1) - lstrlenW(name2);
1372

1373
  while (diff == 0 && *name1 != 0)
1374
  {
1375
    /*
Francois Gouget's avatar
Francois Gouget committed
1376
     * We compare the string themselves only when they are of the same length
1377
     */
1378
    diff = toupperW(*name1++) - toupperW(*name2++);
1379 1380
  }

1381
  return diff;
1382 1383 1384 1385 1386 1387
}

/****************************************************************************
 *
 * Internal Method
 *
1388
 * Add a directory entry to a storage
1389
 */
1390
static HRESULT insertIntoTree(
1391
  StorageBaseImpl *This,
1392 1393
  DirRef        parentStorageIndex,
  DirRef        newEntryIndex)
1394
{
1395 1396
  DirEntry currentEntry;
  DirEntry newEntry;
1397 1398

  /*
1399
   * Read the inserted entry
1400
   */
1401 1402 1403
  StorageBaseImpl_ReadDirEntry(This,
                               newEntryIndex,
                               &newEntry);
1404 1405

  /*
1406
   * Read the storage entry
1407
   */
1408 1409 1410
  StorageBaseImpl_ReadDirEntry(This,
                               parentStorageIndex,
                               &currentEntry);
1411

1412
  if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1413
  {
1414
    /*
1415 1416 1417
     * The root storage contains some element, therefore, start the research
     * for the appropriate location.
     */
1418
    BOOL found = 0;
1419
    DirRef current, next, previous, currentEntryId;
1420 1421

    /*
1422
     * Keep a reference to the root of the storage's element tree
1423
     */
1424
    currentEntryId = currentEntry.dirRootEntry;
1425 1426

    /*
1427
     * Read
1428
     */
1429 1430 1431
    StorageBaseImpl_ReadDirEntry(This,
                                 currentEntry.dirRootEntry,
                                 &currentEntry);
1432

1433 1434 1435
    previous = currentEntry.leftChild;
    next     = currentEntry.rightChild;
    current  = currentEntryId;
1436 1437 1438

    while (found == 0)
    {
1439
      LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1440

1441 1442
      if (diff < 0)
      {
1443
        if (previous != DIRENTRY_NULL)
1444
        {
1445 1446 1447
          StorageBaseImpl_ReadDirEntry(This,
                                       previous,
                                       &currentEntry);
1448 1449 1450 1451
          current = previous;
        }
        else
        {
1452
          currentEntry.leftChild = newEntryIndex;
1453 1454 1455
          StorageBaseImpl_WriteDirEntry(This,
                                        current,
                                        &currentEntry);
1456 1457 1458
          found = 1;
        }
      }
1459
      else if (diff > 0)
1460
      {
1461
        if (next != DIRENTRY_NULL)
1462
        {
1463 1464 1465
          StorageBaseImpl_ReadDirEntry(This,
                                       next,
                                       &currentEntry);
1466 1467 1468 1469
          current = next;
        }
        else
        {
1470
          currentEntry.rightChild = newEntryIndex;
1471 1472 1473
          StorageBaseImpl_WriteDirEntry(This,
                                        current,
                                        &currentEntry);
1474 1475 1476
          found = 1;
        }
      }
1477 1478 1479 1480 1481 1482
      else
      {
	/*
	 * Trying to insert an item with the same name in the
	 * subtree structure.
	 */
1483
	return STG_E_FILEALREADYEXISTS;
1484
      }
1485

1486 1487
      previous = currentEntry.leftChild;
      next     = currentEntry.rightChild;
1488 1489 1490 1491
    }
  }
  else
  {
1492
    /*
1493
     * The storage is empty, make the new entry the root of its element tree
1494
     */
1495
    currentEntry.dirRootEntry = newEntryIndex;
1496 1497 1498
    StorageBaseImpl_WriteDirEntry(This,
                                  parentStorageIndex,
                                  &currentEntry);
1499
  }
1500 1501

  return S_OK;
1502 1503
}

1504 1505 1506 1507 1508 1509
/****************************************************************************
 *
 * Internal Method
 *
 * Find and read the element of a storage with the given name.
 */
1510
static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1511
    const OLECHAR *name, DirEntry *data)
1512
{
1513
  DirRef currentEntry;
1514 1515

  /* Read the storage entry to find the root of the tree. */
1516
  StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1517

1518
  currentEntry = data->dirRootEntry;
1519

1520
  while (currentEntry != DIRENTRY_NULL)
1521 1522 1523
  {
    LONG cmp;

1524
    StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1525

1526
    cmp = entryNameCmp(name, data->name);
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541

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

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

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

  return currentEntry;
}

1542 1543 1544 1545 1546 1547 1548 1549 1550
/****************************************************************************
 *
 * Internal Method
 *
 * 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.
 */
1551
static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1552
    const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1553 1554
    ULONG *relation)
{
1555
  DirRef childEntry;
1556
  DirEntry childData;
1557 1558

  /* Read the storage entry to find the root of the tree. */
1559
  StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1560 1561

  *parentEntry = storageEntry;
1562
  *relation = DIRENTRY_RELATION_DIR;
1563

1564
  childEntry = parentData->dirRootEntry;
1565

1566
  while (childEntry != DIRENTRY_NULL)
1567 1568 1569
  {
    LONG cmp;

1570
    StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1571

1572
    cmp = entryNameCmp(childName, childData.name);
1573 1574 1575 1576 1577 1578 1579 1580 1581

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

    else if (cmp < 0)
    {
      *parentData = childData;
      *parentEntry = childEntry;
1582
      *relation = DIRENTRY_RELATION_PREVIOUS;
1583 1584 1585 1586 1587 1588 1589 1590

      childEntry = parentData->leftChild;
    }

    else if (cmp > 0)
    {
      *parentData = childData;
      *parentEntry = childEntry;
1591
      *relation = DIRENTRY_RELATION_NEXT;
1592 1593 1594 1595 1596

      childEntry = parentData->rightChild;
    }
  }

1597
  if (childEntry == DIRENTRY_NULL)
1598 1599 1600 1601 1602
    return STG_E_FILENOTFOUND;
  else
    return S_OK;
}

1603

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

1608 1609 1610 1611 1612 1613 1614 1615 1616 1617
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;
1618

1619 1620
  if (srcEntry == DIRENTRY_NULL)
    return S_OK;
1621

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

1624
  if (FAILED(hr))
1625 1626
    return hr;

1627
  if ( snbExclude )
1628
  {
1629
    WCHAR **snb = snbExclude;
1630

1631
    while ( *snb != NULL && !skip )
1632
    {
1633 1634 1635
      if ( lstrcmpW(data.name, *snb) == 0 )
        skip = TRUE;
      ++snb;
1636
    }
1637
  }
1638

1639 1640 1641
  if (!skip)
  {
    if (data.stgType == STGTY_STORAGE && !skip_storage)
1642 1643 1644 1645
    {
      /*
       * create a new storage in destination storage
       */
1646
      hr = IStorage_CreateStorage( pstgDest, data.name,
1647
                                   STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1648
                                   0, 0,
1649
                                   &pstgTmp );
1650

1651 1652 1653 1654 1655
      /*
       * if it already exist, don't create a new one use this one
       */
      if (hr == STG_E_FILEALREADYEXISTS)
      {
1656
        hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1657 1658 1659
                                   STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                   NULL, 0, &pstgTmp );
      }
1660

1661
      if (SUCCEEDED(hr))
1662
      {
1663 1664
        hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
                                                 skip_stream, NULL, pstgTmp );
1665

1666
        IStorage_Release(pstgTmp);
1667
      }
1668
    }
1669
    else if (data.stgType == STGTY_STREAM && !skip_stream)
1670 1671 1672 1673 1674
    {
      /*
       * create a new stream in destination storage. If the stream already
       * exist, it will be deleted and a new one will be created.
       */
1675
      hr = IStorage_CreateStream( pstgDest, data.name,
1676 1677 1678 1679
                                  STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                  0, 0, &pstrTmp );

      /*
1680 1681
       * open child stream storage. This operation must succeed even if the
       * stream is already open, so we use internal functions to do it.
1682
       */
1683 1684
      if (hr == S_OK)
      {
1685 1686 1687 1688 1689 1690 1691 1692
        StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);

        if (streamimpl)
        {
          pstrChild = &streamimpl->IStream_iface;
          if (pstrChild)
            IStream_AddRef(pstrChild);
        }
1693
        else
1694 1695
        {
          pstrChild = NULL;
1696
          hr = E_OUTOFMEMORY;
1697
        }
1698
      }
1699

1700 1701 1702 1703 1704 1705
      if (hr == S_OK)
      {
        /*
         * Get the size of the source stream
         */
        IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1706

1707 1708 1709 1710
        /*
         * Set the size of the destination stream.
         */
        IStream_SetSize(pstrTmp, strStat.cbSize);
1711

1712 1713 1714 1715 1716
        /*
         * do the copy
         */
        hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
                             NULL, NULL );
1717

1718 1719
        IStream_Release( pstrChild );
      }
1720

1721 1722
      IStream_Release( pstrTmp );
    }
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
  }

  /* copy siblings */
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
                                           skip_stream, snbExclude, pstgDest );

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

  return hr;
}

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

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

  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;
}

/*************************************************************************
 * CopyTo (IStorage)
 */
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] */
{
1766
  StorageBaseImpl *This = impl_from_IStorage(iface);
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783

  BOOL         skip_storage = FALSE, skip_stream = FALSE;
  int          i;

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

  if ( pstgDest == 0 )
    return STG_E_INVALIDPOINTER;

  for(i = 0; i < ciidExclude; ++i)
  {
    if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
        skip_storage = TRUE;
    else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
        skip_stream = TRUE;
1784
    else
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
        WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
  }

  if (!skip_storage)
  {
    /* 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;

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

      if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
      {
1802
        TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1803

1804
        pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1805 1806 1807
      }
      else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
      {
1808
        StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1809

1810
        pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1811 1812 1813
      }
      else
        break;
1814 1815
    }

1816 1817 1818
    if (pstgDestAncestor == iface)
    {
      BOOL fail = TRUE;
1819

1820 1821 1822 1823 1824
      if (pstgDestAncestorChild && snbExclude)
      {
        StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
        DirEntry data;
        WCHAR **snb = snbExclude;
1825

1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
        StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);

        while ( *snb != NULL && fail )
        {
          if ( lstrcmpW(data.name, *snb) == 0 )
            fail = FALSE;
          ++snb;
        }
      }

      if (fail)
        return STG_E_ACCESSDENIED;
    }
  }

  return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
    skip_storage, skip_stream, snbExclude, pstgDest );
1843
}
1844

1845 1846 1847
/*************************************************************************
 * MoveElementTo (IStorage)
 */
1848
static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1849
  IStorage*     iface,
1850 1851 1852 1853
  const OLECHAR *pwcsName,   /* [string][in] */
  IStorage      *pstgDest,   /* [unique][in] */
  const OLECHAR *pwcsNewName,/* [string][in] */
  DWORD           grfFlags)    /* [in] */
1854
{
1855
  FIXME("(%p %s %p %s %u): stub\n", iface,
1856 1857
         debugstr_w(pwcsName), pstgDest,
         debugstr_w(pwcsNewName), grfFlags);
1858 1859
  return E_NOTIMPL;
}
1860

1861 1862
/*************************************************************************
 * Commit (IStorage)
1863 1864 1865 1866
 *
 * Ensures that any changes made to a storage object open in transacted mode
 * are reflected in the parent storage
 *
1867
 * In a non-transacted mode, this ensures all cached writes are completed.
1868
 */
1869
static HRESULT WINAPI StorageImpl_Commit(
1870
  IStorage*   iface,
1871
  DWORD         grfCommitFlags)/* [in] */
1872
{
1873
  StorageBaseImpl* This = impl_from_IStorage(iface);
1874
  TRACE("(%p %d)\n", iface, grfCommitFlags);
1875
  return StorageBaseImpl_Flush(This);
1876
}
1877

1878 1879
/*************************************************************************
 * Revert (IStorage)
1880 1881
 *
 * Discard all changes that have been made since the last commit operation
1882
 */
1883
static HRESULT WINAPI StorageImpl_Revert(
1884
  IStorage* iface)
1885
{
1886 1887
  TRACE("(%p)\n", iface);
  return S_OK;
1888 1889 1890
}

/*************************************************************************
1891
 * DestroyElement (IStorage)
1892
 *
1893 1894
 * Strategy: This implementation is built this way for simplicity not for speed.
 *          I always delete the topmost element of the enumeration and adjust
1895 1896
 *          the deleted element pointer all the time.  This takes longer to
 *          do but allow to reinvoke DestroyElement whenever we encounter a
1897 1898
 *          storage object.  The optimisation resides in the usage of another
 *          enumeration strategy that would give all the leaves of a storage
1899 1900
 *          first. (postfix order)
 */
1901
static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1902
  IStorage*     iface,
1903
  const OLECHAR *pwcsName)/* [string][in] */
1904
{
1905
  StorageBaseImpl *This = impl_from_IStorage(iface);
1906

1907
  HRESULT           hr = S_OK;
1908
  DirEntry          entryToDelete;
1909
  DirRef            entryToDeleteRef;
1910

1911
  TRACE("(%p, %s)\n",
1912 1913
	iface, debugstr_w(pwcsName));

1914
  if (pwcsName==NULL)
1915
    return STG_E_INVALIDPOINTER;
1916

1917
  if (This->reverted)
1918 1919
    return STG_E_REVERTED;

1920 1921
  if ( !(This->openFlags & STGM_TRANSACTED) &&
       STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1922 1923
    return STG_E_ACCESSDENIED;

1924
  entryToDeleteRef = findElement(
1925
    This,
1926
    This->storageDirEntry,
1927
    pwcsName,
1928
    &entryToDelete);
1929

1930
  if ( entryToDeleteRef == DIRENTRY_NULL )
1931 1932 1933 1934
  {
    return STG_E_FILENOTFOUND;
  }

1935
  if ( entryToDelete.stgType == STGTY_STORAGE )
1936
  {
1937
    hr = deleteStorageContents(
1938
           This,
1939 1940
           entryToDeleteRef,
           entryToDelete);
1941
  }
1942
  else if ( entryToDelete.stgType == STGTY_STREAM )
1943
  {
1944
    hr = deleteStreamContents(
1945
           This,
1946 1947
           entryToDeleteRef,
           entryToDelete);
1948 1949
  }

1950
  if (hr!=S_OK)
1951 1952 1953
    return hr;

  /*
1954
   * Remove the entry from its parent storage
1955
   */
1956
  hr = removeFromTree(
1957
        This,
1958
        This->storageDirEntry,
1959
        entryToDeleteRef);
1960

1961
  /*
1962
   * Invalidate the entry
1963
   */
1964
  if (SUCCEEDED(hr))
1965
    StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1966

1967 1968 1969
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_Flush(This);

1970 1971 1972 1973
  return hr;
}


1974
/******************************************************************************
1975
 * Internal stream list handlers
1976 1977
 */

1978
void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
{
  TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
  list_add_tail(&stg->strmHead,&strm->StrmListEntry);
}

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

1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
{
  StgStreamImpl *strm;

  LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
  {
    if (strm->dirEntry == streamEntry)
    {
      return TRUE;
    }
  }

  return FALSE;
}

2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
{
  StorageInternalImpl *childstg;

  LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
  {
    if (childstg->base.storageDirEntry == storageEntry)
    {
      return TRUE;
    }
  }

  return FALSE;
}

2020
static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2021 2022 2023
{
  struct list *cur, *cur2;
  StgStreamImpl *strm=NULL;
2024
  StorageInternalImpl *childstg=NULL;
2025 2026 2027

  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
    strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2028
    TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2029
    strm->parentStorage = NULL;
2030 2031
    list_remove(cur);
  }
2032 2033 2034

  LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
    childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2035
    StorageBaseImpl_Invalidate( &childstg->base );
2036
  }
2037 2038 2039 2040 2041 2042 2043

  if (stg->transactedChild)
  {
    StorageBaseImpl_Invalidate(stg->transactedChild);

    stg->transactedChild = NULL;
  }
2044
}
2045 2046


2047 2048 2049 2050
/*********************************************************************
 *
 * Internal Method
 *
2051
 * Delete the contents of a storage entry.
2052 2053
 *
 */
2054
static HRESULT deleteStorageContents(
2055
  StorageBaseImpl *parentStorage,
2056
  DirRef       indexToDelete,
2057
  DirEntry     entryDataToDelete)
2058 2059
{
  IEnumSTATSTG *elements     = 0;
2060
  IStorage   *childStorage = 0;
2061 2062 2063
  STATSTG      currentElement;
  HRESULT      hr;
  HRESULT      destroyHr = S_OK;
2064 2065 2066 2067 2068 2069 2070
  StorageInternalImpl *stg, *stg2;

  /* Invalidate any open storage objects. */
  LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
  {
    if (stg->base.storageDirEntry == indexToDelete)
    {
2071
      StorageBaseImpl_Invalidate(&stg->base);
2072 2073
    }
  }
2074 2075 2076 2077

  /*
   * Open the storage and enumerate it
   */
2078
  hr = IStorage_OpenStorage(
2079
        &parentStorage->IStorage_iface,
2080
        entryDataToDelete.name,
2081
        0,
2082
        STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2083 2084 2085 2086 2087 2088 2089 2090 2091
        0,
        0,
        &childStorage);

  if (hr != S_OK)
  {
    return hr;
  }

2092
  /*
2093 2094
   * Enumerate the elements
   */
2095
  IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2096 2097 2098 2099 2100 2101 2102 2103 2104

  do
  {
    /*
     * Obtain the next element
     */
    hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
    if (hr==S_OK)
    {
2105
      destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117

      CoTaskMemFree(currentElement.pwcsName);
    }

    /*
     * We need to Reset the enumeration every time because we delete elements
     * and the enumeration could be invalid
     */
    IEnumSTATSTG_Reset(elements);

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

2118
  IStorage_Release(childStorage);
2119
  IEnumSTATSTG_Release(elements);
2120

2121 2122 2123 2124 2125 2126 2127
  return destroyHr;
}

/*********************************************************************
 *
 * Internal Method
 *
2128
 * Perform the deletion of a stream's data
2129 2130
 *
 */
2131
static HRESULT deleteStreamContents(
2132
  StorageBaseImpl *parentStorage,
2133
  DirRef        indexToDelete,
2134
  DirEntry      entryDataToDelete)
2135
{
2136
  IStream      *pis;
2137 2138
  HRESULT        hr;
  ULARGE_INTEGER size;
2139
  StgStreamImpl *strm, *strm2;
2140 2141

  /* Invalidate any open stream objects. */
2142
  LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2143 2144 2145 2146 2147 2148 2149 2150
  {
    if (strm->dirEntry == indexToDelete)
    {
      TRACE("Stream deleted %p\n", strm);
      strm->parentStorage = NULL;
      list_remove(&strm->StrmListEntry);
    }
  }
2151

2152 2153
  size.u.HighPart = 0;
  size.u.LowPart = 0;
2154

2155
  hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2156
        entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2157

2158 2159 2160 2161 2162
  if (hr!=S_OK)
  {
    return(hr);
  }

2163 2164 2165 2166
  /*
   * Zap the stream
   */
  hr = IStream_SetSize(pis, size);
2167 2168 2169 2170 2171 2172

  if(hr != S_OK)
  {
    return hr;
  }

2173 2174 2175 2176 2177
  /*
   * Release the stream object.
   */
  IStream_Release(pis);

2178 2179 2180
  return S_OK;
}

2181
static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2182
{
2183
  switch (relation)
2184
  {
2185
    case DIRENTRY_RELATION_PREVIOUS:
2186
      entry->leftChild = new_target;
2187
      break;
2188
    case DIRENTRY_RELATION_NEXT:
2189
      entry->rightChild = new_target;
2190
      break;
2191
    case DIRENTRY_RELATION_DIR:
2192
      entry->dirRootEntry = new_target;
2193 2194 2195
      break;
    default:
      assert(0);
2196 2197 2198 2199 2200 2201 2202
  }
}

/*************************************************************************
 *
 * Internal Method
 *
2203 2204
 * This method removes a directory entry from its parent storage tree without
 * freeing any resources attached to it.
2205
 */
2206
static HRESULT removeFromTree(
2207
  StorageBaseImpl *This,
2208 2209
  DirRef        parentStorageIndex,
  DirRef        deletedIndex)
2210
{
2211 2212
  DirEntry   entryToDelete;
  DirEntry   parentEntry;
2213
  DirRef parentEntryRef;
2214
  ULONG typeOfRelation;
2215
  HRESULT hr;
2216

2217
  hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2218 2219 2220

  if (hr != S_OK)
    return hr;
2221 2222

  /*
2223
   * Find the element that links to the one we want to delete.
2224
   */
2225 2226
  hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
    &parentEntry, &parentEntryRef, &typeOfRelation);
2227 2228 2229

  if (hr != S_OK)
    return hr;
2230

2231
  if (entryToDelete.leftChild != DIRENTRY_NULL)
2232
  {
2233 2234 2235
    /*
     * Replace the deleted entry with its left child
     */
2236
    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2237

2238
    hr = StorageBaseImpl_WriteDirEntry(
2239
            This,
2240 2241
            parentEntryRef,
            &parentEntry);
2242
    if(FAILED(hr))
2243
    {
2244
      return hr;
2245
    }
2246

2247
    if (entryToDelete.rightChild != DIRENTRY_NULL)
2248
    {
2249
      /*
2250 2251 2252
       * 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.
2253
       */
2254
      DirRef newRightChildParent = entryToDelete.leftChild;
2255
      DirEntry newRightChildParentEntry;
2256 2257 2258

      do
      {
2259
        hr = StorageBaseImpl_ReadDirEntry(
2260
                This,
2261
                newRightChildParent,
2262
                &newRightChildParentEntry);
2263
        if (FAILED(hr))
2264
        {
2265
          return hr;
2266 2267
        }

2268 2269 2270
        if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
          newRightChildParent = newRightChildParentEntry.rightChild;
      } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2271

2272
      newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2273

2274
      hr = StorageBaseImpl_WriteDirEntry(
2275
              This,
2276
              newRightChildParent,
2277
              &newRightChildParentEntry);
2278
      if (FAILED(hr))
2279
      {
2280
        return hr;
2281
      }
2282
    }
2283
  }
2284
  else
2285
  {
2286
    /*
2287
     * Replace the deleted entry with its right child
2288
     */
2289
    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2290

2291
    hr = StorageBaseImpl_WriteDirEntry(
2292
            This,
2293 2294
            parentEntryRef,
            &parentEntry);
2295
    if(FAILED(hr))
2296
    {
2297
      return hr;
2298
    }
2299 2300 2301 2302 2303 2304
  }

  return hr;
}


2305 2306 2307
/******************************************************************************
 * SetElementTimes (IStorage)
 */
2308
static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2309
  IStorage*     iface,
2310 2311 2312 2313
  const OLECHAR *pwcsName,/* [string][in] */
  const FILETIME  *pctime,  /* [in] */
  const FILETIME  *patime,  /* [in] */
  const FILETIME  *pmtime)  /* [in] */
2314
{
2315 2316
  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
  return S_OK;
2317 2318
}

2319 2320 2321
/******************************************************************************
 * SetStateBits (IStorage)
 */
2322
static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2323
  IStorage*   iface,
2324 2325
  DWORD         grfStateBits,/* [in] */
  DWORD         grfMask)     /* [in] */
2326
{
2327
  StorageBaseImpl *This = impl_from_IStorage(iface);
2328

2329
  if (This->reverted)
2330 2331
    return STG_E_REVERTED;

2332
  This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2333
  return S_OK;
2334 2335
}

2336 2337 2338 2339 2340 2341 2342
static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
{
  StorageImpl *This = (StorageImpl*)base;
  return StorageImpl_WriteDirEntry(This, index, data);
}

2343 2344 2345 2346 2347 2348 2349
static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
{
  StorageImpl *This = (StorageImpl*)base;
  return StorageImpl_ReadDirEntry(This, index, data);
}

2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406
static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
{
  int i;

  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
  {
    if (!This->blockChainCache[i])
    {
      return &This->blockChainCache[i];
    }
  }

  i = This->blockChainToEvict;

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

  This->blockChainToEvict++;
  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
    This->blockChainToEvict = 0;

  return &This->blockChainCache[i];
}

static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
    DirRef index)
{
  int i, free_index=-1;

  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];
    }
  }

  if (free_index == -1)
  {
    free_index = This->blockChainToEvict;

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

    This->blockChainToEvict++;
    if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
      This->blockChainToEvict = 0;
  }

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

2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421
static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
{
  int i;

  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
  {
    if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
    {
      BlockChainStream_Destroy(This->blockChainCache[i]);
      This->blockChainCache[i] = NULL;
      return;
    }
  }
}

2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462
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;

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

  if (data.size.QuadPart == 0)
  {
    *bytesRead = 0;
    return S_OK;
  }

  if (offset.QuadPart + size > data.size.QuadPart)
  {
    bytesToRead = data.size.QuadPart - offset.QuadPart;
  }
  else
  {
    bytesToRead = size;
  }

  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
    SmallBlockChainStream *stream;

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

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

    SmallBlockChainStream_Destroy(stream);

    return hr;
  }
  else
  {
2463
    BlockChainStream *stream = NULL;
2464

2465
    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2466 2467 2468 2469 2470 2471 2472 2473
    if (!stream) return E_OUTOFMEMORY;

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

    return hr;
  }
}

2474 2475 2476 2477 2478 2479 2480
static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
  ULARGE_INTEGER newsize)
{
  StorageImpl *This = (StorageImpl*)base;
  DirEntry data;
  HRESULT hr;
  SmallBlockChainStream *smallblock=NULL;
2481
  BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502

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

  /* 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);

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

  /* 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
    {
2503 2504
      pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
      bigblock = *pbigblock;
2505 2506 2507 2508 2509 2510 2511 2512 2513 2514
      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
  {
2515 2516
    pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
    bigblock = *pbigblock;
2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
    if (!bigblock) return E_OUTOFMEMORY;
  }

  /* 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;
    }
2529 2530 2531

    pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
    *pbigblock = bigblock;
2532 2533 2534
  }
  else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
2535
    smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561
    if (!smallblock)
      return E_FAIL;
  }

  /* Set the size of the block chain. */
  if (smallblock)
  {
    SmallBlockChainStream_SetSize(smallblock, newsize);
    SmallBlockChainStream_Destroy(smallblock);
  }
  else
  {
    BlockChainStream_SetSize(bigblock, newsize);
  }

  /* 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);
  }
  return hr;
}

2562 2563 2564 2565 2566 2567
static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
  ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
  StorageImpl *This = (StorageImpl*)base;
  DirEntry data;
  HRESULT hr;
2568
  ULARGE_INTEGER newSize;
2569 2570 2571 2572

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

2573 2574 2575
  /* Grow the stream if necessary */
  newSize.QuadPart = 0;
  newSize.QuadPart = offset.QuadPart + size;
2576

2577
  if (newSize.QuadPart > data.size.QuadPart)
2578
  {
2579 2580 2581 2582
    hr = StorageImpl_StreamSetSize(base, index, newSize);
    if (FAILED(hr))
      return hr;

2583 2584
    hr = StorageImpl_ReadDirEntry(This, index, &data);
    if (FAILED(hr)) return hr;
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603
  }

  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
  {
    SmallBlockChainStream *stream;

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

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

    SmallBlockChainStream_Destroy(stream);

    return hr;
  }
  else
  {
    BlockChainStream *stream;

2604
    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2605 2606 2607 2608 2609 2610 2611 2612
    if (!stream) return E_OUTOFMEMORY;

    hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);

    return hr;
  }
}

2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
  DirRef src)
{
  StorageImpl *This = (StorageImpl*)base;
  DirEntry dst_data, src_data;
  HRESULT hr;

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

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

  if (SUCCEEDED(hr))
  {
    StorageImpl_DeleteCachedBlockChainStream(This, src);
    dst_data.startingBlock = src_data.startingBlock;
    dst_data.size = src_data.size;

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

  return hr;
}

2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649
static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
  StorageImpl *This = (StorageImpl*) iface;
  STATSTG statstg;
  HRESULT hr;

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

  *result = statstg.pwcsName;

  return hr;
}

2650 2651 2652
/*
 * Virtual function table for the IStorage32Impl class.
 */
2653
static const IStorageVtbl Storage32Impl_Vtbl =
2654 2655 2656 2657 2658 2659
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
2660
    StorageBaseImpl_CreateStorage,
2661
    StorageBaseImpl_OpenStorage,
2662
    StorageBaseImpl_CopyTo,
2663
    StorageBaseImpl_MoveElementTo,
2664 2665 2666
    StorageImpl_Commit,
    StorageImpl_Revert,
    StorageBaseImpl_EnumElements,
2667
    StorageBaseImpl_DestroyElement,
2668
    StorageBaseImpl_RenameElement,
2669
    StorageBaseImpl_SetElementTimes,
2670
    StorageBaseImpl_SetClass,
2671
    StorageBaseImpl_SetStateBits,
2672
    StorageBaseImpl_Stat
2673 2674
};

2675 2676
static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
{
2677
  StorageImpl_Destroy,
2678
  StorageImpl_Invalidate,
2679
  StorageImpl_Flush,
2680
  StorageImpl_GetFilename,
2681
  StorageImpl_CreateDirEntry,
2682 2683
  StorageImpl_BaseWriteDirEntry,
  StorageImpl_BaseReadDirEntry,
2684
  StorageImpl_DestroyDirEntry,
2685
  StorageImpl_StreamReadAt,
2686
  StorageImpl_StreamWriteAt,
2687 2688
  StorageImpl_StreamSetSize,
  StorageImpl_StreamLink
2689 2690
};

2691
static HRESULT StorageImpl_Construct(
2692
  HANDLE       hFile,
2693
  LPCOLESTR    pwcsName,
2694 2695
  ILockBytes*  pLkbyt,
  DWORD        openFlags,
2696
  BOOL         fileBased,
2697
  BOOL         create,
2698
  ULONG        sector_size,
2699
  StorageImpl** result)
2700
{
2701
  StorageImpl* This;
2702
  HRESULT     hr = S_OK;
2703
  DirEntry currentEntry;
2704
  DirRef      currentEntryRef;
2705

2706 2707 2708
  if ( FAILED( validateSTGM(openFlags) ))
    return STG_E_INVALIDFLAG;

2709 2710 2711 2712
  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
  if (!This)
    return E_OUTOFMEMORY;

2713
  memset(This, 0, sizeof(StorageImpl));
2714

2715 2716
  list_init(&This->base.strmHead);

2717 2718
  list_init(&This->base.storageHead);

2719
  This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2720
  This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2721
  This->base.baseVtbl = &StorageImpl_BaseVtbl;
2722
  This->base.openFlags = (openFlags & ~STGM_CREATE);
2723
  This->base.ref = 1;
2724
  This->base.create = create;
2725

2726 2727
  This->base.reverted = 0;

2728 2729 2730
  /*
   * Initialize the big block cache.
   */
2731
  This->bigBlockSize   = sector_size;
2732
  This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2733
  if (hFile)
2734
    hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2735
  else
2736
  {
2737 2738
    This->lockBytes = pLkbyt;
    ILockBytes_AddRef(pLkbyt);
2739
  }
2740

2741 2742 2743
  if (FAILED(hr))
    goto end;

2744
  if (create)
2745 2746
  {
    ULARGE_INTEGER size;
2747
    BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2748

2749 2750 2751 2752
    /* Discard any existing data. */
    size.QuadPart = 0;
    ILockBytes_SetSize(This->lockBytes, size);

2753 2754 2755
    /*
     * Initialize all header variables:
     * - The big block depot consists of one block and it is at block 0
2756
     * - The directory table starts at block 1
2757 2758
     * - There is no small block depot
     */
2759 2760
    memset( This->bigBlockDepotStart,
            BLOCK_UNUSED,
2761 2762 2763 2764 2765
            sizeof(This->bigBlockDepotStart));

    This->bigBlockDepotCount    = 1;
    This->bigBlockDepotStart[0] = 0;
    This->rootStartBlock        = 1;
2766
    This->smallBlockLimit       = LIMIT_TO_USE_SMALL_BLOCK;
2767
    This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2768 2769 2770 2771
    if (sector_size == 4096)
      This->bigBlockSizeBits      = MAX_BIG_BLOCK_SIZE_BITS;
    else
      This->bigBlockSizeBits      = MIN_BIG_BLOCK_SIZE_BITS;
2772 2773 2774 2775
    This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
    This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
    This->extBigBlockDepotCount = 0;

2776
    StorageImpl_SaveFileHeader(This);
2777 2778

    /*
2779
     * Add one block for the big block depot and one block for the directory table
2780
     */
2781 2782
    size.u.HighPart = 0;
    size.u.LowPart  = This->bigBlockSize * 3;
2783
    ILockBytes_SetSize(This->lockBytes, size);
2784 2785 2786 2787 2788 2789 2790

    /*
     * 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);
2791
    StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2792 2793
  }
  else
2794 2795 2796 2797
  {
    /*
     * Load the header for the file.
     */
2798 2799 2800 2801
    hr = StorageImpl_LoadFileHeader(This);

    if (FAILED(hr))
    {
2802
      goto end;
2803
    }
2804 2805 2806 2807 2808 2809
  }

  /*
   * There is no block depot cached yet.
   */
  This->indexBlockDepotCached = 0xFFFFFFFF;
2810
  This->indexExtBlockDepotCached = 0xFFFFFFFF;
2811 2812 2813 2814 2815

  /*
   * Start searching for free blocks with block 0.
   */
  This->prevFreeBlock = 0;
2816

2817 2818
  This->firstFreeSmallBlock = 0;

2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852
  /* Read the extended big block depot locations. */
  if (This->extBigBlockDepotCount != 0)
  {
    ULONG current_block = This->extBigBlockDepotStart;
    ULONG cache_size = This->extBigBlockDepotCount * 2;
    int i;

    This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
    if (!This->extBigBlockDepotLocations)
    {
      hr = E_OUTOFMEMORY;
      goto end;
    }

    This->extBigBlockDepotLocationsSize = cache_size;

    for (i=0; i<This->extBigBlockDepotCount; i++)
    {
      if (current_block == BLOCK_END_OF_CHAIN)
      {
        WARN("File has too few extended big block depot blocks.\n");
        hr = STG_E_DOCFILECORRUPT;
        goto end;
      }
      This->extBigBlockDepotLocations[i] = current_block;
      current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
    }
  }
  else
  {
    This->extBigBlockDepotLocations = NULL;
    This->extBigBlockDepotLocationsSize = 0;
  }

2853 2854 2855
  /*
   * Create the block chain abstractions.
   */
2856
  if(!(This->rootBlockChain =
2857
       BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2858 2859 2860 2861
  {
    hr = STG_E_READFAULT;
    goto end;
  }
2862

2863 2864
  if(!(This->smallBlockDepotChain =
       BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2865
				  DIRENTRY_NULL)))
2866 2867 2868 2869
  {
    hr = STG_E_READFAULT;
    goto end;
  }
2870 2871

  /*
2872
   * Write the root storage entry (memory only)
2873
   */
2874
  if (create)
2875
  {
2876
    static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2877
    DirEntry rootEntry;
2878
    /*
2879
     * Initialize the directory table
2880
     */
2881
    memset(&rootEntry, 0, sizeof(rootEntry));
2882 2883
    strcpyW(rootEntry.name, rootentryW);
    rootEntry.sizeOfNameString = sizeof(rootentryW);
2884
    rootEntry.stgType          = STGTY_ROOT;
2885 2886
    rootEntry.leftChild        = DIRENTRY_NULL;
    rootEntry.rightChild       = DIRENTRY_NULL;
2887 2888
    rootEntry.dirRootEntry     = DIRENTRY_NULL;
    rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
2889 2890
    rootEntry.size.u.HighPart  = 0;
    rootEntry.size.u.LowPart   = 0;
2891 2892

    StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2893 2894 2895
  }

  /*
2896
   * Find the ID of the root storage.
2897
   */
2898
  currentEntryRef = 0;
2899

2900 2901
  do
  {
2902
    hr = StorageImpl_ReadDirEntry(
2903
                      This,
2904 2905
                      currentEntryRef,
                      &currentEntry);
2906

2907
    if (SUCCEEDED(hr))
2908
    {
2909 2910
      if ( (currentEntry.sizeOfNameString != 0 ) &&
           (currentEntry.stgType          == STGTY_ROOT) )
2911
      {
2912
        This->base.storageDirEntry = currentEntryRef;
2913 2914 2915
      }
    }

2916
    currentEntryRef++;
2917

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

2920
  if (FAILED(hr))
2921
  {
2922 2923
    hr = STG_E_READFAULT;
    goto end;
2924 2925 2926 2927 2928
  }

  /*
   * Create the block chain abstraction for the small block root chain.
   */
2929
  if(!(This->smallBlockRootChain =
2930
       BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2931 2932 2933 2934 2935 2936 2937
  {
    hr = STG_E_READFAULT;
  }

end:
  if (FAILED(hr))
  {
2938
    IStorage_Release(&This->base.IStorage_iface);
2939 2940 2941
    *result = NULL;
  }
  else
2942 2943
  {
    StorageImpl_Flush((StorageBaseImpl*)This);
2944
    *result = This;
2945
  }
2946

2947 2948 2949
  return hr;
}

2950 2951 2952 2953 2954 2955 2956 2957 2958
static void StorageImpl_Invalidate(StorageBaseImpl* iface)
{
  StorageImpl *This = (StorageImpl*) iface;

  StorageBaseImpl_DeleteAll(&This->base);

  This->base.reverted = 1;
}

2959
static void StorageImpl_Destroy(StorageBaseImpl* iface)
2960
{
2961
  StorageImpl *This = (StorageImpl*) iface;
2962
  int i;
2963
  TRACE("(%p)\n", This);
2964

2965 2966
  StorageImpl_Flush(iface);

2967
  StorageImpl_Invalidate(iface);
2968

2969 2970
  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);

2971 2972 2973 2974
  BlockChainStream_Destroy(This->smallBlockRootChain);
  BlockChainStream_Destroy(This->rootBlockChain);
  BlockChainStream_Destroy(This->smallBlockDepotChain);

2975 2976 2977
  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
    BlockChainStream_Destroy(This->blockChainCache[i]);

2978 2979
  if (This->lockBytes)
    ILockBytes_Release(This->lockBytes);
2980
  HeapFree(GetProcessHeap(), 0, This);
2981 2982
}

2983 2984 2985
static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
{
  StorageImpl *This = (StorageImpl*) iface;
2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003
  int i;
  HRESULT hr;
  TRACE("(%p)\n", This);

  hr = BlockChainStream_Flush(This->smallBlockRootChain);

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

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

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

  if (SUCCEEDED(hr))
    hr = ILockBytes_Flush(This->lockBytes);
3004

3005
  return hr;
3006 3007
}

3008 3009 3010 3011 3012 3013 3014
/******************************************************************************
 *      Storage32Impl_GetNextFreeBigBlock
 *
 * Returns the index of the next free big block.
 * If the big block depot is filled, this method will enlarge it.
 *
 */
3015
static ULONG StorageImpl_GetNextFreeBigBlock(
3016
  StorageImpl* This)
3017 3018
{
  ULONG depotBlockIndexPos;
3019
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3020
  BOOL success;
3021 3022 3023 3024
  ULONG depotBlockOffset;
  ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
  ULONG nextBlockIndex    = BLOCK_SPECIAL;
  int   depotIndex        = 0;
3025
  ULONG freeBlock         = BLOCK_UNUSED;
3026
  ULARGE_INTEGER neededSize;
3027
  STATSTG statstg;
3028 3029 3030

  depotIndex = This->prevFreeBlock / blocksPerDepot;
  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3031 3032 3033 3034

  /*
   * Scan the entire big block depot until we find a block marked free
   */
3035
  while (nextBlockIndex != BLOCK_UNUSED)
3036
  {
3037
    if (depotIndex < COUNT_BBDEPOTINHEADER)
3038
    {
3039 3040
      depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];

3041
      /*
3042
       * Grow the primary depot.
3043
       */
3044 3045 3046
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        depotBlockIndexPos = depotIndex*blocksPerDepot;
3047

3048 3049 3050 3051 3052 3053
        /*
         * Add a block depot.
         */
        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
        This->bigBlockDepotCount++;
        This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3054

3055 3056 3057
        /*
         * Flag it as a block depot.
         */
3058
        StorageImpl_SetNextBlockInChain(This,
3059 3060
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);
3061

3062 3063
        /* Save new header information.
         */
3064
        StorageImpl_SaveFileHeader(This);
3065
      }
3066 3067 3068 3069
    }
    else
    {
      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3070

3071 3072 3073 3074 3075 3076 3077 3078
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        /*
         * Grow the extended depot.
         */
        ULONG extIndex       = BLOCK_UNUSED;
        ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
        ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3079

3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099
        if (extBlockOffset == 0)
        {
          /* We need an extended block.
           */
          extIndex = Storage32Impl_AddExtBlockDepot(This);
          This->extBigBlockDepotCount++;
          depotBlockIndexPos = extIndex + 1;
        }
        else
          depotBlockIndexPos = depotIndex * blocksPerDepot;

        /*
         * Add a block depot and mark it in the extended block.
         */
        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
        This->bigBlockDepotCount++;
        Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);

        /* Flag the block depot.
         */
3100
        StorageImpl_SetNextBlockInChain(This,
3101 3102 3103 3104 3105 3106
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);

        /* If necessary, flag the extended depot block.
         */
        if (extIndex != BLOCK_UNUSED)
3107
          StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3108 3109 3110

        /* Save header information.
         */
3111
        StorageImpl_SaveFileHeader(This);
3112
      }
3113 3114
    }

3115
    success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3116

3117
    if (success)
3118
    {
3119
      while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3120 3121 3122 3123
              ( nextBlockIndex != BLOCK_UNUSED))
      {
        StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);

3124 3125 3126 3127 3128
        if (nextBlockIndex == BLOCK_UNUSED)
        {
          freeBlock = (depotIndex * blocksPerDepot) +
                      (depotBlockOffset/sizeof(ULONG));
        }
3129 3130 3131 3132 3133 3134

        depotBlockOffset += sizeof(ULONG);
      }
    }

    depotIndex++;
3135
    depotBlockOffset = 0;
3136 3137
  }

3138 3139 3140
  /*
   * make sure that the block physically exists before using it
   */
3141
  neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3142 3143 3144 3145 3146

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

  if (neededSize.QuadPart > statstg.cbSize.QuadPart)
    ILockBytes_SetSize(This->lockBytes, neededSize);
3147

3148 3149 3150
  This->prevFreeBlock = freeBlock;

  return freeBlock;
3151 3152
}

3153 3154 3155 3156 3157 3158
/******************************************************************************
 *      Storage32Impl_AddBlockDepot
 *
 * This will create a depot block, essentially it is a block initialized
 * to BLOCK_UNUSEDs.
 */
3159
static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3160
{
3161
  BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3162 3163 3164 3165 3166

  /*
   * Initialize blocks as free
   */
  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3167
  StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3168 3169 3170 3171 3172 3173 3174 3175 3176
}

/******************************************************************************
 *      Storage32Impl_GetExtDepotBlock
 *
 * 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.
 */
3177
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3178 3179 3180 3181 3182 3183
{
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
  ULONG blockIndex             = BLOCK_UNUSED;
3184
  ULONG extBlockIndex;
3185 3186
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
  int index, num_blocks;
3187 3188 3189

  assert(depotIndex >= COUNT_BBDEPOTINHEADER);

3190
  if (extBlockCount >= This->extBigBlockDepotCount)
3191 3192
    return BLOCK_UNUSED;

3193 3194 3195
  if (This->indexExtBlockDepotCached != extBlockCount)
  {
    extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3196

3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210
    StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);

    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;
  }

  blockIndex = This->extBlockDepotCached[extBlockOffset];
3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221

  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.
 */
3222
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3223 3224 3225 3226 3227
{
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
3228
  ULONG extBlockIndex;
3229 3230 3231

  assert(depotIndex >= COUNT_BBDEPOTINHEADER);

3232 3233 3234
  assert(extBlockCount < This->extBigBlockDepotCount);

  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3235 3236 3237

  if (extBlockIndex != BLOCK_UNUSED)
  {
3238 3239 3240
    StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
                        extBlockOffset * sizeof(ULONG),
                        blockIndex);
3241
  }
3242 3243 3244 3245 3246

  if (This->indexExtBlockDepotCached == extBlockCount)
  {
    This->extBlockDepotCached[extBlockOffset] = blockIndex;
  }
3247 3248 3249 3250 3251 3252 3253
}

/******************************************************************************
 *      Storage32Impl_AddExtBlockDepot
 *
 * Creates an extended depot block.
 */
3254
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3255 3256 3257
{
  ULONG numExtBlocks           = This->extBigBlockDepotCount;
  ULONG nextExtBlock           = This->extBigBlockDepotStart;
3258
  BYTE  depotBuffer[MAX_BIG_BLOCK_SIZE];
3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276
  ULONG index                  = BLOCK_UNUSED;
  ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
  ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
  ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;

  index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
          blocksPerDepotBlock;

  if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
  {
    /*
     * The first extended block.
     */
    This->extBigBlockDepotStart = index;
  }
  else
  {
    /*
3277
     * Find the last existing extended block.
3278
     */
3279
    nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3280 3281 3282 3283

    /*
     * Add the new extended block to the chain.
     */
3284 3285
    StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
                                     index);
3286 3287 3288 3289 3290 3291
  }

  /*
   * Initialize this block.
   */
  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3292
  StorageImpl_WriteBigBlock(This, index, depotBuffer);
3293

3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307
  /* 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);

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

    This->extBigBlockDepotLocations = new_cache;
    This->extBigBlockDepotLocationsSize = new_cache_size;
  }
  This->extBigBlockDepotLocations[numExtBlocks] = index;

3308 3309 3310
  return index;
}

3311 3312 3313 3314 3315
/******************************************************************************
 *      Storage32Impl_FreeBigBlock
 *
 * This method will flag the specified block as free in the big block depot.
 */
3316
static void StorageImpl_FreeBigBlock(
3317
  StorageImpl* This,
3318 3319
  ULONG          blockIndex)
{
3320
  StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3321 3322 3323

  if (blockIndex < This->prevFreeBlock)
    This->prevFreeBlock = blockIndex;
3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334
}

/************************************************************************
 * Storage32Impl_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.
3335
 *          nextBlockIndex - receives the return value.
3336 3337 3338 3339 3340 3341 3342 3343 3344
 *
 * 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.
3345 3346 3347
 *              BLOCK_EXTBBDEPOT - This block is part of the extended
 *                                 big block depot.
 *
3348
 * See Windows documentation for more details on IStorage methods.
3349
 */
3350
static HRESULT StorageImpl_GetNextBlockInChain(
3351
  StorageImpl* This,
3352 3353
  ULONG        blockIndex,
  ULONG*       nextBlockIndex)
3354 3355 3356 3357
{
  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3358
  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3359
  BOOL success;
3360
  ULONG depotBlockIndexPos;
3361
  int index, num_blocks;
3362

3363 3364 3365 3366
  *nextBlockIndex   = BLOCK_SPECIAL;

  if(depotBlockCount >= This->bigBlockDepotCount)
  {
3367
    WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3368 3369 3370
	 This->bigBlockDepotCount);
    return STG_E_READFAULT;
  }
3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390

  /*
   * Cache the currently accessed depot block.
   */
  if (depotBlockCount != This->indexBlockDepotCached)
  {
    This->indexBlockDepotCached = depotBlockCount;

    if (depotBlockCount < COUNT_BBDEPOTINHEADER)
    {
      depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
    }
    else
    {
      /*
       * We have to look in the extended depot.
       */
      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
    }

3391
    success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3392

3393
    if (!success)
3394
      return STG_E_READFAULT;
3395

3396 3397 3398
    num_blocks = This->bigBlockSize / 4;

    for (index = 0; index < num_blocks; index++)
3399 3400 3401
    {
      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
      This->blockDepotCached[index] = *nextBlockIndex;
3402 3403 3404
    }
  }

3405
  *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3406

3407
  return S_OK;
3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424
}

/******************************************************************************
 *      Storage32Impl_GetNextExtendedBlock
 *
 * 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.
 */
3425
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3426 3427 3428
{
  ULONG nextBlockIndex   = BLOCK_SPECIAL;
  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3429

3430 3431
  StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
                        &nextBlockIndex);
3432

3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449
  return nextBlockIndex;
}

/******************************************************************************
 *      Storage32Impl_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
 *
 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
 *
 */
3450
static void StorageImpl_SetNextBlockInChain(
3451
          StorageImpl* This,
3452 3453 3454 3455 3456 3457 3458 3459 3460
          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);
Thuy Nguyen's avatar
Thuy Nguyen committed
3461
  assert(blockIndex != nextBlock);
3462

3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473
  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
  {
    depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
  }
  else
  {
    /*
     * We have to look in the extended depot.
     */
    depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
  }
3474

3475 3476
  StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
                        nextBlock);
3477 3478 3479 3480 3481 3482 3483
  /*
   * Update the cached block depot, if necessary.
   */
  if (depotBlockCount == This->indexBlockDepotCached)
  {
    This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
  }
3484 3485 3486 3487 3488
}

/******************************************************************************
 *      Storage32Impl_LoadFileHeader
 *
3489
 * This method will read in the file header
3490
 */
3491
static HRESULT StorageImpl_LoadFileHeader(
3492
          StorageImpl* This)
3493
{
3494 3495
  HRESULT hr;
  BYTE    headerBigBlock[HEADER_SIZE];
3496
  int     index;
3497 3498
  ULARGE_INTEGER offset;
  DWORD bytes_read;
3499

3500
  TRACE("\n");
3501 3502 3503
  /*
   * Get a pointer to the big block of data containing the header.
   */
3504 3505 3506 3507 3508
  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;
3509 3510 3511 3512

  /*
   * Extract the information from the header.
   */
3513
  if (SUCCEEDED(hr))
3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529
  {
    /*
     * 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;
    }

    if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
    {
      return STG_E_INVALIDHEADER;
    }

    StorageUtl_ReadWord(
3530 3531
      headerBigBlock,
      OFFSET_BIGBLOCKSIZEBITS,
3532 3533 3534
      &This->bigBlockSizeBits);

    StorageUtl_ReadWord(
3535 3536
      headerBigBlock,
      OFFSET_SMALLBLOCKSIZEBITS,
3537 3538 3539
      &This->smallBlockSizeBits);

    StorageUtl_ReadDWord(
3540 3541
      headerBigBlock,
      OFFSET_BBDEPOTCOUNT,
3542 3543 3544
      &This->bigBlockDepotCount);

    StorageUtl_ReadDWord(
3545 3546
      headerBigBlock,
      OFFSET_ROOTSTARTBLOCK,
3547 3548
      &This->rootStartBlock);

3549 3550 3551 3552 3553
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_SMALLBLOCKLIMIT,
      &This->smallBlockLimit);

3554
    StorageUtl_ReadDWord(
3555 3556
      headerBigBlock,
      OFFSET_SBDEPOTSTART,
3557 3558
      &This->smallBlockDepotStart);

3559 3560 3561
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_EXTBBDEPOTSTART,
3562 3563 3564
      &This->extBigBlockDepotStart);

    StorageUtl_ReadDWord(
3565 3566
      headerBigBlock,
      OFFSET_EXTBBDEPOTCOUNT,
3567
      &This->extBigBlockDepotCount);
3568

3569 3570 3571
    for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
    {
      StorageUtl_ReadDWord(
3572
        headerBigBlock,
3573 3574 3575
        OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
        &(This->bigBlockDepotStart[index]));
    }
3576

3577 3578 3579
    /*
     * Make the bitwise arithmetic to get the size of the blocks in bytes.
     */
3580 3581
    This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
    This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3582

3583
    /*
3584
     * Right now, the code is making some assumptions about the size of the
3585 3586
     * blocks, just make sure they are what we're expecting.
     */
3587
    if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3588 3589
	This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
	This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
Alexandre Julliard's avatar
Alexandre Julliard committed
3590
    {
3591 3592
	FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
	    This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
Alexandre Julliard's avatar
Alexandre Julliard committed
3593 3594 3595 3596
	hr = STG_E_INVALIDHEADER;
    }
    else
	hr = S_OK;
3597
  }
3598

3599 3600 3601 3602 3603 3604
  return hr;
}

/******************************************************************************
 *      Storage32Impl_SaveFileHeader
 *
3605
 * This method will save to the file the header
3606
 */
3607
static void StorageImpl_SaveFileHeader(
3608
          StorageImpl* This)
3609
{
3610
  BYTE   headerBigBlock[HEADER_SIZE];
3611
  int    index;
3612 3613 3614
  HRESULT hr;
  ULARGE_INTEGER offset;
  DWORD bytes_read, bytes_written;
3615
  DWORD major_version, dirsectorcount;
3616 3617 3618 3619

  /*
   * Get a pointer to the big block of data containing the header.
   */
3620 3621 3622 3623 3624
  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;
3625

3626 3627 3628 3629 3630 3631 3632 3633 3634 3635
  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;
  }

3636 3637 3638
  /*
   * If the block read failed, the file is probably new.
   */
3639
  if (FAILED(hr))
3640 3641 3642 3643
  {
    /*
     * Initialize for all unknown fields.
     */
3644
    memset(headerBigBlock, 0, HEADER_SIZE);
3645

3646 3647 3648 3649 3650
    /*
     * Initialize the magic number.
     */
    memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
  }
3651

3652 3653 3654
  /*
   * Write the information to the header.
   */
3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_MINORVERSION,
    0x3e);

  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_MAJORVERSION,
    major_version);

  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_BYTEORDERMARKER,
    (WORD)-2);

3670 3671 3672 3673
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_BIGBLOCKSIZEBITS,
    This->bigBlockSizeBits);
3674

3675 3676 3677 3678
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_SMALLBLOCKSIZEBITS,
    This->smallBlockSizeBits);
3679

3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696
  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;

  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_DIRSECTORCOUNT,
    dirsectorcount);

3697 3698 3699 3700
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_BBDEPOTCOUNT,
    This->bigBlockDepotCount);
3701

3702 3703 3704 3705
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_ROOTSTARTBLOCK,
    This->rootStartBlock);
3706

3707 3708 3709 3710 3711
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SMALLBLOCKLIMIT,
    This->smallBlockLimit);

3712 3713 3714 3715
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTSTART,
    This->smallBlockDepotStart);
3716

3717 3718 3719 3720 3721 3722 3723 3724 3725 3726
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTCOUNT,
    This->smallBlockDepotChain ?
     BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);

  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_EXTBBDEPOTSTART,
    This->extBigBlockDepotStart);
3727

3728 3729 3730 3731 3732 3733 3734
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_EXTBBDEPOTCOUNT,
    This->extBigBlockDepotCount);

  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
  {
3735
    StorageUtl_WriteDWord(
3736
      headerBigBlock,
3737 3738
      OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
      (This->bigBlockDepotStart[index]));
3739
  }
3740

3741 3742 3743
  /*
   * Write the big block back to the file.
   */
3744
  StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3745 3746
}

3747 3748 3749 3750 3751
/******************************************************************************
 *      StorageImpl_ReadRawDirEntry
 *
 * This method will read the raw data from a directory entry in the file.
 *
3752
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3753 3754 3755 3756 3757 3758 3759 3760
 */
HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
{
  ULARGE_INTEGER offset;
  HRESULT hr;
  ULONG bytesRead;

  offset.u.HighPart = 0;
3761
  offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
3762 3763 3764 3765

  hr = BlockChainStream_ReadAt(
                    This->rootBlockChain,
                    offset,
3766
                    RAW_DIRENTRY_SIZE,
3767 3768 3769
                    buffer,
                    &bytesRead);

3770 3771 3772
  if (bytesRead != RAW_DIRENTRY_SIZE)
    return STG_E_READFAULT;

3773 3774 3775
  return hr;
}

3776 3777 3778 3779 3780
/******************************************************************************
 *      StorageImpl_WriteRawDirEntry
 *
 * This method will write the raw data from a directory entry in the file.
 *
3781
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3782 3783 3784 3785 3786 3787 3788 3789
 */
HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
{
  ULARGE_INTEGER offset;
  HRESULT hr;
  ULONG bytesRead;

  offset.u.HighPart = 0;
3790
  offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
3791 3792 3793 3794

  hr = BlockChainStream_WriteAt(
                    This->rootBlockChain,
                    offset,
3795
                    RAW_DIRENTRY_SIZE,
3796 3797 3798 3799 3800 3801
                    buffer,
                    &bytesRead);

  return hr;
}

3802 3803 3804 3805 3806
/******************************************************************************
 *      UpdateRawDirEntry
 *
 * Update raw directory entry data from the fields in newData.
 *
3807
 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3808
 */
3809
void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3810
{
3811
  memset(buffer, 0, RAW_DIRENTRY_SIZE);
3812 3813 3814 3815

  memcpy(
    buffer + OFFSET_PS_NAME,
    newData->name,
3816
    DIRENTRY_NAME_BUFFER_LEN );
3817

3818
  memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836

  StorageUtl_WriteWord(
    buffer,
      OFFSET_PS_NAMELENGTH,
      newData->sizeOfNameString);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_LEFTCHILD,
      newData->leftChild);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_RIGHTCHILD,
      newData->rightChild);

  StorageUtl_WriteDWord(
    buffer,
3837 3838
      OFFSET_PS_DIRROOT,
      newData->dirRootEntry);
3839 3840 3841 3842

  StorageUtl_WriteGUID(
    buffer,
      OFFSET_PS_GUID,
3843
      &newData->clsid);
3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_CTIMELOW,
      newData->ctime.dwLowDateTime);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_CTIMEHIGH,
      newData->ctime.dwHighDateTime);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_MTIMELOW,
      newData->mtime.dwLowDateTime);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_MTIMEHIGH,
      newData->ctime.dwHighDateTime);

  StorageUtl_WriteDWord(
    buffer,
      OFFSET_PS_STARTBLOCK,
      newData->startingBlock);

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

3876
/******************************************************************************
3877
 *      Storage32Impl_ReadDirEntry
3878
 *
3879
 * This method will read the specified directory entry.
3880
 */
3881
HRESULT StorageImpl_ReadDirEntry(
3882
  StorageImpl* This,
3883
  DirRef         index,
3884
  DirEntry*      buffer)
3885
{
3886
  BYTE           currentEntry[RAW_DIRENTRY_SIZE];
3887
  HRESULT        readRes;
Thuy Nguyen's avatar
Thuy Nguyen committed
3888

3889
  readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
Alexandre Julliard's avatar
Alexandre Julliard committed
3890

3891
  if (SUCCEEDED(readRes))
3892 3893 3894
  {
    memset(buffer->name, 0, sizeof(buffer->name));
    memcpy(
3895
      buffer->name,
3896
      (WCHAR *)currentEntry+OFFSET_PS_NAME,
3897
      DIRENTRY_NAME_BUFFER_LEN );
Alexandre Julliard's avatar
Alexandre Julliard committed
3898
    TRACE("storage name: %s\n", debugstr_w(buffer->name));
3899

3900
    memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3901

3902
    StorageUtl_ReadWord(
3903
      currentEntry,
3904
      OFFSET_PS_NAMELENGTH,
3905 3906 3907
      &buffer->sizeOfNameString);

    StorageUtl_ReadDWord(
3908
      currentEntry,
3909 3910
      OFFSET_PS_LEFTCHILD,
      &buffer->leftChild);
3911 3912

    StorageUtl_ReadDWord(
3913
      currentEntry,
3914 3915
      OFFSET_PS_RIGHTCHILD,
      &buffer->rightChild);
3916 3917

    StorageUtl_ReadDWord(
3918
      currentEntry,
3919 3920
      OFFSET_PS_DIRROOT,
      &buffer->dirRootEntry);
3921 3922

    StorageUtl_ReadGUID(
3923
      currentEntry,
3924
      OFFSET_PS_GUID,
3925
      &buffer->clsid);
3926 3927

    StorageUtl_ReadDWord(
3928
      currentEntry,
3929 3930
      OFFSET_PS_CTIMELOW,
      &buffer->ctime.dwLowDateTime);
3931 3932

    StorageUtl_ReadDWord(
3933
      currentEntry,
3934 3935
      OFFSET_PS_CTIMEHIGH,
      &buffer->ctime.dwHighDateTime);
3936 3937

    StorageUtl_ReadDWord(
3938
      currentEntry,
3939 3940
      OFFSET_PS_MTIMELOW,
      &buffer->mtime.dwLowDateTime);
3941 3942

    StorageUtl_ReadDWord(
3943
      currentEntry,
3944 3945
      OFFSET_PS_MTIMEHIGH,
      &buffer->mtime.dwHighDateTime);
3946 3947

    StorageUtl_ReadDWord(
3948
      currentEntry,
3949
      OFFSET_PS_STARTBLOCK,
3950 3951 3952
      &buffer->startingBlock);

    StorageUtl_ReadDWord(
3953
      currentEntry,
3954
      OFFSET_PS_SIZE,
3955
      &buffer->size.u.LowPart);
3956

3957
    buffer->size.u.HighPart = 0;
3958 3959
  }

3960
  return readRes;
3961 3962 3963
}

/*********************************************************************
3964
 * Write the specified directory entry to the file
3965
 */
3966
HRESULT StorageImpl_WriteDirEntry(
3967
  StorageImpl*          This,
3968
  DirRef                index,
3969
  const DirEntry*       buffer)
3970
{
3971
  BYTE           currentEntry[RAW_DIRENTRY_SIZE];
3972
  HRESULT        writeRes;
3973

3974
  UpdateRawDirEntry(currentEntry, buffer);
3975

3976
  writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3977
  return writeRes;
3978 3979
}

3980
static BOOL StorageImpl_ReadBigBlock(
3981
  StorageImpl* This,
3982 3983 3984
  ULONG          blockIndex,
  void*          buffer)
{
3985
  ULARGE_INTEGER ulOffset;
3986
  DWORD  read=0;
3987

3988
  ulOffset.u.HighPart = 0;
3989
  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3990

3991
  StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3992 3993 3994 3995 3996 3997 3998 3999

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

  return (read != 0);
4000
}
4001

4002 4003 4004 4005 4006 4007 4008 4009 4010
static BOOL StorageImpl_ReadDWordFromBigBlock(
  StorageImpl*  This,
  ULONG         blockIndex,
  ULONG         offset,
  DWORD*        value)
{
  ULARGE_INTEGER ulOffset;
  DWORD  read;
  DWORD  tmp;
4011

4012
  ulOffset.u.HighPart = 0;
4013
  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4014
  ulOffset.u.LowPart += offset;
4015

4016
  StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4017
  *value = lendian32toh(tmp);
4018
  return (read == sizeof(DWORD));
4019 4020
}

4021
static BOOL StorageImpl_WriteBigBlock(
4022 4023 4024
  StorageImpl*  This,
  ULONG         blockIndex,
  const void*   buffer)
4025
{
4026 4027
  ULARGE_INTEGER ulOffset;
  DWORD  wrote;
4028

4029
  ulOffset.u.HighPart = 0;
4030
  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4031

4032 4033
  StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
  return (wrote == This->bigBlockSize);
4034 4035
}

4036
static BOOL StorageImpl_WriteDWordToBigBlock(
4037
  StorageImpl* This,
4038 4039 4040
  ULONG         blockIndex,
  ULONG         offset,
  DWORD         value)
4041
{
4042 4043
  ULARGE_INTEGER ulOffset;
  DWORD  wrote;
4044

4045
  ulOffset.u.HighPart = 0;
4046
  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4047
  ulOffset.u.LowPart += offset;
4048

4049 4050 4051
  value = htole32(value);
  StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
  return (wrote == sizeof(DWORD));
4052 4053
}

4054 4055 4056 4057 4058 4059 4060
/******************************************************************************
 *              Storage32Impl_SmallBlocksToBigBlocks
 *
 * This method will convert a small block chain to a big block chain.
 * The small block chain will be destroyed.
 */
BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4061
                      StorageImpl* This,
4062 4063 4064 4065
                      SmallBlockChainStream** ppsbChain)
{
  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
  ULARGE_INTEGER size, offset;
4066 4067
  ULONG cbRead, cbWritten;
  ULARGE_INTEGER cbTotalRead;
4068
  DirRef streamEntryRef;
4069
  HRESULT resWrite = S_OK;
4070
  HRESULT resRead;
4071
  DirEntry streamEntry;
4072
  BYTE *buffer;
4073 4074 4075 4076 4077
  BlockChainStream *bbTempChain = NULL;
  BlockChainStream *bigBlockChain = NULL;

  /*
   * Create a temporary big block chain that doesn't have
4078
   * an associated directory entry. This temporary chain will be
4079 4080 4081 4082
   * used to copy data from small blocks to big blocks.
   */
  bbTempChain = BlockChainStream_Construct(This,
                                           &bbHeadOfChain,
4083
                                           DIRENTRY_NULL);
4084
  if(!bbTempChain) return NULL;
4085 4086 4087 4088 4089 4090 4091 4092 4093 4094
  /*
   * Grow the big block chain.
   */
  size = SmallBlockChainStream_GetSize(*ppsbChain);
  BlockChainStream_SetSize(bbTempChain, size);

  /*
   * Copy the contents of the small block chain to the big block chain
   * by small block size increments.
   */
4095 4096
  offset.u.LowPart = 0;
  offset.u.HighPart = 0;
4097
  cbTotalRead.QuadPart = 0;
4098

4099
  buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4100 4101
  do
  {
4102 4103
    resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
                                           offset,
4104
                                           min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4105 4106 4107
                                           buffer,
                                           &cbRead);
    if (FAILED(resRead))
4108 4109 4110 4111
        break;

    if (cbRead > 0)
    {
4112
        cbTotalRead.QuadPart += cbRead;
4113

4114
        resWrite = BlockChainStream_WriteAt(bbTempChain,
4115
                                            offset,
4116
                                            cbRead,
4117 4118
                                            buffer,
                                            &cbWritten);
4119

4120
        if (FAILED(resWrite))
4121
            break;
4122

4123
        offset.u.LowPart += cbRead;
4124
    }
4125 4126 4127 4128 4129
    else
    {
        resRead = STG_E_READFAULT;
        break;
    }
4130
  } while (cbTotalRead.QuadPart < size.QuadPart);
4131
  HeapFree(GetProcessHeap(),0,buffer);
4132

4133 4134 4135
  size.u.HighPart = 0;
  size.u.LowPart  = 0;

4136 4137
  if (FAILED(resRead) || FAILED(resWrite))
  {
4138
    ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4139
    BlockChainStream_SetSize(bbTempChain, size);
4140 4141 4142
    BlockChainStream_Destroy(bbTempChain);
    return NULL;
  }
4143 4144 4145 4146

  /*
   * Destroy the small block chain.
   */
4147
  streamEntryRef = (*ppsbChain)->ownerDirEntry;
4148 4149 4150 4151 4152
  SmallBlockChainStream_SetSize(*ppsbChain, size);
  SmallBlockChainStream_Destroy(*ppsbChain);
  *ppsbChain = 0;

  /*
4153
   * Change the directory entry. This chain is now a big block chain
4154 4155
   * and it doesn't reside in the small blocks chain anymore.
   */
4156
  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4157

4158
  streamEntry.startingBlock = bbHeadOfChain;
4159

4160
  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4161 4162

  /*
4163 4164
   * Destroy the temporary entryless big block chain.
   * Create a new big block chain associated with this entry.
4165 4166 4167 4168
   */
  BlockChainStream_Destroy(bbTempChain);
  bigBlockChain = BlockChainStream_Construct(This,
                                             NULL,
4169
                                             streamEntryRef);
4170 4171 4172 4173

  return bigBlockChain;
}

4174 4175 4176 4177 4178 4179 4180 4181
/******************************************************************************
 *              Storage32Impl_BigBlocksToSmallBlocks
 *
 * This method will convert a big block chain to a small block chain.
 * The big block chain will be destroyed on success.
 */
SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
                           StorageImpl* This,
4182 4183
                           BlockChainStream** ppbbChain,
                           ULARGE_INTEGER newSize)
4184 4185
{
    ULARGE_INTEGER size, offset, cbTotalRead;
4186 4187
    ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
    DirRef streamEntryRef;
4188
    HRESULT resWrite = S_OK, resRead = S_OK;
4189
    DirEntry streamEntry;
4190 4191 4192 4193 4194 4195
    BYTE* buffer;
    SmallBlockChainStream* sbTempChain;

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

    sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4196
            DIRENTRY_NULL);
4197 4198 4199 4200

    if(!sbTempChain)
        return NULL;

4201
    SmallBlockChainStream_SetSize(sbTempChain, newSize);
4202
    size = BlockChainStream_GetSize(*ppbbChain);
4203
    size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4204 4205 4206 4207 4208

    offset.u.HighPart = 0;
    offset.u.LowPart = 0;
    cbTotalRead.QuadPart = 0;
    buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4209
    while(cbTotalRead.QuadPart < size.QuadPart)
4210 4211
    {
        resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4212 4213
                min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
                buffer, &cbRead);
4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227

        if(FAILED(resRead))
            break;

        if(cbRead > 0)
        {
            cbTotalRead.QuadPart += cbRead;

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

            if(FAILED(resWrite))
                break;

4228
            offset.u.LowPart += cbRead;
4229
        }
4230 4231 4232 4233 4234
        else
        {
            resRead = STG_E_READFAULT;
            break;
        }
4235
    }
4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249
    HeapFree(GetProcessHeap(), 0, buffer);

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

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

    /* destroy the original big block chain */
4250
    streamEntryRef = (*ppbbChain)->ownerDirEntry;
4251 4252 4253 4254
    BlockChainStream_SetSize(*ppbbChain, size);
    BlockChainStream_Destroy(*ppbbChain);
    *ppbbChain = NULL;

4255 4256 4257
    StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
    streamEntry.startingBlock = sbHeadOfChain;
    StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4258 4259

    SmallBlockChainStream_Destroy(sbTempChain);
4260
    return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4261 4262
}

4263 4264 4265
static HRESULT StorageBaseImpl_CopyStream(
  StorageBaseImpl *dst, DirRef dst_entry,
  StorageBaseImpl *src, DirRef src_entry)
4266 4267
{
  HRESULT hr;
4268 4269 4270 4271
  BYTE data[4096];
  DirEntry srcdata;
  ULARGE_INTEGER bytes_copied;
  ULONG bytestocopy, bytesread, byteswritten;
4272

4273
  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4274 4275 4276

  if (SUCCEEDED(hr))
  {
4277
    hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4278

4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290
    bytes_copied.QuadPart = 0;
    while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
    {
      bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);

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

      if (SUCCEEDED(hr))
        hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
          data, &byteswritten);
4291 4292 4293 4294 4295
      if (SUCCEEDED(hr))
      {
        if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
        bytes_copied.QuadPart += byteswritten;
      }
4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354
    }
  }

  return hr;
}

static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
{
  DirRef result=This->firstFreeEntry;

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

  if (result == This->entries_size)
  {
    ULONG new_size = This->entries_size * 2;
    TransactedDirEntry *new_entries;

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

    memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
    HeapFree(GetProcessHeap(), 0, This->entries);

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

  This->entries[result].inuse = 1;

  This->firstFreeEntry = result+1;

  return result;
}

static DirRef TransactedSnapshotImpl_CreateStubEntry(
  TransactedSnapshotImpl *This, DirRef parentEntryRef)
{
  DirRef stubEntryRef;
  TransactedDirEntry *entry;

  stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);

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

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

    entry->read = 0;
  }

  return stubEntryRef;
}

static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
  TransactedSnapshotImpl *This, DirRef entry)
{
  HRESULT hr=S_OK;
4355
  DirEntry data;
4356 4357 4358 4359 4360

  if (!This->entries[entry].read)
  {
    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
        This->entries[entry].transactedParentEntry,
4361
        &data);
4362

4363
    if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4364
    {
4365
      data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4366

4367
      if (data.leftChild == DIRENTRY_NULL)
4368 4369 4370
        hr = E_OUTOFMEMORY;
    }

4371
    if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4372
    {
4373
      data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4374

4375
      if (data.rightChild == DIRENTRY_NULL)
4376 4377 4378
        hr = E_OUTOFMEMORY;
    }

4379
    if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4380
    {
4381
      data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4382

4383
      if (data.dirRootEntry == DIRENTRY_NULL)
4384 4385
        hr = E_OUTOFMEMORY;
    }
4386 4387

    if (SUCCEEDED(hr))
4388 4389
    {
      memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4390
      This->entries[entry].read = 1;
4391
    }
4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418
  }

  return hr;
}

static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
  TransactedSnapshotImpl *This, DirRef entry)
{
  HRESULT hr = S_OK;

  if (!This->entries[entry].stream_dirty)
  {
    DirEntry new_entrydata;

    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;

    hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
      &This->entries[entry].stream_entry);

    if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4419
    {
4420 4421 4422 4423 4424 4425
      hr = StorageBaseImpl_CopyStream(
        This->scratch, This->entries[entry].stream_entry,
        This->transactedParent, This->entries[entry].transactedParentEntry);

      if (FAILED(hr))
        StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4426 4427 4428
    }

    if (SUCCEEDED(hr))
4429 4430 4431 4432 4433 4434 4435 4436
      This->entries[entry].stream_dirty = 1;

    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);
4437

4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548
      if (delete_ref != DIRENTRY_NULL)
        This->entries[delete_ref].deleted = 1;

      This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }

  return hr;
}

/* Find the first entry in a depth-first traversal. */
static DirRef TransactedSnapshotImpl_FindFirstChild(
  TransactedSnapshotImpl* This, DirRef parent)
{
  DirRef cursor, prev;
  TransactedDirEntry *entry;

  cursor = parent;
  entry = &This->entries[cursor];
  while (entry->read)
  {
    if (entry->data.leftChild != DIRENTRY_NULL)
    {
      prev = cursor;
      cursor = entry->data.leftChild;
      entry = &This->entries[cursor];
      entry->parent = prev;
    }
    else if (entry->data.rightChild != DIRENTRY_NULL)
    {
      prev = cursor;
      cursor = entry->data.rightChild;
      entry = &This->entries[cursor];
      entry->parent = prev;
    }
    else if (entry->data.dirRootEntry != DIRENTRY_NULL)
    {
      prev = cursor;
      cursor = entry->data.dirRootEntry;
      entry = &This->entries[cursor];
      entry->parent = prev;
    }
    else
      break;
  }

  return cursor;
}

/* Find the next entry in a depth-first traversal. */
static DirRef TransactedSnapshotImpl_FindNextChild(
  TransactedSnapshotImpl* This, DirRef current)
{
  DirRef parent;
  TransactedDirEntry *parent_entry;

  parent = This->entries[current].parent;
  parent_entry = &This->entries[parent];

  if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
  {
    if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
    {
      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);
    }
  }

  return parent;
}

/* 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)
{
  return entry != DIRENTRY_NULL &&
    This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
}

/* Destroy the entries created by CopyTree. */
static void TransactedSnapshotImpl_DestroyTemporaryCopy(
  TransactedSnapshotImpl* This, DirRef stop)
{
  DirRef cursor;
  TransactedDirEntry *entry;
  ULARGE_INTEGER zero;

  zero.QuadPart = 0;

  if (!This->entries[This->base.storageDirEntry].read)
    return;

  cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;

  if (cursor == DIRENTRY_NULL)
    return;

  cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);

  while (cursor != DIRENTRY_NULL && cursor != stop)
  {
    if (TransactedSnapshotImpl_MadeCopy(This, cursor))
    {
      entry = &This->entries[cursor];

4549 4550 4551
      if (entry->stream_dirty)
        StorageBaseImpl_StreamSetSize(This->transactedParent,
          entry->newTransactedParentEntry, zero);
4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567

      StorageBaseImpl_DestroyDirEntry(This->transactedParent,
        entry->newTransactedParentEntry);

      entry->newTransactedParentEntry = entry->transactedParentEntry;
    }

    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
  }
}

/* Make a copy of our edited tree that we can use in the parent. */
static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
{
  DirRef cursor;
  TransactedDirEntry *entry;
4568
  HRESULT hr = S_OK;
4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617

  cursor = This->base.storageDirEntry;
  entry = &This->entries[cursor];
  entry->parent = DIRENTRY_NULL;
  entry->newTransactedParentEntry = entry->transactedParentEntry;

  if (entry->data.dirRootEntry == DIRENTRY_NULL)
    return S_OK;

  This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;

  cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
  entry = &This->entries[cursor];

  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;

      memcpy(&newData, &entry->data, sizeof(DirEntry));

      newData.size.QuadPart = 0;
      newData.startingBlock = BLOCK_END_OF_CHAIN;

      if (newData.leftChild != DIRENTRY_NULL)
        newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;

      if (newData.rightChild != DIRENTRY_NULL)
        newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;

      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;
      }

4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630
      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);
      }

4631 4632 4633 4634 4635 4636 4637 4638 4639 4640
      if (FAILED(hr))
      {
        cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
        return hr;
      }
    }

    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
    entry = &This->entries[cursor];
4641 4642 4643 4644 4645
  }

  return hr;
}

4646 4647 4648 4649
static HRESULT WINAPI TransactedSnapshotImpl_Commit(
  IStorage*            iface,
  DWORD                  grfCommitFlags)  /* [in] */
{
4650
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4651 4652 4653 4654
  TransactedDirEntry *root_entry;
  DirRef i, dir_root_ref;
  DirEntry data;
  ULARGE_INTEGER zero;
4655
  HRESULT hr;
4656 4657

  zero.QuadPart = 0;
4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670

  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;

  /* 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. */

4671
  root_entry = &This->entries[This->base.storageDirEntry];
4672

4673 4674
  if (!root_entry->read)
    return S_OK;
4675

4676 4677
  hr = TransactedSnapshotImpl_CopyTree(This);
  if (FAILED(hr)) return hr;
4678

4679 4680
  if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
    dir_root_ref = DIRENTRY_NULL;
4681
  else
4682
    dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4683

4684 4685
  hr = StorageBaseImpl_Flush(This->transactedParent);

4686
  /* Update the storage to use the new data in one step. */
4687 4688 4689
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
      root_entry->transactedParentEntry, &data);
4690 4691 4692

  if (SUCCEEDED(hr))
  {
4693 4694 4695 4696
    data.dirRootEntry = dir_root_ref;
    data.clsid = root_entry->data.clsid;
    data.ctime = root_entry->data.ctime;
    data.mtime = root_entry->data.mtime;
4697 4698

    hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4699
      root_entry->transactedParentEntry, &data);
4700 4701
  }

4702 4703 4704 4705 4706
  /* 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);

4707 4708 4709
  if (SUCCEEDED(hr))
  {
    /* Destroy the old now-orphaned data. */
4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739
    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 = 0;
          }
          entry->dirty = 0;
          entry->transactedParentEntry = entry->newTransactedParentEntry;
        }
      }
    }
4740 4741 4742
  }
  else
  {
4743
    TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4744 4745
  }

4746 4747 4748
  if (SUCCEEDED(hr))
    hr = StorageBaseImpl_Flush(This->transactedParent);

4749
  return hr;
4750 4751 4752 4753 4754
}

static HRESULT WINAPI TransactedSnapshotImpl_Revert(
  IStorage*            iface)
{
4755
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4756 4757
  ULARGE_INTEGER zero;
  ULONG i;
4758 4759 4760 4761 4762 4763

  TRACE("(%p)\n", iface);

  /* Destroy the open objects. */
  StorageBaseImpl_DeleteAll(&This->base);

4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780
  /* Clear out the scratch file. */
  zero.QuadPart = 0;
  for (i=0; i<This->entries_size; i++)
  {
    if (This->entries[i].stream_dirty)
    {
      StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
        zero);

      StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
    }
  }

  memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);

  This->firstFreeEntry = 0;
  This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4781

4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800
  return S_OK;
}

static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
{
  if (!This->reverted)
  {
    TRACE("Storage invalidated (stg=%p)\n", This);

    This->reverted = 1;

    StorageBaseImpl_DeleteAll(This);
  }
}

static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;

4801
  IStorage_Revert(&This->base.IStorage_iface);
4802 4803
  IStorage_Release(&This->transactedParent->IStorage_iface);
  IStorage_Release(&This->scratch->IStorage_iface);
4804
  HeapFree(GetProcessHeap(), 0, This->entries);
4805 4806 4807
  HeapFree(GetProcessHeap(), 0, This);
}

4808 4809 4810 4811 4812 4813
static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
{
  /* We only need to flush when committing. */
  return S_OK;
}

4814 4815 4816 4817 4818 4819 4820
static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;

  return StorageBaseImpl_GetFilename(This->transactedParent, result);
}

4821 4822 4823 4824
static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
  const DirEntry *newData, DirRef *index)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4825 4826 4827 4828 4829 4830 4831 4832
  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];
4833

4834 4835 4836 4837 4838 4839 4840 4841 4842 4843
  new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
  new_entry->read = 1;
  new_entry->dirty = 1;
  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;
4844 4845 4846 4847 4848 4849
}

static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876
  HRESULT hr;

  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 = 1;

    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 = 1;

      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }
4877

4878
  return S_OK;
4879 4880 4881 4882 4883 4884
}

static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4885 4886 4887 4888 4889 4890 4891 4892
  HRESULT hr;

  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);
4893

4894
  return S_OK;
4895 4896 4897 4898 4899 4900 4901
}

static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
  DirRef index)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;

4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916
  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 = 1;
  }

  return S_OK;
4917 4918 4919 4920 4921 4922 4923
}

static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;

4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940
  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);
  }
4941 4942 4943 4944 4945 4946
}

static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4947 4948 4949 4950 4951 4952 4953
  HRESULT hr;

  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
  if (FAILED(hr)) return hr;

  hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
  if (FAILED(hr)) return hr;
4954

4955 4956 4957 4958 4959 4960 4961 4962 4963
  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;
4964 4965 4966 4967 4968 4969
}

static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER newsize)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005
  HRESULT hr;

  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 = 0;
    }
    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 = 1;

      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
    }
  }
  else
  {
    hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
    if (FAILED(hr)) return hr;
5006

5007 5008 5009 5010 5011 5012 5013 5014
    hr = StorageBaseImpl_StreamSetSize(This->scratch,
      This->entries[index].stream_entry, newsize);
  }

  if (SUCCEEDED(hr))
    This->entries[index].data.size = newsize;

  return hr;
5015 5016
}

5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041
static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
  DirRef dst, DirRef src)
{
  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
  HRESULT hr;
  TransactedDirEntry *dst_entry, *src_entry;

  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;
}

5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067
static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
{
    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
};

static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
{
  TransactedSnapshotImpl_Destroy,
  TransactedSnapshotImpl_Invalidate,
5068
  TransactedSnapshotImpl_Flush,
5069
  TransactedSnapshotImpl_GetFilename,
5070 5071 5072 5073 5074 5075
  TransactedSnapshotImpl_CreateDirEntry,
  TransactedSnapshotImpl_WriteDirEntry,
  TransactedSnapshotImpl_ReadDirEntry,
  TransactedSnapshotImpl_DestroyDirEntry,
  TransactedSnapshotImpl_StreamReadAt,
  TransactedSnapshotImpl_StreamWriteAt,
5076 5077
  TransactedSnapshotImpl_StreamSetSize,
  TransactedSnapshotImpl_StreamLink
5078 5079 5080 5081 5082
};

static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
  TransactedSnapshotImpl** result)
{
5083 5084
  HRESULT hr;

5085 5086 5087
  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
  if (*result)
  {
5088 5089 5090
    IStorage *scratch;

    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5091 5092

    /* This is OK because the property set storage functions use the IStorage functions. */
5093
    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5094 5095 5096 5097 5098 5099 5100 5101 5102 5103
    (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;

    list_init(&(*result)->base.strmHead);

    list_init(&(*result)->base.storageHead);

    (*result)->base.ref = 1;

    (*result)->base.openFlags = parentStorage->openFlags;

5104
    /* Create a new temporary storage to act as the scratch file. */
5105
    hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5106 5107
        0, &scratch);
    (*result)->scratch = impl_from_IStorage(scratch);
5108

5109 5110
    if (SUCCEEDED(hr))
    {
5111
        ULONG num_entries = 20;
5112

5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127
        (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
        (*result)->entries_size = num_entries;
        (*result)->firstFreeEntry = 0;

        if ((*result)->entries)
        {
            /* parentStorage already has 1 reference, which we take over here. */
            (*result)->transactedParent = parentStorage;

            parentStorage->transactedChild = (StorageBaseImpl*)*result;

            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
        }
        else
        {
5128
            IStorage_Release(scratch);
5129 5130 5131

            hr = E_OUTOFMEMORY;
        }
5132 5133 5134 5135 5136
    }

    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));

    return hr;
5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162
  }
  else
    return E_OUTOFMEMORY;
}

static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
  StorageBaseImpl** result)
{
  static int fixme=0;

  if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
  {
    FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
  }

  return TransactedSnapshotImpl_Construct(parentStorage,
    (TransactedSnapshotImpl**)result);
}

static HRESULT Storage_Construct(
  HANDLE       hFile,
  LPCOLESTR    pwcsName,
  ILockBytes*  pLkbyt,
  DWORD        openFlags,
  BOOL         fileBased,
  BOOL         create,
5163
  ULONG        sector_size,
5164 5165 5166 5167 5168 5169
  StorageBaseImpl** result)
{
  StorageImpl *newStorage;
  StorageBaseImpl *newTransactedStorage;
  HRESULT hr;

5170
  hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5171 5172 5173 5174 5175 5176
  if (FAILED(hr)) goto end;

  if (openFlags & STGM_TRANSACTED)
  {
    hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
    if (FAILED(hr))
5177
      IStorage_Release(&newStorage->base.IStorage_iface);
5178 5179 5180 5181 5182 5183 5184 5185 5186 5187
    else
      *result = newTransactedStorage;
  }
  else
    *result = &newStorage->base;

end:
  return hr;
}

5188
static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5189
{
5190 5191
  StorageInternalImpl* This = (StorageInternalImpl*) base;

5192
  if (!This->base.reverted)
5193 5194 5195
  {
    TRACE("Storage invalidated (stg=%p)\n", This);

5196 5197 5198 5199
    This->base.reverted = 1;

    This->parentStorage = NULL;

5200 5201 5202 5203 5204 5205
    StorageBaseImpl_DeleteAll(&This->base);

    list_remove(&This->ParentListEntry);
  }
}

5206
static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5207
{
5208 5209
  StorageInternalImpl* This = (StorageInternalImpl*) iface;

5210
  StorageInternalImpl_Invalidate(&This->base);
5211

5212 5213 5214
  HeapFree(GetProcessHeap(), 0, This);
}

5215 5216 5217 5218 5219 5220 5221
static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
{
  StorageInternalImpl* This = (StorageInternalImpl*) iface;

  return StorageBaseImpl_Flush(This->parentStorage);
}

5222 5223 5224 5225 5226 5227 5228
static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
{
  StorageInternalImpl* This = (StorageInternalImpl*) iface;

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

5229 5230 5231
static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
  const DirEntry *newData, DirRef *index)
{
5232 5233 5234
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5235 5236 5237
    newData, index);
}

5238 5239 5240
static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
  DirRef index, const DirEntry *data)
{
5241 5242 5243
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5244 5245 5246
    index, data);
}

5247 5248 5249
static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
  DirRef index, DirEntry *data)
{
5250 5251 5252
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5253 5254 5255
    index, data);
}

5256 5257 5258
static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
  DirRef index)
{
5259 5260 5261
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5262 5263 5264
    index);
}

5265 5266 5267
static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
5268 5269 5270
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_StreamReadAt(This->parentStorage,
5271 5272 5273
    index, offset, size, buffer, bytesRead);
}

5274 5275 5276
static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
{
5277 5278 5279
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5280 5281 5282
    index, offset, size, buffer, bytesWritten);
}

5283 5284 5285
static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
  DirRef index, ULARGE_INTEGER newsize)
{
5286 5287 5288
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_StreamSetSize(This->parentStorage,
5289 5290 5291
    index, newsize);
}

5292 5293 5294 5295 5296 5297 5298 5299 5300
static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
  DirRef dst, DirRef src)
{
  StorageInternalImpl* This = (StorageInternalImpl*) base;

  return StorageBaseImpl_StreamLink(This->parentStorage,
    dst, src);
}

5301 5302 5303 5304 5305
/******************************************************************************
**
** Storage32InternalImpl_Commit
**
*/
5306
static HRESULT WINAPI StorageInternalImpl_Commit(
5307
  IStorage*            iface,
5308
  DWORD                  grfCommitFlags)  /* [in] */
5309
{
5310
  StorageBaseImpl* This = impl_from_IStorage(iface);
5311
  TRACE("(%p,%x)\n", iface, grfCommitFlags);
5312
  return StorageBaseImpl_Flush(This);
5313
}
5314

5315 5316 5317 5318 5319
/******************************************************************************
**
** Storage32InternalImpl_Revert
**
*/
5320
static HRESULT WINAPI StorageInternalImpl_Revert(
5321
  IStorage*            iface)
5322
{
5323
  FIXME("(%p): stub\n", iface);
5324 5325 5326
  return S_OK;
}

5327
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5328
{
5329
  IStorage_Release(&This->parentStorage->IStorage_iface);
5330 5331 5332
  HeapFree(GetProcessHeap(), 0, This);
}

5333
static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5334
  IEnumSTATSTG*     iface,
5335 5336 5337
  REFIID            riid,
  void**            ppvObject)
{
5338
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5339

5340 5341 5342 5343 5344
  if (ppvObject==0)
    return E_INVALIDARG;

  *ppvObject = 0;

5345
  if (IsEqualGUID(&IID_IUnknown, riid) ||
5346
      IsEqualGUID(&IID_IEnumSTATSTG, riid))
5347
  {
5348
    *ppvObject = This;
5349
    IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5350
    return S_OK;
5351 5352
  }

5353
  return E_NOINTERFACE;
5354
}
5355

5356
static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
5357
  IEnumSTATSTG* iface)
5358
{
5359
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5360
  return InterlockedIncrement(&This->ref);
5361
}
5362

5363
static ULONG   WINAPI IEnumSTATSTGImpl_Release(
5364
  IEnumSTATSTG* iface)
5365
{
5366
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5367

5368 5369
  ULONG newRef;

5370
  newRef = InterlockedDecrement(&This->ref);
5371 5372 5373 5374 5375 5376

  if (newRef==0)
  {
    IEnumSTATSTGImpl_Destroy(This);
  }

5377
  return newRef;
5378 5379
}

5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424
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];

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

  while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
  {
    hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);

    if (SUCCEEDED(hr))
    {
      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;
      }
    }
  }

  if (SUCCEEDED(hr))
  {
    *ref = result;
    if (result != DIRENTRY_NULL)
      memcpy(This->name, result_name, sizeof(result_name));
  }

  return hr;
}

5425
static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5426
  IEnumSTATSTG* iface,
5427 5428 5429 5430
  ULONG             celt,
  STATSTG*          rgelt,
  ULONG*            pceltFetched)
{
5431
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5432

5433
  DirEntry    currentEntry;
5434 5435
  STATSTG*    currentReturnStruct = rgelt;
  ULONG       objectFetched       = 0;
5436
  DirRef      currentSearchNode;
5437
  HRESULT     hr=S_OK;
5438 5439

  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5440 5441
    return E_INVALIDARG;

5442 5443 5444
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;

5445 5446 5447 5448 5449 5450
  /*
   * To avoid the special case, get another pointer to a ULONG value if
   * the caller didn't supply one.
   */
  if (pceltFetched==0)
    pceltFetched = &objectFetched;
5451

5452 5453 5454 5455 5456 5457
  /*
   * 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
   */
  *pceltFetched = 0;

5458
  while ( *pceltFetched < celt )
5459
  {
5460 5461 5462 5463
    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);

    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
      break;
5464 5465

    /*
5466
     * Read the entry from the storage.
5467
     */
5468
    StorageBaseImpl_ReadDirEntry(This->parentStorage,
5469
      currentSearchNode,
5470
      &currentEntry);
5471 5472 5473 5474

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

5480 5481 5482 5483 5484 5485 5486
    /*
     * Step to the next item in the iteration
     */
    (*pceltFetched)++;
    currentReturnStruct++;
  }

5487 5488
  if (SUCCEEDED(hr) && *pceltFetched != celt)
    hr = S_FALSE;
5489

5490
  return hr;
5491 5492
}

5493

5494
static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5495
  IEnumSTATSTG* iface,
5496 5497
  ULONG             celt)
{
5498
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5499

5500
  ULONG       objectFetched = 0;
5501
  DirRef      currentSearchNode;
5502
  HRESULT     hr=S_OK;
5503

5504 5505 5506
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;

5507
  while ( (objectFetched < celt) )
5508
  {
5509
    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5510

5511 5512
    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
      break;
5513

5514 5515 5516
    objectFetched++;
  }

5517 5518
  if (SUCCEEDED(hr) && objectFetched != celt)
    return S_FALSE;
5519

5520
  return hr;
5521
}
5522

5523
static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5524
  IEnumSTATSTG* iface)
5525
{
5526
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5527

5528 5529 5530
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;

5531
  This->name[0] = 0;
5532

5533
  return S_OK;
5534
}
5535

5536
static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5537
  IEnumSTATSTG* iface,
5538 5539
  IEnumSTATSTG**    ppenum)
{
5540
  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5541

5542 5543
  IEnumSTATSTGImpl* newClone;

5544 5545 5546
  if (This->parentStorage->reverted)
    return STG_E_REVERTED;

5547 5548 5549 5550 5551
  /*
   * Perform a sanity check on the parameters.
   */
  if (ppenum==0)
    return E_INVALIDARG;
5552

5553
  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5554
               This->storageDirEntry);
5555

5556

5557 5558 5559 5560
  /*
   * The new clone enumeration must point to the same current node as
   * the ole one.
   */
5561
  memcpy(newClone->name, This->name, sizeof(newClone->name));
5562

5563
  *ppenum = &newClone->IEnumSTATSTG_iface;
5564

5565 5566 5567 5568
  /*
   * Don't forget to nail down a reference to the clone before
   * returning it.
   */
5569
  IEnumSTATSTGImpl_AddRef(*ppenum);
5570 5571 5572 5573

  return S_OK;
}

5574 5575 5576
/*
 * Virtual function table for the IEnumSTATSTGImpl class.
 */
5577
static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591
{
    IEnumSTATSTGImpl_QueryInterface,
    IEnumSTATSTGImpl_AddRef,
    IEnumSTATSTGImpl_Release,
    IEnumSTATSTGImpl_Next,
    IEnumSTATSTGImpl_Skip,
    IEnumSTATSTGImpl_Reset,
    IEnumSTATSTGImpl_Clone
};

/******************************************************************************
** IEnumSTATSTGImpl implementation
*/

5592
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5593
  StorageBaseImpl* parentStorage,
5594
  DirRef         storageDirEntry)
5595 5596 5597 5598 5599 5600 5601 5602 5603 5604
{
  IEnumSTATSTGImpl* newEnumeration;

  newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));

  if (newEnumeration!=0)
  {
    /*
     * Set-up the virtual function table and reference count.
     */
5605
    newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5606 5607 5608 5609 5610 5611 5612
    newEnumeration->ref       = 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;
5613
    IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5614

5615
    newEnumeration->storageDirEntry   = storageDirEntry;
5616 5617 5618 5619

    /*
     * Make sure the current node of the iterator is the first one.
     */
5620
    IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5621 5622 5623 5624 5625 5626 5627 5628
  }

  return newEnumeration;
}

/*
 * Virtual function table for the Storage32InternalImpl class.
 */
5629
static const IStorageVtbl Storage32InternalImpl_Vtbl =
5630 5631 5632 5633 5634 5635
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
5636
    StorageBaseImpl_CreateStorage,
5637
    StorageBaseImpl_OpenStorage,
5638
    StorageBaseImpl_CopyTo,
5639
    StorageBaseImpl_MoveElementTo,
5640 5641 5642
    StorageInternalImpl_Commit,
    StorageInternalImpl_Revert,
    StorageBaseImpl_EnumElements,
5643
    StorageBaseImpl_DestroyElement,
5644
    StorageBaseImpl_RenameElement,
5645
    StorageBaseImpl_SetElementTimes,
5646
    StorageBaseImpl_SetClass,
5647
    StorageBaseImpl_SetStateBits,
5648 5649 5650
    StorageBaseImpl_Stat
};

5651 5652
static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
{
5653
  StorageInternalImpl_Destroy,
5654
  StorageInternalImpl_Invalidate,
5655
  StorageInternalImpl_Flush,
5656
  StorageInternalImpl_GetFilename,
5657
  StorageInternalImpl_CreateDirEntry,
5658
  StorageInternalImpl_WriteDirEntry,
5659
  StorageInternalImpl_ReadDirEntry,
5660
  StorageInternalImpl_DestroyDirEntry,
5661
  StorageInternalImpl_StreamReadAt,
5662
  StorageInternalImpl_StreamWriteAt,
5663 5664
  StorageInternalImpl_StreamSetSize,
  StorageInternalImpl_StreamLink
5665 5666
};

5667 5668 5669 5670
/******************************************************************************
** Storage32InternalImpl implementation
*/

5671
static StorageInternalImpl* StorageInternalImpl_Construct(
5672
  StorageBaseImpl* parentStorage,
5673
  DWORD        openFlags,
5674
  DirRef       storageDirEntry)
5675 5676 5677
{
  StorageInternalImpl* newStorage;

5678
  newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5679 5680 5681

  if (newStorage!=0)
  {
5682
    list_init(&newStorage->base.strmHead);
5683 5684

    list_init(&newStorage->base.storageHead);
5685

5686 5687 5688
    /*
     * Initialize the virtual function table.
     */
5689
    newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5690
    newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5691
    newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5692
    newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5693

5694 5695
    newStorage->base.reverted = 0;

5696 5697
    newStorage->base.ref = 1;

5698
    newStorage->parentStorage = parentStorage;
5699 5700

    /*
5701
     * Keep a reference to the directory entry of this storage
5702
     */
5703
    newStorage->base.storageDirEntry = storageDirEntry;
5704

5705 5706
    newStorage->base.create = 0;

5707 5708 5709 5710 5711 5712
    return newStorage;
  }

  return 0;
}

5713 5714 5715 5716
/******************************************************************************
** StorageUtl implementation
*/

5717
void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5718
{
5719 5720 5721
  WORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(WORD));
5722
  *value = lendian16toh(tmp);
5723 5724
}

5725
void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5726
{
5727
  value = htole16(value);
5728
  memcpy(buffer+offset, &value, sizeof(WORD));
5729 5730
}

5731
void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5732
{
5733 5734 5735
  DWORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(DWORD));
5736
  *value = lendian32toh(tmp);
5737 5738
}

5739
void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5740
{
5741
  value = htole32(value);
5742
  memcpy(buffer+offset, &value, sizeof(DWORD));
5743 5744
}

Juan Lang's avatar
Juan Lang committed
5745 5746 5747 5748 5749 5750 5751
void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
 ULARGE_INTEGER* value)
{
#ifdef WORDS_BIGENDIAN
    ULARGE_INTEGER tmp;

    memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5752 5753
    value->u.LowPart = htole32(tmp.u.HighPart);
    value->u.HighPart = htole32(tmp.u.LowPart);
Juan Lang's avatar
Juan Lang committed
5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764
#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;

5765 5766
    tmp.u.LowPart = htole32(value->u.HighPart);
    tmp.u.HighPart = htole32(value->u.LowPart);
Juan Lang's avatar
Juan Lang committed
5767 5768 5769 5770 5771 5772
    memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
#else
    memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
#endif
}

5773
void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5774 5775 5776 5777 5778
{
  StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
  StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
  StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));

5779
  memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5780 5781
}

5782
void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5783 5784 5785 5786 5787
{
  StorageUtl_WriteDWord(buffer, offset,   value->Data1);
  StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
  StorageUtl_WriteWord(buffer,  offset+6, value->Data3);

5788
  memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5789 5790
}

5791
void StorageUtl_CopyDirEntryToSTATSTG(
5792
  StorageBaseImpl*      storage,
5793
  STATSTG*              destination,
5794
  const DirEntry*       source,
5795
  int                   statFlags)
5796 5797 5798 5799
{
  /*
   * The copy of the string occurs only when the flag is not set
   */
5800 5801 5802 5803 5804 5805 5806 5807
  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) )
5808 5809 5810 5811 5812
  {
    destination->pwcsName = 0;
  }
  else
  {
5813
    destination->pwcsName =
5814
      CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5815

5816
    strcpyW(destination->pwcsName, source->name);
5817
  }
5818

5819
  switch (source->stgType)
5820
  {
5821 5822
    case STGTY_STORAGE:
    case STGTY_ROOT:
5823 5824
      destination->type = STGTY_STORAGE;
      break;
5825
    case STGTY_STREAM:
5826 5827 5828 5829
      destination->type = STGTY_STREAM;
      break;
    default:
      destination->type = STGTY_STREAM;
5830
      break;
5831 5832 5833
  }

  destination->cbSize            = source->size;
5834
/*
5835 5836
  currentReturnStruct->mtime     = {0}; TODO
  currentReturnStruct->ctime     = {0};
5837
  currentReturnStruct->atime     = {0};
5838 5839
*/
  destination->grfMode           = 0;
5840
  destination->grfLocksSupported = 0;
5841
  destination->clsid             = source->clsid;
5842 5843
  destination->grfStateBits      = 0;
  destination->reserved          = 0;
5844 5845 5846 5847 5848 5849
}

/******************************************************************************
** BlockChainStream implementation
*/

5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956
/* Read and save the index of all blocks in this stream. */
HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
{
  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. */
ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
{
  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;
}

5957 5958 5959 5960 5961 5962 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
HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
    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;
      result->dirty = 0;
    }

    result->read = 0;
    result->index = index;
    result->sector = *sector;
  }

  *block = result;
  return S_OK;
}

6004
BlockChainStream* BlockChainStream_Construct(
6005
  StorageImpl* parentStorage,
6006
  ULONG*         headOfStreamPlaceHolder,
6007
  DirRef         dirEntry)
6008 6009 6010 6011 6012 6013 6014
{
  BlockChainStream* newStream;

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));

  newStream->parentStorage           = parentStorage;
  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6015
  newStream->ownerDirEntry           = dirEntry;
6016 6017 6018
  newStream->indexCache              = NULL;
  newStream->indexCacheLen           = 0;
  newStream->indexCacheSize          = 0;
6019 6020 6021 6022 6023
  newStream->cachedBlocks[0].index = 0xffffffff;
  newStream->cachedBlocks[0].dirty = 0;
  newStream->cachedBlocks[1].index = 0xffffffff;
  newStream->cachedBlocks[1].dirty = 0;
  newStream->blockToEvict          = 0;
Thuy Nguyen's avatar
Thuy Nguyen committed
6024

6025
  if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
Thuy Nguyen's avatar
Thuy Nguyen committed
6026
  {
6027 6028 6029
    HeapFree(GetProcessHeap(), 0, newStream->indexCache);
    HeapFree(GetProcessHeap(), 0, newStream);
    return NULL;
Thuy Nguyen's avatar
Thuy Nguyen committed
6030
  }
6031 6032 6033 6034

  return newStream;
}

6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051
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))
        This->cachedBlocks[i].dirty = 0;
      else
        return STG_E_WRITEFAULT;
    }
  }
  return S_OK;
}

6052 6053
void BlockChainStream_Destroy(BlockChainStream* This)
{
6054
  if (This)
6055 6056
  {
    BlockChainStream_Flush(This);
6057
    HeapFree(GetProcessHeap(), 0, This->indexCache);
6058
  }
6059 6060 6061 6062 6063 6064 6065
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
 *      BlockChainStream_GetHeadOfChain
 *
 * Returns the head of this stream chain.
6066
 * Some special chains don't have directory entries, their heads are kept in
6067 6068 6069
 * This->headOfStreamPlaceHolder.
 *
 */
6070
static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6071
{
6072
  DirEntry  chainEntry;
6073
  HRESULT   hr;
6074 6075 6076 6077

  if (This->headOfStreamPlaceHolder != 0)
    return *(This->headOfStreamPlaceHolder);

6078
  if (This->ownerDirEntry != DIRENTRY_NULL)
6079
  {
6080
    hr = StorageImpl_ReadDirEntry(
6081
                      This->parentStorage,
6082
                      This->ownerDirEntry,
6083
                      &chainEntry);
6084

6085
    if (SUCCEEDED(hr))
6086
    {
6087
      return chainEntry.startingBlock;
6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099
    }
  }

  return BLOCK_END_OF_CHAIN;
}

/******************************************************************************
 *       BlockChainStream_GetCount
 *
 * 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!
 */
6100
static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6101
{
6102
  return This->numBlocks;
6103 6104 6105
}

/******************************************************************************
6106
 *      BlockChainStream_ReadAt
6107 6108 6109 6110 6111
 *
 * 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.
 */
6112
HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6113 6114 6115 6116 6117
  ULARGE_INTEGER offset,
  ULONG          size,
  void*          buffer,
  ULONG*         bytesRead)
{
6118 6119
  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
6120 6121 6122
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  BYTE* bufferWalker;
6123
  ULARGE_INTEGER stream_size;
6124 6125
  HRESULT hr;
  BlockChainBlock *cachedBlock;
6126 6127

  TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6128 6129 6130 6131

  /*
   * Find the first block in the stream that contains part of the buffer.
   */
6132
  blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6133

6134 6135 6136 6137 6138 6139 6140
  *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;
6141

6142 6143 6144 6145
  /*
   * Start reading the buffer.
   */
  bufferWalker = buffer;
6146

6147
  while (size > 0)
6148
  {
6149 6150
    ULARGE_INTEGER ulOffset;
    DWORD bytesReadAt;
6151

6152 6153 6154
    /*
     * Calculate how many bytes we can copy from this big block.
     */
6155
    bytesToReadInBuffer =
6156
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6157

6158
    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6159

6160 6161
    if (FAILED(hr))
      return hr;
6162

6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190
    if (!cachedBlock)
    {
      /* Not in cache, and we're going to read past the end of the block. */
      ulOffset.u.HighPart = 0;
      ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                               offsetInBlock;

      StorageImpl_ReadAt(This->parentStorage,
           ulOffset,
           bufferWalker,
           bytesToReadInBuffer,
           &bytesReadAt);
    }
    else
    {
      if (!cachedBlock->read)
      {
        if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
          return STG_E_READFAULT;

        cachedBlock->read = 1;
      }

      memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
      bytesReadAt = bytesToReadInBuffer;
    }

    blockNoInSequence++;
6191 6192 6193
    bufferWalker += bytesReadAt;
    size         -= bytesReadAt;
    *bytesRead   += bytesReadAt;
6194 6195
    offsetInBlock = 0;  /* There is no offset on the next block */

6196 6197
    if (bytesToReadInBuffer != bytesReadAt)
        break;
6198
  }
6199

6200
  return S_OK;
6201 6202 6203 6204 6205 6206 6207 6208
}

/******************************************************************************
 *      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.
 */
6209
HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6210 6211 6212 6213 6214
  ULARGE_INTEGER    offset,
  ULONG             size,
  const void*       buffer,
  ULONG*            bytesWritten)
{
6215 6216
  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
6217 6218
  ULONG bytesToWrite;
  ULONG blockIndex;
Eric Pouech's avatar
Eric Pouech committed
6219
  const BYTE* bufferWalker;
6220 6221
  HRESULT hr;
  BlockChainBlock *cachedBlock;
6222

6223
  *bytesWritten   = 0;
6224
  bufferWalker = buffer;
6225

6226
  while (size > 0)
6227
  {
6228 6229
    ULARGE_INTEGER ulOffset;
    DWORD bytesWrittenAt;
6230

6231
    /*
6232
     * Calculate how many bytes we can copy to this big block.
6233
     */
6234
    bytesToWrite =
6235
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6236

6237
    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6238

6239 6240 6241 6242 6243 6244 6245
    /* 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;
    }
6246

6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272
    if (!cachedBlock)
    {
      /* Not in cache, and we're going to write past the end of the block. */
      ulOffset.u.HighPart = 0;
      ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                               offsetInBlock;

      StorageImpl_WriteAt(This->parentStorage,
           ulOffset,
           bufferWalker,
           bytesToWrite,
           &bytesWrittenAt);
    }
    else
    {
      if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
      {
        if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
          return STG_E_READFAULT;
      }

      memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
      bytesWrittenAt = bytesToWrite;
      cachedBlock->read = 1;
      cachedBlock->dirty = 1;
    }
6273

6274
    blockNoInSequence++;
6275 6276 6277
    bufferWalker  += bytesWrittenAt;
    size          -= bytesWrittenAt;
    *bytesWritten += bytesWrittenAt;
6278
    offsetInBlock  = 0;      /* There is no offset on the next block */
6279 6280 6281

    if (bytesWrittenAt != bytesToWrite)
      break;
6282
  }
6283

6284
  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6285 6286 6287 6288 6289 6290 6291
}

/******************************************************************************
 *      BlockChainStream_Shrink
 *
 * Shrinks this chain in the big block depot.
 */
6292 6293
static BOOL BlockChainStream_Shrink(BlockChainStream* This,
                                    ULARGE_INTEGER    newSize)
6294
{
6295
  ULONG blockIndex;
6296
  ULONG numBlocks;
6297
  int i;
6298

6299 6300 6301
  /*
   * Figure out how many blocks are needed to contain the new size
   */
6302
  numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6303

6304
  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6305 6306
    numBlocks++;

6307
  if (numBlocks)
6308
  {
6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320
    /*
     * 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;
6321
  }
6322 6323 6324 6325 6326 6327 6328 6329 6330 6331
  else
  {
    if (This->headOfStreamPlaceHolder != 0)
    {
      *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
    }
    else
    {
      DirEntry chainEntry;
      assert(This->ownerDirEntry != DIRENTRY_NULL);
6332

6333 6334 6335 6336
      StorageImpl_ReadDirEntry(
        This->parentStorage,
        This->ownerDirEntry,
        &chainEntry);
6337

6338 6339 6340 6341 6342 6343 6344 6345 6346 6347
      chainEntry.startingBlock = BLOCK_END_OF_CHAIN;

      StorageImpl_WriteDirEntry(
        This->parentStorage,
        This->ownerDirEntry,
        &chainEntry);
    }

    This->tailIndex = BLOCK_END_OF_CHAIN;
  }
6348

Thuy Nguyen's avatar
Thuy Nguyen committed
6349 6350
  This->numBlocks = numBlocks;

6351 6352 6353
  /*
   * Mark the extra blocks as free
   */
6354
  while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6355
  {
6356 6357 6358 6359 6360 6361 6362
    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--;
6363 6364
  }

6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376
  /*
   * Reset the last accessed block cache.
   */
  for (i=0; i<2; i++)
  {
    if (This->cachedBlocks[i].index >= numBlocks)
    {
      This->cachedBlocks[i].index = 0xffffffff;
      This->cachedBlocks[i].dirty = 0;
    }
  }

6377 6378 6379 6380 6381 6382 6383 6384
  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_Enlarge
 *
 * Grows this chain in the big block depot.
 */
6385 6386
static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
                                     ULARGE_INTEGER    newSize)
6387 6388 6389 6390 6391 6392 6393 6394
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = BlockChainStream_GetHeadOfChain(This);

  /*
6395
   * Empty chain. Create the head.
6396 6397 6398
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
6399 6400
    blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
    StorageImpl_SetNextBlockInChain(This->parentStorage,
6401 6402 6403 6404 6405 6406 6407 6408 6409
                                      blockIndex,
                                      BLOCK_END_OF_CHAIN);

    if (This->headOfStreamPlaceHolder != 0)
    {
      *(This->headOfStreamPlaceHolder) = blockIndex;
    }
    else
    {
6410
      DirEntry chainEntry;
6411
      assert(This->ownerDirEntry != DIRENTRY_NULL);
6412

6413
      StorageImpl_ReadDirEntry(
6414
        This->parentStorage,
6415
        This->ownerDirEntry,
6416
        &chainEntry);
6417

6418
      chainEntry.startingBlock = blockIndex;
6419

6420
      StorageImpl_WriteDirEntry(
6421
        This->parentStorage,
6422
        This->ownerDirEntry,
6423
        &chainEntry);
Thuy Nguyen's avatar
Thuy Nguyen committed
6424
    }
6425

Thuy Nguyen's avatar
Thuy Nguyen committed
6426 6427 6428
    This->tailIndex = blockIndex;
    This->numBlocks = 1;
  }
6429 6430 6431 6432

  /*
   * Figure out how many blocks are needed to contain this stream
   */
6433
  newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6434

6435
  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6436 6437 6438 6439 6440
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
Thuy Nguyen's avatar
Thuy Nguyen committed
6441
  if (This->tailIndex == BLOCK_END_OF_CHAIN)
6442 6443 6444
  {
    currentBlock = blockIndex;

Thuy Nguyen's avatar
Thuy Nguyen committed
6445 6446 6447 6448 6449
    while (blockIndex != BLOCK_END_OF_CHAIN)
    {
      This->numBlocks++;
      currentBlock = blockIndex;

6450 6451 6452
      if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
						&blockIndex)))
	return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
6453 6454 6455
    }

    This->tailIndex = currentBlock;
6456 6457
  }

Thuy Nguyen's avatar
Thuy Nguyen committed
6458 6459 6460
  currentBlock = This->tailIndex;
  oldNumBlocks = This->numBlocks;

6461 6462 6463
  /*
   * Add new blocks to the chain
   */
6464
  if (oldNumBlocks < newNumBlocks)
6465
  {
6466 6467 6468
    while (oldNumBlocks < newNumBlocks)
    {
      blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6469

6470
      StorageImpl_SetNextBlockInChain(
6471 6472
	This->parentStorage,
	currentBlock,
6473
	blockIndex);
6474

6475
      StorageImpl_SetNextBlockInChain(
6476 6477
        This->parentStorage,
	blockIndex,
6478
	BLOCK_END_OF_CHAIN);
6479

6480 6481 6482
      currentBlock = blockIndex;
      oldNumBlocks++;
    }
6483

6484 6485 6486
    This->tailIndex = blockIndex;
    This->numBlocks = newNumBlocks;
  }
Thuy Nguyen's avatar
Thuy Nguyen committed
6487

6488 6489 6490
  if (FAILED(BlockChainStream_UpdateIndexCache(This)))
    return FALSE;

6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501
  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_SetSize
 *
 * 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
6502
 *       doesn't shrink even if we shrink streams.
6503
 */
6504
BOOL BlockChainStream_SetSize(
6505 6506 6507 6508 6509
  BlockChainStream* This,
  ULARGE_INTEGER    newSize)
{
  ULARGE_INTEGER size = BlockChainStream_GetSize(This);

6510
  if (newSize.u.LowPart == size.u.LowPart)
6511 6512
    return TRUE;

6513
  if (newSize.u.LowPart < size.u.LowPart)
6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528
  {
    BlockChainStream_Shrink(This, newSize);
  }
  else
  {
    BlockChainStream_Enlarge(This, newSize);
  }

  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_GetSize
 *
 * Returns the size of this chain.
6529
 * Will return the block count if this chain doesn't have a directory entry.
6530
 */
6531
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6532
{
6533
  DirEntry chainEntry;
6534 6535 6536

  if(This->headOfStreamPlaceHolder == NULL)
  {
6537
    /*
6538
     * This chain has a directory entry so use the size value from there.
6539
     */
6540
    StorageImpl_ReadDirEntry(
6541
      This->parentStorage,
6542
      This->ownerDirEntry,
6543
      &chainEntry);
6544

6545
    return chainEntry.size;
6546 6547 6548 6549
  }
  else
  {
    /*
6550
     * this chain is a chain that does not have a directory entry, figure out the
6551
     * size by making the product number of used blocks times the
6552 6553 6554
     * size of them
     */
    ULARGE_INTEGER result;
6555
    result.u.HighPart = 0;
6556

6557
    result.u.LowPart  =
6558
      BlockChainStream_GetCount(This) *
6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569
      This->parentStorage->bigBlockSize;

    return result;
  }
}

/******************************************************************************
** SmallBlockChainStream implementation
*/

SmallBlockChainStream* SmallBlockChainStream_Construct(
6570
  StorageImpl* parentStorage,
6571
  ULONG*         headOfStreamPlaceHolder,
6572
  DirRef         dirEntry)
6573 6574 6575 6576 6577 6578
{
  SmallBlockChainStream* newStream;

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));

  newStream->parentStorage      = parentStorage;
6579
  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6580
  newStream->ownerDirEntry      = dirEntry;
6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595

  return newStream;
}

void SmallBlockChainStream_Destroy(
  SmallBlockChainStream* This)
{
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
 *      SmallBlockChainStream_GetHeadOfChain
 *
 * Returns the head of this chain of small blocks.
 */
6596
static ULONG SmallBlockChainStream_GetHeadOfChain(
6597 6598
  SmallBlockChainStream* This)
{
6599
  DirEntry  chainEntry;
6600
  HRESULT   hr;
6601

6602 6603 6604
  if (This->headOfStreamPlaceHolder != NULL)
    return *(This->headOfStreamPlaceHolder);

6605
  if (This->ownerDirEntry)
6606
  {
6607
    hr = StorageImpl_ReadDirEntry(
6608
                      This->parentStorage,
6609
                      This->ownerDirEntry,
6610
                      &chainEntry);
6611

6612
    if (SUCCEEDED(hr))
6613
    {
6614
      return chainEntry.startingBlock;
6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625
    }

  }

  return BLOCK_END_OF_CHAIN;
}

/******************************************************************************
 *      SmallBlockChainStream_GetNextBlockInChain
 *
 * Returns the index of the next small block in this chain.
6626
 *
6627 6628 6629 6630
 * Return Values:
 *    - BLOCK_END_OF_CHAIN: end of this chain
 *    - BLOCK_UNUSED: small block 'blockIndex' is free
 */
6631
static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6632
  SmallBlockChainStream* This,
6633 6634
  ULONG                  blockIndex,
  ULONG*                 nextBlockInChain)
6635 6636 6637 6638
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesRead;
6639
  HRESULT res;
6640

6641 6642
  *nextBlockInChain = BLOCK_END_OF_CHAIN;

6643 6644
  offsetOfBlockInDepot.u.HighPart = 0;
  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
6645 6646 6647 6648

  /*
   * Read those bytes in the buffer from the small block file.
   */
6649
  res = BlockChainStream_ReadAt(
6650 6651 6652 6653 6654 6655
              This->parentStorage->smallBlockDepotChain,
              offsetOfBlockInDepot,
              sizeof(DWORD),
              &buffer,
              &bytesRead);

6656 6657 6658
  if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
    res = STG_E_READFAULT;

6659
  if (SUCCEEDED(res))
6660
  {
6661
    StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6662
    return S_OK;
6663 6664
  }

6665
  return res;
6666 6667 6668 6669 6670 6671 6672 6673 6674 6675
}

/******************************************************************************
 *       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.
 */
6676
static void SmallBlockChainStream_SetNextBlockInChain(
6677 6678 6679 6680 6681 6682 6683 6684
  SmallBlockChainStream* This,
  ULONG                  blockIndex,
  ULONG                  nextBlock)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesWritten;

6685 6686
  offsetOfBlockInDepot.u.HighPart = 0;
  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
6687

6688
  StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705

  /*
   * 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.
 */
6706
static void SmallBlockChainStream_FreeBlock(
6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719
  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.
 */
6720
static ULONG SmallBlockChainStream_GetNextFreeBlock(
6721 6722 6723 6724 6725
  SmallBlockChainStream* This)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD buffer;
  ULONG bytesRead;
6726
  ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6727
  ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6728
  HRESULT res = S_OK;
6729
  ULONG smallBlocksPerBigBlock;
6730 6731 6732
  DirEntry rootEntry;
  ULONG blocksRequired;
  ULARGE_INTEGER old_size, size_required;
6733

6734
  offsetOfBlockInDepot.u.HighPart = 0;
6735 6736 6737 6738 6739 6740

  /*
   * Scan the small block depot for a free block
   */
  while (nextBlockIndex != BLOCK_UNUSED)
  {
6741
    offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6742

6743
    res = BlockChainStream_ReadAt(
6744 6745 6746 6747 6748 6749 6750 6751 6752
                This->parentStorage->smallBlockDepotChain,
                offsetOfBlockInDepot,
                sizeof(DWORD),
                &buffer,
                &bytesRead);

    /*
     * If we run out of space for the small block depot, enlarge it
     */
6753
    if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6754
    {
6755
      StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6756 6757 6758 6759 6760 6761

      if (nextBlockIndex != BLOCK_UNUSED)
        blockIndex++;
    }
    else
    {
6762
      ULONG count =
6763 6764
        BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);

6765
      BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6766 6767
      ULARGE_INTEGER newSize, offset;
      ULONG bytesWritten;
6768

6769 6770
      newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
      BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6771 6772 6773 6774 6775

      /*
       * Initialize all the small blocks to free
       */
      memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6776 6777 6778
      offset.QuadPart = count * This->parentStorage->bigBlockSize;
      BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
        offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6779

6780
      StorageImpl_SaveFileHeader(This->parentStorage);
6781 6782 6783
    }
  }

6784 6785
  This->parentStorage->firstFreeSmallBlock = blockIndex+1;

6786
  smallBlocksPerBigBlock =
6787 6788 6789 6790 6791
    This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;

  /*
   * Verify if we have to allocate big blocks to contain small blocks
   */
6792
  blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6793

6794
  size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6795

6796
  old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6797

6798 6799 6800 6801 6802
  if (size_required.QuadPart > old_size.QuadPart)
  {
    BlockChainStream_SetSize(
      This->parentStorage->smallBlockRootChain,
      size_required);
6803

6804 6805 6806 6807
    StorageImpl_ReadDirEntry(
      This->parentStorage,
      This->parentStorage->base.storageDirEntry,
      &rootEntry);
6808

6809
    rootEntry.size = size_required;
6810

6811 6812 6813 6814
    StorageImpl_WriteDirEntry(
      This->parentStorage,
      This->parentStorage->base.storageDirEntry,
      &rootEntry);
6815 6816 6817 6818 6819 6820 6821 6822 6823 6824
  }

  return blockIndex;
}

/******************************************************************************
 *      SmallBlockChainStream_ReadAt
 *
 * Reads a specified number of bytes from this chain at the specified offset.
 * bytesRead may be NULL.
6825
 * Failure will be returned if the specified number of bytes has not been read.
6826
 */
6827
HRESULT SmallBlockChainStream_ReadAt(
6828 6829 6830 6831 6832 6833
  SmallBlockChainStream* This,
  ULARGE_INTEGER         offset,
  ULONG                  size,
  void*                  buffer,
  ULONG*                 bytesRead)
{
6834
  HRESULT rc = S_OK;
6835
  ULARGE_INTEGER offsetInBigBlockFile;
6836
  ULONG blockNoInSequence =
6837
    offset.u.LowPart / This->parentStorage->smallBlockSize;
6838

6839
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6840 6841 6842 6843
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  ULONG bytesReadFromBigBlockFile;
  BYTE* bufferWalker;
6844
  ULARGE_INTEGER stream_size;
6845 6846 6847 6848

  /*
   * This should never happen on a small block file.
   */
6849
  assert(offset.u.HighPart==0);
6850

6851 6852 6853 6854 6855 6856 6857 6858
  *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;

6859 6860 6861 6862 6863 6864 6865
  /*
   * 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))
  {
6866 6867 6868
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
      return rc;
6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881
    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.
     */
6882
    bytesToReadInBuffer =
6883
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6884 6885 6886 6887

    /*
     * Calculate the offset of the small block in the small block file.
     */
6888 6889
    offsetInBigBlockFile.u.HighPart  = 0;
    offsetInBigBlockFile.u.LowPart   =
6890 6891
      blockIndex * This->parentStorage->smallBlockSize;

6892
    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
6893 6894 6895

    /*
     * Read those bytes in the buffer from the small block file.
6896 6897
     * The small block has already been identified so it shouldn't fail
     * unless the file is corrupt.
6898
     */
6899
    rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6900 6901 6902
      offsetInBigBlockFile,
      bytesToReadInBuffer,
      bufferWalker,
6903 6904 6905 6906
      &bytesReadFromBigBlockFile);

    if (FAILED(rc))
      return rc;
6907

6908 6909 6910
    if (!bytesReadFromBigBlockFile)
      return STG_E_DOCFILECORRUPT;

6911 6912 6913
    /*
     * Step to the next big block.
     */
6914 6915
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
6916
      return STG_E_DOCFILECORRUPT;
6917

6918 6919 6920 6921
    bufferWalker += bytesReadFromBigBlockFile;
    size         -= bytesReadFromBigBlockFile;
    *bytesRead   += bytesReadFromBigBlockFile;
    offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6922 6923
  }

6924
  return S_OK;
6925 6926 6927 6928 6929 6930 6931 6932
}

/******************************************************************************
 *       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.
 */
6933
HRESULT SmallBlockChainStream_WriteAt(
6934 6935 6936 6937 6938 6939 6940
  SmallBlockChainStream* This,
  ULARGE_INTEGER offset,
  ULONG          size,
  const void*    buffer,
  ULONG*         bytesWritten)
{
  ULARGE_INTEGER offsetInBigBlockFile;
6941
  ULONG blockNoInSequence =
6942
    offset.u.LowPart / This->parentStorage->smallBlockSize;
6943

6944
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6945 6946
  ULONG bytesToWriteInBuffer;
  ULONG blockIndex;
6947
  ULONG bytesWrittenToBigBlockFile;
Eric Pouech's avatar
Eric Pouech committed
6948
  const BYTE* bufferWalker;
6949
  HRESULT res;
6950

6951 6952 6953
  /*
   * This should never happen on a small block file.
   */
6954
  assert(offset.u.HighPart==0);
6955

6956 6957 6958 6959
  /*
   * Find the first block in the stream that contains part of the buffer.
   */
  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6960

6961 6962
  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
6963
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6964
      return STG_E_DOCFILECORRUPT;
6965 6966
    blockNoInSequence--;
  }
6967

6968 6969 6970 6971
  /*
   * Start writing the buffer.
   */
  *bytesWritten   = 0;
6972
  bufferWalker = buffer;
6973 6974 6975 6976 6977
  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy to this small block.
     */
6978
    bytesToWriteInBuffer =
6979
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6980

6981 6982 6983
    /*
     * Calculate the offset of the small block in the small block file.
     */
6984 6985
    offsetInBigBlockFile.u.HighPart  = 0;
    offsetInBigBlockFile.u.LowPart   =
6986 6987
      blockIndex * This->parentStorage->smallBlockSize;

6988
    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
6989

6990 6991 6992
    /*
     * Write those bytes in the buffer to the small block file.
     */
6993 6994
    res = BlockChainStream_WriteAt(
      This->parentStorage->smallBlockRootChain,
6995 6996 6997
      offsetInBigBlockFile,
      bytesToWriteInBuffer,
      bufferWalker,
6998
      &bytesWrittenToBigBlockFile);
6999 7000
    if (FAILED(res))
      return res;
7001

7002 7003 7004
    /*
     * Step to the next big block.
     */
7005 7006 7007
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&blockIndex)))
      return FALSE;
7008 7009 7010 7011
    bufferWalker  += bytesWrittenToBigBlockFile;
    size          -= bytesWrittenToBigBlockFile;
    *bytesWritten += bytesWrittenToBigBlockFile;
    offsetInBlock  = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7012
  }
7013

7014
  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7015 7016 7017 7018 7019
}

/******************************************************************************
 *       SmallBlockChainStream_Shrink
 *
7020
 * Shrinks this chain in the small block depot.
7021
 */
7022
static BOOL SmallBlockChainStream_Shrink(
7023 7024 7025 7026 7027
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, extraBlock;
  ULONG numBlocks;
7028
  ULONG count = 0;
7029

7030
  numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7031

7032
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7033 7034 7035 7036 7037 7038 7039 7040 7041
    numBlocks++;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
   * Go to the new end of chain
   */
  while (count < numBlocks)
  {
7042 7043 7044
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&blockIndex)))
      return FALSE;
7045 7046 7047
    count++;
  }

7048 7049
  /*
   * If the count is 0, we have a special case, the head of the chain was
7050
   * just freed.
7051 7052 7053
   */
  if (count == 0)
  {
7054
    DirEntry chainEntry;
7055

7056
    StorageImpl_ReadDirEntry(This->parentStorage,
7057
			     This->ownerDirEntry,
7058
			     &chainEntry);
7059

7060
    chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7061

7062
    StorageImpl_WriteDirEntry(This->parentStorage,
7063
			      This->ownerDirEntry,
7064
			      &chainEntry);
7065 7066 7067 7068 7069 7070 7071 7072 7073

    /*
     * We start freeing the chain at the head block.
     */
    extraBlock = blockIndex;
  }
  else
  {
    /* Get the next block before marking the new end */
7074 7075 7076
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&extraBlock)))
      return FALSE;
7077 7078 7079

    /* Mark the new end of chain */
    SmallBlockChainStream_SetNextBlockInChain(
7080 7081
      This,
      blockIndex,
7082 7083
      BLOCK_END_OF_CHAIN);
  }
7084 7085 7086 7087 7088 7089

  /*
   * Mark the extra blocks as free
   */
  while (extraBlock != BLOCK_END_OF_CHAIN)
  {
7090 7091 7092
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
							&blockIndex)))
      return FALSE;
7093
    SmallBlockChainStream_FreeBlock(This, extraBlock);
7094
    This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7095 7096 7097
    extraBlock = blockIndex;
  }

7098
  return TRUE;
7099 7100 7101 7102 7103 7104 7105
}

/******************************************************************************
 *      SmallBlockChainStream_Enlarge
 *
 * Grows this chain in the small block depot.
 */
7106
static BOOL SmallBlockChainStream_Enlarge(
7107 7108 7109 7110 7111 7112 7113 7114 7115 7116
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
7117
   * Empty chain. Create the head.
7118 7119 7120
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
7121 7122 7123 7124 7125
    blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
    SmallBlockChainStream_SetNextBlockInChain(
        This,
        blockIndex,
        BLOCK_END_OF_CHAIN);
7126

7127 7128 7129 7130 7131 7132
    if (This->headOfStreamPlaceHolder != NULL)
    {
      *(This->headOfStreamPlaceHolder) = blockIndex;
    }
    else
    {
7133
      DirEntry chainEntry;
7134

7135
      StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7136
                                   &chainEntry);
7137

7138
      chainEntry.startingBlock = blockIndex;
7139

7140
      StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7141
                                  &chainEntry);
7142
    }
7143 7144 7145 7146 7147 7148 7149
  }

  currentBlock = blockIndex;

  /*
   * Figure out how many blocks are needed to contain this stream
   */
7150
  newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7151

7152
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7153 7154 7155 7156 7157 7158 7159 7160 7161
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
  while (blockIndex != BLOCK_END_OF_CHAIN)
  {
    oldNumBlocks++;
    currentBlock = blockIndex;
7162 7163
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
      return FALSE;
7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174
  }

  /*
   * Add new blocks to the chain
   */
  while (oldNumBlocks < newNumBlocks)
  {
    blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
    SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);

    SmallBlockChainStream_SetNextBlockInChain(
7175 7176
      This,
      blockIndex,
7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193
      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
7194
 *       doesn't shrink even if we shrink streams.
7195
 */
7196
BOOL SmallBlockChainStream_SetSize(
7197 7198 7199 7200 7201
                SmallBlockChainStream* This,
                ULARGE_INTEGER    newSize)
{
  ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);

7202
  if (newSize.u.LowPart == size.u.LowPart)
7203 7204
    return TRUE;

7205
  if (newSize.u.LowPart < size.u.LowPart)
7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216
  {
    SmallBlockChainStream_Shrink(This, newSize);
  }
  else
  {
    SmallBlockChainStream_Enlarge(This, newSize);
  }

  return TRUE;
}

7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242
/******************************************************************************
 *       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;
}

7243 7244 7245 7246 7247
/******************************************************************************
 *      SmallBlockChainStream_GetSize
 *
 * Returns the size of this chain.
 */
7248
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7249
{
7250
  DirEntry chainEntry;
7251

7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262
  if(This->headOfStreamPlaceHolder != NULL)
  {
    ULARGE_INTEGER result;
    result.u.HighPart = 0;

    result.u.LowPart = SmallBlockChainStream_GetCount(This) *
        This->parentStorage->smallBlockSize;

    return result;
  }

7263
  StorageImpl_ReadDirEntry(
7264
    This->parentStorage,
7265
    This->ownerDirEntry,
7266
    &chainEntry);
7267

7268
  return chainEntry.size;
7269 7270
}

7271
static HRESULT create_storagefile(
7272
  LPCOLESTR pwcsName,
7273
  DWORD       grfMode,
7274 7275 7276 7277
  DWORD       grfAttrs,
  STGOPTIONS* pStgOptions,
  REFIID      riid,
  void**      ppstgOpen)
7278
{
7279
  StorageBaseImpl* newStorage = 0;
7280
  HANDLE       hFile      = INVALID_HANDLE_VALUE;
7281
  HRESULT        hr         = STG_E_INVALIDFLAG;
7282 7283 7284 7285
  DWORD          shareMode;
  DWORD          accessMode;
  DWORD          creationMode;
  DWORD          fileAttributes;
7286
  WCHAR          tempFileName[MAX_PATH];
7287

7288
  if (ppstgOpen == 0)
7289
    return STG_E_INVALIDPOINTER;
7290 7291

  if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7292
    return STG_E_INVALIDPARAMETER;
7293

7294 7295 7296 7297
  /* if no share mode given then DENY_NONE is the default */
  if (STGM_SHARE_MODE(grfMode) == 0)
      grfMode |= STGM_SHARE_DENY_NONE;

7298
  if ( FAILED( validateSTGM(grfMode) ))
7299
    goto end;
7300

7301
  /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7302 7303 7304 7305 7306 7307
  switch(STGM_ACCESS_MODE(grfMode))
  {
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
7308
    goto end;
7309
  }
7310

7311 7312
  /* in direct mode, can only use SHARE_EXCLUSIVE */
  if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7313
    goto end;
7314

7315 7316
  /* but in transacted mode, any share mode is valid */

7317 7318 7319 7320 7321 7322
  /*
   * Generate a unique name.
   */
  if (pwcsName == 0)
  {
    WCHAR tempPath[MAX_PATH];
7323
    static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7324 7325 7326 7327 7328 7329 7330 7331 7332 7333

    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
7334 7335 7336 7337
    {
      hr = STG_E_INSUFFICIENTMEMORY;
      goto end;
    }
7338 7339 7340 7341 7342 7343

    creationMode = TRUNCATE_EXISTING;
  }
  else
  {
    creationMode = GetCreationModeFromSTGM(grfMode);
7344 7345
  }

7346
  /*
7347
   * Interpret the STGM value grfMode
7348
   */
7349
  shareMode    = FILE_SHARE_READ | FILE_SHARE_WRITE;
7350 7351 7352 7353 7354 7355 7356
  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;

7357
  if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7358 7359 7360
  {
    static int fixme;
    if (!fixme++)
7361
      FIXME("Storage share mode not implemented.\n");
7362
  }
7363

7364 7365
  *ppstgOpen = 0;

7366
  hFile = CreateFileW(pwcsName,
7367 7368
                        accessMode,
                        shareMode,
7369
                        NULL,
7370 7371
                        creationMode,
                        fileAttributes,
7372
                        0);
7373

7374
  if (hFile == INVALID_HANDLE_VALUE)
7375
  {
7376
    if(GetLastError() == ERROR_FILE_EXISTS)
7377 7378 7379 7380
      hr = STG_E_FILEALREADYEXISTS;
    else
      hr = E_FAIL;
    goto end;
7381 7382 7383 7384 7385
  }

  /*
   * Allocate and initialize the new IStorage32object.
   */
7386
  hr = Storage_Construct(
7387
         hFile,
7388
        pwcsName,
7389 7390
         NULL,
         grfMode,
7391
         TRUE,
7392
         TRUE,
7393
         pStgOptions->ulSectorSize,
7394
         &newStorage);
7395

7396
  if (FAILED(hr))
7397
  {
7398
    goto end;
7399
  }
7400

7401 7402
  hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
  IStorage_Release(&newStorage->IStorage_iface);
7403

7404
end:
7405
  TRACE("<-- %p  r = %08x\n", *ppstgOpen, hr);
7406 7407 7408 7409

  return hr;
}

7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448
/******************************************************************************
 *    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);
}

7449 7450 7451
/******************************************************************************
 *              StgCreateStorageEx        [OLE32.@]
 */
7452 7453
HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
7454
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7455
          grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7456 7457 7458 7459 7460 7461 7462

    if (stgfmt != STGFMT_FILE && grfAttrs != 0)
    {
        ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
        return STG_E_INVALIDPARAMETER;  
    }

7463
    if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476
    {
        ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
        return STG_E_INVALIDPARAMETER;  
    }

    if (stgfmt == STGFMT_FILE)
    {
        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
        return STG_E_INVALIDPARAMETER;
    }

    if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
    {
7477 7478 7479
        STGOPTIONS defaultOptions = {1, 0, 512};

        if (!pStgOptions) pStgOptions = &defaultOptions;
7480
        return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7481 7482
    }

7483

7484 7485
    ERR("Invalid stgfmt argument\n");
    return STG_E_INVALIDPARAMETER;
7486 7487
}

7488 7489 7490 7491
/******************************************************************************
 *              StgCreatePropSetStg       [OLE32.@]
 */
HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7492
 IPropertySetStorage **propset)
7493
{
7494
    TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7495
    if (reserved)
7496 7497 7498
        return STG_E_INVALIDPARAMETER;

    return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7499 7500
}

7501 7502 7503 7504 7505
/******************************************************************************
 *              StgOpenStorageEx      [OLE32.@]
 */
HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
7506
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7507 7508 7509 7510 7511 7512 7513 7514
          grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);

    if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
    {
        ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
        return STG_E_INVALIDPARAMETER;  
    }

7515
    switch (stgfmt)
7516
    {
7517
    case STGFMT_FILE:
7518 7519
        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
        return STG_E_INVALIDPARAMETER;
7520 7521 7522
        
    case STGFMT_STORAGE:
        break;
7523

7524 7525 7526 7527 7528 7529
    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");
            return STG_E_INVALIDPARAMETER;  
        }
7530
        FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7531 7532 7533 7534 7535 7536 7537 7538
        break;

    case STGFMT_ANY:
        WARN("STGFMT_ANY assuming storage\n");
        break;

    default:
        return STG_E_INVALIDPARAMETER;
7539 7540
    }

7541
    return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7542 7543 7544
}


7545
/******************************************************************************
7546
 *              StgOpenStorage        [OLE32.@]
7547
 */
7548 7549 7550
HRESULT WINAPI StgOpenStorage(
  const OLECHAR *pwcsName,
  IStorage      *pstgPriority,
7551 7552 7553 7554
  DWORD          grfMode,
  SNB            snbExclude,
  DWORD          reserved,
  IStorage     **ppstgOpen)
7555
{
7556
  StorageBaseImpl* newStorage = 0;
7557
  HRESULT        hr = S_OK;
7558
  HANDLE         hFile = 0;
7559 7560
  DWORD          shareMode;
  DWORD          accessMode;
7561

7562
  TRACE("(%s, %p, %x, %p, %d, %p)\n",
7563 7564 7565
	debugstr_w(pwcsName), pstgPriority, grfMode,
	snbExclude, reserved, ppstgOpen);

7566 7567 7568 7569 7570 7571 7572
  if (pwcsName == 0)
  {
    hr = STG_E_INVALIDNAME;
    goto end;
  }

  if (ppstgOpen == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
7573 7574 7575 7576
  {
    hr = STG_E_INVALIDPOINTER;
    goto end;
  }
7577

7578 7579 7580 7581 7582 7583
  if (reserved)
  {
    hr = STG_E_INVALIDPARAMETER;
    goto end;
  }

7584 7585
  if (grfMode & STGM_PRIORITY)
  {
7586 7587 7588 7589 7590 7591
    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;
7592
    grfMode &= ~0xf0; /* remove the existing sharing mode */
7593 7594 7595 7596 7597 7598 7599 7600
    grfMode |= STGM_SHARE_DENY_NONE;

    /* STGM_PRIORITY stops other IStorage objects on the same file from
     * committing until the STGM_PRIORITY IStorage is closed. it also
     * stops non-transacted mode StgOpenStorage calls with write access from
     * succeeding. obviously, both of these cannot be achieved through just
     * file share flags */
    FIXME("STGM_PRIORITY mode not implemented correctly\n");
7601 7602
  }

7603 7604 7605
  /*
   * Validate the sharing mode
   */
7606
  if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7607 7608 7609 7610 7611 7612 7613 7614 7615
    switch(STGM_SHARE_MODE(grfMode))
    {
      case STGM_SHARE_EXCLUSIVE:
      case STGM_SHARE_DENY_WRITE:
        break;
      default:
        hr = STG_E_INVALIDFLAG;
        goto end;
    }
7616 7617 7618

  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode&STGM_CREATE))
Alexandre Julliard's avatar
Alexandre Julliard committed
7619 7620 7621 7622
  {
    hr = STG_E_INVALIDFLAG;
    goto end;
  }
7623

7624 7625 7626 7627 7628 7629 7630 7631 7632
  /* shared reading requires transacted mode */
  if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
      STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
     !(grfMode&STGM_TRANSACTED) )
  {
    hr = STG_E_INVALIDFLAG;
    goto end;
  }

7633 7634 7635 7636 7637 7638
  /*
   * Interpret the STGM value grfMode
   */
  shareMode    = GetShareModeFromSTGM(grfMode);
  accessMode   = GetAccessModeFromSTGM(grfMode);

7639
  *ppstgOpen = 0;
7640

7641 7642 7643 7644 7645 7646 7647
  hFile = CreateFileW( pwcsName,
                       accessMode,
                       shareMode,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
                       0);
7648

7649
  if (hFile==INVALID_HANDLE_VALUE)
7650
  {
7651 7652
    DWORD last_error = GetLastError();

Alexandre Julliard's avatar
Alexandre Julliard committed
7653 7654
    hr = E_FAIL;

7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677
    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
7678
    goto end;
7679 7680
  }

7681 7682 7683 7684
  /*
   * 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
   */
7685
  if (GetFileSize(hFile, NULL) < 0x100)
7686 7687 7688 7689 7690
  {
    CloseHandle(hFile);
    hr = STG_E_FILEALREADYEXISTS;
    goto end;
  }
7691

7692 7693 7694
  /*
   * Allocate and initialize the new IStorage32object.
   */
7695
  hr = Storage_Construct(
7696
         hFile,
7697
         pwcsName,
7698 7699
         NULL,
         grfMode,
7700
         TRUE,
7701
         FALSE,
7702
         512,
7703
         &newStorage);
7704

7705
  if (FAILED(hr))
7706
  {
7707 7708 7709 7710
    /*
     * 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
7711 7712
	hr = STG_E_FILEALREADYEXISTS;
    goto end;
7713
  }
7714

7715
  *ppstgOpen = &newStorage->IStorage_iface;
7716

Alexandre Julliard's avatar
Alexandre Julliard committed
7717
end:
7718
  TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7719 7720 7721 7722
  return hr;
}

/******************************************************************************
7723
 *    StgCreateDocfileOnILockBytes    [OLE32.@]
7724 7725 7726 7727 7728 7729 7730
 */
HRESULT WINAPI StgCreateDocfileOnILockBytes(
      ILockBytes *plkbyt,
      DWORD grfMode,
      DWORD reserved,
      IStorage** ppstgOpen)
{
7731
  StorageBaseImpl* newStorage = 0;
7732 7733 7734 7735 7736 7737 7738 7739
  HRESULT        hr         = S_OK;

  if ((ppstgOpen == 0) || (plkbyt == 0))
    return STG_E_INVALIDPOINTER;

  /*
   * Allocate and initialize the new IStorage object.
   */
7740
  hr = Storage_Construct(
7741
         0,
7742
        0,
7743 7744
         plkbyt,
         grfMode,
7745
         FALSE,
7746
         TRUE,
7747
         512,
7748
         &newStorage);
7749 7750 7751 7752 7753 7754

  if (FAILED(hr))
  {
    return hr;
  }

7755
  *ppstgOpen = &newStorage->IStorage_iface;
7756

7757
  return hr;
7758 7759 7760
}

/******************************************************************************
7761
 *    StgOpenStorageOnILockBytes    [OLE32.@]
7762 7763 7764 7765 7766 7767 7768 7769 7770
 */
HRESULT WINAPI StgOpenStorageOnILockBytes(
      ILockBytes *plkbyt,
      IStorage *pstgPriority,
      DWORD grfMode,
      SNB snbExclude,
      DWORD reserved,
      IStorage **ppstgOpen)
{
7771
  StorageBaseImpl* newStorage = 0;
7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784
  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.
   */
7785
  hr = Storage_Construct(
7786
         0,
7787
         0,
7788 7789
         plkbyt,
         grfMode,
7790
         FALSE,
7791
         FALSE,
7792
         512,
7793
         &newStorage);
7794 7795 7796 7797 7798 7799

  if (FAILED(hr))
  {
    return hr;
  }

7800
  *ppstgOpen = &newStorage->IStorage_iface;
7801 7802 7803 7804

  return hr;
}

Jim Aston's avatar
Jim Aston committed
7805
/******************************************************************************
7806 7807
 *              StgSetTimes [ole32.@]
 *              StgSetTimes [OLE32.@]
Jim Aston's avatar
Jim Aston committed
7808 7809 7810
 *
 *
 */
Mike McCormack's avatar
Mike McCormack committed
7811 7812
HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
                           FILETIME const *patime, FILETIME const *pmtime)
Jim Aston's avatar
Jim Aston committed
7813
{
Mike McCormack's avatar
Mike McCormack committed
7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827
  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;
Jim Aston's avatar
Jim Aston committed
7828 7829
}

7830
/******************************************************************************
7831
 *              StgIsStorageILockBytes        [OLE32.@]
7832 7833 7834 7835 7836
 *
 * Determines if the ILockBytes contains a storage object.
 */
HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
{
7837
  BYTE sig[sizeof(STORAGE_magic)];
7838
  ULARGE_INTEGER offset;
7839
  ULONG read = 0;
7840

7841 7842
  offset.u.HighPart = 0;
  offset.u.LowPart  = 0;
7843

7844
  ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7845

7846
  if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7847 7848 7849 7850 7851 7852
    return S_OK;

  return S_FALSE;
}

/******************************************************************************
7853
 *              WriteClassStg        [OLE32.@]
7854 7855 7856
 *
 * This method will store the specified CLSID in the specified storage object
 */
7857
HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7858
{
7859 7860
  if(!pStg)
    return E_INVALIDARG;
7861

7862 7863 7864
  if(!rclsid)
    return STG_E_INVALIDPOINTER;

7865
  return IStorage_SetClass(pStg, rclsid);
7866 7867
}

7868
/***********************************************************************
7869
 *    ReadClassStg (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
7870
 *
7871 7872 7873 7874 7875 7876 7877 7878 7879 7880
 * 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.
Noomen Hamza's avatar
Noomen Hamza committed
7881 7882 7883 7884 7885
 */
HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){

    STATSTG pstatstg;
    HRESULT hRes;
7886

7887 7888 7889 7890
    TRACE("(%p, %p)\n", pstg, pclsid);

    if(!pstg || !pclsid)
        return E_INVALIDARG;
Noomen Hamza's avatar
Noomen Hamza committed
7891 7892 7893 7894

   /*
    * read a STATSTG structure (contains the clsid) from the storage
    */
7895
    hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
Noomen Hamza's avatar
Noomen Hamza committed
7896 7897 7898 7899 7900 7901 7902

    if(SUCCEEDED(hRes))
        *pclsid=pstatstg.clsid;

    return hRes;
}

7903
/***********************************************************************
7904
 *    OleLoadFromStream (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
7905 7906 7907 7908 7909
 *
 * This function loads an object from stream
 */
HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
{
7910 7911 7912
    CLSID	clsid;
    HRESULT	res;
    LPPERSISTSTREAM	xstm;
Noomen Hamza's avatar
Noomen Hamza committed
7913

7914
    TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
Noomen Hamza's avatar
Noomen Hamza committed
7915 7916

    res=ReadClassStm(pStm,&clsid);
7917
    if (FAILED(res))
7918 7919
	return res;
    res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7920
    if (FAILED(res))
7921
	return res;
7922
    res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7923
    if (FAILED(res)) {
7924 7925
	IUnknown_Release((IUnknown*)*ppvObj);
	return res;
Noomen Hamza's avatar
Noomen Hamza committed
7926
    }
7927 7928 7929 7930 7931 7932 7933
    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)
     */
Noomen Hamza's avatar
Noomen Hamza committed
7934 7935 7936
    return res;
}

7937
/***********************************************************************
7938
 *    OleSaveToStream (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
7939
 *
7940 7941
 * This function saves an object with the IPersistStream interface on it
 * to the specified stream.
Noomen Hamza's avatar
Noomen Hamza committed
7942 7943 7944 7945 7946 7947
 */
HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
{

    CLSID clsid;
    HRESULT res;
7948

7949
    TRACE("(%p,%p)\n",pPStm,pStm);
Noomen Hamza's avatar
Noomen Hamza committed
7950 7951 7952 7953

    res=IPersistStream_GetClassID(pPStm,&clsid);

    if (SUCCEEDED(res)){
7954

Noomen Hamza's avatar
Noomen Hamza committed
7955 7956 7957 7958
        res=WriteClassStm(pStm,&clsid);

        if (SUCCEEDED(res))

7959
            res=IPersistStream_Save(pPStm,pStm,TRUE);
Noomen Hamza's avatar
Noomen Hamza committed
7960 7961
    }

7962
    TRACE("Finished Save\n");
Noomen Hamza's avatar
Noomen Hamza committed
7963 7964
    return res;
}
7965

7966 7967 7968
/****************************************************************************
 * This method validate a STGM parameter that can contain the values below
 *
7969 7970 7971
 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
 * The stgm values contained in 0xffff0000 are bitmasks.
 *
7972 7973 7974
 * STGM_DIRECT               0x00000000
 * STGM_TRANSACTED           0x00010000
 * STGM_SIMPLE               0x08000000
7975
 *
7976 7977 7978
 * STGM_READ                 0x00000000
 * STGM_WRITE                0x00000001
 * STGM_READWRITE            0x00000002
7979
 *
7980 7981 7982 7983
 * STGM_SHARE_DENY_NONE      0x00000040
 * STGM_SHARE_DENY_READ      0x00000030
 * STGM_SHARE_DENY_WRITE     0x00000020
 * STGM_SHARE_EXCLUSIVE      0x00000010
7984
 *
7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996
 * STGM_PRIORITY             0x00040000
 * STGM_DELETEONRELEASE      0x04000000
 *
 * STGM_CREATE               0x00001000
 * STGM_CONVERT              0x00020000
 * STGM_FAILIFTHERE          0x00000000
 *
 * STGM_NOSCRATCH            0x00100000
 * STGM_NOSNAPSHOT           0x00200000
 */
static HRESULT validateSTGM(DWORD stgm)
{
7997 7998 7999
  DWORD access = STGM_ACCESS_MODE(stgm);
  DWORD share  = STGM_SHARE_MODE(stgm);
  DWORD create = STGM_CREATE_MODE(stgm);
8000

8001 8002
  if (stgm&~STGM_KNOWN_FLAGS)
  {
8003
    ERR("unknown flags %08x\n", stgm);
8004 8005
    return E_FAIL;
  }
8006

8007 8008 8009 8010 8011 8012 8013 8014 8015
  switch (access)
  {
  case STGM_READ:
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
    return E_FAIL;
  }
8016

8017 8018 8019 8020 8021 8022 8023 8024 8025 8026
  switch (share)
  {
  case STGM_SHARE_DENY_NONE:
  case STGM_SHARE_DENY_READ:
  case STGM_SHARE_DENY_WRITE:
  case STGM_SHARE_EXCLUSIVE:
    break;
  default:
    return E_FAIL;
  }
8027

8028 8029 8030 8031 8032 8033 8034 8035
  switch (create)
  {
  case STGM_CREATE:
  case STGM_FAILIFTHERE:
    break;
  default:
    return E_FAIL;
  }
8036

8037
  /*
8038 8039
   * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
   */
8040
  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8041 8042 8043 8044 8045 8046
      return E_FAIL;

  /*
   * STGM_CREATE | STGM_CONVERT
   * if both are false, STGM_FAILIFTHERE is set to TRUE
   */
8047
  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8048 8049 8050 8051 8052
    return E_FAIL;

  /*
   * STGM_NOSCRATCH requires STGM_TRANSACTED
   */
8053
  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8054
    return E_FAIL;
8055

8056
  /*
8057
   * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8058 8059
   * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
   */
8060 8061 8062 8063
  if ( (stgm & STGM_NOSNAPSHOT) &&
        (!(stgm & STGM_TRANSACTED) ||
         share == STGM_SHARE_EXCLUSIVE ||
         share == STGM_SHARE_DENY_WRITE) )
8064 8065 8066 8067 8068 8069 8070 8071 8072
    return E_FAIL;

  return S_OK;
}

/****************************************************************************
 *      GetShareModeFromSTGM
 *
 * This method will return a share mode flag from a STGM value.
8073
 * The STGM value is assumed valid.
8074 8075 8076
 */
static DWORD GetShareModeFromSTGM(DWORD stgm)
{
8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090
  switch (STGM_SHARE_MODE(stgm))
  {
  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:
    return FILE_SHARE_READ;
  case STGM_SHARE_EXCLUSIVE:
    return 0;
  }
  ERR("Invalid share mode!\n");
  assert(0);
  return 0;
8091 8092 8093 8094 8095 8096 8097 8098 8099 8100
}

/****************************************************************************
 *      GetAccessModeFromSTGM
 *
 * This method will return an access mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetAccessModeFromSTGM(DWORD stgm)
{
8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111
  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;
8112 8113 8114 8115 8116 8117 8118 8119 8120 8121
}

/****************************************************************************
 *      GetCreationModeFromSTGM
 *
 * This method will return a creation mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetCreationModeFromSTGM(DWORD stgm)
{
8122 8123 8124
  switch(STGM_CREATE_MODE(stgm))
  {
  case STGM_CREATE:
8125
    return CREATE_ALWAYS;
8126
  case STGM_CONVERT:
8127
    FIXME("STGM_CONVERT not implemented!\n");
8128
    return CREATE_NEW;
8129 8130
  case STGM_FAILIFTHERE:
    return CREATE_NEW;
8131
  }
8132 8133 8134
  ERR("Invalid create mode!\n");
  assert(0);
  return 0;
8135
}
8136 8137 8138


/*************************************************************************
8139
 * OLECONVERT_LoadOLE10 [Internal]
8140
 *
8141
 * Loads the OLE10 STREAM to memory
8142 8143 8144 8145 8146 8147 8148 8149
 *
 * 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
8150
 *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8151 8152 8153
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
8154
 *
8155 8156
 *     Memory allocated for pData must be freed by the caller
 */
8157
static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8158 8159 8160
{
	DWORD dwSize;
	HRESULT hRes = S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
8161 8162 8163
	int nTryCnt=0;
	int max_try = 6;

8164
	pData->pData = NULL;
8165
	pData->pstrOleObjFileName = NULL;
8166

Alexandre Julliard's avatar
Alexandre Julliard committed
8167 8168
	for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
	{
8169 8170 8171 8172 8173 8174 8175 8176 8177 8178
	/* 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
8179 8180 8181 8182 8183 8184
		else
		{
			hRes = S_OK;
			break;
		}
	}
8185 8186 8187

	if(hRes == S_OK)
	{
8188
		/* Get the TypeID... more info needed for this field */
8189 8190 8191 8192 8193 8194 8195 8196 8197 8198
		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
8199
			/* Get the length of the OleTypeName */
8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210
			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 */
8211
					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8212 8213 8214 8215 8216 8217
					if(dwSize != pData->dwOleTypeNameLength)
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
8218 8219 8220 8221 8222 8223 8224
			if(bStrem1)
			{
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
				if(dwSize != sizeof(pData->dwOleObjFileNameLength))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
8225
			if(hRes == S_OK)
Alexandre Julliard's avatar
Alexandre Julliard committed
8226
			{
8227
					if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
Alexandre Julliard's avatar
Alexandre Julliard committed
8228
						pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8229
					pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
Alexandre Julliard's avatar
Alexandre Julliard committed
8230 8231
					if(pData->pstrOleObjFileName)
					{
8232
						dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
Alexandre Julliard's avatar
Alexandre Julliard committed
8233 8234 8235 8236 8237 8238 8239 8240 8241 8242
						if(dwSize != pData->dwOleObjFileNameLength)
						{
							hRes = CONVERT10_E_OLESTREAM_GET;
						}
					}
					else
						hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}
			else
8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258
			{
				/* 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
8259
			}
8260 8261
			if(hRes == S_OK)
			{
Francois Gouget's avatar
Francois Gouget committed
8262
				/* Get the Length of the Data */
8263 8264 8265 8266 8267 8268 8269
				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
8270
			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
8271
			{
8272
				if(!bStrem1) /* if it is a second OLE stream data */
Alexandre Julliard's avatar
Alexandre Julliard committed
8273 8274
				{
					pData->dwDataLength -= 8;
8275
					dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
Alexandre Julliard's avatar
Alexandre Julliard committed
8276 8277 8278 8279 8280 8281
					if(dwSize != sizeof(pData->strUnknown))
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
8282 8283 8284 8285
			if(hRes == S_OK)
			{
				if(pData->dwDataLength > 0)
				{
8286
					pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308

					/* 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;
}

/*************************************************************************
8309
 * OLECONVERT_SaveOLE10 [Internal]
8310
 *
8311
 * Saves the OLE10 STREAM From memory
8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322
 *
 * PARAMS
 *     pData        [I] Data Structure for the OLESTREAM Data
 *     pOleStream   [I] The OLESTREAM to save
 *
 * RETURNS
 *     Success:  S_OK
 *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
 *
 * NOTES
 *     This function is used by OleConvertIStorageToOLESTREAM only.
8323
 *
8324
 */
8325
static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349
{
    DWORD dwSize;
    HRESULT hRes = S_OK;


   /* Set the OleID */
    dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
    if(dwSize != sizeof(pData->dwOleID))
    {
        hRes = CONVERT10_E_OLESTREAM_PUT;
    }

    if(hRes == S_OK)
    {
        /* Set the TypeID */
        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
        if(dwSize != sizeof(pData->dwTypeID))
        {
            hRes = CONVERT10_E_OLESTREAM_PUT;
        }
    }

    if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
    {
Francois Gouget's avatar
Francois Gouget committed
8350
        /* Set the Length of the OleTypeName */
8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361
        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
        if(dwSize != sizeof(pData->dwOleTypeNameLength))
        {
            hRes = CONVERT10_E_OLESTREAM_PUT;
        }

        if(hRes == S_OK)
        {
            if(pData->dwOleTypeNameLength > 0)
            {
                /* Set the OleTypeName */
8362
                dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391
                if(dwSize != pData->dwOleTypeNameLength)
                {
                    hRes = CONVERT10_E_OLESTREAM_PUT;
                }
            }
        }

        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;
            }
        }

        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;
            }
        }

        if(hRes == S_OK)
        {
Francois Gouget's avatar
Francois Gouget committed
8392
            /* Set the length of the Data */
8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416
            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
            if(dwSize != sizeof(pData->dwDataLength))
            {
                hRes = CONVERT10_E_OLESTREAM_PUT;
            }
        }

        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;
}

/*************************************************************************
8417
 * OLECONVERT_GetOLE20FromOLE10[Internal]
8418 8419
 *
 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8420
 * opens it, and copies the content to the dest IStorage for
8421
 * OleConvertOLESTREAMToIStorage
8422
 *
8423 8424 8425 8426 8427 8428 8429 8430 8431 8432
 *
 * 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
8433 8434
 *
 *
8435
 */
8436
static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8437 8438 8439 8440 8441 8442
{
    HRESULT hRes;
    HANDLE hFile;
    IStorage *pTempStorage;
    DWORD dwNumOfBytesWritten;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8443
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8444 8445 8446 8447

    /* Create a temp File */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
Patrik Stridvall's avatar
Patrik Stridvall committed
8448
    hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459

    if(hFile != INVALID_HANDLE_VALUE)
    {
        /* Write IStorage Data to File */
        WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
        CloseHandle(hFile);

        /* Open and copy temp storage to the Dest Storage */
        hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
        if(hRes == S_OK)
        {
8460 8461
            hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
            IStorage_Release(pTempStorage);
8462 8463 8464 8465 8466 8467 8468
        }
        DeleteFileW(wstrTempFile);
    }
}


/*************************************************************************
8469
 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8470
 *
8471
 * Saves the OLE10 STREAM From memory
8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483
 *
 * 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.
8484
 *
8485
 */
8486
static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8487 8488 8489 8490 8491 8492
{
    HANDLE hFile;
    HRESULT hRes;
    DWORD nDataLength = 0;
    IStorage *pTempStorage;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8493
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8494 8495

    *pData = NULL;
8496

8497 8498 8499
    /* Create temp Storage */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
Patrik Stridvall's avatar
Patrik Stridvall committed
8500
    hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8501 8502 8503 8504

    if(hRes == S_OK)
    {
        /* Copy Src Storage to the Temp Storage */
8505 8506
        IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
        IStorage_Release(pTempStorage);
8507 8508

        /* Open Temp Storage as a file and copy to memory */
Patrik Stridvall's avatar
Patrik Stridvall committed
8509
        hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8510 8511 8512
        if(hFile != INVALID_HANDLE_VALUE)
        {
            nDataLength = GetFileSize(hFile, NULL);
8513
            *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8514 8515 8516 8517 8518 8519 8520 8521 8522
            ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
            CloseHandle(hFile);
        }
        DeleteFileW(wstrTempFile);
    }
    return nDataLength;
}

/*************************************************************************
8523
 * STORAGE_CreateOleStream [Internal]
8524
 *
8525
 * Creates the "\001OLE" stream in the IStorage if necessary.
8526 8527
 *
 * PARAMS
8528 8529
 *     storage     [I] Dest storage to create the stream in
 *     flags       [I] flags to be set for newly created stream
8530 8531
 *
 * RETURNS
8532
 *     HRESULT return value
8533 8534 8535 8536 8537
 *
 * 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
8538
 *     no need to recreate the stream.  If the stream is manually
8539
 *     deleted it will create it with this default data.
8540
 *
8541
 */
8542
HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8543
{
8544 8545 8546 8547 8548 8549 8550
    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)
8551
    {
8552 8553 8554
        struct empty_1ole_stream {
            DWORD version_magic;
            DWORD flags;
8555 8556 8557
            DWORD update_options;
            DWORD reserved;
            DWORD mon_stream_size;
8558 8559
        };
        struct empty_1ole_stream stream_data;
8560

8561 8562
        stream_data.version_magic = version_magic;
        stream_data.flags = flags;
8563 8564 8565
        stream_data.update_options = 0;
        stream_data.reserved = 0;
        stream_data.mon_stream_size = 0;
8566

8567 8568
        hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
        IStream_Release(stream);
8569
    }
8570 8571

    return hr;
8572 8573
}

8574 8575 8576 8577 8578 8579 8580 8581 8582
/* 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);
8583
    r = IStream_Write( stm, &len, sizeof(len), NULL);
8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602
    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;

8603
    r = IStream_Read( stm, &len, sizeof(len), &count );
8604 8605
    if( FAILED( r ) )
        return r;
8606
    if( count != sizeof(len) )
8607 8608
        return E_OUTOFMEMORY;

8609
    TRACE("%d bytes\n",len);
8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642
    
    str = CoTaskMemAlloc( len );
    if( !str )
        return E_OUTOFMEMORY;
    count = 0;
    r = IStream_Read( stm, str, len, &count );
    if( FAILED( r ) )
        return r;
    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 );
    CoTaskMemFree( str );

    *string = wstr;

    return r;
}


static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
    LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
{
    IStream *pstm;
    HRESULT r = S_OK;
8643
    static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655

    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 };

    TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
           debugstr_w(lpszUserType), debugstr_w(szClipName),
           debugstr_w(szProgIDName));

8656
    /*  Create a CompObj stream */
8657
    r = IStorage_CreateStream(pstg, szwStreamName,
8658
        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8659 8660 8661 8662
    if( FAILED (r) )
        return r;

    /* Write CompObj Structure to stream */
8663
    r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674

    if( SUCCEEDED( r ) )
        r = WriteClassStm( pstm, clsid );

    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 ) )
8675
        r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8676 8677 8678 8679 8680 8681 8682

    IStream_Release( pstm );

    return r;
}

/***********************************************************************
8683
 *               WriteFmtUserTypeStg (OLE32.@)
8684 8685 8686 8687 8688 8689
 */
HRESULT WINAPI WriteFmtUserTypeStg(
	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
{
    HRESULT r;
    WCHAR szwClipName[0x40];
8690 8691
    CLSID clsid = CLSID_NULL;
    LPWSTR wstrProgID = NULL;
8692 8693 8694 8695 8696
    DWORD n;

    TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));

    /* get the clipboard format name */
8697
    n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8698 8699 8700 8701
    szwClipName[n]=0;

    TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));

8702 8703 8704 8705
    /* FIXME: There's room to save a CLSID and its ProgID, but
       the CLSID is not looked up in the registry and in all the
       tests I wrote it was CLSID_NULL.  Where does it come from?
    */
8706

8707 8708
    /* get the real program ID.  This may fail, but that's fine */
    ProgIDFromCLSID(&clsid, &wstrProgID);
8709 8710 8711

    TRACE("progid is %s\n",debugstr_w(wstrProgID));

8712 8713
    r = STORAGE_WriteCompObj( pstg, &clsid, 
                              lpszUserType, szwClipName, wstrProgID );
8714

8715
    CoTaskMemFree(wstrProgID);
8716 8717 8718 8719 8720 8721

    return r;
}


/******************************************************************************
8722
 *              ReadFmtUserTypeStg        [OLE32.@]
8723 8724 8725 8726 8727
 */
HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
{
    HRESULT r;
    IStream *stm = 0;
8728
    static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740
    unsigned char unknown1[12];
    unsigned char unknown2[16];
    DWORD count;
    LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
    CLSID clsid;

    TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);

    r = IStorage_OpenStream( pstg, szCompObj, NULL, 
                    STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
    if( FAILED ( r ) )
    {
8741
        WARN("Failed to open stream r = %08x\n", r);
8742 8743 8744 8745
        return r;
    }

    /* read the various parts of the structure */
8746 8747
    r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
    if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764
        goto end;
    r = ReadClassStm( stm, &clsid );
    if( FAILED( r ) )
        goto end;

    r = STREAM_ReadString( stm, &szCLSIDName );
    if( FAILED( r ) )
        goto end;

    r = STREAM_ReadString( stm, &szOleTypeName );
    if( FAILED( r ) )
        goto end;

    r = STREAM_ReadString( stm, &szProgIDName );
    if( FAILED( r ) )
        goto end;

8765 8766
    r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
    if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783
        goto end;

    /* ok, success... now we just need to store what we found */
    if( pcf )
        *pcf = RegisterClipboardFormatW( szOleTypeName );
    CoTaskMemFree( szOleTypeName );

    if( lplpszUserType )
        *lplpszUserType = szCLSIDName;
    CoTaskMemFree( szProgIDName );

end:
    IStream_Release( stm );

    return r;
}

8784 8785

/*************************************************************************
8786
 * OLECONVERT_CreateCompObjStream [Internal]
8787 8788 8789 8790
 *
 * Creates a "\001CompObj" is the destination IStorage if necessary.
 *
 * PARAMS
8791
 *     pStorage       [I] The dest IStorage to create the CompObj Stream
8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802
 *                        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
8803
 *     no need to recreate the stream.  If the stream is manually
8804 8805
 *     deleted it will attempt to create it by querying the registry.
 *
8806
 *
8807 8808 8809 8810 8811 8812
 */
HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
{
    IStream *pStream;
    HRESULT hStorageRes, hRes = S_OK;
    OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8813
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8814
    WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8815 8816 8817 8818 8819 8820

    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));
8821 8822
    memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
    memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8823 8824 8825


    /*  Create a CompObj stream if it doesn't exist */
8826
    hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8827 8828 8829 8830 8831 8832 8833 8834
        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 */
8835
        /* Note: in the test made, these were Identical      */
8836 8837 8838 8839
        IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
        strcpy(IStorageCompObj.strProgIDName, strOleTypeName);

        /* Get the CLSID */
8840 8841 8842
        MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
                             bufferW, OLESTREAM_MAX_STR_LEN );
        hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8843

8844
        if(hRes == S_OK)
8845 8846 8847 8848
        {
            HKEY hKey;
            LONG hErr;
            /* Get the CLSID Default Name from the Registry */
8849
            hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8850 8851 8852 8853
            if(hErr == ERROR_SUCCESS)
            {
                char strTemp[OLESTREAM_MAX_STR_LEN];
                IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8854
                hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8855 8856 8857 8858 8859 8860 8861 8862
                if(hErr == ERROR_SUCCESS)
                {
                    strcpy(IStorageCompObj.strCLSIDName, strTemp);
                }
                RegCloseKey(hKey);
            }
        }

8863 8864 8865 8866 8867 8868 8869
        /* 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)
8870
        {
8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881
            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);
8882
        }
8883
        hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8884 8885 8886 8887 8888 8889 8890
        IStream_Release(pStream);
    }
    return hRes;
}


/*************************************************************************
8891
 * OLECONVERT_CreateOlePresStream[Internal]
8892 8893 8894 8895 8896 8897
 *
 * 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
8898
 *     dwExtentY    [I] Height of the Metafile
8899 8900 8901 8902 8903 8904 8905 8906 8907
 *     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.
8908
 *
8909
 */
8910
static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8911 8912 8913
{
    HRESULT hRes;
    IStream *pStream;
8914
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8915
    BYTE pOlePresStreamHeader [] =
8916
    {
8917 8918
        0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8919 8920 8921 8922
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };

8923
    BYTE pOlePresStreamHeaderEmpty [] =
8924
    {
8925 8926
        0x00, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8927 8928 8929
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };
8930

8931
    /* Create the OlePres000 Stream */
8932
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955
        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
8956
        /* Set Data and Length */
8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975
        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);
    }
}

/*************************************************************************
8976
 * OLECONVERT_CreateOle10NativeStream [Internal]
8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991
 *
 * 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
8992
 *
8993
 */
8994
static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8995 8996 8997
{
    HRESULT hRes;
    IStream *pStream;
8998
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8999

9000
    /* Create the Ole10Native Stream */
9001
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014
        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);
    }

}

/*************************************************************************
9015
 * OLECONVERT_GetOLE10ProgID [Internal]
9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030
 *
 * 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.
 *
9031
 *
9032
 */
9033
static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9034 9035 9036 9037 9038
{
    HRESULT hRes;
    IStream *pStream;
    LARGE_INTEGER iSeekPos;
    OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9039
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9040 9041

    /* Open the CompObj Stream */
9042
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9043 9044 9045 9046 9047
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {

        /*Get the OleType from the CompObj Stream */
9048 9049
        iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
        iSeekPos.u.HighPart = 0;
9050 9051 9052

        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
        IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9053
        iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9054 9055
        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
        IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9056
        iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078
        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);
        }
9079

9080 9081 9082 9083 9084
    }
    return hRes;
}

/*************************************************************************
9085
 * OLECONVERT_GetOle10PresData [Internal]
9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099
 *
 * 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
9100 9101
 *
 *
9102
 */
9103
static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9104 9105 9106 9107
{

    HRESULT hRes;
    IStream *pStream;
9108
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120

    /* 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 */
9121
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9122 9123 9124 9125 9126 9127 9128 9129
        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)
        {
9130
            pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9131 9132 9133 9134 9135 9136 9137 9138 9139
            IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
        }
        IStream_Release(pStream);
    }

}


/*************************************************************************
9140
 * OLECONVERT_GetOle20PresData[Internal]
9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152
 *
 * 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.
9153
 *
9154 9155
 *     Memory allocated for pData must be freed by the caller
 */
9156
static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9157 9158 9159 9160
{
    HRESULT hRes;
    IStream *pStream;
    OLECONVERT_ISTORAGE_OLEPRES olePress;
9161
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179

    /* 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 */
9180
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9181 9182 9183 9184 9185
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {
        LARGE_INTEGER iSeekPos;
        METAFILEPICT16 MetaFilePict;
9186
        static const char strMetafilePictName[] = "METAFILEPICT";
9187 9188 9189 9190 9191 9192 9193 9194

        /* 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);

9195 9196
        iSeekPos.u.HighPart = 0;
        iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218

        /* 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 */
9219
            pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9220 9221 9222 9223 9224 9225 9226 9227
            memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
            IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
        }
        IStream_Release(pStream);
    }
}

/*************************************************************************
9228
 * OleConvertOLESTREAMToIStorage [OLE32.@]
9229 9230 9231 9232
 *
 * Read info on MSDN
 *
 * TODO
Austin English's avatar
Austin English committed
9233
 *      DVTARGETDEVICE parameter is not handled
9234 9235 9236
 *      Still unsure of some mem fields for OLE 10 Stream
 *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
 *      and "\001OLE" streams
9237
 *
9238 9239
 */
HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9240 9241
    LPOLESTREAM pOleStream,
    LPSTORAGE pstg,
9242 9243 9244 9245 9246 9247
    const DVTARGETDEVICE* ptd)
{
    int i;
    HRESULT hRes=S_OK;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];

9248 9249
    TRACE("%p %p %p\n", pOleStream, pstg, ptd);

9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264
    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
9265
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9266 9267 9268 9269 9270
    }

    if(hRes == S_OK)
    {
        /* Load the OLESTREAM to Memory (part 2)*/
Alexandre Julliard's avatar
Alexandre Julliard committed
9271
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301
    }

    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 */
9302
            STORAGE_CreateOleStream(pstg, 0);
9303 9304 9305 9306 9307 9308 9309
        }
    }


    /* Free allocated memory */
    for(i=0; i < 2; i++)
    {
9310 9311 9312
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
        pOleStreamData[i].pstrOleObjFileName = NULL;
9313 9314 9315 9316 9317
    }
    return hRes;
}

/*************************************************************************
9318
 * OleConvertIStorageToOLESTREAM [OLE32.@]
9319 9320 9321 9322 9323 9324 9325 9326 9327
 *
 * 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.
9328
 *
9329 9330
 */
HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9331
    LPSTORAGE pstg,
9332 9333 9334 9335 9336 9337
    LPOLESTREAM pOleStream)
{
    int i;
    HRESULT hRes = S_OK;
    IStream *pStream;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9338
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9339

9340
    TRACE("%p %p\n", pstg, pOleStream);
9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355

    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
9356
        /* Was it originally Ole10 */
9357
        hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9358 9359 9360
        if(hRes == S_OK)
        {
            IStream_Release(pStream);
Alexandre Julliard's avatar
Alexandre Julliard committed
9361
            /* Get Presentation Data for Ole10Native */
9362 9363 9364 9365
            OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
        }
        else
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
9366
            /* Get Presentation Data (OLE20) */
9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381
            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++)
    {
9382
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9383 9384 9385 9386
    }

    return hRes;
}
9387

9388 9389
enum stream_1ole_flags {
    OleStream_LinkedObject = 0x00000001,
9390
    OleStream_Convert      = 0x00000004
9391 9392
};

9393
/***********************************************************************
9394
 *		GetConvertStg (OLE32.@)
9395
 */
9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410
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;

9411
    hr = IStream_Read(stream, header, sizeof(header), NULL);
9412 9413 9414 9415 9416
    IStream_Release(stream);
    if (FAILED(hr)) return hr;

    if (header[0] != version_magic)
    {
9417
        ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9418 9419 9420 9421
        return E_FAIL;
    }

    return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9422
}
9423

9424 9425 9426 9427 9428 9429 9430 9431 9432 9433
/***********************************************************************
 *		SetConvertStg (OLE32.@)
 */
HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
{
    DWORD flags = convert ? OleStream_Convert : 0;
    HRESULT hr;

    TRACE("(%p, %d)\n", storage, convert);

9434 9435
    hr = STORAGE_CreateOleStream(storage, flags);
    if (hr == STG_E_FILEALREADYEXISTS)
9436
    {
9437 9438
        static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
        IStream *stream;
9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476
        DWORD header[2];

        hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
        if (FAILED(hr)) return hr;

        hr = IStream_Read(stream, header, sizeof(header), NULL);
        if (FAILED(hr))
        {
            IStream_Release(stream);
            return hr;
        }

        /* update flag if differs */
        if ((header[1] ^ flags) & OleStream_Convert)
        {
            LARGE_INTEGER pos;

            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);
            if (FAILED(hr))
            {
                IStream_Release(stream);
                return hr;
            }

            hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
        }
        IStream_Release(stream);
    }

    return hr;
}

9477
/******************************************************************************
9478
 * StgIsStorageFile [OLE32.@]
9479 9480 9481 9482 9483 9484 9485 9486
 * Verify if the file contains a storage object
 *
 * PARAMS
 *  fn      [ I] Filename
 *
 * RETURNS
 *  S_OK    if file has magic bytes as a storage object
 *  S_FALSE if file is not storage
9487 9488 9489 9490 9491 9492 9493 9494
 */
HRESULT WINAPI
StgIsStorageFile(LPCOLESTR fn)
{
	HANDLE		hf;
	BYTE		magic[8];
	DWORD		bytes_read;

9495
	TRACE("%s\n", debugstr_w(fn));
9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 9510 9511 9512
	hf = CreateFileW(fn, GENERIC_READ,
	                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
	                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

	if (hf == INVALID_HANDLE_VALUE)
		return STG_E_FILENOTFOUND;

	if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
	{
		WARN(" unable to read file\n");
		CloseHandle(hf);
		return S_FALSE;
	}

	CloseHandle(hf);

	if (bytes_read != 8) {
9513
		TRACE(" too short\n");
9514 9515 9516 9517
		return S_FALSE;
	}

	if (!memcmp(magic,STORAGE_magic,8)) {
9518
		TRACE(" -> YES\n");
9519 9520 9521
		return S_OK;
	}

9522
	TRACE(" -> Invalid header.\n");
9523 9524
	return S_FALSE;
}
9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542

/***********************************************************************
 *		WriteClassStm (OLE32.@)
 *
 * Writes a CLSID to a stream.
 *
 * PARAMS
 *  pStm   [I] Stream to write to.
 *  rclsid [I] CLSID to write.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
{
    TRACE("(%p,%p)\n",pStm,rclsid);

9543
    if (!pStm || !rclsid)
9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568
        return E_INVALIDARG;

    return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
}

/***********************************************************************
 *		ReadClassStm (OLE32.@)
 *
 * Reads a CLSID from a stream.
 *
 * PARAMS
 *  pStm   [I] Stream to read from.
 *  rclsid [O] CLSID to read.
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: HRESULT code.
 */
HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
{
    ULONG nbByte;
    HRESULT res;

    TRACE("(%p,%p)\n",pStm,pclsid);

9569
    if (!pStm || !pclsid)
9570 9571
        return E_INVALIDARG;

9572
    /* clear the output args */
9573
    *pclsid = CLSID_NULL;
9574

9575
    res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9576 9577 9578 9579 9580

    if (FAILED(res))
        return res;

    if (nbByte != sizeof(CLSID))
9581
        return STG_E_READFAULT;
9582 9583 9584
    else
        return S_OK;
}