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