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