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