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