sync with trunk r46493
[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;