storage32.c 212 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 31 32 33
 *
 * 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.
 *
 * MSDN
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
34 35 36
 */

#include <assert.h>
37
#include <stdarg.h>
38
#include <stdio.h>
Patrik Stridvall's avatar
Patrik Stridvall committed
39
#include <stdlib.h>
40 41
#include <string.h>

42
#define COBJMACROS
43 44
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
45

46 47
#include "windef.h"
#include "winbase.h"
48
#include "winnls.h"
49
#include "winuser.h"
50
#include "wine/unicode.h"
51
#include "wine/debug.h"
52 53

#include "storage32.h"
54
#include "ole2.h"      /* For Write/ReadClassStm */
55

56 57 58
#include "winreg.h"
#include "wine/wingdi16.h"

59
WINE_DEFAULT_DEBUG_CHANNEL(storage);
60

61 62 63 64
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
#define OLESTREAM_ID 0x501
#define OLESTREAM_MAX_STR_LEN 255

65 66
static const char rootPropertyName[] = "Root Entry";

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
/****************************************************************************
 * 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;
  /*
   * There is no specific data for this class.
   */
};
typedef struct StorageInternalImpl StorageInternalImpl;

/* Method definitions for the Storage32InternalImpl class. */
static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
                                                          DWORD openFlags, ULONG rootTropertyIndex);
static void StorageImpl_Destroy(StorageBaseImpl* iface);
static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
static void StorageImpl_SaveFileHeader(StorageImpl* This);

static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm);

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

110 111 112

/* OLESTREAM memory structure to use for Get and Put Routines */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
113
typedef struct
114 115 116 117 118
{
    DWORD dwOleID;
    DWORD dwTypeID;
    DWORD dwOleTypeNameLength;
    CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
Alexandre Julliard's avatar
Alexandre Julliard committed
119 120
    CHAR  *pstrOleObjFileName;
    DWORD dwOleObjFileNameLength;
121 122
    DWORD dwMetaFileWidth;
    DWORD dwMetaFileHeight;
123
    CHAR  strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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;


/* Ole Presention Stream structure */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
    BYTE byUnknown1[28];
    DWORD dwExtentX;
    DWORD dwExtentY;
151
    DWORD dwSize;
152 153 154 155 156
    BYTE *pData;
}OLECONVERT_ISTORAGE_OLEPRES;



157 158 159 160
/***********************************************************************
 * Forward declaration of internal functions used by the method DestroyElement
 */
static HRESULT deleteStorageProperty(
161
  StorageImpl *parentStorage,
162 163
  ULONG        foundPropertyIndexToDelete,
  StgProperty  propertyToDelete);
164 165

static HRESULT deleteStreamProperty(
166
  StorageImpl *parentStorage,
167 168 169 170
  ULONG         foundPropertyIndexToDelete,
  StgProperty   propertyToDelete);

static HRESULT findPlaceholder(
171
  StorageImpl *storage,
172 173
  ULONG         propertyIndexToStore,
  ULONG         storagePropertyIndex,
174
  INT         typeOfRelation);
175

176
static HRESULT adjustPropertyChain(
177
  StorageImpl *This,
178 179 180
  StgProperty   propertyToDelete,
  StgProperty   parentProperty,
  ULONG         parentPropertyId,
181
  INT         typeOfRelation);
182 183 184 185 186 187

/***********************************************************************
 * Declaration of the functions used to manipulate StgProperty
 */

static ULONG getFreeProperty(
188
  StorageImpl *storage);
189 190

static void updatePropertyChain(
191
  StorageImpl *storage,
192 193 194 195
  ULONG       newPropertyIndex,
  StgProperty newProperty);

static LONG propertyNameCmp(
Eric Pouech's avatar
Eric Pouech committed
196 197
    const OLECHAR *newProperty,
    const OLECHAR *currentProperty);
198

199 200 201 202

/***********************************************************************
 * Declaration of miscellaneous functions...
 */
203
static HRESULT validateSTGM(DWORD stgmValue);
204 205 206 207 208

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

209
extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
210 211


212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
/****************************************************************************
 * 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
{
  const IEnumSTATSTGVtbl *lpVtbl;    /* Needs to be the first item in the struct
				* since we want to cast this in an IEnumSTATSTG pointer */

  LONG           ref;                   /* Reference count */
  StorageImpl*   parentStorage;         /* Reference to the parent storage */
  ULONG          firstPropertyNode;     /* Index of the root of the storage to enumerate */

  /*
   * The current implementation of the IEnumSTATSTGImpl class uses a stack
   * to walk the property sets to get the content of a storage. This stack
   * is implemented by the following 3 data members
   */
  ULONG          stackSize;
  ULONG          stackMaxSize;
  ULONG*         stackToVisit;

#define ENUMSTATSGT_SIZE_INCREMENT 10
};


static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
                                           StgProperty* buffer);
static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
                                               StgProperty *currentProperty, ULONG *propertyId);

250

251 252 253 254 255 256 257 258 259
/************************************************************************
** Storage32BaseImpl implementatiion
*/

/************************************************************************
 * Storage32BaseImpl_QueryInterface (IUnknown)
 *
 * This method implements the common QueryInterface for all IStorage32
 * implementations contained in this file.
260
 *
261 262
 * See Windows documentation for more details on IUnknown methods.
 */
263
static HRESULT WINAPI StorageBaseImpl_QueryInterface(
264
  IStorage*        iface,
265 266 267
  REFIID             riid,
  void**             ppvObject)
{
268
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
269 270 271 272 273
  /*
   * Perform a sanity check on the parameters.
   */
  if ( (This==0) || (ppvObject==0) )
    return E_INVALIDARG;
274

275 276 277 278
  /*
   * Initialize the return parameter.
   */
  *ppvObject = 0;
279

280 281 282
  /*
   * Compare the riid with the interface IDs implemented by this object.
   */
283 284
  if (IsEqualGUID(&IID_IUnknown, riid) ||
      IsEqualGUID(&IID_IStorage, riid))
285
  {
286
    *ppvObject = (IStorage*)This;
287
  }
288
  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
289 290 291
  {
    *ppvObject = (IStorage*)&This->pssVtbl;
  }
292

293 294 295 296 297
  /*
   * Check that we obtained an interface.
   */
  if ((*ppvObject)==0)
    return E_NOINTERFACE;
298

299 300 301 302
  /*
   * Query Interface always increases the reference count by one when it is
   * successful
   */
303
  IStorage_AddRef(iface);
304

305
  return S_OK;
306
}
307

308 309 310 311 312
/************************************************************************
 * Storage32BaseImpl_AddRef (IUnknown)
 *
 * This method implements the common AddRef for all IStorage32
 * implementations contained in this file.
313
 *
314 315
 * See Windows documentation for more details on IUnknown methods.
 */
316
static ULONG WINAPI StorageBaseImpl_AddRef(
317
            IStorage* iface)
318
{
319
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 321
  ULONG ref = InterlockedIncrement(&This->ref);

322
  TRACE("(%p) AddRef to %d\n", This, ref);
323 324

  return ref;
325
}
326

327 328 329 330 331
/************************************************************************
 * Storage32BaseImpl_Release (IUnknown)
 *
 * This method implements the common Release for all IStorage32
 * implementations contained in this file.
332
 *
333 334
 * See Windows documentation for more details on IUnknown methods.
 */
335
static ULONG WINAPI StorageBaseImpl_Release(
336
      IStorage* iface)
337
{
338
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
339 340 341
  /*
   * Decrease the reference count on this object.
   */
342
  ULONG ref = InterlockedDecrement(&This->ref);
343

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

346 347 348
  /*
   * If the reference count goes down to 0, perform suicide.
   */
349
  if (ref == 0)
350 351
  {
    /*
352 353
     * 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
354 355 356 357
     * using virtual functions to implement the destructor.
     */
    This->v_destructor(This);
  }
358

359
  return ref;
360 361 362 363 364 365 366 367 368
}

/************************************************************************
 * Storage32BaseImpl_OpenStream (IStorage)
 *
 * This method will open the specified stream object from the current storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
369
static HRESULT WINAPI StorageBaseImpl_OpenStream(
370 371
  IStorage*        iface,
  const OLECHAR*   pwcsName,  /* [string][in] */
372
  void*            reserved1, /* [unique][in] */
373 374 375
  DWORD            grfMode,   /* [in]  */
  DWORD            reserved2, /* [in]  */
  IStream**        ppstm)     /* [out] */
376
{
377
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
378 379 380 381
  IEnumSTATSTGImpl* propertyEnumeration;
  StgStreamImpl*    newStream;
  StgProperty       currentProperty;
  ULONG             foundPropertyIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
382
  HRESULT           res = STG_E_UNKNOWN;
383

384
  TRACE("(%p, %s, %p, %x, %d, %p)\n",
385 386
	iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);

387 388 389 390
  /*
   * Perform a sanity check on the parameters.
   */
  if ( (pwcsName==NULL) || (ppstm==0) )
Alexandre Julliard's avatar
Alexandre Julliard committed
391 392 393 394
  {
    res = E_INVALIDARG;
    goto end;
  }
395

396 397 398
  /*
   * Initialize the out parameter
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
399
  *ppstm = NULL;
400

401 402 403
  /*
   * Validate the STGM flags
   */
404 405
  if ( FAILED( validateSTGM(grfMode) ) ||
       STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
Alexandre Julliard's avatar
Alexandre Julliard committed
406 407 408 409
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
410 411 412 413

  /*
   * As documented.
   */
414
  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
Alexandre Julliard's avatar
Alexandre Julliard committed
415 416 417 418
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
419

420
  /*
421 422
   * Check that we're compatible with the parent's storage mode, but
   * only if we are not in transacted mode
423
   */
424
  if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
425
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
426 427 428 429
    {
      res = STG_E_ACCESSDENIED;
      goto end;
    }
430 431
  }

432 433 434 435
  /*
   * Create a property enumeration to search the properties
   */
  propertyEnumeration = IEnumSTATSTGImpl_Construct(
436
    This->ancestorStorage,
437
    This->rootPropertySetIndex);
438

439 440 441 442 443 444 445
  /*
   * Search the enumeration for the property with the given name
   */
  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
    propertyEnumeration,
    pwcsName,
    &currentProperty);
446

447 448 449 450
  /*
   * Delete the property enumeration since we don't need it anymore
   */
  IEnumSTATSTGImpl_Destroy(propertyEnumeration);
451

452 453 454
  /*
   * If it was found, construct the stream object and return a pointer to it.
   */
455
  if ( (foundPropertyIndex!=PROPERTY_NULL) &&
456 457
       (currentProperty.propertyType==PROPTYPE_STREAM) )
  {
458
    newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
459

460 461
    if (newStream!=0)
    {
462
      newStream->grfMode = grfMode;
463
      *ppstm = (IStream*)newStream;
464

465
      /*
466
       * Since we are returning a pointer to the interface, we have to
467 468
       * nail down the reference.
       */
469
      IStream_AddRef(*ppstm);
470

471 472 473 474 475 476
      /*
       * add us to the storage's list of active streams
       */

      StorageBaseImpl_AddStream(This,newStream);

Alexandre Julliard's avatar
Alexandre Julliard committed
477 478
      res = S_OK;
      goto end;
479
    }
480

Alexandre Julliard's avatar
Alexandre Julliard committed
481 482
    res = E_OUTOFMEMORY;
    goto end;
483
  }
484

Alexandre Julliard's avatar
Alexandre Julliard committed
485 486 487 488 489
  res = STG_E_FILENOTFOUND;

end:
  if (res == S_OK)
    TRACE("<-- IStream %p\n", *ppstm);
490
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
491
  return res;
492 493 494 495 496 497
}

/************************************************************************
 * Storage32BaseImpl_OpenStorage (IStorage)
 *
 * This method will open a new storage object from the current storage.
498
 *
499
 * See Windows documentation for more details on IStorage methods.
500
 */
501
static HRESULT WINAPI StorageBaseImpl_OpenStorage(
502
  IStorage*        iface,
503 504 505 506 507 508
  const OLECHAR*   pwcsName,      /* [string][unique][in] */
  IStorage*        pstgPriority,  /* [unique][in] */
  DWORD            grfMode,       /* [in] */
  SNB              snbExclude,    /* [unique][in] */
  DWORD            reserved,      /* [in] */
  IStorage**       ppstg)         /* [out] */
509
{
510
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
511
  StorageInternalImpl* newStorage;
512 513 514
  IEnumSTATSTGImpl*      propertyEnumeration;
  StgProperty            currentProperty;
  ULONG                  foundPropertyIndex;
Alexandre Julliard's avatar
Alexandre Julliard committed
515
  HRESULT                res = STG_E_UNKNOWN;
516

517
  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
518
	iface, debugstr_w(pwcsName), pstgPriority,
519
	grfMode, snbExclude, reserved, ppstg);
520

521 522 523 524
  /*
   * Perform a sanity check on the parameters.
   */
  if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
Alexandre Julliard's avatar
Alexandre Julliard committed
525 526 527 528
  {
    res = E_INVALIDARG;
    goto end;
  }
529

Alexandre Julliard's avatar
Alexandre Julliard committed
530 531 532 533 534 535
  /* as documented */
  if (snbExclude != NULL)
  {
    res = STG_E_INVALIDPARAMETER;
    goto end;
  }
536

537 538 539 540
  /*
   * Validate the STGM flags
   */
  if ( FAILED( validateSTGM(grfMode) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
541 542 543 544
  {
    res = STG_E_INVALIDFLAG;
    goto end;
  }
545 546 547 548

  /*
   * As documented.
   */
549
  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 551
        (grfMode & STGM_DELETEONRELEASE) ||
        (grfMode & STGM_PRIORITY) )
Alexandre Julliard's avatar
Alexandre Julliard committed
552 553 554 555
  {
    res = STG_E_INVALIDFUNCTION;
    goto end;
  }
556

557
  /*
558 559
   * Check that we're compatible with the parent's storage mode,
   * but only if we are not transacted
560
   */
561
  if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
562
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
563 564 565 566
    {
      res = STG_E_ACCESSDENIED;
      goto end;
    }
567 568
  }

569 570 571
  /*
   * Initialize the out parameter
   */
Alexandre Julliard's avatar
Alexandre Julliard committed
572
  *ppstg = NULL;
573

574 575 576 577
  /*
   * Create a property enumeration to search the properties
   */
  propertyEnumeration = IEnumSTATSTGImpl_Construct(
578
                          This->ancestorStorage,
579
                          This->rootPropertySetIndex);
580

581 582 583 584 585 586 587
  /*
   * Search the enumeration for the property with the given name
   */
  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
                         propertyEnumeration,
                         pwcsName,
                         &currentProperty);
588

589 590 591 592
  /*
   * Delete the property enumeration since we don't need it anymore
   */
  IEnumSTATSTGImpl_Destroy(propertyEnumeration);
593

594 595 596
  /*
   * If it was found, construct the stream object and return a pointer to it.
   */
597
  if ( (foundPropertyIndex!=PROPERTY_NULL) &&
598 599 600 601 602
       (currentProperty.propertyType==PROPTYPE_STORAGE) )
  {
    /*
     * Construct a new Storage object
     */
603
    newStorage = StorageInternalImpl_Construct(
604
                   This->ancestorStorage,
605
                   grfMode,
606
                   foundPropertyIndex);
607

608 609
    if (newStorage != 0)
    {
610
      *ppstg = (IStorage*)newStorage;
611

612
      /*
613
       * Since we are returning a pointer to the interface,
614 615
       * we have to nail down the reference.
       */
616
      StorageBaseImpl_AddRef(*ppstg);
617

Alexandre Julliard's avatar
Alexandre Julliard committed
618 619
      res = S_OK;
      goto end;
620
    }
621

Alexandre Julliard's avatar
Alexandre Julliard committed
622 623
    res = STG_E_INSUFFICIENTMEMORY;
    goto end;
624
  }
625

Alexandre Julliard's avatar
Alexandre Julliard committed
626 627 628
  res = STG_E_FILENOTFOUND;

end:
629
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
630
  return res;
631 632 633 634 635
}

/************************************************************************
 * Storage32BaseImpl_EnumElements (IStorage)
 *
636
 * This method will create an enumerator object that can be used to
637
 * retrieve informatino about all the properties in the storage object.
638
 *
639
 * See Windows documentation for more details on IStorage methods.
640
 */
641
static HRESULT WINAPI StorageBaseImpl_EnumElements(
642
  IStorage*       iface,
643 644 645 646
  DWORD           reserved1, /* [in] */
  void*           reserved2, /* [size_is][unique][in] */
  DWORD           reserved3, /* [in] */
  IEnumSTATSTG**  ppenum)    /* [out] */
647
{
648
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
649 650
  IEnumSTATSTGImpl* newEnum;

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

654 655 656 657 658
  /*
   * Perform a sanity check on the parameters.
   */
  if ( (This==0) || (ppenum==0))
    return E_INVALIDARG;
659

660 661 662 663 664 665 666 667 668
  /*
   * Construct the enumerator.
   */
  newEnum = IEnumSTATSTGImpl_Construct(
              This->ancestorStorage,
              This->rootPropertySetIndex);

  if (newEnum!=0)
  {
669 670
    *ppenum = (IEnumSTATSTG*)newEnum;

671 672 673 674
    /*
     * Don't forget to nail down a reference to the new object before
     * returning it.
     */
675
    IEnumSTATSTG_AddRef(*ppenum);
676

677 678 679 680 681 682 683 684 685 686
    return S_OK;
  }

  return E_OUTOFMEMORY;
}

/************************************************************************
 * Storage32BaseImpl_Stat (IStorage)
 *
 * This method will retrieve information about this storage object.
687
 *
688
 * See Windows documentation for more details on IStorage methods.
689
 */
690
static HRESULT WINAPI StorageBaseImpl_Stat(
691
  IStorage*        iface,
692 693
  STATSTG*         pstatstg,     /* [out] */
  DWORD            grfStatFlag)  /* [in] */
694
{
695
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
696
  StgProperty    curProperty;
Alexandre Julliard's avatar
Alexandre Julliard committed
697 698
  BOOL           readSuccessful;
  HRESULT        res = STG_E_UNKNOWN;
699

700
  TRACE("(%p, %p, %x)\n",
701 702
	iface, pstatstg, grfStatFlag);

703 704 705 706
  /*
   * Perform a sanity check on the parameters.
   */
  if ( (This==0) || (pstatstg==0))
Alexandre Julliard's avatar
Alexandre Julliard committed
707 708 709 710
  {
    res = E_INVALIDARG;
    goto end;
  }
711 712 713 714

  /*
   * Read the information from the property.
   */
715
  readSuccessful = StorageImpl_ReadProperty(
716 717 718 719
                    This->ancestorStorage,
                    This->rootPropertySetIndex,
                    &curProperty);

720
  if (readSuccessful)
721 722
  {
    StorageUtl_CopyPropertyToSTATSTG(
723 724
      pstatstg,
      &curProperty,
725
      grfStatFlag);
726

727 728
    pstatstg->grfMode = This->openFlags;

Alexandre Julliard's avatar
Alexandre Julliard committed
729 730
    res = S_OK;
    goto end;
731
  }
732

Alexandre Julliard's avatar
Alexandre Julliard committed
733 734 735 736 737
  res = E_FAIL;

end:
  if (res == S_OK)
  {
738
    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
739
  }
740
  TRACE("<-- %08x\n", res);
Alexandre Julliard's avatar
Alexandre Julliard committed
741
  return res;
742 743 744 745 746
}

/************************************************************************
 * Storage32BaseImpl_RenameElement (IStorage)
 *
747
 * This method will rename the specified element.
748 749
 *
 * See Windows documentation for more details on IStorage methods.
750 751 752
 *
 * Implementation notes: The method used to rename consists of creating a clone
 *    of the deleted StgProperty object setting it with the new name and to
753 754
 *    perform a DestroyElement of the old StgProperty.
 */
755
static HRESULT WINAPI StorageBaseImpl_RenameElement(
756 757 758
            IStorage*        iface,
            const OLECHAR*   pwcsOldName,  /* [in] */
            const OLECHAR*   pwcsNewName)  /* [in] */
759
{
760
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
761 762 763 764
  IEnumSTATSTGImpl* propertyEnumeration;
  StgProperty       currentProperty;
  ULONG             foundPropertyIndex;

765
  TRACE("(%p, %s, %s)\n",
766 767
	iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));

768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
  /*
   * Create a property enumeration to search the properties
   */
  propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
                                                   This->rootPropertySetIndex);

  /*
   * Search the enumeration for the new property name
   */
  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
                                                     pwcsNewName,
                                                     &currentProperty);

  if (foundPropertyIndex != PROPERTY_NULL)
  {
    /*
     * There is already a property with the new name
     */
    IEnumSTATSTGImpl_Destroy(propertyEnumeration);
    return STG_E_FILEALREADYEXISTS;
  }

790
  IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811

  /*
   * Search the enumeration for the old property name
   */
  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
                                                     pwcsOldName,
                                                     &currentProperty);

  /*
   * Delete the property enumeration since we don't need it anymore
   */
  IEnumSTATSTGImpl_Destroy(propertyEnumeration);

  if (foundPropertyIndex != PROPERTY_NULL)
  {
    StgProperty renamedProperty;
    ULONG       renamedPropertyIndex;

    /*
     * Setup a new property for the renamed property
     */
812
    renamedProperty.sizeOfNameString =
813
      ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
814

815 816
    if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
      return STG_E_INVALIDNAME;
817

818
    strcpyW(renamedProperty.name, pwcsNewName);
819

820 821
    renamedProperty.propertyType  = currentProperty.propertyType;
    renamedProperty.startingBlock = currentProperty.startingBlock;
822 823
    renamedProperty.size.u.LowPart  = currentProperty.size.u.LowPart;
    renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
824

825 826
    renamedProperty.previousProperty = PROPERTY_NULL;
    renamedProperty.nextProperty     = PROPERTY_NULL;
827

828 829 830 831 832
    /*
     * Bring the dirProperty link in case it is a storage and in which
     * case the renamed storage elements don't require to be reorganized.
     */
    renamedProperty.dirProperty = currentProperty.dirProperty;
833 834

    /* call CoFileTime to get the current time
835 836 837 838
    renamedProperty.timeStampS1
    renamedProperty.timeStampD1
    renamedProperty.timeStampS2
    renamedProperty.timeStampD2
839
    renamedProperty.propertyUniqueID
840
    */
841 842

    /*
843 844 845
     * Obtain a free property in the property chain
     */
    renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
846

847 848
    /*
     * Save the new property into the new property spot
849
     */
850
    StorageImpl_WriteProperty(
851
      This->ancestorStorage,
852
      renamedPropertyIndex,
853
      &renamedProperty);
854 855

    /*
856 857 858
     * Find a spot in the property chain for our newly created property.
     */
    updatePropertyChain(
859
      (StorageImpl*)This,
860
      renamedPropertyIndex,
861 862 863
      renamedProperty);

    /*
864
     * At this point the renamed property has been inserted in the tree,
865
     * now, before Destroying the old property we must zero its dirProperty
866
     * otherwise the DestroyProperty below will zap it all and we do not want
867 868 869
     * this to happen.
     * Also, we fake that the old property is a storage so the DestroyProperty
     * will not do a SetSize(0) on the stream data.
870
     *
871
     * This means that we need to tweak the StgProperty if it is a stream or a
872 873
     * non empty storage.
     */
874 875 876 877
    StorageImpl_ReadProperty(This->ancestorStorage,
                             foundPropertyIndex,
                             &currentProperty);

878 879
    currentProperty.dirProperty  = PROPERTY_NULL;
    currentProperty.propertyType = PROPTYPE_STORAGE;
880
    StorageImpl_WriteProperty(
881
      This->ancestorStorage,
882
      foundPropertyIndex,
883 884
      &currentProperty);

885 886
    /*
     * Invoke Destroy to get rid of the ole property and automatically redo
887
     * the linking of its previous and next members...
888
     */
889
    IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905

  }
  else
  {
    /*
     * There is no property with the old name
     */
    return STG_E_FILENOTFOUND;
  }

  return S_OK;
}

/************************************************************************
 * Storage32BaseImpl_CreateStream (IStorage)
 *
906
 * This method will create a stream object within this storage
907 908 909
 *
 * See Windows documentation for more details on IStorage methods.
 */
910
static HRESULT WINAPI StorageBaseImpl_CreateStream(
911 912
            IStorage*        iface,
            const OLECHAR*   pwcsName,  /* [string][in] */
913 914 915
            DWORD            grfMode,   /* [in] */
            DWORD            reserved1, /* [in] */
            DWORD            reserved2, /* [in] */
916
            IStream**        ppstm)     /* [out] */
917
{
918
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
919 920 921 922 923
  IEnumSTATSTGImpl* propertyEnumeration;
  StgStreamImpl*    newStream;
  StgProperty       currentProperty, newStreamProperty;
  ULONG             foundPropertyIndex, newPropertyIndex;

924
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
925
	iface, debugstr_w(pwcsName), grfMode,
926 927
	reserved1, reserved2, ppstm);

928 929 930 931 932 933 934 935 936
  /*
   * Validate parameters
   */
  if (ppstm == 0)
    return STG_E_INVALIDPOINTER;

  if (pwcsName == 0)
    return STG_E_INVALIDNAME;

937 938 939
  if (reserved1 || reserved2)
    return STG_E_INVALIDPARAMETER;

940 941 942 943 944 945
  /*
   * Validate the STGM flags
   */
  if ( FAILED( validateSTGM(grfMode) ))
    return STG_E_INVALIDFLAG;

946
  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
947 948
    return STG_E_INVALIDFLAG;

949 950 951
  /*
   * As documented.
   */
952 953
  if ((grfMode & STGM_DELETEONRELEASE) ||
      (grfMode & STGM_TRANSACTED))
954 955
    return STG_E_INVALIDFUNCTION;

956 957
  /*
   * Check that we're compatible with the parent's storage mode
958
   * if not in transacted mode
959
   */
960 961
  if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 963
      return STG_E_ACCESSDENIED;
  }
964

965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
  /*
   * Initialize the out parameter
   */
  *ppstm = 0;

  /*
   * Create a property enumeration to search the properties
   */
  propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
                                                   This->rootPropertySetIndex);

  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
                                                     pwcsName,
                                                     &currentProperty);

  IEnumSTATSTGImpl_Destroy(propertyEnumeration);

  if (foundPropertyIndex != PROPERTY_NULL)
  {
    /*
985
     * An element with this name already exists
986
     */
987
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
988
    {
989
      IStorage_DestroyElement(iface, pwcsName);
990
    }
991
    else
992 993
      return STG_E_FILEALREADYEXISTS;
  }
994 995 996 997 998
  else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
  {
    WARN("read-only storage\n");
    return STG_E_ACCESSDENIED;
  }
999

1000 1001
  /*
   * memset the empty property
1002 1003 1004 1005
   */
  memset(&newStreamProperty, 0, sizeof(StgProperty));

  newStreamProperty.sizeOfNameString =
1006
      ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1007 1008 1009 1010

  if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
    return STG_E_INVALIDNAME;

1011
  strcpyW(newStreamProperty.name, pwcsName);
1012 1013 1014

  newStreamProperty.propertyType  = PROPTYPE_STREAM;
  newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1015 1016
  newStreamProperty.size.u.LowPart  = 0;
  newStreamProperty.size.u.HighPart = 0;
1017 1018 1019 1020 1021

  newStreamProperty.previousProperty = PROPERTY_NULL;
  newStreamProperty.nextProperty     = PROPERTY_NULL;
  newStreamProperty.dirProperty      = PROPERTY_NULL;

1022
  /* call CoFileTime to get the current time
1023 1024 1025 1026 1027 1028 1029 1030 1031
  newStreamProperty.timeStampS1
  newStreamProperty.timeStampD1
  newStreamProperty.timeStampS2
  newStreamProperty.timeStampD2
  */

  /*  newStreamProperty.propertyUniqueID */

  /*
1032
   * Get a free property or create a new one
1033 1034 1035 1036 1037
   */
  newPropertyIndex = getFreeProperty(This->ancestorStorage);

  /*
   * Save the new property into the new property spot
1038
   */
1039
  StorageImpl_WriteProperty(
1040
    This->ancestorStorage,
1041
    newPropertyIndex,
1042 1043
    &newStreamProperty);

1044
  /*
1045 1046 1047
   * Find a spot in the property chain for our newly created property.
   */
  updatePropertyChain(
1048
    (StorageImpl*)This,
1049
    newPropertyIndex,
1050 1051
    newStreamProperty);

1052
  /*
1053 1054
   * Open the stream to return it.
   */
1055
  newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1056 1057 1058

  if (newStream != 0)
  {
1059
    *ppstm = (IStream*)newStream;
1060

1061 1062 1063 1064
    /*
     * Since we are returning a pointer to the interface, we have to nail down
     * the reference.
     */
1065
    IStream_AddRef(*ppstm);
1066 1067 1068 1069 1070

    /* add us to the storage's list of active streams
     */
    StorageBaseImpl_AddStream(This,newStream);

1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
  }
  else
  {
    return STG_E_INSUFFICIENTMEMORY;
  }

  return S_OK;
}

/************************************************************************
 * Storage32BaseImpl_SetClass (IStorage)
 *
1083
 * This method will write the specified CLSID in the property of this
1084 1085 1086 1087
 * storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
1088
static HRESULT WINAPI StorageBaseImpl_SetClass(
1089
  IStorage*        iface,
1090
  REFCLSID         clsid) /* [in] */
1091
{
1092
  StorageBaseImpl *This = (StorageBaseImpl *)iface;
1093 1094
  HRESULT hRes = E_FAIL;
  StgProperty curProperty;
1095
  BOOL success;
1096

1097
  TRACE("(%p, %p)\n", iface, clsid);
1098

1099
  success = StorageImpl_ReadProperty(This->ancestorStorage,
1100 1101 1102 1103 1104 1105
                                       This->rootPropertySetIndex,
                                       &curProperty);
  if (success)
  {
    curProperty.propertyUniqueID = *clsid;

1106
    success =  StorageImpl_WriteProperty(This->ancestorStorage,
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
                                           This->rootPropertySetIndex,
                                           &curProperty);
    if (success)
      hRes = S_OK;
  }

  return hRes;
}

/************************************************************************
** Storage32Impl implementation
*/
1119

1120
/************************************************************************
1121
 * Storage32Impl_CreateStorage (IStorage)
1122 1123 1124 1125 1126
 *
 * This method will create the storage object within the provided storage.
 *
 * See Windows documentation for more details on IStorage methods.
 */
1127
static HRESULT WINAPI StorageImpl_CreateStorage(
1128
  IStorage*      iface,
1129 1130 1131 1132 1133
  const OLECHAR  *pwcsName, /* [string][in] */
  DWORD            grfMode,   /* [in] */
  DWORD            reserved1, /* [in] */
  DWORD            reserved2, /* [in] */
  IStorage       **ppstg)   /* [out] */
1134
{
1135
  StorageImpl* const This=(StorageImpl*)iface;
1136

1137 1138 1139 1140 1141 1142 1143
  IEnumSTATSTGImpl *propertyEnumeration;
  StgProperty      currentProperty;
  StgProperty      newProperty;
  ULONG            foundPropertyIndex;
  ULONG            newPropertyIndex;
  HRESULT          hr;

1144
  TRACE("(%p, %s, %x, %d, %d, %p)\n",
1145
	iface, debugstr_w(pwcsName), grfMode,
1146
	reserved1, reserved2, ppstg);
1147

1148 1149 1150 1151 1152 1153 1154 1155 1156
  /*
   * Validate parameters
   */
  if (ppstg == 0)
    return STG_E_INVALIDPOINTER;

  if (pwcsName == 0)
    return STG_E_INVALIDNAME;

1157 1158 1159 1160 1161
  /*
   * Initialize the out parameter
   */
  *ppstg = NULL;

1162 1163 1164 1165 1166
  /*
   * Validate the STGM flags
   */
  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode & STGM_DELETEONRELEASE) )
1167
  {
1168
    WARN("bad grfMode: 0x%x\n", grfMode);
1169
    return STG_E_INVALIDFLAG;
1170
  }
1171

1172 1173 1174
  /*
   * Check that we're compatible with the parent's storage mode
   */
1175
  if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1176 1177
  {
    WARN("access denied\n");
1178
    return STG_E_ACCESSDENIED;
1179
  }
1180 1181 1182 1183

  /*
   * Create a property enumeration and search the properties
   */
1184 1185
  propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
                                                    This->base.rootPropertySetIndex);
1186 1187 1188 1189 1190 1191 1192 1193 1194

  foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
                                                     pwcsName,
                                                     &currentProperty);
  IEnumSTATSTGImpl_Destroy(propertyEnumeration);

  if (foundPropertyIndex != PROPERTY_NULL)
  {
    /*
1195
     * An element with this name already exists
1196
     */
1197
    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1198 1199
      IStorage_DestroyElement(iface, pwcsName);
    else
1200 1201
    {
      WARN("file already exists\n");
1202
      return STG_E_FILEALREADYEXISTS;
1203
    }
1204
  }
1205 1206 1207 1208 1209
  else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
  {
    WARN("read-only storage\n");
    return STG_E_ACCESSDENIED;
  }
1210

1211 1212
  /*
   * memset the empty property
1213 1214 1215
   */
  memset(&newProperty, 0, sizeof(StgProperty));

1216
  newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1217 1218

  if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1219 1220
  {
    FIXME("name too long\n");
1221
    return STG_E_INVALIDNAME;
1222
  }
1223

1224
  strcpyW(newProperty.name, pwcsName);
1225 1226 1227

  newProperty.propertyType  = PROPTYPE_STORAGE;
  newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1228 1229
  newProperty.size.u.LowPart  = 0;
  newProperty.size.u.HighPart = 0;
1230 1231 1232 1233 1234

  newProperty.previousProperty = PROPERTY_NULL;
  newProperty.nextProperty     = PROPERTY_NULL;
  newProperty.dirProperty      = PROPERTY_NULL;

1235
  /* call CoFileTime to get the current time
1236 1237 1238 1239 1240 1241 1242 1243
  newProperty.timeStampS1
  newProperty.timeStampD1
  newProperty.timeStampS2
  newProperty.timeStampD2
  */

  /*  newStorageProperty.propertyUniqueID */

1244
  /*
1245 1246
   * Obtain a free property in the property chain
   */
1247
  newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1248 1249 1250

  /*
   * Save the new property into the new property spot
1251
   */
1252
  StorageImpl_WriteProperty(
1253
    This->base.ancestorStorage,
1254
    newPropertyIndex,
1255 1256
    &newProperty);

1257
  /*
1258 1259 1260 1261
   * Find a spot in the property chain for our newly created property.
   */
  updatePropertyChain(
    This,
1262
    newPropertyIndex,
1263 1264
    newProperty);

1265
  /*
1266 1267
   * Open it to get a pointer to return.
   */
1268
  hr = IStorage_OpenStorage(
1269
         iface,
Eric Pouech's avatar
Eric Pouech committed
1270
         (const OLECHAR*)pwcsName,
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
         0,
         grfMode,
         0,
         0,
         ppstg);

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

1282

1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
  return S_OK;
}


/***************************************************************************
 *
 * Internal Method
 *
 * Get a free property or create a new one.
 */
static ULONG getFreeProperty(
1294
  StorageImpl *storage)
1295 1296 1297
{
  ULONG       currentPropertyIndex = 0;
  ULONG       newPropertyIndex     = PROPERTY_NULL;
1298
  BOOL      readSuccessful        = TRUE;
1299 1300 1301 1302 1303 1304 1305
  StgProperty currentProperty;

  do
  {
    /*
     * Start by reading the root property
     */
1306
    readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1307 1308
                                               currentPropertyIndex,
                                               &currentProperty);
1309
    if (readSuccessful)
1310 1311 1312
    {
      if (currentProperty.sizeOfNameString == 0)
      {
1313
        /*
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
         * The property existis and is available, we found it.
         */
        newPropertyIndex = currentPropertyIndex;
      }
    }
    else
    {
      /*
       * We exhausted the property list, we will create more space below
       */
      newPropertyIndex = currentPropertyIndex;
    }
    currentPropertyIndex++;

  } while (newPropertyIndex == PROPERTY_NULL);

1330 1331
  /*
   * grow the property chain
1332
   */
1333
  if (! readSuccessful)
1334 1335 1336 1337 1338 1339 1340
  {
    StgProperty    emptyProperty;
    ULARGE_INTEGER newSize;
    ULONG          propertyIndex;
    ULONG          lastProperty  = 0;
    ULONG          blockCount    = 0;

1341 1342
    /*
     * obtain the new count of property blocks
1343 1344
     */
    blockCount = BlockChainStream_GetCount(
1345
                   storage->base.ancestorStorage->rootBlockChain)+1;
1346

1347 1348
    /*
     * initialize the size used by the property stream
1349
     */
1350 1351
    newSize.u.HighPart = 0;
    newSize.u.LowPart  = storage->bigBlockSize * blockCount;
1352

1353 1354
    /*
     * add a property block to the property chain
1355
     */
1356
    BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1357

1358 1359
    /*
     * memset the empty property in order to initialize the unused newly
1360 1361 1362 1363
     * created property
     */
    memset(&emptyProperty, 0, sizeof(StgProperty));

1364
    /*
1365 1366
     * initialize them
     */
1367 1368
    lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;

1369 1370 1371 1372 1373
    for(
      propertyIndex = newPropertyIndex;
      propertyIndex < lastProperty;
      propertyIndex++)
    {
1374
      StorageImpl_WriteProperty(
1375
        storage->base.ancestorStorage,
1376
        propertyIndex,
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
        &emptyProperty);
    }
  }

  return newPropertyIndex;
}

/****************************************************************************
 *
 * Internal Method
 *
1388
 * Case insensitive comparaison of StgProperty.name by first considering
1389 1390 1391 1392 1393 1394 1395
 * their size.
 *
 * Returns <0 when newPrpoerty < currentProperty
 *         >0 when newPrpoerty > currentProperty
 *          0 when newPrpoerty == currentProperty
 */
static LONG propertyNameCmp(
Eric Pouech's avatar
Eric Pouech committed
1396 1397
    const OLECHAR *newProperty,
    const OLECHAR *currentProperty)
1398
{
1399
  LONG diff      = lstrlenW(newProperty) - lstrlenW(currentProperty);
1400

1401
  if (diff == 0)
1402
  {
1403
    /*
Francois Gouget's avatar
Francois Gouget committed
1404
     * We compare the string themselves only when they are of the same length
1405
     */
1406
    diff = lstrcmpiW( newProperty, currentProperty);
1407 1408
  }

1409
  return diff;
1410 1411 1412 1413 1414 1415 1416 1417 1418
}

/****************************************************************************
 *
 * Internal Method
 *
 * Properly link this new element in the property chain.
 */
static void updatePropertyChain(
1419
  StorageImpl *storage,
1420
  ULONG         newPropertyIndex,
1421
  StgProperty   newProperty)
1422 1423 1424 1425 1426 1427
{
  StgProperty currentProperty;

  /*
   * Read the root property
   */
1428 1429
  StorageImpl_ReadProperty(storage->base.ancestorStorage,
                             storage->base.rootPropertySetIndex,
1430 1431 1432 1433
                             &currentProperty);

  if (currentProperty.dirProperty != PROPERTY_NULL)
  {
1434
    /*
1435 1436 1437
     * The root storage contains some element, therefore, start the research
     * for the appropriate location.
     */
1438
    BOOL found = 0;
1439 1440 1441 1442 1443 1444 1445 1446
    ULONG  current, next, previous, currentPropertyId;

    /*
     * Keep the StgProperty sequence number of the storage first property
     */
    currentPropertyId = currentProperty.dirProperty;

    /*
1447
     * Read
1448
     */
1449
    StorageImpl_ReadProperty(storage->base.ancestorStorage,
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
                               currentProperty.dirProperty,
                               &currentProperty);

    previous = currentProperty.previousProperty;
    next     = currentProperty.nextProperty;
    current  = currentPropertyId;

    while (found == 0)
    {
      LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1460

1461 1462 1463 1464
      if (diff < 0)
      {
        if (previous != PROPERTY_NULL)
        {
1465
          StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466 1467 1468 1469 1470 1471 1472
                                     previous,
                                     &currentProperty);
          current = previous;
        }
        else
        {
          currentProperty.previousProperty = newPropertyIndex;
1473
          StorageImpl_WriteProperty(storage->base.ancestorStorage,
1474 1475 1476 1477 1478
                                      current,
                                      &currentProperty);
          found = 1;
        }
      }
1479
      else if (diff > 0)
1480 1481 1482
      {
        if (next != PROPERTY_NULL)
        {
1483
          StorageImpl_ReadProperty(storage->base.ancestorStorage,
1484 1485 1486 1487 1488 1489 1490
                                     next,
                                     &currentProperty);
          current = next;
        }
        else
        {
          currentProperty.nextProperty = newPropertyIndex;
1491
          StorageImpl_WriteProperty(storage->base.ancestorStorage,
1492 1493 1494 1495 1496
                                      current,
                                      &currentProperty);
          found = 1;
        }
      }
1497 1498 1499 1500 1501 1502 1503 1504
      else
      {
	/*
	 * Trying to insert an item with the same name in the
	 * subtree structure.
	 */
	assert(FALSE);
      }
1505 1506 1507 1508 1509 1510 1511

      previous = currentProperty.previousProperty;
      next     = currentProperty.nextProperty;
    }
  }
  else
  {
1512
    /*
1513
     * The root storage is empty, link the new property to its dir property
1514 1515
     */
    currentProperty.dirProperty = newPropertyIndex;
1516 1517
    StorageImpl_WriteProperty(storage->base.ancestorStorage,
                                storage->base.rootPropertySetIndex,
1518 1519 1520 1521
                                &currentProperty);
  }
}

1522

1523 1524 1525
/*************************************************************************
 * CopyTo (IStorage)
 */
1526
static HRESULT WINAPI StorageImpl_CopyTo(
1527
  IStorage*   iface,
1528 1529 1530 1531
  DWORD       ciidExclude,  /* [in] */
  const IID*  rgiidExclude, /* [size_is][unique][in] */
  SNB         snbExclude,   /* [unique][in] */
  IStorage*   pstgDest)     /* [unique][in] */
1532
{
1533 1534 1535 1536 1537 1538 1539
  IEnumSTATSTG *elements     = 0;
  STATSTG      curElement, strStat;
  HRESULT      hr;
  IStorage     *pstgTmp, *pstgChild;
  IStream      *pstrTmp, *pstrChild;

  if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1540
    FIXME("Exclude option not implemented\n");
1541

1542
  TRACE("(%p, %d, %p, %p, %p)\n",
1543
	iface, ciidExclude, rgiidExclude,
1544
	snbExclude, pstgDest);
1545 1546 1547 1548 1549 1550 1551

  /*
   * Perform a sanity check
   */
  if ( pstgDest == 0 )
    return STG_E_INVALIDPOINTER;

1552
  /*
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
   * Enumerate the elements
   */
  hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );

  if ( hr != S_OK )
    return hr;

  /*
   * set the class ID
   */
1563
  IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1564
  IStorage_SetClass( pstgDest, &curElement.clsid );
1565

1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583
  do
  {
    /*
     * Obtain the next element
     */
    hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );

    if ( hr == S_FALSE )
    {
      hr = S_OK;   /* done, every element has been copied */
      break;
    }

    if (curElement.type == STGTY_STORAGE)
    {
      /*
       * open child source storage
       */
1584 1585 1586
      hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
				 STGM_READ|STGM_SHARE_EXCLUSIVE,
				 NULL, 0, &pstgChild );
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600

      if (hr != S_OK)
        break;

      /*
       * Check if destination storage is not a child of the source
       * storage, which will cause an infinite loop
       */
      if (pstgChild == pstgDest)
      {
	IEnumSTATSTG_Release(elements);

	return STG_E_ACCESSDENIED;
      }
1601

1602 1603 1604 1605
      /*
       * create a new storage in destination storage
       */
      hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1606 1607
                                   STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
				   0, 0,
1608 1609 1610 1611 1612 1613 1614 1615 1616 1617
                                   &pstgTmp );
      /*
       * if it already exist, don't create a new one use this one
       */
      if (hr == STG_E_FILEALREADYEXISTS)
      {
        hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
                                   STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                   NULL, 0, &pstgTmp );
      }
1618

1619 1620 1621
      if (hr != S_OK)
        break;

1622

1623 1624 1625 1626 1627
      /*
       * do the copy recursively
       */
      hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
                               snbExclude, pstgTmp );
1628

1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
      IStorage_Release( pstgTmp );
      IStorage_Release( pstgChild );
    }
    else if (curElement.type == STGTY_STREAM)
    {
      /*
       * create a new stream in destination storage. If the stream already
       * exist, it will be deleted and a new one will be created.
       */
      hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
                                  STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
                                  0, 0, &pstrTmp );

      if (hr != S_OK)
        break;

      /*
       * open child stream storage
       */
1648 1649 1650
      hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
				STGM_READ|STGM_SHARE_EXCLUSIVE,
				0, &pstrChild );
1651 1652 1653 1654 1655

      if (hr != S_OK)
        break;

      /*
1656
       * Get the size of the source stream
1657 1658
       */
      IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1659 1660 1661 1662 1663

      /*
       * Set the size of the destination stream.
       */
      IStream_SetSize(pstrTmp, strStat.cbSize);
1664

1665 1666 1667 1668 1669
      /*
       * do the copy
       */
      hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
                           NULL, NULL );
1670

1671 1672 1673 1674 1675
      IStream_Release( pstrTmp );
      IStream_Release( pstrChild );
    }
    else
    {
1676
      WARN("unknown element type: %d\n", curElement.type);
1677 1678 1679 1680 1681 1682 1683 1684
    }

  } while (hr == S_OK);

  /*
   * Clean-up
   */
  IEnumSTATSTG_Release(elements);
1685

1686
  return hr;
1687
}
1688

1689 1690 1691
/*************************************************************************
 * MoveElementTo (IStorage)
 */
1692
static HRESULT WINAPI StorageImpl_MoveElementTo(
1693
  IStorage*     iface,
1694 1695 1696 1697
  const OLECHAR *pwcsName,   /* [string][in] */
  IStorage      *pstgDest,   /* [unique][in] */
  const OLECHAR *pwcsNewName,/* [string][in] */
  DWORD           grfFlags)    /* [in] */
1698
{
1699
  FIXME("(%p %s %p %s %u): stub\n", iface,
1700 1701
         debugstr_w(pwcsName), pstgDest,
         debugstr_w(pwcsNewName), grfFlags);
1702 1703
  return E_NOTIMPL;
}
1704

1705 1706
/*************************************************************************
 * Commit (IStorage)
1707 1708 1709 1710 1711 1712 1713
 *
 * Ensures that any changes made to a storage object open in transacted mode
 * are reflected in the parent storage
 *
 * NOTES
 *  Wine doesn't implement transacted mode, which seems to be a basic
 *  optimization, so we can ignore this stub for now.
1714
 */
1715
static HRESULT WINAPI StorageImpl_Commit(
1716
  IStorage*   iface,
1717
  DWORD         grfCommitFlags)/* [in] */
1718
{
1719
  FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1720
  return S_OK;
1721
}
1722

1723 1724
/*************************************************************************
 * Revert (IStorage)
1725 1726
 *
 * Discard all changes that have been made since the last commit operation
1727
 */
1728
static HRESULT WINAPI StorageImpl_Revert(
1729
  IStorage* iface)
1730
{
1731
  FIXME("(%p): stub\n", iface);
1732 1733 1734 1735
  return E_NOTIMPL;
}

/*************************************************************************
1736
 * DestroyElement (IStorage)
1737
 *
1738 1739
 * Strategy: This implementation is built this way for simplicity not for speed.
 *          I always delete the topmost element of the enumeration and adjust
1740 1741
 *          the deleted element pointer all the time.  This takes longer to
 *          do but allow to reinvoke DestroyElement whenever we encounter a
1742 1743
 *          storage object.  The optimisation resides in the usage of another
 *          enumeration strategy that would give all the leaves of a storage
1744 1745
 *          first. (postfix order)
 */
1746
static HRESULT WINAPI StorageImpl_DestroyElement(
1747
  IStorage*     iface,
1748
  const OLECHAR *pwcsName)/* [string][in] */
1749
{
1750
  StorageImpl* const This=(StorageImpl*)iface;
1751

1752 1753
  IEnumSTATSTGImpl* propertyEnumeration;
  HRESULT           hr = S_OK;
1754
  BOOL            res;
1755 1756 1757 1758
  StgProperty       propertyToDelete;
  StgProperty       parentProperty;
  ULONG             foundPropertyIndexToDelete;
  ULONG             typeOfRelation;
1759
  ULONG             parentPropertyId = 0;
1760

1761
  TRACE("(%p, %s)\n",
1762 1763
	iface, debugstr_w(pwcsName));

1764 1765 1766
  /*
   * Perform a sanity check on the parameters.
   */
1767
  if (pwcsName==NULL)
1768
    return STG_E_INVALIDPOINTER;
1769

1770 1771 1772 1773
  /*
   * Create a property enumeration to search the property with the given name
   */
  propertyEnumeration = IEnumSTATSTGImpl_Construct(
1774 1775
    This->base.ancestorStorage,
    This->base.rootPropertySetIndex);
1776

1777 1778 1779 1780 1781 1782 1783
  foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
    propertyEnumeration,
    pwcsName,
    &propertyToDelete);

  IEnumSTATSTGImpl_Destroy(propertyEnumeration);

1784
  if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1785 1786 1787 1788
  {
    return STG_E_FILENOTFOUND;
  }

1789 1790 1791
  /*
   * Find the parent property of the property to delete (the one that
   * link to it).  If This->dirProperty == foundPropertyIndexToDelete,
1792
   * the parent is This. Otherwise, the parent is one of its sibling...
1793 1794
   */

1795
  /*
1796 1797
   * First, read This's StgProperty..
   */
1798
  res = StorageImpl_ReadProperty(
1799 1800
          This->base.ancestorStorage,
          This->base.rootPropertySetIndex,
1801 1802
          &parentProperty);

1803
  assert(res);
1804

1805
  /*
1806 1807 1808 1809 1810
   * Second, check to see if by any chance the actual storage (This) is not
   * the parent of the property to delete... We never know...
   */
  if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
  {
1811
    /*
1812 1813 1814
     * Set data as it would have been done in the else part...
     */
    typeOfRelation   = PROPERTY_RELATION_DIR;
1815
    parentPropertyId = This->base.rootPropertySetIndex;
1816
  }
1817 1818
  else
  {
1819
    /*
1820
     * Create a property enumeration to search the parent properties, and
1821 1822 1823 1824 1825
     * delete it once done.
     */
    IEnumSTATSTGImpl* propertyEnumeration2;

    propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1826 1827
      This->base.ancestorStorage,
      This->base.rootPropertySetIndex);
1828

1829 1830 1831 1832 1833 1834 1835 1836 1837
    typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
      propertyEnumeration2,
      foundPropertyIndexToDelete,
      &parentProperty,
      &parentPropertyId);

    IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
  }

1838
  if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1839 1840
  {
    hr = deleteStorageProperty(
1841
           This,
1842 1843
           foundPropertyIndexToDelete,
           propertyToDelete);
1844
  }
1845 1846 1847
  else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
  {
    hr = deleteStreamProperty(
1848
           This,
1849 1850 1851 1852
           foundPropertyIndexToDelete,
           propertyToDelete);
  }

1853
  if (hr!=S_OK)
1854 1855 1856 1857 1858 1859 1860
    return hr;

  /*
   * Adjust the property chain
   */
  hr = adjustPropertyChain(
        This,
1861
        propertyToDelete,
1862 1863 1864 1865 1866 1867 1868 1869
        parentProperty,
        parentPropertyId,
        typeOfRelation);

  return hr;
}


1870 1871 1872 1873 1874 1875
/************************************************************************
 * StorageImpl_Stat (IStorage)
 *
 * This method will retrieve information about this storage object.
 *
 * See Windows documentation for more details on IStorage methods.
1876
 */
1877
static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
                                 STATSTG*  pstatstg,     /* [out] */
                                 DWORD     grfStatFlag)  /* [in] */
{
  StorageImpl* const This = (StorageImpl*)iface;
  HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );

  if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
  {
      CoTaskMemFree(pstatstg->pwcsName);
      pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
      strcpyW(pstatstg->pwcsName, This->pwcsName);
  }

  return result;
}

1894
/******************************************************************************
1895
 * Internal stream list handlers
1896 1897
 */

1898
static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
{
  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));
}

1910
static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1911 1912 1913 1914 1915 1916 1917
{
  struct list *cur, *cur2;
  StgStreamImpl *strm=NULL;

  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
    strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
    TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1918
    strm->parentStorage = NULL;
1919 1920 1921
    list_remove(cur);
  }
}
1922 1923


1924 1925 1926 1927 1928 1929 1930 1931
/*********************************************************************
 *
 * Internal Method
 *
 * Perform the deletion of a complete storage node
 *
 */
static HRESULT deleteStorageProperty(
1932
  StorageImpl *parentStorage,
1933 1934
  ULONG        indexOfPropertyToDelete,
  StgProperty  propertyToDelete)
1935 1936
{
  IEnumSTATSTG *elements     = 0;
1937
  IStorage   *childStorage = 0;
1938 1939 1940 1941 1942 1943 1944
  STATSTG      currentElement;
  HRESULT      hr;
  HRESULT      destroyHr = S_OK;

  /*
   * Open the storage and enumerate it
   */
1945 1946
  hr = StorageBaseImpl_OpenStorage(
        (IStorage*)parentStorage,
1947
        propertyToDelete.name,
1948
        0,
1949
        STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1950 1951 1952 1953 1954 1955 1956 1957 1958
        0,
        0,
        &childStorage);

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

1959
  /*
1960 1961
   * Enumerate the elements
   */
1962
  IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1963 1964 1965 1966 1967 1968 1969 1970 1971

  do
  {
    /*
     * Obtain the next element
     */
    hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
    if (hr==S_OK)
    {
1972
      destroyHr = StorageImpl_DestroyElement(
1973
                    (IStorage*)childStorage,
1974
                    (OLECHAR*)currentElement.pwcsName);
1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986

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

1987
  /*
1988
   * Invalidate the property by zeroing its name member.
1989 1990 1991
   */
  propertyToDelete.sizeOfNameString = 0;

1992
  StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1993 1994 1995
                            indexOfPropertyToDelete,
                            &propertyToDelete);

1996
  IStorage_Release(childStorage);
1997
  IEnumSTATSTG_Release(elements);
1998

1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
  return destroyHr;
}

/*********************************************************************
 *
 * Internal Method
 *
 * Perform the deletion of a stream node
 *
 */
static HRESULT deleteStreamProperty(
2010
  StorageImpl *parentStorage,
2011 2012 2013
  ULONG         indexOfPropertyToDelete,
  StgProperty   propertyToDelete)
{
2014
  IStream      *pis;
2015 2016 2017
  HRESULT        hr;
  ULARGE_INTEGER size;

2018 2019
  size.u.HighPart = 0;
  size.u.LowPart = 0;
2020

2021 2022 2023
  hr = StorageBaseImpl_OpenStream(
         (IStorage*)parentStorage,
         (OLECHAR*)propertyToDelete.name,
2024
         NULL,
2025
         STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2026 2027
         0,
         &pis);
2028

2029 2030 2031 2032 2033
  if (hr!=S_OK)
  {
    return(hr);
  }

2034 2035 2036 2037
  /*
   * Zap the stream
   */
  hr = IStream_SetSize(pis, size);
2038 2039 2040 2041 2042 2043

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

2044 2045 2046 2047 2048
  /*
   * Release the stream object.
   */
  IStream_Release(pis);

2049
  /*
2050
   * Invalidate the property by zeroing its name member.
2051 2052 2053
   */
  propertyToDelete.sizeOfNameString = 0;

2054
  /*
2055 2056 2057
   * Here we should re-read the property so we get the updated pointer
   * but since we are here to zap it, I don't do it...
   */
2058
  StorageImpl_WriteProperty(
2059
    parentStorage->base.ancestorStorage,
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073
    indexOfPropertyToDelete,
    &propertyToDelete);

  return S_OK;
}

/*********************************************************************
 *
 * Internal Method
 *
 * Finds a placeholder for the StgProperty within the Storage
 *
 */
static HRESULT findPlaceholder(
2074
  StorageImpl *storage,
2075 2076
  ULONG         propertyIndexToStore,
  ULONG         storePropertyIndex,
2077
  INT         typeOfRelation)
2078 2079 2080
{
  StgProperty storeProperty;
  HRESULT     hr = S_OK;
2081
  BOOL      res = TRUE;
2082 2083 2084 2085

  /*
   * Read the storage property
   */
2086
  res = StorageImpl_ReadProperty(
2087
          storage->base.ancestorStorage,
2088
          storePropertyIndex,
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101
          &storeProperty);

  if(! res)
  {
    return E_FAIL;
  }

  if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
  {
    if (storeProperty.previousProperty != PROPERTY_NULL)
    {
      return findPlaceholder(
               storage,
2102
               propertyIndexToStore,
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116
               storeProperty.previousProperty,
               typeOfRelation);
    }
    else
    {
      storeProperty.previousProperty = propertyIndexToStore;
    }
  }
  else if (typeOfRelation == PROPERTY_RELATION_NEXT)
  {
    if (storeProperty.nextProperty != PROPERTY_NULL)
    {
      return findPlaceholder(
               storage,
2117
               propertyIndexToStore,
2118 2119 2120 2121 2122 2123 2124
               storeProperty.nextProperty,
               typeOfRelation);
    }
    else
    {
      storeProperty.nextProperty = propertyIndexToStore;
    }
2125
  }
2126 2127 2128 2129 2130 2131
  else if (typeOfRelation == PROPERTY_RELATION_DIR)
  {
    if (storeProperty.dirProperty != PROPERTY_NULL)
    {
      return findPlaceholder(
               storage,
2132
               propertyIndexToStore,
2133 2134 2135 2136 2137 2138 2139 2140 2141
               storeProperty.dirProperty,
               typeOfRelation);
    }
    else
    {
      storeProperty.dirProperty = propertyIndexToStore;
    }
  }

2142
  hr = StorageImpl_WriteProperty(
2143
         storage->base.ancestorStorage,
2144
         storePropertyIndex,
2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158
         &storeProperty);

  if(! hr)
  {
    return E_FAIL;
  }

  return S_OK;
}

/*************************************************************************
 *
 * Internal Method
 *
2159
 * This method takes the previous and the next property link of a property
2160 2161
 * to be deleted and find them a place in the Storage.
 */
2162
static HRESULT adjustPropertyChain(
2163
  StorageImpl *This,
2164 2165 2166
  StgProperty   propertyToDelete,
  StgProperty   parentProperty,
  ULONG         parentPropertyId,
2167
  INT         typeOfRelation)
2168 2169
{
  ULONG   newLinkProperty        = PROPERTY_NULL;
2170
  BOOL  needToFindAPlaceholder = FALSE;
2171 2172
  ULONG   storeNode              = PROPERTY_NULL;
  ULONG   toStoreNode            = PROPERTY_NULL;
2173
  INT   relationType           = 0;
2174
  HRESULT hr                     = S_OK;
2175
  BOOL  res                    = TRUE;
2176 2177

  if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2178
  {
2179
    if (propertyToDelete.previousProperty != PROPERTY_NULL)
2180
    {
2181
      /*
2182 2183 2184 2185
       * Set the parent previous to the property to delete previous
       */
      newLinkProperty = propertyToDelete.previousProperty;

2186
      if (propertyToDelete.nextProperty != PROPERTY_NULL)
2187 2188
      {
        /*
2189
         * We also need to find a storage for the other link, setup variables
2190
         * to do this at the end...
2191
         */
2192 2193 2194 2195 2196
        needToFindAPlaceholder = TRUE;
        storeNode              = propertyToDelete.previousProperty;
        toStoreNode            = propertyToDelete.nextProperty;
        relationType           = PROPERTY_RELATION_NEXT;
      }
2197 2198
    }
    else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2199
    {
2200
      /*
2201 2202 2203 2204
       * Set the parent previous to the property to delete next
       */
      newLinkProperty = propertyToDelete.nextProperty;
    }
2205 2206

    /*
2207
     * Link it for real...
2208
     */
2209
    parentProperty.previousProperty = newLinkProperty;
2210 2211 2212

  }
  else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2213
  {
2214
    if (propertyToDelete.previousProperty != PROPERTY_NULL)
2215
    {
2216
      /*
2217 2218 2219
       * Set the parent next to the property to delete next previous
       */
      newLinkProperty = propertyToDelete.previousProperty;
2220 2221

      if (propertyToDelete.nextProperty != PROPERTY_NULL)
2222 2223
      {
        /*
2224
         * We also need to find a storage for the other link, setup variables
2225
         * to do this at the end...
2226
         */
2227 2228 2229 2230 2231
        needToFindAPlaceholder = TRUE;
        storeNode              = propertyToDelete.previousProperty;
        toStoreNode            = propertyToDelete.nextProperty;
        relationType           = PROPERTY_RELATION_NEXT;
      }
2232 2233
    }
    else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2234
    {
2235
      /*
2236 2237 2238 2239 2240
       * Set the parent next to the property to delete next
       */
      newLinkProperty = propertyToDelete.nextProperty;
    }

2241
    /*
2242
     * Link it for real...
2243
     */
2244
    parentProperty.nextProperty = newLinkProperty;
2245
  }
2246 2247
  else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
  {
2248
    if (propertyToDelete.previousProperty != PROPERTY_NULL)
2249
    {
2250
      /*
2251 2252 2253 2254
       * Set the parent dir to the property to delete previous
       */
      newLinkProperty = propertyToDelete.previousProperty;

2255
      if (propertyToDelete.nextProperty != PROPERTY_NULL)
2256 2257
      {
        /*
2258
         * We also need to find a storage for the other link, setup variables
2259
         * to do this at the end...
2260
         */
2261 2262 2263 2264 2265
        needToFindAPlaceholder = TRUE;
        storeNode              = propertyToDelete.previousProperty;
        toStoreNode            = propertyToDelete.nextProperty;
        relationType           = PROPERTY_RELATION_NEXT;
      }
2266 2267
    }
    else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2268
    {
2269
      /*
2270 2271 2272 2273 2274
       * Set the parent dir to the property to delete next
       */
      newLinkProperty = propertyToDelete.nextProperty;
    }

2275
    /*
2276
     * Link it for real...
2277
     */
2278 2279 2280
    parentProperty.dirProperty = newLinkProperty;
  }

2281 2282
  /*
   * Write back the parent property
2283
   */
2284
  res = StorageImpl_WriteProperty(
2285
          This->base.ancestorStorage,
2286 2287 2288 2289 2290 2291 2292 2293
          parentPropertyId,
          &parentProperty);
  if(! res)
  {
    return E_FAIL;
  }

  /*
2294
   * If a placeholder is required for the other link, then, find one and
2295 2296
   * get out of here...
   */
2297
  if (needToFindAPlaceholder)
2298 2299
  {
    hr = findPlaceholder(
2300 2301
           This,
           toStoreNode,
2302 2303 2304 2305 2306 2307 2308 2309
           storeNode,
           relationType);
  }

  return hr;
}


2310 2311 2312
/******************************************************************************
 * SetElementTimes (IStorage)
 */
2313
static HRESULT WINAPI StorageImpl_SetElementTimes(
2314
  IStorage*     iface,
2315 2316 2317 2318
  const OLECHAR *pwcsName,/* [string][in] */
  const FILETIME  *pctime,  /* [in] */
  const FILETIME  *patime,  /* [in] */
  const FILETIME  *pmtime)  /* [in] */
2319
{
2320 2321
  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
  return S_OK;
2322 2323
}

2324 2325 2326
/******************************************************************************
 * SetStateBits (IStorage)
 */
2327
static HRESULT WINAPI StorageImpl_SetStateBits(
2328
  IStorage*   iface,
2329 2330
  DWORD         grfStateBits,/* [in] */
  DWORD         grfMask)     /* [in] */
2331
{
2332
  FIXME("not implemented!\n");
2333 2334 2335
  return E_NOTIMPL;
}

2336 2337 2338
/*
 * Virtual function table for the IStorage32Impl class.
 */
2339
static const IStorageVtbl Storage32Impl_Vtbl =
2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
    StorageImpl_CreateStorage,
    StorageBaseImpl_OpenStorage,
    StorageImpl_CopyTo,
    StorageImpl_MoveElementTo,
    StorageImpl_Commit,
    StorageImpl_Revert,
    StorageBaseImpl_EnumElements,
    StorageImpl_DestroyElement,
    StorageBaseImpl_RenameElement,
    StorageImpl_SetElementTimes,
    StorageBaseImpl_SetClass,
    StorageImpl_SetStateBits,
    StorageImpl_Stat
};

2361
static HRESULT StorageImpl_Construct(
2362 2363
  StorageImpl* This,
  HANDLE       hFile,
2364
  LPCOLESTR    pwcsName,
2365 2366
  ILockBytes*  pLkbyt,
  DWORD        openFlags,
2367 2368
  BOOL         fileBased,
  BOOL         fileCreate)
2369 2370 2371
{
  HRESULT     hr = S_OK;
  StgProperty currentProperty;
2372
  BOOL      readSuccessful;
2373
  ULONG       currentPropertyIndex;
2374

2375 2376 2377
  if ( FAILED( validateSTGM(openFlags) ))
    return STG_E_INVALIDFLAG;

2378
  memset(This, 0, sizeof(StorageImpl));
2379

2380 2381 2382 2383 2384 2385
  /*
   * Initialize stream list
   */

  list_init(&This->base.strmHead);

2386
  /*
Alexandre Julliard's avatar
Alexandre Julliard committed
2387
   * Initialize the virtual function table.
2388
   */
2389 2390 2391
  This->base.lpVtbl = &Storage32Impl_Vtbl;
  This->base.pssVtbl = &IPropertySetStorage_Vtbl;
  This->base.v_destructor = &StorageImpl_Destroy;
2392
  This->base.openFlags = (openFlags & ~STGM_CREATE);
2393

2394
  /*
Alexandre Julliard's avatar
Alexandre Julliard committed
2395
   * This is the top-level storage so initialize the ancestor pointer
2396 2397
   * to this.
   */
2398
  This->base.ancestorStorage = This;
2399

2400 2401 2402 2403
  /*
   * Initialize the physical support of the storage.
   */
  This->hFile = hFile;
2404

2405 2406 2407 2408
  /*
   * Store copy of file path.
   */
  if(pwcsName) {
2409
      This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2410 2411 2412
                                (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
      if (!This->pwcsName)
         return STG_E_INSUFFICIENTMEMORY;
2413
      strcpyW(This->pwcsName, pwcsName);
2414 2415
  }

2416 2417 2418 2419 2420
  /*
   * Initialize the big block cache.
   */
  This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
  This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2421
  This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2422
                                                pLkbyt,
2423
                                                openFlags,
2424 2425
                                                This->bigBlockSize,
                                                fileBased);
2426 2427 2428

  if (This->bigBlockFile == 0)
    return E_FAIL;
2429

2430
  if (fileCreate)
2431 2432 2433 2434 2435 2436 2437 2438 2439 2440
  {
    ULARGE_INTEGER size;
    BYTE* bigBlockBuffer;

    /*
     * Initialize all header variables:
     * - The big block depot consists of one block and it is at block 0
     * - The properties start at block 1
     * - There is no small block depot
     */
2441 2442
    memset( This->bigBlockDepotStart,
            BLOCK_UNUSED,
2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453
            sizeof(This->bigBlockDepotStart));

    This->bigBlockDepotCount    = 1;
    This->bigBlockDepotStart[0] = 0;
    This->rootStartBlock        = 1;
    This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
    This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
    This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
    This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
    This->extBigBlockDepotCount = 0;

2454
    StorageImpl_SaveFileHeader(This);
2455 2456 2457 2458

    /*
     * Add one block for the big block depot and one block for the properties
     */
2459 2460
    size.u.HighPart = 0;
    size.u.LowPart  = This->bigBlockSize * 3;
2461 2462 2463 2464 2465
    BIGBLOCKFILE_SetSize(This->bigBlockFile, size);

    /*
     * Initialize the big block depot
     */
2466
    bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2467 2468 2469
    memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
    StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
    StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2470
    StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2471 2472
  }
  else
2473 2474 2475 2476
  {
    /*
     * Load the header for the file.
     */
2477 2478 2479 2480 2481 2482 2483 2484
    hr = StorageImpl_LoadFileHeader(This);

    if (FAILED(hr))
    {
      BIGBLOCKFILE_Destructor(This->bigBlockFile);

      return hr;
    }
2485 2486 2487 2488 2489 2490
  }

  /*
   * There is no block depot cached yet.
   */
  This->indexBlockDepotCached = 0xFFFFFFFF;
2491 2492 2493 2494 2495

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

2497 2498 2499
  /*
   * Create the block chain abstractions.
   */
2500 2501 2502
  if(!(This->rootBlockChain =
       BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
    return STG_E_READFAULT;
2503

2504 2505 2506 2507
  if(!(This->smallBlockDepotChain =
       BlockChainStream_Construct(This, &This->smallBlockDepotStart,
				  PROPERTY_NULL)))
    return STG_E_READFAULT;
2508 2509

  /*
2510
   * Write the root property (memory only)
2511
   */
2512
  if (fileCreate)
2513 2514 2515 2516 2517 2518
  {
    StgProperty rootProp;
    /*
     * Initialize the property chain
     */
    memset(&rootProp, 0, sizeof(rootProp));
2519 2520 2521
    MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
                         sizeof(rootProp.name)/sizeof(WCHAR) );
    rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2522 2523 2524 2525 2526
    rootProp.propertyType     = PROPTYPE_ROOT;
    rootProp.previousProperty = PROPERTY_NULL;
    rootProp.nextProperty     = PROPERTY_NULL;
    rootProp.dirProperty      = PROPERTY_NULL;
    rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2527 2528
    rootProp.size.u.HighPart    = 0;
    rootProp.size.u.LowPart     = 0;
2529

2530
    StorageImpl_WriteProperty(This, 0, &rootProp);
2531 2532 2533
  }

  /*
Alexandre Julliard's avatar
Alexandre Julliard committed
2534
   * Find the ID of the root in the property sets.
2535 2536
   */
  currentPropertyIndex = 0;
2537

2538 2539
  do
  {
2540
    readSuccessful = StorageImpl_ReadProperty(
2541 2542
                      This,
                      currentPropertyIndex,
2543
                      &currentProperty);
2544

2545
    if (readSuccessful)
2546 2547 2548 2549
    {
      if ( (currentProperty.sizeOfNameString != 0 ) &&
           (currentProperty.propertyType     == PROPTYPE_ROOT) )
      {
2550
        This->base.rootPropertySetIndex = currentPropertyIndex;
2551 2552 2553 2554
      }
    }

    currentPropertyIndex++;
2555

2556
  } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2557

2558
  if (!readSuccessful)
2559 2560
  {
    /* TODO CLEANUP */
2561
    return STG_E_READFAULT;
2562 2563 2564 2565 2566
  }

  /*
   * Create the block chain abstraction for the small block root chain.
   */
2567
  if(!(This->smallBlockRootChain =
2568
       BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2569
    return STG_E_READFAULT;
2570

2571 2572 2573
  return hr;
}

2574
static void StorageImpl_Destroy(StorageBaseImpl* iface)
2575
{
2576
  StorageImpl *This = (StorageImpl*) iface;
2577
  TRACE("(%p)\n", This);
2578

2579 2580
  StorageBaseImpl_DeleteAll(&This->base);

2581
  HeapFree(GetProcessHeap(), 0, This->pwcsName);
2582

2583 2584 2585 2586 2587
  BlockChainStream_Destroy(This->smallBlockRootChain);
  BlockChainStream_Destroy(This->rootBlockChain);
  BlockChainStream_Destroy(This->smallBlockDepotChain);

  BIGBLOCKFILE_Destructor(This->bigBlockFile);
2588
  HeapFree(GetProcessHeap(), 0, This);
2589 2590 2591 2592 2593 2594 2595 2596 2597
}

/******************************************************************************
 *      Storage32Impl_GetNextFreeBigBlock
 *
 * Returns the index of the next free big block.
 * If the big block depot is filled, this method will enlarge it.
 *
 */
2598
static ULONG StorageImpl_GetNextFreeBigBlock(
2599
  StorageImpl* This)
2600 2601 2602 2603 2604 2605 2606
{
  ULONG depotBlockIndexPos;
  void  *depotBuffer;
  ULONG depotBlockOffset;
  ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
  ULONG nextBlockIndex    = BLOCK_SPECIAL;
  int   depotIndex        = 0;
2607 2608 2609 2610
  ULONG freeBlock         = BLOCK_UNUSED;

  depotIndex = This->prevFreeBlock / blocksPerDepot;
  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2611 2612 2613 2614

  /*
   * Scan the entire big block depot until we find a block marked free
   */
2615
  while (nextBlockIndex != BLOCK_UNUSED)
2616
  {
2617
    if (depotIndex < COUNT_BBDEPOTINHEADER)
2618
    {
2619 2620
      depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];

2621
      /*
2622
       * Grow the primary depot.
2623
       */
2624 2625 2626
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        depotBlockIndexPos = depotIndex*blocksPerDepot;
2627

2628 2629 2630 2631 2632 2633
        /*
         * Add a block depot.
         */
        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
        This->bigBlockDepotCount++;
        This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2634

2635 2636 2637
        /*
         * Flag it as a block depot.
         */
2638
        StorageImpl_SetNextBlockInChain(This,
2639 2640
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);
2641

2642 2643
        /* Save new header information.
         */
2644
        StorageImpl_SaveFileHeader(This);
2645
      }
2646 2647 2648 2649
    }
    else
    {
      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2650

2651 2652 2653 2654 2655 2656 2657 2658
      if (depotBlockIndexPos == BLOCK_UNUSED)
      {
        /*
         * Grow the extended depot.
         */
        ULONG extIndex       = BLOCK_UNUSED;
        ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
        ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2659

2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679
        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.
         */
2680
        StorageImpl_SetNextBlockInChain(This,
2681 2682 2683 2684 2685 2686
                                          depotBlockIndexPos,
                                          BLOCK_SPECIAL);

        /* If necessary, flag the extended depot block.
         */
        if (extIndex != BLOCK_UNUSED)
2687
          StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2688 2689 2690

        /* Save header information.
         */
2691
        StorageImpl_SaveFileHeader(This);
2692
      }
2693 2694
    }

2695
    depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2696 2697 2698

    if (depotBuffer != 0)
    {
2699
      while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2700 2701 2702 2703
              ( nextBlockIndex != BLOCK_UNUSED))
      {
        StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);

2704 2705 2706 2707 2708
        if (nextBlockIndex == BLOCK_UNUSED)
        {
          freeBlock = (depotIndex * blocksPerDepot) +
                      (depotBlockOffset/sizeof(ULONG));
        }
2709 2710 2711 2712

        depotBlockOffset += sizeof(ULONG);
      }

2713
      StorageImpl_ReleaseBigBlock(This, depotBuffer);
2714 2715 2716
    }

    depotIndex++;
2717
    depotBlockOffset = 0;
2718 2719
  }

2720 2721 2722 2723 2724
  /*
   * make sure that the block physically exists before using it
   */
  BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);

2725 2726 2727
  This->prevFreeBlock = freeBlock;

  return freeBlock;
2728 2729
}

2730 2731 2732 2733 2734 2735
/******************************************************************************
 *      Storage32Impl_AddBlockDepot
 *
 * This will create a depot block, essentially it is a block initialized
 * to BLOCK_UNUSEDs.
 */
2736
static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2737 2738 2739
{
  BYTE* blockBuffer;

2740
  blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2741 2742 2743 2744 2745 2746

  /*
   * Initialize blocks as free
   */
  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);

2747
  StorageImpl_ReleaseBigBlock(This, blockBuffer);
2748 2749 2750 2751 2752 2753 2754 2755 2756
}

/******************************************************************************
 *      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.
 */
2757
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780
{
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
  ULONG blockIndex             = BLOCK_UNUSED;
  ULONG extBlockIndex          = This->extBigBlockDepotStart;

  assert(depotIndex >= COUNT_BBDEPOTINHEADER);

  if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
    return BLOCK_UNUSED;

  while (extBlockCount > 0)
  {
    extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
    extBlockCount--;
  }

  if (extBlockIndex != BLOCK_UNUSED)
  {
    BYTE* depotBuffer;

2781
    depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2782 2783 2784 2785 2786 2787 2788

    if (depotBuffer != 0)
    {
      StorageUtl_ReadDWord(depotBuffer,
                           extBlockOffset * sizeof(ULONG),
                           &blockIndex);

2789
      StorageImpl_ReleaseBigBlock(This, depotBuffer);
2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802
    }
  }

  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.
 */
2803
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822
{
  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
  ULONG extBlockIndex          = This->extBigBlockDepotStart;

  assert(depotIndex >= COUNT_BBDEPOTINHEADER);

  while (extBlockCount > 0)
  {
    extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
    extBlockCount--;
  }

  if (extBlockIndex != BLOCK_UNUSED)
  {
    BYTE* depotBuffer;

2823
    depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2824 2825 2826 2827 2828 2829 2830

    if (depotBuffer != 0)
    {
      StorageUtl_WriteDWord(depotBuffer,
                            extBlockOffset * sizeof(ULONG),
                            blockIndex);

2831
      StorageImpl_ReleaseBigBlock(This, depotBuffer);
2832 2833 2834 2835 2836 2837 2838 2839 2840
    }
  }
}

/******************************************************************************
 *      Storage32Impl_AddExtBlockDepot
 *
 * Creates an extended depot block.
 */
2841
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862
{
  ULONG numExtBlocks           = This->extBigBlockDepotCount;
  ULONG nextExtBlock           = This->extBigBlockDepotStart;
  BYTE* depotBuffer            = NULL;
  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
  {
2863
    unsigned int i;
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
    /*
     * Follow the chain to the last one.
     */
    for (i = 0; i < (numExtBlocks - 1); i++)
    {
      nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
    }

    /*
     * Add the new extended block to the chain.
     */
2875
    depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2876
    StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2877
    StorageImpl_ReleaseBigBlock(This, depotBuffer);
2878 2879 2880 2881 2882
  }

  /*
   * Initialize this block.
   */
2883
  depotBuffer = StorageImpl_GetBigBlock(This, index);
2884
  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2885
  StorageImpl_ReleaseBigBlock(This, depotBuffer);
2886 2887 2888 2889

  return index;
}

2890 2891 2892 2893 2894
/******************************************************************************
 *      Storage32Impl_FreeBigBlock
 *
 * This method will flag the specified block as free in the big block depot.
 */
2895
static void StorageImpl_FreeBigBlock(
2896
  StorageImpl* This,
2897 2898
  ULONG          blockIndex)
{
2899
  StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2900 2901 2902

  if (blockIndex < This->prevFreeBlock)
    This->prevFreeBlock = blockIndex;
2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913
}

/************************************************************************
 * 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.
2914
 *          nextBlockIndex - receives the return value.
2915 2916 2917 2918 2919 2920 2921 2922 2923
 *
 * 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.
2924 2925 2926
 *              BLOCK_EXTBBDEPOT - This block is part of the extended
 *                                 big block depot.
 *
2927
 * See Windows documentation for more details on IStorage methods.
2928
 */
2929
static HRESULT StorageImpl_GetNextBlockInChain(
2930
  StorageImpl* This,
2931 2932
  ULONG        blockIndex,
  ULONG*       nextBlockIndex)
2933 2934 2935 2936 2937 2938
{
  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
  void* depotBuffer;
  ULONG depotBlockIndexPos;
2939
  int index;
2940

2941 2942 2943 2944
  *nextBlockIndex   = BLOCK_SPECIAL;

  if(depotBlockCount >= This->bigBlockDepotCount)
  {
2945
    WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2946 2947 2948
	 This->bigBlockDepotCount);
    return STG_E_READFAULT;
  }
2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968

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

2969
    depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2970

2971 2972
    if (!depotBuffer)
      return STG_E_READFAULT;
2973

2974 2975 2976 2977
    for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
    {
      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
      This->blockDepotCached[index] = *nextBlockIndex;
2978
    }
2979
    StorageImpl_ReleaseBigBlock(This, depotBuffer);
2980 2981
  }

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

2984
  return S_OK;
2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001
}

/******************************************************************************
 *      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.
 */
3002
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3003 3004 3005 3006 3007
{
  ULONG nextBlockIndex   = BLOCK_SPECIAL;
  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
  void* depotBuffer;

3008
  depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3009 3010 3011 3012

  if (depotBuffer!=0)
  {
    StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3013

3014
    StorageImpl_ReleaseBigBlock(This, depotBuffer);
3015
  }
3016

3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033
  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);
 *
 */
3034
static void StorageImpl_SetNextBlockInChain(
3035
          StorageImpl* This,
3036 3037 3038 3039 3040 3041 3042 3043 3044 3045
          ULONG          blockIndex,
          ULONG          nextBlock)
{
  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
  ULONG depotBlockIndexPos;
  void* depotBuffer;

  assert(depotBlockCount < This->bigBlockDepotCount);
Thuy Nguyen's avatar
Thuy Nguyen committed
3046
  assert(blockIndex != nextBlock);
3047

3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058
  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
  {
    depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
  }
  else
  {
    /*
     * We have to look in the extended depot.
     */
    depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
  }
3059

3060
  depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3061 3062 3063 3064

  if (depotBuffer!=0)
  {
    StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3065
    StorageImpl_ReleaseBigBlock(This, depotBuffer);
3066 3067
  }

3068 3069 3070 3071 3072 3073 3074
  /*
   * Update the cached block depot, if necessary.
   */
  if (depotBlockCount == This->indexBlockDepotCached)
  {
    This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
  }
3075 3076 3077 3078 3079 3080 3081
}

/******************************************************************************
 *      Storage32Impl_LoadFileHeader
 *
 * This method will read in the file header, i.e. big block index -1.
 */
3082
static HRESULT StorageImpl_LoadFileHeader(
3083
          StorageImpl* This)
3084 3085 3086 3087 3088
{
  HRESULT hr = STG_E_FILENOTFOUND;
  void*   headerBigBlock = NULL;
  int     index;

3089
  TRACE("\n");
3090 3091 3092
  /*
   * Get a pointer to the big block of data containing the header.
   */
3093
  headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105

  /*
   * Extract the information from the header.
   */
  if (headerBigBlock!=0)
  {
    /*
     * Check for the "magic number" signature and return an error if it is not
     * found.
     */
    if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
    {
3106
      StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3107 3108 3109 3110 3111
      return STG_E_OLDFORMAT;
    }

    if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
    {
3112
      StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 3114 3115 3116
      return STG_E_INVALIDHEADER;
    }

    StorageUtl_ReadWord(
3117 3118
      headerBigBlock,
      OFFSET_BIGBLOCKSIZEBITS,
3119 3120 3121
      &This->bigBlockSizeBits);

    StorageUtl_ReadWord(
3122 3123
      headerBigBlock,
      OFFSET_SMALLBLOCKSIZEBITS,
3124 3125 3126
      &This->smallBlockSizeBits);

    StorageUtl_ReadDWord(
3127 3128
      headerBigBlock,
      OFFSET_BBDEPOTCOUNT,
3129 3130 3131
      &This->bigBlockDepotCount);

    StorageUtl_ReadDWord(
3132 3133
      headerBigBlock,
      OFFSET_ROOTSTARTBLOCK,
3134 3135 3136
      &This->rootStartBlock);

    StorageUtl_ReadDWord(
3137 3138
      headerBigBlock,
      OFFSET_SBDEPOTSTART,
3139 3140
      &This->smallBlockDepotStart);

3141 3142 3143
    StorageUtl_ReadDWord(
      headerBigBlock,
      OFFSET_EXTBBDEPOTSTART,
3144 3145 3146
      &This->extBigBlockDepotStart);

    StorageUtl_ReadDWord(
3147 3148
      headerBigBlock,
      OFFSET_EXTBBDEPOTCOUNT,
3149
      &This->extBigBlockDepotCount);
3150

3151 3152 3153
    for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
    {
      StorageUtl_ReadDWord(
3154
        headerBigBlock,
3155 3156 3157
        OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
        &(This->bigBlockDepotStart[index]));
    }
3158

3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171
    /*
     * Make the bitwise arithmetic to get the size of the blocks in bytes.
     */
    if ((1 << 2) == 4)
    {
      This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
      This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
    }
    else
    {
      This->bigBlockSize   = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
      This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
    }
3172

3173
    /*
3174
     * Right now, the code is making some assumptions about the size of the
3175 3176
     * blocks, just make sure they are what we're expecting.
     */
Alexandre Julliard's avatar
Alexandre Julliard committed
3177 3178 3179 3180 3181 3182 3183 3184
    if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
	This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
    {
	WARN("Broken OLE storage file\n");
	hr = STG_E_INVALIDHEADER;
    }
    else
	hr = S_OK;
3185

3186 3187 3188
    /*
     * Release the block.
     */
3189
    StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3190
  }
3191

3192 3193 3194 3195 3196 3197 3198 3199
  return hr;
}

/******************************************************************************
 *      Storage32Impl_SaveFileHeader
 *
 * This method will save to the file the header, i.e. big block -1.
 */
3200
static void StorageImpl_SaveFileHeader(
3201
          StorageImpl* This)
3202 3203 3204
{
  BYTE   headerBigBlock[BIG_BLOCK_SIZE];
  int    index;
3205
  BOOL success;
3206 3207 3208 3209

  /*
   * Get a pointer to the big block of data containing the header.
   */
3210
  success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3211

3212 3213 3214 3215 3216 3217 3218 3219 3220
  /*
   * If the block read failed, the file is probably new.
   */
  if (!success)
  {
    /*
     * Initialize for all unknown fields.
     */
    memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3221

3222 3223 3224 3225
    /*
     * Initialize the magic number.
     */
    memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3226

3227 3228 3229 3230 3231 3232 3233 3234
    /*
     * And a bunch of things we don't know what they mean
     */
    StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
    StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
    StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
    StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
  }
3235

3236 3237 3238
  /*
   * Write the information to the header.
   */
3239 3240 3241 3242
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_BIGBLOCKSIZEBITS,
    This->bigBlockSizeBits);
3243

3244 3245 3246 3247
  StorageUtl_WriteWord(
    headerBigBlock,
    OFFSET_SMALLBLOCKSIZEBITS,
    This->smallBlockSizeBits);
3248

3249 3250 3251 3252
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_BBDEPOTCOUNT,
    This->bigBlockDepotCount);
3253

3254 3255 3256 3257
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_ROOTSTARTBLOCK,
    This->rootStartBlock);
3258

3259 3260 3261 3262
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTSTART,
    This->smallBlockDepotStart);
3263

3264 3265 3266 3267 3268 3269 3270 3271 3272 3273
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_SBDEPOTCOUNT,
    This->smallBlockDepotChain ?
     BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);

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

3275 3276 3277 3278 3279 3280 3281
  StorageUtl_WriteDWord(
    headerBigBlock,
    OFFSET_EXTBBDEPOTCOUNT,
    This->extBigBlockDepotCount);

  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
  {
3282
    StorageUtl_WriteDWord(
3283
      headerBigBlock,
3284 3285
      OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
      (This->bigBlockDepotStart[index]));
3286
  }
3287

3288 3289 3290
  /*
   * Write the big block back to the file.
   */
3291
  StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3292 3293 3294 3295 3296 3297 3298
}

/******************************************************************************
 *      Storage32Impl_ReadProperty
 *
 * This method will read the specified property from the property chain.
 */
3299 3300
BOOL StorageImpl_ReadProperty(
  StorageImpl* This,
3301 3302 3303 3304 3305
  ULONG          index,
  StgProperty*   buffer)
{
  BYTE           currentProperty[PROPSET_BLOCK_SIZE];
  ULARGE_INTEGER offsetInPropSet;
3306
  HRESULT        readRes;
3307
  ULONG          bytesRead;
Thuy Nguyen's avatar
Thuy Nguyen committed
3308

3309 3310
  offsetInPropSet.u.HighPart = 0;
  offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3311

3312
  readRes = BlockChainStream_ReadAt(
3313 3314 3315 3316 3317
                    This->rootBlockChain,
                    offsetInPropSet,
                    PROPSET_BLOCK_SIZE,
                    currentProperty,
                    &bytesRead);
Alexandre Julliard's avatar
Alexandre Julliard committed
3318

3319
  if (SUCCEEDED(readRes))
3320
  {
Alexandre Julliard's avatar
Alexandre Julliard committed
3321
    /* replace the name of root entry (often "Root Entry") by the file name */
3322
    WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
Alexandre Julliard's avatar
Alexandre Julliard committed
3323 3324
	    		This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;

3325 3326
    memset(buffer->name, 0, sizeof(buffer->name));
    memcpy(
3327 3328
      buffer->name,
      propName,
3329
      PROPERTY_NAME_BUFFER_LEN );
Alexandre Julliard's avatar
Alexandre Julliard committed
3330
    TRACE("storage name: %s\n", debugstr_w(buffer->name));
3331 3332

    memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3333

3334
    StorageUtl_ReadWord(
3335 3336
      currentProperty,
      OFFSET_PS_NAMELENGTH,
3337 3338 3339
      &buffer->sizeOfNameString);

    StorageUtl_ReadDWord(
3340 3341
      currentProperty,
      OFFSET_PS_PREVIOUSPROP,
3342 3343 3344
      &buffer->previousProperty);

    StorageUtl_ReadDWord(
3345 3346
      currentProperty,
      OFFSET_PS_NEXTPROP,
3347 3348 3349
      &buffer->nextProperty);

    StorageUtl_ReadDWord(
3350 3351
      currentProperty,
      OFFSET_PS_DIRPROP,
3352 3353 3354
      &buffer->dirProperty);

    StorageUtl_ReadGUID(
3355 3356
      currentProperty,
      OFFSET_PS_GUID,
3357 3358 3359
      &buffer->propertyUniqueID);

    StorageUtl_ReadDWord(
3360 3361
      currentProperty,
      OFFSET_PS_TSS1,
3362 3363 3364
      &buffer->timeStampS1);

    StorageUtl_ReadDWord(
3365 3366
      currentProperty,
      OFFSET_PS_TSD1,
3367 3368 3369
      &buffer->timeStampD1);

    StorageUtl_ReadDWord(
3370 3371
      currentProperty,
      OFFSET_PS_TSS2,
3372 3373 3374
      &buffer->timeStampS2);

    StorageUtl_ReadDWord(
3375 3376
      currentProperty,
      OFFSET_PS_TSD2,
3377 3378 3379
      &buffer->timeStampD2);

    StorageUtl_ReadDWord(
3380 3381
      currentProperty,
      OFFSET_PS_STARTBLOCK,
3382 3383 3384
      &buffer->startingBlock);

    StorageUtl_ReadDWord(
3385 3386
      currentProperty,
      OFFSET_PS_SIZE,
3387
      &buffer->size.u.LowPart);
3388

3389
    buffer->size.u.HighPart = 0;
3390 3391
  }

3392
  return SUCCEEDED(readRes) ? TRUE : FALSE;
3393 3394 3395 3396 3397
}

/*********************************************************************
 * Write the specified property into the property chain
 */
3398 3399
BOOL StorageImpl_WriteProperty(
  StorageImpl* This,
3400 3401 3402 3403 3404
  ULONG          index,
  StgProperty*   buffer)
{
  BYTE           currentProperty[PROPSET_BLOCK_SIZE];
  ULARGE_INTEGER offsetInPropSet;
3405
  HRESULT        writeRes;
3406 3407
  ULONG          bytesWritten;

3408 3409
  offsetInPropSet.u.HighPart = 0;
  offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3410 3411 3412 3413

  memset(currentProperty, 0, PROPSET_BLOCK_SIZE);

  memcpy(
3414 3415
    currentProperty + OFFSET_PS_NAME,
    buffer->name,
3416 3417 3418 3419 3420
    PROPERTY_NAME_BUFFER_LEN );

  memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);

  StorageUtl_WriteWord(
3421 3422
    currentProperty,
      OFFSET_PS_NAMELENGTH,
3423 3424 3425
      buffer->sizeOfNameString);

  StorageUtl_WriteDWord(
3426 3427
    currentProperty,
      OFFSET_PS_PREVIOUSPROP,
3428 3429 3430
      buffer->previousProperty);

  StorageUtl_WriteDWord(
3431 3432
    currentProperty,
      OFFSET_PS_NEXTPROP,
3433 3434 3435
      buffer->nextProperty);

  StorageUtl_WriteDWord(
3436 3437
    currentProperty,
      OFFSET_PS_DIRPROP,
3438 3439 3440
      buffer->dirProperty);

  StorageUtl_WriteGUID(
3441 3442
    currentProperty,
      OFFSET_PS_GUID,
3443 3444 3445
      &buffer->propertyUniqueID);

  StorageUtl_WriteDWord(
3446 3447
    currentProperty,
      OFFSET_PS_TSS1,
3448 3449 3450
      buffer->timeStampS1);

  StorageUtl_WriteDWord(
3451 3452
    currentProperty,
      OFFSET_PS_TSD1,
3453 3454 3455
      buffer->timeStampD1);

  StorageUtl_WriteDWord(
3456 3457
    currentProperty,
      OFFSET_PS_TSS2,
3458 3459 3460
      buffer->timeStampS2);

  StorageUtl_WriteDWord(
3461 3462
    currentProperty,
      OFFSET_PS_TSD2,
3463 3464 3465
      buffer->timeStampD2);

  StorageUtl_WriteDWord(
3466 3467
    currentProperty,
      OFFSET_PS_STARTBLOCK,
3468 3469 3470
      buffer->startingBlock);

  StorageUtl_WriteDWord(
3471 3472
    currentProperty,
      OFFSET_PS_SIZE,
3473
      buffer->size.u.LowPart);
3474

3475 3476 3477 3478 3479 3480
  writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
                                      offsetInPropSet,
                                      PROPSET_BLOCK_SIZE,
                                      currentProperty,
                                      &bytesWritten);
  return SUCCEEDED(writeRes) ? TRUE : FALSE;
3481 3482
}

3483
static BOOL StorageImpl_ReadBigBlock(
3484
  StorageImpl* This,
3485 3486 3487 3488 3489
  ULONG          blockIndex,
  void*          buffer)
{
  void* bigBlockBuffer;

3490
  bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3491 3492 3493 3494 3495

  if (bigBlockBuffer!=0)
  {
    memcpy(buffer, bigBlockBuffer, This->bigBlockSize);

3496
    StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3497 3498 3499 3500 3501 3502 3503

    return TRUE;
  }

  return FALSE;
}

3504
static BOOL StorageImpl_WriteBigBlock(
3505
  StorageImpl* This,
3506 3507 3508 3509 3510
  ULONG          blockIndex,
  void*          buffer)
{
  void* bigBlockBuffer;

3511
  bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3512 3513 3514 3515 3516

  if (bigBlockBuffer!=0)
  {
    memcpy(bigBlockBuffer, buffer, This->bigBlockSize);

3517
    StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3518

3519 3520
    return TRUE;
  }
3521

3522 3523 3524
  return FALSE;
}

3525
static void* StorageImpl_GetROBigBlock(
3526
  StorageImpl* This,
3527 3528 3529 3530 3531
  ULONG          blockIndex)
{
  return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
}

3532
static void* StorageImpl_GetBigBlock(
3533
  StorageImpl* This,
3534 3535 3536 3537 3538
  ULONG          blockIndex)
{
  return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
}

3539
static void StorageImpl_ReleaseBigBlock(
3540
  StorageImpl* This,
3541 3542 3543 3544 3545
  void*          pBigBlock)
{
  BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
}

3546 3547 3548 3549 3550 3551 3552
/******************************************************************************
 *              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(
3553
                      StorageImpl* This,
3554 3555 3556 3557
                      SmallBlockChainStream** ppsbChain)
{
  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
  ULARGE_INTEGER size, offset;
3558 3559
  ULONG cbRead, cbWritten;
  ULARGE_INTEGER cbTotalRead;
3560
  ULONG propertyIndex;
3561
  HRESULT resWrite = S_OK;
3562
  HRESULT resRead;
3563
  StgProperty chainProperty;
3564
  BYTE *buffer;
3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575
  BlockChainStream *bbTempChain = NULL;
  BlockChainStream *bigBlockChain = NULL;

  /*
   * Create a temporary big block chain that doesn't have
   * an associated property. This temporary chain will be
   * used to copy data from small blocks to big blocks.
   */
  bbTempChain = BlockChainStream_Construct(This,
                                           &bbHeadOfChain,
                                           PROPERTY_NULL);
3576
  if(!bbTempChain) return NULL;
3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
  /*
   * 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.
   */
3587 3588
  offset.u.LowPart = 0;
  offset.u.HighPart = 0;
3589
  cbTotalRead.QuadPart = 0;
3590

3591
  buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3592 3593
  do
  {
3594 3595
    resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
                                           offset,
3596
                                           This->smallBlockSize,
3597 3598 3599
                                           buffer,
                                           &cbRead);
    if (FAILED(resRead))
3600 3601 3602 3603
        break;

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

3606
        resWrite = BlockChainStream_WriteAt(bbTempChain,
3607
                                            offset,
3608
                                            cbRead,
3609 3610
                                            buffer,
                                            &cbWritten);
3611

3612
        if (FAILED(resWrite))
3613
            break;
3614

3615 3616
        offset.u.LowPart += This->smallBlockSize;
    }
3617
  } while (cbTotalRead.QuadPart < size.QuadPart);
3618
  HeapFree(GetProcessHeap(),0,buffer);
3619

3620 3621
  if (FAILED(resRead) || FAILED(resWrite))
  {
3622
    ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3623 3624 3625
    BlockChainStream_Destroy(bbTempChain);
    return NULL;
  }
3626 3627 3628 3629 3630

  /*
   * Destroy the small block chain.
   */
  propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3631 3632
  size.u.HighPart = 0;
  size.u.LowPart  = 0;
3633 3634 3635 3636 3637 3638 3639 3640
  SmallBlockChainStream_SetSize(*ppsbChain, size);
  SmallBlockChainStream_Destroy(*ppsbChain);
  *ppsbChain = 0;

  /*
   * Change the property information. This chain is now a big block chain
   * and it doesn't reside in the small blocks chain anymore.
   */
3641
  StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3642 3643 3644

  chainProperty.startingBlock = bbHeadOfChain;

3645
  StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658

  /*
   * Destroy the temporary propertyless big block chain.
   * Create a new big block chain associated with this property.
   */
  BlockChainStream_Destroy(bbTempChain);
  bigBlockChain = BlockChainStream_Construct(This,
                                             NULL,
                                             propertyIndex);

  return bigBlockChain;
}

3659
static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3660
{
3661 3662 3663
  StorageInternalImpl* This = (StorageInternalImpl*) iface;

  StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3664 3665 3666 3667 3668 3669 3670 3671 3672 3673
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
**
** Storage32InternalImpl_Commit
**
** The non-root storages cannot be opened in transacted mode thus this function
** does nothing.
*/
3674
static HRESULT WINAPI StorageInternalImpl_Commit(
3675
  IStorage*            iface,
3676
  DWORD                  grfCommitFlags)  /* [in] */
3677 3678 3679
{
  return S_OK;
}
3680

3681 3682 3683 3684 3685 3686 3687
/******************************************************************************
**
** Storage32InternalImpl_Revert
**
** The non-root storages cannot be opened in transacted mode thus this function
** does nothing.
*/
3688
static HRESULT WINAPI StorageInternalImpl_Revert(
3689
  IStorage*            iface)
3690 3691 3692 3693
{
  return S_OK;
}

3694
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3695
{
3696
  IStorage_Release((IStorage*)This->parentStorage);
3697 3698 3699 3700
  HeapFree(GetProcessHeap(), 0, This->stackToVisit);
  HeapFree(GetProcessHeap(), 0, This);
}

3701
static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3702
  IEnumSTATSTG*     iface,
3703 3704 3705
  REFIID            riid,
  void**            ppvObject)
{
3706 3707
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721
  /*
   * Perform a sanity check on the parameters.
   */
  if (ppvObject==0)
    return E_INVALIDARG;

  /*
   * Initialize the return parameter.
   */
  *ppvObject = 0;

  /*
   * Compare the riid with the interface IDs implemented by this object.
   */
3722
  if (IsEqualGUID(&IID_IUnknown, riid) ||
3723
      IsEqualGUID(&IID_IEnumSTATSTG, riid))
3724 3725
  {
    *ppvObject = (IEnumSTATSTG*)This;
3726 3727
    IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
    return S_OK;
3728 3729
  }

3730
  return E_NOINTERFACE;
3731
}
3732

3733
static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3734
  IEnumSTATSTG* iface)
3735
{
3736
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3737
  return InterlockedIncrement(&This->ref);
3738
}
3739

3740
static ULONG   WINAPI IEnumSTATSTGImpl_Release(
3741
  IEnumSTATSTG* iface)
3742
{
3743 3744
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3745 3746
  ULONG newRef;

3747
  newRef = InterlockedDecrement(&This->ref);
3748 3749 3750 3751 3752 3753 3754 3755 3756

  /*
   * If the reference count goes down to 0, perform suicide.
   */
  if (newRef==0)
  {
    IEnumSTATSTGImpl_Destroy(This);
  }

3757
  return newRef;
3758 3759
}

3760
static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3761
  IEnumSTATSTG* iface,
3762 3763 3764 3765
  ULONG             celt,
  STATSTG*          rgelt,
  ULONG*            pceltFetched)
{
3766 3767
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3768 3769 3770 3771 3772 3773 3774 3775 3776
  StgProperty currentProperty;
  STATSTG*    currentReturnStruct = rgelt;
  ULONG       objectFetched       = 0;
  ULONG      currentSearchNode;

  /*
   * Perform a sanity check on the parameters.
   */
  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3777 3778
    return E_INVALIDARG;

3779 3780 3781 3782 3783 3784
  /*
   * To avoid the special case, get another pointer to a ULONG value if
   * the caller didn't supply one.
   */
  if (pceltFetched==0)
    pceltFetched = &objectFetched;
3785

3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796
  /*
   * 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;

  /*
   * Start with the node at the top of the stack.
   */
  currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);

3797
  while ( ( *pceltFetched < celt) &&
3798 3799
          ( currentSearchNode!=PROPERTY_NULL) )
  {
3800
    /*
3801 3802 3803 3804 3805 3806 3807
     * Remove the top node from the stack
     */
    IEnumSTATSTGImpl_PopSearchNode(This, TRUE);

    /*
     * Read the property from the storage.
     */
3808
    StorageImpl_ReadProperty(This->parentStorage,
3809
      currentSearchNode,
3810 3811 3812 3813 3814 3815 3816 3817
      &currentProperty);

    /*
     * Copy the information to the return buffer.
     */
    StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
      &currentProperty,
      STATFLAG_DEFAULT);
3818

3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841
    /*
     * Step to the next item in the iteration
     */
    (*pceltFetched)++;
    currentReturnStruct++;

    /*
     * Push the next search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);

    /*
     * continue the iteration.
     */
    currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
  }

  if (*pceltFetched == celt)
    return S_OK;

  return S_FALSE;
}

3842

3843
static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3844
  IEnumSTATSTG* iface,
3845 3846
  ULONG             celt)
{
3847 3848
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3849 3850 3851 3852 3853 3854 3855 3856 3857
  StgProperty currentProperty;
  ULONG       objectFetched       = 0;
  ULONG       currentSearchNode;

  /*
   * Start with the node at the top of the stack.
   */
  currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);

3858
  while ( (objectFetched < celt) &&
3859 3860
          (currentSearchNode!=PROPERTY_NULL) )
  {
3861
    /*
3862 3863 3864 3865 3866 3867 3868
     * Remove the top node from the stack
     */
    IEnumSTATSTGImpl_PopSearchNode(This, TRUE);

    /*
     * Read the property from the storage.
     */
3869
    StorageImpl_ReadProperty(This->parentStorage,
3870
      currentSearchNode,
3871
      &currentProperty);
3872

3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893
    /*
     * Step to the next item in the iteration
     */
    objectFetched++;

    /*
     * Push the next search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);

    /*
     * continue the iteration.
     */
    currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
  }

  if (objectFetched == celt)
    return S_OK;

  return S_FALSE;
}
3894

3895
static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3896
  IEnumSTATSTG* iface)
3897
{
3898 3899
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3900
  StgProperty rootProperty;
3901
  BOOL      readSuccessful;
3902 3903 3904 3905 3906 3907 3908 3909 3910

  /*
   * Re-initialize the search stack to an empty stack
   */
  This->stackSize = 0;

  /*
   * Read the root property from the storage.
   */
3911
  readSuccessful = StorageImpl_ReadProperty(
3912
                    This->parentStorage,
3913
                    This->firstPropertyNode,
3914 3915
                    &rootProperty);

3916
  if (readSuccessful)
3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927
  {
    assert(rootProperty.sizeOfNameString!=0);

    /*
     * Push the search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
  }

  return S_OK;
}
3928

3929
static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3930
  IEnumSTATSTG* iface,
3931 3932
  IEnumSTATSTG**    ppenum)
{
3933 3934
  IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;

3935 3936 3937 3938 3939 3940 3941
  IEnumSTATSTGImpl* newClone;

  /*
   * Perform a sanity check on the parameters.
   */
  if (ppenum==0)
    return E_INVALIDARG;
3942

3943 3944 3945
  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
               This->firstPropertyNode);

3946

3947 3948 3949 3950 3951 3952
  /*
   * The new clone enumeration must point to the same current node as
   * the ole one.
   */
  newClone->stackSize    = This->stackSize    ;
  newClone->stackMaxSize = This->stackMaxSize ;
3953
  newClone->stackToVisit =
3954 3955 3956
    HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);

  memcpy(
3957 3958
    newClone->stackToVisit,
    This->stackToVisit,
3959 3960
    sizeof(ULONG) * newClone->stackSize);

3961 3962
  *ppenum = (IEnumSTATSTG*)newClone;

3963 3964 3965 3966
  /*
   * Don't forget to nail down a reference to the clone before
   * returning it.
   */
3967
  IEnumSTATSTGImpl_AddRef(*ppenum);
3968 3969 3970 3971

  return S_OK;
}

3972
static INT IEnumSTATSTGImpl_FindParentProperty(
3973
  IEnumSTATSTGImpl *This,
3974
  ULONG             childProperty,
3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991
  StgProperty      *currentProperty,
  ULONG            *thisNodeId)
{
  ULONG currentSearchNode;
  ULONG foundNode;

  /*
   * To avoid the special case, get another pointer to a ULONG value if
   * the caller didn't supply one.
   */
  if (thisNodeId==0)
    thisNodeId = &foundNode;

  /*
   * Start with the node at the top of the stack.
   */
  currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3992

3993 3994 3995 3996 3997 3998 3999 4000

  while (currentSearchNode!=PROPERTY_NULL)
  {
    /*
     * Store the current node in the returned parameters
     */
    *thisNodeId = currentSearchNode;

4001
    /*
4002 4003 4004 4005 4006 4007 4008
     * Remove the top node from the stack
     */
    IEnumSTATSTGImpl_PopSearchNode(This, TRUE);

    /*
     * Read the property from the storage.
     */
4009
    StorageImpl_ReadProperty(
4010
      This->parentStorage,
4011
      currentSearchNode,
4012
      currentProperty);
4013

4014 4015 4016
    if (currentProperty->previousProperty == childProperty)
      return PROPERTY_RELATION_PREVIOUS;

4017
    else if (currentProperty->nextProperty == childProperty)
4018
      return PROPERTY_RELATION_NEXT;
4019

4020 4021
    else if (currentProperty->dirProperty == childProperty)
      return PROPERTY_RELATION_DIR;
4022

4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036
    /*
     * Push the next search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);

    /*
     * continue the iteration.
     */
    currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
  }

  return PROPERTY_NULL;
}

4037
static ULONG IEnumSTATSTGImpl_FindProperty(
4038
  IEnumSTATSTGImpl* This,
4039
  const OLECHAR*  lpszPropName,
4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050
  StgProperty*      currentProperty)
{
  ULONG currentSearchNode;

  /*
   * Start with the node at the top of the stack.
   */
  currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);

  while (currentSearchNode!=PROPERTY_NULL)
  {
4051
    /*
4052 4053 4054 4055 4056 4057 4058
     * Remove the top node from the stack
     */
    IEnumSTATSTGImpl_PopSearchNode(This, TRUE);

    /*
     * Read the property from the storage.
     */
4059
    StorageImpl_ReadProperty(This->parentStorage,
4060
      currentSearchNode,
4061 4062 4063
      currentProperty);

    if ( propertyNameCmp(
Eric Pouech's avatar
Eric Pouech committed
4064 4065
          (const OLECHAR*)currentProperty->name,
          (const OLECHAR*)lpszPropName) == 0)
4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081
      return currentSearchNode;

    /*
     * Push the next search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);

    /*
     * continue the iteration.
     */
    currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
  }

  return PROPERTY_NULL;
}

4082
static void IEnumSTATSTGImpl_PushSearchNode(
4083 4084 4085 4086
  IEnumSTATSTGImpl* This,
  ULONG             nodeToPush)
{
  StgProperty rootProperty;
4087
  BOOL      readSuccessful;
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102

  /*
   * First, make sure we're not trying to push an unexisting node.
   */
  if (nodeToPush==PROPERTY_NULL)
    return;

  /*
   * First push the node to the stack
   */
  if (This->stackSize == This->stackMaxSize)
  {
    This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;

    This->stackToVisit = HeapReAlloc(
4103
                           GetProcessHeap(),
4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114
                           0,
                           This->stackToVisit,
                           sizeof(ULONG) * This->stackMaxSize);
  }

  This->stackToVisit[This->stackSize] = nodeToPush;
  This->stackSize++;

  /*
   * Read the root property from the storage.
   */
4115
  readSuccessful = StorageImpl_ReadProperty(
4116
                    This->parentStorage,
4117
                    nodeToPush,
4118 4119
                    &rootProperty);

4120
  if (readSuccessful)
4121 4122 4123 4124 4125 4126 4127 4128 4129 4130
  {
    assert(rootProperty.sizeOfNameString!=0);

    /*
     * Push the previous search node in the search stack.
     */
    IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
  }
}

4131
static ULONG IEnumSTATSTGImpl_PopSearchNode(
4132
  IEnumSTATSTGImpl* This,
4133
  BOOL            remove)
4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147
{
  ULONG topNode;

  if (This->stackSize == 0)
    return PROPERTY_NULL;

  topNode = This->stackToVisit[This->stackSize-1];

  if (remove)
    This->stackSize--;

  return topNode;
}

4148 4149 4150
/*
 * Virtual function table for the IEnumSTATSTGImpl class.
 */
4151
static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165
{
    IEnumSTATSTGImpl_QueryInterface,
    IEnumSTATSTGImpl_AddRef,
    IEnumSTATSTGImpl_Release,
    IEnumSTATSTGImpl_Next,
    IEnumSTATSTGImpl_Skip,
    IEnumSTATSTGImpl_Reset,
    IEnumSTATSTGImpl_Clone
};

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

4166
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210
  StorageImpl* parentStorage,
  ULONG          firstPropertyNode)
{
  IEnumSTATSTGImpl* newEnumeration;

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

  if (newEnumeration!=0)
  {
    /*
     * Set-up the virtual function table and reference count.
     */
    newEnumeration->lpVtbl    = &IEnumSTATSTGImpl_Vtbl;
    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;
    IStorage_AddRef((IStorage*)newEnumeration->parentStorage);

    newEnumeration->firstPropertyNode   = firstPropertyNode;

    /*
     * Initialize the search stack
     */
    newEnumeration->stackSize    = 0;
    newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
    newEnumeration->stackToVisit =
      HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);

    /*
     * Make sure the current node of the iterator is the first one.
     */
    IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
  }

  return newEnumeration;
}

/*
 * Virtual function table for the Storage32InternalImpl class.
 */
4211
static const IStorageVtbl Storage32InternalImpl_Vtbl =
4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236
{
    StorageBaseImpl_QueryInterface,
    StorageBaseImpl_AddRef,
    StorageBaseImpl_Release,
    StorageBaseImpl_CreateStream,
    StorageBaseImpl_OpenStream,
    StorageImpl_CreateStorage,
    StorageBaseImpl_OpenStorage,
    StorageImpl_CopyTo,
    StorageImpl_MoveElementTo,
    StorageInternalImpl_Commit,
    StorageInternalImpl_Revert,
    StorageBaseImpl_EnumElements,
    StorageImpl_DestroyElement,
    StorageBaseImpl_RenameElement,
    StorageImpl_SetElementTimes,
    StorageBaseImpl_SetClass,
    StorageImpl_SetStateBits,
    StorageBaseImpl_Stat
};

/******************************************************************************
** Storage32InternalImpl implementation
*/

4237
static StorageInternalImpl* StorageInternalImpl_Construct(
4238
  StorageImpl* ancestorStorage,
4239 4240
  DWORD        openFlags,
  ULONG        rootPropertyIndex)
4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252
{
  StorageInternalImpl* newStorage;

  /*
   * Allocate space for the new storage object
   */
  newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));

  if (newStorage!=0)
  {
    memset(newStorage, 0, sizeof(StorageInternalImpl));

4253 4254 4255 4256 4257 4258
    /*
     * Initialize the stream list
     */

    list_init(&newStorage->base.strmHead);

4259 4260 4261 4262 4263
    /*
     * Initialize the virtual function table.
     */
    newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
    newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4264
    newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282

    /*
     * Keep the ancestor storage pointer and nail a reference to it.
     */
    newStorage->base.ancestorStorage = ancestorStorage;
    StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));

    /*
     * Keep the index of the root property set for this storage,
     */
    newStorage->base.rootPropertySetIndex = rootPropertyIndex;

    return newStorage;
  }

  return 0;
}

4283 4284 4285 4286
/******************************************************************************
** StorageUtl implementation
*/

4287
void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4288
{
4289 4290 4291 4292
  WORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(WORD));
  *value = le16toh(tmp);
4293 4294
}

4295
void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4296
{
4297
  value = htole16(value);
4298
  memcpy(buffer+offset, &value, sizeof(WORD));
4299 4300
}

4301
void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4302
{
4303 4304 4305 4306
  DWORD tmp;

  memcpy(&tmp, buffer+offset, sizeof(DWORD));
  *value = le32toh(tmp);
4307 4308
}

4309
void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4310
{
4311
  value = htole32(value);
4312
  memcpy(buffer+offset, &value, sizeof(DWORD));
4313 4314
}

Juan Lang's avatar
Juan Lang committed
4315 4316 4317 4318 4319 4320 4321
void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
 ULARGE_INTEGER* value)
{
#ifdef WORDS_BIGENDIAN
    ULARGE_INTEGER tmp;

    memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4322 4323
    value->u.LowPart = htole32(tmp.u.HighPart);
    value->u.HighPart = htole32(tmp.u.LowPart);
Juan Lang's avatar
Juan Lang committed
4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334
#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;

4335 4336
    tmp.u.LowPart = htole32(value->u.HighPart);
    tmp.u.HighPart = htole32(value->u.LowPart);
Juan Lang's avatar
Juan Lang committed
4337 4338 4339 4340 4341 4342
    memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
#else
    memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
#endif
}

4343
void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4344 4345 4346 4347 4348
{
  StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
  StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
  StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));

4349
  memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4350 4351
}

4352
void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4353 4354 4355 4356 4357
{
  StorageUtl_WriteDWord(buffer, offset,   value->Data1);
  StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
  StorageUtl_WriteWord(buffer,  offset+6, value->Data3);

4358
  memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4359 4360 4361 4362 4363 4364 4365 4366 4367 4368
}

void StorageUtl_CopyPropertyToSTATSTG(
  STATSTG*     destination,
  StgProperty* source,
  int          statFlags)
{
  /*
   * The copy of the string occurs only when the flag is not set
   */
4369 4370 4371
  if( ((statFlags & STATFLAG_NONAME) != 0) || 
       (source->name == NULL) || 
       (source->name[0] == 0) )
4372 4373 4374 4375 4376
  {
    destination->pwcsName = 0;
  }
  else
  {
4377
    destination->pwcsName =
4378
      CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4379

4380
    strcpyW((LPWSTR)destination->pwcsName, source->name);
4381
  }
4382

4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393
  switch (source->propertyType)
  {
    case PROPTYPE_STORAGE:
    case PROPTYPE_ROOT:
      destination->type = STGTY_STORAGE;
      break;
    case PROPTYPE_STREAM:
      destination->type = STGTY_STREAM;
      break;
    default:
      destination->type = STGTY_STREAM;
4394
      break;
4395 4396 4397
  }

  destination->cbSize            = source->size;
4398
/*
4399 4400
  currentReturnStruct->mtime     = {0}; TODO
  currentReturnStruct->ctime     = {0};
4401
  currentReturnStruct->atime     = {0};
4402 4403
*/
  destination->grfMode           = 0;
4404
  destination->grfLocksSupported = 0;
4405
  destination->clsid             = source->propertyUniqueID;
4406 4407
  destination->grfStateBits      = 0;
  destination->reserved          = 0;
4408 4409 4410 4411 4412 4413 4414
}

/******************************************************************************
** BlockChainStream implementation
*/

BlockChainStream* BlockChainStream_Construct(
4415
  StorageImpl* parentStorage,
4416 4417 4418 4419
  ULONG*         headOfStreamPlaceHolder,
  ULONG          propertyIndex)
{
  BlockChainStream* newStream;
Thuy Nguyen's avatar
Thuy Nguyen committed
4420
  ULONG blockIndex;
4421 4422 4423 4424 4425 4426

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));

  newStream->parentStorage           = parentStorage;
  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
  newStream->ownerPropertyIndex      = propertyIndex;
Thuy Nguyen's avatar
Thuy Nguyen committed
4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437
  newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
  newStream->tailIndex               = BLOCK_END_OF_CHAIN;
  newStream->numBlocks               = 0;

  blockIndex = BlockChainStream_GetHeadOfChain(newStream);

  while (blockIndex != BLOCK_END_OF_CHAIN)
  {
    newStream->numBlocks++;
    newStream->tailIndex = blockIndex;

4438 4439 4440 4441 4442 4443 4444 4445
    if(FAILED(StorageImpl_GetNextBlockInChain(
	      parentStorage,
	      blockIndex,
	      &blockIndex)))
    {
      HeapFree(GetProcessHeap(), 0, newStream);
      return NULL;
    }
Thuy Nguyen's avatar
Thuy Nguyen committed
4446
  }
4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463

  return newStream;
}

void BlockChainStream_Destroy(BlockChainStream* This)
{
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
 *      BlockChainStream_GetHeadOfChain
 *
 * Returns the head of this stream chain.
 * Some special chains don't have properties, their heads are kept in
 * This->headOfStreamPlaceHolder.
 *
 */
4464
static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4465 4466
{
  StgProperty chainProperty;
4467
  BOOL      readSuccessful;
4468 4469 4470 4471 4472 4473

  if (This->headOfStreamPlaceHolder != 0)
    return *(This->headOfStreamPlaceHolder);

  if (This->ownerPropertyIndex != PROPERTY_NULL)
  {
4474
    readSuccessful = StorageImpl_ReadProperty(
4475 4476 4477 4478
                      This->parentStorage,
                      This->ownerPropertyIndex,
                      &chainProperty);

4479
    if (readSuccessful)
4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492
    {
      return chainProperty.startingBlock;
    }
  }

  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!
4493
 *
4494
 */
4495
static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4496 4497 4498 4499 4500 4501 4502 4503 4504 4505
{
  ULONG blockIndex;
  ULONG count = 0;

  blockIndex = BlockChainStream_GetHeadOfChain(This);

  while (blockIndex != BLOCK_END_OF_CHAIN)
  {
    count++;

4506
    if(FAILED(StorageImpl_GetNextBlockInChain(
4507
                   This->parentStorage,
4508 4509 4510
                   blockIndex,
		   &blockIndex)))
      return 0;
4511 4512 4513 4514 4515 4516
  }

  return count;
}

/******************************************************************************
4517
 *      BlockChainStream_ReadAt
4518 4519 4520 4521 4522
 *
 * 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.
 */
4523
HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4524 4525 4526 4527 4528
  ULARGE_INTEGER offset,
  ULONG          size,
  void*          buffer,
  ULONG*         bytesRead)
{
4529 4530
  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4531 4532 4533 4534 4535 4536 4537 4538
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  BYTE* bufferWalker;
  BYTE* bigBlockBuffer;

  /*
   * Find the first block in the stream that contains part of the buffer.
   */
4539 4540 4541 4542 4543 4544 4545 4546
  if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
       (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
       (blockNoInSequence < This->lastBlockNoInSequence) )
  {
    blockIndex = BlockChainStream_GetHeadOfChain(This);
    This->lastBlockNoInSequence = blockNoInSequence;
  }
  else
Thuy Nguyen's avatar
Thuy Nguyen committed
4547 4548 4549 4550 4551 4552 4553
  {
    ULONG temp = blockNoInSequence;

    blockIndex = This->lastBlockNoInSequenceIndex;
    blockNoInSequence -= This->lastBlockNoInSequence;
    This->lastBlockNoInSequence = temp;
  }
4554 4555 4556

  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
4557
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4558
      return STG_E_DOCFILECORRUPT;
4559 4560 4561
    blockNoInSequence--;
  }

4562
  if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4563
      return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4564

Thuy Nguyen's avatar
Thuy Nguyen committed
4565 4566
  This->lastBlockNoInSequenceIndex = blockIndex;

4567 4568 4569 4570 4571
  /*
   * Start reading the buffer.
   */
  *bytesRead   = 0;
  bufferWalker = buffer;
4572

4573 4574 4575 4576 4577
  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy from this big block.
     */
4578
    bytesToReadInBuffer =
4579
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4580

4581 4582 4583
    /*
     * Copy those bytes to the buffer
     */
4584
    bigBlockBuffer =
4585
      StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4586
    if (!bigBlockBuffer)
4587
        return STG_E_READFAULT;
4588

4589
    memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4590

4591
    StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4592

4593 4594 4595
    /*
     * Step to the next big block.
     */
4596
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4597
      return STG_E_DOCFILECORRUPT;
4598 4599 4600 4601 4602 4603 4604

    bufferWalker += bytesToReadInBuffer;
    size         -= bytesToReadInBuffer;
    *bytesRead   += bytesToReadInBuffer;
    offsetInBlock = 0;  /* There is no offset on the next block */

  }
4605

4606
  return (size == 0) ? S_OK : STG_E_READFAULT;
4607 4608 4609 4610 4611 4612 4613 4614 4615
}

/******************************************************************************
 *      BlockChainStream_WriteAt
 *
 * Writes the specified number of bytes to this chain at the specified offset.
 * bytesWritten may be NULL.
 * Will fail if not all specified number of bytes have been written.
 */
4616
HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4617 4618 4619 4620 4621
  ULARGE_INTEGER    offset,
  ULONG             size,
  const void*       buffer,
  ULONG*            bytesWritten)
{
4622 4623
  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4624 4625
  ULONG bytesToWrite;
  ULONG blockIndex;
Eric Pouech's avatar
Eric Pouech committed
4626
  const BYTE* bufferWalker;
4627 4628 4629 4630 4631
  BYTE* bigBlockBuffer;

  /*
   * Find the first block in the stream that contains part of the buffer.
   */
4632 4633 4634 4635 4636 4637 4638 4639
  if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
       (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
       (blockNoInSequence < This->lastBlockNoInSequence) )
  {
    blockIndex = BlockChainStream_GetHeadOfChain(This);
    This->lastBlockNoInSequence = blockNoInSequence;
  }
  else
Thuy Nguyen's avatar
Thuy Nguyen committed
4640 4641 4642 4643 4644 4645 4646 4647
  {
    ULONG temp = blockNoInSequence;

    blockIndex = This->lastBlockNoInSequenceIndex;
    blockNoInSequence -= This->lastBlockNoInSequence;
    This->lastBlockNoInSequence = temp;
  }

4648 4649
  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
4650 4651
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
					      &blockIndex)))
4652
      return STG_E_DOCFILECORRUPT;
4653 4654 4655
    blockNoInSequence--;
  }

Thuy Nguyen's avatar
Thuy Nguyen committed
4656 4657
  This->lastBlockNoInSequenceIndex = blockIndex;

4658 4659 4660 4661 4662 4663 4664 4665
  /* BlockChainStream_SetSize should have already been called to ensure we have
   * enough blocks in the chain to write into */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
    ERR("not enough blocks in chain to write data\n");
    return STG_E_DOCFILECORRUPT;
  }

4666 4667 4668 4669 4670
  /*
   * Here, I'm casting away the constness on the buffer variable
   * This is OK since we don't intend to modify that buffer.
   */
  *bytesWritten   = 0;
Eric Pouech's avatar
Eric Pouech committed
4671
  bufferWalker = (const BYTE*)buffer;
4672 4673 4674 4675 4676 4677

  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy from this big block.
     */
4678
    bytesToWrite =
4679
      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4680

4681 4682 4683
    /*
     * Copy those bytes to the buffer
     */
4684
    bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4685

4686
    memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4687

4688
    StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4689

4690 4691 4692
    /*
     * Step to the next big block.
     */
4693 4694
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
					      &blockIndex)))
4695
      return STG_E_DOCFILECORRUPT;
4696 4697 4698 4699 4700
    bufferWalker  += bytesToWrite;
    size          -= bytesToWrite;
    *bytesWritten += bytesToWrite;
    offsetInBlock  = 0;      /* There is no offset on the next block */
  }
4701

4702
  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4703 4704 4705 4706 4707 4708 4709
}

/******************************************************************************
 *      BlockChainStream_Shrink
 *
 * Shrinks this chain in the big block depot.
 */
4710 4711
static BOOL BlockChainStream_Shrink(BlockChainStream* This,
                                    ULARGE_INTEGER    newSize)
4712 4713 4714 4715 4716
{
  ULONG blockIndex, extraBlock;
  ULONG numBlocks;
  ULONG count = 1;

4717 4718 4719 4720 4721 4722
  /*
   * Reset the last accessed block cache.
   */
  This->lastBlockNoInSequence = 0xFFFFFFFF;
  This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;

4723 4724 4725
  /*
   * Figure out how many blocks are needed to contain the new size
   */
4726
  numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4727

4728
  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4729 4730 4731 4732 4733 4734 4735 4736 4737
    numBlocks++;

  blockIndex = BlockChainStream_GetHeadOfChain(This);

  /*
   * Go to the new end of chain
   */
  while (count < numBlocks)
  {
4738 4739 4740
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
					      &blockIndex)))
      return FALSE;
4741 4742 4743 4744
    count++;
  }

  /* Get the next block before marking the new end */
4745 4746 4747
  if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
					    &extraBlock)))
    return FALSE;
4748 4749

  /* Mark the new end of chain */
4750
  StorageImpl_SetNextBlockInChain(
4751 4752
    This->parentStorage,
    blockIndex,
4753 4754
    BLOCK_END_OF_CHAIN);

Thuy Nguyen's avatar
Thuy Nguyen committed
4755 4756 4757
  This->tailIndex = blockIndex;
  This->numBlocks = numBlocks;

4758 4759 4760 4761 4762
  /*
   * Mark the extra blocks as free
   */
  while (extraBlock != BLOCK_END_OF_CHAIN)
  {
4763 4764 4765
    if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
					      &blockIndex)))
      return FALSE;
4766
    StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777
    extraBlock = blockIndex;
  }

  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_Enlarge
 *
 * Grows this chain in the big block depot.
 */
4778 4779
static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
                                     ULARGE_INTEGER    newSize)
4780 4781 4782 4783 4784 4785 4786 4787
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = BlockChainStream_GetHeadOfChain(This);

  /*
4788
   * Empty chain. Create the head.
4789 4790 4791
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
4792 4793
    blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
    StorageImpl_SetNextBlockInChain(This->parentStorage,
4794 4795 4796 4797 4798 4799 4800 4801 4802
                                      blockIndex,
                                      BLOCK_END_OF_CHAIN);

    if (This->headOfStreamPlaceHolder != 0)
    {
      *(This->headOfStreamPlaceHolder) = blockIndex;
    }
    else
    {
Thuy Nguyen's avatar
Thuy Nguyen committed
4803 4804
      StgProperty chainProp;
      assert(This->ownerPropertyIndex != PROPERTY_NULL);
4805

Thuy Nguyen's avatar
Thuy Nguyen committed
4806
      StorageImpl_ReadProperty(
4807
        This->parentStorage,
Thuy Nguyen's avatar
Thuy Nguyen committed
4808 4809
        This->ownerPropertyIndex,
        &chainProp);
4810

4811
      chainProp.startingBlock = blockIndex;
4812

Thuy Nguyen's avatar
Thuy Nguyen committed
4813
      StorageImpl_WriteProperty(
4814
        This->parentStorage,
Thuy Nguyen's avatar
Thuy Nguyen committed
4815 4816 4817
        This->ownerPropertyIndex,
        &chainProp);
    }
4818

Thuy Nguyen's avatar
Thuy Nguyen committed
4819 4820 4821
    This->tailIndex = blockIndex;
    This->numBlocks = 1;
  }
4822 4823 4824 4825

  /*
   * Figure out how many blocks are needed to contain this stream
   */
4826
  newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4827

4828
  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4829 4830 4831 4832 4833
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
Thuy Nguyen's avatar
Thuy Nguyen committed
4834
  if (This->tailIndex == BLOCK_END_OF_CHAIN)
4835 4836 4837
  {
    currentBlock = blockIndex;

Thuy Nguyen's avatar
Thuy Nguyen committed
4838 4839 4840 4841 4842
    while (blockIndex != BLOCK_END_OF_CHAIN)
    {
      This->numBlocks++;
      currentBlock = blockIndex;

4843 4844 4845
      if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
						&blockIndex)))
	return FALSE;
Thuy Nguyen's avatar
Thuy Nguyen committed
4846 4847 4848
    }

    This->tailIndex = currentBlock;
4849 4850
  }

Thuy Nguyen's avatar
Thuy Nguyen committed
4851 4852 4853
  currentBlock = This->tailIndex;
  oldNumBlocks = This->numBlocks;

4854 4855 4856
  /*
   * Add new blocks to the chain
   */
4857
  if (oldNumBlocks < newNumBlocks)
4858
  {
4859 4860 4861
    while (oldNumBlocks < newNumBlocks)
    {
      blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4862

4863
      StorageImpl_SetNextBlockInChain(
4864 4865
	This->parentStorage,
	currentBlock,
4866
	blockIndex);
4867

4868
      StorageImpl_SetNextBlockInChain(
4869 4870
        This->parentStorage,
	blockIndex,
4871
	BLOCK_END_OF_CHAIN);
4872

4873 4874 4875
      currentBlock = blockIndex;
      oldNumBlocks++;
    }
4876

4877 4878 4879
    This->tailIndex = blockIndex;
    This->numBlocks = newNumBlocks;
  }
Thuy Nguyen's avatar
Thuy Nguyen committed
4880

4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891
  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
4892
 *       doesn't shrink even if we shrink streams.
4893
 */
4894
BOOL BlockChainStream_SetSize(
4895 4896 4897 4898 4899
  BlockChainStream* This,
  ULARGE_INTEGER    newSize)
{
  ULARGE_INTEGER size = BlockChainStream_GetSize(This);

4900
  if (newSize.u.LowPart == size.u.LowPart)
4901 4902
    return TRUE;

4903
  if (newSize.u.LowPart < size.u.LowPart)
4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920
  {
    BlockChainStream_Shrink(This, newSize);
  }
  else
  {
    BlockChainStream_Enlarge(This, newSize);
  }

  return TRUE;
}

/******************************************************************************
 *      BlockChainStream_GetSize
 *
 * Returns the size of this chain.
 * Will return the block count if this chain doesn't have a property.
 */
4921
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4922 4923 4924 4925 4926
{
  StgProperty chainProperty;

  if(This->headOfStreamPlaceHolder == NULL)
  {
4927 4928
    /*
     * This chain is a data stream read the property and return
4929 4930
     * the appropriate size
     */
4931
    StorageImpl_ReadProperty(
4932 4933 4934 4935 4936 4937 4938 4939 4940
      This->parentStorage,
      This->ownerPropertyIndex,
      &chainProperty);

    return chainProperty.size;
  }
  else
  {
    /*
4941 4942
     * this chain is a chain that does not have a property, figure out the
     * size by making the product number of used blocks times the
4943 4944 4945
     * size of them
     */
    ULARGE_INTEGER result;
4946
    result.u.HighPart = 0;
4947

4948
    result.u.LowPart  =
4949
      BlockChainStream_GetCount(This) *
4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960
      This->parentStorage->bigBlockSize;

    return result;
  }
}

/******************************************************************************
** SmallBlockChainStream implementation
*/

SmallBlockChainStream* SmallBlockChainStream_Construct(
4961
  StorageImpl* parentStorage,
4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984
  ULONG          propertyIndex)
{
  SmallBlockChainStream* newStream;

  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));

  newStream->parentStorage      = parentStorage;
  newStream->ownerPropertyIndex = propertyIndex;

  return newStream;
}

void SmallBlockChainStream_Destroy(
  SmallBlockChainStream* This)
{
  HeapFree(GetProcessHeap(), 0, This);
}

/******************************************************************************
 *      SmallBlockChainStream_GetHeadOfChain
 *
 * Returns the head of this chain of small blocks.
 */
4985
static ULONG SmallBlockChainStream_GetHeadOfChain(
4986 4987 4988
  SmallBlockChainStream* This)
{
  StgProperty chainProperty;
4989
  BOOL      readSuccessful;
4990 4991 4992

  if (This->ownerPropertyIndex)
  {
4993
    readSuccessful = StorageImpl_ReadProperty(
4994 4995 4996 4997
                      This->parentStorage,
                      This->ownerPropertyIndex,
                      &chainProperty);

4998
    if (readSuccessful)
4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011
    {
      return chainProperty.startingBlock;
    }

  }

  return BLOCK_END_OF_CHAIN;
}

/******************************************************************************
 *      SmallBlockChainStream_GetNextBlockInChain
 *
 * Returns the index of the next small block in this chain.
5012
 *
5013 5014 5015 5016
 * Return Values:
 *    - BLOCK_END_OF_CHAIN: end of this chain
 *    - BLOCK_UNUSED: small block 'blockIndex' is free
 */
5017
static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5018
  SmallBlockChainStream* This,
5019 5020
  ULONG                  blockIndex,
  ULONG*                 nextBlockInChain)
5021 5022 5023 5024
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesRead;
5025
  HRESULT res;
5026

5027 5028
  *nextBlockInChain = BLOCK_END_OF_CHAIN;

5029 5030
  offsetOfBlockInDepot.u.HighPart = 0;
  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
5031 5032 5033 5034

  /*
   * Read those bytes in the buffer from the small block file.
   */
5035
  res = BlockChainStream_ReadAt(
5036 5037 5038 5039 5040 5041
              This->parentStorage->smallBlockDepotChain,
              offsetOfBlockInDepot,
              sizeof(DWORD),
              &buffer,
              &bytesRead);

5042
  if (SUCCEEDED(res))
5043
  {
5044
    StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5045
    return S_OK;
5046 5047
  }

5048
  return res;
5049 5050 5051 5052 5053 5054 5055 5056 5057 5058
}

/******************************************************************************
 *       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.
 */
5059
static void SmallBlockChainStream_SetNextBlockInChain(
5060 5061 5062 5063 5064 5065 5066 5067
  SmallBlockChainStream* This,
  ULONG                  blockIndex,
  ULONG                  nextBlock)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD  buffer;
  ULONG  bytesWritten;

5068 5069
  offsetOfBlockInDepot.u.HighPart = 0;
  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
5070

5071
  StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088

  /*
   * 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.
 */
5089
static void SmallBlockChainStream_FreeBlock(
5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102
  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.
 */
5103
static ULONG SmallBlockChainStream_GetNextFreeBlock(
5104 5105 5106 5107 5108 5109 5110
  SmallBlockChainStream* This)
{
  ULARGE_INTEGER offsetOfBlockInDepot;
  DWORD buffer;
  ULONG bytesRead;
  ULONG blockIndex = 0;
  ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5111
  HRESULT res = S_OK;
5112 5113
  ULONG smallBlocksPerBigBlock;

5114
  offsetOfBlockInDepot.u.HighPart = 0;
5115 5116 5117 5118 5119 5120

  /*
   * Scan the small block depot for a free block
   */
  while (nextBlockIndex != BLOCK_UNUSED)
  {
5121
    offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5122

5123
    res = BlockChainStream_ReadAt(
5124 5125 5126 5127 5128 5129 5130 5131 5132
                This->parentStorage->smallBlockDepotChain,
                offsetOfBlockInDepot,
                sizeof(DWORD),
                &buffer,
                &bytesRead);

    /*
     * If we run out of space for the small block depot, enlarge it
     */
5133
    if (SUCCEEDED(res))
5134
    {
5135
      StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5136 5137 5138 5139 5140 5141

      if (nextBlockIndex != BLOCK_UNUSED)
        blockIndex++;
    }
    else
    {
5142
      ULONG count =
5143 5144 5145 5146 5147 5148 5149 5150 5151 5152
        BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);

      ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
      ULONG nextBlock, newsbdIndex;
      BYTE* smallBlockDepot;

      nextBlock = sbdIndex;
      while (nextBlock != BLOCK_END_OF_CHAIN)
      {
        sbdIndex = nextBlock;
5153
	StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5154 5155
      }

5156
      newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5157
      if (sbdIndex != BLOCK_END_OF_CHAIN)
5158
        StorageImpl_SetNextBlockInChain(
5159 5160
          This->parentStorage,
          sbdIndex,
5161 5162
          newsbdIndex);

5163
      StorageImpl_SetNextBlockInChain(
5164 5165
        This->parentStorage,
        newsbdIndex,
5166 5167 5168 5169 5170
        BLOCK_END_OF_CHAIN);

      /*
       * Initialize all the small blocks to free
       */
5171
      smallBlockDepot =
5172
        StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5173 5174

      memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5175
      StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5176 5177 5178 5179 5180 5181 5182

      if (count == 0)
      {
        /*
         * We have just created the small block depot.
         */
        StgProperty rootProp;
5183
        ULONG sbStartIndex;
5184 5185 5186 5187 5188

        /*
         * Save it in the header
         */
        This->parentStorage->smallBlockDepotStart = newsbdIndex;
5189
        StorageImpl_SaveFileHeader(This->parentStorage);
5190 5191

        /*
5192
         * And allocate the first big block that will contain small blocks
5193
         */
5194
        sbStartIndex =
5195
          StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5196

5197
        StorageImpl_SetNextBlockInChain(
5198 5199
          This->parentStorage,
          sbStartIndex,
5200 5201
          BLOCK_END_OF_CHAIN);

5202
        StorageImpl_ReadProperty(
5203
          This->parentStorage,
5204
          This->parentStorage->base.rootPropertySetIndex,
5205 5206 5207
          &rootProp);

        rootProp.startingBlock = sbStartIndex;
5208 5209
        rootProp.size.u.HighPart = 0;
        rootProp.size.u.LowPart  = This->parentStorage->bigBlockSize;
5210

5211
        StorageImpl_WriteProperty(
5212
          This->parentStorage,
5213
          This->parentStorage->base.rootPropertySetIndex,
5214 5215 5216 5217 5218
          &rootProp);
      }
    }
  }

5219
  smallBlocksPerBigBlock =
5220 5221 5222 5223 5224 5225 5226 5227 5228 5229
    This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;

  /*
   * Verify if we have to allocate big blocks to contain small blocks
   */
  if (blockIndex % smallBlocksPerBigBlock == 0)
  {
    StgProperty rootProp;
    ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;

5230
    StorageImpl_ReadProperty(
5231
      This->parentStorage,
5232
      This->parentStorage->base.rootPropertySetIndex,
5233 5234
      &rootProp);

5235
    if (rootProp.size.u.LowPart <
5236 5237
       (blocksRequired * This->parentStorage->bigBlockSize))
    {
5238
      rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5239 5240

      BlockChainStream_SetSize(
5241
        This->parentStorage->smallBlockRootChain,
5242 5243
        rootProp.size);

5244
      StorageImpl_WriteProperty(
5245
        This->parentStorage,
5246
        This->parentStorage->base.rootPropertySetIndex,
5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258
        &rootProp);
    }
  }

  return blockIndex;
}

/******************************************************************************
 *      SmallBlockChainStream_ReadAt
 *
 * Reads a specified number of bytes from this chain at the specified offset.
 * bytesRead may be NULL.
5259
 * Failure will be returned if the specified number of bytes has not been read.
5260
 */
5261
HRESULT SmallBlockChainStream_ReadAt(
5262 5263 5264 5265 5266 5267
  SmallBlockChainStream* This,
  ULARGE_INTEGER         offset,
  ULONG                  size,
  void*                  buffer,
  ULONG*                 bytesRead)
{
5268
  HRESULT rc = S_OK;
5269
  ULARGE_INTEGER offsetInBigBlockFile;
5270
  ULONG blockNoInSequence =
5271
    offset.u.LowPart / This->parentStorage->smallBlockSize;
5272

5273
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5274 5275 5276 5277 5278 5279 5280 5281
  ULONG bytesToReadInBuffer;
  ULONG blockIndex;
  ULONG bytesReadFromBigBlockFile;
  BYTE* bufferWalker;

  /*
   * This should never happen on a small block file.
   */
5282
  assert(offset.u.HighPart==0);
5283 5284 5285 5286 5287 5288 5289 5290

  /*
   * 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))
  {
5291 5292 5293
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
      return rc;
5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307
    blockNoInSequence--;
  }

  /*
   * Start reading the buffer.
   */
  *bytesRead   = 0;
  bufferWalker = buffer;

  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy from this small block.
     */
5308
    bytesToReadInBuffer =
5309
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5310 5311 5312 5313

    /*
     * Calculate the offset of the small block in the small block file.
     */
5314 5315
    offsetInBigBlockFile.u.HighPart  = 0;
    offsetInBigBlockFile.u.LowPart   =
5316 5317
      blockIndex * This->parentStorage->smallBlockSize;

5318
    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5319 5320 5321

    /*
     * Read those bytes in the buffer from the small block file.
5322 5323
     * The small block has already been identified so it shouldn't fail
     * unless the file is corrupt.
5324
     */
5325
    rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5326 5327 5328
      offsetInBigBlockFile,
      bytesToReadInBuffer,
      bufferWalker,
5329 5330 5331 5332
      &bytesReadFromBigBlockFile);

    if (FAILED(rc))
      return rc;
5333 5334 5335 5336

    /*
     * Step to the next big block.
     */
5337 5338
    rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
    if(FAILED(rc))
5339
      return STG_E_DOCFILECORRUPT;
5340

5341 5342 5343 5344
    bufferWalker += bytesReadFromBigBlockFile;
    size         -= bytesReadFromBigBlockFile;
    *bytesRead   += bytesReadFromBigBlockFile;
    offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5345 5346
  }

5347
  return (size == 0) ? S_OK : STG_E_READFAULT;
5348 5349 5350 5351 5352 5353 5354 5355 5356
}

/******************************************************************************
 *       SmallBlockChainStream_WriteAt
 *
 * Writes the specified number of bytes to this chain at the specified offset.
 * bytesWritten may be NULL.
 * Will fail if not all specified number of bytes have been written.
 */
5357
HRESULT SmallBlockChainStream_WriteAt(
5358 5359 5360 5361 5362 5363 5364
  SmallBlockChainStream* This,
  ULARGE_INTEGER offset,
  ULONG          size,
  const void*    buffer,
  ULONG*         bytesWritten)
{
  ULARGE_INTEGER offsetInBigBlockFile;
5365
  ULONG blockNoInSequence =
5366
    offset.u.LowPart / This->parentStorage->smallBlockSize;
5367

5368
  ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5369 5370
  ULONG bytesToWriteInBuffer;
  ULONG blockIndex;
5371
  ULONG bytesWrittenToBigBlockFile;
Eric Pouech's avatar
Eric Pouech committed
5372
  const BYTE* bufferWalker;
5373
  HRESULT res;
5374

5375 5376 5377
  /*
   * This should never happen on a small block file.
   */
5378
  assert(offset.u.HighPart==0);
5379

5380 5381 5382 5383
  /*
   * Find the first block in the stream that contains part of the buffer.
   */
  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5384

5385 5386
  while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
  {
5387
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5388
      return STG_E_DOCFILECORRUPT;
5389 5390
    blockNoInSequence--;
  }
5391

5392 5393 5394 5395 5396 5397 5398
  /*
   * Start writing the buffer.
   *
   * Here, I'm casting away the constness on the buffer variable
   * This is OK since we don't intend to modify that buffer.
   */
  *bytesWritten   = 0;
Eric Pouech's avatar
Eric Pouech committed
5399
  bufferWalker = (const BYTE*)buffer;
5400 5401 5402 5403 5404
  while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
  {
    /*
     * Calculate how many bytes we can copy to this small block.
     */
5405
    bytesToWriteInBuffer =
5406
      min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5407

5408 5409 5410
    /*
     * Calculate the offset of the small block in the small block file.
     */
5411 5412
    offsetInBigBlockFile.u.HighPart  = 0;
    offsetInBigBlockFile.u.LowPart   =
5413 5414
      blockIndex * This->parentStorage->smallBlockSize;

5415
    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5416

5417 5418 5419
    /*
     * Write those bytes in the buffer to the small block file.
     */
5420 5421
    res = BlockChainStream_WriteAt(
      This->parentStorage->smallBlockRootChain,
5422 5423 5424
      offsetInBigBlockFile,
      bytesToWriteInBuffer,
      bufferWalker,
5425
      &bytesWrittenToBigBlockFile);
5426 5427
    if (FAILED(res))
      return res;
5428

5429 5430 5431
    /*
     * Step to the next big block.
     */
5432 5433 5434
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&blockIndex)))
      return FALSE;
5435 5436 5437 5438
    bufferWalker  += bytesWrittenToBigBlockFile;
    size          -= bytesWrittenToBigBlockFile;
    *bytesWritten += bytesWrittenToBigBlockFile;
    offsetInBlock  = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5439
  }
5440

5441
  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5442 5443 5444 5445 5446
}

/******************************************************************************
 *       SmallBlockChainStream_Shrink
 *
5447
 * Shrinks this chain in the small block depot.
5448
 */
5449
static BOOL SmallBlockChainStream_Shrink(
5450 5451 5452 5453 5454
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, extraBlock;
  ULONG numBlocks;
5455
  ULONG count = 0;
5456

5457
  numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5458

5459
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5460 5461 5462 5463 5464 5465 5466 5467 5468
    numBlocks++;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
   * Go to the new end of chain
   */
  while (count < numBlocks)
  {
5469 5470 5471
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&blockIndex)))
      return FALSE;
5472 5473 5474
    count++;
  }

5475 5476
  /*
   * If the count is 0, we have a special case, the head of the chain was
5477
   * just freed.
5478 5479 5480 5481
   */
  if (count == 0)
  {
    StgProperty chainProp;
5482

5483
    StorageImpl_ReadProperty(This->parentStorage,
5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500
			     This->ownerPropertyIndex,
			     &chainProp);

    chainProp.startingBlock = BLOCK_END_OF_CHAIN;

    StorageImpl_WriteProperty(This->parentStorage,
			      This->ownerPropertyIndex,
			      &chainProp);

    /*
     * We start freeing the chain at the head block.
     */
    extraBlock = blockIndex;
  }
  else
  {
    /* Get the next block before marking the new end */
5501 5502 5503
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
							&extraBlock)))
      return FALSE;
5504 5505 5506

    /* Mark the new end of chain */
    SmallBlockChainStream_SetNextBlockInChain(
5507 5508
      This,
      blockIndex,
5509 5510
      BLOCK_END_OF_CHAIN);
  }
5511 5512 5513 5514 5515 5516

  /*
   * Mark the extra blocks as free
   */
  while (extraBlock != BLOCK_END_OF_CHAIN)
  {
5517 5518 5519
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
							&blockIndex)))
      return FALSE;
5520 5521 5522 5523
    SmallBlockChainStream_FreeBlock(This, extraBlock);
    extraBlock = blockIndex;
  }

5524
  return TRUE;
5525 5526 5527 5528 5529 5530 5531
}

/******************************************************************************
 *      SmallBlockChainStream_Enlarge
 *
 * Grows this chain in the small block depot.
 */
5532
static BOOL SmallBlockChainStream_Enlarge(
5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546
  SmallBlockChainStream* This,
  ULARGE_INTEGER newSize)
{
  ULONG blockIndex, currentBlock;
  ULONG newNumBlocks;
  ULONG oldNumBlocks = 0;

  blockIndex = SmallBlockChainStream_GetHeadOfChain(This);

  /*
   * Empty chain
   */
  if (blockIndex == BLOCK_END_OF_CHAIN)
  {
5547

5548 5549
    StgProperty chainProp;

5550
    StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5551 5552 5553 5554
                               &chainProp);

    chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);

5555
    StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5556 5557 5558 5559
                                &chainProp);

    blockIndex = chainProp.startingBlock;
    SmallBlockChainStream_SetNextBlockInChain(
5560 5561
      This,
      blockIndex,
5562 5563 5564 5565 5566 5567 5568 5569
      BLOCK_END_OF_CHAIN);
  }

  currentBlock = blockIndex;

  /*
   * Figure out how many blocks are needed to contain this stream
   */
5570
  newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5571

5572
  if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5573 5574 5575 5576 5577 5578 5579 5580 5581
    newNumBlocks++;

  /*
   * Go to the current end of chain
   */
  while (blockIndex != BLOCK_END_OF_CHAIN)
  {
    oldNumBlocks++;
    currentBlock = blockIndex;
5582 5583
    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
      return FALSE;
5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594
  }

  /*
   * Add new blocks to the chain
   */
  while (oldNumBlocks < newNumBlocks)
  {
    blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
    SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);

    SmallBlockChainStream_SetNextBlockInChain(
5595 5596
      This,
      blockIndex,
5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613
      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
5614
 *       doesn't shrink even if we shrink streams.
5615
 */
5616
BOOL SmallBlockChainStream_SetSize(
5617 5618 5619 5620 5621
                SmallBlockChainStream* This,
                ULARGE_INTEGER    newSize)
{
  ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);

5622
  if (newSize.u.LowPart == size.u.LowPart)
5623 5624
    return TRUE;

5625
  if (newSize.u.LowPart < size.u.LowPart)
5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641
  {
    SmallBlockChainStream_Shrink(This, newSize);
  }
  else
  {
    SmallBlockChainStream_Enlarge(This, newSize);
  }

  return TRUE;
}

/******************************************************************************
 *      SmallBlockChainStream_GetSize
 *
 * Returns the size of this chain.
 */
5642
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5643 5644 5645
{
  StgProperty chainProperty;

5646
  StorageImpl_ReadProperty(
5647 5648 5649 5650 5651 5652 5653 5654
    This->parentStorage,
    This->ownerPropertyIndex,
    &chainProperty);

  return chainProperty.size;
}

/******************************************************************************
5655
 *    StgCreateDocfile  [OLE32.@]
5656 5657 5658 5659 5660 5661 5662 5663 5664
 * 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
5665
 *  S_OK if the file was successfully created
5666 5667 5668 5669 5670 5671
 *  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)
5672
 */
5673 5674
HRESULT WINAPI StgCreateDocfile(
  LPCOLESTR pwcsName,
5675 5676
  DWORD       grfMode,
  DWORD       reserved,
5677
  IStorage  **ppstgOpen)
5678
{
5679 5680
  StorageImpl* newStorage = 0;
  HANDLE       hFile      = INVALID_HANDLE_VALUE;
5681
  HRESULT        hr         = STG_E_INVALIDFLAG;
5682 5683 5684 5685
  DWORD          shareMode;
  DWORD          accessMode;
  DWORD          creationMode;
  DWORD          fileAttributes;
5686
  WCHAR          tempFileName[MAX_PATH];
5687

5688
  TRACE("(%s, %x, %d, %p)\n",
5689
	debugstr_w(pwcsName), grfMode,
5690 5691
	reserved, ppstgOpen);

5692 5693 5694
  /*
   * Validate the parameters
   */
5695
  if (ppstgOpen == 0)
5696
    return STG_E_INVALIDPOINTER;
5697 5698
  if (reserved != 0)
    return STG_E_INVALIDPARAMETER;
5699

5700 5701 5702 5703
  /*
   * Validate the STGM flags
   */
  if ( FAILED( validateSTGM(grfMode) ))
5704
    goto end;
5705

5706
  /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5707 5708 5709 5710 5711 5712
  switch(STGM_ACCESS_MODE(grfMode))
  {
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
5713
    goto end;
5714
  }
5715

5716 5717 5718
  /* if no share mode given then DENY_NONE is the default */     
  if (STGM_SHARE_MODE(grfMode) == 0)
    grfMode |= STGM_SHARE_DENY_NONE;
5719

5720 5721 5722 5723 5724 5725
  /* must have at least one access mode */
  if (STGM_ACCESS_MODE(grfMode) == 0)
    goto end;
  
  /* in direct mode, can only use SHARE_EXCLUSIVE */
  if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5726
    goto end;
5727

5728 5729
  /* but in transacted mode, any share mode is valid */

5730 5731 5732 5733 5734 5735
  /*
   * Generate a unique name.
   */
  if (pwcsName == 0)
  {
    WCHAR tempPath[MAX_PATH];
5736
    static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5737 5738 5739 5740 5741 5742 5743 5744 5745 5746

    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
5747 5748 5749 5750
    {
      hr = STG_E_INSUFFICIENTMEMORY;
      goto end;
    }
5751 5752 5753 5754 5755 5756

    creationMode = TRUNCATE_EXISTING;
  }
  else
  {
    creationMode = GetCreationModeFromSTGM(grfMode);
5757 5758
  }

5759
  /*
5760
   * Interpret the STGM value grfMode
5761 5762 5763 5764 5765 5766 5767 5768 5769 5770
   */
  shareMode    = GetShareModeFromSTGM(grfMode);
  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;

  if (grfMode & STGM_TRANSACTED)
5771
    FIXME("Transacted mode not implemented.\n");
5772

5773 5774 5775 5776 5777
  /*
   * Initialize the "out" parameter.
   */
  *ppstgOpen = 0;

5778
  hFile = CreateFileW(pwcsName,
5779 5780
                        accessMode,
                        shareMode,
5781
                        NULL,
5782 5783
                        creationMode,
                        fileAttributes,
5784
                        0);
5785

5786
  if (hFile == INVALID_HANDLE_VALUE)
5787
  {
5788
    if(GetLastError() == ERROR_FILE_EXISTS)
5789 5790 5791 5792
      hr = STG_E_FILEALREADYEXISTS;
    else
      hr = E_FAIL;
    goto end;
5793 5794 5795 5796 5797
  }

  /*
   * Allocate and initialize the new IStorage32object.
   */
5798
  newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5799

5800
  if (newStorage == 0)
5801 5802 5803 5804
  {
    hr = STG_E_INSUFFICIENTMEMORY;
    goto end;
  }
5805

5806
  hr = StorageImpl_Construct(
5807 5808
         newStorage,
         hFile,
5809
        pwcsName,
5810 5811
         NULL,
         grfMode,
5812
         TRUE,
5813
         TRUE);
5814

5815
  if (FAILED(hr))
5816 5817
  {
    HeapFree(GetProcessHeap(), 0, newStorage);
5818
    goto end;
5819
  }
5820 5821 5822 5823

  /*
   * Get an "out" pointer for the caller.
   */
5824 5825
  hr = StorageBaseImpl_QueryInterface(
         (IStorage*)newStorage,
5826
         (REFIID)&IID_IStorage,
5827
         (void**)ppstgOpen);
5828
end:
5829
  TRACE("<-- %p  r = %08x\n", *ppstgOpen, hr);
5830 5831 5832 5833

  return hr;
}

5834 5835 5836
/******************************************************************************
 *              StgCreateStorageEx        [OLE32.@]
 */
5837 5838
HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
5839
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5840
          grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5841 5842 5843 5844 5845 5846 5847

    if (stgfmt != STGFMT_FILE && grfAttrs != 0)
    {
        ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
        return STG_E_INVALIDPARAMETER;  
    }

5848
    if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867
    {
        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)
    {
        FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
        return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen); 
    }

    ERR("Invalid stgfmt argument\n");
    return STG_E_INVALIDPARAMETER;
5868 5869
}

5870 5871 5872 5873 5874 5875 5876 5877
/******************************************************************************
 *              StgCreatePropSetStg       [OLE32.@]
 */
HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
 IPropertySetStorage **ppPropSetStg)
{
    HRESULT hr;

5878
    TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5879 5880 5881 5882 5883 5884 5885 5886
    if (reserved)
        hr = STG_E_INVALIDPARAMETER;
    else
        hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
         (void**)ppPropSetStg);
    return hr;
}

5887 5888 5889 5890 5891
/******************************************************************************
 *              StgOpenStorageEx      [OLE32.@]
 */
HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
5892
    TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5893 5894 5895 5896 5897 5898 5899 5900
          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;  
    }

5901
    switch (stgfmt)
5902
    {
5903
    case STGFMT_FILE:
5904 5905
        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
        return STG_E_INVALIDPARAMETER;
5906 5907 5908
        
    case STGFMT_STORAGE:
        break;
5909

5910 5911 5912 5913 5914 5915
    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;  
        }
5916
        FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5917 5918 5919 5920 5921 5922 5923 5924
        break;

    case STGFMT_ANY:
        WARN("STGFMT_ANY assuming storage\n");
        break;

    default:
        return STG_E_INVALIDPARAMETER;
5925 5926
    }

5927
    return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen); 
5928 5929 5930
}


5931
/******************************************************************************
5932
 *              StgOpenStorage        [OLE32.@]
5933
 */
5934 5935 5936
HRESULT WINAPI StgOpenStorage(
  const OLECHAR *pwcsName,
  IStorage      *pstgPriority,
5937 5938 5939 5940
  DWORD          grfMode,
  SNB            snbExclude,
  DWORD          reserved,
  IStorage     **ppstgOpen)
5941
{
5942
  StorageImpl*   newStorage = 0;
5943
  HRESULT        hr = S_OK;
5944
  HANDLE         hFile = 0;
5945 5946
  DWORD          shareMode;
  DWORD          accessMode;
Alexandre Julliard's avatar
Alexandre Julliard committed
5947
  WCHAR          fullname[MAX_PATH];
5948
  BOOL           newFile;
5949

5950
  TRACE("(%s, %p, %x, %p, %d, %p)\n",
5951 5952 5953
	debugstr_w(pwcsName), pstgPriority, grfMode,
	snbExclude, reserved, ppstgOpen);

5954
  /*
5955
   * Perform sanity checks
5956
   */
5957 5958 5959 5960 5961 5962 5963
  if (pwcsName == 0)
  {
    hr = STG_E_INVALIDNAME;
    goto end;
  }

  if (ppstgOpen == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
5964 5965 5966 5967
  {
    hr = STG_E_INVALIDPOINTER;
    goto end;
  }
5968

5969 5970 5971 5972 5973 5974
  if (reserved)
  {
    hr = STG_E_INVALIDPARAMETER;
    goto end;
  }

5975 5976
  if (grfMode & STGM_PRIORITY)
  {
5977 5978 5979 5980 5981 5982
    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;
5983
    grfMode &= ~0xf0; /* remove the existing sharing mode */
5984 5985 5986 5987 5988 5989 5990 5991
    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");
5992 5993
  }

5994 5995 5996
  /*
   * Validate the sharing mode
   */
5997
  if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5998 5999 6000 6001 6002 6003 6004 6005 6006
    switch(STGM_SHARE_MODE(grfMode))
    {
      case STGM_SHARE_EXCLUSIVE:
      case STGM_SHARE_DENY_WRITE:
        break;
      default:
        hr = STG_E_INVALIDFLAG;
        goto end;
    }
6007

6008 6009 6010
  /*
   * Validate the STGM flags
   */
6011 6012
  if ( FAILED( validateSTGM(grfMode) ) ||
       (grfMode&STGM_CREATE))
Alexandre Julliard's avatar
Alexandre Julliard committed
6013 6014 6015 6016
  {
    hr = STG_E_INVALIDFLAG;
    goto end;
  }
6017

6018 6019 6020 6021 6022 6023 6024 6025 6026
  /* 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;
  }

6027 6028 6029 6030 6031 6032
  /*
   * Interpret the STGM value grfMode
   */
  shareMode    = GetShareModeFromSTGM(grfMode);
  accessMode   = GetAccessModeFromSTGM(grfMode);

6033 6034 6035 6036
  /*
   * Initialize the "out" parameter.
   */
  *ppstgOpen = 0;
6037

6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055
  if ((accessMode & GENERIC_WRITE) && /* try to create a file if no yet exists */
      ((hFile = CreateFileW( pwcsName, accessMode, shareMode, NULL, CREATE_NEW,
                             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0))
          != INVALID_HANDLE_VALUE))
  {
      newFile = TRUE;
  }
  else
  {
      newFile = FALSE;
      hFile = CreateFileW( pwcsName,
                           accessMode,
                           shareMode,
                           NULL,
                           OPEN_EXISTING,
                           FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
                           0);
  }
6056

6057
  if (hFile==INVALID_HANDLE_VALUE)
6058
  {
6059 6060
    DWORD last_error = GetLastError();

Alexandre Julliard's avatar
Alexandre Julliard committed
6061 6062
    hr = E_FAIL;

6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085
    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
6086
    goto end;
6087 6088
  }

6089 6090 6091 6092
  /*
   * 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
   */
6093
  if (!newFile && GetFileSize(hFile, NULL) < 0x100)
6094 6095 6096 6097 6098
  {
    CloseHandle(hFile);
    hr = STG_E_FILEALREADYEXISTS;
    goto end;
  }
6099

6100 6101 6102
  /*
   * Allocate and initialize the new IStorage32object.
   */
6103
  newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6104

6105
  if (newStorage == 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
6106 6107 6108 6109
  {
    hr = STG_E_INSUFFICIENTMEMORY;
    goto end;
  }
6110

6111
  /* if we created new file, initialize the storage */
6112
  hr = StorageImpl_Construct(
6113 6114
         newStorage,
         hFile,
6115
         pwcsName,
6116 6117
         NULL,
         grfMode,
6118
         TRUE,
6119
         newFile );
6120

6121
  if (FAILED(hr))
6122 6123
  {
    HeapFree(GetProcessHeap(), 0, newStorage);
6124 6125 6126 6127
    /*
     * 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
6128 6129
	hr = STG_E_FILEALREADYEXISTS;
    goto end;
6130
  }
6131

Alexandre Julliard's avatar
Alexandre Julliard committed
6132 6133 6134
  /* prepare the file name string given in lieu of the root property name */
  GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
  memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6135
  newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
6136

6137 6138 6139
  /*
   * Get an "out" pointer for the caller.
   */
6140 6141
  hr = StorageBaseImpl_QueryInterface(
         (IStorage*)newStorage,
6142
         (REFIID)&IID_IStorage,
6143
         (void**)ppstgOpen);
6144

Alexandre Julliard's avatar
Alexandre Julliard committed
6145
end:
6146
  TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6147 6148 6149 6150
  return hr;
}

/******************************************************************************
6151
 *    StgCreateDocfileOnILockBytes    [OLE32.@]
6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178
 */
HRESULT WINAPI StgCreateDocfileOnILockBytes(
      ILockBytes *plkbyt,
      DWORD grfMode,
      DWORD reserved,
      IStorage** ppstgOpen)
{
  StorageImpl*   newStorage = 0;
  HRESULT        hr         = S_OK;

  /*
   * Validate the parameters
   */
  if ((ppstgOpen == 0) || (plkbyt == 0))
    return STG_E_INVALIDPOINTER;

  /*
   * Allocate and initialize the new IStorage object.
   */
  newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));

  if (newStorage == 0)
    return STG_E_INSUFFICIENTMEMORY;

  hr = StorageImpl_Construct(
         newStorage,
         0,
6179
        0,
6180 6181
         plkbyt,
         grfMode,
6182 6183
         FALSE,
         TRUE);
6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198

  if (FAILED(hr))
  {
    HeapFree(GetProcessHeap(), 0, newStorage);
    return hr;
  }

  /*
   * Get an "out" pointer for the caller.
   */
  hr = StorageBaseImpl_QueryInterface(
         (IStorage*)newStorage,
         (REFIID)&IID_IStorage,
         (void**)ppstgOpen);

6199
  return hr;
6200 6201 6202
}

/******************************************************************************
6203
 *    StgOpenStorageOnILockBytes    [OLE32.@]
6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236
 */
HRESULT WINAPI StgOpenStorageOnILockBytes(
      ILockBytes *plkbyt,
      IStorage *pstgPriority,
      DWORD grfMode,
      SNB snbExclude,
      DWORD reserved,
      IStorage **ppstgOpen)
{
  StorageImpl* newStorage = 0;
  HRESULT        hr = S_OK;

  /*
   * Perform a sanity check
   */
  if ((plkbyt == 0) || (ppstgOpen == 0))
    return STG_E_INVALIDPOINTER;

  /*
   * Validate the STGM flags
   */
  if ( FAILED( validateSTGM(grfMode) ))
    return STG_E_INVALIDFLAG;

  /*
   * Initialize the "out" parameter.
   */
  *ppstgOpen = 0;

  /*
   * Allocate and initialize the new IStorage object.
   */
  newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6237

6238 6239 6240 6241 6242 6243
  if (newStorage == 0)
    return STG_E_INSUFFICIENTMEMORY;

  hr = StorageImpl_Construct(
         newStorage,
         0,
6244
         0,
6245 6246
         plkbyt,
         grfMode,
6247
         FALSE,
6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266
         FALSE);

  if (FAILED(hr))
  {
    HeapFree(GetProcessHeap(), 0, newStorage);
    return hr;
  }

  /*
   * Get an "out" pointer for the caller.
   */
  hr = StorageBaseImpl_QueryInterface(
         (IStorage*)newStorage,
         (REFIID)&IID_IStorage,
         (void**)ppstgOpen);

  return hr;
}

Jim Aston's avatar
Jim Aston committed
6267
/******************************************************************************
6268 6269
 *              StgSetTimes [ole32.@]
 *              StgSetTimes [OLE32.@]
Jim Aston's avatar
Jim Aston committed
6270 6271 6272
 *
 *
 */
Mike McCormack's avatar
Mike McCormack committed
6273 6274
HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
                           FILETIME const *patime, FILETIME const *pmtime)
Jim Aston's avatar
Jim Aston committed
6275
{
Mike McCormack's avatar
Mike McCormack committed
6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289
  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
6290 6291
}

6292
/******************************************************************************
6293
 *              StgIsStorageILockBytes        [OLE32.@]
6294 6295 6296 6297 6298 6299 6300 6301
 *
 * Determines if the ILockBytes contains a storage object.
 */
HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
{
  BYTE sig[8];
  ULARGE_INTEGER offset;

6302 6303
  offset.u.HighPart = 0;
  offset.u.LowPart  = 0;
6304 6305 6306 6307 6308 6309 6310 6311 6312 6313

  ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);

  if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
    return S_OK;

  return S_FALSE;
}

/******************************************************************************
6314
 *              WriteClassStg        [OLE32.@]
6315 6316 6317
 *
 * This method will store the specified CLSID in the specified storage object
 */
6318
HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6319 6320 6321
{
  HRESULT hRes;

6322 6323
  if(!pStg)
    return E_INVALIDARG;
6324

6325
  hRes = IStorage_SetClass(pStg, rclsid);
6326 6327 6328 6329

  return hRes;
}

6330
/***********************************************************************
6331
 *    ReadClassStg (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
6332
 *
6333 6334 6335 6336 6337 6338 6339 6340 6341 6342
 * 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
6343 6344 6345 6346 6347
 */
HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){

    STATSTG pstatstg;
    HRESULT hRes;
6348

6349 6350 6351 6352
    TRACE("(%p, %p)\n", pstg, pclsid);

    if(!pstg || !pclsid)
        return E_INVALIDARG;
Noomen Hamza's avatar
Noomen Hamza committed
6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364

   /*
    * read a STATSTG structure (contains the clsid) from the storage
    */
    hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);

    if(SUCCEEDED(hRes))
        *pclsid=pstatstg.clsid;

    return hRes;
}

6365
/***********************************************************************
6366
 *    OleLoadFromStream (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
6367 6368 6369 6370 6371
 *
 * This function loads an object from stream
 */
HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
{
6372 6373 6374
    CLSID	clsid;
    HRESULT	res;
    LPPERSISTSTREAM	xstm;
Noomen Hamza's avatar
Noomen Hamza committed
6375

6376
    TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
Noomen Hamza's avatar
Noomen Hamza committed
6377 6378

    res=ReadClassStm(pStm,&clsid);
6379 6380 6381 6382 6383
    if (!SUCCEEDED(res))
	return res;
    res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
    if (!SUCCEEDED(res))
	return res;
6384
    res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6385 6386 6387
    if (!SUCCEEDED(res)) {
	IUnknown_Release((IUnknown*)*ppvObj);
	return res;
Noomen Hamza's avatar
Noomen Hamza committed
6388
    }
6389 6390 6391 6392 6393 6394 6395
    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
6396 6397 6398
    return res;
}

6399
/***********************************************************************
6400
 *    OleSaveToStream (OLE32.@)
Noomen Hamza's avatar
Noomen Hamza committed
6401
 *
6402 6403
 * This function saves an object with the IPersistStream interface on it
 * to the specified stream.
Noomen Hamza's avatar
Noomen Hamza committed
6404 6405 6406 6407 6408 6409
 */
HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
{

    CLSID clsid;
    HRESULT res;
6410

6411
    TRACE("(%p,%p)\n",pPStm,pStm);
Noomen Hamza's avatar
Noomen Hamza committed
6412 6413 6414 6415

    res=IPersistStream_GetClassID(pPStm,&clsid);

    if (SUCCEEDED(res)){
6416

Noomen Hamza's avatar
Noomen Hamza committed
6417 6418 6419 6420
        res=WriteClassStm(pStm,&clsid);

        if (SUCCEEDED(res))

6421
            res=IPersistStream_Save(pPStm,pStm,TRUE);
Noomen Hamza's avatar
Noomen Hamza committed
6422 6423
    }

6424
    TRACE("Finished Save\n");
Noomen Hamza's avatar
Noomen Hamza committed
6425 6426
    return res;
}
6427

6428 6429 6430
/****************************************************************************
 * This method validate a STGM parameter that can contain the values below
 *
6431 6432 6433
 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
 * The stgm values contained in 0xffff0000 are bitmasks.
 *
6434 6435 6436
 * STGM_DIRECT               0x00000000
 * STGM_TRANSACTED           0x00010000
 * STGM_SIMPLE               0x08000000
6437
 *
6438 6439 6440
 * STGM_READ                 0x00000000
 * STGM_WRITE                0x00000001
 * STGM_READWRITE            0x00000002
6441
 *
6442 6443 6444 6445
 * STGM_SHARE_DENY_NONE      0x00000040
 * STGM_SHARE_DENY_READ      0x00000030
 * STGM_SHARE_DENY_WRITE     0x00000020
 * STGM_SHARE_EXCLUSIVE      0x00000010
6446
 *
6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458
 * 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)
{
6459 6460 6461
  DWORD access = STGM_ACCESS_MODE(stgm);
  DWORD share  = STGM_SHARE_MODE(stgm);
  DWORD create = STGM_CREATE_MODE(stgm);
6462

6463 6464
  if (stgm&~STGM_KNOWN_FLAGS)
  {
6465
    ERR("unknown flags %08x\n", stgm);
6466 6467
    return E_FAIL;
  }
6468

6469 6470 6471 6472 6473 6474 6475 6476 6477
  switch (access)
  {
  case STGM_READ:
  case STGM_WRITE:
  case STGM_READWRITE:
    break;
  default:
    return E_FAIL;
  }
6478

6479 6480 6481 6482 6483 6484 6485 6486 6487 6488
  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;
  }
6489

6490 6491 6492 6493 6494 6495 6496 6497
  switch (create)
  {
  case STGM_CREATE:
  case STGM_FAILIFTHERE:
    break;
  default:
    return E_FAIL;
  }
6498

6499
  /*
6500 6501
   * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
   */
6502
  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6503 6504 6505 6506 6507 6508
      return E_FAIL;

  /*
   * STGM_CREATE | STGM_CONVERT
   * if both are false, STGM_FAILIFTHERE is set to TRUE
   */
6509
  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6510 6511 6512 6513 6514
    return E_FAIL;

  /*
   * STGM_NOSCRATCH requires STGM_TRANSACTED
   */
6515
  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6516
    return E_FAIL;
6517

6518
  /*
6519
   * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6520 6521
   * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
   */
6522 6523 6524 6525
  if ( (stgm & STGM_NOSNAPSHOT) &&
        (!(stgm & STGM_TRANSACTED) ||
         share == STGM_SHARE_EXCLUSIVE ||
         share == STGM_SHARE_DENY_WRITE) )
6526 6527 6528 6529 6530 6531 6532 6533 6534
    return E_FAIL;

  return S_OK;
}

/****************************************************************************
 *      GetShareModeFromSTGM
 *
 * This method will return a share mode flag from a STGM value.
6535
 * The STGM value is assumed valid.
6536 6537 6538
 */
static DWORD GetShareModeFromSTGM(DWORD stgm)
{
6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552
  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;
6553 6554 6555 6556 6557 6558 6559 6560 6561 6562
}

/****************************************************************************
 *      GetAccessModeFromSTGM
 *
 * This method will return an access mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetAccessModeFromSTGM(DWORD stgm)
{
6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573
  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;
6574 6575 6576 6577 6578 6579 6580 6581 6582 6583
}

/****************************************************************************
 *      GetCreationModeFromSTGM
 *
 * This method will return a creation mode flag from a STGM value.
 * The STGM value is assumed valid.
 */
static DWORD GetCreationModeFromSTGM(DWORD stgm)
{
6584 6585 6586
  switch(STGM_CREATE_MODE(stgm))
  {
  case STGM_CREATE:
6587
    return CREATE_ALWAYS;
6588
  case STGM_CONVERT:
6589
    FIXME("STGM_CONVERT not implemented!\n");
6590
    return CREATE_NEW;
6591 6592
  case STGM_FAILIFTHERE:
    return CREATE_NEW;
6593
  }
6594 6595 6596
  ERR("Invalid create mode!\n");
  assert(0);
  return 0;
6597
}
6598 6599 6600


/*************************************************************************
6601
 * OLECONVERT_LoadOLE10 [Internal]
6602
 *
6603
 * Loads the OLE10 STREAM to memory
6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615
 *
 * 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
 *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
6616
 *
6617 6618
 *     Memory allocated for pData must be freed by the caller
 */
6619
static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6620 6621 6622
{
	DWORD dwSize;
	HRESULT hRes = S_OK;
Alexandre Julliard's avatar
Alexandre Julliard committed
6623 6624 6625
	int nTryCnt=0;
	int max_try = 6;

6626
	pData->pData = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
6627
	pData->pstrOleObjFileName = (CHAR *) NULL;
6628

Alexandre Julliard's avatar
Alexandre Julliard committed
6629 6630
	for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
	{
6631 6632 6633 6634 6635 6636 6637 6638 6639 6640
	/* 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
6641 6642 6643 6644 6645 6646
		else
		{
			hRes = S_OK;
			break;
		}
	}
6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660

	if(hRes == S_OK)
	{
		/* Get the TypeID...more info needed for this field */
		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
6661
			/* Get the length of the OleTypeName */
6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679
			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 */
					dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
					if(dwSize != pData->dwOleTypeNameLength)
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
Alexandre Julliard's avatar
Alexandre Julliard committed
6680 6681 6682 6683 6684 6685 6686
			if(bStrem1)
			{
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
				if(dwSize != sizeof(pData->dwOleObjFileNameLength))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
6687
			if(hRes == S_OK)
Alexandre Julliard's avatar
Alexandre Julliard committed
6688
			{
6689
					if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
Alexandre Julliard's avatar
Alexandre Julliard committed
6690
						pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6691
					pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
Alexandre Julliard's avatar
Alexandre Julliard committed
6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704
					if(pData->pstrOleObjFileName)
					{
						dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
						if(dwSize != pData->dwOleObjFileNameLength)
						{
							hRes = CONVERT10_E_OLESTREAM_GET;
						}
					}
					else
						hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}
			else
6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720
			{
				/* 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
6721
			}
6722 6723
			if(hRes == S_OK)
			{
Francois Gouget's avatar
Francois Gouget committed
6724
				/* Get the Length of the Data */
6725 6726 6727 6728 6729 6730 6731
				dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
				if(dwSize != sizeof(pData->dwDataLength))
				{
					hRes = CONVERT10_E_OLESTREAM_GET;
				}
			}

6732
			if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
Alexandre Julliard's avatar
Alexandre Julliard committed
6733
			{
6734
				if(!bStrem1) /* if it is a second OLE stream data */
Alexandre Julliard's avatar
Alexandre Julliard committed
6735 6736 6737 6738 6739 6740 6741 6742 6743
				{
					pData->dwDataLength -= 8;
					dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
					if(dwSize != sizeof(pData->strUnknown))
					{
						hRes = CONVERT10_E_OLESTREAM_GET;
					}
				}
			}
6744 6745 6746 6747
			if(hRes == S_OK)
			{
				if(pData->dwDataLength > 0)
				{
6748
					pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770

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

/*************************************************************************
6771
 * OLECONVERT_SaveOLE10 [Internal]
6772
 *
6773
 * Saves the OLE10 STREAM From memory
6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784
 *
 * 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.
6785
 *
6786
 */
6787
static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811
{
    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
6812
        /* Set the Length of the OleTypeName */
6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853
        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 */
                dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->strOleTypeName, pData->dwOleTypeNameLength);
                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
6854
            /* Set the length of the Data */
6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878
            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;
}

/*************************************************************************
6879
 * OLECONVERT_GetOLE20FromOLE10[Internal]
6880 6881
 *
 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6882
 * opens it, and copies the content to the dest IStorage for
6883
 * OleConvertOLESTREAMToIStorage
6884
 *
6885 6886 6887 6888 6889 6890 6891 6892 6893 6894
 *
 * 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
6895 6896
 *
 *
6897
 */
6898
static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6899 6900 6901 6902 6903 6904
{
    HRESULT hRes;
    HANDLE hFile;
    IStorage *pTempStorage;
    DWORD dwNumOfBytesWritten;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6905
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6906 6907 6908 6909

    /* Create a temp File */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
Patrik Stridvall's avatar
Patrik Stridvall committed
6910
    hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921

    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)
        {
Patrik Stridvall's avatar
Patrik Stridvall committed
6922
            hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6923 6924 6925 6926 6927 6928 6929 6930
            StorageBaseImpl_Release(pTempStorage);
        }
        DeleteFileW(wstrTempFile);
    }
}


/*************************************************************************
6931
 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6932
 *
6933
 * Saves the OLE10 STREAM From memory
6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945
 *
 * 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.
6946
 *
6947
 */
6948
static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6949 6950 6951 6952 6953 6954
{
    HANDLE hFile;
    HRESULT hRes;
    DWORD nDataLength = 0;
    IStorage *pTempStorage;
    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6955
    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6956 6957

    *pData = NULL;
6958

6959 6960 6961
    /* Create temp Storage */
    GetTempPathW(MAX_PATH, wstrTempDir);
    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
Patrik Stridvall's avatar
Patrik Stridvall committed
6962
    hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6963 6964 6965 6966

    if(hRes == S_OK)
    {
        /* Copy Src Storage to the Temp Storage */
Patrik Stridvall's avatar
Patrik Stridvall committed
6967
        StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6968 6969 6970
        StorageBaseImpl_Release(pTempStorage);

        /* Open Temp Storage as a file and copy to memory */
Patrik Stridvall's avatar
Patrik Stridvall committed
6971
        hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6972 6973 6974
        if(hFile != INVALID_HANDLE_VALUE)
        {
            nDataLength = GetFileSize(hFile, NULL);
6975
            *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6976 6977 6978 6979 6980 6981 6982 6983 6984
            ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
            CloseHandle(hFile);
        }
        DeleteFileW(wstrTempFile);
    }
    return nDataLength;
}

/*************************************************************************
6985
 * OLECONVERT_CreateOleStream [Internal]
6986
 *
6987
 * Creates the "\001OLE" stream in the IStorage if necessary.
6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999
 *
 * PARAMS
 *     pStorage     [I] Dest storage to create the stream in
 *
 * RETURNS
 *     Nothing
 *
 * NOTES
 *     This function is used by OleConvertOLESTREAMToIStorage only.
 *
 *     This stream is still unknown, MS Word seems to have extra data
 *     but since the data is stored in the OLESTREAM there should be
7000
 *     no need to recreate the stream.  If the stream is manually
7001
 *     deleted it will create it with this default data.
7002
 *
7003 7004 7005 7006 7007
 */
void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
{
    HRESULT hRes;
    IStream *pStream;
7008
    static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7009
    BYTE pOleStreamHeader [] =
7010
    {
7011 7012 7013
        0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
7014
    };
7015

7016
    /* Create stream if not present */
7017
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7018 7019 7020 7021 7022 7023 7024 7025 7026 7027
        STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );

    if(hRes == S_OK)
    {
        /* Write default Data */
        hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
        IStream_Release(pStream);
    }
}

7028 7029 7030 7031 7032 7033 7034 7035 7036
/* 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);
7037
    r = IStream_Write( stm, &len, sizeof(len), NULL);
7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056
    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;

7057
    r = IStream_Read( stm, &len, sizeof(len), &count );
7058 7059
    if( FAILED( r ) )
        return r;
7060
    if( count != sizeof(len) )
7061 7062
        return E_OUTOFMEMORY;

7063
    TRACE("%d bytes\n",len);
7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096
    
    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;
7097
    static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116

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

    /*  Create a CompObj stream if it doesn't exist */
    r = IStorage_CreateStream(pstg, szwStreamName,
        STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
    if( FAILED (r) )
        return r;

    /* Write CompObj Structure to stream */
7117
    r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128

    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 ) )
7129
        r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7130 7131 7132 7133 7134 7135 7136

    IStream_Release( pstm );

    return r;
}

/***********************************************************************
7137
 *               WriteFmtUserTypeStg (OLE32.@)
7138 7139 7140 7141 7142 7143
 */
HRESULT WINAPI WriteFmtUserTypeStg(
	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
{
    HRESULT r;
    WCHAR szwClipName[0x40];
7144 7145
    CLSID clsid = CLSID_NULL;
    LPWSTR wstrProgID = NULL;
7146 7147 7148 7149 7150
    DWORD n;

    TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));

    /* get the clipboard format name */
7151
    n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7152 7153 7154 7155
    szwClipName[n]=0;

    TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));

7156 7157 7158 7159
    /* 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?
    */
7160

7161 7162
    /* get the real program ID.  This may fail, but that's fine */
    ProgIDFromCLSID(&clsid, &wstrProgID);
7163 7164 7165

    TRACE("progid is %s\n",debugstr_w(wstrProgID));

7166 7167
    r = STORAGE_WriteCompObj( pstg, &clsid, 
                              lpszUserType, szwClipName, wstrProgID );
7168

7169
    CoTaskMemFree(wstrProgID);
7170 7171 7172 7173 7174 7175

    return r;
}


/******************************************************************************
7176
 *              ReadFmtUserTypeStg        [OLE32.@]
7177 7178 7179 7180 7181
 */
HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
{
    HRESULT r;
    IStream *stm = 0;
7182
    static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194
    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 ) )
    {
7195
        WARN("Failed to open stream r = %08x\n", r);
7196 7197 7198 7199
        return r;
    }

    /* read the various parts of the structure */
7200 7201
    r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
    if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218
        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;

7219 7220
    r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
    if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237
        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;
}

7238 7239

/*************************************************************************
7240
 * OLECONVERT_CreateCompObjStream [Internal]
7241 7242 7243 7244
 *
 * Creates a "\001CompObj" is the destination IStorage if necessary.
 *
 * PARAMS
7245
 *     pStorage       [I] The dest IStorage to create the CompObj Stream
7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256
 *                        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
7257
 *     no need to recreate the stream.  If the stream is manually
7258 7259
 *     deleted it will attempt to create it by querying the registry.
 *
7260
 *
7261 7262 7263 7264 7265 7266
 */
HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
{
    IStream *pStream;
    HRESULT hStorageRes, hRes = S_OK;
    OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7267
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7268
    WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279

    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));
    memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
    memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));


    /*  Create a CompObj stream if it doesn't exist */
7280
    hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7281 7282 7283 7284 7285 7286 7287 7288
        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 */
7289
        /* Note: in the test made, these were Identical      */
7290 7291 7292 7293
        IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
        strcpy(IStorageCompObj.strProgIDName, strOleTypeName);

        /* Get the CLSID */
7294 7295 7296
        MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
                             bufferW, OLESTREAM_MAX_STR_LEN );
        hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7297

7298
        if(hRes == S_OK)
7299 7300 7301 7302
        {
            HKEY hKey;
            LONG hErr;
            /* Get the CLSID Default Name from the Registry */
7303
            hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7304 7305 7306 7307
            if(hErr == ERROR_SUCCESS)
            {
                char strTemp[OLESTREAM_MAX_STR_LEN];
                IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7308
                hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7309 7310 7311 7312 7313 7314 7315 7316
                if(hErr == ERROR_SUCCESS)
                {
                    strcpy(IStorageCompObj.strCLSIDName, strTemp);
                }
                RegCloseKey(hKey);
            }
        }

7317 7318 7319 7320 7321 7322 7323
        /* 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)
7324
        {
7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335
            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);
7336
        }
7337
        hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7338 7339 7340 7341 7342 7343 7344
        IStream_Release(pStream);
    }
    return hRes;
}


/*************************************************************************
7345
 * OLECONVERT_CreateOlePresStream[Internal]
7346 7347 7348 7349 7350 7351
 *
 * 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
7352
 *     dwExtentY    [I] Height of the Metafile
7353 7354 7355 7356 7357 7358 7359 7360 7361
 *     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.
7362
 *
7363
 */
7364
static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7365 7366 7367
{
    HRESULT hRes;
    IStream *pStream;
7368
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7369
    BYTE pOlePresStreamHeader [] =
7370
    {
7371 7372
        0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7373 7374 7375 7376
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };

7377
    BYTE pOlePresStreamHeaderEmpty [] =
7378
    {
7379 7380
        0x00, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7381 7382 7383
        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };
7384

7385
    /* Create the OlePres000 Stream */
7386
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409
        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
7410
        /* Set Data and Length */
7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429
        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);
    }
}

/*************************************************************************
7430
 * OLECONVERT_CreateOle10NativeStream [Internal]
7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445
 *
 * 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
7446
 *
7447
 */
7448
static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7449 7450 7451
{
    HRESULT hRes;
    IStream *pStream;
7452
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7453

7454
    /* Create the Ole10Native Stream */
7455
    hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468
        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);
    }

}

/*************************************************************************
7469
 * OLECONVERT_GetOLE10ProgID [Internal]
7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484
 *
 * 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.
 *
7485
 *
7486
 */
7487
static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7488 7489 7490 7491 7492
{
    HRESULT hRes;
    IStream *pStream;
    LARGE_INTEGER iSeekPos;
    OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7493
    static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7494 7495

    /* Open the CompObj Stream */
7496
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7497 7498 7499 7500 7501
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {

        /*Get the OleType from the CompObj Stream */
7502 7503
        iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
        iSeekPos.u.HighPart = 0;
7504 7505 7506

        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
        IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7507
        iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7508 7509
        IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
        IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7510
        iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532
        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);
        }
7533

7534 7535 7536 7537 7538
    }
    return hRes;
}

/*************************************************************************
7539
 * OLECONVERT_GetOle10PresData [Internal]
7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553
 *
 * 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
7554 7555
 *
 *
7556
 */
7557
static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7558 7559 7560 7561
{

    HRESULT hRes;
    IStream *pStream;
7562
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574

    /* 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 */
7575
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7576 7577 7578 7579 7580 7581 7582 7583
        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)
        {
7584
            pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7585 7586 7587 7588 7589 7590 7591 7592 7593
            IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
        }
        IStream_Release(pStream);
    }

}


/*************************************************************************
7594
 * OLECONVERT_GetOle20PresData[Internal]
7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606
 *
 * 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.
7607
 *
7608 7609
 *     Memory allocated for pData must be freed by the caller
 */
7610
static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7611 7612 7613 7614
{
    HRESULT hRes;
    IStream *pStream;
    OLECONVERT_ISTORAGE_OLEPRES olePress;
7615
    static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633

    /* 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 */
7634
    hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7635 7636 7637 7638 7639
        STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
    if(hRes == S_OK)
    {
        LARGE_INTEGER iSeekPos;
        METAFILEPICT16 MetaFilePict;
7640
        static const char strMetafilePictName[] = "METAFILEPICT";
7641 7642 7643 7644 7645 7646 7647 7648

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

7649 7650
        iSeekPos.u.HighPart = 0;
        iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672

        /* 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 */
7673
            pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7674 7675 7676 7677 7678 7679 7680 7681
            memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
            IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
        }
        IStream_Release(pStream);
    }
}

/*************************************************************************
7682
 * OleConvertOLESTREAMToIStorage [OLE32.@]
7683 7684 7685 7686 7687 7688 7689 7690
 *
 * Read info on MSDN
 *
 * TODO
 *      DVTARGETDEVICE paramenter is not handled
 *      Still unsure of some mem fields for OLE 10 Stream
 *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
 *      and "\001OLE" streams
7691
 *
7692 7693
 */
HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7694 7695
    LPOLESTREAM pOleStream,
    LPSTORAGE pstg,
7696 7697 7698 7699 7700 7701
    const DVTARGETDEVICE* ptd)
{
    int i;
    HRESULT hRes=S_OK;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];

7702 7703
    TRACE("%p %p %p\n", pOleStream, pstg, ptd);

7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718
    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
7719
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7720 7721 7722 7723 7724
    }

    if(hRes == S_OK)
    {
        /* Load the OLESTREAM to Memory (part 2)*/
Alexandre Julliard's avatar
Alexandre Julliard committed
7725
        hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763
    }

    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 */
            OLECONVERT_CreateOleStream(pstg);
        }
    }


    /* Free allocated memory */
    for(i=0; i < 2; i++)
    {
7764 7765 7766
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
        pOleStreamData[i].pstrOleObjFileName = NULL;
7767 7768 7769 7770 7771
    }
    return hRes;
}

/*************************************************************************
7772
 * OleConvertIStorageToOLESTREAM [OLE32.@]
7773 7774 7775 7776 7777 7778 7779 7780 7781
 *
 * 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.
7782
 *
7783 7784
 */
HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7785
    LPSTORAGE pstg,
7786 7787 7788 7789 7790 7791
    LPOLESTREAM pOleStream)
{
    int i;
    HRESULT hRes = S_OK;
    IStream *pStream;
    OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7792
    static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7793

7794
    TRACE("%p %p\n", pstg, pOleStream);
7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809

    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
7810
        /* Was it originally Ole10 */
7811
        hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7812 7813 7814
        if(hRes == S_OK)
        {
            IStream_Release(pStream);
Alexandre Julliard's avatar
Alexandre Julliard committed
7815
            /* Get Presentation Data for Ole10Native */
7816 7817 7818 7819
            OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
        }
        else
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
7820
            /* Get Presentation Data (OLE20) */
7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835
            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++)
    {
7836
        HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7837 7838 7839 7840
    }

    return hRes;
}
7841

7842
/***********************************************************************
7843
 *		GetConvertStg (OLE32.@)
7844
 */
7845 7846
HRESULT WINAPI GetConvertStg(IStorage *stg) {
    FIXME("unimplemented stub!\n");
7847 7848
    return E_FAIL;
}
7849 7850

/******************************************************************************
7851
 * StgIsStorageFile [OLE32.@]
7852 7853 7854 7855 7856 7857 7858 7859
 * 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
7860 7861 7862 7863 7864 7865 7866 7867
 */
HRESULT WINAPI
StgIsStorageFile(LPCOLESTR fn)
{
	HANDLE		hf;
	BYTE		magic[8];
	DWORD		bytes_read;

7868
	TRACE("%s\n", debugstr_w(fn));
7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897
	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) {
		WARN(" too short\n");
		return S_FALSE;
	}

	if (!memcmp(magic,STORAGE_magic,8)) {
		WARN(" -> YES\n");
		return S_OK;
	}

	WARN(" -> Invalid header.\n");
	return S_FALSE;
}
7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954

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

    if (rclsid==NULL)
        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);

    if (pclsid==NULL)
        return E_INVALIDARG;

    res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);

    if (FAILED(res))
        return res;

    if (nbByte != sizeof(CLSID))
        return S_FALSE;
    else
        return S_OK;
}