- Add Wow64* functions declarations to winbase.h
[reactos.git] / reactos / 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 ULONG sector_size,
2587 StorageImpl** result)
2588 {
2589 StorageImpl* This;
2590 HRESULT hr = S_OK;
2591 DirEntry currentEntry;
2592 DirRef currentEntryRef;
2593 WCHAR fullpath[MAX_PATH];
2594
2595 if ( FAILED( validateSTGM(openFlags) ))
2596 return STG_E_INVALIDFLAG;
2597
2598 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2599 if (!This)
2600 return E_OUTOFMEMORY;
2601
2602 memset(This, 0, sizeof(StorageImpl));
2603
2604 list_init(&This->base.strmHead);
2605
2606 list_init(&This->base.storageHead);
2607
2608 This->base.lpVtbl = &Storage32Impl_Vtbl;
2609 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2610 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2611 This->base.openFlags = (openFlags & ~STGM_CREATE);
2612 This->base.ref = 1;
2613 This->base.create = create;
2614
2615 This->base.reverted = 0;
2616
2617 This->hFile = hFile;
2618
2619 if(pwcsName) {
2620 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2621 {
2622 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2623 }
2624 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2625 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2626 if (!This->pwcsName)
2627 {
2628 hr = STG_E_INSUFFICIENTMEMORY;
2629 goto end;
2630 }
2631 strcpyW(This->pwcsName, fullpath);
2632 This->base.filename = This->pwcsName;
2633 }
2634
2635 /*
2636 * Initialize the big block cache.
2637 */
2638 This->bigBlockSize = sector_size;
2639 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2640 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2641 pLkbyt,
2642 openFlags,
2643 fileBased);
2644
2645 if (This->bigBlockFile == 0)
2646 {
2647 hr = E_FAIL;
2648 goto end;
2649 }
2650
2651 if (create)
2652 {
2653 ULARGE_INTEGER size;
2654 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2655
2656 /*
2657 * Initialize all header variables:
2658 * - The big block depot consists of one block and it is at block 0
2659 * - The directory table starts at block 1
2660 * - There is no small block depot
2661 */
2662 memset( This->bigBlockDepotStart,
2663 BLOCK_UNUSED,
2664 sizeof(This->bigBlockDepotStart));
2665
2666 This->bigBlockDepotCount = 1;
2667 This->bigBlockDepotStart[0] = 0;
2668 This->rootStartBlock = 1;
2669 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2670 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2671 if (sector_size == 4096)
2672 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2673 else
2674 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2675 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2676 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2677 This->extBigBlockDepotCount = 0;
2678
2679 StorageImpl_SaveFileHeader(This);
2680
2681 /*
2682 * Add one block for the big block depot and one block for the directory table
2683 */
2684 size.u.HighPart = 0;
2685 size.u.LowPart = This->bigBlockSize * 3;
2686 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2687
2688 /*
2689 * Initialize the big block depot
2690 */
2691 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2692 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2693 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2694 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2695 }
2696 else
2697 {
2698 /*
2699 * Load the header for the file.
2700 */
2701 hr = StorageImpl_LoadFileHeader(This);
2702
2703 if (FAILED(hr))
2704 {
2705 goto end;
2706 }
2707 }
2708
2709 /*
2710 * There is no block depot cached yet.
2711 */
2712 This->indexBlockDepotCached = 0xFFFFFFFF;
2713
2714 /*
2715 * Start searching for free blocks with block 0.
2716 */
2717 This->prevFreeBlock = 0;
2718
2719 This->firstFreeSmallBlock = 0;
2720
2721 /*
2722 * Create the block chain abstractions.
2723 */
2724 if(!(This->rootBlockChain =
2725 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2726 {
2727 hr = STG_E_READFAULT;
2728 goto end;
2729 }
2730
2731 if(!(This->smallBlockDepotChain =
2732 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2733 DIRENTRY_NULL)))
2734 {
2735 hr = STG_E_READFAULT;
2736 goto end;
2737 }
2738
2739 /*
2740 * Write the root storage entry (memory only)
2741 */
2742 if (create)
2743 {
2744 DirEntry rootEntry;
2745 /*
2746 * Initialize the directory table
2747 */
2748 memset(&rootEntry, 0, sizeof(rootEntry));
2749 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2750 sizeof(rootEntry.name)/sizeof(WCHAR) );
2751 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2752 rootEntry.stgType = STGTY_ROOT;
2753 rootEntry.leftChild = DIRENTRY_NULL;
2754 rootEntry.rightChild = DIRENTRY_NULL;
2755 rootEntry.dirRootEntry = DIRENTRY_NULL;
2756 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2757 rootEntry.size.u.HighPart = 0;
2758 rootEntry.size.u.LowPart = 0;
2759
2760 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2761 }
2762
2763 /*
2764 * Find the ID of the root storage.
2765 */
2766 currentEntryRef = 0;
2767
2768 do
2769 {
2770 hr = StorageImpl_ReadDirEntry(
2771 This,
2772 currentEntryRef,
2773 &currentEntry);
2774
2775 if (SUCCEEDED(hr))
2776 {
2777 if ( (currentEntry.sizeOfNameString != 0 ) &&
2778 (currentEntry.stgType == STGTY_ROOT) )
2779 {
2780 This->base.storageDirEntry = currentEntryRef;
2781 }
2782 }
2783
2784 currentEntryRef++;
2785
2786 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2787
2788 if (FAILED(hr))
2789 {
2790 hr = STG_E_READFAULT;
2791 goto end;
2792 }
2793
2794 /*
2795 * Create the block chain abstraction for the small block root chain.
2796 */
2797 if(!(This->smallBlockRootChain =
2798 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2799 {
2800 hr = STG_E_READFAULT;
2801 }
2802
2803 end:
2804 if (FAILED(hr))
2805 {
2806 IStorage_Release((IStorage*)This);
2807 *result = NULL;
2808 }
2809 else
2810 *result = This;
2811
2812 return hr;
2813 }
2814
2815 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2816 {
2817 StorageImpl *This = (StorageImpl*) iface;
2818
2819 StorageBaseImpl_DeleteAll(&This->base);
2820
2821 This->base.reverted = 1;
2822 }
2823
2824 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2825 {
2826 StorageImpl *This = (StorageImpl*) iface;
2827 int i;
2828 TRACE("(%p)\n", This);
2829
2830 StorageImpl_Invalidate(iface);
2831
2832 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2833
2834 BlockChainStream_Destroy(This->smallBlockRootChain);
2835 BlockChainStream_Destroy(This->rootBlockChain);
2836 BlockChainStream_Destroy(This->smallBlockDepotChain);
2837
2838 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2839 BlockChainStream_Destroy(This->blockChainCache[i]);
2840
2841 if (This->bigBlockFile)
2842 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2843 HeapFree(GetProcessHeap(), 0, This);
2844 }
2845
2846 /******************************************************************************
2847 * Storage32Impl_GetNextFreeBigBlock
2848 *
2849 * Returns the index of the next free big block.
2850 * If the big block depot is filled, this method will enlarge it.
2851 *
2852 */
2853 static ULONG StorageImpl_GetNextFreeBigBlock(
2854 StorageImpl* This)
2855 {
2856 ULONG depotBlockIndexPos;
2857 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2858 BOOL success;
2859 ULONG depotBlockOffset;
2860 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2861 ULONG nextBlockIndex = BLOCK_SPECIAL;
2862 int depotIndex = 0;
2863 ULONG freeBlock = BLOCK_UNUSED;
2864 ULARGE_INTEGER neededSize;
2865
2866 depotIndex = This->prevFreeBlock / blocksPerDepot;
2867 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2868
2869 /*
2870 * Scan the entire big block depot until we find a block marked free
2871 */
2872 while (nextBlockIndex != BLOCK_UNUSED)
2873 {
2874 if (depotIndex < COUNT_BBDEPOTINHEADER)
2875 {
2876 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2877
2878 /*
2879 * Grow the primary depot.
2880 */
2881 if (depotBlockIndexPos == BLOCK_UNUSED)
2882 {
2883 depotBlockIndexPos = depotIndex*blocksPerDepot;
2884
2885 /*
2886 * Add a block depot.
2887 */
2888 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2889 This->bigBlockDepotCount++;
2890 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2891
2892 /*
2893 * Flag it as a block depot.
2894 */
2895 StorageImpl_SetNextBlockInChain(This,
2896 depotBlockIndexPos,
2897 BLOCK_SPECIAL);
2898
2899 /* Save new header information.
2900 */
2901 StorageImpl_SaveFileHeader(This);
2902 }
2903 }
2904 else
2905 {
2906 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2907
2908 if (depotBlockIndexPos == BLOCK_UNUSED)
2909 {
2910 /*
2911 * Grow the extended depot.
2912 */
2913 ULONG extIndex = BLOCK_UNUSED;
2914 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2915 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2916
2917 if (extBlockOffset == 0)
2918 {
2919 /* We need an extended block.
2920 */
2921 extIndex = Storage32Impl_AddExtBlockDepot(This);
2922 This->extBigBlockDepotCount++;
2923 depotBlockIndexPos = extIndex + 1;
2924 }
2925 else
2926 depotBlockIndexPos = depotIndex * blocksPerDepot;
2927
2928 /*
2929 * Add a block depot and mark it in the extended block.
2930 */
2931 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2932 This->bigBlockDepotCount++;
2933 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2934
2935 /* Flag the block depot.
2936 */
2937 StorageImpl_SetNextBlockInChain(This,
2938 depotBlockIndexPos,
2939 BLOCK_SPECIAL);
2940
2941 /* If necessary, flag the extended depot block.
2942 */
2943 if (extIndex != BLOCK_UNUSED)
2944 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2945
2946 /* Save header information.
2947 */
2948 StorageImpl_SaveFileHeader(This);
2949 }
2950 }
2951
2952 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2953
2954 if (success)
2955 {
2956 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2957 ( nextBlockIndex != BLOCK_UNUSED))
2958 {
2959 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2960
2961 if (nextBlockIndex == BLOCK_UNUSED)
2962 {
2963 freeBlock = (depotIndex * blocksPerDepot) +
2964 (depotBlockOffset/sizeof(ULONG));
2965 }
2966
2967 depotBlockOffset += sizeof(ULONG);
2968 }
2969 }
2970
2971 depotIndex++;
2972 depotBlockOffset = 0;
2973 }
2974
2975 /*
2976 * make sure that the block physically exists before using it
2977 */
2978 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
2979 BIGBLOCKFILE_Expand(This->bigBlockFile, neededSize);
2980
2981 This->prevFreeBlock = freeBlock;
2982
2983 return freeBlock;
2984 }
2985
2986 /******************************************************************************
2987 * Storage32Impl_AddBlockDepot
2988 *
2989 * This will create a depot block, essentially it is a block initialized
2990 * to BLOCK_UNUSEDs.
2991 */
2992 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2993 {
2994 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
2995
2996 /*
2997 * Initialize blocks as free
2998 */
2999 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3000 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3001 }
3002
3003 /******************************************************************************
3004 * Storage32Impl_GetExtDepotBlock
3005 *
3006 * Returns the index of the block that corresponds to the specified depot
3007 * index. This method is only for depot indexes equal or greater than
3008 * COUNT_BBDEPOTINHEADER.
3009 */
3010 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3011 {
3012 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3013 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3014 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3015 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3016 ULONG blockIndex = BLOCK_UNUSED;
3017 ULONG extBlockIndex = This->extBigBlockDepotStart;
3018
3019 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3020
3021 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3022 return BLOCK_UNUSED;
3023
3024 while (extBlockCount > 0)
3025 {
3026 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3027 extBlockCount--;
3028 }
3029
3030 if (extBlockIndex != BLOCK_UNUSED)
3031 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3032 extBlockOffset * sizeof(ULONG), &blockIndex);
3033
3034 return blockIndex;
3035 }
3036
3037 /******************************************************************************
3038 * Storage32Impl_SetExtDepotBlock
3039 *
3040 * Associates the specified block index to the specified depot index.
3041 * This method is only for depot indexes equal or greater than
3042 * COUNT_BBDEPOTINHEADER.
3043 */
3044 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3045 {
3046 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3047 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3048 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3049 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3050 ULONG extBlockIndex = This->extBigBlockDepotStart;
3051
3052 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3053
3054 while (extBlockCount > 0)
3055 {
3056 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3057 extBlockCount--;
3058 }
3059
3060 if (extBlockIndex != BLOCK_UNUSED)
3061 {
3062 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3063 extBlockOffset * sizeof(ULONG),
3064 blockIndex);
3065 }
3066 }
3067
3068 /******************************************************************************
3069 * Storage32Impl_AddExtBlockDepot
3070 *
3071 * Creates an extended depot block.
3072 */
3073 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3074 {
3075 ULONG numExtBlocks = This->extBigBlockDepotCount;
3076 ULONG nextExtBlock = This->extBigBlockDepotStart;
3077 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3078 ULONG index = BLOCK_UNUSED;
3079 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3080 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3081 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3082
3083 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3084 blocksPerDepotBlock;
3085
3086 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3087 {
3088 /*
3089 * The first extended block.
3090 */
3091 This->extBigBlockDepotStart = index;
3092 }
3093 else
3094 {
3095 unsigned int i;
3096 /*
3097 * Follow the chain to the last one.
3098 */
3099 for (i = 0; i < (numExtBlocks - 1); i++)
3100 {
3101 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3102 }
3103
3104 /*
3105 * Add the new extended block to the chain.
3106 */
3107 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3108 index);
3109 }
3110
3111 /*
3112 * Initialize this block.
3113 */
3114 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3115 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3116
3117 return index;
3118 }
3119
3120 /******************************************************************************
3121 * Storage32Impl_FreeBigBlock
3122 *
3123 * This method will flag the specified block as free in the big block depot.
3124 */
3125 static void StorageImpl_FreeBigBlock(
3126 StorageImpl* This,
3127 ULONG blockIndex)
3128 {
3129 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3130
3131 if (blockIndex < This->prevFreeBlock)
3132 This->prevFreeBlock = blockIndex;
3133 }
3134
3135 /************************************************************************
3136 * Storage32Impl_GetNextBlockInChain
3137 *
3138 * This method will retrieve the block index of the next big block in
3139 * in the chain.
3140 *
3141 * Params: This - Pointer to the Storage object.
3142 * blockIndex - Index of the block to retrieve the chain
3143 * for.
3144 * nextBlockIndex - receives the return value.
3145 *
3146 * Returns: This method returns the index of the next block in the chain.
3147 * It will return the constants:
3148 * BLOCK_SPECIAL - If the block given was not part of a
3149 * chain.
3150 * BLOCK_END_OF_CHAIN - If the block given was the last in
3151 * a chain.
3152 * BLOCK_UNUSED - If the block given was not past of a chain
3153 * and is available.
3154 * BLOCK_EXTBBDEPOT - This block is part of the extended
3155 * big block depot.
3156 *
3157 * See Windows documentation for more details on IStorage methods.
3158 */
3159 static HRESULT StorageImpl_GetNextBlockInChain(
3160 StorageImpl* This,
3161 ULONG blockIndex,
3162 ULONG* nextBlockIndex)
3163 {
3164 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3165 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3166 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3167 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3168 BOOL success;
3169 ULONG depotBlockIndexPos;
3170 int index, num_blocks;
3171
3172 *nextBlockIndex = BLOCK_SPECIAL;
3173
3174 if(depotBlockCount >= This->bigBlockDepotCount)
3175 {
3176 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3177 This->bigBlockDepotCount);
3178 return STG_E_READFAULT;
3179 }
3180
3181 /*
3182 * Cache the currently accessed depot block.
3183 */
3184 if (depotBlockCount != This->indexBlockDepotCached)
3185 {
3186 This->indexBlockDepotCached = depotBlockCount;
3187
3188 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3189 {
3190 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3191 }
3192 else
3193 {
3194 /*
3195 * We have to look in the extended depot.
3196 */
3197 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3198 }
3199
3200 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3201
3202 if (!success)
3203 return STG_E_READFAULT;
3204
3205 num_blocks = This->bigBlockSize / 4;
3206
3207 for (index = 0; index < num_blocks; index++)
3208 {
3209 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3210 This->blockDepotCached[index] = *nextBlockIndex;
3211 }
3212 }
3213
3214 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3215
3216 return S_OK;
3217 }
3218
3219 /******************************************************************************
3220 * Storage32Impl_GetNextExtendedBlock
3221 *
3222 * Given an extended block this method will return the next extended block.
3223 *
3224 * NOTES:
3225 * The last ULONG of an extended block is the block index of the next
3226 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3227 * depot.
3228 *
3229 * Return values:
3230 * - The index of the next extended block
3231 * - BLOCK_UNUSED: there is no next extended block.
3232 * - Any other return values denotes failure.
3233 */
3234 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3235 {
3236 ULONG nextBlockIndex = BLOCK_SPECIAL;
3237 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3238
3239 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3240 &nextBlockIndex);
3241
3242 return nextBlockIndex;
3243 }
3244
3245 /******************************************************************************
3246 * Storage32Impl_SetNextBlockInChain
3247 *
3248 * This method will write the index of the specified block's next block
3249 * in the big block depot.
3250 *
3251 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3252 * do the following
3253 *
3254 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3255 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3256 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3257 *
3258 */
3259 static void StorageImpl_SetNextBlockInChain(
3260 StorageImpl* This,
3261 ULONG blockIndex,
3262 ULONG nextBlock)
3263 {
3264 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3265 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3266 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3267 ULONG depotBlockIndexPos;
3268
3269 assert(depotBlockCount < This->bigBlockDepotCount);
3270 assert(blockIndex != nextBlock);
3271
3272 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3273 {
3274 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3275 }
3276 else
3277 {
3278 /*
3279 * We have to look in the extended depot.
3280 */
3281 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3282 }
3283
3284 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3285 nextBlock);
3286 /*
3287 * Update the cached block depot, if necessary.
3288 */
3289 if (depotBlockCount == This->indexBlockDepotCached)
3290 {
3291 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3292 }
3293 }
3294
3295 /******************************************************************************
3296 * Storage32Impl_LoadFileHeader
3297 *
3298 * This method will read in the file header
3299 */
3300 static HRESULT StorageImpl_LoadFileHeader(
3301 StorageImpl* This)
3302 {
3303 HRESULT hr;
3304 BYTE headerBigBlock[HEADER_SIZE];
3305 int index;
3306 ULARGE_INTEGER offset;
3307 DWORD bytes_read;
3308
3309 TRACE("\n");
3310 /*
3311 * Get a pointer to the big block of data containing the header.
3312 */
3313 offset.u.HighPart = 0;
3314 offset.u.LowPart = 0;
3315 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3316 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3317 hr = STG_E_FILENOTFOUND;
3318
3319 /*
3320 * Extract the information from the header.
3321 */
3322 if (SUCCEEDED(hr))
3323 {
3324 /*
3325 * Check for the "magic number" signature and return an error if it is not
3326 * found.
3327 */
3328 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3329 {
3330 return STG_E_OLDFORMAT;
3331 }
3332
3333 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3334 {
3335 return STG_E_INVALIDHEADER;
3336 }
3337
3338 StorageUtl_ReadWord(
3339 headerBigBlock,
3340 OFFSET_BIGBLOCKSIZEBITS,
3341 &This->bigBlockSizeBits);
3342
3343 StorageUtl_ReadWord(
3344 headerBigBlock,
3345 OFFSET_SMALLBLOCKSIZEBITS,
3346 &This->smallBlockSizeBits);
3347
3348 StorageUtl_ReadDWord(
3349 headerBigBlock,
3350 OFFSET_BBDEPOTCOUNT,
3351 &This->bigBlockDepotCount);
3352
3353 StorageUtl_ReadDWord(
3354 headerBigBlock,
3355 OFFSET_ROOTSTARTBLOCK,
3356 &This->rootStartBlock);
3357
3358 StorageUtl_ReadDWord(
3359 headerBigBlock,
3360 OFFSET_SMALLBLOCKLIMIT,
3361 &This->smallBlockLimit);
3362
3363 StorageUtl_ReadDWord(
3364 headerBigBlock,
3365 OFFSET_SBDEPOTSTART,
3366 &This->smallBlockDepotStart);
3367
3368 StorageUtl_ReadDWord(
3369 headerBigBlock,
3370 OFFSET_EXTBBDEPOTSTART,
3371 &This->extBigBlockDepotStart);
3372
3373 StorageUtl_ReadDWord(
3374 headerBigBlock,
3375 OFFSET_EXTBBDEPOTCOUNT,
3376 &This->extBigBlockDepotCount);
3377
3378 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3379 {
3380 StorageUtl_ReadDWord(
3381 headerBigBlock,
3382 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3383 &(This->bigBlockDepotStart[index]));
3384 }
3385
3386 /*
3387 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3388 */
3389 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3390 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3391
3392 /*
3393 * Right now, the code is making some assumptions about the size of the
3394 * blocks, just make sure they are what we're expecting.
3395 */
3396 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3397 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3398 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3399 {
3400 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3401 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3402 hr = STG_E_INVALIDHEADER;
3403 }
3404 else
3405 hr = S_OK;
3406 }
3407
3408 return hr;
3409 }
3410
3411 /******************************************************************************
3412 * Storage32Impl_SaveFileHeader
3413 *
3414 * This method will save to the file the header
3415 */
3416 static void StorageImpl_SaveFileHeader(