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